@interop/wallet-core 0.4.1 → 0.5.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.
@@ -0,0 +1,36 @@
1
+ import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
2
+ /**
3
+ * Default cosmetic label for a per-collection key-agreement agent. Local naming
4
+ * only (does not affect key material); safe to override.
5
+ */
6
+ export declare const DEFAULT_KAK_HANDLE = "was-react-kak";
7
+ /**
8
+ * Derives one collection's X25519 key agreement key (KAK) plus its resolver, the
9
+ * material the doc-cipher needs. The concrete KAK type carries `type` /
10
+ * `publicKeyMultibase`; it is widened to `IKeyAgreementKey` only at the
11
+ * boundary.
12
+ */
13
+ export interface CollectionKeys {
14
+ keyAgreementKey: IKeyAgreementKey;
15
+ keyResolver: IKeyResolver;
16
+ }
17
+ /**
18
+ * Derives one collection's vault key material from the master seed:
19
+ * `HKDF(master, 'kak:v1:<collectionId>')` to a per-collection seed, then the
20
+ * Ed25519-to-X25519 (Montgomery-form) key-agreement key. Deterministic, so the
21
+ * same seed decrypts the same envelopes on any device. Encryption uses these
22
+ * per-collection KAKs from day one -- never share one KAK across collections.
23
+ *
24
+ * @param options {object}
25
+ * @param options.seed {Uint8Array} the 32-byte master seed
26
+ * @param options.collectionId {string} the WAS collection id (the HKDF label)
27
+ * @param [options.kakHandle] {string} cosmetic agent label; does not affect
28
+ * keys (defaults to `DEFAULT_KAK_HANDLE`)
29
+ * @returns {Promise<CollectionKeys>}
30
+ */
31
+ export declare function deriveCollectionKeys({ seed, collectionId, kakHandle }: {
32
+ seed: Uint8Array;
33
+ collectionId: string;
34
+ kakHandle?: string;
35
+ }): Promise<CollectionKeys>;
36
+ //# sourceMappingURL=collectionKeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectionKeys.d.ts","sourceRoot":"","sources":["../../src/identity/collectionKeys.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AAIrC;;;GAGG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAMjD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B;AAiCD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,IAAI,EACJ,YAAY,EACZ,SAA8B,EAC/B,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,OAAO,CAAC,cAAc,CAAC,CAgB1B"}
@@ -0,0 +1,92 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * Per-collection vault-key (KAK) derivation from a 32-byte master seed.
6
+ *
7
+ * SEED-DERIVATION CONVENTION (pinned, part of the shared-key contract): the
8
+ * pinned `@interop/webkms-client` exposes `CapabilityAgent.fromSeed({ seed })`,
9
+ * which takes the raw 32 bytes AS-IS (no hashing). We use it for the
10
+ * per-collection key-agreement keys, feeding raw bytes -- never `fromSecret`,
11
+ * which salt-hashes a STRING and would derive a different key for a byte array
12
+ * vs its text form.
13
+ *
14
+ * WHAT SELECTS THE KEY (verified against `CapabilityAgent.fromSeed`): the key
15
+ * material is derived from the `seed` bytes (the HMAC key) and the `keyName`
16
+ * string (the HMAC message) alone. The `handle` is stored on the returned agent
17
+ * as a cosmetic identifier and does NOT enter key derivation -- nor the derived
18
+ * did:key id, which is the fingerprint of the seed+keyName key pair. So the
19
+ * PINNED derivation inputs are the seed bytes and the `keyName` value
20
+ * (`KAK_KEY_NAME` below); changing THAT after first use is a data-migration
21
+ * event. The `kakHandle` parameter is safe to change and exists only so an app
22
+ * can supply a label for cosmetic continuity; it does not affect the keys or
23
+ * any stored data.
24
+ *
25
+ * Per-collection KAKs derive via `HKDF-SHA256(master, info = 'kak:v1:<id>')` to
26
+ * a 32-byte per-collection seed, then the standard Ed25519-to-X25519 path. HKDF
27
+ * one-wayness means a shared per-collection key exposes nothing about the master
28
+ * or sibling collections -- the future multi-app sharing unit.
29
+ *
30
+ * Not test-node-safe on React Native, but fine under Node/Vitest: the crypto
31
+ * stack (`webkms-client`, `x25519-key-agreement-key`) runs on the standard Web
32
+ * Crypto that Node 24 provides.
33
+ */
34
+ import { CapabilityAgent } from '@interop/webkms-client';
35
+ import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
36
+ import { singleKeyResolver } from './keyResolver.js';
37
+ /**
38
+ * Default cosmetic label for a per-collection key-agreement agent. Local naming
39
+ * only (does not affect key material); safe to override.
40
+ */
41
+ export const DEFAULT_KAK_HANDLE = 'was-react-kak';
42
+ // PINNED key-derivation input (the HMAC message that, with the seed, selects
43
+ // the key). Changing it after first use is a data-migration event.
44
+ const KAK_KEY_NAME = 'kak';
45
+ /**
46
+ * HKDF-SHA256 expansion of the master seed into a 32-byte per-context seed.
47
+ *
48
+ * @param master {Uint8Array}
49
+ * @param info {string} the domain-separation label (e.g. `kak:v1:projects`)
50
+ * @returns {Promise<Uint8Array>}
51
+ */
52
+ async function hkdfExpand(master, info) {
53
+ const key = await globalThis.crypto.subtle.importKey('raw', master, 'HKDF', false, ['deriveBits']);
54
+ const bits = await globalThis.crypto.subtle.deriveBits({
55
+ name: 'HKDF',
56
+ hash: 'SHA-256',
57
+ salt: new Uint8Array(0),
58
+ info: new TextEncoder().encode(info)
59
+ }, key, 256);
60
+ return new Uint8Array(bits);
61
+ }
62
+ /**
63
+ * Derives one collection's vault key material from the master seed:
64
+ * `HKDF(master, 'kak:v1:<collectionId>')` to a per-collection seed, then the
65
+ * Ed25519-to-X25519 (Montgomery-form) key-agreement key. Deterministic, so the
66
+ * same seed decrypts the same envelopes on any device. Encryption uses these
67
+ * per-collection KAKs from day one -- never share one KAK across collections.
68
+ *
69
+ * @param options {object}
70
+ * @param options.seed {Uint8Array} the 32-byte master seed
71
+ * @param options.collectionId {string} the WAS collection id (the HKDF label)
72
+ * @param [options.kakHandle] {string} cosmetic agent label; does not affect
73
+ * keys (defaults to `DEFAULT_KAK_HANDLE`)
74
+ * @returns {Promise<CollectionKeys>}
75
+ */
76
+ export async function deriveCollectionKeys({ seed, collectionId, kakHandle = DEFAULT_KAK_HANDLE }) {
77
+ const collectionSeed = await hkdfExpand(seed, `kak:v1:${collectionId}`);
78
+ const keyAgent = await CapabilityAgent.fromSeed({
79
+ seed: collectionSeed,
80
+ handle: kakHandle,
81
+ keyName: KAK_KEY_NAME
82
+ });
83
+ const keyAgreementKey = X25519KeyAgreementKey2020.fromEd25519VerificationKey2020({
84
+ keyPair: keyAgent.getVerificationKeyPair()
85
+ });
86
+ const keyResolver = singleKeyResolver({ keyAgreementKey });
87
+ return {
88
+ keyAgreementKey: keyAgreementKey,
89
+ keyResolver
90
+ };
91
+ }
92
+ //# sourceMappingURL=collectionKeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectionKeys.js","sourceRoot":"","sources":["../../src/identity/collectionKeys.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAA;AAM7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAEpD;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAA;AAEjD,6EAA6E;AAC7E,mEAAmE;AACnE,MAAM,YAAY,GAAG,KAAK,CAAA;AAa1B;;;;;;GAMG;AACH,KAAK,UAAU,UAAU,CACvB,MAAkB,EAClB,IAAY;IAEZ,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAClD,KAAK,EACL,MAAiC,EACjC,MAAM,EACN,KAAK,EACL,CAAC,YAAY,CAAC,CACf,CAAA;IACD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CACpD;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC;QACvB,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;KACrC,EACD,GAAG,EACH,GAAG,CACJ,CAAA;IACD,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAA;AAC7B,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,EACzC,IAAI,EACJ,YAAY,EACZ,SAAS,GAAG,kBAAkB,EAK/B;IACC,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,UAAU,YAAY,EAAE,CAAC,CAAA;IACvE,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC;QAC9C,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,YAAY;KACtB,CAAC,CAAA;IACF,MAAM,eAAe,GACnB,yBAAyB,CAAC,8BAA8B,CAAC;QACvD,OAAO,EAAE,QAAQ,CAAC,sBAAsB,EAAE;KAC3C,CAAC,CAAA;IACJ,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAA;IAC1D,OAAO;QACL,eAAe,EAAE,eAAmC;QACpD,WAAW;KACZ,CAAA;AACH,CAAC"}
@@ -10,6 +10,9 @@
10
10
  * single-key resolver) under the fixed bootstrap handle / key name.
