@dotrino/identity 0.109.0 → 0.110.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
package/vault/core.js
CHANGED
|
@@ -1121,9 +1121,22 @@ 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
|
+
if (!entrada.keys.enc || !entrada.encPublickey) {
|
|
1128
|
+
throw Object.assign(new Error('this login has no encryption key: the phone needs both'), { code: 'native-import-needs-enc' })
|
|
1129
|
+
}
|
|
1130
|
+
await keyStore.importPair({
|
|
1131
|
+
sign: { name: _scoped(KEY_STORAGE), privateJwk: sinTopes(entrada.keys.sign), publickey: entrada.publickey },
|
|
1132
|
+
enc: { name: _scoped(ENC_KEY_STORAGE), privateJwk: sinTopes(entrada.keys.enc), publickey: entrada.encPublickey }
|
|
1133
|
+
})
|
|
1134
|
+
kv.removeItem(KEY_STORAGE); kv.removeItem(ENC_KEY_STORAGE)
|
|
1135
|
+
} else {
|
|
1136
|
+
await adoptJwkPair('sign', KEY_STORAGE, sinTopes(entrada.keys.sign), JSON.parse(entrada.publickey))
|
|
1137
|
+
if (entrada.keys.enc && entrada.encPublickey) {
|
|
1138
|
+
await adoptJwkPair('enc', ENC_KEY_STORAGE, sinTopes(entrada.keys.enc), JSON.parse(entrada.encPublickey))
|
|
1139
|
+
}
|
|
1127
1140
|
}
|
|
1128
1141
|
saveActa(entrada.acta)
|
|
1129
1142
|
kv.setItem(ACTA_HISTORY_STORAGE, '[]')
|
|
@@ -1147,6 +1160,18 @@ export async function createIdentityCore ({ kv: hostKv, peers, makeSync = null,
|
|
|
1147
1160
|
list.push({ id: pid, name: entrada.user, pubkey: publickeyJwkStr })
|
|
1148
1161
|
saveProfiles(list)
|
|
1149
1162
|
emitVault({ phase: 'login', address: entrada.address, volatile: !remember })
|
|
1163
|
+
// En una app nativa, la cuenta pasa también a su pantalla de Pedidos, con el mismo papel
|
|
1164
|
+
// (como al emparejar). Si la app no la guarda, se dice: la entrada en sí ya está hecha.
|
|
1165
|
+
if (keypair?.privateKey?.external) {
|
|
1166
|
+
keypair.privateKey.paired({
|
|
1167
|
+
cert: entrada.cert, vault: entrada.iss, proxy: proxy || DEFAULT_PROXY,
|
|
1168
|
+
deviceId: await deviceIdDe(entrada.publickey),
|
|
1169
|
+
name: entrada.acta?.name || entrada.address, profileId: entrada.acta?.profileId || null
|
|
1170
|
+
}).then(
|
|
1171
|
+
() => emitVault({ phase: 'native-account', ok: true }),
|
|
1172
|
+
(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 }) }
|
|
1173
|
+
)
|
|
1174
|
+
}
|
|
1150
1175
|
return { id: pid, name: entrada.user, address: entrada.address, user: entrada.user, sid: entrada.sid, volatile: !remember }
|
|
1151
1176
|
} catch (e) {
|
|
1152
1177
|
// NADA DE MEDIAS CUENTAS: si algo falla, no se queda una identidad a medio adoptar en
|
package/vault/externalKeys.js
CHANGED
|
@@ -78,6 +78,21 @@ 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).
|
|
87
|
+
*/
|
|
88
|
+
async importPair ({ sign, enc }) {
|
|
89
|
+
const k = await bridge.call('import', {
|
|
90
|
+
sign: sign.privateJwk, enc: enc.privateJwk, publickey: sign.publickey, encPub: enc.publickey
|
|
91
|
+
})
|
|
92
|
+
if (!k?.kid) throw Object.assign(new Error('the phone did not keep the key'), { code: 'native-no-key' })
|
|
93
|
+
await keyStore.set(sign.name, { external: k.kid, kind: 'sign', publicJwk: JSON.parse(sign.publickey) })
|
|
94
|
+
await keyStore.set(enc.name, { external: k.kid, kind: 'enc', publicJwk: JSON.parse(enc.publickey) })
|
|
95
|
+
},
|
|
81
96
|
async create (kind, name) {
|
|
82
97
|
// Las dos mitades del perfil van bajo la MISMA llave del chip: si la otra ya nació
|
|
83
98
|
// 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
|