@dotrino/vault 0.4.0 → 0.6.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/enroll.js +27 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/vault",
3
- "version": "0.4.0",
3
+ "version": "0.6.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",
@@ -42,7 +42,7 @@
42
42
  "pairing"
43
43
  ],
44
44
  "peerDependencies": {
45
- "@dotrino/identity": ">=0.23.0",
45
+ "@dotrino/identity": ">=0.25.0",
46
46
  "@dotrino/proxy-client": ">=0.6.0"
47
47
  },
48
48
  "license": "MIT",
package/src/enroll.js CHANGED
@@ -30,6 +30,7 @@
30
30
  * `docs/pairing-protocol.md`).
31
31
  */
32
32
  import { verifyDeviceSig, pubkeyId, commitCode } from '@dotrino/identity/capabilities'
33
+ import { verifyContinuity } from '@dotrino/identity/acta'
33
34
 
34
35
  /** Un token de emparejamiento vale 5 min. */
35
36
  export const PAIRING_TTL_MS = 5 * 60 * 1000
@@ -44,6 +45,11 @@ export const MSG_ENROLLED = 'vault.enrolled'
44
45
  export const MSG_REVOKED = 'vault.revoked'
45
46
  export const MSG_ERROR = 'vault.error'
46
47
 
48
+ /** Los scopes del cert se corresponden 1:1 con las capacidades del acta (§D7). */
49
+ const SCOPE_TO_CAP = { 'vault:sign': 'sign', 'vault:store': 'store', 'vault:read': 'read' }
50
+ export const scopeToCaps = (scope) =>
51
+ (Array.isArray(scope) ? scope : [scope]).map((s) => SCOPE_TO_CAP[s]).filter(Boolean)
52
+
47
53
  /** Token aleatorio de 128 bits en hex. */
48
54
  export function randToken () {
49
55
  const b = crypto.getRandomValues(new Uint8Array(16))
@@ -152,6 +158,12 @@ export function createEnrollDesk ({
152
158
  pend.dpub = d.dpub
153
159
  pend.deviceId = deviceId
154
160
  pend.commit = d.commit
161
+ // Certificado de continuidad (opcional): lo firma la identidad que se une, con su
162
+ // propia llave. Se comprueba aquí y se guarda con el miembro al aprobar.
163
+ if (d.continuity) {
164
+ const okC = await verifyContinuity(d.continuity)
165
+ pend.continuity = (okC && d.continuity.member === d.dpub) ? d.continuity : null
166
+ }
155
167
  pend.from = from // la bóveda NO conoce el código: lo aprende cuando lo tipeas
156
168
  if (d.label) pend.label = String(d.label).slice(0, 60)
157
169
 
@@ -193,10 +205,24 @@ export function createEnrollDesk ({
193
205
  }
194
206
 
195
207
  const { cert } = await identity.signDelegation(pend.dpub, pend.scope, { ttlMs: pend.ttlMs, label: pend.label })
208
+
209
+ // Aprobar un emparejamiento ES admitir al dispositivo en el perfil: el cert es la
210
+ // credencial y el acta es la política, y no tiene sentido emitir una sin la otra.
211
+ // Las capacidades salen del scope que se pidió al emparejar (cert ∩ acta, §2.3).
212
+ let acta = null
213
+ try {
214
+ if (typeof identity.admitMember === 'function') {
215
+ const caps = scopeToCaps(pend.scope)
216
+ if (caps.length) await identity.admitMember({ pub: pend.dpub, label: pend.label || '', caps, cert, continuity: pend.continuity || null })
217
+ }
218
+ acta = (await identity.profileActa?.())?.acta || null
219
+ } catch (e) { log('[vault] no se pudo admitir en el acta:', e.message) }
220
+
196
221
  audit('enroll', { device: pend.deviceId, label: pend.label || '', scope: pend.scope })
197
222
  // Echamos el código tipeado junto al cert: el DISPOSITIVO acepta solo si coincide
198
223
  // con el que generó → una bóveda falsa (que no lo conoce) no puede enrolarlo.
199
- reply(pend.from, { type: MSG_ENROLLED, code, cert, iss })
224
+ // El acta viaja con el cert: el dispositivo ya sabe de quién es el perfil al que entra.
225
+ reply(pend.from, { type: MSG_ENROLLED, code, cert, iss, acta })
200
226
  pend.state = 'DONE'
201
227
  pending.delete(pend.token)
202
228
  fire(onPendingChange)