@interop/wallet-core 0.5.0 → 0.7.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 (113) hide show
  1. package/dist/enrollment/enrollment.d.ts +144 -0
  2. package/dist/enrollment/enrollment.d.ts.map +1 -0
  3. package/dist/enrollment/enrollment.js +296 -0
  4. package/dist/enrollment/enrollment.js.map +1 -0
  5. package/dist/enrollment/index.d.ts +20 -0
  6. package/dist/enrollment/index.d.ts.map +1 -0
  7. package/dist/enrollment/index.js +19 -0
  8. package/dist/enrollment/index.js.map +1 -0
  9. package/dist/identity/index.d.ts +0 -5
  10. package/dist/identity/index.d.ts.map +1 -1
  11. package/dist/identity/index.js +0 -4
  12. package/dist/identity/index.js.map +1 -1
  13. package/dist/index.d.ts +16 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +16 -3
  16. package/dist/index.js.map +1 -1
  17. package/dist/keyring/fetch.d.ts +22 -0
  18. package/dist/keyring/fetch.d.ts.map +1 -0
  19. package/dist/keyring/fetch.js +35 -0
  20. package/dist/keyring/fetch.js.map +1 -0
  21. package/dist/keyring/index.d.ts +30 -0
  22. package/dist/keyring/index.d.ts.map +1 -0
  23. package/dist/keyring/index.js +28 -0
  24. package/dist/keyring/index.js.map +1 -0
  25. package/dist/keyring/kdf.d.ts +102 -0
  26. package/dist/keyring/kdf.d.ts.map +1 -0
  27. package/dist/keyring/kdf.js +148 -0
  28. package/dist/keyring/kdf.js.map +1 -0
  29. package/dist/keyring/record.d.ts +98 -0
  30. package/dist/keyring/record.d.ts.map +1 -0
  31. package/dist/keyring/record.js +119 -0
  32. package/dist/keyring/record.js.map +1 -0
  33. package/dist/keyring/unlockSpace.d.ts +101 -0
  34. package/dist/keyring/unlockSpace.d.ts.map +1 -0
  35. package/dist/keyring/unlockSpace.js +226 -0
  36. package/dist/keyring/unlockSpace.js.map +1 -0
  37. package/dist/keys/index.d.ts +24 -0
  38. package/dist/keys/index.d.ts.map +1 -0
  39. package/dist/keys/index.js +22 -0
  40. package/dist/keys/index.js.map +1 -0
  41. package/dist/keys/puk.d.ts +42 -0
  42. package/dist/keys/puk.d.ts.map +1 -0
  43. package/dist/keys/puk.js +56 -0
  44. package/dist/keys/puk.js.map +1 -0
  45. package/dist/keys/pukRoster.d.ts +210 -0
  46. package/dist/keys/pukRoster.d.ts.map +1 -0
  47. package/dist/keys/pukRoster.js +263 -0
  48. package/dist/keys/pukRoster.js.map +1 -0
  49. package/dist/keys/rosterStore.d.ts +18 -0
  50. package/dist/keys/rosterStore.d.ts.map +1 -0
  51. package/dist/keys/rosterStore.js +38 -0
  52. package/dist/keys/rosterStore.js.map +1 -0
  53. package/dist/markers/acquire.d.ts +107 -0
  54. package/dist/markers/acquire.d.ts.map +1 -0
  55. package/dist/markers/acquire.js +83 -0
  56. package/dist/markers/acquire.js.map +1 -0
  57. package/dist/markers/cipher.d.ts +56 -0
  58. package/dist/markers/cipher.d.ts.map +1 -0
  59. package/dist/markers/cipher.js +73 -0
  60. package/dist/markers/cipher.js.map +1 -0
  61. package/dist/markers/index.d.ts +30 -0
  62. package/dist/markers/index.d.ts.map +1 -0
  63. package/dist/markers/index.js +29 -0
  64. package/dist/markers/index.js.map +1 -0
  65. package/dist/markers/refresh.d.ts +60 -0
  66. package/dist/markers/refresh.d.ts.map +1 -0
  67. package/dist/markers/refresh.js +67 -0
  68. package/dist/markers/refresh.js.map +1 -0
  69. package/dist/recovery/index.d.ts +31 -0
  70. package/dist/recovery/index.d.ts.map +1 -0
  71. package/dist/recovery/index.js +28 -0
  72. package/dist/recovery/index.js.map +1 -0
  73. package/dist/recovery/recoveryCode.d.ts +105 -0
  74. package/dist/recovery/recoveryCode.d.ts.map +1 -0
  75. package/dist/recovery/recoveryCode.js +155 -0
  76. package/dist/recovery/recoveryCode.js.map +1 -0
  77. package/dist/recovery/recoveryRecord.d.ts +75 -0
  78. package/dist/recovery/recoveryRecord.d.ts.map +1 -0
  79. package/dist/recovery/recoveryRecord.js +89 -0
  80. package/dist/recovery/recoveryRecord.js.map +1 -0
  81. package/dist/recovery/recoveryWebvh.d.ts +126 -0
  82. package/dist/recovery/recoveryWebvh.d.ts.map +1 -0
  83. package/dist/recovery/recoveryWebvh.js +379 -0
  84. package/dist/recovery/recoveryWebvh.js.map +1 -0
  85. package/dist/space/collections.d.ts +57 -0
  86. package/dist/space/collections.d.ts.map +1 -1
  87. package/dist/space/collections.js +48 -0
  88. package/dist/space/collections.js.map +1 -1
  89. package/dist/space/index.d.ts +4 -0
  90. package/dist/space/index.d.ts.map +1 -1
  91. package/dist/space/index.js +4 -0
  92. package/dist/space/index.js.map +1 -1
  93. package/dist/webvh/didWeb.d.ts +41 -0
  94. package/dist/webvh/didWeb.d.ts.map +1 -0
  95. package/dist/webvh/didWeb.js +24 -0
  96. package/dist/webvh/didWeb.js.map +1 -0
  97. package/dist/webvh/didWebvh.d.ts +330 -0
  98. package/dist/webvh/didWebvh.d.ts.map +1 -0
  99. package/dist/webvh/didWebvh.js +775 -0
  100. package/dist/webvh/didWebvh.js.map +1 -0
  101. package/dist/webvh/index.d.ts +27 -0
  102. package/dist/webvh/index.d.ts.map +1 -0
  103. package/dist/webvh/index.js +24 -0
  104. package/dist/webvh/index.js.map +1 -0
  105. package/dist/webvh/zcap.d.ts +104 -0
  106. package/dist/webvh/zcap.d.ts.map +1 -0
  107. package/dist/webvh/zcap.js +117 -0
  108. package/dist/webvh/zcap.js.map +1 -0
  109. package/package.json +42 -4
  110. package/dist/identity/collectionKeys.d.ts +0 -36
  111. package/dist/identity/collectionKeys.d.ts.map +0 -1
  112. package/dist/identity/collectionKeys.js +0 -92
  113. package/dist/identity/collectionKeys.js.map +0 -1
