@dotrino/identity 0.101.0 → 0.102.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotrino/identity",
3
- "version": "0.101.0",
3
+ "version": "0.102.0",
4
4
  "description": "Identidad y rating de usuarios compartidos entre apps de Dotrino (vault iframe + postMessage)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -210,7 +210,18 @@ export async function signDelegationWith (privateKey, iss, { sub, scope, iat, se
210
210
  * Firma datos con la clave de DISPOSITIVO (formato byte-idéntico a `signData` del
211
211
  * vault → lo que el dispositivo/bridge usa para firmar cada pin/acción).
212
212
  */
213
- export async function signWithDevice ({ privateJwk, privateKey, publickey, data }) {
213
+ export async function signWithDevice ({ privateJwk, privateKey, publickey, data, sign }) {
214
+ // UNA LLAVE QUE NO VIVE AQUÍ (la del chip del teléfono, en la app nativa): se le pasa el
215
+ // texto canónico y devuelve la firma P1363 en base64. La privada no entra en este proceso
216
+ // en ningún momento. Con firmador externo es obligatorio decir de quién es la llave.
217
+ if (typeof sign === 'function') {
218
+ if (!publickey) throw new Error('signWithDevice: publickey is required with an external sign()')
219
+ const signature = await sign(canonicalStringify(data))
220
+ if (typeof signature !== 'string' || !signature) {
221
+ throw Object.assign(new Error('signWithDevice: the external sign() returned no signature'), { code: 'no-signature' })
222
+ }
223
+ return { signature, publickey }
224
+ }
214
225
  // `privateKey` (CryptoKey, posiblemente NO extractable) tiene prioridad: firma
215
226
  // sin tocar bytes de la privada. Con CryptoKey es obligatorio pasar `publickey`.
216
227
  if (privateKey) {
package/vault/remote.js CHANGED
@@ -75,7 +75,7 @@ export async function isAuthenticRevoke ({ body, signature, master, devicePubkey
75
75
  async function identifyAsDevice (client, device, { cert = null, acta = null } = {}) {
76
76
  if (!client.token) return
77
77
  const data = { op: 'identify', publickey: device.publickey, token: client.token, ts: Date.now() }
78
- const { signature } = await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, data })
78
+ const { signature } = await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, sign: device.sign, data })
79
79
  // cert → el proxy enruta lo dirigido a la maestra; acta → lo dirigido a la PERSONA.
80
80
  await client.identify({ data, signature, cert, acta })
81
81
  }
@@ -169,7 +169,8 @@ export async function enrollDevice ({ qr, device, onChallenge, label = '', conti
169
169
  ...(adopting && profileId ? { profileId } : {}),
170
170
  ...(continuity ? { continuity } : {}), ...(encPub ? { encPub } : {})
171
171
  }
172
- const { signature } = await signWithDevice({ privateJwk: dev.privateJwk, privateKey: dev.privateKey, publickey: dev.publickey, data })
172
+ // `dev.sign`: la llave vive fuera (el chip del teléfono) y solo se le pide la firma.
173
+ const { signature } = await signWithDevice({ privateJwk: dev.privateJwk, privateKey: dev.privateKey, publickey: dev.publickey, sign: dev.sign, data })
173
174
 
174
175
  const enrolled = new Promise((resolve, reject) => {
175
176
  let sellando = false
@@ -272,14 +273,14 @@ function vaultError (p) {
272
273
  }
273
274
 
274
275
  async function vaultRpc ({ master, proxy, device, cert, acta = null, sendType, okType, data, onRevoked, timeoutMs = 15000 }) {
275
- if (!master || !proxy || !(device?.privateJwk || device?.privateKey) || !cert) throw new Error('missing pairing data')
276
+ if (!master || !proxy || !(device?.privateJwk || device?.privateKey || typeof device?.sign === 'function') || !cert) throw new Error('missing pairing data')
276
277
  const { WebSocketProxyClient } = await import('@dotrino/proxy-client')
277
278
  const client = new WebSocketProxyClient({ url: proxy, enableWebRTC: false, autoReconnect: false })
278
279
  await client.connect()
279
280
  try {
280
281
  try { await identifyAsDevice(client, device, { cert, acta }) } catch (_) { /* sin identify seguimos: solo perdemos la cola */ }
281
282
  const signed = { ...data, publickey: device.publickey, ts: Date.now() }
282
- const { signature } = await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, data: signed })
283
+ const { signature } = await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, sign: device.sign, data: signed })
283
284
  const pending = new Promise((resolve, reject) => {
284
285
  let graceTimer = null
285
286
  const off = client.on('message', (_f, p) => {
@@ -325,7 +326,7 @@ async function vaultRpc ({ master, proxy, device, cert, acta = null, sendType, o
325
326
  * nada, como cualquier otro mensaje sin firma.
326
327
  */
327
328
  export async function checkMembership ({ master, proxy, device, onRevoked, timeoutMs = 12000 } = {}) {
328
- if (!master || !proxy || !(device?.privateJwk || device?.privateKey)) throw new Error('missing device data')
329
+ if (!master || !proxy || !(device?.privateJwk || device?.privateKey || typeof device?.sign === 'function')) throw new Error('missing device data')
329
330
  const { WebSocketProxyClient } = await import('@dotrino/proxy-client')
330
331
  const client = new WebSocketProxyClient({ url: proxy, enableWebRTC: false, autoReconnect: false })
331
332
  await client.connect()
@@ -334,7 +335,7 @@ export async function checkMembership ({ master, proxy, device, onRevoked, timeo
334
335
  // estaba apagado, si todavía está dentro de las 24 h).
335
336
  try { await identifyAsDevice(client, device) } catch (_) {}
336
337
  const data = { op: 'check', publickey: device.publickey, ts: Date.now() }
337
- const { signature } = await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, data })
338
+ const { signature } = await signWithDevice({ privateJwk: device.privateJwk, privateKey: device.privateKey, publickey: device.publickey, sign: device.sign, data })
338
339
  const res = await new Promise((resolve) => {
339
340
  let settled = false
340
341
  const done = (v) => { if (!settled) { settled = true; cleanup(); resolve(v) } }
@@ -1,4 +1,4 @@
1
- Copia vendorizada de @dotrino/proxy-client@0.23.1 (dotrino-proxy-client/src/{index,client,signature,canonical,sealing,encpub,webrtc}.js).
1
+ Copia vendorizada de @dotrino/proxy-client@0.24.0 (dotrino-proxy-client/src/{index,client,signature,canonical,sealing,encpub,webrtc}.js).
2
2
  NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
3
3
  sealing.js resuelve @dotrino/identity/content de forma PEREZOSA (= ../../content.js
4
4
  por el import map): solo se carga si de verdad se sella algo.
@@ -437,6 +437,12 @@ export class WebSocketProxyClient {
437
437
  * señalización WebRTC, presencia—: entregar eso mañana no es tarde, es
438
438
  * incorrecto (reinicia negociaciones imposibles y muestra movimientos fuera de
439
439
  * contexto). NO lo uses para mensajes de chat, que sí quieren esperar.
440
+ *
441
+ * `opts.quiet` hace lo contrario de despertar: el mensaje SE GUARDA en la cola igual,
442
+ * pero el proxio no toca el timbre push del destinatario. Úsalo para lo que puede
443
+ * esperar a que la otra punta abra por su cuenta —un aviso de que algo cambió—. Sin
444
+ * él, cada aviso hace sonar el teléfono, y un timbre que no trae nada que hacer
445
+ * enseña a ignorar el siguiente, que sí lo trae.
440
446
  */
441
447
  /**
442
448
  * Seal a payload towards a peer's encryption key and send it. This is what an app
@@ -708,6 +714,7 @@ export class WebSocketProxyClient {
708
714
  message: typeof payload === 'string' ? payload : JSON.stringify(payload)
709
715
  }
710
716
  if (opts.ephemeral) msg.ephemeral = true
717
+ if (opts.quiet) msg.quiet = true
711
718
  this._sendRaw(msg)
712
719
  }
713
720
 
@@ -1,4 +1,4 @@
1
- Copia vendorizada de @dotrino/vault@0.71.0 (dotrino-vault/lib/src/{index,enroll,protocol,passwordLogins,loginClient,b64}.js).
1
+ Copia vendorizada de @dotrino/vault@0.72.0 (dotrino-vault/lib/src/{index,enroll,protocol,passwordLogins,loginClient,b64}.js).
2
2
  NO se edita a mano: la escribe `node vendor.mjs` y la vigila test/vendor-up-to-date.test.mjs.
3
3
  index.js importa ./enroll.js y ./protocol.js (relativos, van en esta misma copia),
4
4
  @dotrino/identity/{capabilities,acta} (= ../../{capabilities,acta}.js) y