@interop/wallet-core 0.20.0 → 0.22.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/README.md +5 -2
- package/dist/descriptors/cipher.d.ts +4 -1
- package/dist/descriptors/cipher.d.ts.map +1 -1
- package/dist/descriptors/cipher.js +16 -8
- package/dist/descriptors/cipher.js.map +1 -1
- package/dist/keyring/record.d.ts +69 -18
- package/dist/keyring/record.d.ts.map +1 -1
- package/dist/keyring/record.js +90 -23
- package/dist/keyring/record.js.map +1 -1
- package/dist/keys/index.d.ts +4 -0
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +4 -0
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/spaceEpochs.d.ts +56 -0
- package/dist/keys/spaceEpochs.d.ts.map +1 -0
- package/dist/keys/spaceEpochs.js +55 -0
- package/dist/keys/spaceEpochs.js.map +1 -0
- package/dist/keys/userKeyCascade.d.ts +14 -16
- package/dist/keys/userKeyCascade.d.ts.map +1 -1
- package/dist/keys/userKeyCascade.js +9 -43
- package/dist/keys/userKeyCascade.js.map +1 -1
- package/dist/recovery/recoveryRecord.d.ts +16 -13
- package/dist/recovery/recoveryRecord.d.ts.map +1 -1
- package/dist/recovery/recoveryRecord.js +14 -17
- package/dist/recovery/recoveryRecord.js.map +1 -1
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +4 -0
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/space/collections.d.ts +66 -32
- package/dist/space/collections.d.ts.map +1 -1
- package/dist/space/collections.js +88 -12
- package/dist/space/collections.js.map +1 -1
- package/dist/space/index.d.ts +13 -5
- package/dist/space/index.d.ts.map +1 -1
- package/dist/space/index.js +12 -4
- package/dist/space/index.js.map +1 -1
- package/dist/space/provisioning.d.ts +46 -0
- package/dist/space/provisioning.d.ts.map +1 -0
- package/dist/space/provisioning.js +52 -0
- package/dist/space/provisioning.js.map +1 -0
- package/dist/sync/engine.d.ts +17 -1
- package/dist/sync/engine.d.ts.map +1 -1
- package/dist/sync/engine.js +5 -0
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/index.d.ts +8 -3
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +8 -3
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/push.d.ts.map +1 -1
- package/dist/sync/push.js +3 -2
- package/dist/sync/push.js.map +1 -1
- package/dist/sync/remint.d.ts +78 -0
- package/dist/sync/remint.d.ts.map +1 -0
- package/dist/sync/remint.js +73 -0
- package/dist/sync/remint.js.map +1 -0
- package/dist/sync/types.d.ts +26 -4
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js +4 -4
- package/dist/sync/types.js.map +1 -1
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +3 -0
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +1 -0
- package/dist/webvh/revokeClient.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -81,8 +81,11 @@ The subpaths:
|
|
|
81
81
|
staleness detected from durable state alone, history escrowed -- also the
|
|
82
82
|
completion sweep's building block), plus the detector that converges a roster
|
|
83
83
|
left wrapping the current key to a recipient the account document no longer
|
|
84
|
-
keys. Also the
|
|
85
|
-
|
|
84
|
+
keys. Also `ensureWalletSpaceEpochs`, the provision-time install of each
|
|
85
|
+
encrypted wallet collection's key epoch[0] (a fresh random epoch key wrapped
|
|
86
|
+
to the user key) -- the EDV-bearing second step of `provisionWalletSpace`.
|
|
87
|
+
Also the enrolled-client display labels (`key-map/client-labels.json`) and
|
|
88
|
+
their WAS-backed store. Also the client-key record codec: the contents and
|
|
86
89
|
strict validation of the local record each wallet client keeps its own key
|
|
87
90
|
material in (storage and wrapping stay app-side).
|
|
88
91
|
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
* Built, the cipher acquires the collection's descriptor (fetch, cache the
|
|
11
11
|
* success, cached fallback on failure -- see `acquire.ts`) and constructs the
|
|
12
12
|
* underlying EDV cipher from it; with no descriptor, or a descriptor with no
|
|
13
|
-
* epochs,
|
|
13
|
+
* epochs, the build refuses fail-closed (every encrypted collection's
|
|
14
|
+
* descriptor carries an epoch roster from provisioning, so the absence can
|
|
15
|
+
* only mean the install has not landed or the host is lying). When a decrypt
|
|
16
|
+
* throws
|
|
14
17
|
* `UnknownEpochError`, the cipher re-acquires the descriptor, rebuilds itself,
|
|
15
18
|
* and retries that decrypt exactly once -- and only once per cipher instance,
|
|
16
19
|
* which the host scopes to one `(profile, collection)` session by dropping
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cipher.d.ts","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"cipher.d.ts","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAGL,KAAK,SAAS,EACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,cAAc,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,4BAA4B,CAAC,EACjD,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,YAAY,EACb,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACnC,MAAM,CAAC,EAAE,0BAA0B,CAAA;IACnC,KAAK,EAAE,yBAAyB,CAAA;IAChC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;CACtE,GAAG,OAAO,CAAC,SAAS,CAAC,CA8DrB"}
|
|
@@ -23,18 +23,26 @@ import { acquireDescriptor } from './acquire.js';
|
|
|
23
23
|
* @returns {Promise<DocCipher>}
|
|
24
24
|
*/
|
|
25
25
|
export async function createRefreshingEdvDocCipher({ keyAgreementKey, keyResolver, collectionId, idDerivation, source, cache, onFetchError }) {
|
|
26
|
-
const build = async () =>
|
|
27
|
-
|
|
28
|
-
keyResolver,
|
|
29
|
-
collectionId,
|
|
30
|
-
idDerivation,
|
|
31
|
-
encryption: await acquireDescriptor({
|
|
26
|
+
const build = async () => {
|
|
27
|
+
const encryption = await acquireDescriptor({
|
|
32
28
|
source,
|
|
33
29
|
cache,
|
|
34
30
|
collectionId,
|
|
35
31
|
onFetchError
|
|
36
|
-
})
|
|
37
|
-
|
|
32
|
+
});
|
|
33
|
+
if (!encryption) {
|
|
34
|
+
throw new Error(`Collection "${collectionId}" has no encryption descriptor available ` +
|
|
35
|
+
'(fetched or cached). Every encrypted collection carries its key ' +
|
|
36
|
+
'epochs from provisioning; refusing to build a cipher without them.');
|
|
37
|
+
}
|
|
38
|
+
return createEdvDocCipher({
|
|
39
|
+
keyAgreementKey,
|
|
40
|
+
keyResolver,
|
|
41
|
+
collectionId,
|
|
42
|
+
idDerivation,
|
|
43
|
+
encryption
|
|
44
|
+
});
|
|
45
|
+
};
|
|
38
46
|
let inner = await build();
|
|
39
47
|
// The one descriptor refresh this cipher instance (= this collection this
|
|
40
48
|
// session) may spend, shared so concurrent unknown-epoch decrypts ride a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cipher.js","sourceRoot":"","sources":["../../src/descriptors/cipher.ts"],"names":[],"mappings":"AA0BA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,iBAAiB,EAGlB,MAAM,cAAc,CAAA;AAErB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,EACjD,eAAe,EACf,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,YAAY,EASb;IACC,MAAM,KAAK,GAAG,KAAK,IAAwB,EAAE;QAC3C,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC;YACzC,MAAM;YACN,KAAK;YACL,YAAY;YACZ,YAAY;SACb,CAAC,CAAA;QACF,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,2CAA2C;gBACpE,kEAAkE;gBAClE,oEAAoE,CACvE,CAAA;QACH,CAAC;QACD,OAAO,kBAAkB,CAAC;YACxB,eAAe;YACf,WAAW;YACX,YAAY;YACZ,YAAY;YACZ,UAAU;SACX,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,IAAI,KAAK,GAAG,MAAM,KAAK,EAAE,CAAA;IACzB,0EAA0E;IAC1E,yEAAyE;IACzE,+CAA+C;IAC/C,IAAI,SAAS,GAAyB,IAAI,CAAA;IAE1C,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAE1C,aAAa,EAAE,OAAO,CAAC,EAAE;YACvB,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CACb,eAAe,YAAY,kCAAkC,CAC9D,CAAA;YACH,CAAC;YACD,OAAO,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QACrC,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE;YACxB,IAAI,CAAC;gBACH,OAAO,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YAC1C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACnD,MAAM,GAAG,CAAA;gBACX,CAAC;gBACD,SAAS,KAAK,KAAK,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;oBAClC,KAAK,GAAG,MAAM,CAAA;gBAChB,CAAC,CAAC,CAAA;gBACF,MAAM,SAAS,CAAA;gBACf,iEAAiE;gBACjE,8DAA8D;gBAC9D,+DAA+D;gBAC/D,iEAAiE;gBACjE,2BAA2B;gBAC3B,OAAO,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/keyring/record.d.ts
CHANGED
|
@@ -2,24 +2,74 @@
|
|
|
2
2
|
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* The keyring record codec: the `{ version, wrapped }` envelope
|
|
6
|
-
* one resource of an account's unlock Space. Its plaintext
|
|
7
|
-
* controller, the email captured at bind time, and the
|
|
8
|
-
* deliberately no key material of any kind, so the
|
|
9
|
-
* without authorizing anything against it.
|
|
5
|
+
* The keyring record codec: the `{ version, encryption, wrapped }` envelope
|
|
6
|
+
* stored as the one resource of an account's unlock Space. Its plaintext
|
|
7
|
+
* carries the account controller, the email captured at bind time, and the
|
|
8
|
+
* account pointer -- and deliberately no key material of any kind, so the
|
|
9
|
+
* record locates an account without authorizing anything against it.
|
|
10
10
|
*
|
|
11
|
-
* The wrap is an EDV document envelope
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* The wrap is an EDV document envelope bound to the `keyring` cipher context
|
|
12
|
+
* (so a keyring envelope can never be mistaken for, or swapped with, any other
|
|
13
|
+
* wrapped record), sealed under the record's own key epoch: every EDV envelope
|
|
14
|
+
* seals to an epoch key, so the record carries its one-epoch descriptor in its
|
|
15
|
+
* `encryption` member, epoch[0] wrapped to the unlock key-agreement key. The
|
|
16
|
+
* record stays self-contained -- unlock KAK in, contents out.
|
|
14
17
|
*/
|
|
15
18
|
import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
|
|
19
|
+
import type { CollectionEncryption } from '@interop/was-client';
|
|
20
|
+
import { type DocCipher } from '@interop/was-client/edv';
|
|
16
21
|
/**
|
|
17
|
-
* The version stamped on the stored `{ version, wrapped }` keyring
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* re-provisioned, not migrated.
|
|
22
|
+
* The version stamped on the stored `{ version, encryption, wrapped }` keyring
|
|
23
|
+
* envelope: the record whose envelope seals under the record's own key epoch
|
|
24
|
+
* (the `encryption` member). Any other version is refused as unusable -- such
|
|
25
|
+
* accounts are re-provisioned, not migrated.
|
|
21
26
|
*/
|
|
22
|
-
export declare const KEYRING_RECORD_VERSION =
|
|
27
|
+
export declare const KEYRING_RECORD_VERSION = 1;
|
|
28
|
+
/**
|
|
29
|
+
* Mints the one-epoch descriptor a fresh record is sealed under: epoch[0]
|
|
30
|
+
* wrapped to the unlock KAK alone, built through `initRecipients` against a
|
|
31
|
+
* throwaway in-memory store (the descriptor's home is the record itself).
|
|
32
|
+
*
|
|
33
|
+
* @param options {object}
|
|
34
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
|
|
35
|
+
* @returns {Promise<CollectionEncryption>}
|
|
36
|
+
*/
|
|
37
|
+
export declare function mintRecordEncryption({ keyAgreementKey }: {
|
|
38
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
39
|
+
}): Promise<CollectionEncryption>;
|
|
40
|
+
/**
|
|
41
|
+
* Builds the record cipher: the keyring-context EDV cipher over the record's
|
|
42
|
+
* own descriptor. Shared by the wrap and unwrap paths (and by the recovery
|
|
43
|
+
* record, which reuses the keyring cipher context verbatim).
|
|
44
|
+
*
|
|
45
|
+
* @param options {object}
|
|
46
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
|
|
47
|
+
* @param options.keyResolver {IKeyResolver}
|
|
48
|
+
* @param options.encryption {CollectionEncryption} the record's descriptor
|
|
49
|
+
* @returns {Promise<DocCipher>}
|
|
50
|
+
*/
|
|
51
|
+
export declare function recordCipher({ keyAgreementKey, keyResolver, encryption }: {
|
|
52
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
53
|
+
keyResolver: IKeyResolver;
|
|
54
|
+
encryption: CollectionEncryption;
|
|
55
|
+
}): Promise<DocCipher>;
|
|
56
|
+
/**
|
|
57
|
+
* Validates the common `{ version, encryption, wrapped }` frame of a stored
|
|
58
|
+
* record (keyring or recovery -- `label` names the refusals) and returns its
|
|
59
|
+
* members.
|
|
60
|
+
*
|
|
61
|
+
* @param options {object}
|
|
62
|
+
* @param options.record {unknown}
|
|
63
|
+
* @param options.label {string} `'keyring'` or `'recovery'`
|
|
64
|
+
* @returns {{ encryption: CollectionEncryption, wrapped: unknown }}
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseRecordFrame({ record, label }: {
|
|
67
|
+
record: unknown;
|
|
68
|
+
label: string;
|
|
69
|
+
}): {
|
|
70
|
+
encryption: CollectionEncryption;
|
|
71
|
+
wrapped: unknown;
|
|
72
|
+
};
|
|
23
73
|
/**
|
|
24
74
|
* The account pointer a keyring record carries in place of the retired data
|
|
25
75
|
* seed: where the account lives (`spaceId` + `host`, the WAS server origin)
|
|
@@ -45,8 +95,9 @@ export interface KeyringRecordContents {
|
|
|
45
95
|
}
|
|
46
96
|
/**
|
|
47
97
|
* Wraps the account-pointer contents into a keyring record: the controller,
|
|
48
|
-
* email, and pointer (+ timestamp)
|
|
49
|
-
*
|
|
98
|
+
* email, and pointer (+ timestamp) sealed under a freshly minted record epoch
|
|
99
|
+
* whose key is wrapped to the unlock KAK. Deliberately carries no key material
|
|
100
|
+
* of any kind.
|
|
50
101
|
*
|
|
51
102
|
* @param options {object}
|
|
52
103
|
* @param options.controller {string} the account did:key
|
|
@@ -55,7 +106,7 @@ export interface KeyringRecordContents {
|
|
|
55
106
|
* no-WAS deployments)
|
|
56
107
|
* @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
|
|
57
108
|
* @param options.keyResolver {IKeyResolver}
|
|
58
|
-
* @returns {Promise<{ version: number, wrapped: unknown }>}
|
|
109
|
+
* @returns {Promise<{ version: number, encryption: unknown, wrapped: unknown }>}
|
|
59
110
|
*/
|
|
60
111
|
export declare function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }: {
|
|
61
112
|
controller: string;
|
|
@@ -65,12 +116,12 @@ export declare function wrapKeyringRecord({ controller, email, pointer, keyAgree
|
|
|
65
116
|
keyResolver: IKeyResolver;
|
|
66
117
|
}): Promise<{
|
|
67
118
|
version: number;
|
|
119
|
+
encryption: unknown;
|
|
68
120
|
wrapped: unknown;
|
|
69
121
|
}>;
|
|
70
122
|
/**
|
|
71
123
|
* Unwraps and validates a keyring record. Rejects a record whose `version` is
|
|
72
|
-
* not the current one (
|
|
73
|
-
* seed and are refused -- accounts are re-provisioned, not migrated), and
|
|
124
|
+
* not the current one (accounts are re-provisioned, not migrated), and
|
|
74
125
|
* sanity-checks the decrypted plaintext (non-empty controller, well-formed
|
|
75
126
|
* pointer when present).
|
|
76
127
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,yBAAyB,CAAA;AAGhC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAA;AAEvC;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,eAAe,EAChB,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;CAClC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiBhC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,EACjC,eAAe,EACf,WAAW,EACX,UAAU,EACX,EAAE;IACD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;IACzB,UAAU,EAAE,oBAAoB,CAAA;CACjC,GAAG,OAAO,CAAC,SAAS,CAAC,CAOrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,KAAK,EACN,EAAE;IACD,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd,GAAG;IAAE,UAAU,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAgBzD;AAED;;;;;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;;;;;;;;;;;;;;GAcG;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,UAAU,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAwBtE;AAED;;;;;;;;;;;GAWG;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,CAgCjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAsB7E"}
|
package/dist/keyring/record.js
CHANGED
|
@@ -1,16 +1,86 @@
|
|
|
1
|
-
import { createEdvDocCipher } from '@interop/was-client/edv';
|
|
1
|
+
import { createEdvDocCipher, initRecipients, ownerRecipient } from '@interop/was-client/edv';
|
|
2
2
|
import { KEYRING_COLLECTION } from '../space/collections.js';
|
|
3
3
|
/**
|
|
4
|
-
* The version stamped on the stored `{ version, wrapped }` keyring
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* re-provisioned, not migrated.
|
|
4
|
+
* The version stamped on the stored `{ version, encryption, wrapped }` keyring
|
|
5
|
+
* envelope: the record whose envelope seals under the record's own key epoch
|
|
6
|
+
* (the `encryption` member). Any other version is refused as unusable -- such
|
|
7
|
+
* accounts are re-provisioned, not migrated.
|
|
8
8
|
*/
|
|
9
|
-
export const KEYRING_RECORD_VERSION =
|
|
9
|
+
export const KEYRING_RECORD_VERSION = 1;
|
|
10
|
+
/**
|
|
11
|
+
* Mints the one-epoch descriptor a fresh record is sealed under: epoch[0]
|
|
12
|
+
* wrapped to the unlock KAK alone, built through `initRecipients` against a
|
|
13
|
+
* throwaway in-memory store (the descriptor's home is the record itself).
|
|
14
|
+
*
|
|
15
|
+
* @param options {object}
|
|
16
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
|
|
17
|
+
* @returns {Promise<CollectionEncryption>}
|
|
18
|
+
*/
|
|
19
|
+
export async function mintRecordEncryption({ keyAgreementKey }) {
|
|
20
|
+
let stored = null;
|
|
21
|
+
const store = {
|
|
22
|
+
async read() {
|
|
23
|
+
return stored ? { descriptor: stored } : null;
|
|
24
|
+
},
|
|
25
|
+
async replace(next) {
|
|
26
|
+
stored = next;
|
|
27
|
+
},
|
|
28
|
+
async create(next) {
|
|
29
|
+
stored = next;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return initRecipients({
|
|
33
|
+
store,
|
|
34
|
+
recipients: [ownerRecipient({ keyAgreementKey })]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds the record cipher: the keyring-context EDV cipher over the record's
|
|
39
|
+
* own descriptor. Shared by the wrap and unwrap paths (and by the recovery
|
|
40
|
+
* record, which reuses the keyring cipher context verbatim).
|
|
41
|
+
*
|
|
42
|
+
* @param options {object}
|
|
43
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
|
|
44
|
+
* @param options.keyResolver {IKeyResolver}
|
|
45
|
+
* @param options.encryption {CollectionEncryption} the record's descriptor
|
|
46
|
+
* @returns {Promise<DocCipher>}
|
|
47
|
+
*/
|
|
48
|
+
export async function recordCipher({ keyAgreementKey, keyResolver, encryption }) {
|
|
49
|
+
return createEdvDocCipher({
|
|
50
|
+
keyAgreementKey,
|
|
51
|
+
keyResolver,
|
|
52
|
+
collectionId: KEYRING_COLLECTION.id,
|
|
53
|
+
encryption
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Validates the common `{ version, encryption, wrapped }` frame of a stored
|
|
58
|
+
* record (keyring or recovery -- `label` names the refusals) and returns its
|
|
59
|
+
* members.
|
|
60
|
+
*
|
|
61
|
+
* @param options {object}
|
|
62
|
+
* @param options.record {unknown}
|
|
63
|
+
* @param options.label {string} `'keyring'` or `'recovery'`
|
|
64
|
+
* @returns {{ encryption: CollectionEncryption, wrapped: unknown }}
|
|
65
|
+
*/
|
|
66
|
+
export function parseRecordFrame({ record, label }) {
|
|
67
|
+
if (record === null || typeof record !== 'object') {
|
|
68
|
+
throw new Error(`Malformed ${label} record.`);
|
|
69
|
+
}
|
|
70
|
+
const { version, encryption, wrapped } = record;
|
|
71
|
+
if (version !== KEYRING_RECORD_VERSION) {
|
|
72
|
+
throw new Error(`Unsupported ${label} record version "${String(version)}".`);
|
|
73
|
+
}
|
|
74
|
+
if (encryption === null || typeof encryption !== 'object') {
|
|
75
|
+
throw new Error(`The ${label} record is missing its encryption descriptor.`);
|
|
76
|
+
}
|
|
77
|
+
return { encryption: encryption, wrapped };
|
|
78
|
+
}
|
|
10
79
|
/**
|
|
11
80
|
* Wraps the account-pointer contents into a keyring record: the controller,
|
|
12
|
-
* email, and pointer (+ timestamp)
|
|
13
|
-
*
|
|
81
|
+
* email, and pointer (+ timestamp) sealed under a freshly minted record epoch
|
|
82
|
+
* whose key is wrapped to the unlock KAK. Deliberately carries no key material
|
|
83
|
+
* of any kind.
|
|
14
84
|
*
|
|
15
85
|
* @param options {object}
|
|
16
86
|
* @param options.controller {string} the account did:key
|
|
@@ -19,13 +89,14 @@ export const KEYRING_RECORD_VERSION = 2;
|
|
|
19
89
|
* no-WAS deployments)
|
|
20
90
|
* @param options.keyAgreementKey {IKeyAgreementKey} the unlock KAK
|
|
21
91
|
* @param options.keyResolver {IKeyResolver}
|
|
22
|
-
* @returns {Promise<{ version: number, wrapped: unknown }>}
|
|
92
|
+
* @returns {Promise<{ version: number, encryption: unknown, wrapped: unknown }>}
|
|
23
93
|
*/
|
|
24
94
|
export async function wrapKeyringRecord({ controller, email, pointer, keyAgreementKey, keyResolver }) {
|
|
25
|
-
const
|
|
95
|
+
const encryption = await mintRecordEncryption({ keyAgreementKey });
|
|
96
|
+
const cipher = await recordCipher({
|
|
26
97
|
keyAgreementKey,
|
|
27
98
|
keyResolver,
|
|
28
|
-
|
|
99
|
+
encryption
|
|
29
100
|
});
|
|
30
101
|
const { envelope } = await cipher.encrypt({
|
|
31
102
|
data: {
|
|
@@ -43,12 +114,11 @@ export async function wrapKeyringRecord({ controller, email, pointer, keyAgreeme
|
|
|
43
114
|
createdAt: new Date().toISOString()
|
|
44
115
|
}
|
|
45
116
|
});
|
|
46
|
-
return { version: KEYRING_RECORD_VERSION, wrapped: envelope };
|
|
117
|
+
return { version: KEYRING_RECORD_VERSION, encryption, wrapped: envelope };
|
|
47
118
|
}
|
|
48
119
|
/**
|
|
49
120
|
* Unwraps and validates a keyring record. Rejects a record whose `version` is
|
|
50
|
-
* not the current one (
|
|
51
|
-
* seed and are refused -- accounts are re-provisioned, not migrated), and
|
|
121
|
+
* not the current one (accounts are re-provisioned, not migrated), and
|
|
52
122
|
* sanity-checks the decrypted plaintext (non-empty controller, well-formed
|
|
53
123
|
* pointer when present).
|
|
54
124
|
*
|
|
@@ -59,17 +129,14 @@ export async function wrapKeyringRecord({ controller, email, pointer, keyAgreeme
|
|
|
59
129
|
* @returns {Promise<KeyringRecordContents>}
|
|
60
130
|
*/
|
|
61
131
|
export async function unwrapKeyringRecord({ record, keyAgreementKey, keyResolver }) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new Error(`Unsupported keyring record version "${String(version)}".`);
|
|
68
|
-
}
|
|
69
|
-
const cipher = await createEdvDocCipher({
|
|
132
|
+
const { encryption, wrapped } = parseRecordFrame({
|
|
133
|
+
record,
|
|
134
|
+
label: 'keyring'
|
|
135
|
+
});
|
|
136
|
+
const cipher = await recordCipher({
|
|
70
137
|
keyAgreementKey,
|
|
71
138
|
keyResolver,
|
|
72
|
-
|
|
139
|
+
encryption
|
|
73
140
|
});
|
|
74
141
|
const plaintext = (await cipher.decrypt({
|
|
75
142
|
envelope: wrapped
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/keyring/record.ts"],"names":[],"mappings":"AAsBA,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,EAGf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,EACzC,eAAe,EAGhB;IACC,IAAI,MAAM,GAAgC,IAAI,CAAA;IAC9C,MAAM,KAAK,GAA8B;QACvC,KAAK,CAAC,IAAI;YACR,OAAO,MAAM,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QAC/C,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,IAAI;YAChB,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,IAAI;YACf,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;KACF,CAAA;IACD,OAAO,cAAc,CAAC;QACpB,KAAK;QACL,UAAU,EAAE,CAAC,cAAc,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC;KAClD,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EACjC,eAAe,EACf,WAAW,EACX,UAAU,EAKX;IACC,OAAO,kBAAkB,CAAC;QACxB,eAAe;QACf,WAAW;QACX,YAAY,EAAE,kBAAkB,CAAC,EAAE;QACnC,UAAU;KACX,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,KAAK,EAIN;IACC,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,UAAU,CAAC,CAAA;IAC/C,CAAC;IACD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,MAIxC,CAAA;IACD,IAAI,OAAO,KAAK,sBAAsB,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,oBAAoB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9E,CAAC;IACD,IAAI,UAAU,KAAK,IAAI,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,OAAO,KAAK,+CAA+C,CAAC,CAAA;IAC9E,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,UAAkC,EAAE,OAAO,EAAE,CAAA;AACpE,CAAC;AA2BD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EACtC,UAAU,EACV,KAAK,EACL,OAAO,EACP,eAAe,EACf,WAAW,EAOZ;IACC,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAA;IAClE,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,eAAe;QACf,WAAW;QACX,UAAU;KACX,CAAC,CAAA;IACF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACxC,IAAI,EAAE;YACJ,UAAU;YACV,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,OAAO;gBACT,CAAC,CAAC;oBACE,OAAO,EAAE;wBACP,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC5C,OAAO,EAAE,OAAO,CAAC,OAAO;wBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;qBACnB;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC;KACF,CAAC,CAAA;IACF,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;AAC3E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EACxC,MAAM,EACN,eAAe,EACf,WAAW,EAKZ;IACC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC;QAC/C,MAAM;QACN,KAAK,EAAE,SAAS;KACjB,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;QAChC,eAAe;QACf,WAAW;QACX,UAAU;KACX,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC;QACtC,QAAQ,EAAE,OAAgB;KAC3B,CAAC,CAID,CAAA;IAED,IAAI,OAAO,SAAS,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAA;IAC5D,CAAC;IACD,MAAM,OAAO,GAAG,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IAErD,OAAO;QACL,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,gEAAgE;QAChE,oCAAoC;QACpC,GAAG,CAAC,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK;YACxD,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE;YAC5B,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChC,CAAA;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAA;IACpE,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,KAI9B,CAAA;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;AACnD,CAAC"}
|
package/dist/keys/index.d.ts
CHANGED
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
* re-epoch that brings an encrypted collection onto the roster's current
|
|
37
37
|
* user key, and the parallel best-effort fan-out over the collections the wallet
|
|
38
38
|
* names (also the completion sweep's driver).
|
|
39
|
+
* - `ensureWalletSpaceEpochs` -- the provision-time epoch[0] install for the
|
|
40
|
+
* wallet Space's encrypted collections, the EDV-bearing second step of
|
|
41
|
+
* `provisionWalletSpace`.
|
|
39
42
|
*/
|
|
40
43
|
export { mintUserKey, userKeyVaultKeys } from './userKey.js';
|
|
41
44
|
export type { UserKey } from './userKey.js';
|
|
@@ -46,6 +49,7 @@ export { cascadeCollectionsToUserKey, userKeyAsRecipient, rotateCollectionEpochs
|
|
|
46
49
|
export type { CollectionUserKeyRotationOutcome, UserKeyCascadeResult } from './userKeyCascade.js';
|
|
47
50
|
export type { UserKeyRosterReadResult, RosterRecipientDocument } from './userKeyRoster.js';
|
|
48
51
|
export { userKeyRosterDescriptorStore } from './rosterStore.js';
|
|
52
|
+
export { ensureWalletSpaceEpochs } from './spaceEpochs.js';
|
|
49
53
|
export { readClientLabels, removeClientLabel, setClientLabel } from './clientLabels.js';
|
|
50
54
|
export type { ClientLabelsRecord, ClientLabelsStore } from './clientLabels.js';
|
|
51
55
|
export { wasClientLabelsStore } from './wasLabelsStore.js';
|
package/dist/keys/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC5D,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,mBAAmB,EACnB,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,gCAAgC,EAChC,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EACV,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/keys/index.js
CHANGED
|
@@ -36,12 +36,16 @@
|
|
|
36
36
|
* re-epoch that brings an encrypted collection onto the roster's current
|
|
37
37
|
* user key, and the parallel best-effort fan-out over the collections the wallet
|
|
38
38
|
* names (also the completion sweep's driver).
|
|
39
|
+
* - `ensureWalletSpaceEpochs` -- the provision-time epoch[0] install for the
|
|
40
|
+
* wallet Space's encrypted collections, the EDV-bearing second step of
|
|
41
|
+
* `provisionWalletSpace`.
|
|
39
42
|
*/
|
|
40
43
|
export { mintUserKey, userKeyVaultKeys } from './userKey.js';
|
|
41
44
|
export { assertEnrolledClientKeyRecord, decodeClientKeyRecord, encodeClientKeyRecord, parseClientRecordUserKey, parseClientRecordWebvhKeys } from './clientKeyRecord.js';
|
|
42
45
|
export { addUserKeyRosterRecipient, convergeUserKeyRosterToDocument, ensureUserKeyRoster, UserKeyRosterContinuityError, UserKeyRosterIntegrityError, UserKeyRosterUnwrapError, userKeyRosterEpochsSigner, userKeyRosterRecipientResolver, readUserKeyRoster, rosterRecipientKid, rotateUserKeyRoster, verifyUserKeyRosterEpochsSig } from './userKeyRoster.js';
|
|
43
46
|
export { cascadeCollectionsToUserKey, userKeyAsRecipient, rotateCollectionEpochsToUserKey, unwrapUserKeyGenerations } from './userKeyCascade.js';
|
|
44
47
|
export { userKeyRosterDescriptorStore } from './rosterStore.js';
|
|
48
|
+
export { ensureWalletSpaceEpochs } from './spaceEpochs.js';
|
|
45
49
|
export { readClientLabels, removeClientLabel, setClientLabel } from './clientLabels.js';
|
|
46
50
|
export { wasClientLabelsStore } from './wasLabelsStore.js';
|
|
47
51
|
//# sourceMappingURL=index.js.map
|
package/dist/keys/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAG5D,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAA;AAO7B,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,mBAAmB,EACnB,4BAA4B,EAC5B,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,4BAA4B,EAC7B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAU5B,OAAO,EAAE,4BAA4B,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAE1D,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Provision-time key-epoch install for the wallet Space's encrypted
|
|
6
|
+
* collections: every encrypted collection's descriptor carries an epoch roster
|
|
7
|
+
* from birth (epoch[0] a fresh random epoch key, never a user-key generation),
|
|
8
|
+
* and reads/writes are refused fail-closed until it does. `provisionWalletSpace`
|
|
9
|
+
* (the crypto-free container ensure in `space`) declares the collections; this
|
|
10
|
+
* EDV-bearing second step installs each one's epoch[0], wrapped to the user key
|
|
11
|
+
* (recipient zero). It lives in `keys` rather than `space` so the root barrel,
|
|
12
|
+
* which re-exports `space`, stays free of the EDV crypto graph -- the same
|
|
13
|
+
* split was-client makes between `ensureSpaceAndCollection` and
|
|
14
|
+
* `ensureFirstEpoch`.
|
|
15
|
+
*/
|
|
16
|
+
import type { WasClient } from '@interop/was-client';
|
|
17
|
+
import type { UserKey } from './userKey.js';
|
|
18
|
+
/**
|
|
19
|
+
* Installs key epoch[0] on every encrypted collection of the wallet Space
|
|
20
|
+
* roster (or on the given `collectionIds`), concurrently, wrapped to the user
|
|
21
|
+
* key. Each install is `ensureFirstEpoch`: create-if-absent through the
|
|
22
|
+
* descriptor-store seam, adopting (never overwriting) a roster another
|
|
23
|
+
* provisioner already landed -- so re-running after a tear converges, and
|
|
24
|
+
* exactly one epoch[0] ever exists per collection. Run it after
|
|
25
|
+
* `provisionWalletSpace` has declared the collections; the wallet Space's
|
|
26
|
+
* provisioning is complete only once both steps have.
|
|
27
|
+
*
|
|
28
|
+
* Run both steps from the sync engine's `ensureProvisioned` seam (or, for a
|
|
29
|
+
* driver of its own, equally before the collection's first content push): the
|
|
30
|
+
* descriptor-before-first-content-push invariant rests on it. A caller that
|
|
31
|
+
* minted envelopes eagerly against a locally-minted roster and finds
|
|
32
|
+
* `installed: false` here (another provisioner's create won) has adopted the
|
|
33
|
+
* winner's descriptor and must re-mint its pending envelopes under that
|
|
34
|
+
* descriptor's current epoch before pushing -- `remintPendingEnvelopes`
|
|
35
|
+
* (`@interop/wallet-core/sync`) is that path.
|
|
36
|
+
*
|
|
37
|
+
* @param options {object}
|
|
38
|
+
* @param options.was {WasClient}
|
|
39
|
+
* @param options.spaceId {string}
|
|
40
|
+
* @param options.userKey {UserKey} the account's user key, epoch[0]'s one
|
|
41
|
+
* initial recipient
|
|
42
|
+
* @param [options.collectionIds] {string[]} the encrypted collections to
|
|
43
|
+
* cover; defaults to the wallet Space roster's encrypted collections. A
|
|
44
|
+
* caller naming its own ids (e.g. `contacts`) must name only collections
|
|
45
|
+
* declared encrypted
|
|
46
|
+
* @returns {Promise<Record<string, boolean>>} per collection id, whether
|
|
47
|
+
* this call installed its epoch[0] (`false` means an existing roster was
|
|
48
|
+
* adopted)
|
|
49
|
+
*/
|
|
50
|
+
export declare function ensureWalletSpaceEpochs({ was, spaceId, userKey, collectionIds }: {
|
|
51
|
+
was: WasClient;
|
|
52
|
+
spaceId: string;
|
|
53
|
+
userKey: UserKey;
|
|
54
|
+
collectionIds?: string[];
|
|
55
|
+
}): Promise<Record<string, boolean>>;
|
|
56
|
+
//# sourceMappingURL=spaceEpochs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaceEpochs.d.ts","sourceRoot":"","sources":["../../src/keys/spaceEpochs.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAKpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,uBAAuB,CAAC,EAC5C,GAAG,EACH,OAAO,EACP,OAAO,EACP,aAAa,EACd,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAyBnC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ensureFirstEpoch } from '@interop/was-client/edv';
|
|
2
|
+
import { WALLET_SPACE_PROVISION_ROSTER } from '../space/collections.js';
|
|
3
|
+
import { userKeyAsRecipient } from './userKeyCascade.js';
|
|
4
|
+
/**
|
|
5
|
+
* Installs key epoch[0] on every encrypted collection of the wallet Space
|
|
6
|
+
* roster (or on the given `collectionIds`), concurrently, wrapped to the user
|
|
7
|
+
* key. Each install is `ensureFirstEpoch`: create-if-absent through the
|
|
8
|
+
* descriptor-store seam, adopting (never overwriting) a roster another
|
|
9
|
+
* provisioner already landed -- so re-running after a tear converges, and
|
|
10
|
+
* exactly one epoch[0] ever exists per collection. Run it after
|
|
11
|
+
* `provisionWalletSpace` has declared the collections; the wallet Space's
|
|
12
|
+
* provisioning is complete only once both steps have.
|
|
13
|
+
*
|
|
14
|
+
* Run both steps from the sync engine's `ensureProvisioned` seam (or, for a
|
|
15
|
+
* driver of its own, equally before the collection's first content push): the
|
|
16
|
+
* descriptor-before-first-content-push invariant rests on it. A caller that
|
|
17
|
+
* minted envelopes eagerly against a locally-minted roster and finds
|
|
18
|
+
* `installed: false` here (another provisioner's create won) has adopted the
|
|
19
|
+
* winner's descriptor and must re-mint its pending envelopes under that
|
|
20
|
+
* descriptor's current epoch before pushing -- `remintPendingEnvelopes`
|
|
21
|
+
* (`@interop/wallet-core/sync`) is that path.
|
|
22
|
+
*
|
|
23
|
+
* @param options {object}
|
|
24
|
+
* @param options.was {WasClient}
|
|
25
|
+
* @param options.spaceId {string}
|
|
26
|
+
* @param options.userKey {UserKey} the account's user key, epoch[0]'s one
|
|
27
|
+
* initial recipient
|
|
28
|
+
* @param [options.collectionIds] {string[]} the encrypted collections to
|
|
29
|
+
* cover; defaults to the wallet Space roster's encrypted collections. A
|
|
30
|
+
* caller naming its own ids (e.g. `contacts`) must name only collections
|
|
31
|
+
* declared encrypted
|
|
32
|
+
* @returns {Promise<Record<string, boolean>>} per collection id, whether
|
|
33
|
+
* this call installed its epoch[0] (`false` means an existing roster was
|
|
34
|
+
* adopted)
|
|
35
|
+
*/
|
|
36
|
+
export async function ensureWalletSpaceEpochs({ was, spaceId, userKey, collectionIds }) {
|
|
37
|
+
const ids = collectionIds ??
|
|
38
|
+
WALLET_SPACE_PROVISION_ROSTER.filter(spec => spec.encryption === 'edv').map(spec => spec.collectionId);
|
|
39
|
+
const installed = {};
|
|
40
|
+
await Promise.all(ids.map(async (collectionId) => {
|
|
41
|
+
try {
|
|
42
|
+
const result = await ensureFirstEpoch({
|
|
43
|
+
collection: was.space(spaceId).collection(collectionId),
|
|
44
|
+
recipients: [userKeyAsRecipient({ userKey })]
|
|
45
|
+
});
|
|
46
|
+
installed[collectionId] = result.installed;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
throw new Error(`Error installing the first key epoch for collection ` +
|
|
50
|
+
`"${collectionId}" in space "${spaceId}".`, { cause: err });
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
return installed;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=spaceEpochs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaceEpochs.js","sourceRoot":"","sources":["../../src/keys/spaceEpochs.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAA;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAGxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAC5C,GAAG,EACH,OAAO,EACP,OAAO,EACP,aAAa,EAMd;IACC,MAAM,GAAG,GACP,aAAa;QACb,6BAA6B,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC,GAAG,CACzE,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAC1B,CAAA;IACH,MAAM,SAAS,GAA4B,EAAE,CAAA;IAC7C,MAAM,OAAO,CAAC,GAAG,CACf,GAAG,CAAC,GAAG,CAAC,KAAK,EAAC,YAAY,EAAC,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC;gBACpC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC;gBACvD,UAAU,EAAE,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;aAC9C,CAAC,CAAA;YACF,SAAS,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,SAAS,CAAA;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,sDAAsD;gBACpD,IAAI,YAAY,eAAe,OAAO,IAAI,EAC5C,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAA;QACH,CAAC;IACH,CAAC,CAAC,CACH,CAAA;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
|
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
* unwrapUserKeyGenerations}) -- needed both to recognize a stale epoch and to
|
|
20
20
|
* escrow the fresh user key into a stranded collection's history.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* The cascade is rotation-only: every encrypted collection's descriptor
|
|
23
|
+
* carries an epoch roster from provisioning (`ensureWalletSpaceEpochs`), so a
|
|
24
|
+
* descriptor met without epochs can only mean a tampering or pre-provisioning
|
|
25
|
+
* host and is refused fail-closed. No construction anywhere installs a
|
|
26
|
+
* user-key secret as a collection epoch secret, which is what keeps a
|
|
27
|
+
* collection-epoch escrow (an App Connect grant, a share) from ever handing a
|
|
28
|
+
* grantee the user key itself.
|
|
28
29
|
*/
|
|
29
30
|
import type { IKeyAgreementKey } from '@interop/data-integrity-core';
|
|
30
31
|
import type { CollectionEncryption } from '@interop/was-client';
|
|
@@ -64,21 +65,15 @@ export declare function unwrapUserKeyGenerations({ descriptor, clientKeyAgreemen
|
|
|
64
65
|
}): Promise<UserKey[]>;
|
|
65
66
|
/**
|
|
66
67
|
* What one collection's cascade step did: `noop` (already on the current user
|
|
67
|
-
* key), `
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* `rotated` (a fresh epoch sealed to the current user key, the stale
|
|
71
|
-
* generations retired -- the pre-epoch install falls through to this).
|
|
68
|
+
* key), `escrowed` (the current user key's wrap was completed into history
|
|
69
|
+
* with no stale epoch to rotate), or `rotated` (a fresh epoch sealed to the
|
|
70
|
+
* current user key, the stale generations retired).
|
|
72
71
|
*/
|
|
73
|
-
export type CollectionUserKeyRotationOutcome = 'noop' | '
|
|
72
|
+
export type CollectionUserKeyRotationOutcome = 'noop' | 'escrowed' | 'rotated';
|
|
74
73
|
/**
|
|
75
74
|
* Brings ONE encrypted collection's epoch roster onto the current user key --
|
|
76
75
|
* the per-collection op of the revocation cascade and of the completion sweep:
|
|
77
76
|
*
|
|
78
|
-
* - **No epochs yet**: install the newest PRIOR generation as the first epoch
|
|
79
|
-
* (see the module doc), wrapped to that generation and the current user key,
|
|
80
|
-
* then fall through to the rotation. A first-generation account (nothing
|
|
81
|
-
* prior) installs the current user key alone and is done.
|
|
82
77
|
* - **Stale current epoch** (names a non-current generation): one
|
|
83
78
|
* `replaceRecipient` write -- the current user key escrowed into every epoch,
|
|
84
79
|
* a fresh epoch minted without the stale generations. Two requests per
|
|
@@ -86,6 +81,9 @@ export type CollectionUserKeyRotationOutcome = 'noop' | 'installed' | 'escrowed'
|
|
|
86
81
|
* default did:key resolver re-wraps them).
|
|
87
82
|
* - **Current already** and fully escrowed: no write at all, so a naive
|
|
88
83
|
* re-run after a mid-cascade crash converges with zero redundant epochs.
|
|
84
|
+
* - **No epochs**: refused fail-closed (see the module doc) -- provisioning
|
|
85
|
+
* installs every encrypted collection's epoch[0], so the cascade never
|
|
86
|
+
* mints a first epoch.
|
|
89
87
|
*
|
|
90
88
|
* The pull axis is deliberately a no-op here: a user key rotation follows a
|
|
91
89
|
* document edit (client revocation, code retirement) that already killed the
|