11
11
  * - `singleKeyResolver` -- the one-key `IKeyResolver` factory (also used by
12
12
  * app-side derivations such as a keyring unlock identity).
13
+ * - `deriveCollectionKeys` -- per-collection vault-key (KAK) derivation:
14
+ * `HKDF-SHA256(master seed, 'kak:v1:<collectionId>')` to a per-collection
15
+ * seed, then the Ed25519-to-X25519 key-agreement key plus its resolver.
13
16
  *
14
17
  * Kept out of the root export: this subpath pulls the webkms-client / ezcap /
15
18
  * x25519 dependency graph (the same isolation pattern as `./request`).
@@ -17,4 +20,6 @@
17
20
  export { BOOTSTRAP_HANDLE, BOOTSTRAP_KEY_NAME, agentsFromSecret, agentsFromSeed } from './agents.js';
18
21
  export type { ProfileAgents } from './agents.js';
19
22
  export { singleKeyResolver } from './keyResolver.js';
23
+ export { deriveCollectionKeys, DEFAULT_KAK_HANDLE } from './collectionKeys.js';
24
+ export type { CollectionKeys } from './collectionKeys.js';
20
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/identity/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACf,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/identity/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACf,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC9E,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA"}
@@ -10,10 +10,14 @@
10
10
  * single-key resolver) under the fixed bootstrap handle / key name.
