@interop/wallet-core 0.40.0 → 0.42.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 +17 -3
- package/dist/clients/listing.d.ts +4 -3
- package/dist/clients/listing.d.ts.map +1 -1
- package/dist/clients/listing.js.map +1 -1
- package/dist/clients/policy.d.ts +3 -2
- package/dist/clients/policy.d.ts.map +1 -1
- package/dist/clients/policy.js +3 -3
- package/dist/clients/policy.js.map +1 -1
- package/dist/clients/revocation.d.ts +14 -4
- package/dist/clients/revocation.d.ts.map +1 -1
- package/dist/clients/revocation.js +29 -14
- package/dist/clients/revocation.js.map +1 -1
- package/dist/clients/rosterPolicy.d.ts +22 -15
- package/dist/clients/rosterPolicy.d.ts.map +1 -1
- package/dist/clients/rosterPolicy.js +32 -15
- package/dist/clients/rosterPolicy.js.map +1 -1
- package/dist/descriptors/index.d.ts +1 -1
- package/dist/descriptors/index.d.ts.map +1 -1
- package/dist/descriptors/index.js +1 -1
- package/dist/descriptors/index.js.map +1 -1
- package/dist/descriptors/logSource.d.ts +58 -6
- package/dist/descriptors/logSource.d.ts.map +1 -1
- package/dist/descriptors/logSource.js +57 -15
- package/dist/descriptors/logSource.js.map +1 -1
- package/dist/enrollment/enrollment.d.ts +30 -2
- package/dist/enrollment/enrollment.d.ts.map +1 -1
- package/dist/enrollment/enrollment.js +58 -30
- package/dist/enrollment/enrollment.js.map +1 -1
- package/dist/enrollment/index.d.ts +4 -1
- package/dist/enrollment/index.d.ts.map +1 -1
- package/dist/enrollment/index.js +4 -1
- package/dist/enrollment/index.js.map +1 -1
- package/dist/keyring/fetch.d.ts +12 -0
- package/dist/keyring/fetch.d.ts.map +1 -1
- package/dist/keyring/fetch.js +0 -2
- package/dist/keyring/fetch.js.map +1 -1
- package/dist/keyring/index.d.ts +7 -6
- package/dist/keyring/index.d.ts.map +1 -1
- package/dist/keyring/index.js +7 -6
- package/dist/keyring/index.js.map +1 -1
- package/dist/keyring/kdf.d.ts +22 -6
- package/dist/keyring/kdf.d.ts.map +1 -1
- package/dist/keyring/kdf.js +9 -14
- package/dist/keyring/kdf.js.map +1 -1
- package/dist/keyring/record.d.ts +30 -10
- package/dist/keyring/record.d.ts.map +1 -1
- package/dist/keyring/record.js +31 -15
- package/dist/keyring/record.js.map +1 -1
- package/dist/keys/index.d.ts +5 -3
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +3 -2
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/rosterLogStore.d.ts +22 -2
- package/dist/keys/rosterLogStore.d.ts.map +1 -1
- package/dist/keys/rosterLogStore.js +40 -20
- package/dist/keys/rosterLogStore.js.map +1 -1
- package/dist/keys/rosterStore.d.ts +13 -1
- package/dist/keys/rosterStore.d.ts.map +1 -1
- package/dist/keys/rosterStore.js +18 -0
- package/dist/keys/rosterStore.js.map +1 -1
- package/dist/keys/userKeyCascade.d.ts +10 -3
- package/dist/keys/userKeyCascade.d.ts.map +1 -1
- package/dist/keys/userKeyCascade.js +10 -4
- package/dist/keys/userKeyCascade.js.map +1 -1
- package/dist/keys/userKeyRoster.d.ts +29 -21
- package/dist/keys/userKeyRoster.d.ts.map +1 -1
- package/dist/keys/userKeyRoster.js +47 -23
- package/dist/keys/userKeyRoster.js.map +1 -1
- package/dist/recovery/index.d.ts +21 -11
- package/dist/recovery/index.d.ts.map +1 -1
- package/dist/recovery/index.js +19 -10
- package/dist/recovery/index.js.map +1 -1
- package/dist/recovery/recoveryCode.d.ts +5 -17
- package/dist/recovery/recoveryCode.d.ts.map +1 -1
- package/dist/recovery/recoveryCode.js +4 -14
- package/dist/recovery/recoveryCode.js.map +1 -1
- package/dist/recovery/recoveryDelegation.d.ts +151 -0
- package/dist/recovery/recoveryDelegation.d.ts.map +1 -0
- package/dist/recovery/recoveryDelegation.js +261 -0
- package/dist/recovery/recoveryDelegation.js.map +1 -0
- package/dist/recovery/recoveryWebvh.d.ts +13 -22
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +53 -139
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/resourceLog/append.d.ts +12 -3
- package/dist/resourceLog/append.d.ts.map +1 -1
- package/dist/resourceLog/append.js +17 -10
- package/dist/resourceLog/append.js.map +1 -1
- package/dist/resourceLog/index.d.ts +1 -1
- package/dist/resourceLog/index.d.ts.map +1 -1
- package/dist/resourceLog/index.js +1 -1
- package/dist/resourceLog/index.js.map +1 -1
- package/dist/resourceLog/pin.d.ts +46 -8
- package/dist/resourceLog/pin.d.ts.map +1 -1
- package/dist/resourceLog/pin.js +32 -7
- package/dist/resourceLog/pin.js.map +1 -1
- package/dist/resourceLog/seal.d.ts +4 -1
- package/dist/resourceLog/seal.d.ts.map +1 -1
- package/dist/resourceLog/seal.js +6 -2
- package/dist/resourceLog/seal.js.map +1 -1
- package/dist/unlock/index.d.ts +40 -0
- package/dist/unlock/index.d.ts.map +1 -0
- package/dist/unlock/index.js +36 -0
- package/dist/unlock/index.js.map +1 -0
- package/dist/unlock/ladder.d.ts +100 -0
- package/dist/unlock/ladder.d.ts.map +1 -0
- package/dist/unlock/ladder.js +144 -0
- package/dist/unlock/ladder.js.map +1 -0
- package/dist/unlock/selfEnroll.d.ts +83 -0
- package/dist/unlock/selfEnroll.d.ts.map +1 -0
- package/dist/unlock/selfEnroll.js +126 -0
- package/dist/unlock/selfEnroll.js.map +1 -0
- package/dist/unlock/standingClient.d.ts +68 -0
- package/dist/unlock/standingClient.d.ts.map +1 -0
- package/dist/unlock/standingClient.js +81 -0
- package/dist/unlock/standingClient.js.map +1 -0
- package/dist/unlock/standingWebvh.d.ts +153 -0
- package/dist/unlock/standingWebvh.d.ts.map +1 -0
- package/dist/unlock/standingWebvh.js +370 -0
- package/dist/unlock/standingWebvh.js.map +1 -0
- package/dist/unlock/unlockRecord.d.ts +207 -0
- package/dist/unlock/unlockRecord.d.ts.map +1 -0
- package/dist/unlock/unlockRecord.js +435 -0
- package/dist/unlock/unlockRecord.js.map +1 -0
- package/dist/webvh/didWebvh.d.ts +166 -7
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +286 -31
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/index.d.ts +15 -3
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +14 -3
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/keyAgreement.d.ts +72 -0
- package/dist/webvh/keyAgreement.d.ts.map +1 -0
- package/dist/webvh/keyAgreement.js +47 -0
- package/dist/webvh/keyAgreement.js.map +1 -0
- package/dist/webvh/listClients.d.ts +69 -20
- package/dist/webvh/listClients.d.ts.map +1 -1
- package/dist/webvh/listClients.js +102 -14
- package/dist/webvh/listClients.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts +21 -11
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +31 -14
- package/dist/webvh/revokeClient.js.map +1 -1
- package/dist/webvh/verifyLog.d.ts +15 -2
- package/dist/webvh/verifyLog.d.ts.map +1 -1
- package/dist/webvh/verifyLog.js +22 -3
- package/dist/webvh/verifyLog.js.map +1 -1
- package/package.json +11 -5
- package/dist/recovery/recoveryRecord.d.ts +0 -171
- package/dist/recovery/recoveryRecord.d.ts.map +0 -1
- package/dist/recovery/recoveryRecord.js +0 -290
- package/dist/recovery/recoveryRecord.js.map +0 -1
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The did:webvh half of the standing unlock-credential lifecycle: the split
|
|
6
|
+
* posture every unlock method holds under the standing model, and the
|
|
7
|
+
* self-enrolling continuation a fresh browser runs with nothing but the
|
|
8
|
+
* credential in hand.
|
|
9
|
+
*
|
|
10
|
+
* At bind time the document gains the credential's `keyAgreement` entry --
|
|
11
|
+
* the key verbatim for a high-entropy credential (a passkey PRF output, a
|
|
12
|
+
* recovery code), or a `MultikeyCommitment` entry for a low-entropy-derived
|
|
13
|
+
* key (a passphrase), so the world-readable document carries a check on the
|
|
14
|
+
* key without carrying the key -- and `nextKeyHashes` gains the hash of the
|
|
15
|
+
* credential's current update key (a ladder rung, or a code's single derived
|
|
16
|
+
* key).
|
|
17
|
+
* Decryption standing, authority latent: the credential's update key joins
|
|
18
|
+
* `updateKeys` nowhere, and both entries are deliberately unmarked, so client
|
|
19
|
+
* listings (keyed on `capabilityInvocation`) and revocation removals never
|
|
20
|
+
* see them. {@link publishUnlockKey} / {@link removeUnlockKey} are one merged
|
|
21
|
+
* add/remove pair, shared verbatim by the recovery-code wrappers.
|
|
22
|
+
*
|
|
23
|
+
* Self-enrollment ({@link selfEnrollWebvhClient}) is the recovery
|
|
24
|
+
* continuation generalized to a non-spending credential: two entries through
|
|
25
|
+
* the record's delegated `did.jsonl` bridge --
|
|
26
|
+
*
|
|
27
|
+
* 1. **Reveal + commit**: ladder rung `i` joins `updateKeys` (its hash stands
|
|
28
|
+
* committed, which is what makes the entry verify) and `nextKeyHashes`
|
|
29
|
+
* extends with the new ordinary client's update- and staged-key hashes
|
|
30
|
+
* plus `hash(rung i + 1)` -- the credential's next standing commitment.
|
|
31
|
+
* 2. **Add + retire the rung**: signed by the new client's update key
|
|
32
|
+
* (revealed from the commit), this entry publishes the new client's
|
|
33
|
+
* verification methods and update key and drops the spent rung and its
|
|
34
|
+
* hash. The credential's own posture -- its `keyAgreement` entry and the
|
|
35
|
+
* freshly committed `hash(rung i + 1)` -- stands untouched, ready for the
|
|
36
|
+
* next self-enrollment. Nothing is spent, and no replacement exists.
|
|
37
|
+
*
|
|
38
|
+
* Which rung is current is recovered from the log itself
|
|
39
|
+
* (`attributeLadderRung`, fail-closed); a lost compare-and-swap race re-runs,
|
|
40
|
+
* re-attributes, and climbs to the winner's committed rung -- the
|
|
41
|
+
* retry-up-the-ladder resolution. Every stage is idempotent and resumable
|
|
42
|
+
* from durable state alone, on the recovery continuation's pattern.
|
|
43
|
+
*/
|
|
44
|
+
import { deriveNextKeyHash, updateDID } from '@interop/did-method-webvh';
|
|
45
|
+
import { assertCarryOverCommitments, BYOE_CONTEXT_URL, markedVerificationMethodPair, MULTIKEY_COMMITMENT_VM_TYPE, MULTIKEY_VM_TYPE, publishUpdatedLog, putLogResource, readPublishedLog, relationIds, updateKeyMultibase, updateKeySigner, withLogConflictRetry } from '../webvh/didWebvh.js';
|
|
46
|
+
import { attributeLadderRung, ladderRung } from './ladder.js';
|
|
47
|
+
/**
|
|
48
|
+
* The verification-method id a credential's key-agreement entry publishes
|
|
49
|
+
* under: `<did>#<multibase>` for a verbatim key (indistinguishable by id from
|
|
50
|
+
* any other keyAgreement entry), `<did>#<commitment>` for a commitment entry
|
|
51
|
+
* (the commitment string is deterministic, so the id is too).
|
|
52
|
+
*
|
|
53
|
+
* @param options {object}
|
|
54
|
+
* @param options.did {string} the account's did:webvh
|
|
55
|
+
* @param options.keyAgreement {UnlockKeyAgreementPublication}
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
export function unlockKeyVmId({ did, keyAgreement }) {
|
|
59
|
+
const fragment = 'publicKeyMultibase' in keyAgreement
|
|
60
|
+
? keyAgreement.publicKeyMultibase
|
|
61
|
+
: keyAgreement.commitment;
|
|
62
|
+
return `${did}#${fragment}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The credential's `keyAgreement` verification method: an ordinary unmarked
|
|
66
|
+
* entry carrying either the key verbatim (a `Multikey` with
|
|
67
|
+
* `publicKeyMultibase`) or its hash commitment (a `MultikeyCommitment` with
|
|
68
|
+
* `publicKeyCommitment` -- the document convention for a low-entropy-derived
|
|
69
|
+
* key, which withholds the key material and gives the roster resolver a
|
|
70
|
+
* document-anchored check). Controlled by the account and deliberately
|
|
71
|
+
* unmarked: a credential is not a listed client, so its entry must never
|
|
72
|
+
* carry the controller marker a client listing or a revocation removal
|
|
73
|
+
* matches on.
|
|
74
|
+
*
|
|
75
|
+
* @param options {object}
|
|
76
|
+
* @param options.did {string} the account's did:webvh
|
|
77
|
+
* @param options.keyAgreement {UnlockKeyAgreementPublication}
|
|
78
|
+
* @returns {VerificationMethod}
|
|
79
|
+
*/
|
|
80
|
+
export function unlockKeyVerificationMethod({ did, keyAgreement }) {
|
|
81
|
+
const id = unlockKeyVmId({ did, keyAgreement });
|
|
82
|
+
if ('publicKeyMultibase' in keyAgreement) {
|
|
83
|
+
return {
|
|
84
|
+
id,
|
|
85
|
+
type: MULTIKEY_VM_TYPE,
|
|
86
|
+
controller: did,
|
|
87
|
+
publicKeyMultibase: keyAgreement.publicKeyMultibase
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
id,
|
|
92
|
+
type: MULTIKEY_COMMITMENT_VM_TYPE,
|
|
93
|
+
controller: did,
|
|
94
|
+
publicKeyCommitment: keyAgreement.commitment
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Reads and resolves the published log through the narrow store seam.
|
|
99
|
+
*
|
|
100
|
+
* @param options {object}
|
|
101
|
+
* @param options.store {UnlockLogStore}
|
|
102
|
+
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
103
|
+
* to, where the caller holds one
|
|
104
|
+
* @returns {Promise<PublishedWebvhLog>}
|
|
105
|
+
*/
|
|
106
|
+
async function readLogOrThrow({ store, expectedDid }) {
|
|
107
|
+
// readPublishedLog only calls getIdResourceRaw, so the narrow seam is safe.
|
|
108
|
+
const published = await readPublishedLog({
|
|
109
|
+
idStore: store,
|
|
110
|
+
...(expectedDid !== undefined ? { expectedDid } : {})
|
|
111
|
+
});
|
|
112
|
+
if (!published) {
|
|
113
|
+
throw new Error('did:webvh: did.jsonl is missing; nothing to enroll into.');
|
|
114
|
+
}
|
|
115
|
+
return published;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Publishes `did.jsonl` through the narrow seam -- the log only, never
|
|
119
|
+
* `did.json` (the bridge delegation covers nothing else; the enrolled session
|
|
120
|
+
* republishes the projection once it is the controller). Conditional on the
|
|
121
|
+
* read the entry was built on; a lost race surfaces as a
|
|
122
|
+
* `WebvhLogConflictError` (the mapping lives in `putLogResource`).
|
|
123
|
+
*
|
|
124
|
+
* @param options {object}
|
|
125
|
+
* @param options.store {UnlockLogStore}
|
|
126
|
+
* @param options.log {DIDLog}
|
|
127
|
+
* @param [options.ifMatch] {string} publish only if `did.jsonl` is unchanged
|
|
128
|
+
* @returns {Promise<void>}
|
|
129
|
+
*/
|
|
130
|
+
async function publishLogOnly({ store, log, ifMatch }) {
|
|
131
|
+
await putLogResource({ store, log, ifMatch });
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* BIND (run by an enrolled client, root authority): publishes a standing
|
|
135
|
+
* credential's split posture into the document -- one entry adding the
|
|
136
|
+
* credential's `keyAgreement` entry (verbatim or commitment) and committing
|
|
137
|
+
* its current update key's hash in `nextKeyHashes`. The update key joins
|
|
138
|
+
* `updateKeys` nowhere. Idempotent: a posture already published is a no-op,
|
|
139
|
+
* so re-running a torn bind converges. The entry publishes conditionally on
|
|
140
|
+
* the log this call read; a race lost to a concurrent ceremony re-runs and
|
|
141
|
+
* rebases on the new head.
|
|
142
|
+
*
|
|
143
|
+
* @param options {object}
|
|
144
|
+
* @param options.idStore {WebvhIdStore}
|
|
145
|
+
* @param options.updateKeys {ClientWebvhUpdateKeys} the BINDING client's own
|
|
146
|
+
* did:webvh update-key seeds
|
|
147
|
+
* @param options.unlockKeys {StandingUnlockKeys} the credential's public
|
|
148
|
+
* posture
|
|
149
|
+
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
150
|
+
* to, from the caller's stored account pointer
|
|
151
|
+
* @param [options.verb] {string} what the caller is doing, for the
|
|
152
|
+
* pending-rotation refusal message (e.g. `'issuing a recovery code'`)
|
|
153
|
+
* @returns {Promise<{ did: string }>}
|
|
154
|
+
*/
|
|
155
|
+
export async function publishUnlockKey(options) {
|
|
156
|
+
return withLogConflictRetry(() => setUnlockKeyPostureOnce({ ...options, polarity: 'publish' }));
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* REMOVAL (run by an enrolled client, root authority): removes a standing
|
|
160
|
+
* credential's posture from the document -- its `keyAgreement` entry and its
|
|
161
|
+
* committed update-key hash -- in one entry. Idempotent. The roster-side half
|
|
162
|
+
* (rotating the user key epoch off the credential's wrap) is the caller's,
|
|
163
|
+
* and runs after this so the resolver's document no longer backs the removed
|
|
164
|
+
* entry.
|
|
165
|
+
*
|
|
166
|
+
* @param options {object} see {@link publishUnlockKey}
|
|
167
|
+
* @returns {Promise<{ did: string }>}
|
|
168
|
+
*/
|
|
169
|
+
export async function removeUnlockKey(options) {
|
|
170
|
+
return withLogConflictRetry(() => setUnlockKeyPostureOnce({ ...options, polarity: 'remove' }));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* One attempt of the merged posture edit, re-invoked by the conflict retry.
|
|
174
|
+
* The publish and remove polarities are one function because the entry they
|
|
175
|
+
* build is the same edit with the set operations inverted -- a divergence
|
|
176
|
+
* between two copies would be published into an append-only log.
|
|
177
|
+
*
|
|
178
|
+
* @param options {object} see {@link publishUnlockKey}, plus `polarity`
|
|
179
|
+
* @returns {Promise<{ did: string }>}
|
|
180
|
+
*/
|
|
181
|
+
async function setUnlockKeyPostureOnce({ idStore, updateKeys, unlockKeys, expectedDid, verb, polarity }) {
|
|
182
|
+
const published = await readLogOrThrow({
|
|
183
|
+
store: idStore,
|
|
184
|
+
...(expectedDid !== undefined ? { expectedDid } : {})
|
|
185
|
+
});
|
|
186
|
+
const { did, doc } = published;
|
|
187
|
+
const keyHash = await deriveNextKeyHash(unlockKeys.updateKeyMultibase);
|
|
188
|
+
const vmId = unlockKeyVmId({ did, keyAgreement: unlockKeys.keyAgreement });
|
|
189
|
+
const vmPresent = (doc.verificationMethod ?? []).some(method => method.id === vmId);
|
|
190
|
+
const hashCommitted = published.nextKeyHashes.includes(keyHash);
|
|
191
|
+
const settled = polarity === 'publish'
|
|
192
|
+
? vmPresent && hashCommitted
|
|
193
|
+
: !vmPresent && !hashCommitted;
|
|
194
|
+
if (settled) {
|
|
195
|
+
return { did };
|
|
196
|
+
}
|
|
197
|
+
const activeKey = await updateKeyMultibase({ seed: updateKeys.updateSeed });
|
|
198
|
+
if (!published.updateKeys.includes(activeKey)) {
|
|
199
|
+
throw new Error("did:webvh: the published log does not authorize this client's active " +
|
|
200
|
+
'update key; finalize the pending rotation before ' +
|
|
201
|
+
`${verb ?? 'changing an unlock credential'}.`);
|
|
202
|
+
}
|
|
203
|
+
await assertCarryOverCommitments({ published });
|
|
204
|
+
const existingMethods = (doc.verificationMethod ?? []);
|
|
205
|
+
const signer = await updateKeySigner({ seed: updateKeys.updateSeed });
|
|
206
|
+
const nextKeyHashes = polarity === 'publish'
|
|
207
|
+
? [...new Set([...published.nextKeyHashes, keyHash])]
|
|
208
|
+
: published.nextKeyHashes.filter(hash => hash !== keyHash);
|
|
209
|
+
const verificationMethods = polarity === 'publish'
|
|
210
|
+
? [
|
|
211
|
+
...existingMethods.filter(method => method.id !== vmId),
|
|
212
|
+
unlockKeyVerificationMethod({
|
|
213
|
+
did,
|
|
214
|
+
keyAgreement: unlockKeys.keyAgreement
|
|
215
|
+
})
|
|
216
|
+
]
|
|
217
|
+
: existingMethods.filter(method => method.id !== vmId);
|
|
218
|
+
const keyAgreement = polarity === 'publish'
|
|
219
|
+
? [...new Set([...relationIds(doc.keyAgreement), vmId])]
|
|
220
|
+
: relationIds(doc.keyAgreement).filter(id => id !== vmId);
|
|
221
|
+
const updated = await updateDID({
|
|
222
|
+
log: published.log,
|
|
223
|
+
signer,
|
|
224
|
+
alsoKnownAsWeb: true,
|
|
225
|
+
// A commitment entry's terms are defined by the byoe context, so the
|
|
226
|
+
// posture publish appends it to the carried-forward context. The append
|
|
227
|
+
// is deduplicated, so a document already carrying it is unchanged.
|
|
228
|
+
additionalContext: [BYOE_CONTEXT_URL],
|
|
229
|
+
updateKeys: published.updateKeys,
|
|
230
|
+
nextKeyHashes,
|
|
231
|
+
verificationMethods,
|
|
232
|
+
authentication: relationIds(doc.authentication),
|
|
233
|
+
assertionMethod: relationIds(doc.assertionMethod),
|
|
234
|
+
keyAgreement,
|
|
235
|
+
capabilityInvocation: relationIds(doc.capabilityInvocation),
|
|
236
|
+
capabilityDelegation: relationIds(doc.capabilityDelegation)
|
|
237
|
+
});
|
|
238
|
+
await publishUpdatedLog({ idStore, updated, ifMatch: published.etag });
|
|
239
|
+
return { did: updated.did };
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* SELF-ENROLLMENT (run by the credential-derived client through the delegated
|
|
243
|
+
* `did.jsonl` PUT): writes the standing continuation described in the module
|
|
244
|
+
* doc -- the reveal-and-commit entry signed by the attributed ladder rung,
|
|
245
|
+
* then the add entry signed by the new ordinary client's update key, which
|
|
246
|
+
* also retires the spent rung. Resumable from durable state alone: a
|
|
247
|
+
* completed continuation is detected by the new client's update key already
|
|
248
|
+
* being authorized (no-op), a torn one by the attribution finding the rung
|
|
249
|
+
* already revealed. Both entries publish conditionally on the read they were
|
|
250
|
+
* built on, and a race lost to a concurrent ceremony re-runs from the top --
|
|
251
|
+
* re-attributing, which is exactly the retry-up-the-ladder resolution.
|
|
252
|
+
*
|
|
253
|
+
* @param options {object}
|
|
254
|
+
* @param options.store {UnlockLogStore} public log read + delegated PUT
|
|
255
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed, from
|
|
256
|
+
* its unlock record
|
|
257
|
+
* @param options.newClientKeys {WebvhEnrollmentKeys} the new ordinary
|
|
258
|
+
* client's public halves
|
|
259
|
+
* @param options.newClientUpdateSeeds {ClientWebvhUpdateKeys} the new
|
|
260
|
+
* client's update-key seeds (minted by the self-enrolling flow, which
|
|
261
|
+
* therefore holds them and can sign the add entry)
|
|
262
|
+
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
263
|
+
* to, from the credential-authenticated pointer
|
|
264
|
+
* @returns {Promise<{ did: string, webDoc?: object }>} the account DID and,
|
|
265
|
+
* when the add entry ran here, the final `did.json` projection for the
|
|
266
|
+
* enrolled session to republish
|
|
267
|
+
*/
|
|
268
|
+
export async function selfEnrollWebvhClient(options) {
|
|
269
|
+
return withLogConflictRetry(() => selfEnrollWebvhClientOnce(options));
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* One attempt of {@link selfEnrollWebvhClient}, re-invoked by the conflict
|
|
273
|
+
* retry.
|
|
274
|
+
*
|
|
275
|
+
* @param options {object} see {@link selfEnrollWebvhClient}
|
|
276
|
+
* @returns {Promise<{ did: string, webDoc?: object }>}
|
|
277
|
+
*/
|
|
278
|
+
async function selfEnrollWebvhClientOnce({ store, ladderSeed, newClientKeys, newClientUpdateSeeds, expectedDid }) {
|
|
279
|
+
let published = await readLogOrThrow({
|
|
280
|
+
store,
|
|
281
|
+
...(expectedDid !== undefined ? { expectedDid } : {})
|
|
282
|
+
});
|
|
283
|
+
// Already complete (a torn earlier run finished the add entry): the new
|
|
284
|
+
// client's update key is authorized, which only the add entry writes.
|
|
285
|
+
if (published.updateKeys.includes(newClientKeys.updateKeyMultibase)) {
|
|
286
|
+
return { did: published.did };
|
|
287
|
+
}
|
|
288
|
+
// Which rung is current, recovered from the log itself. Fails closed with
|
|
289
|
+
// `LadderAttributionError` for a revoked (or never-bound) credential and
|
|
290
|
+
// for any ambiguous history.
|
|
291
|
+
const { rung, state } = await attributeLadderRung({ ladderSeed, published });
|
|
292
|
+
const nextRung = await ladderRung({ ladderSeed, index: rung.index + 1 });
|
|
293
|
+
const rungHash = await deriveNextKeyHash(rung.keyMultibase);
|
|
294
|
+
const newUpdateHash = await deriveNextKeyHash(newClientKeys.updateKeyMultibase);
|
|
295
|
+
const newStagedHash = await deriveNextKeyHash(newClientKeys.stagedUpdateKeyMultibase);
|
|
296
|
+
const nextRungHash = await deriveNextKeyHash(nextRung.keyMultibase);
|
|
297
|
+
// The reveal-and-commit entry, skipped when a torn earlier run already
|
|
298
|
+
// published it (the rung revealed AND every needed hash committed).
|
|
299
|
+
const revealed = state === 'revealed';
|
|
300
|
+
const committed = [newUpdateHash, newStagedHash, nextRungHash].every(hash => published.nextKeyHashes.includes(hash));
|
|
301
|
+
if (!revealed || !committed) {
|
|
302
|
+
await assertCarryOverCommitments({ published });
|
|
303
|
+
const signer = await updateKeySigner({ seed: rung.seed });
|
|
304
|
+
const updated = await updateDID({
|
|
305
|
+
log: published.log,
|
|
306
|
+
signer,
|
|
307
|
+
alsoKnownAsWeb: true,
|
|
308
|
+
updateKeys: [...new Set([...published.updateKeys, rung.keyMultibase])],
|
|
309
|
+
// The spent rung's own hash is kept through this entry (so a resumed
|
|
310
|
+
// commit can re-state the revealed key); the add entry drops it, while
|
|
311
|
+
// the next rung's hash stays as the credential's standing commitment.
|
|
312
|
+
nextKeyHashes: [
|
|
313
|
+
...new Set([
|
|
314
|
+
...published.nextKeyHashes,
|
|
315
|
+
rungHash,
|
|
316
|
+
newUpdateHash,
|
|
317
|
+
newStagedHash,
|
|
318
|
+
nextRungHash
|
|
319
|
+
])
|
|
320
|
+
]
|
|
321
|
+
});
|
|
322
|
+
await publishLogOnly({ store, log: updated.log, ifMatch: published.etag });
|
|
323
|
+
// The same account the reveal entry just extended.
|
|
324
|
+
published = await readLogOrThrow({ store, expectedDid: published.did });
|
|
325
|
+
}
|
|
326
|
+
// The add entry: the new client's verification methods and update key in;
|
|
327
|
+
// the spent rung's key and hash out. The credential's keyAgreement entry
|
|
328
|
+
// and the next rung's committed hash stand untouched. Signed by the new
|
|
329
|
+
// client's update key, whose hash the commit entry just committed.
|
|
330
|
+
const { did, doc } = published;
|
|
331
|
+
const vmId = (publicKeyMultibase) => `${did}#${publicKeyMultibase}`;
|
|
332
|
+
const addedMethods = markedVerificationMethodPair({
|
|
333
|
+
controller: did,
|
|
334
|
+
signingKeyMultibase: newClientKeys.signingKeyMultibase,
|
|
335
|
+
keyAgreementKeyMultibase: newClientKeys.keyAgreementKeyMultibase
|
|
336
|
+
});
|
|
337
|
+
const existingMethods = (doc.verificationMethod ?? []);
|
|
338
|
+
const verificationMethods = [
|
|
339
|
+
...existingMethods.filter(method => !addedMethods.some(added => added.id === method.id)),
|
|
340
|
+
...addedMethods
|
|
341
|
+
];
|
|
342
|
+
const withReference = (relation, id) => [...new Set([...relationIds(relation), id])];
|
|
343
|
+
const signingVmId = vmId(newClientKeys.signingKeyMultibase);
|
|
344
|
+
const signer = await updateKeySigner({
|
|
345
|
+
seed: newClientUpdateSeeds.updateSeed
|
|
346
|
+
});
|
|
347
|
+
const updated = await updateDID({
|
|
348
|
+
log: published.log,
|
|
349
|
+
signer,
|
|
350
|
+
alsoKnownAsWeb: true,
|
|
351
|
+
updateKeys: [
|
|
352
|
+
...new Set([
|
|
353
|
+
...published.updateKeys.filter(key => key !== rung.keyMultibase),
|
|
354
|
+
newClientKeys.updateKeyMultibase
|
|
355
|
+
])
|
|
356
|
+
],
|
|
357
|
+
nextKeyHashes: published.nextKeyHashes.filter(hash => hash !== rungHash),
|
|
358
|
+
verificationMethods,
|
|
359
|
+
authentication: withReference(doc.authentication, signingVmId),
|
|
360
|
+
assertionMethod: withReference(doc.assertionMethod, signingVmId),
|
|
361
|
+
keyAgreement: withReference(doc.keyAgreement, vmId(newClientKeys.keyAgreementKeyMultibase)),
|
|
362
|
+
capabilityInvocation: withReference(doc.capabilityInvocation, signingVmId),
|
|
363
|
+
capabilityDelegation: withReference(doc.capabilityDelegation, signingVmId)
|
|
364
|
+
});
|
|
365
|
+
// Conditional on the read this entry was built on: the re-read above when
|
|
366
|
+
// the commit entry ran here, the first read when it was skipped.
|
|
367
|
+
await publishLogOnly({ store, log: updated.log, ifMatch: published.etag });
|
|
368
|
+
return { did: updated.did, webDoc: updated.webDoc };
|
|
369
|
+
}
|
|
370
|
+
//# sourceMappingURL=standingWebvh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standingWebvh.js","sourceRoot":"","sources":["../../src/unlock/standingWebvh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { IKeyAgreementKey, IKeyResolver, IZcap } from '@interop/data-integrity-core';
|
|
2
|
+
import type { CollectionEncryption } from '@interop/was-client';
|
|
3
|
+
import type { AccountPointer, RecordSigner, SignedRecord } from '../keyring/record.js';
|
|
4
|
+
/**
|
|
5
|
+
* An unlock record's account binding is absent, malformed, or does not verify
|
|
6
|
+
* under the credential's binding MAC key. Its own class, distinct from a
|
|
7
|
+
* proof or decrypt failure: this is the refusal that says the record's
|
|
8
|
+
* account core (and ladder seed, where one rides) was not written by a holder
|
|
9
|
+
* of this credential -- a forged record redirecting login at another account,
|
|
10
|
+
* or a record from before the account moved hosts (either way the credential
|
|
11
|
+
* must be rebound).
|
|
12
|
+
*/
|
|
13
|
+
export declare class UnlockBindingError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A self-contained sealed record member: its own one-epoch descriptor plus
|
|
18
|
+
* the envelope sealed under it. Self-containment is what lets a member travel
|
|
19
|
+
* VERBATIM through a re-mint that re-seals its siblings -- each member
|
|
20
|
+
* decrypts against its own descriptor, not the frame's.
|
|
21
|
+
*/
|
|
22
|
+
export interface SealedRecordMember {
|
|
23
|
+
encryption: CollectionEncryption;
|
|
24
|
+
wrapped: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The unwrapped contents of an unlock record: the credential-authenticated
|
|
28
|
+
* account core (controller + pointer, plus the ladder seed where the
|
|
29
|
+
* credential is a standing method), the bridge delegation, the optional bind
|
|
30
|
+
* email, and the bind timestamp. `pointer` is required -- the record exists
|
|
31
|
+
* only on WAS deployments.
|
|
32
|
+
*/
|
|
33
|
+
export interface UnlockRecordContents {
|
|
34
|
+
controller: string;
|
|
35
|
+
email?: string;
|
|
36
|
+
pointer: AccountPointer;
|
|
37
|
+
delegation: IZcap;
|
|
38
|
+
ladderSeed?: Uint8Array;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A stored unlock record: the shared signed frame plus the record-kind
|
|
43
|
+
* members the frame proof also covers.
|
|
44
|
+
*/
|
|
45
|
+
export interface SignedUnlockRecord extends SignedRecord {
|
|
46
|
+
binding: string;
|
|
47
|
+
bridge: SealedRecordMember;
|
|
48
|
+
ladder?: SealedRecordMember;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Where an unlock record's proof stands after the unwrap: `'verified'` when
|
|
52
|
+
* the credential-derived unlock key signed it (checked before decryption), or
|
|
53
|
+
* a pending marker naming the signer the caller must still check against the
|
|
54
|
+
* account's verified did:webvh document -- the re-mint case, where an
|
|
55
|
+
* enrolled client signed on the credential's behalf. The pending case defers
|
|
56
|
+
* the shell's authenticity only, never the account identity: the binding is
|
|
57
|
+
* verified either way.
|
|
58
|
+
*/
|
|
59
|
+
export type UnlockRecordProofState = 'verified' | {
|
|
60
|
+
pending: {
|
|
61
|
+
verificationMethod: string;
|
|
62
|
+
keyMultibase: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Computes the account-binding tag: HMAC-SHA-256 over the core values under
|
|
67
|
+
* the credential-derived binding MAC key, base64url (no pad). Bind time calls
|
|
68
|
+
* it to stamp the record; login recomputes it to verify.
|
|
69
|
+
*
|
|
70
|
+
* @param options {object}
|
|
71
|
+
* @param options.bindingMacKey {Uint8Array} the credential-derived MAC key
|
|
72
|
+
* @param options.controller {string} the account controller
|
|
73
|
+
* @param options.pointer {AccountPointer} the account pointer
|
|
74
|
+
* @param [options.ladderSeed] {Uint8Array} the ladder seed, for a standing
|
|
75
|
+
* credential
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
export declare function computeUnlockBinding({ bindingMacKey, controller, pointer, ladderSeed }: {
|
|
79
|
+
bindingMacKey: Uint8Array;
|
|
80
|
+
controller: string;
|
|
81
|
+
pointer: AccountPointer;
|
|
82
|
+
ladderSeed?: Uint8Array;
|
|
83
|
+
}): string;
|
|
84
|
+
/**
|
|
85
|
+
* Reads the `binding` frame member off a stored unlock record without
|
|
86
|
+
* decrypting anything. The re-mint path uses it to confirm the record is
|
|
87
|
+
* re-mintable before touching it. Refuses a record with no binding: such a
|
|
88
|
+
* record predates the credential-authenticated core and cannot be re-minted
|
|
89
|
+
* -- its credential must be rebound.
|
|
90
|
+
*
|
|
91
|
+
* @param options {object}
|
|
92
|
+
* @param options.record {unknown} the stored record envelope
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
export declare function unlockRecordBinding({ record }: {
|
|
96
|
+
record: unknown;
|
|
97
|
+
}): string;
|
|
98
|
+
/**
|
|
99
|
+
* Wraps an unlock record at bind time: the shell (controller, optional
|
|
100
|
+
* email, pointer, timestamp) sealed to the credential's unlock KAK, the
|
|
101
|
+
* bridge delegation and the optional ladder seed sealed as their own
|
|
102
|
+
* members, the credential-authenticated binding computed over the core, and
|
|
103
|
+
* the whole frame signed -- by the credential-derived unlock key at bind
|
|
104
|
+
* time, or by an acting client's account key where a ceremony rebuilds a
|
|
105
|
+
* record whole (the reader settles the mixed-signer policy either way).
|
|
106
|
+
*
|
|
107
|
+
* @param options {object}
|
|
108
|
+
* @param options.controller {string} the account controller
|
|
109
|
+
* @param [options.email] {string} the account email, when known
|
|
110
|
+
* @param options.pointer {AccountPointer} the account pointer
|
|
111
|
+
* @param options.delegation {IZcap} the PUT-on-`did.jsonl` delegation to
|
|
112
|
+
* the credential-derived signing DID
|
|
113
|
+
* @param [options.ladderSeed] {Uint8Array} the update-key ladder seed, for
|
|
114
|
+
* a standing credential (a recovery code carries none)
|
|
115
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the credential's unlock
|
|
116
|
+
* KAK -- its public half is all the wrap uses
|
|
117
|
+
* @param options.signer {RecordSigner} the signing key
|
|
118
|
+
* @param options.bindingMacKey {Uint8Array} the credential-derived binding
|
|
119
|
+
* MAC key
|
|
120
|
+
* @param [options.createdAt] {string} the bind timestamp to stamp, as an
|
|
121
|
+
* ISO string; defaults to now. Supplied by a caller that pins record
|
|
122
|
+
* freshness.
|
|
123
|
+
* @returns {Promise<SignedUnlockRecord>}
|
|
124
|
+
*/
|
|
125
|
+
export declare function wrapUnlockRecord({ controller, email, pointer, delegation, ladderSeed, keyAgreementKey, signer, bindingMacKey, createdAt }: {
|
|
126
|
+
controller: string;
|
|
127
|
+
email?: string;
|
|
128
|
+
pointer: AccountPointer;
|
|
129
|
+
delegation: IZcap;
|
|
130
|
+
ladderSeed?: Uint8Array;
|
|
131
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
132
|
+
signer: RecordSigner;
|
|
133
|
+
bindingMacKey: Uint8Array;
|
|
134
|
+
createdAt?: string;
|
|
135
|
+
}): Promise<SignedUnlockRecord>;
|
|
136
|
+
/**
|
|
137
|
+
* Unwraps and validates an unlock record: the ordinary keyring-record frame
|
|
138
|
+
* checks plus the required pointer, bridge delegation, and
|
|
139
|
+
* credential-authenticated binding. A record without a bridge member is not
|
|
140
|
+
* an unlock record in the standing layout (an ordinary keyring record found
|
|
141
|
+
* under a credential's unlock Space would mean a torn bind) and is refused.
|
|
142
|
+
*
|
|
143
|
+
* The binding is verified before the contents are returned: the decrypted
|
|
144
|
+
* core must carry the tag the credential-derived MAC key computes over it, or
|
|
145
|
+
* the record is refused as forged ({@link UnlockBindingError}) -- the check
|
|
146
|
+
* that closes the host-forgery redirect, since the host never holds the MAC
|
|
147
|
+
* key. Nothing downstream may trust the pointer (or the ladder seed) before
|
|
148
|
+
* this returns.
|
|
149
|
+
*
|
|
150
|
+
* Proof verification is mixed-signer and ordered deliberately, exactly as the
|
|
151
|
+
* recovery record's was: a proof by the credential's own unlock key is
|
|
152
|
+
* verified BEFORE decryption; a proof by any other key comes back as a
|
|
153
|
+
* pending state naming the signer, for the caller to settle with
|
|
154
|
+
* `verifyRecordProof` against the verified document of the account the
|
|
155
|
+
* credential-authenticated pointer names.
|
|
156
|
+
*
|
|
157
|
+
* @param options {object}
|
|
158
|
+
* @param options.record {unknown} the stored record envelope
|
|
159
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the credential's unlock
|
|
160
|
+
* KAK
|
|
161
|
+
* @param options.keyResolver {IKeyResolver}
|
|
162
|
+
* @param options.expectedKeyMultibase {string} the credential-derived
|
|
163
|
+
* unlock signing key's multibase
|
|
164
|
+
* @param options.bindingMacKey {Uint8Array} the credential-derived binding
|
|
165
|
+
* MAC key the record's account binding must verify under
|
|
166
|
+
* @returns {Promise<{ contents: UnlockRecordContents,
|
|
167
|
+
* proofState: UnlockRecordProofState }>}
|
|
168
|
+
*/
|
|
169
|
+
export declare function unwrapUnlockRecord({ record, keyAgreementKey, keyResolver, expectedKeyMultibase, bindingMacKey }: {
|
|
170
|
+
record: unknown;
|
|
171
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
172
|
+
keyResolver: IKeyResolver;
|
|
173
|
+
expectedKeyMultibase: string;
|
|
174
|
+
bindingMacKey: Uint8Array;
|
|
175
|
+
}): Promise<{
|
|
176
|
+
contents: UnlockRecordContents;
|
|
177
|
+
proofState: UnlockRecordProofState;
|
|
178
|
+
}>;
|
|
179
|
+
/**
|
|
180
|
+
* Re-mints an unlock record's bridge: the revocation-cascade path that
|
|
181
|
+
* replaces a rotted or expiring delegation while touching nothing else. The
|
|
182
|
+
* shell, the ladder member, and the binding are carried VERBATIM (the re-mint
|
|
183
|
+
* cannot decrypt any of them and does not need to -- each is self-contained
|
|
184
|
+
* and the binding covers the core), a fresh bridge is sealed to the
|
|
185
|
+
* credential's unlock KAK public half, and the frame is re-signed by the
|
|
186
|
+
* acting client's account key -- the mixed-signer case the reader settles
|
|
187
|
+
* against the verified document.
|
|
188
|
+
*
|
|
189
|
+
* A record with no binding cannot be re-minted ({@link UnlockBindingError});
|
|
190
|
+
* its credential must be rebound.
|
|
191
|
+
*
|
|
192
|
+
* @param options {object}
|
|
193
|
+
* @param options.record {unknown} the standing stored record
|
|
194
|
+
* @param options.delegation {IZcap} the freshly minted `did.jsonl`
|
|
195
|
+
* delegation
|
|
196
|
+
* @param options.keyAgreementKey {IKeyAgreementKey} the credential's unlock
|
|
197
|
+
* KAK, public half only
|
|
198
|
+
* @param options.signer {RecordSigner} the acting client's account key
|
|
199
|
+
* @returns {Promise<SignedUnlockRecord>}
|
|
200
|
+
*/
|
|
201
|
+
export declare function remintUnlockRecordBridge({ record, delegation, keyAgreementKey, signer }: {
|
|
202
|
+
record: unknown;
|
|
203
|
+
delegation: IZcap;
|
|
204
|
+
keyAgreementKey: IKeyAgreementKey;
|
|
205
|
+
signer: RecordSigner;
|
|
206
|
+
}): Promise<SignedUnlockRecord>;
|
|
207
|
+
//# sourceMappingURL=unlockRecord.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unlockRecord.d.ts","sourceRoot":"","sources":["../../src/unlock/unlockRecord.ts"],"names":[],"mappings":"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"}
|