@learncard/sss-key-manager 0.2.5 → 0.2.6
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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/sss-key-manager.cjs.development.js +563 -134
- package/dist/sss-key-manager.cjs.development.js.map +3 -3
- package/dist/sss-key-manager.cjs.production.min.js +32 -32
- package/dist/sss-key-manager.cjs.production.min.js.map +3 -3
- package/dist/sss-key-manager.esm.js +563 -134
- package/dist/sss-key-manager.esm.js.map +3 -3
- package/dist/sss-strategy.d.ts.map +1 -1
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +2 -0
- package/src/sss-authority.test.ts +294 -1
- package/src/sss-strategy.test.ts +637 -8
- package/src/sss-strategy.ts +786 -159
- package/src/types.ts +20 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sss-strategy.d.ts","sourceRoot":"","sources":["../src/sss-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACR,wBAAwB,EAaxB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"sss-strategy.d.ts","sourceRoot":"","sources":["../src/sss-strategy.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Re-exports provider-agnostic interfaces from @learncard/types
|
|
5
5
|
* and defines SSS-specific types (recovery shapes, backup files, etc.).
|
|
6
6
|
*/
|
|
7
|
-
export { AuthSessionError, type AuthProviderType, type AuthUser, type AuthProvider, type RecoveryMethodInfo, type RecoveryResult, type IdentityRecoverySession, type ServerKeyStatus, type SssActivationState, type KeyDerivationStrategy, type DidAuthVpSigner, type EscrowEnrollmentOptions, type EscrowEnrollmentState, type EscrowBlobStaleReason, type EscrowPinStatus, } from '@learncard/types';
|
|
7
|
+
export { AuthSessionError, type AuthProviderType, type AuthUser, type AuthProvider, type RecoveryMethodInfo, type RecoveryResult, type RevokeOtherDevicesResult, type IdentityRecoverySession, type ServerKeyStatus, type SssActivationState, type KeyDerivationStrategy, type DidAuthVpSigner, type EscrowEnrollmentOptions, type EscrowEnrollmentState, type EscrowBlobStaleReason, type EscrowPinStatus, } from '@learncard/types';
|
|
8
8
|
import type { AuthProvider, AuthProviderType, KeyDerivationStrategy, RecoveryMethodInfo, SssActivationState } from '@learncard/types';
|
|
9
9
|
export type ContactMethodType = 'email' | 'phone';
|
|
10
10
|
export interface ContactMethod {
|
|
@@ -96,6 +96,14 @@ export declare class EscrowHoldRestartThrottledError extends Error {
|
|
|
96
96
|
export declare class EscrowPinLockedError extends Error {
|
|
97
97
|
constructor();
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* A PIN sign-in released the key, but the released share version is no longer on the
|
|
101
|
+
* server (another device secured the account or recovered meanwhile). Nothing was
|
|
102
|
+
* stored; a revoke is never silently undone.
|
|
103
|
+
*/
|
|
104
|
+
export declare class PinSignInStaleError extends Error {
|
|
105
|
+
constructor();
|
|
106
|
+
}
|
|
99
107
|
export interface PasskeyRecoveryMethod {
|
|
100
108
|
type: 'passkey';
|
|
101
109
|
credentialId?: string;
|
|
@@ -118,6 +126,8 @@ export type RecoveryInput = {
|
|
|
118
126
|
method: 'escrow-pin';
|
|
119
127
|
pin: string;
|
|
120
128
|
expectedDid?: string;
|
|
129
|
+
/** Verified recovery-email session; set only when no login is bound yet. */
|
|
130
|
+
recoverySessionToken?: string;
|
|
121
131
|
} | {
|
|
122
132
|
method: 'escrow';
|
|
123
133
|
holdId: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/sss-key-manager",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Shamir Secret Sharing key manager for LearnCard - replaces Web3Auth SFA",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/sss-key-manager.esm.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"vitest": "4.1.11"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@learncard/types": "5.24.
|
|
42
|
+
"@learncard/types": "5.24.1",
|
|
43
43
|
"@noble/ed25519": "^2.0.0",
|
|
44
44
|
"@peculiar/x509": "^2.1.0",
|
|
45
45
|
"@scure/bip39": "^1.2.1",
|
package/src/index.ts
CHANGED
|
@@ -121,6 +121,7 @@ export {
|
|
|
121
121
|
EscrowPinThrottledError,
|
|
122
122
|
EscrowHoldRestartThrottledError,
|
|
123
123
|
EscrowPinUnavailableError,
|
|
124
|
+
PinSignInStaleError,
|
|
124
125
|
} from './types';
|
|
125
126
|
export {
|
|
126
127
|
PIN_MIN_LENGTH,
|
|
@@ -234,6 +235,7 @@ export type {
|
|
|
234
235
|
RecoveryMethodInfo,
|
|
235
236
|
RecoveryInput,
|
|
236
237
|
RecoveryResult,
|
|
238
|
+
RevokeOtherDevicesResult,
|
|
237
239
|
RecoverySetupInput,
|
|
238
240
|
RecoverySetupResult,
|
|
239
241
|
RecoveryConfirmationInput,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
import type { EscrowEnvelope } from './escrow-crypto';
|
|
21
21
|
import { encodeEscrowEnrollmentIntent, getEscrowBlobIdentity } from './escrow-enrollment-intent';
|
|
22
22
|
import { splitPrivateKey } from './sss';
|
|
23
|
+
import { derivePinProof } from './escrow-pin';
|
|
23
24
|
import type {
|
|
24
25
|
EscrowAuthorityRecord,
|
|
25
26
|
EscrowAuthorityState,
|
|
@@ -151,6 +152,8 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
151
152
|
attemptsUsed: 0,
|
|
152
153
|
};
|
|
153
154
|
let version = 1;
|
|
155
|
+
// lca-api's charged PIN budget: like the server, every PIN release is charged.
|
|
156
|
+
let releaseCharges = 0;
|
|
154
157
|
const recoveryShares = await splitPrivateKey(owner.seedHex);
|
|
155
158
|
let authShare = recoveryShares.authShare;
|
|
156
159
|
let pendingHold: EscrowHoldRecordLike | undefined;
|
|
@@ -180,6 +183,7 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
180
183
|
const calls: string[] = [];
|
|
181
184
|
const nonces: string[] = [];
|
|
182
185
|
const intents: SignedEscrowEnrollmentIntent[] = [];
|
|
186
|
+
const enrollments: Array<{ pinSalt?: unknown; clearPin?: unknown; pinVerifier?: string }> = [];
|
|
183
187
|
const faults: {
|
|
184
188
|
snapshotNonce?: boolean;
|
|
185
189
|
returnedEnvelope?: boolean;
|
|
@@ -199,6 +203,17 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
199
203
|
oldServer?: boolean;
|
|
200
204
|
/** A compromised lca-api forging post-rebind status for a healthy account. */
|
|
201
205
|
forgedRebindStatus?: boolean;
|
|
206
|
+
/** lca-api's verified wrong-entry count for the current PIN, when reported. */
|
|
207
|
+
wrongPinAttempts?: number;
|
|
208
|
+
/**
|
|
209
|
+
* lca-api's escrow copy identity on /keys/auth-share: 'authority' reports the
|
|
210
|
+
* authority's current copy; a string is a stale copy (a lost PIN-operation write).
|
|
211
|
+
*/
|
|
212
|
+
serverBlobIdentity?: 'authority' | string;
|
|
213
|
+
/** lca-api reports a PIN operation holding its lease (`escrowUpdating`). */
|
|
214
|
+
serverUpdating?: boolean;
|
|
215
|
+
/** lca-api refuses the next PIN change (e.g. a concurrent compare): 409. */
|
|
216
|
+
refusePinChange?: boolean;
|
|
202
217
|
} = {};
|
|
203
218
|
const signHold = async (record: EscrowHoldRecordLike): Promise<EscrowHoldRecordLike> => {
|
|
204
219
|
const h = record.hold;
|
|
@@ -265,9 +280,31 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
265
280
|
...(faults.oldServer
|
|
266
281
|
? {}
|
|
267
282
|
: { priorEscrowEnrollment: !enrolled && !!faults.missingPinMetadata }),
|
|
283
|
+
...(faults.serverBlobIdentity === undefined
|
|
284
|
+
? {}
|
|
285
|
+
: {
|
|
286
|
+
escrowBlobIdentity:
|
|
287
|
+
faults.serverBlobIdentity === 'authority'
|
|
288
|
+
? state.envelope &&
|
|
289
|
+
(await getEscrowBlobIdentity(state.envelope))
|
|
290
|
+
: faults.serverBlobIdentity,
|
|
291
|
+
}),
|
|
292
|
+
...(faults.serverUpdating ? { escrowUpdating: true } : {}),
|
|
268
293
|
...(faults.missingPinMetadata
|
|
269
294
|
? { escrowPin: { state: 'none', enabled: false, attemptsRemaining: 0 } }
|
|
270
|
-
:
|
|
295
|
+
: faults.wrongPinAttempts !== undefined
|
|
296
|
+
? {
|
|
297
|
+
escrowPin: {
|
|
298
|
+
state: 'enabled',
|
|
299
|
+
enabled: true,
|
|
300
|
+
attemptsRemaining: Math.max(
|
|
301
|
+
0,
|
|
302
|
+
10 - state.attemptsUsed - releaseCharges
|
|
303
|
+
),
|
|
304
|
+
wrongAttempts: faults.wrongPinAttempts,
|
|
305
|
+
},
|
|
306
|
+
}
|
|
307
|
+
: {}),
|
|
271
308
|
});
|
|
272
309
|
}
|
|
273
310
|
if (path === '/keys/escrow/attestation') return response({ attestation: {} });
|
|
@@ -297,6 +334,13 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
297
334
|
expect(signed.intent.expectedEnrollmentVersion).toBe(state.enrollmentVersion);
|
|
298
335
|
intents.push(signed);
|
|
299
336
|
if (faults.failEnrollment) throw new Error('Temporary enrollment outage');
|
|
337
|
+
if (faults.refusePinChange && signed.intent.pinAction === 'change') {
|
|
338
|
+
faults.refusePinChange = false;
|
|
339
|
+
return response(
|
|
340
|
+
{ message: 'Automatic recovery changed; please refresh.' },
|
|
341
|
+
409
|
|
342
|
+
);
|
|
343
|
+
}
|
|
300
344
|
// Mirror escrow.enroll: rebind removes Mongo metadata, so carry cannot work.
|
|
301
345
|
if (
|
|
302
346
|
faults.missingPinMetadata &&
|
|
@@ -315,6 +359,11 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
315
359
|
parseEscrowEnvelope(body.envelope),
|
|
316
360
|
keys.privateKey
|
|
317
361
|
);
|
|
362
|
+
enrollments.push({
|
|
363
|
+
pinSalt: body.pinSalt,
|
|
364
|
+
clearPin: body.clearPin,
|
|
365
|
+
pinVerifier: plaintext.pinVerifier,
|
|
366
|
+
});
|
|
318
367
|
if (signed.intent.action === 'carry')
|
|
319
368
|
plaintext.pinVerifier = (
|
|
320
369
|
await decryptEscrowBlob(state.envelope!, keys.privateKey)
|
|
@@ -333,6 +382,7 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
333
382
|
pinGeneration: state.pinGeneration + Number(pinChange),
|
|
334
383
|
attemptsUsed: pinChange ? 0 : state.attemptsUsed,
|
|
335
384
|
};
|
|
385
|
+
if (pinChange) releaseCharges = 0;
|
|
336
386
|
enrolled = !!output;
|
|
337
387
|
const receipt = await sign('commit', {
|
|
338
388
|
commit: {
|
|
@@ -413,6 +463,9 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
413
463
|
requestedAt: new Date().toISOString(),
|
|
414
464
|
releaseAfter: new Date(Date.now() + 604800000).toISOString(),
|
|
415
465
|
holdRecord,
|
|
466
|
+
...(body.releasePolicy === 'pin'
|
|
467
|
+
? { pinSalt: 'AAAAAAAAAAAAAAAAAAAAAA==' }
|
|
468
|
+
: {}),
|
|
416
469
|
});
|
|
417
470
|
}
|
|
418
471
|
if (path === '/keys/escrow/status') {
|
|
@@ -494,6 +547,7 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
494
547
|
}
|
|
495
548
|
if (path === '/keys/escrow/complete') {
|
|
496
549
|
if (!pendingHold) throw new Error('No test hold');
|
|
550
|
+
if (body.pinProof !== undefined) releaseCharges++;
|
|
497
551
|
return response({
|
|
498
552
|
sealedShare: await sealEscrowRelease(
|
|
499
553
|
{
|
|
@@ -560,6 +614,7 @@ const setup = async (legacyEnrolled = false) => {
|
|
|
560
614
|
calls,
|
|
561
615
|
nonces,
|
|
562
616
|
intents,
|
|
617
|
+
enrollments,
|
|
563
618
|
faults,
|
|
564
619
|
config,
|
|
565
620
|
identity,
|
|
@@ -616,6 +671,244 @@ describe('Nitro recovery initiating-client bindings and measurement rollover', (
|
|
|
616
671
|
expect(f.state()).toMatchObject({ status: 'active', hasPin: false });
|
|
617
672
|
expect(f.calls.filter(call => call === 'PUT:/keys/auth-share')).toHaveLength(writes);
|
|
618
673
|
});
|
|
674
|
+
it('keeps the account and re-sets the proven PIN with an owner-signed change after a PIN sign-in', async () => {
|
|
675
|
+
const f = await setup();
|
|
676
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
677
|
+
...f.params,
|
|
678
|
+
options: { ...f.params.options, pin: '135790' },
|
|
679
|
+
});
|
|
680
|
+
f.spend();
|
|
681
|
+
// Charged attempts (e.g. earlier successful releases), but no verified wrong entry.
|
|
682
|
+
f.faults.wrongPinAttempts = 0;
|
|
683
|
+
const generation = f.state().pinGeneration;
|
|
684
|
+
const shareVersion = f.intents.at(-1)!.intent.shareVersion;
|
|
685
|
+
const writes = f.calls.filter(call => call === 'PUT:/keys/auth-share').length;
|
|
686
|
+
const recovered = await f.strategy.executeRecovery({
|
|
687
|
+
token: 'token',
|
|
688
|
+
providerType: 'firebase',
|
|
689
|
+
input: { method: 'escrow-pin', pin: '135790', expectedDid: owner.intent.did },
|
|
690
|
+
didFromPrivateKey: async () => owner.intent.did,
|
|
691
|
+
signDidAuthVp: f.params.signDidAuthVp,
|
|
692
|
+
options: f.params.options,
|
|
693
|
+
});
|
|
694
|
+
expect(recovered).toMatchObject({
|
|
695
|
+
escrowEnrolled: true,
|
|
696
|
+
escrowRecovery: true,
|
|
697
|
+
pinSignIn: { mode: 'kept', pinRefreshed: true, newPinRequired: false },
|
|
698
|
+
});
|
|
699
|
+
// Keep mode: no rebind, no re-split; the re-set targets the released version.
|
|
700
|
+
expect(f.calls).not.toContain('POST:/keys/recovery-session/rebind');
|
|
701
|
+
expect(f.calls.filter(call => call === 'PUT:/keys/auth-share')).toHaveLength(writes);
|
|
702
|
+
expect(f.intents.at(-1)?.intent).toMatchObject({
|
|
703
|
+
shareVersion,
|
|
704
|
+
action: 'replace',
|
|
705
|
+
pinAction: 'change',
|
|
706
|
+
recoveryEmail: 'owner@example.com',
|
|
707
|
+
recoveryLanguage: 'en',
|
|
708
|
+
});
|
|
709
|
+
expect(f.state()).toMatchObject({
|
|
710
|
+
status: 'active',
|
|
711
|
+
hasPin: true,
|
|
712
|
+
pinGeneration: generation + 1,
|
|
713
|
+
attemptsUsed: 0,
|
|
714
|
+
});
|
|
715
|
+
const enrollment = f.enrollments.at(-1)!;
|
|
716
|
+
expect(enrollment.clearPin).toBeUndefined();
|
|
717
|
+
expect(typeof enrollment.pinSalt).toBe('string');
|
|
718
|
+
expect(enrollment.pinVerifier).toBe(
|
|
719
|
+
await derivePinProof('135790', String(enrollment.pinSalt))
|
|
720
|
+
);
|
|
721
|
+
// The repair intent is not needed: a current PIN enrollment needs no change.
|
|
722
|
+
const intents = f.intents.length;
|
|
723
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toMatchObject({
|
|
724
|
+
enrolled: true,
|
|
725
|
+
changed: false,
|
|
726
|
+
});
|
|
727
|
+
expect(f.intents).toHaveLength(intents);
|
|
728
|
+
});
|
|
729
|
+
it('signs no PIN change after a PIN sign-in when wrong entries are recorded', async () => {
|
|
730
|
+
const f = await setup();
|
|
731
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
732
|
+
...f.params,
|
|
733
|
+
options: { ...f.params.options, pin: '135790' },
|
|
734
|
+
});
|
|
735
|
+
f.spend();
|
|
736
|
+
f.faults.wrongPinAttempts = 2;
|
|
737
|
+
const before = { ...f.state() };
|
|
738
|
+
const intents = f.intents.length;
|
|
739
|
+
await expect(
|
|
740
|
+
f.strategy.executeRecovery({
|
|
741
|
+
token: 'token',
|
|
742
|
+
providerType: 'firebase',
|
|
743
|
+
input: { method: 'escrow-pin', pin: '135790', expectedDid: owner.intent.did },
|
|
744
|
+
didFromPrivateKey: async () => owner.intent.did,
|
|
745
|
+
signDidAuthVp: f.params.signDidAuthVp,
|
|
746
|
+
options: f.params.options,
|
|
747
|
+
})
|
|
748
|
+
).resolves.toMatchObject({
|
|
749
|
+
escrowEnrolled: true,
|
|
750
|
+
pinSignIn: { mode: 'kept', pinRefreshed: false, newPinRequired: true },
|
|
751
|
+
});
|
|
752
|
+
// No owner-signed change: same PIN generation, the used attempts stand.
|
|
753
|
+
expect(f.intents).toHaveLength(intents);
|
|
754
|
+
expect(f.state()).toMatchObject({
|
|
755
|
+
pinGeneration: before.pinGeneration,
|
|
756
|
+
attemptsUsed: before.attemptsUsed,
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
it('reports a refused same-PIN refresh instead of a silent success, and keeps the PIN', async () => {
|
|
760
|
+
const f = await setup();
|
|
761
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
762
|
+
...f.params,
|
|
763
|
+
options: { ...f.params.options, pin: '135790' },
|
|
764
|
+
});
|
|
765
|
+
f.spend();
|
|
766
|
+
f.faults.wrongPinAttempts = 0;
|
|
767
|
+
f.faults.refusePinChange = true;
|
|
768
|
+
const before = { ...f.state() };
|
|
769
|
+
const recovered = await f.strategy.executeRecovery({
|
|
770
|
+
token: 'token',
|
|
771
|
+
providerType: 'firebase',
|
|
772
|
+
input: { method: 'escrow-pin', pin: '135790', expectedDid: owner.intent.did },
|
|
773
|
+
didFromPrivateKey: async () => owner.intent.did,
|
|
774
|
+
signDidAuthVp: f.params.signDidAuthVp,
|
|
775
|
+
options: f.params.options,
|
|
776
|
+
});
|
|
777
|
+
expect(recovered).toMatchObject({
|
|
778
|
+
escrowEnrolled: true,
|
|
779
|
+
pinSignIn: {
|
|
780
|
+
mode: 'kept',
|
|
781
|
+
pinRefreshed: false,
|
|
782
|
+
newPinRequired: false,
|
|
783
|
+
pinRefreshFailed: true,
|
|
784
|
+
},
|
|
785
|
+
});
|
|
786
|
+
// Nothing committed: the PIN keeps its generation and reduced budget.
|
|
787
|
+
expect(f.state()).toMatchObject({
|
|
788
|
+
pinGeneration: before.pinGeneration,
|
|
789
|
+
attemptsUsed: before.attemptsUsed,
|
|
790
|
+
});
|
|
791
|
+
});
|
|
792
|
+
it('does not report a refresh failure when none was attempted', async () => {
|
|
793
|
+
const f = await setup();
|
|
794
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
795
|
+
...f.params,
|
|
796
|
+
options: { ...f.params.options, pin: '135790' },
|
|
797
|
+
});
|
|
798
|
+
f.spend();
|
|
799
|
+
f.faults.wrongPinAttempts = 2;
|
|
800
|
+
const recovered = await f.strategy.executeRecovery({
|
|
801
|
+
token: 'token',
|
|
802
|
+
providerType: 'firebase',
|
|
803
|
+
input: { method: 'escrow-pin', pin: '135790', expectedDid: owner.intent.did },
|
|
804
|
+
didFromPrivateKey: async () => owner.intent.did,
|
|
805
|
+
signDidAuthVp: f.params.signDidAuthVp,
|
|
806
|
+
options: f.params.options,
|
|
807
|
+
});
|
|
808
|
+
expect(recovered.pinSignIn).not.toHaveProperty('pinRefreshFailed');
|
|
809
|
+
});
|
|
810
|
+
it('treats an enrollment whose lca-api copy differs from the authority as unenrolled', async () => {
|
|
811
|
+
const f = await setup();
|
|
812
|
+
await f.strategy.ensureEscrowEnrollment!(f.params);
|
|
813
|
+
expect(f.state()).toMatchObject({ status: 'active', hasPin: false });
|
|
814
|
+
f.faults.serverBlobIdentity = 'authority';
|
|
815
|
+
const intents = f.intents.length;
|
|
816
|
+
// Matching copies: nothing to repair.
|
|
817
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toMatchObject({
|
|
818
|
+
enrolled: true,
|
|
819
|
+
changed: false,
|
|
820
|
+
});
|
|
821
|
+
expect(f.intents).toHaveLength(intents);
|
|
822
|
+
// lca-api kept an old copy the authority already replaced: re-enroll (no PIN).
|
|
823
|
+
f.faults.serverBlobIdentity = 'ef'.repeat(32);
|
|
824
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toMatchObject({
|
|
825
|
+
enrolled: true,
|
|
826
|
+
changed: true,
|
|
827
|
+
});
|
|
828
|
+
expect(f.intents.length).toBeGreaterThan(intents);
|
|
829
|
+
});
|
|
830
|
+
it('leaves a mismatch alone while lca-api reports a PIN operation in progress', async () => {
|
|
831
|
+
const f = await setup();
|
|
832
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
833
|
+
...f.params,
|
|
834
|
+
options: { ...f.params.options, pin: '135790' },
|
|
835
|
+
});
|
|
836
|
+
// Authority committed, lca-api's lease-held write not landed yet.
|
|
837
|
+
f.faults.serverBlobIdentity = 'ef'.repeat(32);
|
|
838
|
+
f.faults.serverUpdating = true;
|
|
839
|
+
const intents = f.intents.length;
|
|
840
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toEqual({
|
|
841
|
+
enrolled: true,
|
|
842
|
+
changed: false,
|
|
843
|
+
});
|
|
844
|
+
expect(f.intents).toHaveLength(intents);
|
|
845
|
+
// Once the lease is gone, a copy still left behind is a real mismatch.
|
|
846
|
+
f.faults.serverUpdating = false;
|
|
847
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toEqual({
|
|
848
|
+
enrolled: false,
|
|
849
|
+
reason: 'pin-reset-required',
|
|
850
|
+
});
|
|
851
|
+
});
|
|
852
|
+
it('asks for a new PIN, signing nothing, when a PIN-protected copy is stale in lca-api', async () => {
|
|
853
|
+
const f = await setup();
|
|
854
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
855
|
+
...f.params,
|
|
856
|
+
options: { ...f.params.options, pin: '135790' },
|
|
857
|
+
});
|
|
858
|
+
f.faults.serverBlobIdentity = 'ef'.repeat(32);
|
|
859
|
+
const intents = f.intents.length;
|
|
860
|
+
const writes = f.calls.filter(call => call === 'PUT:/keys/auth-share').length;
|
|
861
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toEqual({
|
|
862
|
+
enrolled: false,
|
|
863
|
+
reason: 'pin-reset-required',
|
|
864
|
+
});
|
|
865
|
+
expect(f.intents).toHaveLength(intents);
|
|
866
|
+
expect(f.calls.filter(call => call === 'PUT:/keys/auth-share')).toHaveLength(writes);
|
|
867
|
+
// A user-chosen PIN repairs it.
|
|
868
|
+
await f.strategy.setEscrowPin!({ ...f.params, pin: '246813' });
|
|
869
|
+
expect(f.intents.at(-1)?.intent).toMatchObject({ pinAction: 'change' });
|
|
870
|
+
});
|
|
871
|
+
it('re-reads lca-api before repairing, so a copy settled by the state read is not re-enrolled', async () => {
|
|
872
|
+
const f = await setup();
|
|
873
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
874
|
+
...f.params,
|
|
875
|
+
options: { ...f.params.options, pin: '135790' },
|
|
876
|
+
});
|
|
877
|
+
f.faults.serverBlobIdentity = 'ef'.repeat(32);
|
|
878
|
+
// lca-api adopts the authority's copy while serving the authority-state read.
|
|
879
|
+
f.faults.beforeSnapshot = async () => {
|
|
880
|
+
f.faults.serverBlobIdentity = 'authority';
|
|
881
|
+
};
|
|
882
|
+
const intents = f.intents.length;
|
|
883
|
+
await expect(f.strategy.ensureEscrowEnrollment!(f.params)).resolves.toMatchObject({
|
|
884
|
+
enrolled: true,
|
|
885
|
+
changed: false,
|
|
886
|
+
});
|
|
887
|
+
expect(f.intents).toHaveLength(intents);
|
|
888
|
+
});
|
|
889
|
+
it('still clears the PIN after a hold recovery', async () => {
|
|
890
|
+
const f = await setup();
|
|
891
|
+
await f.strategy.ensureEscrowEnrollment!({
|
|
892
|
+
...f.params,
|
|
893
|
+
options: { ...f.params.options, pin: '135790' },
|
|
894
|
+
});
|
|
895
|
+
const hold = await f.strategy.startEscrowRecovery!({
|
|
896
|
+
recoverySessionToken: 'otp-session',
|
|
897
|
+
expectedDid: owner.intent.did,
|
|
898
|
+
});
|
|
899
|
+
await f.strategy.executeRecovery({
|
|
900
|
+
token: '',
|
|
901
|
+
providerType: 'firebase',
|
|
902
|
+
input: { method: 'escrow', ...hold, resumeToken: hold.resumeToken! },
|
|
903
|
+
didFromPrivateKey: async () => owner.intent.did,
|
|
904
|
+
});
|
|
905
|
+
await expect(f.strategy.completeIdentityRecovery!(f.params)).resolves.toMatchObject({
|
|
906
|
+
escrowEnrolled: true,
|
|
907
|
+
});
|
|
908
|
+
expect(f.intents.at(-1)?.intent).toMatchObject({ action: 'replace', pinAction: 'clear' });
|
|
909
|
+
expect(f.enrollments.at(-1)?.pinSalt).toBeUndefined();
|
|
910
|
+
expect(f.state()).toMatchObject({ status: 'active', hasPin: false });
|
|
911
|
+
});
|
|
619
912
|
it('treats a successful recovered clear-PIN enrollment as satisfying the repair', async () => {
|
|
620
913
|
const f = await setup();
|
|
621
914
|
await f.strategy.ensureEscrowEnrollment!({
|