11
11
  * - `singleKeyResolver` -- the one-key `IKeyResolver` factory (also used by
12
12
  * app-side derivations such as a keyring unlock identity).
13
+ * - `deriveCollectionKeys` -- per-collection vault-key (KAK) derivation:
14
+ * `HKDF-SHA256(master seed, 'kak:v1:<collectionId>')` to a per-collection
15
+ * seed, then the Ed25519-to-X25519 key-agreement key plus its resolver.
13
16
  *
14
17
  * Kept out of the root export: this subpath pulls the webkms-client / ezcap /
15
18
  * x25519 dependency graph (the same isolation pattern as `./request`).
16
19
  */
17
20
  export { BOOTSTRAP_HANDLE, BOOTSTRAP_KEY_NAME, agentsFromSecret, agentsFromSeed } from './agents.js';
18
21
  export { singleKeyResolver } from './keyResolver.js';
22
+ export { deriveCollectionKeys, DEFAULT_KAK_HANDLE } from './collectionKeys.js';
19
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/identity/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;GAYG;AACH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/identity/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@interop/wallet-core",
3
3
  "description": "Shared wallet-domain logic (WAS sync engine core and wallet Space layout contracts) for Interop wallet apps.",
4
- "version": "0.4.1",
4
+ "version": "0.5.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "pnpm run clear && tsc",