@dotrino/identity 0.109.0 → 0.110.1
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
package/vault/core.js
CHANGED
|
@@ -1121,9 +1121,21 @@ export async function createIdentityCore ({ kv: hostKv, peers, makeSync = null,
|
|
|
1121
1121
|
// se hace al generar una propia — quién puede hacer qué con esta llave lo decide esta
|
|
1122
1122
|
// casa, no una etiqueta que viajó dentro del paquete.
|
|
1123
1123
|
const sinTopes = (jwk) => { const { key_ops: _o, ...resto } = jwk || {}; return resto }
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1124
|
+
if (keyStore?.importPair) {
|
|
1125
|
+
// DENTRO DE UNA APP NATIVA: la llave que entrega la bóveda la guarda la app (cifrada
|
|
1126
|
+
// con una llave del chip) y firma ella. Van las dos mitades juntas, bajo un mismo id.
|
|
1127
|
+
// Si la bóveda no entrega llave de cifrado, la app estrena una (como hace el navegador).
|
|
1128
|
+
const traeEnc = !!(entrada.keys.enc && entrada.encPublickey)
|
|
1129
|
+
await keyStore.importPair({
|
|
1130
|
+
sign: { name: _scoped(KEY_STORAGE), privateJwk: sinTopes(entrada.keys.sign), publickey: entrada.publickey },
|
|
1131
|
+
enc: { name: _scoped(ENC_KEY_STORAGE), privateJwk: traeEnc ? sinTopes(entrada.keys.enc) : null, publickey: traeEnc ? entrada.encPublickey : null }
|
|
1132
|
+
})
|
|
1133
|
+
kv.removeItem(KEY_STORAGE); kv.removeItem(ENC_KEY_STORAGE)
|
|
1134
|
+
} else {
|
|
1135
|
+
await adoptJwkPair('sign', KEY_STORAGE, sinTopes(entrada.keys.sign), JSON.parse(entrada.publickey))
|
|
1136
|
+
if (entrada.keys.enc && entrada.encPublickey) {
|
|
1137
|
+
await adoptJwkPair('enc', ENC_KEY_STORAGE, sinTopes(entrada.keys.enc), JSON.parse(entrada.encPublickey))
|
|
1138
|
+
}
|
|
1127
1139
|
}
|
|
1128
1140
|
saveActa(entrada.acta)
|
|
1129
1141
|
kv.setItem(ACTA_HISTORY_STORAGE, '[]')
|
|
@@ -1147,6 +1159,18 @@ export async function createIdentityCore ({ kv: hostKv, peers, makeSync = null,
|
|
|
1147
1159
|
list.push({ id: pid, name: entrada.user, pubkey: publickeyJwkStr })
|
|
1148
1160
|
saveProfiles(list)
|
|
1149
1161
|
emitVault({ phase: 'login', address: entrada.address, volatile: !remember })
|
|
1162
|
+
// En una app nativa, la cuenta pasa también a su pantalla de Pedidos, con el mismo papel
|
|
1163
|
+
// (como al emparejar). Si la app no la guarda, se dice: la entrada en sí ya está hecha.
|
|
1164
|
+
if (keypair?.privateKey?.external) {
|
|
1165
|
+
keypair.privateKey.paired({
|
|
1166
|
+
cert: entrada.cert, vault: entrada.iss, proxy: proxy || DEFAULT_PROXY,
|
|
1167
|
+
deviceId: await deviceIdDe(entrada.publickey),
|
|
1168
|
+
name: entrada.acta?.name || entrada.address, profileId: entrada.acta?.profileId || null
|
|
1169
|
+
}).then(
|
|
1170
|
+
() => emitVault({ phase: 'native-account', ok: true }),
|
|
1171
|
+
(e) => { console.warn('[identity] the phone did not save the account: ' + e.message); emitVault({ phase: 'native-account', ok: false, code: e.code || 'native-error', reason: e.message }) }
|
|
1172
|
+
)
|
|
1173
|
+
}
|
|
1150
1174
|
return { id: pid, name: entrada.user, address: entrada.address, user: entrada.user, sid: entrada.sid, volatile: !remember }
|
|
1151
1175
|
} catch (e) {
|
|
1152
1176
|
// NADA DE MEDIAS CUENTAS: si algo falla, no se queda una identidad a medio adoptar en
|
package/vault/externalKeys.js
CHANGED
|
@@ -78,6 +78,22 @@ export function withExternalKeys (keyStore, bridge) {
|
|
|
78
78
|
const other = sib ? await keyStore.get(sib) : null
|
|
79
79
|
if (rec?.external && !(other?.external === rec.external)) await bridge.call('remove', { kid: rec.external })
|
|
80
80
|
},
|
|
81
|
+
/**
|
|
82
|
+
* UNA LLAVE QUE YA EXISTE (entrar con usuario y contraseña): la bóveda la entrega y no se
|
|
83
|
+
* puede meter en el chip, así que la app la guarda cifrada con una llave del chip y firma
|
|
84
|
+
* ella. Desde aquí es una llave externa más: la página no se queda con la privada.
|
|
85
|
+
* `sign` y `enc`: `{ name, privateJwk, publickey }`, con la pública TAL CUAL la escribe el
|
|
86
|
+
* acta (es la cadena con la que esa llave es miembro). Si la bóveda no entrega la de
|
|
87
|
+
* cifrado (`enc.privateJwk` nulo), la app estrena una y devuelve su pública.
|
|
88
|
+
*/
|
|
89
|
+
async importPair ({ sign, enc }) {
|
|
90
|
+
const params = { sign: sign.privateJwk, publickey: sign.publickey }
|
|
91
|
+
if (enc.privateJwk) { params.enc = enc.privateJwk; params.encPub = enc.publickey }
|
|
92
|
+
const k = await bridge.call('import', params)
|
|
93
|
+
if (!k?.kid || !k.encPub) throw Object.assign(new Error('the phone did not keep the key'), { code: 'native-no-key' })
|
|
94
|
+
await keyStore.set(sign.name, { external: k.kid, kind: 'sign', publicJwk: JSON.parse(sign.publickey) })
|
|
95
|
+
await keyStore.set(enc.name, { external: k.kid, kind: 'enc', publicJwk: JSON.parse(enc.publickey || k.encPub) })
|
|
96
|
+
},
|
|
81
97
|
async create (kind, name) {
|
|
82
98
|
// Las dos mitades del perfil van bajo la MISMA llave del chip: si la otra ya nació
|
|
83
99
|
// allí, se reutiliza; si no, se crea una nueva.
|
package/vault/nativeStore.js
CHANGED
|
@@ -28,8 +28,9 @@ function plainKeyRecord (rec) {
|
|
|
28
28
|
if (rec && typeof rec.external === 'string' && rec.publicJwk && (rec.kind === 'sign' || rec.kind === 'enc')) {
|
|
29
29
|
return { external: rec.external, kind: rec.kind, publicJwk: rec.publicJwk }
|
|
30
30
|
}
|
|
31
|
-
// Una llave de SOFTWARE
|
|
32
|
-
//
|
|
31
|
+
// Una llave de SOFTWARE no se guarda aquí: guardarla a medias sería perder la cuenta sin
|
|
32
|
+
// decirlo. La de entrar con contraseña no llega a este punto: la guarda la app, cifrada con
|
|
33
|
+
// una llave del chip (`importPair` de `externalKeys.js`), y aquí queda su registro externo.
|
|
33
34
|
throw Object.assign(new Error('this app keeps keys in the phone chip only: a software key cannot be stored here'), { code: 'native-no-import' })
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copia vendorizada de @dotrino/proxy-client@0.30.
|
|
1
|
+
Copia vendorizada de @dotrino/proxy-client@0.30.4 (dotrino-proxy-client/src/{index,client,signature,canonical,sealing,encpub,webrtc,stats,stats-log}.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.
|
|
@@ -37,16 +37,27 @@ const pairs = (prefix, o) => PATHS.map((k) => `${prefix}.${k}=${o[k]}`).join(' '
|
|
|
37
37
|
*/
|
|
38
38
|
export function formatStats (s, opts = {}) {
|
|
39
39
|
const tag = opts.label ? `[net] ${opts.label}` : '[net]'
|
|
40
|
+
// QUIÉN CONTESTÓ Y QUIÉN NO. Un sondeo (un ping a cada aparato del acta) deja una entrada
|
|
41
|
+
// por destinatario aunque nadie responda: esas no son conexiones y van resumidas
|
|
42
|
+
// (`unanswered`), igual que en el informe del topbar y de las apps nativas.
|
|
43
|
+
// `unknown`: quien nos escribió, nunca recibió respuesta y no sabemos su llave (otro aparato
|
|
44
|
+
// sondeando qué máquinas están encendidas). Tampoco es una conexión.
|
|
45
|
+
const isUnknown = (p) => p.msgsIn > 0 && !(p.msgsOut > 0) && !p.pubkey
|
|
46
|
+
const unknown = s.peers.filter(isUnknown)
|
|
47
|
+
const talking = s.peers.filter((p) => p.msgsIn > 0 && !isUnknown(p))
|
|
48
|
+
const silent = s.peers.filter((p) => !(p.msgsIn > 0))
|
|
40
49
|
const routes = {}
|
|
41
|
-
for (const p of
|
|
50
|
+
for (const p of talking) routes[p.route] = (routes[p.route] || 0) + 1
|
|
42
51
|
const routeList = Object.keys(routes).sort().map((r) => `${r}=${routes[r]}`).join(',') || 'none'
|
|
43
52
|
const lines = [
|
|
44
53
|
`${tag} url=${s.url} connected=${s.connected ? 'yes' : 'no'} webrtc=${s.webrtc ? 'on' : 'off'}` +
|
|
45
54
|
` ws.in=${s.proxy.bytesIn} ws.out=${s.proxy.bytesOut}` +
|
|
46
55
|
` ${pairs('in', sum(s.peers, 'bytesIn'))} ${pairs('out', sum(s.peers, 'bytesOut'))}` +
|
|
47
|
-
` peers=${
|
|
56
|
+
` peers=${talking.length} routes=${routeList}` +
|
|
57
|
+
(silent.length ? ` unanswered=${silent.length} unanswered.out=${silent.reduce((n, p) => n + PATHS.reduce((m, k) => m + p.bytesOut[k], 0), 0)}` : '') +
|
|
58
|
+
(unknown.length ? ` unknown=${unknown.length} unknown.in=${unknown.reduce((n, p) => n + PATHS.reduce((m, k) => m + p.bytesIn[k], 0), 0)}` : '')
|
|
48
59
|
]
|
|
49
|
-
for (const p of
|
|
60
|
+
for (const p of talking) {
|
|
50
61
|
lines.push(
|
|
51
62
|
`${tag} peer=${p.token || 'by-key'} route=${p.route}` +
|
|
52
63
|
` ${pairs('in', p.bytesIn)} ${pairs('out', p.bytesOut)} msgs.in=${p.msgsIn} msgs.out=${p.msgsOut}`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copia vendorizada de @dotrino/vault@0.
|
|
1
|
+
Copia vendorizada de @dotrino/vault@0.82.1 (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
|