@dotrino/vault 0.78.0 → 0.79.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/package.json +1 -1
- package/src/protocol.js +3 -3
- package/src/service.js +14 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotrino/vault",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.79.0",
|
|
4
4
|
"description": "Usa ESTE dispositivo (navegador) como bóveda/CA del ecosistema Dotrino: atiende enrolamientos por el proxy y firma certificados de delegación a tus máquinas. Incluye el cliente de SERVICIO (Node): un proyecto se enrola una vez y jala sus credenciales del vault en vez del .env (`import '@dotrino/vault/config'`).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/protocol.js
CHANGED
|
@@ -65,7 +65,7 @@ export const MSG = Object.freeze({
|
|
|
65
65
|
// empieza con el heap limpio.
|
|
66
66
|
// Va FIRMADO por la maestra y el agente lo verifica contra su `iss` pineada: un
|
|
67
67
|
// aviso de reinicio sin autenticar ES un ataque de denegación.
|
|
68
|
-
SECRETS_CHANGED: 'vault.secrets.changed', // vault → servicio: { body:{op,ns,ts},
|
|
68
|
+
SECRETS_CHANGED: 'vault.secrets.changed', // vault → servicio: { body:{op,ns,ts}, seal, acta }
|
|
69
69
|
// REPARTIR LA LLAVE DE UN CAJÓN A UN MIEMBRO NUEVO. Lo hace el SERVICIO y no la
|
|
70
70
|
// bóveda, porque la bóveda no puede abrir la llave sin la frase y el servicio ya la
|
|
71
71
|
// tiene: re-envolverla no le añade ningún poder. Ver `docs/secretos-sellados.md` §8.11.
|
|
@@ -74,7 +74,7 @@ export const MSG = Object.freeze({
|
|
|
74
74
|
// el ACTA que viene dentro (también firmada) y saca de ahí la llave pública del
|
|
75
75
|
// destinatario. Así, ni siquiera una bóveda comprometida puede hacerle envolver la
|
|
76
76
|
// llave para alguien que la maestra no haya metido en su propio cajón.
|
|
77
|
-
REWRAP: 'vault.rewrap', // vault → servicio: { body:{op,owner,gen,wrap,target,acta,ts},
|
|
77
|
+
REWRAP: 'vault.rewrap', // vault → servicio: { body:{op,owner,gen,wrap,target,acta,ts}, seal }
|
|
78
78
|
REWRAP_OK: 'vault.rewrap.ok', // servicio → vault: { data:{op,owner,gen,target,wrap,ts}, signature, cert }
|
|
79
79
|
// --- CONSOLA REMOTA (docs/consola-remota.md) — requiere cert `vault:admin` ---
|
|
80
80
|
// Un solo mensaje con `data.op`: pending · pair · approve · reject · revoke · audit.
|
|
@@ -85,7 +85,7 @@ export const MSG = Object.freeze({
|
|
|
85
85
|
// Aviso a TODOS los miembros de que el perfil cambió (alguien entró o salió). Es la
|
|
86
86
|
// contrapartida de administrar a distancia: sin esto, un enrolamiento remoto sería
|
|
87
87
|
// invisible para el resto de tus dispositivos.
|
|
88
|
-
ADMIN_EVENT: 'vault.admin.event', // vault → todos: { body:{ev,deviceId,by,ts},
|
|
88
|
+
ADMIN_EVENT: 'vault.admin.event', // vault → todos: { body:{ev,deviceId,by,ts}, acta } (pedidos: + seal)
|
|
89
89
|
// RÉPLICAS: la principal empuja lo que hay que servir (el acta y los sobres, que van
|
|
90
90
|
// firmados de antes y no se pueden falsificar) y la réplica acusa hasta qué `seq` tiene.
|
|
91
91
|
REPLICA_PUSH: 'vault.replica.push', // master → réplica: { body:{seq,acta,secrets,ts}, signature }
|
package/src/service.js
CHANGED
|
@@ -1137,8 +1137,9 @@ export async function watchSecretsChanges ({
|
|
|
1137
1137
|
* NO SE FÍA DE LO QUE LE MANDAN, y esto es lo que hace que sea seguro incluso si la
|
|
1138
1138
|
* bóveda estuviera comprometida:
|
|
1139
1139
|
*
|
|
1140
|
-
* · la petición
|
|
1141
|
-
*
|
|
1140
|
+
* · la petición la firma la llave de SELLADO que nombra el acta (`verifyResponder`), no la
|
|
1141
|
+
* maestra: esto tiene que funcionar con la bóveda cerrada;
|
|
1142
|
+
* · el acta viaja dentro y la selló la maestra que este agente conoce;
|
|
1142
1143
|
* · **la llave pública del destinatario se saca del ACTA, nunca del mensaje** — si
|
|
1143
1144
|
* se cogiera del mensaje, quien lo mandara podría hacer que este agente envolviera
|
|
1144
1145
|
* la llave para una pública suya;
|
|
@@ -1150,9 +1151,9 @@ export async function watchSecretsChanges ({
|
|
|
1150
1151
|
if (!body || body.op !== 'rewrap') return
|
|
1151
1152
|
const mineOwners = [`ns:${ns}`, `dev:${saved.device.publickey}`]
|
|
1152
1153
|
if (!mineOwners.includes(body.owner)) return log('[vault] rewrap for a drawer that is not mine: ignored')
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1154
|
+
try {
|
|
1155
|
+
await verifyResponder({ body, seal: payload.seal, acta: body.acta, masterPubkey: master, knownSeq: readServiceIdentity(dir)?.actaSeq ?? null })
|
|
1156
|
+
} catch (e) { return log(`[vault] rewrap request BADLY SIGNED: ignored (${e.message})`) }
|
|
1156
1157
|
const acta = body.acta
|
|
1157
1158
|
if (!acta || acta.sealedBy !== master || !(await verifyActa({ acta })).ok) {
|
|
1158
1159
|
return log('[vault] rewrap request without a valid record: ignored')
|
|
@@ -1192,11 +1193,15 @@ export async function watchSecretsChanges ({
|
|
|
1192
1193
|
// que un aviso falso con fecha lejana no pueda dejar fuera a los de verdad.
|
|
1193
1194
|
if (inFlight.has(body.ts)) return
|
|
1194
1195
|
inFlight.add(body.ts)
|
|
1195
|
-
|
|
1196
|
+
// FIRMADO COMO TODO LO QUE SIRVE LA BÓVEDA: la llave de sellado que nombra el acta, con
|
|
1197
|
+
// el acta al lado. Lo firmaba la maestra, y la bóveda vive CERRADA: el aviso reventaba
|
|
1198
|
+
// con «vault locked» antes de salir y el servicio no se enteraba de ningún cambio
|
|
1199
|
+
// (la maestra solo firma el acta y reenvuelve sobres, CLAUDE.md).
|
|
1200
|
+
let bad = null
|
|
1196
1201
|
try {
|
|
1197
|
-
|
|
1198
|
-
} finally { inFlight.delete(body.ts) }
|
|
1199
|
-
if (
|
|
1202
|
+
await verifyResponder({ body, seal: payload.seal, acta: payload.acta, masterPubkey: master, knownSeq: readServiceIdentity(dir)?.actaSeq ?? null })
|
|
1203
|
+
} catch (e) { bad = e } finally { inFlight.delete(body.ts) }
|
|
1204
|
+
if (bad) return log(`[vault] change notice BADLY SIGNED: ignored (${bad.message})`)
|
|
1200
1205
|
if (body.ts <= lastTs) return
|
|
1201
1206
|
lastTs = body.ts
|
|
1202
1207
|
|