@interop/wallet-core 0.48.0 → 0.50.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/clientAnnex/credentialAnchoredGenesis.d.ts +118 -0
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -0
- package/dist/clientAnnex/credentialAnchoredGenesis.js +174 -0
- package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -0
- package/dist/clientAnnex/forget.d.ts +138 -0
- package/dist/clientAnnex/forget.d.ts.map +1 -0
- package/dist/clientAnnex/forget.js +133 -0
- package/dist/clientAnnex/forget.js.map +1 -0
- package/dist/clientAnnex/forgetLast.d.ts +267 -0
- package/dist/clientAnnex/forgetLast.d.ts.map +1 -0
- package/dist/clientAnnex/forgetLast.js +374 -0
- package/dist/clientAnnex/forgetLast.js.map +1 -0
- package/dist/clientAnnex/gc.d.ts +255 -0
- package/dist/clientAnnex/gc.d.ts.map +1 -0
- package/dist/clientAnnex/gc.js +445 -0
- package/dist/clientAnnex/gc.js.map +1 -0
- package/dist/clientAnnex/index.d.ts +60 -0
- package/dist/clientAnnex/index.d.ts.map +1 -0
- package/dist/clientAnnex/index.js +60 -0
- package/dist/clientAnnex/index.js.map +1 -0
- package/dist/{unlock → clientAnnex}/ladder.d.ts +99 -23
- package/dist/clientAnnex/ladder.d.ts.map +1 -0
- package/dist/clientAnnex/ladder.js +474 -0
- package/dist/clientAnnex/ladder.js.map +1 -0
- package/dist/clientAnnex/ladderAnchored.d.ts +276 -0
- package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -0
- package/dist/clientAnnex/ladderAnchored.js +631 -0
- package/dist/clientAnnex/ladderAnchored.js.map +1 -0
- package/dist/{webvh/companion.d.ts → clientAnnex/log.d.ts} +373 -152
- package/dist/clientAnnex/log.d.ts.map +1 -0
- package/dist/{webvh/companion.js → clientAnnex/log.js} +654 -261
- package/dist/clientAnnex/log.js.map +1 -0
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts +103 -0
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -0
- package/dist/clientAnnex/recoveryLadderAnchored.js +263 -0
- package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -0
- package/dist/{unlock → clientAnnex}/selfEnroll.d.ts +1 -1
- package/dist/clientAnnex/selfEnroll.d.ts.map +1 -0
- package/dist/{unlock → clientAnnex}/selfEnroll.js +1 -1
- package/dist/clientAnnex/selfEnroll.js.map +1 -0
- package/dist/clientAnnex/zcap.d.ts +49 -0
- package/dist/clientAnnex/zcap.d.ts.map +1 -0
- package/dist/clientAnnex/zcap.js +101 -0
- package/dist/clientAnnex/zcap.js.map +1 -0
- package/dist/clients/index.d.ts +9 -6
- package/dist/clients/index.d.ts.map +1 -1
- package/dist/clients/index.js +8 -5
- package/dist/clients/index.js.map +1 -1
- package/dist/clients/listing.d.ts +32 -0
- package/dist/clients/listing.d.ts.map +1 -1
- package/dist/clients/listing.js +47 -1
- package/dist/clients/listing.js.map +1 -1
- package/dist/clients/revocation.d.ts +33 -1
- package/dist/clients/revocation.d.ts.map +1 -1
- package/dist/clients/revocation.js +16 -4
- package/dist/clients/revocation.js.map +1 -1
- package/dist/enrollment/index.d.ts +6 -4
- package/dist/enrollment/index.d.ts.map +1 -1
- package/dist/enrollment/index.js +6 -4
- package/dist/enrollment/index.js.map +1 -1
- package/dist/enrollment/onboardingInvite.d.ts +5 -88
- package/dist/enrollment/onboardingInvite.d.ts.map +1 -1
- package/dist/enrollment/onboardingInvite.js +5 -168
- package/dist/enrollment/onboardingInvite.js.map +1 -1
- package/dist/genesis/index.d.ts +5 -0
- package/dist/genesis/index.d.ts.map +1 -1
- package/dist/genesis/index.js +5 -0
- package/dist/genesis/index.js.map +1 -1
- package/dist/keys/index.d.ts +1 -1
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +1 -1
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/spaceEpochs.d.ts +7 -2
- package/dist/keys/spaceEpochs.d.ts.map +1 -1
- package/dist/keys/spaceEpochs.js +8 -2
- package/dist/keys/spaceEpochs.js.map +1 -1
- package/dist/keys/userKeyRoster.d.ts +39 -0
- package/dist/keys/userKeyRoster.d.ts.map +1 -1
- package/dist/keys/userKeyRoster.js +43 -1
- package/dist/keys/userKeyRoster.js.map +1 -1
- package/dist/recovery/index.d.ts +5 -3
- package/dist/recovery/index.d.ts.map +1 -1
- package/dist/recovery/index.js +4 -2
- package/dist/recovery/index.js.map +1 -1
- package/dist/recovery/recoveryDelegation.d.ts +35 -12
- package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
- package/dist/recovery/recoveryDelegation.js +84 -86
- package/dist/recovery/recoveryDelegation.js.map +1 -1
- package/dist/recovery/recoveryWebvh.d.ts +32 -1
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +2 -2
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/request/capabilityRequest.d.ts +33 -0
- package/dist/request/capabilityRequest.d.ts.map +1 -0
- package/dist/request/capabilityRequest.js +35 -0
- package/dist/request/capabilityRequest.js.map +1 -0
- package/dist/request/ephemeralExchange.d.ts +118 -0
- package/dist/request/ephemeralExchange.d.ts.map +1 -0
- package/dist/request/ephemeralExchange.js +224 -0
- package/dist/request/ephemeralExchange.js.map +1 -0
- package/dist/request/index.d.ts +6 -0
- package/dist/request/index.d.ts.map +1 -1
- package/dist/request/index.js +6 -0
- package/dist/request/index.js.map +1 -1
- package/dist/space/activity.d.ts +38 -0
- package/dist/space/activity.d.ts.map +1 -1
- package/dist/space/activity.js +41 -1
- package/dist/space/activity.js.map +1 -1
- package/dist/space/index.d.ts +1 -1
- package/dist/space/index.d.ts.map +1 -1
- package/dist/space/index.js +1 -1
- package/dist/space/index.js.map +1 -1
- package/dist/unlock/index.d.ts +11 -23
- package/dist/unlock/index.d.ts.map +1 -1
- package/dist/unlock/index.js +10 -21
- package/dist/unlock/index.js.map +1 -1
- package/dist/unlock/retire.d.ts +47 -3
- package/dist/unlock/retire.d.ts.map +1 -1
- package/dist/unlock/retire.js +21 -5
- package/dist/unlock/retire.js.map +1 -1
- package/dist/unlock/standingWebvh.d.ts +37 -82
- package/dist/unlock/standingWebvh.d.ts.map +1 -1
- package/dist/unlock/standingWebvh.js +64 -247
- package/dist/unlock/standingWebvh.js.map +1 -1
- package/dist/unlock/unlockRecord.d.ts +13 -6
- package/dist/unlock/unlockRecord.d.ts.map +1 -1
- package/dist/unlock/unlockRecord.js +14 -8
- package/dist/unlock/unlockRecord.js.map +1 -1
- package/dist/webvh/delegatedLogStore.d.ts +5 -5
- package/dist/webvh/delegatedLogStore.js +1 -1
- package/dist/webvh/didWebvh.d.ts +20 -10
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +17 -17
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/index.d.ts +9 -23
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +9 -22
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts +77 -5
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +154 -61
- package/dist/webvh/revokeClient.js.map +1 -1
- package/dist/webvh/standingZcap.d.ts +11 -1
- package/dist/webvh/standingZcap.d.ts.map +1 -1
- package/dist/webvh/standingZcap.js +13 -14
- package/dist/webvh/standingZcap.js.map +1 -1
- package/dist/webvh/wasIdStore.d.ts +13 -7
- package/dist/webvh/wasIdStore.d.ts.map +1 -1
- package/dist/webvh/wasIdStore.js +8 -4
- package/dist/webvh/wasIdStore.js.map +1 -1
- package/dist/webvh/zcap.d.ts +0 -24
- package/dist/webvh/zcap.d.ts.map +1 -1
- package/dist/webvh/zcap.js +0 -42
- package/dist/webvh/zcap.js.map +1 -1
- package/package.json +40 -34
- package/dist/unlock/ladder.d.ts.map +0 -1
- package/dist/unlock/ladder.js +0 -254
- package/dist/unlock/ladder.js.map +0 -1
- package/dist/unlock/selfEnroll.d.ts.map +0 -1
- package/dist/unlock/selfEnroll.js.map +0 -1
- package/dist/webvh/companion.d.ts.map +0 -1
- package/dist/webvh/companion.js.map +0 -1
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* The byte length of a ladder seed: 32 random bytes, minted at bind time and
|
|
3
|
-
* carried only inside the unlock record's sealed ladder member.
|
|
4
|
-
*/
|
|
5
|
-
export declare const LADDER_SEED_BYTES = 32;
|
|
1
|
+
import type { DIDLog } from '@interop/did-method-webvh';
|
|
6
2
|
/**
|
|
7
3
|
* How many rungs {@link attributeLadderRung} derives before concluding the
|
|
8
4
|
* log commits none of them. Generous: one rung is consumed per
|
|
@@ -65,7 +61,7 @@ export declare function ladderRung({ ladderSeed, index }: {
|
|
|
65
61
|
* published verbatim in the account document (the seed is random, so the
|
|
66
62
|
* hash-commitment rule permits it) and stable across rung spends, so a
|
|
67
63
|
* delegation it signed survives every ladder advance. It carries the
|
|
68
|
-
*
|
|
64
|
+
* ladder-anchored window's document-visible authority (`assertionMethod` and
|
|
69
65
|
* `capabilityDelegation`), while update authority stays on the rungs -- the
|
|
70
66
|
* two roles never share a key.
|
|
71
67
|
*
|
|
@@ -94,43 +90,45 @@ export declare function ladderVmKeyMultibase({ ladderSeed }: {
|
|
|
94
90
|
ladderSeed: Uint8Array;
|
|
95
91
|
}): Promise<string>;
|
|
96
92
|
/**
|
|
97
|
-
* Derives the 32-byte update-key seed of
|
|
98
|
-
* the credential's STATIC update key on that generation's
|
|
99
|
-
* sequence is domain-separated per generation by the
|
|
100
|
-
* (`<
|
|
101
|
-
*
|
|
93
|
+
* Derives the 32-byte update-key seed of an annex generation's rung 0 --
|
|
94
|
+
* the credential's STATIC update key on that generation's annex log. The
|
|
95
|
+
* sequence is domain-separated per generation by the generation id
|
|
96
|
+
* (`<generationId>/rung/0` under the one {@link LADDER_SALT}): one shared
|
|
97
|
+
* sequence
|
|
98
|
+
* would hand the storage host, a legitimate reader of the private annex,
|
|
102
99
|
* a revealed key matching the ACCOUNT log's standing commitment, and a fresh
|
|
103
100
|
* per-generation sequence is what makes GC replacement self-healing (no rung
|
|
104
101
|
* index survives the deleted log, and none is needed).
|
|
105
102
|
*
|
|
106
|
-
* The
|
|
107
|
-
* ceremonies assert the `gen-<random>` shape (`
|
|
103
|
+
* The generation id is trusted here rather than re-validated -- the annex
|
|
104
|
+
* ceremonies assert the `gen-<random>` shape (`assertGenerationId`)
|
|
108
105
|
* before any derivation, and the label families stay disjoint for any
|
|
109
|
-
*
|
|
106
|
+
* generation id regardless (an account-rung label carries exactly one
|
|
107
|
+
* slash).
|
|
110
108
|
*
|
|
111
109
|
* @param options {object}
|
|
112
110
|
* @param options.ladderSeed {Uint8Array}
|
|
113
|
-
* @param options.
|
|
111
|
+
* @param options.generationId {string} the generation collection's name
|
|
114
112
|
* @returns {Uint8Array}
|
|
115
113
|
*/
|
|
116
|
-
export declare function
|
|
114
|
+
export declare function clientAnnexRungSeed({ ladderSeed, generationId }: {
|
|
117
115
|
ladderSeed: Uint8Array;
|
|
118
|
-
|
|
116
|
+
generationId: string;
|
|
119
117
|
}): Uint8Array;
|
|
120
118
|
/**
|
|
121
|
-
* Derives
|
|
122
|
-
* Deliberately index-free -- the
|
|
119
|
+
* Derives an annex generation's rung 0 in full: seed and public multibase.
|
|
120
|
+
* Deliberately index-free -- the annex chain has length one, so there is
|
|
123
121
|
* no rung to advance to and no attribution scan to run (see
|
|
124
|
-
* {@link
|
|
122
|
+
* {@link clientAnnexRungSeed}).
|
|
125
123
|
*
|
|
126
124
|
* @param options {object}
|
|
127
125
|
* @param options.ladderSeed {Uint8Array}
|
|
128
|
-
* @param options.
|
|
126
|
+
* @param options.generationId {string} the generation collection's name
|
|
129
127
|
* @returns {Promise<{ seed: Uint8Array, keyMultibase: string }>}
|
|
130
128
|
*/
|
|
131
|
-
export declare function
|
|
129
|
+
export declare function clientAnnexRung({ ladderSeed, generationId }: {
|
|
132
130
|
ladderSeed: Uint8Array;
|
|
133
|
-
|
|
131
|
+
generationId: string;
|
|
134
132
|
}): Promise<{
|
|
135
133
|
seed: Uint8Array;
|
|
136
134
|
keyMultibase: string;
|
|
@@ -173,4 +171,82 @@ export declare function attributeLadderRung({ ladderSeed, published, maxScan }:
|
|
|
173
171
|
rung: LadderRung;
|
|
174
172
|
state: LadderRungState;
|
|
175
173
|
}>;
|
|
174
|
+
/**
|
|
175
|
+
* Everything of one ladder that currently stands in the published log's
|
|
176
|
+
* parameters: the revealed rung keys still authorized in `updateKeys` and the
|
|
177
|
+
* committed hashes the ladder accounts for in `nextKeyHashes` -- including,
|
|
178
|
+
* for a torn self-enrollment, the hashes the reveal entry committed under the
|
|
179
|
+
* rung's authority for a client that was never published (its update- and
|
|
180
|
+
* staged-key hashes), which are as much a latent re-seizure credential as the
|
|
181
|
+
* rung's own commitment.
|
|
182
|
+
*/
|
|
183
|
+
export interface LadderStandingPosture {
|
|
184
|
+
revealedKeys: string[];
|
|
185
|
+
committedHashes: string[];
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Attributes a ladder's FULL standing posture from the log -- the retirement
|
|
189
|
+
* counterpart of {@link attributeLadderRung}, which recovers only the single
|
|
190
|
+
* current rung. Retiring a credential must strike every standing artifact its
|
|
191
|
+
* ladder accounts for, so this walks the log's effective parameters forward
|
|
192
|
+
* from an anchor (the recorded bind-time rung, however stale) and tracks the
|
|
193
|
+
* ladder's footprint entry by entry:
|
|
194
|
+
*
|
|
195
|
+
* - a newly authorized key whose hash was a known ladder commitment is a rung
|
|
196
|
+
* REVEAL; the hashes that entry newly commits are claimed by the ladder,
|
|
197
|
+
* as are those of any later entry the ladder itself signed, since the
|
|
198
|
+
* rung's authority stood behind them. Hashes an entry signed by some other
|
|
199
|
+
* key commits stay OUT, even while the rung sits in `updateKeys`: a rung
|
|
200
|
+
* stands revealed indefinitely after a forget (and after a torn
|
|
201
|
+
* self-enrollment), and the account's enrolled clients go on extending the
|
|
202
|
+
* log the whole time;
|
|
203
|
+
* - the entry that retires the revealed rung while authorizing a key whose
|
|
204
|
+
* hash sits among those claims is the enrollment's COMPLETION: the new
|
|
205
|
+
* client's update-key hash and the claim committed immediately after it
|
|
206
|
+
* (its staged hash -- a reveal-and-commit entry appends the credential's
|
|
207
|
+
* own next commitment LAST among its newly committed hashes, the ordering
|
|
208
|
+
* convention in `decisions/0007-ladder-reveal-hash-order.md`) transfer to
|
|
209
|
+
* the client and stop being ladder-owned. What the completion did not
|
|
210
|
+
* transfer stays ladder-owned only where the ladder can say so POSITIVELY:
|
|
211
|
+
* the hash derives from the ladder's own seed (or is the recorded key's),
|
|
212
|
+
* or the credential itself survives the completing entry, which is what
|
|
213
|
+
* makes the residue its next standing commitment. A SPEND -- the recovery
|
|
214
|
+
* continuation, whose one entry retires the code's own posture and
|
|
215
|
+
* publishes its successor's -- leaves the replacement credential's
|
|
216
|
+
* commitment in that position instead, and striking that would leave the
|
|
217
|
+
* replacement unusable and unhealable;
|
|
218
|
+
* - a claim or revealed key that later leaves the parameters without a
|
|
219
|
+
* completion was struck by some other edit and simply stops standing.
|
|
220
|
+
*
|
|
221
|
+
* With the ladder seed in hand (`ladderSeed`), every rung's key and hash are
|
|
222
|
+
* additionally known a priori, so the attribution does not depend on the
|
|
223
|
+
* anchor being current; without it, the walk is anchored on
|
|
224
|
+
* `anchorKeyMultibase` and on `credentialVmId` alone, and a residue neither
|
|
225
|
+
* can attribute is released -- the retirement then strikes what the recorded
|
|
226
|
+
* posture names and nothing more. More than one ladder reveal standing or
|
|
227
|
+
* arriving at once matches no legitimate history and fails closed
|
|
228
|
+
* ({@link LadderAttributionError}).
|
|
229
|
+
*
|
|
230
|
+
* @param options {object}
|
|
231
|
+
* @param options.log {DIDLog} a resolved, caller-verified log
|
|
232
|
+
* @param options.anchorKeyMultibase {string} the credential's recorded
|
|
233
|
+
* update-key multibase (bind-time rung 0, or a refreshed later rung)
|
|
234
|
+
* @param [options.ladderSeed] {Uint8Array} the credential's ladder seed,
|
|
235
|
+
* when the caller holds it
|
|
236
|
+
* @param [options.credentialVmId] {string} the credential's own
|
|
237
|
+
* `keyAgreement` verification-method id, which tells a climb (the
|
|
238
|
+
* credential stands afterwards) from a spend (its posture goes in the same
|
|
239
|
+
* entry)
|
|
240
|
+
* @param [options.maxScan] {number} seeded pre-derivation bound; defaults to
|
|
241
|
+
* {@link LADDER_MAX_SCAN}
|
|
242
|
+
* @returns {Promise<LadderStandingPosture>} what currently stands; both
|
|
243
|
+
* arrays empty when the log carries nothing of the ladder any more
|
|
244
|
+
*/
|
|
245
|
+
export declare function attributeLadderPosture({ log, anchorKeyMultibase, ladderSeed, credentialVmId, maxScan }: {
|
|
246
|
+
log: DIDLog;
|
|
247
|
+
anchorKeyMultibase: string;
|
|
248
|
+
ladderSeed?: Uint8Array;
|
|
249
|
+
credentialVmId?: string;
|
|
250
|
+
maxScan?: number;
|
|
251
|
+
}): Promise<LadderStandingPosture>;
|
|
176
252
|
//# sourceMappingURL=ladder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ladder.d.ts","sourceRoot":"","sources":["../../src/clientAnnex/ladder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The update-key ladder: a standing unlock credential's latent-and-consumed
|
|
6
|
+
* did:webvh update authority. Rungs derive deterministically from a random
|
|
7
|
+
* 32-byte ladder seed carried inside the credential's unlock record -- never
|
|
8
|
+
* from the unlock secret itself, because a revealed rung lives verbatim in
|
|
9
|
+
* the world-readable `updateKeys` forever, where no hash commitment can
|
|
10
|
+
* protect it, so a secret-derived rung would be a standing offline grind
|
|
11
|
+
* oracle against the credential.
|
|
12
|
+
*
|
|
13
|
+
* Between uses only `hash(rung i)` stands in the document's `nextKeyHashes`;
|
|
14
|
+
* each self-enrollment is one loud reveal-and-commit entry signing with rung
|
|
15
|
+
* `i` and committing `hash(rung i + 1)`, after which the add entry retires
|
|
16
|
+
* the spent rung. The credential never holds a standing `updateKeys` member.
|
|
17
|
+
*
|
|
18
|
+
* There is no stored counter: which rung is current is recovered by
|
|
19
|
+
* re-deriving and scanning the published log's standing parameters
|
|
20
|
+
* ({@link attributeLadderRung}), and ambiguity fails closed rather than
|
|
21
|
+
* guessing -- the clients-listing attribution precedent. A lost
|
|
22
|
+
* compare-and-swap race resolves by determinism: the winner's entry commits
|
|
23
|
+
* `hash(rung i + 1)`, which IS the loser's retry key, so a re-run
|
|
24
|
+
* re-attributes and climbs one rung.
|
|
25
|
+
*
|
|
26
|
+
* The rung derivation is wire-level (both wallet apps must climb the same
|
|
27
|
+
* ladder from the same seed), so the salt and info labels are permanent.
|
|
28
|
+
*/
|
|
29
|
+
import { deriveNextKeyHash } from '@interop/did-method-webvh';
|
|
30
|
+
import { hkdf } from '@noble/hashes/hkdf.js';
|
|
31
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
32
|
+
import { effectiveParameters, relationIds, updateKeyMultibase } from '../webvh/didWebvh.js';
|
|
33
|
+
import { LADDER_SEED_BYTES } from '../unlock/unlockRecord.js';
|
|
34
|
+
/**
|
|
35
|
+
* The HKDF salt for rung derivation and the per-rung info prefix (the rung
|
|
36
|
+
* index in decimal follows it). Both permanent -- changing either orphans
|
|
37
|
+
* every bound credential's ladder.
|
|
38
|
+
*/
|
|
39
|
+
const LADDER_SALT = 'freewallet/unlock/update-ladder/v1';
|
|
40
|
+
const LADDER_RUNG_INFO_PREFIX = 'rung/';
|
|
41
|
+
/**
|
|
42
|
+
* The info label of the ladder VM -- the stable sibling key published in the
|
|
43
|
+
* account document while the account has no enrolled durable client. One salt
|
|
44
|
+
* for everything ladder-seed-derived, with the info namespace doing the
|
|
45
|
+
* separation: `vm` can never collide with a `rung/<n>` label. Permanent.
|
|
46
|
+
*/
|
|
47
|
+
const LADDER_VM_INFO = 'vm';
|
|
48
|
+
/**
|
|
49
|
+
* The info-label suffix of a client-annex rung: the label is
|
|
50
|
+
* `<generationId>/rung/<k>` where `<generationId>` is the generation
|
|
51
|
+
* collection's name
|
|
52
|
+
* (`gen-<random>`) and `k` is pinned at 0 -- the annex log's update
|
|
53
|
+
* authority is each standing credential's STATIC rung 0 (chain length one,
|
|
54
|
+
* never advanced), so only `/rung/0` is ever derived. The three families
|
|
55
|
+
* under the one salt stay disjoint: `rung/<n>` labels carry exactly one
|
|
56
|
+
* slash followed by a decimal index, `vm` carries none, and an annex
|
|
57
|
+
* label always carries two slashes behind its `gen-` generation id.
|
|
58
|
+
* Permanent.
|
|
59
|
+
*/
|
|
60
|
+
const CLIENT_ANNEX_RUNG_INFO_SUFFIX = '/rung/0';
|
|
61
|
+
/**
|
|
62
|
+
* How many rungs {@link attributeLadderRung} derives before concluding the
|
|
63
|
+
* log commits none of them. Generous: one rung is consumed per
|
|
64
|
+
* self-enrollment, so a real ladder's standing commitment sits at the number
|
|
65
|
+
* of self-enrollments the credential has ever performed.
|
|
66
|
+
*/
|
|
67
|
+
export const LADDER_MAX_SCAN = 128;
|
|
68
|
+
/**
|
|
69
|
+
* Thrown when the published log's standing parameters match no derivable rung
|
|
70
|
+
* -- the credential's posture was revoked (or never published), the ladder
|
|
71
|
+
* seed does not belong to this account, or the scan bound was exceeded -- or
|
|
72
|
+
* when they match more than one rung in the same role, which no legitimate
|
|
73
|
+
* history produces. Self-enrollment refuses loudly rather than guessing.
|
|
74
|
+
*/
|
|
75
|
+
export class LadderAttributionError extends Error {
|
|
76
|
+
constructor(message) {
|
|
77
|
+
super(message);
|
|
78
|
+
this.name = 'LadderAttributionError';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Generates a fresh random ladder seed.
|
|
83
|
+
*
|
|
84
|
+
* @returns {Uint8Array}
|
|
85
|
+
*/
|
|
86
|
+
export function generateLadderSeed() {
|
|
87
|
+
return crypto.getRandomValues(new Uint8Array(LADDER_SEED_BYTES));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The one HKDF invocation of the ladder derivation family. Every
|
|
91
|
+
* ladder-seed-derived key (rungs, the ladder VM) comes through here, so the
|
|
92
|
+
* permanent wire-level triple -- SHA-256, {@link LADDER_SALT}, 32 bytes --
|
|
93
|
+
* lives in exactly one place and only the info label varies.
|
|
94
|
+
*
|
|
95
|
+
* @param options {object}
|
|
96
|
+
* @param options.ladderSeed {Uint8Array}
|
|
97
|
+
* @param options.info {string}
|
|
98
|
+
* @returns {Uint8Array}
|
|
99
|
+
*/
|
|
100
|
+
function ladderDerive({ ladderSeed, info }) {
|
|
101
|
+
return hkdf(sha256, ladderSeed, new TextEncoder().encode(LADDER_SALT), new TextEncoder().encode(info), 32);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Derives the 32-byte update-key seed of rung `index`.
|
|
105
|
+
*
|
|
106
|
+
* @param options {object}
|
|
107
|
+
* @param options.ladderSeed {Uint8Array}
|
|
108
|
+
* @param options.index {number} the rung index, from 0
|
|
109
|
+
* @returns {Uint8Array}
|
|
110
|
+
*/
|
|
111
|
+
export function ladderRungSeed({ ladderSeed, index }) {
|
|
112
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
113
|
+
throw new Error(`Invalid ladder rung index "${String(index)}".`);
|
|
114
|
+
}
|
|
115
|
+
return ladderDerive({
|
|
116
|
+
ladderSeed,
|
|
117
|
+
info: `${LADDER_RUNG_INFO_PREFIX}${index}`
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Derives rung `index` in full: seed and public multibase.
|
|
122
|
+
*
|
|
123
|
+
* @param options {object}
|
|
124
|
+
* @param options.ladderSeed {Uint8Array}
|
|
125
|
+
* @param options.index {number} the rung index, from 0
|
|
126
|
+
* @returns {Promise<LadderRung>}
|
|
127
|
+
*/
|
|
128
|
+
export async function ladderRung({ ladderSeed, index }) {
|
|
129
|
+
const seed = ladderRungSeed({ ladderSeed, index });
|
|
130
|
+
return { index, seed, keyMultibase: await updateKeyMultibase({ seed }) };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Derives the 32-byte Ed25519 seed of the ladder VM -- the STABLE SIBLING: a
|
|
134
|
+
* dedicated key derived once from the ladder seed, distinct from every rung,
|
|
135
|
+
* published verbatim in the account document (the seed is random, so the
|
|
136
|
+
* hash-commitment rule permits it) and stable across rung spends, so a
|
|
137
|
+
* delegation it signed survives every ladder advance. It carries the
|
|
138
|
+
* ladder-anchored window's document-visible authority (`assertionMethod` and
|
|
139
|
+
* `capabilityDelegation`), while update authority stays on the rungs -- the
|
|
140
|
+
* two roles never share a key.
|
|
141
|
+
*
|
|
142
|
+
* Because the key is derived, removing its verification method is never the
|
|
143
|
+
* terminal remedy: a later reinstall republishes the same key under the same
|
|
144
|
+
* id, and any still-unexpired delegation it signed resumes verifying the
|
|
145
|
+
* moment the method returns. Revoking the delegations themselves (and,
|
|
146
|
+
* ultimately, rotating the credential) is what actually ends its authority.
|
|
147
|
+
*
|
|
148
|
+
* @param options {object}
|
|
149
|
+
* @param options.ladderSeed {Uint8Array}
|
|
150
|
+
* @returns {Uint8Array}
|
|
151
|
+
*/
|
|
152
|
+
export function ladderVmSeed({ ladderSeed }) {
|
|
153
|
+
return ladderDerive({ ladderSeed, info: LADDER_VM_INFO });
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The ladder VM's public key multibase, as the document publishes it (see
|
|
157
|
+
* {@link ladderVmSeed} for what the key is).
|
|
158
|
+
*
|
|
159
|
+
* @param options {object}
|
|
160
|
+
* @param options.ladderSeed {Uint8Array}
|
|
161
|
+
* @returns {Promise<string>}
|
|
162
|
+
*/
|
|
163
|
+
export async function ladderVmKeyMultibase({ ladderSeed }) {
|
|
164
|
+
return updateKeyMultibase({ seed: ladderVmSeed({ ladderSeed }) });
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Derives the 32-byte update-key seed of an annex generation's rung 0 --
|
|
168
|
+
* the credential's STATIC update key on that generation's annex log. The
|
|
169
|
+
* sequence is domain-separated per generation by the generation id
|
|
170
|
+
* (`<generationId>/rung/0` under the one {@link LADDER_SALT}): one shared
|
|
171
|
+
* sequence
|
|
172
|
+
* would hand the storage host, a legitimate reader of the private annex,
|
|
173
|
+
* a revealed key matching the ACCOUNT log's standing commitment, and a fresh
|
|
174
|
+
* per-generation sequence is what makes GC replacement self-healing (no rung
|
|
175
|
+
* index survives the deleted log, and none is needed).
|
|
176
|
+
*
|
|
177
|
+
* The generation id is trusted here rather than re-validated -- the annex
|
|
178
|
+
* ceremonies assert the `gen-<random>` shape (`assertGenerationId`)
|
|
179
|
+
* before any derivation, and the label families stay disjoint for any
|
|
180
|
+
* generation id regardless (an account-rung label carries exactly one
|
|
181
|
+
* slash).
|
|
182
|
+
*
|
|
183
|
+
* @param options {object}
|
|
184
|
+
* @param options.ladderSeed {Uint8Array}
|
|
185
|
+
* @param options.generationId {string} the generation collection's name
|
|
186
|
+
* @returns {Uint8Array}
|
|
187
|
+
*/
|
|
188
|
+
export function clientAnnexRungSeed({ ladderSeed, generationId }) {
|
|
189
|
+
return ladderDerive({
|
|
190
|
+
ladderSeed,
|
|
191
|
+
info: `${generationId}${CLIENT_ANNEX_RUNG_INFO_SUFFIX}`
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Derives an annex generation's rung 0 in full: seed and public multibase.
|
|
196
|
+
* Deliberately index-free -- the annex chain has length one, so there is
|
|
197
|
+
* no rung to advance to and no attribution scan to run (see
|
|
198
|
+
* {@link clientAnnexRungSeed}).
|
|
199
|
+
*
|
|
200
|
+
* @param options {object}
|
|
201
|
+
* @param options.ladderSeed {Uint8Array}
|
|
202
|
+
* @param options.generationId {string} the generation collection's name
|
|
203
|
+
* @returns {Promise<{ seed: Uint8Array, keyMultibase: string }>}
|
|
204
|
+
*/
|
|
205
|
+
export async function clientAnnexRung({ ladderSeed, generationId }) {
|
|
206
|
+
const seed = clientAnnexRungSeed({ ladderSeed, generationId });
|
|
207
|
+
return { seed, keyMultibase: await updateKeyMultibase({ seed }) };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Recovers the ladder's current rung from the published log's standing
|
|
211
|
+
* parameters -- the counter recovery that replaces any stored counter. Scans
|
|
212
|
+
* rungs `0..maxScan - 1`; a rung whose key stands in `updateKeys` is a torn
|
|
213
|
+
* self-enrollment to resume (`'revealed'`), else a rung whose hash stands in
|
|
214
|
+
* `nextKeyHashes` is the standing commitment (`'committed'`). Exactly one
|
|
215
|
+
* revealed rung, or exactly one committed rung beside it, is legitimate --
|
|
216
|
+
* the reveal entry keeps the spent rung's hash committed so a resumed run can
|
|
217
|
+
* re-state it, which is why a revealed rung wins over a committed one.
|
|
218
|
+
* Anything else fails closed with {@link LadderAttributionError}.
|
|
219
|
+
*
|
|
220
|
+
* @param options {object}
|
|
221
|
+
* @param options.ladderSeed {Uint8Array}
|
|
222
|
+
* @param options.published {object} the resolved log's standing parameters
|
|
223
|
+
* @param options.published.updateKeys {string[]}
|
|
224
|
+
* @param options.published.nextKeyHashes {string[]}
|
|
225
|
+
* @param [options.maxScan] {number} how many rungs to derive before giving
|
|
226
|
+
* up; defaults to {@link LADDER_MAX_SCAN}
|
|
227
|
+
* @returns {Promise<{ rung: LadderRung, state: LadderRungState }>}
|
|
228
|
+
*/
|
|
229
|
+
export async function attributeLadderRung({ ladderSeed, published, maxScan = LADDER_MAX_SCAN }) {
|
|
230
|
+
const revealed = [];
|
|
231
|
+
const committed = [];
|
|
232
|
+
for (let index = 0; index < maxScan; index++) {
|
|
233
|
+
const rung = await ladderRung({ ladderSeed, index });
|
|
234
|
+
if (published.updateKeys.includes(rung.keyMultibase)) {
|
|
235
|
+
revealed.push(rung);
|
|
236
|
+
}
|
|
237
|
+
else if (published.nextKeyHashes.includes(await deriveNextKeyHash(rung.keyMultibase))) {
|
|
238
|
+
committed.push(rung);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (revealed.length > 1 || committed.length > 1) {
|
|
242
|
+
throw new LadderAttributionError('The published log commits more than one rung of this ladder in the ' +
|
|
243
|
+
'same role; refusing to self-enroll on an ambiguous attribution.');
|
|
244
|
+
}
|
|
245
|
+
if (revealed.length === 1) {
|
|
246
|
+
return { rung: revealed[0], state: 'revealed' };
|
|
247
|
+
}
|
|
248
|
+
if (committed.length === 1) {
|
|
249
|
+
return { rung: committed[0], state: 'committed' };
|
|
250
|
+
}
|
|
251
|
+
throw new LadderAttributionError('The published log commits no rung of this ladder; the credential has ' +
|
|
252
|
+
'been revoked, was never published, or does not belong to this account.');
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Whether a log entry was signed by a key this ladder accounts for. A
|
|
256
|
+
* did:webvh entry proof names its key as `did:key:<multibase>#<multibase>`
|
|
257
|
+
* -- the id form the resolver matches against the entry's authorized
|
|
258
|
+
* `updateKeys` -- so the fragment IS the update-key multibase. The log is
|
|
259
|
+
* caller-verified, so a proof standing here has already been checked against
|
|
260
|
+
* the authorized set; this only asks whose it was.
|
|
261
|
+
*
|
|
262
|
+
* @param options {object}
|
|
263
|
+
* @param options.entry {DIDLogEntry | undefined}
|
|
264
|
+
* @param options.ladderKeys {Set<string>} the rung keys known so far
|
|
265
|
+
* @returns {boolean}
|
|
266
|
+
*/
|
|
267
|
+
function ladderSigned({ entry, ladderKeys }) {
|
|
268
|
+
return (entry?.proof ?? []).some(proof => {
|
|
269
|
+
const keyMultibase = proof.verificationMethod?.split('#')[1];
|
|
270
|
+
return keyMultibase !== undefined && ladderKeys.has(keyMultibase);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Whether the document an entry publishes still carries the retiring
|
|
275
|
+
* credential's own `keyAgreement` verification method -- asked at the entry
|
|
276
|
+
* that completes an enrollment the ladder's rung revealed, where it is the
|
|
277
|
+
* test of whether the CREDENTIAL survives its own ceremony. A self-enrollment
|
|
278
|
+
* leaves the credential's posture untouched (it climbs to the next rung),
|
|
279
|
+
* while a spend -- the recovery continuation, whose add-and-retire entry
|
|
280
|
+
* strikes the code's posture and publishes its successor's -- ends it.
|
|
281
|
+
*
|
|
282
|
+
* With no id supplied the question cannot be asked, so the answer is a
|
|
283
|
+
* conservative `false`: a claim the ladder cannot attribute is released rather
|
|
284
|
+
* than struck.
|
|
285
|
+
*
|
|
286
|
+
* @param options {object}
|
|
287
|
+
* @param options.entry {DIDLogEntry | undefined}
|
|
288
|
+
* @param options.vmId {string | undefined} the credential's key-agreement
|
|
289
|
+
* verification-method id, where the caller supplied one
|
|
290
|
+
* @returns {boolean}
|
|
291
|
+
*/
|
|
292
|
+
function credentialSurvives({ entry, vmId }) {
|
|
293
|
+
if (vmId === undefined || !entry?.state) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
// The `keyAgreement` RELATION alone, rather than the bare
|
|
297
|
+
// `verificationMethod` array: an entry that dereferences the method while
|
|
298
|
+
// leaving its object behind has ended the credential's posture, and reading
|
|
299
|
+
// that as a climb would claim -- and strike -- a successor credential's
|
|
300
|
+
// commitment.
|
|
301
|
+
return relationIds(entry.state.keyAgreement).includes(vmId);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Attributes a ladder's FULL standing posture from the log -- the retirement
|
|
305
|
+
* counterpart of {@link attributeLadderRung}, which recovers only the single
|
|
306
|
+
* current rung. Retiring a credential must strike every standing artifact its
|
|
307
|
+
* ladder accounts for, so this walks the log's effective parameters forward
|
|
308
|
+
* from an anchor (the recorded bind-time rung, however stale) and tracks the
|
|
309
|
+
* ladder's footprint entry by entry:
|
|
310
|
+
*
|
|
311
|
+
* - a newly authorized key whose hash was a known ladder commitment is a rung
|
|
312
|
+
* REVEAL; the hashes that entry newly commits are claimed by the ladder,
|
|
313
|
+
* as are those of any later entry the ladder itself signed, since the
|
|
314
|
+
* rung's authority stood behind them. Hashes an entry signed by some other
|
|
315
|
+
* key commits stay OUT, even while the rung sits in `updateKeys`: a rung
|
|
316
|
+
* stands revealed indefinitely after a forget (and after a torn
|
|
317
|
+
* self-enrollment), and the account's enrolled clients go on extending the
|
|
318
|
+
* log the whole time;
|
|
319
|
+
* - the entry that retires the revealed rung while authorizing a key whose
|
|
320
|
+
* hash sits among those claims is the enrollment's COMPLETION: the new
|
|
321
|
+
* client's update-key hash and the claim committed immediately after it
|
|
322
|
+
* (its staged hash -- a reveal-and-commit entry appends the credential's
|
|
323
|
+
* own next commitment LAST among its newly committed hashes, the ordering
|
|
324
|
+
* convention in `decisions/0007-ladder-reveal-hash-order.md`) transfer to
|
|
325
|
+
* the client and stop being ladder-owned. What the completion did not
|
|
326
|
+
* transfer stays ladder-owned only where the ladder can say so POSITIVELY:
|
|
327
|
+
* the hash derives from the ladder's own seed (or is the recorded key's),
|
|
328
|
+
* or the credential itself survives the completing entry, which is what
|
|
329
|
+
* makes the residue its next standing commitment. A SPEND -- the recovery
|
|
330
|
+
* continuation, whose one entry retires the code's own posture and
|
|
331
|
+
* publishes its successor's -- leaves the replacement credential's
|
|
332
|
+
* commitment in that position instead, and striking that would leave the
|
|
333
|
+
* replacement unusable and unhealable;
|
|
334
|
+
* - a claim or revealed key that later leaves the parameters without a
|
|
335
|
+
* completion was struck by some other edit and simply stops standing.
|
|
336
|
+
*
|
|
337
|
+
* With the ladder seed in hand (`ladderSeed`), every rung's key and hash are
|
|
338
|
+
* additionally known a priori, so the attribution does not depend on the
|
|
339
|
+
* anchor being current; without it, the walk is anchored on
|
|
340
|
+
* `anchorKeyMultibase` and on `credentialVmId` alone, and a residue neither
|
|
341
|
+
* can attribute is released -- the retirement then strikes what the recorded
|
|
342
|
+
* posture names and nothing more. More than one ladder reveal standing or
|
|
343
|
+
* arriving at once matches no legitimate history and fails closed
|
|
344
|
+
* ({@link LadderAttributionError}).
|
|
345
|
+
*
|
|
346
|
+
* @param options {object}
|
|
347
|
+
* @param options.log {DIDLog} a resolved, caller-verified log
|
|
348
|
+
* @param options.anchorKeyMultibase {string} the credential's recorded
|
|
349
|
+
* update-key multibase (bind-time rung 0, or a refreshed later rung)
|
|
350
|
+
* @param [options.ladderSeed] {Uint8Array} the credential's ladder seed,
|
|
351
|
+
* when the caller holds it
|
|
352
|
+
* @param [options.credentialVmId] {string} the credential's own
|
|
353
|
+
* `keyAgreement` verification-method id, which tells a climb (the
|
|
354
|
+
* credential stands afterwards) from a spend (its posture goes in the same
|
|
355
|
+
* entry)
|
|
356
|
+
* @param [options.maxScan] {number} seeded pre-derivation bound; defaults to
|
|
357
|
+
* {@link LADDER_MAX_SCAN}
|
|
358
|
+
* @returns {Promise<LadderStandingPosture>} what currently stands; both
|
|
359
|
+
* arrays empty when the log carries nothing of the ladder any more
|
|
360
|
+
*/
|
|
361
|
+
export async function attributeLadderPosture({ log, anchorKeyMultibase, ladderSeed, credentialVmId, maxScan = LADDER_MAX_SCAN }) {
|
|
362
|
+
const anchorHash = await deriveNextKeyHash(anchorKeyMultibase);
|
|
363
|
+
const ladderKeys = new Set([anchorKeyMultibase]);
|
|
364
|
+
// What the ladder knows a priori: the recorded key's hash and, with the
|
|
365
|
+
// seed in hand, every rung's. A claim outside this set is held on the
|
|
366
|
+
// evidence of the entry that committed it, and released again when a
|
|
367
|
+
// completion shows it was never the ladder's.
|
|
368
|
+
const derivedHashes = new Set([anchorHash]);
|
|
369
|
+
const ladderHashes = new Set([anchorHash]);
|
|
370
|
+
if (ladderSeed) {
|
|
371
|
+
for (let index = 0; index < maxScan; index++) {
|
|
372
|
+
const rung = await ladderRung({ ladderSeed, index });
|
|
373
|
+
ladderKeys.add(rung.keyMultibase);
|
|
374
|
+
const rungHash = await deriveNextKeyHash(rung.keyMultibase);
|
|
375
|
+
derivedHashes.add(rungHash);
|
|
376
|
+
ladderHashes.add(rungHash);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const params = effectiveParameters(log);
|
|
380
|
+
let pending;
|
|
381
|
+
let prevUpdateKeys = new Set();
|
|
382
|
+
let prevHashes = new Set();
|
|
383
|
+
for (const [index, entry] of params.entries()) {
|
|
384
|
+
const currentUpdateKeys = new Set(entry.updateKeys);
|
|
385
|
+
const addedKeys = entry.updateKeys.filter(key => !prevUpdateKeys.has(key));
|
|
386
|
+
// Order-preserving on purpose: the completion transfer below reads the
|
|
387
|
+
// claim committed immediately after the client's update-key hash as its
|
|
388
|
+
// staged hash (the reveal entry's append order, a ratified convention).
|
|
389
|
+
const addedHashes = entry.nextKeyHashes.filter(hash => !prevHashes.has(hash));
|
|
390
|
+
// Completion first: the pending revealed rung left `updateKeys`. When the
|
|
391
|
+
// same entry authorizes a key whose hash sits among the reveal's claims,
|
|
392
|
+
// the enrollment completed -- that hash and its successor (the client's
|
|
393
|
+
// staged hash) transfer to the client. A rung leaving any other way was
|
|
394
|
+
// struck, and its claims simply stop standing.
|
|
395
|
+
if (pending && !currentUpdateKeys.has(pending.key)) {
|
|
396
|
+
const transferred = new Set();
|
|
397
|
+
for (const key of addedKeys) {
|
|
398
|
+
const at = pending.claims.indexOf(await deriveNextKeyHash(key));
|
|
399
|
+
if (at === -1) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
transferred.add(pending.claims[at]);
|
|
403
|
+
const staged = pending.claims[at + 1];
|
|
404
|
+
if (staged !== undefined) {
|
|
405
|
+
transferred.add(staged);
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
// What the reveal committed and the completion did not transfer is
|
|
410
|
+
// ladder-owned only on positive attribution -- a hash the ladder
|
|
411
|
+
// derives itself, or any residue of a ceremony the credential came out
|
|
412
|
+
// of still standing (a climb, whose residue really is its next rung's
|
|
413
|
+
// commitment). A spend's residue is its SUCCESSOR's commitment, and
|
|
414
|
+
// striking that is silent, unhealable damage: the replacement keeps its
|
|
415
|
+
// verification method and its roster wrap, and only fails when someone
|
|
416
|
+
// finally types it.
|
|
417
|
+
const stands = credentialSurvives({
|
|
418
|
+
entry: log[index],
|
|
419
|
+
vmId: credentialVmId
|
|
420
|
+
});
|
|
421
|
+
for (const hash of pending.claims) {
|
|
422
|
+
if (transferred.has(hash) || !(stands || derivedHashes.has(hash))) {
|
|
423
|
+
ladderHashes.delete(hash);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
pending = undefined;
|
|
427
|
+
}
|
|
428
|
+
// A newly authorized key matching a known ladder commitment is a reveal.
|
|
429
|
+
const reveals = [];
|
|
430
|
+
for (const key of addedKeys) {
|
|
431
|
+
if (ladderKeys.has(key) ||
|
|
432
|
+
ladderHashes.has(await deriveNextKeyHash(key))) {
|
|
433
|
+
reveals.push(key);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (reveals.length > 1 || (reveals.length === 1 && pending)) {
|
|
437
|
+
throw new LadderAttributionError('The published log reveals more than one rung of this ladder at ' +
|
|
438
|
+
'once; refusing to attribute an ambiguous history.');
|
|
439
|
+
}
|
|
440
|
+
if (reveals.length === 1) {
|
|
441
|
+
const key = reveals[0];
|
|
442
|
+
ladderKeys.add(key);
|
|
443
|
+
pending = { key, claims: [...addedHashes] };
|
|
444
|
+
for (const hash of addedHashes) {
|
|
445
|
+
ladderHashes.add(hash);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else if (pending && ladderSigned({ entry: log[index], ladderKeys })) {
|
|
449
|
+
// The rung is still revealed AND signed this entry, so the hashes it
|
|
450
|
+
// commits were committed under the rung's authority (the
|
|
451
|
+
// ladder-anchored window's separate commit entry) and join its claims.
|
|
452
|
+
// Signature, not mere presence in `updateKeys`, is what attributes
|
|
453
|
+
// them: the rung's reveal outlives the ceremony that revealed it, so
|
|
454
|
+
// claiming everything committed afterwards would sweep up hashes of
|
|
455
|
+
// other credentials and of racing enrollments -- and striking those on
|
|
456
|
+
// retirement is silent, unhealable damage.
|
|
457
|
+
pending.claims.push(...addedHashes);
|
|
458
|
+
for (const hash of addedHashes) {
|
|
459
|
+
ladderHashes.add(hash);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
prevUpdateKeys = currentUpdateKeys;
|
|
463
|
+
prevHashes = new Set(entry.nextKeyHashes);
|
|
464
|
+
}
|
|
465
|
+
const final = params[params.length - 1] ?? {
|
|
466
|
+
updateKeys: [],
|
|
467
|
+
nextKeyHashes: []
|
|
468
|
+
};
|
|
469
|
+
return {
|
|
470
|
+
revealedKeys: final.updateKeys.filter(key => ladderKeys.has(key)),
|
|
471
|
+
committedHashes: final.nextKeyHashes.filter(hash => ladderHashes.has(hash))
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=ladder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ladder.js","sourceRoot":"","sources":["../../src/clientAnnex/ladder.ts"],"names":[],"mappings":"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"}
|