@dotrino/identity 0.69.0 → 0.70.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.69.0",
3
+ "version": "0.70.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",
package/vault/acta.js CHANGED
@@ -358,6 +358,35 @@ export async function sealActa ({ acta, privateKey, privateJwk }) {
358
358
  * aceptando a un sellador que ya retiraste. Es el problema de siempre y hace falta un
359
359
  * oráculo, no más firmas.
360
360
  */
361
+ /**
362
+ * ¿DE QUIÉN ES ESTO? La pregunta que se hacen geo, reputación y cualquiera que reciba algo
363
+ * firmado de un desconocido. Es lo único que necesitan llamar.
364
+ *
365
+ * Devuelve el `profileId` —la identidad— o por qué no.
366
+ *
367
+ * EL CERTIFICADO NO HACE FALTA AQUÍ, y conviene decirlo porque es lo que uno espera:
368
+ * el acta ya dice quién puede firmar por la identidad, y la cadena prueba el acta. El
369
+ * certificado es del protocolo aparato↔bóveda (autenticar peticiones), no de atribuir
370
+ * contenido. Mezclarlos obligaba a que cada post arrastrara un papel que no aporta nada.
371
+ *
372
+ * Lo que SÍ hay que entender: esto dice «esta identidad firmó esto», no «esto es
373
+ * reciente». Si la cadena que te llega es vieja, sigue verificando — para eso está el
374
+ * registro público, que es otra capa.
375
+ */
376
+ export async function verifySignedBy ({ data, signature, publickey, chain, expectedProfileId = null } = {}) {
377
+ if (!data || typeof signature !== 'string' || typeof publickey !== 'string') {
378
+ return { ok: false, reason: 'shape' }
379
+ }
380
+ const c = await verifySealerChain(chain, { expectedProfileId })
381
+ if (!c.ok) return { ok: false, reason: 'cadena:' + c.reason }
382
+ const cabeza = chain[chain.length - 1]
383
+ // Quien firmó tiene que ser miembro CON `sign` en el acta vigente de esa cadena. Si le
384
+ // quitaste el permiso, sus firmas dejan de contar en cuanto se ve un acta más nueva.
385
+ if (!memberCanSign(cabeza, publickey)) return { ok: false, reason: 'firmante-no-autorizado' }
386
+ if (!(await verifyDeviceSig({ publickey, data, signature }))) return { ok: false, reason: 'firma-invalida' }
387
+ return { ok: true, profileId: c.profileId, seq: c.seq, signer: publickey }
388
+ }
389
+
361
390
  export async function verifySealerChain (chain, { expectedProfileId = null } = {}) {
362
391
  if (!Array.isArray(chain) || !chain.length) return { ok: false, reason: 'cadena-vacia' }
363
392
  const [raiz] = chain
@@ -909,7 +938,7 @@ export async function canAdopt ({ candidate, current }) {
909
938
  }
910
939
 
911
940
  export default {
912
- ACTA_V, CAPS, CAP_SCOPE, genesisActa, actaBody, actaHash, memberId, checkShape, verifySealerChain,
941
+ ACTA_V, CAPS, CAP_SCOPE, genesisActa, actaBody, actaHash, memberId, checkShape, verifySealerChain, verifySignedBy,
913
942
  sealActa, verifyActa, applyChanges, makeRenounce, verifyRenounce,
914
943
  makeContinuity, verifyContinuity,
915
944
  cardBody, makeProfileCard, verifyProfileCard, canAdoptCard, sealersOf, canSeal,
package/vault/core.js CHANGED
@@ -1357,7 +1357,22 @@ export async function createIdentityCore ({ kv: rawKv, peers, makeSync = null, k
1357
1357
  if (data == null) throw new Error('data required')
1358
1358
  const local = async () => {
1359
1359
  const bytes = new TextEncoder().encode(canonicalStringify(data))
1360
- return { signature: await signBytes(keypair.privateKey, bytes), publickey: publickeyJwkStr }
1360
+ const acta = loadActa()
1361
+ return {
1362
+ signature: await signBytes(keypair.privateKey, bytes),
1363
+ publickey: publickeyJwkStr,
1364
+ // A NOMBRE DE QUIÉN VA. `publickey` es la llave de ESTE aparato, y las apps la
1365
+ // venían guardando como si fuera la identidad: publicar desde el teléfono y
1366
+ // desde el PC quedaba a nombre de dos «personas» distintas, y la reputación se
1367
+ // repartía entre ellas en vez de acumularse.
1368
+ //
1369
+ // La identidad es el `profileId` —la llave del génesis, que no cambia nunca— y
1370
+ // la cadena es lo que prueba que este firmante le pertenece. Van juntos porque
1371
+ // por separado no sirven: el `profileId` solo es una afirmación, y la cadena sin
1372
+ // él no dice a quién atribuir.
1373
+ profileId: acta?.profileId || publickeyJwkStr,
1374
+ chain: sealerChain()
1375
+ }
1361
1376
  }
1362
1377
  const acta = loadActa()
1363
1378
  const puedeFirmar = !acta || Acta.memberCan(acta, publickeyJwkStr, 'sign', loadRenounces())