@@ -0,0 +1,35 @@
1
+ import { deriveUnlockIdentity, KEYRING_KDF } from './kdf.js';
2
+ import { getUnlockKeyring } from './unlockSpace.js';
3
+ import { unwrapKeyringRecord } from './record.js';
4
+ /**
5
+ * Fetches and unwraps the keyring record an unlock secret addresses, or
6
+ * resolves `null` when no record exists there (an unknown secret). The derived
7
+ * unlock Space id is returned alongside the contents -- it is already computed,
8
+ * and callers key their local state on it.
9
+ *
10
+ * @param options {object}
11
+ * @param options.secret {string | Uint8Array} the unlock secret
12
+ * @param [options.kdf] {UnlockKdf} the unlock method's parameter set
13
+ * @param options.storageServerUrl {string} the WAS server origin
14
+ * @returns {Promise<(KeyringRecordContents & { unlockSpaceId: string }) | null>}
15
+ */
16
+ export async function fetchKeyringRecord({ secret, kdf = KEYRING_KDF, storageServerUrl }) {
17
+ const unlock = await deriveUnlockIdentity({ secret, kdf });
18
+ const record = await getUnlockKeyring({
19
+ storageServerUrl,
20
+ zcapClient: unlock.zcapClient,
21
+ spaceId: unlock.spaceId
22
+ });
23
+ if (record === null) {
24
+ return null;
25
+ }
26
+ const contents = await unwrapKeyringRecord({
27
+ record,
28
+ // `id` is always set on the unlock KAK (a controller was supplied at
29
+ // derivation), so it satisfies IKeyAgreementKey's required `id`.
30
+ keyAgreementKey: unlock.keyAgreementKey,
31
+ keyResolver: unlock.keyResolver
32
+ });
33
+ return { ...contents, unlockSpaceId: unlock.spaceId };
34
+ }
35
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/keyring/fetch.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAGjD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EACvC,MAAM,EACN,GAAG,GAAG,WAAW,EACjB,gBAAgB,EAKjB;IACC,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC1D,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;QACpC,gBAAgB;QAChB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAA;IACF,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC;QACzC,MAAM;QACN,qEAAqE;QACrE,iEAAiE;QACjE,eAAe,EAAE,MAAM,CAAC,eAAmC;QAC3D,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAA;IACF,OAAO,EAAE,GAAG,QAAQ,EAAE,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;AACvD,CAAC"}
@@ -0,0 +1,30 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The `@interop/wallet-core/keyring` subpath: the unlock layer -- how an
6
+ * unlock secret (a passphrase, a passkey PRF output) locates an account
7
+ * without authorizing anything against it.
8
+ *
9
+ * - `deriveUnlockIdentity` / `KEYRING_KDF` / `unlockSpaceIdFor` -- the
10
+ * wire-level unlock derivation (implemented over `@noble/hashes`, so it runs
11
+ * unchanged where WebCrypto's `deriveBits` is unavailable) and the unlock
12
+ * Space addressing convention.
13
+ * - `wrapKeyringRecord` / `unwrapKeyringRecord` -- the `{ version, wrapped }`
14
+ * account-pointer record codec.
15
+ * - `ensureUnlockSpace` / `getUnlockKeyring` / `putUnlockKeyring` /
16
+ * `deleteUnlockSpace` / `deleteUnlockSpaceWithCapability` -- the unlock
17
+ * Space's lifecycle and its one resource.
18
+ * - `fetchKeyringRecord` -- the composed lookup (derive, read, unwrap); an
19
+ * app's caching, pinning, and client-key persistence wrap around it.
20
+ *
21
+ * Kept out of the root export: this subpath pulls the webkms-client / ezcap /
22
+ * was-client dependency graph (the same isolation pattern as `./identity`).
23
+ */
24
+ export { deriveUnlockIdentity, KEYRING_KDF, UNLOCK_HANDLE, UNLOCK_KEY_NAME, unlockSpaceIdFor } from './kdf.js';
25
+ export type { UnlockIdentity, UnlockKdf } from './kdf.js';
26
+ export { KEYRING_RECORD_VERSION, parseRecordPointer, unwrapKeyringRecord, wrapKeyringRecord } from './record.js';
27
+ export type { AccountPointer, KeyringRecordContents } from './record.js';
28
+ export { deleteUnlockSpace, deleteUnlockSpaceWithCapability, ensureUnlockSpace, getUnlockKeyring, putUnlockKeyring, UNLOCK_SPACE_NAME } from './unlockSpace.js';
29
+ export { fetchKeyringRecord } from './fetch.js';
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEzD,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAExE,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,28 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The `@interop/wallet-core/keyring` subpath: the unlock layer -- how an
6
+ * unlock secret (a passphrase, a passkey PRF output) locates an account
7
+ * without authorizing anything against it.
8
+ *
9
+ * - `deriveUnlockIdentity` / `KEYRING_KDF` / `unlockSpaceIdFor` -- the
10
+ * wire-level unlock derivation (implemented over `@noble/hashes`, so it runs
11
+ * unchanged where WebCrypto's `deriveBits` is unavailable) and the unlock
12
+ * Space addressing convention.
13
+ * - `wrapKeyringRecord` / `unwrapKeyringRecord` -- the `{ version, wrapped }`
14
+ * account-pointer record codec.
15
+ * - `ensureUnlockSpace` / `getUnlockKeyring` / `putUnlockKeyring` /
16
+ * `deleteUnlockSpace` / `deleteUnlockSpaceWithCapability` -- the unlock
17
+ * Space's lifecycle and its one resource.
18
+ * - `fetchKeyringRecord` -- the composed lookup (derive, read, unwrap); an
19
+ * app's caching, pinning, and client-key persistence wrap around it.
20
+ *
21
+ * Kept out of the root export: this subpath pulls the webkms-client / ezcap /
22
+ * was-client dependency graph (the same isolation pattern as `./identity`).
23
+ */
24
+ export { deriveUnlockIdentity, KEYRING_KDF, UNLOCK_HANDLE, UNLOCK_KEY_NAME, unlockSpaceIdFor } from './kdf.js';
25
+ export { KEYRING_RECORD_VERSION, parseRecordPointer, unwrapKeyringRecord, wrapKeyringRecord } from './record.js';
26
+ export { deleteUnlockSpace, deleteUnlockSpaceWithCapability, ensureUnlockSpace, getUnlockKeyring, putUnlockKeyring, UNLOCK_SPACE_NAME } from './unlockSpace.js';
27
+ export { fetchKeyringRecord } from './fetch.js';
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keyring/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,UAAU,CAAA;AAGjB,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,102 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The unlock derivation: an unlock secret (a passphrase, or a passkey PRF
6
+ * output) to the unlock identity that locates an account's keyring record and
7
+ * wraps/unwraps it. Nothing about the account is derivable from the secret --
8
+ * the identity's only jobs are addressing the minimal unlock Space and holding
9
+ * the key-agreement key the keyring record is encrypted to.
10
+ *
11
+ * The derivation is wire-level: two wallet apps must produce byte-identical
12
+ * output for the same secret and parameter set, or the same passphrase would
13
+ * address two different unlock Spaces. It is therefore implemented over
14
+ * `@noble/hashes` rather than WebCrypto's `crypto.subtle.deriveBits`, which
15
+ * React Native does not provide; PBKDF2 and HKDF are both fully specified
16
+ * (RFC 8018 / RFC 5869), so the two implementations agree bit for bit.
17
+ */
18
+ import { CapabilityAgent } from '@interop/webkms-client';
19
+ import { ZcapClient } from '@interop/ezcap';
20
+ import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
21
+ import type { IKeyResolver } from '@interop/data-integrity-core';
22
+ /**
23
+ * The load-bearing `CapabilityAgent` derivation names for an unlock identity
24
+ * (the counterpart of the data identity's bootstrap names): every unlock
25
+ * derivation runs through these exact strings, so they can never change
26
+ * without stranding existing accounts.
27
+ */
28
+ export declare const UNLOCK_HANDLE = "unlock";
29
+ export declare const UNLOCK_KEY_NAME = "unlock-key";
30
+ /**
31
+ * Unlock-derivation parameters, one variant per KDF family: PBKDF2 stretches
32
+ * a low-entropy passphrase; HKDF expands already-uniform key material (e.g. a
33
+ * passkey PRF output). Each unlock method pins its own parameter set -- and
34
+ * its own salt, so two methods can never derive the same unlock identity.
35
+ * The `version` records which parameter set produced a derivation; the
36
+ * keyring record's own `version` is stamped separately.
37
+ */
38
+ export type UnlockKdf = {
39
+ version: number;
40
+ algorithm: 'PBKDF2';
41
+ iterations: number;
42
+ hash: string;
43
+ salt: string;
44
+ } | {
45
+ version: number;
46
+ algorithm: 'HKDF';
47
+ hash: string;
48
+ salt: string;
49
+ info: string;
50
+ };
51
+ /**
52
+ * PBKDF2 parameters for the passphrase unlock derivation
53
+ * (`unlockSeed = PBKDF2(passphrase)`). Version 1 pins exactly these
54
+ * parameters; the keyring record's `version` field records which set produced
55
+ * it, so changing any of them (iterations, hash, salt) requires minting a new
56
+ * record version rather than silently breaking existing unlock derivations.
57
+ * The salt is a fixed app-wide constant -- login stays passphrase-only, with
58
+ * no email (or other) input mixed into the derivation. Every unlock method's
59
+ * KDF carries a distinct salt, so two methods can never derive the same
60
+ * unlock Space.
61
+ */
62
+ export declare const KEYRING_KDF: UnlockKdf;
63
+ /**
64
+ * Derives the full unlock identity from an unlock secret: the unlock
65
+ * CapabilityAgent, a ZcapClient that can both invoke and delegate (the
66
+ * unlock agent delegates a management zcap on its own Space to the account
67
+ * controller at bind time), the unlock KAK + resolver for wrap/unwrap, and the
68
+ * unlock Space id. Performs no I/O -- the derivation seam for tests and future
69
+ * unlock methods.
70
+ *
71
+ * @param options {object}
72
+ * @param options.secret {string | Uint8Array}
73
+ * @param options.kdf {UnlockKdf}
74
+ * @returns {Promise<object>}
75
+ */
76
+ export declare function deriveUnlockIdentity({ secret, kdf }: {
77
+ secret: string | Uint8Array;
78
+ kdf: UnlockKdf;
79
+ }): Promise<{
80
+ agent: CapabilityAgent;
81
+ zcapClient: ZcapClient;
82
+ keyAgreementKey: X25519KeyAgreementKey2020;
83
+ keyResolver: IKeyResolver;
84
+ spaceId: string;
85
+ }>;
86
+ /**
87
+ * The unlock Space id an unlock identity addresses:
88
+ * `base64url(SHA-256(did))`, unpadded -- a discovery convention, not an
89
+ * authorization one (holding the id grants nothing).
90
+ *
91
+ * @param options {object}
92
+ * @param options.did {string} the unlock identity's did:key
93
+ * @returns {string}
94
+ */
95
+ export declare function unlockSpaceIdFor({ did }: {
96
+ did: string;
97
+ }): string;
98
+ /**
99
+ * The derived unlock identity, as `deriveUnlockIdentity` returns it.
100
+ */
101
+ export type UnlockIdentity = Awaited<ReturnType<typeof deriveUnlockIdentity>>;
102
+ //# sourceMappingURL=kdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kdf.d.ts","sourceRoot":"","sources":["../../src/keyring/kdf.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAOhE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,eAAO,MAAM,eAAe,eAAe,CAAA;AAQ3C;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GACjB;IACE,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,QAAQ,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,GACD;IACE,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAEL;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,EAAE,SAMzB,CAAA;AA2DD;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,MAAM,EACN,GAAG,EACJ,EAAE;IACD,MAAM,EAAE,MAAM,GAAG,UAAU,CAAA;IAC3B,GAAG,EAAE,SAAS,CAAA;CACf;;;;;;GAyBA;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEjE;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,148 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The unlock derivation: an unlock secret (a passphrase, or a passkey PRF
6
+ * output) to the unlock identity that locates an account's keyring record and
7
+ * wraps/unwraps it. Nothing about the account is derivable from the secret --
8
+ * the identity's only jobs are addressing the minimal unlock Space and holding
9
+ * the key-agreement key the keyring record is encrypted to.
10
+ *
11
+ * The derivation is wire-level: two wallet apps must produce byte-identical
12
+ * output for the same secret and parameter set, or the same passphrase would
13
+ * address two different unlock Spaces. It is therefore implemented over
14
+ * `@noble/hashes` rather than WebCrypto's `crypto.subtle.deriveBits`, which
15
+ * React Native does not provide; PBKDF2 and HKDF are both fully specified
16
+ * (RFC 8018 / RFC 5869), so the two implementations agree bit for bit.
17
+ */
18
+ import { CapabilityAgent } from '@interop/webkms-client';
19
+ import { Ed25519Signature2020 } from '@interop/ed25519-signature';
20
+ import { ZcapClient } from '@interop/ezcap';
21
+ import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
22
+ import { base64urlnopad } from '@scure/base';
23
+ import { hkdf } from '@noble/hashes/hkdf.js';
24
+ import { pbkdf2Async } from '@noble/hashes/pbkdf2.js';
25
+ import { sha256, sha512 } from '@noble/hashes/sha2.js';
26
+ import { singleKeyResolver } from '../identity/keyResolver.js';
27
+ /**
28
+ * The load-bearing `CapabilityAgent` derivation names for an unlock identity
29
+ * (the counterpart of the data identity's bootstrap names): every unlock
30
+ * derivation runs through these exact strings, so they can never change
31
+ * without stranding existing accounts.
32
+ */
33
+ export const UNLOCK_HANDLE = 'unlock';
34
+ export const UNLOCK_KEY_NAME = 'unlock-key';
35
+ /**
36
+ * The number of bytes an unlock derivation produces (a 32-byte seed, which is
37
+ * what `CapabilityAgent.fromSeed` takes).
38
+ */
39
+ const UNLOCK_SEED_BYTES = 32;
40
+ /**
41
+ * PBKDF2 parameters for the passphrase unlock derivation
42
+ * (`unlockSeed = PBKDF2(passphrase)`). Version 1 pins exactly these
43
+ * parameters; the keyring record's `version` field records which set produced
44
+ * it, so changing any of them (iterations, hash, salt) requires minting a new
45
+ * record version rather than silently breaking existing unlock derivations.
46
+ * The salt is a fixed app-wide constant -- login stays passphrase-only, with
47
+ * no email (or other) input mixed into the derivation. Every unlock method's
48
+ * KDF carries a distinct salt, so two methods can never derive the same
49
+ * unlock Space.
50
+ */
51
+ export const KEYRING_KDF = {
52
+ version: 1,
53
+ algorithm: 'PBKDF2',
54
+ iterations: 600_000,
55
+ hash: 'SHA-256',
56
+ salt: 'freewallet/keyring/unlock/v1'
57
+ };
58
+ /**
59
+ * The noble hash constructor a WebCrypto hash name selects, so the derivation
60
+ * matches `crypto.subtle.deriveBits` for the same parameters.
61
+ *
62
+ * @param hash {string} a WebCrypto digest name (`SHA-256`, `SHA-512`)
63
+ * @returns {object} the noble hash
64
+ */
65
+ function nobleHash(hash) {
66
+ if (hash === 'SHA-256') {
67
+ return sha256;
68
+ }
69
+ if (hash === 'SHA-512') {
70
+ return sha512;
71
+ }
72
+ throw new Error(`Unsupported unlock KDF hash "${hash}".`);
73
+ }
74
+ /**
75
+ * Derives the 32-byte unlock seed from an unlock secret, branching on the KDF
76
+ * family: PBKDF2 stretches a passphrase, HKDF expands already-uniform key
77
+ * material such as a passkey PRF output.
78
+ *
79
+ * @param options {object}
80
+ * @param options.secret {string | Uint8Array}
81
+ * @param options.kdf {UnlockKdf}
82
+ * @returns {Promise<Uint8Array>}
83
+ */
84
+ async function deriveUnlockSeed({ secret, kdf }) {
85
+ // Copy a bytes secret into a fresh buffer: a caller's slice may be a view
86
+ // into a larger buffer, and the codecs below read the whole view.
87
+ const secretBytes = typeof secret === 'string'
88
+ ? new TextEncoder().encode(secret)
89
+ : new Uint8Array(secret);
90
+ const hash = nobleHash(kdf.hash);
91
+ const salt = new TextEncoder().encode(kdf.salt);
92
+ if (kdf.algorithm === 'PBKDF2') {
93
+ return pbkdf2Async(hash, secretBytes, salt, {
94
+ c: kdf.iterations,
95
+ dkLen: UNLOCK_SEED_BYTES
96
+ });
97
+ }
98
+ return hkdf(hash, secretBytes, salt, new TextEncoder().encode(kdf.info), UNLOCK_SEED_BYTES);
99
+ }
100
+ /**
101
+ * Derives the full unlock identity from an unlock secret: the unlock
102
+ * CapabilityAgent, a ZcapClient that can both invoke and delegate (the
103
+ * unlock agent delegates a management zcap on its own Space to the account
104
+ * controller at bind time), the unlock KAK + resolver for wrap/unwrap, and the
105
+ * unlock Space id. Performs no I/O -- the derivation seam for tests and future
106
+ * unlock methods.
107
+ *
108
+ * @param options {object}
109
+ * @param options.secret {string | Uint8Array}
110
+ * @param options.kdf {UnlockKdf}
111
+ * @returns {Promise<object>}
112
+ */
113
+ export async function deriveUnlockIdentity({ secret, kdf }) {
114
+ const seed = await deriveUnlockSeed({ secret, kdf });
115
+ const agent = await CapabilityAgent.fromSeed({
116
+ seed,
117
+ handle: UNLOCK_HANDLE,
118
+ keyName: UNLOCK_KEY_NAME
119
+ });
120
+ const signer = agent.getSigner();
121
+ const zcapClient = new ZcapClient({
122
+ SuiteClass: Ed25519Signature2020,
123
+ invocationSigner: signer,
124
+ delegationSigner: signer
125
+ });
126
+ // The unlock KAK is the Montgomery form of the unlock signing key -- the same
127
+ // derivation the client side uses (`agentsFromSeed`), so a returning user
128
+ // reconstitutes the exact key that wrapped the keyring record.
129
+ const keyAgreementKey = X25519KeyAgreementKey2020.fromEd25519VerificationKey2020({
130
+ keyPair: agent.getVerificationKeyPair()
131
+ });
132
+ const keyResolver = singleKeyResolver({ keyAgreementKey });
133
+ const spaceId = unlockSpaceIdFor({ did: agent.id });
134
+ return { agent, zcapClient, keyAgreementKey, keyResolver, spaceId };
135
+ }
136
+ /**
137
+ * The unlock Space id an unlock identity addresses:
138
+ * `base64url(SHA-256(did))`, unpadded -- a discovery convention, not an
139
+ * authorization one (holding the id grants nothing).
140
+ *
141
+ * @param options {object}
142
+ * @param options.did {string} the unlock identity's did:key
143
+ * @returns {string}
144
+ */
145
+ export function unlockSpaceIdFor({ did }) {
146
+ return base64urlnopad.encode(sha256(new TextEncoder().encode(did)));
147
+ }
148
+ //# sourceMappingURL=kdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kdf.js","sourceRoot":"","sources":["../../src/keyring/kdf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The keyring record codec: the `{ version, wrapped }` envelope stored as the
6
+ * one resource of an account's unlock Space. Its plaintext carries the account
7
+ * controller, the email captured at bind time, and the account pointer -- and
8
+ * deliberately no key material of any kind, so the record locates an account
9
+ * without authorizing anything against it.
10
+ *
11
+ * The wrap is an EDV document envelope under the unlock key-agreement key,
12
+ * bound to the `keyring` cipher context, so a keyring envelope can never be
13
+ * mistaken for (or swapped with) any other wrapped record.
14
+ */
15
+ import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
16
+ /**
17
+ * The version stamped on the stored `{ version, wrapped }` keyring envelope.
18
+ * Version 2 is the account-pointer record; version-1 records (which carried a
19
+ * wrapped account-wide data seed) are refused as unusable -- such accounts are
20
+ * re-provisioned, not migrated.
21
+ */
22
+ export declare const KEYRING_RECORD_VERSION = 2;
23
+ /**
24
+ * The account pointer a keyring record carries in place of the retired data
25
+ * seed: where the account lives (`spaceId` + `host`, the WAS server origin)
26
+ * and, once provisioning has published it, the account's stable did:webvh id.
27
+ * Discovery only -- holding the pointer authorizes nothing.
28
+ */
29
+ export interface AccountPointer {
30
+ did?: string;
31
+ spaceId: string;
32
+ host: string;
33
+ }
34
+ /**
35
+ * The unwrapped contents of a keyring record: the account controller (the
36
+ * first enrolled client's did:key today), the account email captured at bind
37
+ * time (when one was given -- carried so any unlock method recovers it; a
38
+ * passkey login has no login form to ask on), and the account pointer (absent
39
+ * only on no-WAS deployments, where there is no Space to point at).
40
+ */
41
+ export interface KeyringRecordContents {
42
+ controller: string;
43
+ email?: string;
44
+ pointer?: AccountPointer;
45
+ }
46
+ /**
47
+ * Wraps the account-pointer contents into a keyring record: the controller,
48
+ * email, and pointer (+ timestamp) encrypted under the unlock KAK via the EDV
49
+ * cipher. Deliberately carries no key material of any kind.
50
+ *
51
+ * @param options {object}
52
+ * @param options.controller {string} the account did:key
53
+ * @param [options.email] {string} the account email, when known
54
+ * @param [options.pointer] {AccountPointer} the account pointer (absent on
55
+ * no-WAS deployments)
56
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
57
+ * @param options.keyResolver {IKeyResolver}
58
+ * @returns {Promise<{ version: number, wrapped: unknown }>}
59
+ */
60
+ export declare function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }: {
61
+ controller: string;
62
+ email?: string;
63
+ pointer?: AccountPointer;
64
+ keyAgreementKey: IKeyAgreementKey;
65
+ keyResolver: IKeyResolver;
66
+ }): Promise<{
67
+ version: number;
68
+ wrapped: unknown;
69
+ }>;
70
+ /**
71
+ * Unwraps and validates a keyring record. Rejects a record whose `version` is
72
+ * not the current one (version-1 records carried the retired wrapped data
73
+ * seed and are refused -- accounts are re-provisioned, not migrated), and
74
+ * sanity-checks the decrypted plaintext (non-empty controller, well-formed
75
+ * pointer when present).
76
+ *
77
+ * @param options {object}
78
+ * @param options.record {unknown}
79
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
80
+ * @param options.keyResolver {IKeyResolver}
81
+ * @returns {Promise<KeyringRecordContents>}
82
+ */
83
+ export declare function unwrapKeyringRecord({ record, keyAgreementKey, keyResolver }: {
84
+ record: unknown;
85
+ keyAgreementKey: IKeyAgreementKey;
86
+ keyResolver: IKeyResolver;
87
+ }): Promise<KeyringRecordContents>;
88
+ /**
89
+ * Parses and validates the optional `pointer` member of a keyring record
90
+ * plaintext. An absent member is a no-WAS record (returns undefined); a
91
+ * present-but-malformed one throws -- a record that claims a pointer but
92
+ * cannot state where the account lives is unusable.
93
+ *
94
+ * @param value {unknown} the record's `pointer` member
95
+ * @returns {AccountPointer | undefined}
96
+ */
97
+ export declare function parseRecordPointer(value: unknown): AccountPointer | undefined;
98
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AAIrC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,UAAU,EACV,KAAK,EACL,OAAO,EACP,eAAe,EACf,WAAW,EACZ,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;IACxB,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAuBjD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,MAAM,EACN,eAAe,EACf,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAsCjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAsB7E"}
@@ -0,0 +1,119 @@
1
+ import { createEdvDocCipher } from '@interop/was-client/edv';
2
+ import { KEYRING_COLLECTION } from '../space/collections.js';
3
+ /**
4
+ * The version stamped on the stored `{ version, wrapped }` keyring envelope.
5
+ * Version 2 is the account-pointer record; version-1 records (which carried a
6
+ * wrapped account-wide data seed) are refused as unusable -- such accounts are
7
+ * re-provisioned, not migrated.
8
+ */
9
+ export const KEYRING_RECORD_VERSION = 2;
10
+ /**
11
+ * Wraps the account-pointer contents into a keyring record: the controller,
12
+ * email, and pointer (+ timestamp) encrypted under the unlock KAK via the EDV
13
+ * cipher. Deliberately carries no key material of any kind.
14
+ *
15
+ * @param options {object}
16
+ * @param options.controller {string} the account did:key
17
+ * @param [options.email] {string} the account email, when known
18
+ * @param [options.pointer] {AccountPointer} the account pointer (absent on
19
+ * no-WAS deployments)
20
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
21
+ * @param options.keyResolver {IKeyResolver}
22
+ * @returns {Promise<{ version: number, wrapped: unknown }>}
23
+ */
24
+ export async function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }) {
25
+ const cipher = await createEdvDocCipher({
26
+ keyAgreementKey,
27
+ keyResolver,
28
+ collectionId: KEYRING_COLLECTION.id
29
+ });
30
+ const { envelope } = await cipher.encrypt({
31
+ data: {
32
+ controller,
33
+ ...(email ? { email } : {}),
34
+ ...(pointer
35
+ ? {
36
+ pointer: {
37
+ ...(pointer.did ? { did: pointer.did } : {}),
38
+ spaceId: pointer.spaceId,
39
+ host: pointer.host
40
+ }
41
+ }
42
+ : {}),
43
+ createdAt: new Date().toISOString()
44
+ }
45
+ });
46
+ return { version: KEYRING_RECORD_VERSION, wrapped: envelope };
47
+ }
48
+ /**
49
+ * Unwraps and validates a keyring record. Rejects a record whose `version` is
50
+ * not the current one (version-1 records carried the retired wrapped data
51
+ * seed and are refused -- accounts are re-provisioned, not migrated), and
52
+ * sanity-checks the decrypted plaintext (non-empty controller, well-formed
53
+ * pointer when present).
54
+ *
55
+ * @param options {object}
56
+ * @param options.record {unknown}
57
+ * @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
58
+ * @param options.keyResolver {IKeyResolver}
59
+ * @returns {Promise<KeyringRecordContents>}
60
+ */
61
+ export async function unwrapKeyringRecord({ record, keyAgreementKey, keyResolver }) {
62
+ if (record === null || typeof record !== 'object') {
63
+ throw new Error('Malformed keyring record.');
64
+ }
65
+ const { version, wrapped } = record;
66
+ if (version !== KEYRING_RECORD_VERSION) {
67
+ throw new Error(`Unsupported keyring record version "${String(version)}".`);
68
+ }
69
+ const cipher = await createEdvDocCipher({
70
+ keyAgreementKey,
71
+ keyResolver,
72
+ collectionId: KEYRING_COLLECTION.id
73
+ });
74
+ const plaintext = (await cipher.decrypt({
75
+ envelope: wrapped
76
+ }));
77
+ if (typeof plaintext.controller !== 'string' || !plaintext.controller) {
78
+ throw new Error('Keyring record is missing a controller.');
79
+ }
80
+ const pointer = parseRecordPointer(plaintext.pointer);
81
+ return {
82
+ controller: plaintext.controller,
83
+ // A record bound without an email simply has no email; anything
84
+ // non-string is ignored, not fatal.
85
+ ...(typeof plaintext.email === 'string' && plaintext.email
86
+ ? { email: plaintext.email }
87
+ : {}),
88
+ ...(pointer ? { pointer } : {})
89
+ };
90
+ }
91
+ /**
92
+ * Parses and validates the optional `pointer` member of a keyring record
93
+ * plaintext. An absent member is a no-WAS record (returns undefined); a
94
+ * present-but-malformed one throws -- a record that claims a pointer but
95
+ * cannot state where the account lives is unusable.
96
+ *
97
+ * @param value {unknown} the record's `pointer` member
98
+ * @returns {AccountPointer | undefined}
99
+ */
100
+ export function parseRecordPointer(value) {
101
+ if (value === undefined) {
102
+ return undefined;
103
+ }
104
+ if (value === null || typeof value !== 'object') {
105
+ throw new Error('Keyring record has a malformed account pointer.');
106
+ }
107
+ const { did, spaceId, host } = value;
108
+ if (typeof spaceId !== 'string' || !spaceId) {
109
+ throw new Error('Keyring record account pointer is missing its spaceId.');
110
+ }
111
+ if (typeof host !== 'string' || !host) {
112
+ throw new Error('Keyring record account pointer is missing its host.');
113
+ }
114
+ if (did !== undefined && (typeof did !== 'string' || !did)) {
115
+ throw new Error('Keyring record account pointer has a malformed did.');
116
+ }
117
+ return { ...(did ? { did } : {}), spaceId, host };
118
+ }
119
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"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"}