@learncard/sss-key-manager 0.1.14 → 0.1.16

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/src/storage.ts ADDED
@@ -0,0 +1,467 @@
1
+ /**
2
+ * Device-side storage for SSS shares
3
+ * Reuses patterns from webSecureStorage but specialized for SSS
4
+ */
5
+
6
+ const DB_NAME = 'lcb-sss-keys';
7
+ const DB_VERSION = 1;
8
+ const KEYS_STORE = 'keys';
9
+ const SHARES_STORE = 'shares';
10
+ const DEFAULT_DEVICE_SHARE_ID = 'sss-device-share';
11
+
12
+ type EncryptedPayload = {
13
+ version: 1;
14
+ iv: string;
15
+ cipher: string;
16
+ keyVersion: number;
17
+ };
18
+
19
+ function openDB(): Promise<IDBDatabase> {
20
+ return new Promise((resolve, reject) => {
21
+ const req = indexedDB.open(DB_NAME, DB_VERSION);
22
+ req.onupgradeneeded = () => {
23
+ const db = req.result;
24
+ if (!db.objectStoreNames.contains(KEYS_STORE)) {
25
+ db.createObjectStore(KEYS_STORE);
26
+ }
27
+ if (!db.objectStoreNames.contains(SHARES_STORE)) {
28
+ db.createObjectStore(SHARES_STORE);
29
+ }
30
+ };
31
+ req.onsuccess = () => resolve(req.result);
32
+ req.onerror = () => reject(req.error);
33
+ });
34
+ }
35
+
36
+ function tx<T = unknown>(
37
+ db: IDBDatabase,
38
+ store: string,
39
+ mode: IDBTransactionMode,
40
+ op: (store: IDBObjectStore) => IDBRequest<T>
41
+ ): Promise<T> {
42
+ return new Promise((resolve, reject) => {
43
+ const t = db.transaction(store, mode);
44
+ const s = t.objectStore(store);
45
+ const request = op(s);
46
+ request.onsuccess = () => resolve(request.result as T);
47
+ request.onerror = () => reject(request.error);
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Serialisation lock for master key creation.
53
+ * Prevents two concurrent callers from both seeing "no key" and each
54
+ * generating a different AES key (TOCTOU race).
55
+ */
56
+ let masterKeyPromise: Promise<CryptoKey> | null = null;
57
+
58
+ async function getOrCreateMasterKey(): Promise<CryptoKey> {
59
+ if (masterKeyPromise) return masterKeyPromise;
60
+
61
+ // The singleton promise serialises concurrent callers so only the first
62
+ // one creates the key; all others await the same result.
63
+ masterKeyPromise = (async () => {
64
+ const db = await openDB();
65
+
66
+ try {
67
+ const existing = await tx<CryptoKey | undefined>(db, KEYS_STORE, 'readonly', s =>
68
+ s.get('master-key')
69
+ );
70
+
71
+ if (existing) return existing;
72
+
73
+ const key = await crypto.subtle.generateKey(
74
+ { name: 'AES-GCM', length: 256 },
75
+ false,
76
+ ['encrypt', 'decrypt']
77
+ );
78
+
79
+ await tx(db, KEYS_STORE, 'readwrite', s => s.put(key, 'master-key'));
80
+
81
+ return key;
82
+ } finally {
83
+ db.close();
84
+ }
85
+ })();
86
+
87
+ try {
88
+ return await masterKeyPromise;
89
+ } finally {
90
+ masterKeyPromise = null;
91
+ }
92
+ }
93
+
94
+ function bufferToBase64(buf: ArrayBuffer): string {
95
+ const bytes = new Uint8Array(buf);
96
+ let binary = '';
97
+ for (let i = 0; i < bytes.byteLength; i++) {
98
+ binary += String.fromCharCode(bytes[i]);
99
+ }
100
+ return btoa(binary);
101
+ }
102
+
103
+ function base64ToBuffer(b64: string): ArrayBuffer {
104
+ const binary = atob(b64);
105
+ const bytes = new Uint8Array(binary.length);
106
+ for (let i = 0; i < binary.length; i++) {
107
+ bytes[i] = binary.charCodeAt(i);
108
+ }
109
+ return bytes.buffer;
110
+ }
111
+
112
+ async function encryptShare(share: string, id: string): Promise<EncryptedPayload> {
113
+ const key = await getOrCreateMasterKey();
114
+ const iv = crypto.getRandomValues(new Uint8Array(12));
115
+ const encoder = new TextEncoder();
116
+ const ad = encoder.encode(id);
117
+
118
+ const cipherBuffer = await crypto.subtle.encrypt(
119
+ { name: 'AES-GCM', iv, additionalData: ad },
120
+ key,
121
+ encoder.encode(share)
122
+ );
123
+
124
+ return {
125
+ version: 1,
126
+ iv: bufferToBase64(iv.buffer),
127
+ cipher: bufferToBase64(cipherBuffer),
128
+ keyVersion: 1,
129
+ };
130
+ }
131
+
132
+ async function decryptShare(payload: EncryptedPayload, id: string): Promise<string> {
133
+ const key = await getOrCreateMasterKey();
134
+ const iv = new Uint8Array(base64ToBuffer(payload.iv));
135
+ const cipher = base64ToBuffer(payload.cipher);
136
+ const encoder = new TextEncoder();
137
+ const ad = encoder.encode(id);
138
+
139
+ const plainBuffer = await crypto.subtle.decrypt(
140
+ { name: 'AES-GCM', iv, additionalData: ad },
141
+ key,
142
+ cipher
143
+ );
144
+
145
+ return new TextDecoder().decode(plainBuffer);
146
+ }
147
+
148
+ export async function storeDeviceShare(share: string, id: string = DEFAULT_DEVICE_SHARE_ID): Promise<void> {
149
+ const db = await openDB();
150
+
151
+ try {
152
+ const payload = await encryptShare(share, id);
153
+ await tx(db, SHARES_STORE, 'readwrite', s => s.put(payload, id));
154
+ } finally {
155
+ db.close();
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Store the share version alongside a device share.
161
+ * Stored as a separate key `{id}:version` to avoid changing the encryption format.
162
+ */
163
+ export async function storeShareVersion(version: number, id: string = DEFAULT_DEVICE_SHARE_ID): Promise<void> {
164
+ const db = await openDB();
165
+
166
+ try {
167
+ await tx(db, SHARES_STORE, 'readwrite', s => s.put(version, `${id}:version`));
168
+ } finally {
169
+ db.close();
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Retrieve the share version for a device share.
175
+ * Returns null if no version is stored (legacy shares).
176
+ */
177
+ export async function getShareVersion(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<number | null> {
178
+ const db = await openDB();
179
+
180
+ try {
181
+ const version = await tx<number | undefined>(db, SHARES_STORE, 'readonly', s =>
182
+ s.get(`${id}:version`)
183
+ );
184
+
185
+ return version ?? null;
186
+ } finally {
187
+ db.close();
188
+ }
189
+ }
190
+
191
+ export async function getDeviceShare(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<string | null> {
192
+ const db = await openDB();
193
+
194
+ try {
195
+ const raw = await tx<unknown>(db, SHARES_STORE, 'readonly', s =>
196
+ s.get(id)
197
+ );
198
+
199
+ if (raw == null) {
200
+ return null;
201
+ }
202
+
203
+ // Validate that the stored value looks like an EncryptedPayload before
204
+ // attempting decryption. Phantom entries from stale code paths or
205
+ // corrupt data would crash in base64ToBuffer / AES-GCM otherwise.
206
+ if (
207
+ typeof raw !== 'object' ||
208
+ !('cipher' in (raw as Record<string, unknown>)) ||
209
+ !('iv' in (raw as Record<string, unknown>))
210
+ ) {
211
+ console.warn(
212
+ `SSS Storage: entry "${id}" is not a valid EncryptedPayload (type=${typeof raw}, keys=${typeof raw === 'object' ? Object.keys(raw as object).join(',') : 'n/a'}). Skipping.`
213
+ );
214
+ return null;
215
+ }
216
+
217
+ const payload = raw as EncryptedPayload;
218
+
219
+ try {
220
+ return await decryptShare(payload, id);
221
+ } catch (e) {
222
+ console.warn('SSS Storage: decryption failed for key', id, e);
223
+ return null;
224
+ }
225
+ } finally {
226
+ db.close();
227
+ }
228
+ }
229
+
230
+ export async function hasDeviceShare(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<boolean> {
231
+ const share = await getDeviceShare(id);
232
+ return share !== null;
233
+ }
234
+
235
+ export async function deleteDeviceShare(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<void> {
236
+ const db = await openDB();
237
+
238
+ try {
239
+ await tx(db, SHARES_STORE, 'readwrite', s => s.delete(id));
240
+ // Also remove the companion version metadata key
241
+ await tx(db, SHARES_STORE, 'readwrite', s => s.delete(`${id}:version`));
242
+ } finally {
243
+ db.close();
244
+ }
245
+ }
246
+
247
+ export interface DeviceShareEntry {
248
+ id: string;
249
+ preview: string;
250
+ shareVersion?: number;
251
+ }
252
+
253
+ /**
254
+ * List all device shares stored in IndexedDB.
255
+ * Returns the storage key and a truncated preview for each share.
256
+ * Useful for debugging multi-account storage.
257
+ */
258
+ export async function listAllDeviceShares(): Promise<DeviceShareEntry[]> {
259
+ let db: IDBDatabase;
260
+
261
+ try {
262
+ db = await openDB();
263
+ } catch {
264
+ return [];
265
+ }
266
+
267
+ try {
268
+ const allKeys = await new Promise<IDBValidKey[]>((resolve, reject) => {
269
+ const t = db.transaction(SHARES_STORE, 'readonly');
270
+ const s = t.objectStore(SHARES_STORE);
271
+ const req = s.getAllKeys();
272
+ req.onsuccess = () => resolve(req.result);
273
+ req.onerror = () => reject(req.error);
274
+ });
275
+
276
+ const entries: DeviceShareEntry[] = [];
277
+
278
+ for (const rawKey of allKeys) {
279
+ const id = String(rawKey);
280
+
281
+ // Skip metadata keys (e.g. "sss-device-share:uid:version")
282
+ if (id.endsWith(':version')) continue;
283
+
284
+ try {
285
+ const share = await getDeviceShare(id);
286
+
287
+ if (!share) {
288
+ // Orphaned entry — value exists but can't be decrypted.
289
+ // Auto-clean it (and its companion :version key) to prevent
290
+ // phantom "(decrypt failed)" entries from accumulating.
291
+ console.warn(`SSS Storage: removing orphaned entry that failed to decrypt: ${id}`);
292
+ await deleteDeviceShare(id);
293
+ continue;
294
+ }
295
+
296
+ const preview = share.substring(0, 8) + '...' + share.substring(share.length - 8);
297
+
298
+ const version = await getShareVersion(id);
299
+
300
+ entries.push({ id, preview, shareVersion: version ?? undefined });
301
+ } catch {
302
+ entries.push({ id, preview: '(error)' });
303
+ }
304
+ }
305
+
306
+ return entries;
307
+ } finally {
308
+ db.close();
309
+ }
310
+ }
311
+
312
+ // ---------------------------------------------------------------------------
313
+ // Session-only storage (public / shared computer mode)
314
+ //
315
+ // Uses sessionStorage instead of IndexedDB. Data is cleared when the tab
316
+ // closes. No encryption is applied — sessionStorage is same-origin and
317
+ // tab-scoped, so the threat model is different (goal: don't persist).
318
+ // ---------------------------------------------------------------------------
319
+
320
+ const SESSION_PREFIX = 'sss:';
321
+
322
+ export function isPublicComputerMode(): boolean {
323
+ try {
324
+ return typeof sessionStorage !== 'undefined' &&
325
+ sessionStorage.getItem('lc-session-mode') === 'public';
326
+ } catch {
327
+ return false;
328
+ }
329
+ }
330
+
331
+ export function setPublicComputerMode(enabled: boolean): void {
332
+ try {
333
+ if (enabled) {
334
+ sessionStorage.setItem('lc-session-mode', 'public');
335
+ } else {
336
+ sessionStorage.removeItem('lc-session-mode');
337
+ }
338
+ } catch {
339
+ // sessionStorage unavailable (SSR, etc.)
340
+ }
341
+ }
342
+
343
+ async function sessionStoreDeviceShare(share: string, id: string = DEFAULT_DEVICE_SHARE_ID): Promise<void> {
344
+ sessionStorage.setItem(`${SESSION_PREFIX}${id}`, share);
345
+ }
346
+
347
+ async function sessionGetDeviceShare(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<string | null> {
348
+ return sessionStorage.getItem(`${SESSION_PREFIX}${id}`);
349
+ }
350
+
351
+ async function sessionHasDeviceShare(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<boolean> {
352
+ return sessionStorage.getItem(`${SESSION_PREFIX}${id}`) !== null;
353
+ }
354
+
355
+ async function sessionDeleteDeviceShare(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<void> {
356
+ sessionStorage.removeItem(`${SESSION_PREFIX}${id}`);
357
+ sessionStorage.removeItem(`${SESSION_PREFIX}${id}:version`);
358
+ }
359
+
360
+ async function sessionStoreShareVersion(version: number, id: string = DEFAULT_DEVICE_SHARE_ID): Promise<void> {
361
+ sessionStorage.setItem(`${SESSION_PREFIX}${id}:version`, String(version));
362
+ }
363
+
364
+ async function sessionGetShareVersion(id: string = DEFAULT_DEVICE_SHARE_ID): Promise<number | null> {
365
+ const raw = sessionStorage.getItem(`${SESSION_PREFIX}${id}:version`);
366
+
367
+ return raw !== null ? Number(raw) : null;
368
+ }
369
+
370
+ async function sessionClearAllShares(id?: string): Promise<void> {
371
+ if (id) {
372
+ await sessionDeleteDeviceShare(id);
373
+ return;
374
+ }
375
+
376
+ // Remove all sss: prefixed keys from sessionStorage
377
+ const keysToRemove: string[] = [];
378
+
379
+ for (let i = 0; i < sessionStorage.length; i++) {
380
+ const key = sessionStorage.key(i);
381
+
382
+ if (key?.startsWith(SESSION_PREFIX)) {
383
+ keysToRemove.push(key);
384
+ }
385
+ }
386
+
387
+ keysToRemove.forEach(k => sessionStorage.removeItem(k));
388
+ }
389
+
390
+ /**
391
+ * Create storage functions that dynamically route to sessionStorage (public
392
+ * computer mode) or IndexedDB (normal mode) based on the `lc-session-mode`
393
+ * flag in sessionStorage. Checked at call time, not at creation time.
394
+ */
395
+ export function createAdaptiveStorage() {
396
+ return {
397
+ storeDeviceShare: (share: string, id?: string) =>
398
+ isPublicComputerMode()
399
+ ? sessionStoreDeviceShare(share, id)
400
+ : storeDeviceShare(share, id),
401
+
402
+ getDeviceShare: (id?: string) =>
403
+ isPublicComputerMode()
404
+ ? sessionGetDeviceShare(id)
405
+ : getDeviceShare(id),
406
+
407
+ hasDeviceShare: (id?: string) =>
408
+ isPublicComputerMode()
409
+ ? sessionHasDeviceShare(id)
410
+ : hasDeviceShare(id),
411
+
412
+ clearAllShares: (id?: string) =>
413
+ isPublicComputerMode()
414
+ ? sessionClearAllShares(id)
415
+ : clearAllShares(id),
416
+
417
+ storeShareVersion: (version: number, id?: string) =>
418
+ isPublicComputerMode()
419
+ ? sessionStoreShareVersion(version, id)
420
+ : storeShareVersion(version, id),
421
+
422
+ getShareVersion: (id?: string) =>
423
+ isPublicComputerMode()
424
+ ? sessionGetShareVersion(id)
425
+ : getShareVersion(id),
426
+ };
427
+ }
428
+
429
+ export async function clearAllShares(id?: string): Promise<void> {
430
+ if (id) {
431
+ await deleteDeviceShare(id);
432
+ return;
433
+ }
434
+
435
+ // Reset the cached master-key promise so the next operation generates a fresh key.
436
+ masterKeyPromise = null;
437
+
438
+ // Retry deletion up to 3 times — the first attempt may be blocked by
439
+ // connections that haven't closed yet (e.g. the debug widget).
440
+ for (let attempt = 0; attempt < 3; attempt++) {
441
+ const deleted = await new Promise<boolean>((resolve, reject) => {
442
+ const req = indexedDB.deleteDatabase(DB_NAME);
443
+ req.onsuccess = () => resolve(true);
444
+ req.onerror = () => reject(req.error);
445
+ req.onblocked = () => resolve(false);
446
+ });
447
+
448
+ if (deleted) return;
449
+
450
+ // Brief pause to let blocked connections close
451
+ await new Promise(r => setTimeout(r, 50));
452
+ }
453
+
454
+ // Final fallback: clear all object stores manually instead of deleting the DB
455
+ try {
456
+ const db = await openDB();
457
+
458
+ try {
459
+ await tx(db, SHARES_STORE, 'readwrite', s => s.clear());
460
+ await tx(db, KEYS_STORE, 'readwrite', s => s.clear());
461
+ } finally {
462
+ db.close();
463
+ }
464
+ } catch {
465
+ // Best-effort — if even this fails, the next open will re-create the DB
466
+ }
467
+ }
package/src/types.ts ADDED
@@ -0,0 +1,200 @@
1
+ /**
2
+ * SSS Key Manager Types
3
+ *
4
+ * Re-exports provider-agnostic interfaces from @learncard/types
5
+ * and defines SSS-specific types (recovery shapes, backup files, etc.).
6
+ */
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Re-export provider-agnostic interfaces from @learncard/types
10
+ // ---------------------------------------------------------------------------
11
+
12
+ export {
13
+ AuthSessionError,
14
+ type AuthProviderType,
15
+ type AuthUser,
16
+ type AuthProvider,
17
+ type RecoveryMethodInfo,
18
+ type RecoveryResult,
19
+ type ServerKeyStatus,
20
+ type KeyDerivationStrategy,
21
+ } from '@learncard/types';
22
+
23
+ import type { AuthProvider, AuthProviderType, KeyDerivationStrategy, RecoveryMethodInfo } from '@learncard/types';
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // SSS-specific: Contact & auth provider mapping
27
+ // ---------------------------------------------------------------------------
28
+
29
+ export type ContactMethodType = 'email' | 'phone';
30
+
31
+ export interface ContactMethod {
32
+ type: ContactMethodType;
33
+ value: string;
34
+ }
35
+
36
+ export interface AuthProviderMapping {
37
+ type: AuthProviderType;
38
+ id: string;
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // SSS-specific: Security levels
43
+ // ---------------------------------------------------------------------------
44
+
45
+ export type SecurityLevel = 'basic' | 'enhanced' | 'advanced';
46
+
47
+ export const SecurityLevels: readonly SecurityLevel[] = ['basic', 'enhanced', 'advanced'] as const;
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // SSS-specific: Recovery method types
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /**
54
+ * SSS recovery method type identifiers.
55
+ * These are the specific recovery methods supported by the SSS strategy.
56
+ */
57
+ export type RecoveryMethodType = 'passkey' | 'backup' | 'phrase' | 'email';
58
+
59
+ export interface PasskeyRecoveryMethod {
60
+ type: 'passkey';
61
+ credentialId?: string;
62
+ }
63
+
64
+ export interface BackupFileRecoveryMethod {
65
+ type: 'backup';
66
+ fileContents: string;
67
+ password: string;
68
+ }
69
+
70
+ export interface RecoveryPhraseRecoveryMethod {
71
+ type: 'phrase';
72
+ phrase: string;
73
+ }
74
+
75
+ /** @deprecated Use RecoveryInput instead. Kept for legacy SSSKeyManager class. */
76
+ export type RecoveryMethod = PasskeyRecoveryMethod | BackupFileRecoveryMethod | RecoveryPhraseRecoveryMethod;
77
+
78
+ /**
79
+ * SSS-specific recovery input — what the user provides to recover their key.
80
+ */
81
+ export type RecoveryInput =
82
+ | { method: 'passkey'; credentialId: string }
83
+ | { method: 'phrase'; phrase: string }
84
+ | { method: 'backup'; fileContents: string; password: string }
85
+ | { method: 'email'; emailShare: string };
86
+
87
+ /**
88
+ * SSS-specific recovery setup input — what the user provides to set up a method.
89
+ */
90
+ export type RecoverySetupInput =
91
+ | { method: 'passkey' }
92
+ | { method: 'phrase' }
93
+ | { method: 'backup'; password: string; did: string }
94
+ | { method: 'email' };
95
+
96
+ /**
97
+ * SSS-specific recovery setup result.
98
+ */
99
+ export type RecoverySetupResult =
100
+ | { method: 'passkey'; credentialId: string }
101
+ | { method: 'phrase'; phrase: string }
102
+ | { method: 'backup'; backupFile: BackupFile }
103
+ | { method: 'email' };
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // SSS-specific: Share & encryption types
107
+ // ---------------------------------------------------------------------------
108
+
109
+ export interface EncryptedShare {
110
+ encryptedData: string;
111
+ iv: string;
112
+ salt?: string;
113
+ }
114
+
115
+ export interface ServerEncryptedShare {
116
+ encryptedData: string;
117
+ encryptedDek: string;
118
+ iv: string;
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // SSS-specific: User key record & backup
123
+ // ---------------------------------------------------------------------------
124
+
125
+ export interface UserKeyRecord {
126
+ contactMethod: ContactMethod;
127
+ authProviders: AuthProviderMapping[];
128
+ primaryDid: string;
129
+ linkedDids: string[];
130
+ keyProvider: 'web3auth' | 'sss';
131
+ authShare?: ServerEncryptedShare;
132
+ securityLevel: SecurityLevel;
133
+ recoveryMethods: RecoveryMethodInfo[];
134
+ migratedFromWeb3Auth: boolean;
135
+ migratedAt?: Date;
136
+ createdAt: Date;
137
+ updatedAt: Date;
138
+ }
139
+
140
+ export interface BackupFile {
141
+ version: 1;
142
+ createdAt: string;
143
+ primaryDid: string;
144
+ shareVersion?: number;
145
+ encryptedShare: {
146
+ ciphertext: string;
147
+ iv: string;
148
+ salt: string;
149
+ kdfParams: {
150
+ algorithm: 'argon2id';
151
+ timeCost: number;
152
+ memoryCost: number;
153
+ parallelism: number;
154
+ };
155
+ };
156
+ }
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // SSS-specific: Key manager config
160
+ // ---------------------------------------------------------------------------
161
+
162
+ export interface SSSKeyManagerConfig {
163
+ serverUrl: string;
164
+ authProvider: AuthProvider;
165
+ deviceStorageKey?: string;
166
+ }
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // SSS-specific: Narrowed strategy type
170
+ // ---------------------------------------------------------------------------
171
+
172
+ /**
173
+ * The SSS key derivation strategy — a KeyDerivationStrategy narrowed
174
+ * with SSS-specific recovery input/output types.
175
+ */
176
+ export type SSSKeyDerivationStrategy = KeyDerivationStrategy<RecoveryInput, RecoverySetupInput, RecoverySetupResult>;
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Legacy types (deprecated)
180
+ // ---------------------------------------------------------------------------
181
+
182
+ /** @deprecated Use KeyDerivationStrategy instead. Kept for legacy SSSKeyManager class. */
183
+ export interface KeyDerivationProvider {
184
+ readonly name: string;
185
+ connect(): Promise<string>;
186
+ disconnect(): Promise<void>;
187
+ isInitialized(): boolean;
188
+ hasLocalKey(): Promise<boolean>;
189
+ canMigrate?(): Promise<boolean>;
190
+ migrate?(privateKey: string): Promise<void>;
191
+ }
192
+
193
+ /** @deprecated Use KeyDerivationStrategy instead. Kept for legacy SSSKeyManager class. */
194
+ export interface SSSKeyDerivationProvider extends KeyDerivationProvider {
195
+ addRecoveryMethod(method: RecoveryMethod): Promise<void>;
196
+ getRecoveryMethods(): Promise<RecoveryMethodInfo[]>;
197
+ recover(method: RecoveryMethod): Promise<string>;
198
+ getSecurityLevel(): Promise<SecurityLevel>;
199
+ exportBackup(password: string): Promise<BackupFile>;
200
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Learning Economy Foundation <sdk@learningeconomy.io>
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.