@learncard/sss-key-manager 0.1.20 → 0.1.21

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.
Files changed (38) hide show
  1. package/dist/api-client.d.ts +49 -0
  2. package/dist/api-client.d.ts.map +1 -0
  3. package/dist/atomic-operations.d.ts +101 -0
  4. package/dist/atomic-operations.d.ts.map +1 -0
  5. package/dist/auth-coordinator.d.ts +10 -0
  6. package/dist/auth-coordinator.d.ts.map +1 -0
  7. package/dist/crypto.d.ts +31 -0
  8. package/dist/crypto.d.ts.map +1 -0
  9. package/dist/index.d.ts +26 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/key-manager.d.ts +30 -0
  12. package/dist/key-manager.d.ts.map +1 -0
  13. package/dist/passkey.d.ts +23 -0
  14. package/dist/passkey.d.ts.map +1 -0
  15. package/dist/qr-crypto.d.ts +56 -0
  16. package/dist/qr-crypto.d.ts.map +1 -0
  17. package/dist/qr-login.d.ts +122 -0
  18. package/dist/qr-login.d.ts.map +1 -0
  19. package/dist/recovery-phrase.d.ts +14 -0
  20. package/dist/recovery-phrase.d.ts.map +1 -0
  21. package/dist/sss-key-manager.cjs.development.js +3 -2
  22. package/dist/sss-key-manager.cjs.development.js.map +2 -2
  23. package/dist/sss-key-manager.cjs.production.min.js +6 -6
  24. package/dist/sss-key-manager.cjs.production.min.js.map +3 -3
  25. package/dist/sss-key-manager.esm.js +3 -2
  26. package/dist/sss-key-manager.esm.js.map +2 -2
  27. package/dist/sss-strategy.d.ts +82 -0
  28. package/dist/sss-strategy.d.ts.map +1 -0
  29. package/dist/sss.d.ts +15 -0
  30. package/dist/sss.d.ts.map +1 -0
  31. package/dist/storage.d.ts +46 -0
  32. package/dist/storage.d.ts.map +1 -0
  33. package/dist/types.d.ts +155 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/package.json +5 -6
  36. package/src/crypto.ts +9 -14
  37. package/src/recovery-phrase.ts +7 -4
  38. package/dist/sss-key-manager.d.ts +0 -898
