@dotrino/remote-agent 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/e2e.js +5 -72
- package/package.json +5 -5
- package/src/agent.js +2 -2
package/e2e.js
CHANGED
|
@@ -1,75 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* e2e.js — canal cifrado
|
|
3
|
-
* remoto del ecosistema Dotrino. Isomórfico: usa WebCrypto
|
|
4
|
-
* (`globalThis.crypto.subtle`), disponible tanto en el navegador (PWA) como en
|
|
5
|
-
* Node ≥ 18.
|
|
2
|
+
* e2e.js — el canal cifrado entre el dispositivo enlazado y el agente.
|
|
6
3
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* efímero (ECDH P-256 → HKDF → AES-256-GCM) para que el relay de transporte
|
|
11
|
-
* (`proxy.dotrino.com`) vea únicamente texto cifrado. La identidad de las dos
|
|
12
|
-
* puntas ya la garantizan las firmas del vault que envuelven las claves efímeras.
|
|
13
|
-
*
|
|
14
|
-
* Fuente única para `@dotrino/remote-agent` (consumido por terminal, ia y futuras
|
|
15
|
-
* apps de agente remoto).
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
const subtle = () => globalThis.crypto.subtle
|
|
19
|
-
|
|
20
|
-
const b64 = {
|
|
21
|
-
enc (buf) {
|
|
22
|
-
const b = new Uint8Array(buf)
|
|
23
|
-
let s = ''
|
|
24
|
-
for (let i = 0; i < b.length; i++) s += String.fromCharCode(b[i])
|
|
25
|
-
return btoa(s)
|
|
26
|
-
},
|
|
27
|
-
dec (str) {
|
|
28
|
-
const bin = atob(str)
|
|
29
|
-
const out = new Uint8Array(bin.length)
|
|
30
|
-
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i)
|
|
31
|
-
return out
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** Genera un par ECDH P-256 efímero. Devuelve la pub en raw (base64) + privada. */
|
|
36
|
-
export async function makeEphemeral () {
|
|
37
|
-
const kp = await subtle().generateKey({ name: 'ECDH', namedCurve: 'P-256' }, false, ['deriveBits'])
|
|
38
|
-
const raw = await subtle().exportKey('raw', kp.publicKey)
|
|
39
|
-
return { privateKey: kp.privateKey, pub: b64.enc(raw) }
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Deriva la clave AES-256-GCM de la sesión a partir de la privada propia y la
|
|
44
|
-
* pub efímera (base64 raw) del otro extremo. `salt` liga la clave a la sesión.
|
|
4
|
+
* Vive en `@dotrino/identity/channel` (identity ≥ 0.120.0): aquí solo se reexporta para que
|
|
5
|
+
* quien importaba `makeEphemeral`/`deriveKey`/`seal`/`open` de este paquete siga igual. El
|
|
6
|
+
* cable no cambió: mismo HKDF, mismo `info`, mismos sobres.
|
|
45
7
|
*/
|
|
46
|
-
export
|
|
47
|
-
const otherPub = await subtle().importKey(
|
|
48
|
-
'raw', b64.dec(otherPubB64), { name: 'ECDH', namedCurve: 'P-256' }, false, []
|
|
49
|
-
)
|
|
50
|
-
const bits = await subtle().deriveBits({ name: 'ECDH', public: otherPub }, privateKey, 256)
|
|
51
|
-
const hkdfKey = await subtle().importKey('raw', bits, 'HKDF', false, ['deriveKey'])
|
|
52
|
-
return subtle().deriveKey(
|
|
53
|
-
{ name: 'HKDF', hash: 'SHA-256', salt: new TextEncoder().encode(salt), info: new TextEncoder().encode('dotrino-remote-agent-e2e') },
|
|
54
|
-
hkdfKey,
|
|
55
|
-
{ name: 'AES-GCM', length: 256 },
|
|
56
|
-
false,
|
|
57
|
-
['encrypt', 'decrypt']
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Cifra un objeto JSON → sobre { iv, ct } (ambos base64). */
|
|
62
|
-
export async function seal (key, obj) {
|
|
63
|
-
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12))
|
|
64
|
-
const pt = new TextEncoder().encode(JSON.stringify(obj))
|
|
65
|
-
const ct = await subtle().encrypt({ name: 'AES-GCM', iv }, key, pt)
|
|
66
|
-
return { iv: b64.enc(iv), ct: b64.enc(ct) }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** Descifra un sobre { iv, ct } → objeto JSON. Lanza si el tag GCM no valida. */
|
|
70
|
-
export async function open (key, env) {
|
|
71
|
-
const pt = await subtle().decrypt({ name: 'AES-GCM', iv: b64.dec(env.iv) }, key, b64.dec(env.ct))
|
|
72
|
-
return JSON.parse(new TextDecoder().decode(pt))
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export { b64 }
|
|
8
|
+
export { makeEphemeral, deriveKey, seal, open, b64 } from '@dotrino/identity/channel'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/remote-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Middleware compartido del ecosistema Dotrino para apps de agente remoto cifrado punto a punto (handshake E2E, emparejamiento SAS con vault, revocación, descubrimiento). Lo consumen dotrino-terminal (PTY) y dotrino-ia (chat con IA).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"p2p"
|
|
47
47
|
],
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@dotrino/identity": ">=0.
|
|
49
|
+
"@dotrino/identity": ">=0.120.0",
|
|
50
50
|
"@dotrino/proxy-client": ">=0.36.1",
|
|
51
51
|
"@dotrino/vault": ">=0.85.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@dotrino/identity": "0.
|
|
55
|
-
"@dotrino/proxy-client": "0.36.
|
|
56
|
-
"@dotrino/vault": "0.
|
|
54
|
+
"@dotrino/identity": "0.120.0",
|
|
55
|
+
"@dotrino/proxy-client": "0.36.2",
|
|
56
|
+
"@dotrino/vault": "0.90.2"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/agent.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import fs from 'node:fs'
|
|
23
23
|
import path from 'node:path'
|
|
24
|
-
import { verifyChain, signWithDevice, verifyDeviceSig, pubkeyId } from '@dotrino/identity/capabilities'
|
|
24
|
+
import { verifyChain, signWithDevice, verifyDeviceSig, pubkeyId, keyLabel } from '@dotrino/identity/capabilities'
|
|
25
25
|
import { sealersOf, memberCan, checkVaultReply } from '@dotrino/identity/acta'
|
|
26
26
|
import { adoptRecord } from './record.js'
|
|
27
27
|
import { installNodeGlobals } from '../node-globals.js'
|
|
@@ -134,7 +134,7 @@ export async function startRemoteAgent (opts = {}) {
|
|
|
134
134
|
}
|
|
135
135
|
const master = link.iss
|
|
136
136
|
const myPub = link.device.publickey
|
|
137
|
-
const myId =
|
|
137
|
+
const myId = await keyLabel(myPub)
|
|
138
138
|
|
|
139
139
|
installNodeGlobals(dir)
|
|
140
140
|
|