@interop/wallet-core 0.4.1 → 0.6.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/dist/enrollment/enrollment.d.ts +144 -0
- package/dist/enrollment/enrollment.d.ts.map +1 -0
- package/dist/enrollment/enrollment.js +296 -0
- package/dist/enrollment/enrollment.js.map +1 -0
- package/dist/enrollment/index.d.ts +20 -0
- package/dist/enrollment/index.d.ts.map +1 -0
- package/dist/enrollment/index.js +19 -0
- package/dist/enrollment/index.js.map +1 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/keyring/fetch.d.ts +22 -0
- package/dist/keyring/fetch.d.ts.map +1 -0
- package/dist/keyring/fetch.js +35 -0
- package/dist/keyring/fetch.js.map +1 -0
- package/dist/keyring/index.d.ts +30 -0
- package/dist/keyring/index.d.ts.map +1 -0
- package/dist/keyring/index.js +28 -0
- package/dist/keyring/index.js.map +1 -0
- package/dist/keyring/kdf.d.ts +102 -0
- package/dist/keyring/kdf.d.ts.map +1 -0
- package/dist/keyring/kdf.js +148 -0
- package/dist/keyring/kdf.js.map +1 -0
- package/dist/keyring/record.d.ts +98 -0
- package/dist/keyring/record.d.ts.map +1 -0
- package/dist/keyring/record.js +119 -0
- package/dist/keyring/record.js.map +1 -0
- package/dist/keyring/unlockSpace.d.ts +101 -0
- package/dist/keyring/unlockSpace.d.ts.map +1 -0
- package/dist/keyring/unlockSpace.js +226 -0
- package/dist/keyring/unlockSpace.js.map +1 -0
- package/dist/keys/index.d.ts +24 -0
- package/dist/keys/index.d.ts.map +1 -0
- package/dist/keys/index.js +22 -0
- package/dist/keys/index.js.map +1 -0
- package/dist/keys/puk.d.ts +42 -0
- package/dist/keys/puk.d.ts.map +1 -0
- package/dist/keys/puk.js +56 -0
- package/dist/keys/puk.js.map +1 -0
- package/dist/keys/pukRoster.d.ts +210 -0
- package/dist/keys/pukRoster.d.ts.map +1 -0
- package/dist/keys/pukRoster.js +263 -0
- package/dist/keys/pukRoster.js.map +1 -0
- package/dist/keys/rosterStore.d.ts +18 -0
- package/dist/keys/rosterStore.d.ts.map +1 -0
- package/dist/keys/rosterStore.js +38 -0
- package/dist/keys/rosterStore.js.map +1 -0
- package/dist/space/collections.d.ts +57 -0
- package/dist/space/collections.d.ts.map +1 -1
- package/dist/space/collections.js +48 -0
- package/dist/space/collections.js.map +1 -1
- package/dist/space/index.d.ts +4 -0
- package/dist/space/index.d.ts.map +1 -1
- package/dist/space/index.js +4 -0
- package/dist/space/index.js.map +1 -1
- package/dist/webvh/didWeb.d.ts +41 -0
- package/dist/webvh/didWeb.d.ts.map +1 -0
- package/dist/webvh/didWeb.js +24 -0
- package/dist/webvh/didWeb.js.map +1 -0
- package/dist/webvh/didWebvh.d.ts +251 -0
- package/dist/webvh/didWebvh.d.ts.map +1 -0
- package/dist/webvh/didWebvh.js +775 -0
- package/dist/webvh/didWebvh.js.map +1 -0
- package/dist/webvh/index.d.ts +27 -0
- package/dist/webvh/index.d.ts.map +1 -0
- package/dist/webvh/index.js +24 -0
- package/dist/webvh/index.js.map +1 -0
- package/dist/webvh/zcap.d.ts +104 -0
- package/dist/webvh/zcap.d.ts.map +1 -0
- package/dist/webvh/zcap.js +117 -0
- package/dist/webvh/zcap.js.map +1 -0
- package/package.json +30 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unlockSpace.d.ts","sourceRoot":"","sources":["../../src/keyring/unlockSpace.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,uBAAuB,CAAA;AAoIrD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,UAAU,EACV,IAAwB,EACzB,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,gBAAgB,EAChB,UAAU,EACV,OAAO,EACR,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAQ1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,MAAM,EACP,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,gBAAgB,EAChB,UAAU,EACV,OAAO,EACR,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,+BAA+B,CAAC,EACpD,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,UAAU,EACX,EAAE;IACD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,KAAK,CAAA;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAchB"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The unlock Space: a minimal second Space, controlled by an unlock identity
|
|
6
|
+
* and separate from the wallet data Space, holding the one keyring record.
|
|
7
|
+
* These are standalone functions rather than methods on a wallet's remote-store
|
|
8
|
+
* class (that store is bound to the data identity): each builds its own
|
|
9
|
+
* `WasClient` over the unlock agent's `zcapClient`, whose invocation signer is
|
|
10
|
+
* the unlock root key (root invocation, no capability attached -- the same
|
|
11
|
+
* posture the data Space uses).
|
|
12
|
+
*
|
|
13
|
+
* The one resource is a plaintext JSON document (its keyring payload is
|
|
14
|
+
* already ciphertext), so no encryption provider is wired in -- and the
|
|
15
|
+
* read/write handles pass the explicit `{ encryption: 'plaintext' }` override.
|
|
16
|
+
* The override is load-bearing: without it, the client decides plaintext vs
|
|
17
|
+
* encrypted by reading the collection description, and when the unlock Space
|
|
18
|
+
* does not exist yet (every keyring lookup for a fresh unlock secret) that read
|
|
19
|
+
* 404s and the client refuses to guess, throwing an EncryptionError instead of
|
|
20
|
+
* surfacing the miss as a 404-shaped `null`.
|
|
21
|
+
*/
|
|
22
|
+
import { WasClient } from '@interop/was-client';
|
|
23
|
+
import { errorStatus } from '@interop/was-client/sync';
|
|
24
|
+
import { KEYRING_COLLECTION, KEYRING_RESOURCE } from '../space/collections.js';
|
|
25
|
+
/**
|
|
26
|
+
* The default Space Description name an unlock Space is configured with.
|
|
27
|
+
* Wire-visible (it is the Space's stored name), so it stays stable across
|
|
28
|
+
* apps unless a caller deliberately overrides it.
|
|
29
|
+
*/
|
|
30
|
+
export const UNLOCK_SPACE_NAME = 'Freewallet Keyring';
|
|
31
|
+
/**
|
|
32
|
+
* The bare WAS client for an unlock Space (see the module doc for why it wires
|
|
33
|
+
* in no encryption provider).
|
|
34
|
+
*
|
|
35
|
+
* @param options {object}
|
|
36
|
+
* @param options.storageServerUrl {string}
|
|
37
|
+
* @param options.zcapClient {ZcapClient} built on the unlock agent's signer
|
|
38
|
+
* @returns {WasClient}
|
|
39
|
+
*/
|
|
40
|
+
function unlockSpaceClient({ storageServerUrl, zcapClient }) {
|
|
41
|
+
return new WasClient({
|
|
42
|
+
serverUrl: storageServerUrl,
|
|
43
|
+
zcapClient
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Ensures a plaintext collection exists in a Space (upsert -- idempotent),
|
|
48
|
+
* running with the invoking client's root capability so `force` lets the upsert
|
|
49
|
+
* treat a 404 from the pre-merge describe as genuinely absent rather than
|
|
50
|
+
* unreadable.
|
|
51
|
+
*
|
|
52
|
+
* @param options {object}
|
|
53
|
+
* @param options.storageServerUrl {string}
|
|
54
|
+
* @param options.zcapClient {ZcapClient}
|
|
55
|
+
* @param options.spaceId {string}
|
|
56
|
+
* @param options.collectionId {string}
|
|
57
|
+
* @param options.name {string}
|
|
58
|
+
* @returns {Promise<void>}
|
|
59
|
+
*/
|
|
60
|
+
async function ensurePlaintextCollection({ storageServerUrl, zcapClient, spaceId, collectionId, name }) {
|
|
61
|
+
const was = unlockSpaceClient({ storageServerUrl, zcapClient });
|
|
62
|
+
await was
|
|
63
|
+
.space(spaceId)
|
|
64
|
+
.collection(collectionId)
|
|
65
|
+
.configure({ name, force: true });
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads a single plaintext JSON record from a Space collection, or `null` when
|
|
69
|
+
* it does not exist yet (a missing Space, collection, or resource all surface
|
|
70
|
+
* as a 404-shaped `null` from `resource.get()`). A network / unreachable error
|
|
71
|
+
* propagates, so callers can distinguish "no record" from "could not check".
|
|
72
|
+
* The explicit `plaintext` override is load-bearing (see the module doc).
|
|
73
|
+
*
|
|
74
|
+
* @param options {object}
|
|
75
|
+
* @param options.storageServerUrl {string}
|
|
76
|
+
* @param options.zcapClient {ZcapClient}
|
|
77
|
+
* @param options.spaceId {string}
|
|
78
|
+
* @param options.collectionId {string}
|
|
79
|
+
* @param options.resourceId {string}
|
|
80
|
+
* @returns {Promise<unknown | null>}
|
|
81
|
+
*/
|
|
82
|
+
async function getPlaintextRecord({ storageServerUrl, zcapClient, spaceId, collectionId, resourceId }) {
|
|
83
|
+
const was = unlockSpaceClient({ storageServerUrl, zcapClient });
|
|
84
|
+
const result = await was
|
|
85
|
+
.space(spaceId)
|
|
86
|
+
.collection(collectionId, { encryption: 'plaintext' })
|
|
87
|
+
.resource(resourceId)
|
|
88
|
+
.get();
|
|
89
|
+
return result === null ? null : result;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Writes (upserts) a single plaintext JSON record into a Space collection.
|
|
93
|
+
* Serialized to bytes with an explicit `application/json` content-type.
|
|
94
|
+
*
|
|
95
|
+
* @param options {object}
|
|
96
|
+
* @param options.storageServerUrl {string}
|
|
97
|
+
* @param options.zcapClient {ZcapClient}
|
|
98
|
+
* @param options.spaceId {string}
|
|
99
|
+
* @param options.collectionId {string}
|
|
100
|
+
* @param options.resourceId {string}
|
|
101
|
+
* @param options.record {object}
|
|
102
|
+
* @returns {Promise<void>}
|
|
103
|
+
*/
|
|
104
|
+
async function putPlaintextRecord({ storageServerUrl, zcapClient, spaceId, collectionId, resourceId, record }) {
|
|
105
|
+
const was = unlockSpaceClient({ storageServerUrl, zcapClient });
|
|
106
|
+
const body = new TextEncoder().encode(JSON.stringify(record));
|
|
107
|
+
await was
|
|
108
|
+
.space(spaceId)
|
|
109
|
+
.collection(collectionId, { encryption: 'plaintext' })
|
|
110
|
+
.resource(resourceId)
|
|
111
|
+
.put(body, { contentType: 'application/json' });
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Ensures the unlock Space and its single `keyring` collection exist
|
|
115
|
+
* (upsert -- idempotent). Runs with the unlock root capability, so `force`
|
|
116
|
+
* lets the collection upsert treat a 404 from the pre-merge describe as
|
|
117
|
+
* genuinely absent rather than unreadable.
|
|
118
|
+
*
|
|
119
|
+
* @param options {object}
|
|
120
|
+
* @param options.storageServerUrl {string}
|
|
121
|
+
* @param options.zcapClient {ZcapClient}
|
|
122
|
+
* @param options.spaceId {string} the unlock Space id
|
|
123
|
+
* @param options.controller {string} the unlock did:key
|
|
124
|
+
* @param [options.name] {string} the Space Description name
|
|
125
|
+
* @returns {Promise<void>}
|
|
126
|
+
*/
|
|
127
|
+
export async function ensureUnlockSpace({ storageServerUrl, zcapClient, spaceId, controller, name = UNLOCK_SPACE_NAME }) {
|
|
128
|
+
const was = unlockSpaceClient({ storageServerUrl, zcapClient });
|
|
129
|
+
await was.space(spaceId).configure({ name, controller });
|
|
130
|
+
await ensurePlaintextCollection({
|
|
131
|
+
storageServerUrl,
|
|
132
|
+
zcapClient,
|
|
133
|
+
spaceId,
|
|
134
|
+
collectionId: KEYRING_COLLECTION.id,
|
|
135
|
+
name: KEYRING_COLLECTION.name
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Reads the keyring record from the unlock Space, or returns `null` when it
|
|
140
|
+
* does not exist yet. A network / unreachable error propagates, so callers can
|
|
141
|
+
* distinguish "no keyring" from "could not check".
|
|
142
|
+
*
|
|
143
|
+
* @param options {object}
|
|
144
|
+
* @param options.storageServerUrl {string}
|
|
145
|
+
* @param options.zcapClient {ZcapClient}
|
|
146
|
+
* @param options.spaceId {string} the unlock Space id
|
|
147
|
+
* @returns {Promise<unknown | null>}
|
|
148
|
+
*/
|
|
149
|
+
export async function getUnlockKeyring({ storageServerUrl, zcapClient, spaceId }) {
|
|
150
|
+
return getPlaintextRecord({
|
|
151
|
+
storageServerUrl,
|
|
152
|
+
zcapClient,
|
|
153
|
+
spaceId,
|
|
154
|
+
collectionId: KEYRING_COLLECTION.id,
|
|
155
|
+
resourceId: KEYRING_RESOURCE
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Writes (upserts) the keyring record into the unlock Space as a JSON document.
|
|
160
|
+
*
|
|
161
|
+
* @param options {object}
|
|
162
|
+
* @param options.storageServerUrl {string}
|
|
163
|
+
* @param options.zcapClient {ZcapClient}
|
|
164
|
+
* @param options.spaceId {string} the unlock Space id
|
|
165
|
+
* @param options.record {object} the keyring record
|
|
166
|
+
* @returns {Promise<void>}
|
|
167
|
+
*/
|
|
168
|
+
export async function putUnlockKeyring({ storageServerUrl, zcapClient, spaceId, record }) {
|
|
169
|
+
await putPlaintextRecord({
|
|
170
|
+
storageServerUrl,
|
|
171
|
+
zcapClient,
|
|
172
|
+
spaceId,
|
|
173
|
+
collectionId: KEYRING_COLLECTION.id,
|
|
174
|
+
resourceId: KEYRING_RESOURCE,
|
|
175
|
+
record
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Deletes the whole unlock Space (what retires an old passphrase on a
|
|
180
|
+
* passphrase change). `space.delete()` is idempotent, so an already-absent
|
|
181
|
+
* Space is a success.
|
|
182
|
+
*
|
|
183
|
+
* @param options {object}
|
|
184
|
+
* @param options.storageServerUrl {string}
|
|
185
|
+
* @param options.zcapClient {ZcapClient}
|
|
186
|
+
* @param options.spaceId {string} the unlock Space id
|
|
187
|
+
* @returns {Promise<void>}
|
|
188
|
+
*/
|
|
189
|
+
export async function deleteUnlockSpace({ storageServerUrl, zcapClient, spaceId }) {
|
|
190
|
+
const was = unlockSpaceClient({ storageServerUrl, zcapClient });
|
|
191
|
+
await was.space(spaceId).delete();
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Deletes an unlock Space with an explicitly attached management capability,
|
|
195
|
+
* rather than by root invocation. The `zcapClient` here is the DATA identity's
|
|
196
|
+
* (not the unlock identity's); the attached `capability` -- the management zcap
|
|
197
|
+
* the unlock identity delegated to the data identity at bind time -- is what
|
|
198
|
+
* authorizes the DELETE against the unlock Space. This is the tap-free
|
|
199
|
+
* revocation path for a lost unlock method: the data identity can retire it
|
|
200
|
+
* without re-deriving the unlock identity from the (possibly lost) secret. A
|
|
201
|
+
* 404 is treated as success (idempotent -- the Space is already gone).
|
|
202
|
+
*
|
|
203
|
+
* @param options {object}
|
|
204
|
+
* @param options.storageServerUrl {string}
|
|
205
|
+
* @param options.zcapClient {ZcapClient} the data identity's client
|
|
206
|
+
* @param options.spaceId {string} the unlock Space id
|
|
207
|
+
* @param options.capability {IZcap} the delegated management zcap
|
|
208
|
+
* @returns {Promise<void>}
|
|
209
|
+
*/
|
|
210
|
+
export async function deleteUnlockSpaceWithCapability({ storageServerUrl, zcapClient, spaceId, capability }) {
|
|
211
|
+
const was = unlockSpaceClient({ storageServerUrl, zcapClient });
|
|
212
|
+
try {
|
|
213
|
+
await was.request({
|
|
214
|
+
capability,
|
|
215
|
+
path: `/space/${spaceId}`,
|
|
216
|
+
method: 'DELETE'
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
if (errorStatus(err) === 404) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
throw err;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=unlockSpace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unlockSpace.js","sourceRoot":"","sources":["../../src/keyring/unlockSpace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The `@interop/wallet-core/keys` subpath: the per-user key (PUK) and its
|
|
6
|
+
* wrap-set roster -- recipient zero of every encrypted collection, and the one
|
|
7
|
+
* channel that delivers it to each enrolled wallet client.
|
|
8
|
+
*
|
|
9
|
+
* - `mintPuk` / `pukVaultKeys` -- minting the account's PUK and rebuilding the
|
|
10
|
+
* vault key-agreement key + resolver from stored material.
|
|
11
|
+
* - `ensurePukRoster` / `addPukRosterRecipient` / `readPukRoster` /
|
|
12
|
+
* `pukRosterRecipientResolver` -- the `key-map/puk.json` roster over the
|
|
13
|
+
* was-client marker-store seam, with the three client-side guards a
|
|
14
|
+
* resource-hosted marker needs (`epochsMac`, the latest-seen epoch pin, and
|
|
15
|
+
* a recipient resolver backed by the locally verified did:webvh document).
|
|
16
|
+
* - `pukRosterMarkerStore` -- that marker store, built from a bare signing
|
|
17
|
+
* client for the login-time direct read.
|
|
18
|
+
*/
|
|
19
|
+
export { mintPuk, pukVaultKeys } from './puk.js';
|
|
20
|
+
export type { Puk } from './puk.js';
|
|
21
|
+
export { addPukRosterRecipient, ensurePukRoster, PukRosterContinuityError, PukRosterIntegrityError, PukRosterUnwrapError, pukRosterRecipientResolver, readPukRoster } from './pukRoster.js';
|
|
22
|
+
export type { PukRosterReadResult, RosterRecipientDocument } from './pukRoster.js';
|
|
23
|
+
export { pukRosterMarkerStore } from './rosterStore.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAChD,YAAY,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,aAAa,EACd,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The `@interop/wallet-core/keys` subpath: the per-user key (PUK) and its
|
|
6
|
+
* wrap-set roster -- recipient zero of every encrypted collection, and the one
|
|
7
|
+
* channel that delivers it to each enrolled wallet client.
|
|
8
|
+
*
|
|
9
|
+
* - `mintPuk` / `pukVaultKeys` -- minting the account's PUK and rebuilding the
|
|
10
|
+
* vault key-agreement key + resolver from stored material.
|
|
11
|
+
* - `ensurePukRoster` / `addPukRosterRecipient` / `readPukRoster` /
|
|
12
|
+
* `pukRosterRecipientResolver` -- the `key-map/puk.json` roster over the
|
|
13
|
+
* was-client marker-store seam, with the three client-side guards a
|
|
14
|
+
* resource-hosted marker needs (`epochsMac`, the latest-seen epoch pin, and
|
|
15
|
+
* a recipient resolver backed by the locally verified did:webvh document).
|
|
16
|
+
* - `pukRosterMarkerStore` -- that marker store, built from a bare signing
|
|
17
|
+
* client for the login-time direct read.
|
|
18
|
+
*/
|
|
19
|
+
export { mintPuk, pukVaultKeys } from './puk.js';
|
|
20
|
+
export { addPukRosterRecipient, ensurePukRoster, PukRosterContinuityError, PukRosterIntegrityError, PukRosterUnwrapError, pukRosterRecipientResolver, readPukRoster } from './pukRoster.js';
|
|
21
|
+
export { pukRosterMarkerStore } from './rosterStore.js';
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/keys/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAGhD,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EACpB,0BAA0B,EAC1B,aAAa,EACd,MAAM,gBAAgB,CAAA;AAMvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
|
|
2
|
+
/**
|
|
3
|
+
* The per-user key material: the X25519 key-agreement half as minted by the
|
|
4
|
+
* epoch construction (`id` is the key's own did:key; `secret` its raw 32-byte
|
|
5
|
+
* private key), plus the 32-byte seed of the PUK's Ed25519 signing pair.
|
|
6
|
+
* Random per account; held in memory for the life of a session and persisted
|
|
7
|
+
* only inside a wrapped client-key record. The signing seed is absent on a PUK
|
|
8
|
+
* adopted from a roster rotation -- the roster wraps the key-agreement secret
|
|
9
|
+
* alone, and the signing half has no consumer yet.
|
|
10
|
+
*/
|
|
11
|
+
export interface Puk {
|
|
12
|
+
id: string;
|
|
13
|
+
secret: Uint8Array;
|
|
14
|
+
signingSeed?: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Mints a fresh PUK: the X25519 key-agreement pair via the was-client epoch
|
|
18
|
+
* construction (its did:key is the key id, its raw secret is what wraps), plus
|
|
19
|
+
* a random 32-byte Ed25519 signing seed -- a minted PUK is always a complete
|
|
20
|
+
* identity (only a rotation-adopted one lacks the signing half).
|
|
21
|
+
*
|
|
22
|
+
* @returns {Promise<Required<Puk>>}
|
|
23
|
+
*/
|
|
24
|
+
export declare function mintPuk(): Promise<Required<Puk>>;
|
|
25
|
+
/**
|
|
26
|
+
* Reconstructs the PUK's key-agreement key and its single-key resolver from
|
|
27
|
+
* the stored material -- the vault-key pair a session supplies to the storage
|
|
28
|
+
* layer, making the PUK recipient zero of every encrypted collection. The key
|
|
29
|
+
* id is the self-describing `<did:key>#<fingerprint>` form, so grantee-side
|
|
30
|
+
* did:key recipient resolution routes it like any other roster entry.
|
|
31
|
+
*
|
|
32
|
+
* @param options {object}
|
|
33
|
+
* @param options.puk {Puk}
|
|
34
|
+
* @returns {{ keyAgreementKey: IKeyAgreementKey, keyResolver: IKeyResolver }}
|
|
35
|
+
*/
|
|
36
|
+
export declare function pukVaultKeys({ puk }: {
|
|
37
|
+
puk: Puk;
|
|
38
|
+
}): {
|
|
39
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
40
|
+
keyResolver: IKeyResolver;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=puk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"puk.d.ts","sourceRoot":"","sources":["../../src/keys/puk.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AAIrC;;;;;;;;GAQG;AACH,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,WAAW,CAAC,EAAE,UAAU,CAAA;CACzB;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAKtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG;IACnD,eAAe,EAAE,gBAAgB,CAAA;IACjC,WAAW,EAAE,YAAY,CAAA;CAC1B,CAQA"}
|
package/dist/keys/puk.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The per-user key (PUK): the user's single roster identity for encrypted
|
|
6
|
+
* collections, standing in as "recipient zero" of every key-epoch roster. It is
|
|
7
|
+
* minted at wallet provisioning -- random, client-side, never server-held, and
|
|
8
|
+
* never derivable from any passphrase or seed -- and delivered to each enrolled
|
|
9
|
+
* client through the wrap-set roster, which each client caches in its own local
|
|
10
|
+
* state under the unlock layer.
|
|
11
|
+
*
|
|
12
|
+
* The key-agreement half is exactly what `@interop/was-client`'s epoch
|
|
13
|
+
* construction mints: a fresh X25519 pair whose did:key is the key's id and
|
|
14
|
+
* whose raw 32-byte secret is what gets wrapped to recipients -- so the roster
|
|
15
|
+
* machinery consumes the PUK unchanged. The Ed25519 signing half is a second
|
|
16
|
+
* independent 32-byte seed, minted now so the PUK is a complete identity, with
|
|
17
|
+
* no consumer yet (the pair derives from the seed on demand once one lands).
|
|
18
|
+
*/
|
|
19
|
+
import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
|
|
20
|
+
import { epochKeyIdFor, mintEpoch } from '@interop/was-client/edv';
|
|
21
|
+
import { singleKeyResolver } from '../identity/keyResolver.js';
|
|
22
|
+
/**
|
|
23
|
+
* Mints a fresh PUK: the X25519 key-agreement pair via the was-client epoch
|
|
24
|
+
* construction (its did:key is the key id, its raw secret is what wraps), plus
|
|
25
|
+
* a random 32-byte Ed25519 signing seed -- a minted PUK is always a complete
|
|
26
|
+
* identity (only a rotation-adopted one lacks the signing half).
|
|
27
|
+
*
|
|
28
|
+
* @returns {Promise<Required<Puk>>}
|
|
29
|
+
*/
|
|
30
|
+
export async function mintPuk() {
|
|
31
|
+
const { epochId, secret } = await mintEpoch();
|
|
32
|
+
const signingSeed = new Uint8Array(32);
|
|
33
|
+
crypto.getRandomValues(signingSeed);
|
|
34
|
+
return { id: epochId, secret, signingSeed };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reconstructs the PUK's key-agreement key and its single-key resolver from
|
|
38
|
+
* the stored material -- the vault-key pair a session supplies to the storage
|
|
39
|
+
* layer, making the PUK recipient zero of every encrypted collection. The key
|
|
40
|
+
* id is the self-describing `<did:key>#<fingerprint>` form, so grantee-side
|
|
41
|
+
* did:key recipient resolution routes it like any other roster entry.
|
|
42
|
+
*
|
|
43
|
+
* @param options {object}
|
|
44
|
+
* @param options.puk {Puk}
|
|
45
|
+
* @returns {{ keyAgreementKey: IKeyAgreementKey, keyResolver: IKeyResolver }}
|
|
46
|
+
*/
|
|
47
|
+
export function pukVaultKeys({ puk }) {
|
|
48
|
+
const keyAgreementKey = X25519KeyAgreementKey2020.fromRawSecret({
|
|
49
|
+
secret: puk.secret,
|
|
50
|
+
controller: puk.id,
|
|
51
|
+
id: epochKeyIdFor(puk.id)
|
|
52
|
+
});
|
|
53
|
+
const keyResolver = singleKeyResolver({ keyAgreementKey });
|
|
54
|
+
return { keyAgreementKey, keyResolver };
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=puk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"puk.js","sourceRoot":"","sources":["../../src/keys/puk.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAA;AAK7E,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAiB9D;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,EAAE,CAAA;IAC7C,MAAM,WAAW,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IACtC,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,CAAA;IACnC,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;AAC7C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,GAAG,EAAgB;IAIhD,MAAM,eAAe,GAAG,yBAAyB,CAAC,aAAa,CAAC;QAC9D,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,UAAU,EAAE,GAAG,CAAC,EAAE;QAClB,EAAE,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;KAC1B,CAAqB,CAAA;IACtB,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAAE,eAAe,EAAE,CAAC,CAAA;IAC1D,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,CAAA;AACzC,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The PUK wrap set: the `key-map/puk.json` roster resource. Its body is a
|
|
6
|
+
* `CollectionEncryption` marker verbatim, whose current epoch IS the current
|
|
7
|
+
* per-user key -- the epoch id is the PUK's did:key and the wrapped secret is
|
|
8
|
+
* the PUK's raw 32-byte key, wrapped to each enrolled client's key-agreement
|
|
9
|
+
* key. The roster is the delivery channel for PUK rotation: each client keeps
|
|
10
|
+
* the PUK in its own local state under the unlock layer, and the roster's
|
|
11
|
+
* epoch stamp marks a cached copy stale.
|
|
12
|
+
*
|
|
13
|
+
* Everything mutates through was-client's marker-store seam (the
|
|
14
|
+
* plain-resource adapter): read-with-etag, compare-and-swap writes, and a
|
|
15
|
+
* guarded create for the initially-absent roster. No marker logic is
|
|
16
|
+
* reimplemented here.
|
|
17
|
+
*
|
|
18
|
+
* A resource-hosted marker gets NONE of the server-side epoch invariants a
|
|
19
|
+
* Collection Description enforces (append-only epochs, monotone
|
|
20
|
+
* `currentEpoch`), so three client-side compensations are load-bearing alone
|
|
21
|
+
* against a tampering host:
|
|
22
|
+
*
|
|
23
|
+
* - **`epochsMac`** -- the epoch configuration is authenticated under the
|
|
24
|
+
* current epoch's secret, which the server never holds; a fabricated
|
|
25
|
+
* configuration fails the MAC (`PukRosterIntegrityError`).
|
|
26
|
+
* - **The epoch pin** -- the latest-seen roster epoch is pinned locally by the
|
|
27
|
+
* consuming app (beside the account-pointer pin); a served
|
|
28
|
+
* roster that rolls back behind the pin is refused
|
|
29
|
+
* (`PukRosterContinuityError`) rather than followed. Stale-roster replay
|
|
30
|
+
* thereby lands in the same accepted continuity class as a substituted
|
|
31
|
+
* account pointer.
|
|
32
|
+
* - **The roster delivers, never sources** -- the recipient-key source of
|
|
33
|
+
* record is the locally verified did:webvh document (one `keyAgreement`
|
|
34
|
+
* verification method per enrolled client). When an epoch rotates, each
|
|
35
|
+
* remaining recipient's key is resolved from that document
|
|
36
|
+
* (`pukRosterRecipientResolver`); a roster entry with no matching document
|
|
37
|
+
* verification method is dropped and never receives a wrap, so a
|
|
38
|
+
* server-injected entry sits ignored. Wraps are minted only by enrolled
|
|
39
|
+
* clients, against log-verified keys.
|
|
40
|
+
*/
|
|
41
|
+
import type { IKeyAgreementKey } from '@interop/data-integrity-core';
|
|
42
|
+
import type { CollectionEncryption } from '@interop/was-client';
|
|
43
|
+
import { type MarkerStore, type RecipientPublicKey } from '@interop/was-client/edv';
|
|
44
|
+
import type { Puk } from './puk.js';
|
|
45
|
+
/**
|
|
46
|
+
* Thrown when a served roster fails its client-side authentication: a
|
|
47
|
+
* missing/unsupported/invalid `epochsMac`, or a marker whose `currentEpoch`
|
|
48
|
+
* names no epoch in its own list. The server (or whoever can write to it) has
|
|
49
|
+
* produced a configuration no enrolled client authenticated.
|
|
50
|
+
*/
|
|
51
|
+
export declare class PukRosterIntegrityError extends Error {
|
|
52
|
+
constructor(message: string);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Thrown when a served roster conflicts with the locally pinned latest-seen
|
|
56
|
+
* epoch -- the epochs list no longer contains the pinned epoch, or
|
|
57
|
+
* `currentEpoch` precedes it in the (append-only) list. A rollback/replay of
|
|
58
|
+
* an older consistent configuration, which a valid `epochsMac` alone cannot
|
|
59
|
+
* catch; refused rather than followed.
|
|
60
|
+
*/
|
|
61
|
+
export declare class PukRosterContinuityError extends Error {
|
|
62
|
+
pinnedEpochId: string;
|
|
63
|
+
constructor({ pinnedEpochId }: {
|
|
64
|
+
pinnedEpochId: string;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Thrown when this client holds no usable wrap in the roster's current epoch
|
|
69
|
+
* (no recipient entry for its key-agreement key, or the entry fails to
|
|
70
|
+
* unwrap). The client cannot obtain the current PUK -- it may have been
|
|
71
|
+
* rotated off the roster.
|
|
72
|
+
*/
|
|
73
|
+
export declare class PukRosterUnwrapError extends Error {
|
|
74
|
+
constructor(message: string);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The subset of a resolved DID document the recipient resolver consumes: the
|
|
78
|
+
* `keyAgreement` relation (VM references or embedded VMs) and the
|
|
79
|
+
* `verificationMethod` list references resolve against.
|
|
80
|
+
*/
|
|
81
|
+
export interface RosterRecipientDocument {
|
|
82
|
+
keyAgreement?: Array<string | {
|
|
83
|
+
id?: string;
|
|
84
|
+
publicKeyMultibase?: string;
|
|
85
|
+
}>;
|
|
86
|
+
verificationMethod?: Array<{
|
|
87
|
+
id?: string;
|
|
88
|
+
publicKeyMultibase?: string;
|
|
89
|
+
}>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Builds the recipient resolver for roster rotations, backed by a locally
|
|
93
|
+
* verified did:webvh document -- the enforcement point for "the roster
|
|
94
|
+
* delivers, never sources". Given a remaining recipient's `kid`, it answers
|
|
95
|
+
* with that recipient's public key ONLY when the document carries a matching
|
|
96
|
+
* `keyAgreement` verification method (matched on the public-key multibase, so
|
|
97
|
+
* a did:key-form kid matches its `<did:webvh>#<multibase>` VM); otherwise it
|
|
98
|
+
* resolves `null` -- the was-client skip contract -- so the entry is dropped
|
|
99
|
+
* from the fresh epoch and never receives a wrap.
|
|
100
|
+
*
|
|
101
|
+
* @param options {object}
|
|
102
|
+
* @param options.document {RosterRecipientDocument} the locally verified
|
|
103
|
+
* did:webvh document (never a server-supplied roster field)
|
|
104
|
+
* @returns {function} a `resolveRecipientKey` for `removeRecipient`
|
|
105
|
+
*/
|
|
106
|
+
export declare function pukRosterRecipientResolver({ document }: {
|
|
107
|
+
document: RosterRecipientDocument;
|
|
108
|
+
}): (kid: string) => Promise<RecipientPublicKey | null>;
|
|
109
|
+
/**
|
|
110
|
+
* Ensures the roster exists, create-if-absent: an absent roster is
|
|
111
|
+
* initialized with the account's existing PUK installed as the first epoch,
|
|
112
|
+
* wrapped to this client's key-agreement key; an existing roster is returned
|
|
113
|
+
* as-is (authentication is the read path's job, and provisioning must never
|
|
114
|
+
* clobber an established roster). Idempotent -- losing the guarded-create
|
|
115
|
+
* race to a concurrent first init converges on the winner's roster.
|
|
116
|
+
*
|
|
117
|
+
* @param options {object}
|
|
118
|
+
* @param options.store {MarkerStore} the roster's marker store
|
|
119
|
+
* @param options.puk {Puk} the account's per-user key
|
|
120
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
121
|
+
* (identity) key-agreement key -- the roster recipient
|
|
122
|
+
* @returns {Promise<CollectionEncryption>} the roster marker
|
|
123
|
+
*/
|
|
124
|
+
export declare function ensurePukRoster({ store, puk, clientKeyAgreementKey }: {
|
|
125
|
+
store: MarkerStore;
|
|
126
|
+
puk: Puk;
|
|
127
|
+
clientKeyAgreementKey: IKeyAgreementKey;
|
|
128
|
+
}): Promise<CollectionEncryption>;
|
|
129
|
+
/**
|
|
130
|
+
* Wraps the PUK to a client being enrolled -- the roster half of the
|
|
131
|
+
* enrollment ceremony, and deliberately its FIRST write (decryption material
|
|
132
|
+
* before authorization, the push order): the wrap lands before the did:webvh
|
|
133
|
+
* log entries, so no enrolled client is ever authorized but blind, and a tear
|
|
134
|
+
* right after this write leaves only an orphan wrap -- invisible to
|
|
135
|
+
* authorization, harmless, resumed by re-running the ceremony.
|
|
136
|
+
*
|
|
137
|
+
* Escrow semantics ride on was-client's `addRecipient`: the new client
|
|
138
|
+
* receives EVERY epoch's key, current and prior, so it decrypts
|
|
139
|
+
* pre-enrollment history. The recipient key arrives over the point-to-point
|
|
140
|
+
* enrollment channel and is verified there by the enrolling client (the
|
|
141
|
+
* document VM it writes next comes from the same exchange) -- never sourced
|
|
142
|
+
* from the roster. Idempotent: a wrap already standing in the current epoch
|
|
143
|
+
* is returned as-is.
|
|
144
|
+
*
|
|
145
|
+
* @param options {object}
|
|
146
|
+
* @param options.store {MarkerStore} the roster's marker store
|
|
147
|
+
* @param options.recipient {RecipientPublicKey} the enrollee's public
|
|
148
|
+
* key-agreement key; `id` is the kid its own roster reads will look for
|
|
149
|
+
* @param options.ownerKeyAgreementKey {IKeyAgreementKey} the enrolling
|
|
150
|
+
* client's own (identity) key-agreement key, unwrapping each epoch for
|
|
151
|
+
* re-wrapping
|
|
152
|
+
* @returns {Promise<CollectionEncryption>} the refreshed roster marker
|
|
153
|
+
*/
|
|
154
|
+
export declare function addPukRosterRecipient({ store, recipient, ownerKeyAgreementKey }: {
|
|
155
|
+
store: MarkerStore;
|
|
156
|
+
recipient: RecipientPublicKey;
|
|
157
|
+
ownerKeyAgreementKey: IKeyAgreementKey;
|
|
158
|
+
}): Promise<CollectionEncryption>;
|
|
159
|
+
/**
|
|
160
|
+
* What a roster read resolves to: the authenticated marker, the current PUK
|
|
161
|
+
* (the cached one confirmed current, or a fresh one unwrapped from a rotated
|
|
162
|
+
* epoch -- `rotated` says which), and the epoch id the caller must pin as the
|
|
163
|
+
* new latest-seen.
|
|
164
|
+
*/
|
|
165
|
+
export interface PukRosterReadResult {
|
|
166
|
+
marker: CollectionEncryption;
|
|
167
|
+
puk: Puk;
|
|
168
|
+
rotated: boolean;
|
|
169
|
+
latestEpochId: string;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Reads and authenticates the roster -- the direct read at login and on epoch
|
|
173
|
+
* mismatch. Resolves `null` when the roster does not exist yet (an account
|
|
174
|
+
* provisioned before the roster, or provisioning still in flight); otherwise:
|
|
175
|
+
*
|
|
176
|
+
* 1. **Continuity**: the served epochs must contain the pinned latest-seen
|
|
177
|
+
* epoch, and `currentEpoch` must not precede it in the append-only list
|
|
178
|
+
* (`PukRosterContinuityError` -- the rollback/replay refusal).
|
|
179
|
+
* 2. **Possession**: `currentEpoch === puk.id` confirms the cached PUK
|
|
180
|
+
* current; otherwise the current epoch was rotated by another client and
|
|
181
|
+
* this client's wrap is unwrapped with its own key-agreement key
|
|
182
|
+
* (`PukRosterUnwrapError` when it holds none).
|
|
183
|
+
* 3. **Authentication**: the marker's `epochsMac` is verified under the
|
|
184
|
+
* current epoch's secret (`PukRosterIntegrityError` on any mismatch -- a
|
|
185
|
+
* fabricated configuration).
|
|
186
|
+
*
|
|
187
|
+
* A rotated read returns the fresh PUK; its Ed25519 signing seed does not
|
|
188
|
+
* travel through the roster (the roster wraps the key-agreement secret
|
|
189
|
+
* alone), so the returned PUK carries none.
|
|
190
|
+
*
|
|
191
|
+
* A caller with no cached PUK at all -- a freshly enrolled client making its
|
|
192
|
+
* first post-enrollment read -- omits `puk` and always takes the unwrap path;
|
|
193
|
+
* the result's `rotated` is then true (the PUK was adopted from the roster).
|
|
194
|
+
*
|
|
195
|
+
* @param options {object}
|
|
196
|
+
* @param options.store {MarkerStore} the roster's marker store
|
|
197
|
+
* @param [options.puk] {Puk} this client's cached PUK, when it holds one
|
|
198
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
199
|
+
* (identity) key-agreement key, unwrapping a rotated epoch
|
|
200
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
201
|
+
* roster epoch, when this client has seen the roster before
|
|
202
|
+
* @returns {Promise<PukRosterReadResult | null>}
|
|
203
|
+
*/
|
|
204
|
+
export declare function readPukRoster({ store, puk, clientKeyAgreementKey, pinnedEpochId }: {
|
|
205
|
+
store: MarkerStore;
|
|
206
|
+
puk?: Puk;
|
|
207
|
+
clientKeyAgreementKey: IKeyAgreementKey;
|
|
208
|
+
pinnedEpochId?: string | null;
|
|
209
|
+
}): Promise<PukRosterReadResult | null>;
|
|
210
|
+
//# sourceMappingURL=pukRoster.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pukRoster.d.ts","sourceRoot":"","sources":["../../src/keys/pukRoster.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAML,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnC;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,aAAa,EAAE,MAAM,CAAA;gBACT,EAAE,aAAa,EAAE,EAAE;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE;CAQzD;AAED;;;;;GAKG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC3E,kBAAkB,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACzE;AAgBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACT,EAAE;IACD,QAAQ,EAAE,uBAAuB,CAAA;CAClC,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAsCtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CAAC,EACpC,KAAK,EACL,GAAG,EACH,qBAAqB,EACtB,EAAE;IACD,KAAK,EAAE,WAAW,CAAA;IAClB,GAAG,EAAE,GAAG,CAAA;IACR,qBAAqB,EAAE,gBAAgB,CAAA;CACxC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAUhC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,KAAK,EACL,SAAS,EACT,oBAAoB,EACrB,EAAE;IACD,KAAK,EAAE,WAAW,CAAA;IAClB,SAAS,EAAE,kBAAkB,CAAA;IAC7B,oBAAoB,EAAE,gBAAgB,CAAA;CACvC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0BhC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,oBAAoB,CAAA;IAC5B,GAAG,EAAE,GAAG,CAAA;IACR,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,aAAa,CAAC,EAClC,KAAK,EACL,GAAG,EACH,qBAAqB,EACrB,aAAa,EACd,EAAE;IACD,KAAK,EAAE,WAAW,CAAA;IAClB,GAAG,CAAC,EAAE,GAAG,CAAA;IACT,qBAAqB,EAAE,gBAAgB,CAAA;IACvC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAwEtC"}
|