@@ -1,898 +0,0 @@
1
- // Generated by dts-bundle-generator v6.13.0
2
-
3
- /**
4
- * Provider-agnostic interfaces for authentication and key derivation.
5
- *
6
- * Both @learncard/sss-key-manager and learn-card-base import from here,
7
- * ensuring a single canonical source for abstract interfaces without
8
- * coupling consumers to any specific implementation.
9
- */
10
- /**
11
- * Typed error for auth session issues.
12
- * Auth providers should throw this (instead of generic Error) when the
13
- * session is expired, revoked, or missing so the coordinator can
14
- * distinguish "not logged in" from "unexpected failure".
15
- */
16
- export declare class AuthSessionError extends Error {
17
- readonly reason: "expired" | "no_session" | "revoked" | "network";
18
- constructor(message: string, reason: "expired" | "no_session" | "revoked" | "network");
19
- }
20
- /**
21
- * Auth provider identifier. Known values: 'firebase', 'supertokens', 'keycloak', 'oidc'.
22
- * Use any string to support custom auth providers without modifying this type.
23
- */
24
- export type AuthProviderType = string;
25
- export interface AuthUser {
26
- id: string;
27
- email?: string;
28
- phone?: string;
29
- displayName?: string;
30
- photoUrl?: string;
31
- providerType: AuthProviderType;
32
- /** Account creation timestamp (when available from the auth provider) */
33
- createdAt?: Date;
34
- }
35
- /**
36
- * Abstract auth provider interface.
37
- * Implementations wrap a specific auth SDK (Firebase, Supertokens, etc.)
38
- * and expose a uniform API to the coordinator.
39
- */
40
- export interface AuthProvider {
41
- getIdToken(forceRefresh?: boolean): Promise<string>;
42
- getCurrentUser(): Promise<AuthUser | null>;
43
- getProviderType(): AuthProviderType;
44
- signOut(): Promise<void>;
45
- /**
46
- * Attempt to silently refresh the auth session (e.g., force-refresh
47
- * the JWT using the underlying refresh token).
48
- *
49
- * Returns `true` if the session was successfully refreshed.
50
- * Returns `false` if a full re-authentication is required.
51
- *
52
- * Optional — providers that don't implement this will require full
53
- * re-auth whenever the session expires.
54
- */
55
- refreshSession?(): Promise<boolean>;
56
- /**
57
- * Re-authenticate with a server-issued token (e.g., a Firebase custom
58
- * token returned after a server-side account change that invalidates
59
- * the current session).
60
- *
61
- * Returns the refreshed AuthUser read directly from the auth SDK
62
- * (not from the app store, which may be stale).
63
- *
64
- * Optional — only needed by providers whose server-side account
65
- * mutations invalidate the client session.
66
- */
67
- reauthenticateWithToken?(token: string): Promise<AuthUser | null>;
68
- }
69
- /**
70
- * Recovery method metadata returned by the server.
71
- * The `type` is a string so strategies can define their own method types
72
- * without modifying this interface.
73
- */
74
- export interface RecoveryMethodInfo {
75
- type: string;
76
- createdAt: Date;
77
- credentialId?: string;
78
- }
79
- /**
80
- * Generic result of a successful recovery execution.
81
- * All strategies must produce a private key + DID.
82
- */
83
- export interface RecoveryResult {
84
- privateKey: string;
85
- did: string;
86
- }
87
- /**
88
- * Server key status returned by the strategy's fetchServerKeyStatus.
89
- * The strategy owns the server shape — different strategies may
90
- * have fundamentally different server payloads.
91
- */
92
- export interface ServerKeyStatus {
93
- exists: boolean;
94
- needsMigration: boolean;
95
- primaryDid: string | null;
96
- recoveryMethods: RecoveryMethodInfo[];
97
- authShare: string | null;
98
- shareVersion: number | null;
99
- maskedRecoveryEmail?: string | null;
100
- }
101
- /**
102
- * Declarative capability flags for a key derivation strategy.
103
- *
104
- * UI components read these to decide which features to show.
105
- * Each strategy declares its own capabilities — no strategy-specific
106
- * checks needed in the UI layer.
107
- *
108
- * All flags default to `false` when absent.
109
- *
110
- * @example
111
- * ```ts
112
- * // SSS declares full capabilities:
113
- * capabilities: { recovery: true, deviceLinking: true, localKeyPersistence: true }
114
- *
115
- * // Web3Auth derives keys on-demand, nothing local to manage:
116
- * capabilities: { recovery: false, deviceLinking: false, localKeyPersistence: false }
117
- * ```
118
- */
119
- export interface KeyDerivationCapabilities {
120
- /** Strategy supports user-facing recovery methods (setup + execution) */
121
- recovery: boolean;
122
- /** Strategy supports cross-device key transfer (e.g., QR-based device linking) */
123
- deviceLinking: boolean;
124
- /**
125
- * Strategy persists key material locally (e.g., device share in IndexedDB).
126
- * When true, "public computer" / "forget device" features are relevant.
127
- */
128
- localKeyPersistence: boolean;
129
- /**
130
- * Strategy supports upgrading the user's contact method (e.g., phone → email).
131
- * When true, the `upgradeContactMethod` method is available and the
132
- * email-linking gate can be shown for phone-only users.
133
- */
134
- contactMethodUpgrade: boolean;
135
- }
136
- /**
137
- * Key Derivation Strategy
138
- *
139
- * Abstract interface for different key derivation implementations.
140
- * Used by AuthCoordinator to delegate key operations.
141
- *
142
- * The strategy owns:
143
- * - Local key storage
144
- * - Key splitting and reconstruction
145
- * - Server communication for remote key components
146
- * - Recovery method execution and setup
147
- * - Storage cleanup knowledge
148
- *
149
- * Type parameters allow each strategy to define its own recovery shapes:
150
- * - TRecoveryInput: what the user provides to recover (e.g., password, passkey)
151
- * - TRecoverySetupInput: what the user provides to set up a recovery method
152
- * - TRecoverySetupResult: what setup returns (e.g., generated phrase, credential ID)
153
- *
154
- * @example
155
- * // SSS strategy with specific recovery types:
156
- * type SSSStrategy = KeyDerivationStrategy<SSSRecoveryInput, SSSRecoverySetupInput, SSSRecoverySetupResult>;
157
- *
158
- * // Simple strategy with no recovery:
159
- * type SimpleStrategy = KeyDerivationStrategy<never, never, never>;
160
- */
161
- export interface KeyDerivationStrategy<TRecoveryInput = unknown, TRecoverySetupInput = unknown, TRecoverySetupResult = unknown> {
162
- readonly name: string;
163
- /** Declarative feature flags — UI reads these to gate features */
164
- readonly capabilities: KeyDerivationCapabilities;
165
- /** Check if there's a local key component (e.g., device share) */
166
- hasLocalKey(): Promise<boolean>;
167
- /** Get the local key component */
168
- getLocalKey(): Promise<string | null>;
169
- /** Store a local key component */
170
- storeLocalKey(key: string): Promise<void>;
171
- /** Clear all local key data */
172
- clearLocalKeys(): Promise<void>;
173
- /** Split a private key into shares/components */
174
- splitKey(privateKey: string): Promise<{
175
- localKey: string;
176
- remoteKey: string;
177
- }>;
178
- /** Reconstruct private key from components */
179
- reconstructKey(localKey: string, remoteKey: string): Promise<string>;
180
- /** Verify that stored keys can reconstruct the expected DID */
181
- verifyKeys?(localKey: string, remoteKey: string, expectedDid: string, didFromPrivateKey: (pk: string) => Promise<string>): Promise<boolean>;
182
- /** Fetch the server-side key status for the authenticated user */
183
- fetchServerKeyStatus(token: string, providerType: AuthProviderType): Promise<ServerKeyStatus>;
184
- /** Store the remote key component on the server */
185
- storeAuthShare(token: string, providerType: AuthProviderType, remoteKey: string, did: string, didAuthVp?: string): Promise<void>;
186
- /** Mark migration complete on the server (optional — only needed for migration-capable strategies) */
187
- markMigrated?(token: string, providerType: AuthProviderType, didAuthVp?: string): Promise<void>;
188
- /** Execute a recovery flow and return the recovered private key + DID */
189
- executeRecovery(params: {
190
- token: string;
191
- providerType: AuthProviderType;
192
- input: TRecoveryInput;
193
- /** Optional: validate the reconstructed key's DID before rotating shares */
194
- didFromPrivateKey?: (privateKey: string) => Promise<string>;
195
- }): Promise<RecoveryResult>;
196
- /** Set up a new recovery method */
197
- setupRecoveryMethod?(params: {
198
- token: string;
199
- providerType: AuthProviderType;
200
- privateKey: string;
201
- input: TRecoverySetupInput;
202
- authUser?: AuthUser;
203
- /** Optional: sign a DID-Auth VP JWT for server write operations */
204
- signDidAuthVp?: (privateKey: string) => Promise<string>;
205
- }): Promise<TRecoverySetupResult>;
206
- /** Get configured recovery methods for the authenticated user */
207
- getAvailableRecoveryMethods?(token: string, providerType: AuthProviderType): Promise<RecoveryMethodInfo[]>;
208
- /**
209
- * Verify email ownership and upgrade the user's contact method on the
210
- * server (e.g., phone → email). The server verifies the OTP code, links
211
- * the email to the auth account (passwordless), and atomically updates
212
- * the UserKey contact method.
213
- *
214
- * Strategies that don't manage server-side contact methods can omit this.
215
- *
216
- * @param token - Auth token for the current session
217
- * @param providerType - Auth provider type
218
- * @param previousPhone - The phone number being replaced
219
- * @param email - The new email address (already OTP-verified client-side)
220
- * @param code - The 6-digit verification code
221
- */
222
- upgradeContactMethod?(token: string, providerType: AuthProviderType, previousPhone: string, email: string, code: string): Promise<{
223
- customToken?: string;
224
- } | void>;
225
- /**
226
- * Send a backup share to the user's email for fail-safe recovery.
227
- * Called by the coordinator after key setup or migration.
228
- * Implementation should be fire-and-forget (non-fatal on failure).
229
- *
230
- * @param token - Auth token for server communication
231
- * @param providerType - Auth provider type
232
- * @param privateKey - The private key to derive the email share from
233
- * @param email - Destination email address
234
- */
235
- sendEmailBackupShare?(token: string, providerType: AuthProviderType, privateKey: string, email: string): Promise<void>;
236
- /**
237
- * Get the share version associated with the local device share.
238
- * Used to request the matching auth share from the server and to
239
- * include in QR cross-device transfers.
240
- *
241
- * Returns null for legacy shares with no stored version.
242
- */
243
- getLocalShareVersion?(): Promise<number | null>;
244
- /**
245
- * Store the share version for the local device share.
246
- * Called after receiving a device share + version via QR transfer.
247
- */
248
- storeLocalShareVersion?(version: number): Promise<void>;
249
- /**
250
- * Inform the strategy which user is active so it can scope local storage
251
- * (e.g., device shares) per-user. Called by the coordinator after
252
- * authentication, before any local-key operations.
253
- *
254
- * Strategies that don't need per-user scoping can omit this method.
255
- *
256
- * @param userId - Stable, unique identifier for the authenticated user
257
- * (e.g., Firebase UID). Must NOT change across sessions.
258
- */
259
- setActiveUser?(userId: string): void;
260
- /** Return storage keys (e.g., IndexedDB database names) that should be preserved during logout */
261
- getPreservedStorageKeys(): string[];
262
- /** Strategy-specific cleanup beyond clearLocalKeys (optional) */
263
- cleanup?(): Promise<void>;
264
- }
265
- export type ContactMethodType = "email" | "phone";
266
- export interface ContactMethod {
267
- type: ContactMethodType;
268
- value: string;
269
- }
270
- export interface AuthProviderMapping {
271
- type: AuthProviderType;
272
- id: string;
273
- }
274
- export type SecurityLevel = "basic" | "enhanced" | "advanced";
275
- /**
276
- * SSS recovery method type identifiers.
277
- * These are the specific recovery methods supported by the SSS strategy.
278
- */
279
- export type RecoveryMethodType = "passkey" | "backup" | "phrase" | "email";
280
- export interface PasskeyRecoveryMethod {
281
- type: "passkey";
282
- credentialId?: string;
283
- }
284
- export interface BackupFileRecoveryMethod {
285
- type: "backup";
286
- fileContents: string;
287
- password: string;
288
- }
289
- export interface RecoveryPhraseRecoveryMethod {
290
- type: "phrase";
291
- phrase: string;
292
- }
293
- /** @deprecated Use RecoveryInput instead. Kept for legacy SSSKeyManager class. */
294
- export type RecoveryMethod = PasskeyRecoveryMethod | BackupFileRecoveryMethod | RecoveryPhraseRecoveryMethod;
295
- /**
296
- * SSS-specific recovery input — what the user provides to recover their key.
297
- */
298
- export type RecoveryInput = {
299
- method: "passkey";
300
- credentialId: string;
301
- } | {
302
- method: "phrase";
303
- phrase: string;
304
- } | {
305
- method: "backup";
306
- fileContents: string;
307
- password: string;
308
- } | {
309
- method: "email";
310
- emailShare: string;
311
- };
312
- /**
313
- * SSS-specific recovery setup input — what the user provides to set up a method.
314
- */
315
- export type RecoverySetupInput = {
316
- method: "passkey";
317
- } | {
318
- method: "phrase";
319
- } | {
320
- method: "backup";
321
- password: string;
322
- did: string;
323
- } | {
324
- method: "email";
325
- };
326
- /**
327
- * SSS-specific recovery setup result.
328
- */
329
- export type RecoverySetupResult = {
330
- method: "passkey";
331
- credentialId: string;
332
- } | {
333
- method: "phrase";
334
- phrase: string;
335
- } | {
336
- method: "backup";
337
- backupFile: BackupFile;
338
- } | {
339
- method: "email";
340
- };
341
- export interface EncryptedShare {
342
- encryptedData: string;
343
- iv: string;
344
- salt?: string;
345
- }
346
- export interface ServerEncryptedShare {
347
- encryptedData: string;
348
- encryptedDek: string;
349
- iv: string;
350
- }
351
- export interface UserKeyRecord {
352
- contactMethod: ContactMethod;
353
- authProviders: AuthProviderMapping[];
354
- primaryDid: string;
355
- linkedDids: string[];
356
- keyProvider: "web3auth" | "sss";
357
- authShare?: ServerEncryptedShare;
358
- securityLevel: SecurityLevel;
359
- recoveryMethods: RecoveryMethodInfo[];
360
- migratedFromWeb3Auth: boolean;
361
- migratedAt?: Date;
362
- createdAt: Date;
363
- updatedAt: Date;
364
- }
365
- export interface BackupFile {
366
- version: 1;
367
- createdAt: string;
368
- primaryDid: string;
369
- shareVersion?: number;
370
- encryptedShare: {
371
- ciphertext: string;
372
- iv: string;
373
- salt: string;
374
- kdfParams: {
375
- algorithm: "argon2id";
376
- timeCost: number;
377
- memoryCost: number;
378
- parallelism: number;
379
- };
380
- };
381
- }
382
- export interface SSSKeyManagerConfig {
383
- serverUrl: string;
384
- authProvider: AuthProvider;
385
- deviceStorageKey?: string;
386
- }
387
- /**
388
- * The SSS key derivation strategy — a KeyDerivationStrategy narrowed
389
- * with SSS-specific recovery input/output types.
390
- */
391
- export type SSSKeyDerivationStrategy = KeyDerivationStrategy<RecoveryInput, RecoverySetupInput, RecoverySetupResult>;
392
- /** @deprecated Use KeyDerivationStrategy instead. Kept for legacy SSSKeyManager class. */
393
- export interface KeyDerivationProvider {
394
- readonly name: string;
395
- connect(): Promise<string>;
396
- disconnect(): Promise<void>;
397
- isInitialized(): boolean;
398
- hasLocalKey(): Promise<boolean>;
399
- canMigrate?(): Promise<boolean>;
400
- migrate?(privateKey: string): Promise<void>;
401
- }
402
- /** @deprecated Use KeyDerivationStrategy instead. Kept for legacy SSSKeyManager class. */
403
- export interface SSSKeyDerivationProvider extends KeyDerivationProvider {
404
- addRecoveryMethod(method: RecoveryMethod): Promise<void>;
405
- getRecoveryMethods(): Promise<RecoveryMethodInfo[]>;
406
- recover(method: RecoveryMethod): Promise<string>;
407
- getSecurityLevel(): Promise<SecurityLevel>;
408
- exportBackup(password: string): Promise<BackupFile>;
409
- }
410
- export declare class SSSKeyManager implements SSSKeyDerivationProvider {
411
- readonly name = "sss";
412
- private config;
413
- private apiClient;
414
- private initialized;
415
- private currentPrivateKey;
416
- constructor(config: SSSKeyManagerConfig);
417
- isInitialized(): boolean;
418
- hasLocalKey(): Promise<boolean>;
419
- connect(): Promise<string>;
420
- disconnect(): Promise<void>;
421
- setupNewKey(): Promise<string>;
422
- setupWithKey(privateKey: string, primaryDid?: string): Promise<void>;
423
- migrate(privateKey: string): Promise<void>;
424
- canMigrate(): Promise<boolean>;
425
- addRecoveryMethod(method: RecoveryMethod): Promise<void>;
426
- generateRecoveryPhrase(): Promise<string>;
427
- getRecoveryMethods(): Promise<RecoveryMethodInfo[]>;
428
- recover(method: RecoveryMethod): Promise<string>;
429
- getSecurityLevel(): Promise<SecurityLevel>;
430
- exportBackup(password: string): Promise<BackupFile>;
431
- clearLocalData(): Promise<void>;
432
- deleteAccount(): Promise<void>;
433
- }
434
- export declare function createSSSKeyManager(config: SSSKeyManagerConfig): SSSKeyManager;
435
- export interface GetAuthShareResponse {
436
- authShare: ServerEncryptedShare | null;
437
- primaryDid: string | null;
438
- securityLevel: SecurityLevel;
439
- recoveryMethods: RecoveryMethodInfo[];
440
- keyProvider: "web3auth" | "sss";
441
- maskedRecoveryEmail?: string | null;
442
- }
443
- export interface StoreAuthShareInput {
444
- authShare: ServerEncryptedShare;
445
- primaryDid: string;
446
- securityLevel?: SecurityLevel;
447
- }
448
- export interface StoreRecoveryShareInput {
449
- type: "passkey" | "backup" | "phrase" | "email";
450
- encryptedShare?: EncryptedShare;
451
- credentialId?: string;
452
- shareVersion?: number;
453
- }
454
- export interface ApiClientConfig {
455
- serverUrl: string;
456
- authProvider: AuthProvider;
457
- }
458
- export declare class SSSApiClient {
459
- private serverUrl;
460
- private authProvider;
461
- constructor(config: ApiClientConfig);
462
- private getAuthHeaders;
463
- private getContactMethodFromUser;
464
- getAuthShare(): Promise<GetAuthShareResponse | null>;
465
- storeAuthShare(input: StoreAuthShareInput): Promise<void>;
466
- addRecoveryMethod(input: StoreRecoveryShareInput): Promise<void>;
467
- getRecoveryShare(type: "passkey" | "backup" | "phrase" | "email", credentialId?: string): Promise<{
468
- encryptedShare?: EncryptedShare;
469
- shareVersion?: number;
470
- } | null>;
471
- markMigrated(): Promise<void>;
472
- sendEmailBackupShare(emailShare: string, overrideEmail?: string): Promise<void>;
473
- addRecoveryEmail(email: string): Promise<void>;
474
- verifyRecoveryEmail(code: string): Promise<{
475
- maskedEmail: string;
476
- }>;
477
- deleteUserKey(): Promise<void>;
478
- }
479
- /**
480
- * Shamir Secret Sharing operations
481
- */
482
- export declare const SSS_TOTAL_SHARES = 4;
483
- export declare const SSS_THRESHOLD = 2;
484
- export interface SSSShares {
485
- deviceShare: string;
486
- authShare: string;
487
- recoveryShare: string;
488
- emailShare: string;
489
- }
490
- export declare function splitPrivateKey(privateKeyHex: string): Promise<SSSShares>;
491
- export declare function reconstructPrivateKey(share1Hex: string, share2Hex: string): Promise<string>;
492
- export declare function reconstructFromShares(shares: string[]): Promise<string>;
493
- /**
494
- * Device-side storage for SSS shares
495
- * Reuses patterns from webSecureStorage but specialized for SSS
496
- */
497
- export declare function storeDeviceShare(share: string, id?: string): Promise<void>;
498
- /**
499
- * Store the share version alongside a device share.
500
- * Stored as a separate key `{id}:version` to avoid changing the encryption format.
501
- */
502
- export declare function storeShareVersion(version: number, id?: string): Promise<void>;
503
- /**
504
- * Retrieve the share version for a device share.
505
- * Returns null if no version is stored (legacy shares).
506
- */
507
- export declare function getShareVersion(id?: string): Promise<number | null>;
508
- export declare function getDeviceShare(id?: string): Promise<string | null>;
509
- export declare function hasDeviceShare(id?: string): Promise<boolean>;
510
- export declare function deleteDeviceShare(id?: string): Promise<void>;
511
- export interface DeviceShareEntry {
512
- id: string;
513
- preview: string;
514
- shareVersion?: number;
515
- }
516
- /**
517
- * List all device shares stored in IndexedDB.
518
- * Returns the storage key and a truncated preview for each share.
519
- * Useful for debugging multi-account storage.
520
- */
521
- export declare function listAllDeviceShares(): Promise<DeviceShareEntry[]>;
522
- export declare function isPublicComputerMode(): boolean;
523
- export declare function setPublicComputerMode(enabled: boolean): void;
524
- /**
525
- * Create storage functions that dynamically route to sessionStorage (public
526
- * computer mode) or IndexedDB (normal mode) based on the `lc-session-mode`
527
- * flag in sessionStorage. Checked at call time, not at creation time.
528
- */
529
- export declare function createAdaptiveStorage(): {
530
- storeDeviceShare: (share: string, id?: string) => Promise<void>;
531
- getDeviceShare: (id?: string) => Promise<string | null>;
532
- hasDeviceShare: (id?: string) => Promise<boolean>;
533
- clearAllShares: (id?: string) => Promise<void>;
534
- storeShareVersion: (version: number, id?: string) => Promise<void>;
535
- getShareVersion: (id?: string) => Promise<number | null>;
536
- };
537
- export declare function clearAllShares(id?: string): Promise<void>;
538
- /**
539
- * Cryptographic utilities for SSS Key Manager
540
- */
541
- export interface KdfParams {
542
- algorithm: "argon2id";
543
- timeCost: number;
544
- memoryCost: number;
545
- parallelism: number;
546
- }
547
- export declare const DEFAULT_KDF_PARAMS: KdfParams;
548
- export declare function bufferToBase64(buf: ArrayBuffer): string;
549
- export declare function base64ToBuffer(b64: string): Uint8Array;
550
- export declare function hexToBytes(hex: string): Uint8Array;
551
- export declare function bytesToHex(bytes: Uint8Array): string;
552
- export declare function deriveKeyFromPassword(password: string, salt: Uint8Array, params?: KdfParams): Promise<Uint8Array>;
553
- export declare function encryptWithPassword(plaintext: string, password: string): Promise<{
554
- ciphertext: string;
555
- iv: string;
556
- salt: string;
557
- kdfParams: KdfParams;
558
- }>;
559
- export declare function decryptWithPassword(ciphertext: string, iv: string, salt: string, password: string, params?: KdfParams): Promise<string>;
560
- export declare function generateEd25519PrivateKey(): Promise<string>;
561
- /**
562
- * WebAuthn Passkey utilities for SSS recovery
563
- * Uses the PRF (Pseudo-Random Function) extension to derive encryption keys
564
- */
565
- export interface PasskeyCredential {
566
- credentialId: string;
567
- publicKey: string;
568
- transports?: AuthenticatorTransport[];
569
- }
570
- export interface PasskeyEncryptedShare {
571
- encryptedData: string;
572
- iv: string;
573
- credentialId: string;
574
- }
575
- export declare function isWebAuthnSupported(): boolean;
576
- export declare function isPRFSupported(): Promise<boolean>;
577
- export declare function createPasskeyCredential(userId: string, userName: string): Promise<PasskeyCredential>;
578
- export declare function deriveKeyFromPasskey(credentialId: string): Promise<CryptoKey>;
579
- export declare function encryptShareWithPasskey(share: string, credentialId: string): Promise<PasskeyEncryptedShare>;
580
- export declare function decryptShareWithPasskey(encryptedShare: PasskeyEncryptedShare): Promise<string>;
581
- /**
582
- * BIP39 Recovery Phrase utilities for SSS recovery
583
- * The recovery phrase directly encodes a share (not encryption)
584
- */
585
- export interface RecoveryPhraseData {
586
- phrase: string;
587
- shareHex: string;
588
- }
589
- export declare function shareToRecoveryPhrase(shareHex: string): Promise<string>;
590
- export declare function recoveryPhraseToShare(phrase: string): Promise<string>;
591
- export declare function generateRecoveryPhrase(shareHex: string): Promise<RecoveryPhraseData>;
592
- export declare function validateRecoveryPhrase(phrase: string): Promise<boolean>;
593
- export declare function countWords(phrase: string): number;
594
- export interface AtomicSplitResult {
595
- privateKey: string;
596
- shares: SSSShares;
597
- verified: boolean;
598
- }
599
- export interface AtomicUpdateOptions {
600
- previousDeviceShare?: string;
601
- previousAuthShare?: string;
602
- onRollback?: (reason: string) => void;
603
- }
604
- export interface StorageOperations {
605
- storeDevice: (share: string) => Promise<void>;
606
- storeAuth: (share: string) => Promise<void>;
607
- getDevice?: () => Promise<string | null>;
608
- getAuth?: () => Promise<string | null>;
609
- }
610
- export declare class ShareVerificationError extends Error {
611
- readonly combination: string;
612
- readonly expected: string;
613
- readonly got: string;
614
- constructor(message: string, combination: string, expected: string, got: string);
615
- }
616
- export declare class AtomicUpdateError extends Error {
617
- readonly phase: "split" | "verify" | "store_device" | "store_auth" | "verify_stored";
618
- readonly rolledBack: boolean;
619
- readonly cause?: Error | undefined;
620
- constructor(message: string, phase: "split" | "verify" | "store_device" | "store_auth" | "verify_stored", rolledBack: boolean, cause?: Error | undefined);
621
- }
622
- /**
623
- * Split a private key into shares and verify ALL combinations reconstruct correctly.
624
- *
625
- * This function will NOT return until verification passes. If verification fails,
626
- * it throws an error - no shares are ever returned that don't reconstruct the key.
627
- *
628
- * @param privateKey - The private key to split (hex string)
629
- * @returns Verified shares that are guaranteed to reconstruct the key
630
- * @throws ShareVerificationError if any share combination fails verification
631
- */
632
- export declare function splitAndVerify(privateKey: string): Promise<AtomicSplitResult>;
633
- /**
634
- * Atomically update shares with rollback on failure.
635
- *
636
- * This function ensures that either:
637
- * 1. Both device and auth shares are updated successfully, OR
638
- * 2. The previous state is restored (rollback)
639
- *
640
- * The operation flow:
641
- * 1. Generate and verify new shares
642
- * 2. Store device share locally
643
- * 3. Store auth share on server
644
- * 4. If step 3 fails, rollback step 2
645
- *
646
- * @param privateKey - The private key to split
647
- * @param storage - Storage operations for device and auth shares
648
- * @param options - Options including previous shares for rollback
649
- * @returns The new verified shares
650
- * @throws AtomicUpdateError with rollback status
651
- */
652
- export declare function atomicShareUpdate(privateKey: string, storage: StorageOperations, options?: AtomicUpdateOptions): Promise<SSSShares>;
653
- /**
654
- * Verify that stored shares can reconstruct the expected private key.
655
- *
656
- * This is a health check that should be run after recovery or on login
657
- * to ensure the user's shares are in a consistent state.
658
- *
659
- * @param storage - Storage operations to retrieve shares
660
- * @param expectedDid - The expected DID (used to verify the reconstructed key)
661
- * @param didFromPrivateKey - Function to derive DID from private key
662
- * @returns Object with health status and details
663
- */
664
- export declare function verifyStoredShares(storage: Pick<StorageOperations, "getDevice" | "getAuth">, expectedDid: string, didFromPrivateKey: (privateKey: string) => Promise<string>): Promise<{
665
- healthy: boolean;
666
- hasDeviceShare: boolean;
667
- hasAuthShare: boolean;
668
- didMatches: boolean;
669
- error?: string;
670
- }>;
671
- /**
672
- * Create a recovery operation that atomically updates all shares.
673
- *
674
- * This is used during recovery when we reconstruct from recovery+auth shares
675
- * and need to generate a new device share.
676
- *
677
- * @param recoveryShare - The decrypted recovery share
678
- * @param authShareData - The auth share from server
679
- * @param storage - Storage operations
680
- * @param options - Atomic update options
681
- * @returns The reconstructed private key and new shares
682
- */
683
- export declare function atomicRecovery(recoveryShare: string, authShareData: string, storage: StorageOperations, options?: AtomicUpdateOptions): Promise<{
684
- privateKey: string;
685
- newShares: SSSShares;
686
- }>;
687
- export interface SSSStorageFunctions {
688
- storeDeviceShare: (share: string, id?: string) => Promise<void>;
689
- getDeviceShare: (id?: string) => Promise<string | null>;
690
- hasDeviceShare: (id?: string) => Promise<boolean>;
691
- clearAllShares: (id?: string) => Promise<void>;
692
- storeShareVersion: (version: number, id?: string) => Promise<void>;
693
- getShareVersion: (id?: string) => Promise<number | null>;
694
- }
695
- export interface SSSStrategyConfig {
696
- /** Server URL for key share operations */
697
- serverUrl: string;
698
- /** Custom storage functions (defaults to IndexedDB) */
699
- storage?: SSSStorageFunctions;
700
- /**
701
- * Whether to automatically send a backup share to the user's email
702
- * during key setup and recovery. The share is relayed through the server
703
- * but never persisted — fire-and-forget.
704
- *
705
- * Defaults to false. Controlled by VITE_ENABLE_EMAIL_BACKUP_SHARE env var.
706
- */
707
- enableEmailBackupShare?: boolean;
708
- /**
709
- * Tenant identifier forwarded as `X-Tenant-Id` on every server request.
710
- * The lca-api uses this to brand recovery / OTP emails for the active
711
- * tenant. Defaults to the server's fallback tenant (learncard) when unset.
712
- */
713
- tenantId?: string;
714
- }
715
- /**
716
- * Create an SSS key derivation strategy.
717
- *
718
- * @example
719
- * ```ts
720
- * const sssStrategy = createSSSStrategy({
721
- * serverUrl: 'https://api.learncard.com',
722
- * });
723
- *
724
- * // Use with AuthCoordinator
725
- * const coordinator = createAuthCoordinator({
726
- * authProvider,
727
- * keyDerivation: sssStrategy,
728
- * });
729
- * ```
730
- */
731
- export declare function createSSSStrategy(config: SSSStrategyConfig): SSSKeyDerivationStrategy;
732
- /**
733
- * QR Login Crypto Helpers
734
- *
735
- * X25519 ECDH key exchange + AES-256-GCM encryption for cross-device
736
- * share transfer. Uses the Web Crypto API exclusively — no external deps.
737
- *
738
- * Flow:
739
- * Device B generates an ephemeral X25519 keypair, shares the public key.
740
- * Device A derives a shared secret via ECDH, encrypts the device share
741
- * with AES-256-GCM, and sends the ciphertext.
742
- * Device B decrypts with the same derived shared secret.
743
- */
744
- export interface EphemeralKeypair {
745
- /** Base64-encoded X25519 public key (sent to server / encoded in QR) */
746
- publicKey: string;
747
- /** Raw CryptoKey — kept in memory on Device B, never leaves the device */
748
- privateKey: CryptoKey;
749
- }
750
- export interface EncryptedSharePayload {
751
- /** Base64-encoded AES-256-GCM ciphertext */
752
- ciphertext: string;
753
- /** Base64-encoded 12-byte IV */
754
- iv: string;
755
- /** Base64-encoded ephemeral public key of the *sender* (Device A) */
756
- senderPublicKey: string;
757
- }
758
- /**
759
- * Generate an ephemeral X25519 keypair for the new device (Device B).
760
- * The public key is shared via QR / short code; the private key stays in memory.
761
- */
762
- export declare const generateEphemeralKeypair: () => Promise<EphemeralKeypair>;
763
- /**
764
- * Encrypt a device share for transfer to Device B.
765
- *
766
- * Called by Device A (the logged-in device):
767
- * 1. Generates its own ephemeral X25519 keypair
768
- * 2. Derives a shared secret from its private key + Device B's public key
769
- * 3. Encrypts the share with AES-256-GCM
770
- * 4. Returns the ciphertext + IV + Device A's ephemeral public key
771
- *
772
- * @param deviceShare - Plaintext device share to encrypt
773
- * @param recipientPublicKey - Base64-encoded X25519 public key from Device B
774
- */
775
- export declare const encryptShareForTransfer: (deviceShare: string, recipientPublicKey: string) => Promise<EncryptedSharePayload>;
776
- /**
777
- * Decrypt a device share received from Device A.
778
- *
779
- * Called by Device B (the new device):
780
- * 1. Derives the shared secret from its private key + Device A's public key
781
- * 2. Decrypts the ciphertext with AES-256-GCM
782
- *
783
- * @param payload - The encrypted payload from Device A
784
- * @param recipientPrivateKey - Device B's ephemeral private CryptoKey
785
- */
786
- export declare const decryptShareFromTransfer: (payload: EncryptedSharePayload, recipientPrivateKey: CryptoKey) => Promise<string>;
787
- export interface QrLoginSession {
788
- sessionId: string;
789
- shortCode: string;
790
- expiresInSeconds: number;
791
- }
792
- export interface QrLoginSessionInfo {
793
- sessionId: string;
794
- publicKey: string;
795
- status: "pending" | "approved";
796
- encryptedPayload?: string;
797
- approverDid?: string;
798
- }
799
- export interface QrLoginClientConfig {
800
- serverUrl: string;
801
- }
802
- export interface QrPayload {
803
- /** Session ID for the relay */
804
- sessionId: string;
805
- /** Base64-encoded ephemeral X25519 public key */
806
- publicKey: string;
807
- /** Server URL for the relay */
808
- serverUrl: string;
809
- }
810
- /** Result of polling — either still waiting or the device share is ready */
811
- export type PollResult = {
812
- status: "pending";
813
- } | {
814
- status: "approved";
815
- deviceShare: string;
816
- approverDid: string;
817
- accountHint?: string;
818
- shareVersion?: number;
819
- };
820
- /**
821
- * Create a QR login session and generate the ephemeral keypair.
822
- *
823
- * Returns everything Device B needs to display a QR and start polling.
824
- * The ephemeral private key is kept in memory — never serialized.
825
- */
826
- export declare const createQrLoginSession: (config: QrLoginClientConfig) => Promise<{
827
- session: QrLoginSession;
828
- ephemeralKeypair: EphemeralKeypair;
829
- qrPayload: QrPayload;
830
- }>;
831
- /**
832
- * Poll a QR login session for approval.
833
- *
834
- * When Device A approves, this returns the decrypted device share.
835
- *
836
- * @param config - Server config
837
- * @param sessionId - The session to poll
838
- * @param ephemeralPrivateKey - Device B's ephemeral private key (for decryption)
839
- */
840
- export declare const pollQrLoginSession: (config: QrLoginClientConfig, sessionId: string, ephemeralPrivateKey: CryptoKey) => Promise<PollResult>;
841
- /**
842
- * Convenience: poll in a loop until approved or timeout.
843
- *
844
- * @param config - Server config
845
- * @param sessionId - The session to poll
846
- * @param ephemeralPrivateKey - Device B's ephemeral private key
847
- * @param intervalMs - Polling interval (default 2000ms)
848
- * @param timeoutMs - Total timeout (default 120000ms)
849
- * @param onPoll - Optional callback on each poll (for UI updates)
850
- */
851
- export declare const pollUntilApproved: (config: QrLoginClientConfig, sessionId: string, ephemeralPrivateKey: CryptoKey, options?: {
852
- intervalMs?: number;
853
- timeoutMs?: number;
854
- onPoll?: (attempt: number) => void;
855
- signal?: AbortSignal;
856
- }) => Promise<{
857
- deviceShare: string;
858
- approverDid: string;
859
- accountHint?: string;
860
- shareVersion?: number;
861
- }>;
862
- /**
863
- * Fetch a QR login session's public key (for Device A to encrypt against).
864
- *
865
- * @param config - Server config
866
- * @param lookup - Session ID or 6-digit short code
867
- */
868
- export declare const getQrLoginSessionInfo: (config: QrLoginClientConfig, lookup: string) => Promise<QrLoginSessionInfo>;
869
- /**
870
- * Approve a QR login session by encrypting and pushing the device share.
871
- *
872
- * Called by Device A (the logged-in device).
873
- *
874
- * @param config - Server config
875
- * @param sessionId - The session to approve
876
- * @param deviceShare - Plaintext device share from Device A's local storage
877
- * @param approverDid - DID of the approving device
878
- * @param recipientPublicKey - Base64 X25519 public key from the session
879
- * @param accountHint - Optional email or phone of the approver's account (sent to Device B as a login hint)
880
- * @param shareVersion - Optional share version so Device B can fetch the matching auth share
881
- */
882
- export declare const approveQrLoginSession: (config: QrLoginClientConfig, sessionId: string, deviceShare: string, approverDid: string, recipientPublicKey: string, accountHint?: string, shareVersion?: number) => Promise<void>;
883
- export interface NotifyDevicesResult {
884
- sent: boolean;
885
- deviceCount: number;
886
- }
887
- /**
888
- * Send a push notification to the authenticated user's other devices,
889
- * prompting them to open the approver flow for the given QR session.
890
- *
891
- * Called by Device B (in needs_recovery) after creating a session.
892
- * Requires the user's Firebase (or other auth provider) token.
893
- *
894
- * This is fire-and-forget — failure does not block the QR login flow.
895
- */
896
- export declare const notifyDevicesForQrSession: (config: QrLoginClientConfig, sessionId: string, shortCode: string, authToken: string, providerType?: string) => Promise<NotifyDevicesResult>;
897
-
898
- export {};