@le-space/orbitdb-identity-provider-webauthn-did 0.0.1 → 0.2.1

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.
@@ -0,0 +1,555 @@
1
+ /**
2
+ * OrbitDB Identity Provider for WebAuthn + Keystore
3
+ */
4
+
5
+ import { logger } from '@libp2p/logger';
6
+ import { generateKeyPair, privateKeyFromRaw } from '@libp2p/crypto/keys';
7
+ import * as KeystoreEncryption from './encryption.js';
8
+ import { WebAuthnDIDProvider } from '../webauthn/provider.js';
9
+
10
+ const identityLog = logger('orbitdb-identity-provider-webauthn-did:identity');
11
+
12
+ /**
13
+ * OrbitDB Identity Provider that uses WebAuthn
14
+ */
15
+ export class OrbitDBWebAuthnIdentityProvider {
16
+ /**
17
+ * @param {Object} options - Provider configuration.
18
+ * @param {Object} options.webauthnCredential - WebAuthn credential info.
19
+ * @param {boolean} [options.useKeystoreDID=false] - Use keystore DID instead of WebAuthn DID.
20
+ * @param {Object|null} [options.keystore=null] - OrbitDB keystore instance.
21
+ * @param {string} [options.keystoreKeyType='secp256k1'] - Keystore key type.
22
+ * @param {boolean} [options.encryptKeystore=false] - Encrypt keystore at rest.
23
+ * @param {string} [options.keystoreEncryptionMethod='prf'] - Encryption method.
24
+ */
25
+ constructor({
26
+ webauthnCredential,
27
+ useKeystoreDID = false,
28
+ keystore = null,
29
+ keystoreKeyType = 'secp256k1',
30
+ encryptKeystore = false,
31
+ keystoreEncryptionMethod = 'prf'
32
+ }) {
33
+ this.credential = webauthnCredential;
34
+ this.webauthnProvider = new WebAuthnDIDProvider(webauthnCredential);
35
+ this.type = 'webauthn'; // Set instance property
36
+ this.useKeystoreDID = useKeystoreDID; // Flag to use Ed25519 DID from keystore
37
+ this.keystore = keystore; // OrbitDB keystore instance
38
+ this.keystoreKeyType = keystoreKeyType; // Key type: 'secp256k1' or 'Ed25519'
39
+ this.encryptKeystore = encryptKeystore; // Flag to encrypt keystore
40
+ this.keystoreEncryptionMethod = keystoreEncryptionMethod; // Encryption method
41
+ this.unlockedKeypair = null; // Store unlocked keypair during session
42
+ this.unlockedPrivateKey = null;
43
+ }
44
+
45
+ static get type() {
46
+ return 'webauthn';
47
+ }
48
+
49
+ /**
50
+ * Resolve the identity DID.
51
+ * @returns {Promise<string>} DID string.
52
+ */
53
+ async getId() {
54
+ identityLog('getId() called');
55
+
56
+ // If useKeystoreDID flag is set, create Ed25519 DID from keystore
57
+ if (this.useKeystoreDID && this.keystore) {
58
+ if (this.encryptKeystore) {
59
+ await this.ensureEncryptedKeystore();
60
+ }
61
+ identityLog('Using Ed25519 DID from keystore');
62
+ const did = await this.createEd25519DIDFromKeystore();
63
+ identityLog('getId() returning Ed25519 DID: %s', did.substring(0, 32) + '...');
64
+ return did;
65
+ }
66
+
67
+ // Default: Return P-256 DID from WebAuthn credential
68
+ const did = await WebAuthnDIDProvider.createDID(this.credential);
69
+ identityLog('getId() returning P-256 DID: %s', did.substring(0, 32) + '...');
70
+ return did;
71
+ }
72
+
73
+ async ensureEncryptedKeystore() {
74
+ if (this.unlockedKeypair) {
75
+ return;
76
+ }
77
+
78
+ try {
79
+ await KeystoreEncryption.loadEncryptedKeystore(this.credential.credentialId);
80
+ } catch (error) {
81
+ identityLog('Encrypted keystore missing, creating a new one: %s', error.message);
82
+ await this.createEncryptedKeystore();
83
+ }
84
+
85
+ await this.unlockEncryptedKeystore();
86
+ }
87
+
88
+ /**
89
+ * Create Ed25519 DID from OrbitDB keystore
90
+ * This uses the keystore's Ed25519 key to create a did:key DID
91
+ */
92
+ async createEd25519DIDFromKeystore() {
93
+ if (!this.keystore) {
94
+ throw new Error('Keystore is required to create Ed25519 DID');
95
+ }
96
+
97
+ try {
98
+ // Import multiformats modules
99
+ const multiformats = await import('multiformats');
100
+ const varint = multiformats.varint;
101
+ const { base58btc } = await import('multiformats/bases/base58');
102
+
103
+ if (this.encryptKeystore) {
104
+ const encryptedData = this.unlockedKeypair
105
+ ? { publicKey: this.unlockedKeypair.publicKey, keyType: this.unlockedKeypair.keyType }
106
+ : await KeystoreEncryption.loadEncryptedKeystore(this.credential.credentialId);
107
+ const publicKeyBytes = encryptedData.publicKey instanceof Uint8Array
108
+ ? encryptedData.publicKey
109
+ : new Uint8Array(encryptedData.publicKey);
110
+ const keyType = encryptedData.keyType || this.keystoreKeyType;
111
+
112
+ return this.createDIDFromKeystorePublicKey(publicKeyBytes, keyType, varint, base58btc);
113
+ }
114
+
115
+ // Get the keystore's identity ID (this will be used to retrieve the key)
116
+ // We'll use the WebAuthn DID as the identity ID to get/create the keystore key
117
+ const identityId = await WebAuthnDIDProvider.createDID(this.credential);
118
+
119
+ // Get or create the Ed25519 key from keystore
120
+ // Try getKey first, if it doesn't exist, createKey will create it
121
+ let keystoreKey = await this.keystore.getKey(identityId);
122
+ if (!keystoreKey) {
123
+ identityLog('Key not found, creating new key for: %s with type: %s', identityId.substring(0, 32) + '...', this.keystoreKeyType);
124
+ keystoreKey = await this.keystore.createKey(identityId, this.keystoreKeyType);
125
+ }
126
+
127
+ identityLog('Keystore key obtained, type: %s, keys: %o', typeof keystoreKey, Object.keys(keystoreKey || {}));
128
+
129
+ // The keystore key should have a public property with marshal method
130
+ // But it seems this isn't available immediately after creation
131
+ // Let's try to extract the public key bytes directly
132
+ let publicKeyBytes;
133
+
134
+ // Extract public key bytes from the keystore key
135
+ // OrbitDB uses @libp2p/crypto keys which have different structures
136
+ if (keystoreKey && keystoreKey.publicKey) {
137
+ // Modern libp2p-crypto format - has publicKey property
138
+ const pubKey = keystoreKey.publicKey;
139
+ identityLog('Found publicKey property, type: %s', pubKey.constructor.name);
140
+
141
+ // Try to get raw bytes from the public key
142
+ if (pubKey.raw) {
143
+ publicKeyBytes = pubKey.raw;
144
+ identityLog('Got public key from publicKey.raw: %d bytes', publicKeyBytes.length);
145
+ } else if (pubKey.bytes) {
146
+ publicKeyBytes = pubKey.bytes;
147
+ identityLog('Got public key from publicKey.bytes: %d bytes', publicKeyBytes.length);
148
+ } else if (typeof pubKey.marshal === 'function') {
149
+ publicKeyBytes = pubKey.marshal();
150
+ identityLog('Got public key from publicKey.marshal(): %d bytes', publicKeyBytes.length);
151
+ } else {
152
+ identityLog.error('Cannot extract bytes from publicKey: %o', pubKey);
153
+ throw new Error('Unable to extract bytes from publicKey');
154
+ }
155
+ } else if (keystoreKey && keystoreKey.public && keystoreKey.public.bytes) {
156
+ // Older libp2p-crypto format
157
+ publicKeyBytes = keystoreKey.public.bytes;
158
+ identityLog('Got public key from keystoreKey.public.bytes: %d bytes', publicKeyBytes.length);
159
+ } else if (keystoreKey && keystoreKey.bytes) {
160
+ // Direct bytes
161
+ publicKeyBytes = keystoreKey.bytes;
162
+ identityLog('Got public key from keystoreKey.bytes: %d bytes', publicKeyBytes.length);
163
+ } else {
164
+ identityLog.error('Cannot extract public key from keystoreKey: %o', keystoreKey);
165
+ throw new Error('Unable to extract public key from keystore key');
166
+ }
167
+
168
+ // Note: secp256k1 public keys are 33 or 65 bytes (compressed/uncompressed)
169
+ // Ed25519 public keys are 32 bytes
170
+ // We need to handle both
171
+ identityLog('Public key extracted: %d bytes, key type: %s', publicKeyBytes.length, keystoreKey.type);
172
+
173
+ if (!publicKeyBytes || publicKeyBytes.length < 32) {
174
+ throw new Error(`Invalid public key length: ${publicKeyBytes ? publicKeyBytes.length : 0} bytes`);
175
+ }
176
+
177
+ identityLog('Successfully extracted public key: %d bytes, type: %s', publicKeyBytes.length, keystoreKey.type);
178
+
179
+ return this.createDIDFromKeystorePublicKey(publicKeyBytes, keystoreKey.type, varint, base58btc);
180
+
181
+ } catch (error) {
182
+ identityLog.error('Failed to create Ed25519 DID from keystore: %s', error.message);
183
+ throw new Error(`Failed to create Ed25519 DID from keystore: ${error.message}`);
184
+ }
185
+ }
186
+
187
+ createDIDFromKeystorePublicKey(publicKeyBytes, keyType, varint, base58btc) {
188
+ // Determine the correct multicodec based on key type
189
+ // secp256k1 multicodec code (0xe7) or Ed25519 (0xed)
190
+ let multicodec;
191
+ if (keyType === 'secp256k1') {
192
+ multicodec = 0xe7; // secp256k1-pub
193
+ identityLog('Using secp256k1 multicodec (0xe7)');
194
+ } else if (keyType === 'Ed25519' || keyType === 'ed25519') {
195
+ multicodec = 0xed; // ed25519-pub
196
+ identityLog('Using Ed25519 multicodec (0xed)');
197
+ } else {
198
+ throw new Error(`Unsupported key type: ${keyType}`);
199
+ }
200
+
201
+ const codecLength = varint.encodingLength(multicodec);
202
+ const codecBytes = new Uint8Array(codecLength);
203
+ varint.encodeTo(multicodec, codecBytes, 0);
204
+
205
+ if (codecBytes.length === 0) {
206
+ throw new Error('Failed to encode ED25519_MULTICODEC with varint');
207
+ }
208
+
209
+ // Combine multicodec prefix + public key bytes
210
+ const multikey = new Uint8Array(codecBytes.length + publicKeyBytes.length);
211
+ multikey.set(codecBytes, 0);
212
+ multikey.set(publicKeyBytes, codecBytes.length);
213
+
214
+ // Encode as base58btc and create did:key
215
+ const multikeyEncoded = base58btc.encode(multikey);
216
+ return `did:key:${multikeyEncoded}`;
217
+ }
218
+
219
+ /**
220
+ * Create and encrypt OrbitDB keystore
221
+ * @returns {Promise<void>}
222
+ */
223
+ async createEncryptedKeystore() {
224
+ if (!this.encryptKeystore) {
225
+ console.log('ℹ️ Keystore encryption not enabled, skipping');
226
+ return;
227
+ }
228
+
229
+ console.log('🔐 Creating encrypted keystore with method:', this.keystoreEncryptionMethod);
230
+ identityLog('Creating encrypted keystore with method: %s', this.keystoreEncryptionMethod);
231
+
232
+ try {
233
+ // Generate secret key
234
+ const sk = KeystoreEncryption.generateSecretKey();
235
+
236
+ const keyType = this.keystoreKeyType === 'secp256k1' ? 'secp256k1' : 'Ed25519';
237
+ const keyPair = await generateKeyPair(keyType);
238
+ const privateKeyBytes = keyPair.marshal ? keyPair.marshal() : keyPair.raw;
239
+ const publicKeyBytes = keyPair.publicKey?.marshal ? keyPair.publicKey.marshal() : keyPair.publicKey?.raw;
240
+
241
+ if (!privateKeyBytes || !publicKeyBytes) {
242
+ throw new Error('Failed to serialize keystore keypair');
243
+ }
244
+ const { ciphertext, iv } = await KeystoreEncryption.encryptWithAESGCM(privateKeyBytes, sk);
245
+
246
+ // Store SK in WebAuthn or wrap it
247
+ let encryptedData;
248
+
249
+ if (this.keystoreEncryptionMethod === 'prf') {
250
+ // Wrap SK with PRF (WebAuthn Level 3 - preferred method)
251
+ const wrapped = await KeystoreEncryption.wrapSKWithPRF(
252
+ this.credential.rawCredentialId,
253
+ sk,
254
+ window.location.hostname
255
+ );
256
+
257
+ encryptedData = {
258
+ ciphertext,
259
+ iv,
260
+ credentialId: this.credential.credentialId,
261
+ publicKey: publicKeyBytes,
262
+ keyType,
263
+ wrappedSK: wrapped.wrappedSK,
264
+ wrappingIV: wrapped.wrappingIV,
265
+ salt: wrapped.salt,
266
+ encryptionMethod: 'prf'
267
+ };
268
+ } else if (this.keystoreEncryptionMethod === 'largeBlob') {
269
+ // For largeBlob, we need to store SK during next authentication
270
+ // Store it temporarily for wrapping
271
+ encryptedData = {
272
+ ciphertext,
273
+ iv,
274
+ credentialId: this.credential.credentialId,
275
+ publicKey: publicKeyBytes,
276
+ keyType,
277
+ secretKey: sk, // Will be moved to largeBlob
278
+ encryptionMethod: 'largeBlob'
279
+ };
280
+ } else if (this.keystoreEncryptionMethod === 'hmac-secret') {
281
+ // Wrap SK with hmac-secret
282
+ const wrapped = await KeystoreEncryption.wrapSKWithHmacSecret(
283
+ this.credential.rawCredentialId,
284
+ sk,
285
+ window.location.hostname
286
+ );
287
+
288
+ encryptedData = {
289
+ ciphertext,
290
+ iv,
291
+ credentialId: this.credential.credentialId,
292
+ publicKey: publicKeyBytes,
293
+ keyType,
294
+ wrappedSK: wrapped.wrappedSK,
295
+ wrappingIV: wrapped.wrappingIV,
296
+ salt: wrapped.salt,
297
+ encryptionMethod: 'hmac-secret'
298
+ };
299
+ }
300
+
301
+ // Store encrypted keystore
302
+ console.log('💾 Storing encrypted keystore with credentialId:', this.credential.credentialId?.substring(0, 16) + '...');
303
+ await KeystoreEncryption.storeEncryptedKeystore(encryptedData, this.credential.credentialId);
304
+ this.unlockedKeypair = {
305
+ privateKey: privateKeyBytes,
306
+ publicKey: publicKeyBytes,
307
+ keyType
308
+ };
309
+ this.unlockedPrivateKey = keyPair;
310
+ console.log('✅ Encrypted keystore stored successfully');
311
+
312
+ identityLog('Encrypted keystore created and stored successfully');
313
+
314
+ } catch (error) {
315
+ identityLog.error('Failed to create encrypted keystore: %s', error.message);
316
+ throw new Error(`Failed to create encrypted keystore: ${error.message}`);
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Unlock encrypted keystore
322
+ * @returns {Promise<Object>} Decrypted keypair
323
+ */
324
+ async unlockEncryptedKeystore() {
325
+ if (!this.encryptKeystore) {
326
+ return null;
327
+ }
328
+
329
+ identityLog('Unlocking encrypted keystore with method: %s', this.keystoreEncryptionMethod);
330
+
331
+ try {
332
+ // Load encrypted keystore
333
+ const encryptedData = await KeystoreEncryption.loadEncryptedKeystore(this.credential.credentialId);
334
+
335
+ let sk;
336
+
337
+ if (encryptedData.encryptionMethod === 'prf') {
338
+ // Unwrap SK with PRF
339
+ sk = await KeystoreEncryption.unwrapSKWithPRF(
340
+ this.credential.rawCredentialId,
341
+ encryptedData.wrappedSK,
342
+ encryptedData.wrappingIV,
343
+ encryptedData.salt,
344
+ window.location.hostname
345
+ );
346
+ } else if (encryptedData.encryptionMethod === 'largeBlob') {
347
+ // Retrieve SK from largeBlob
348
+ sk = await KeystoreEncryption.retrieveSKFromLargeBlob(
349
+ this.credential.rawCredentialId,
350
+ window.location.hostname
351
+ );
352
+ } else if (encryptedData.encryptionMethod === 'hmac-secret') {
353
+ // Unwrap SK with hmac-secret
354
+ sk = await KeystoreEncryption.unwrapSKWithHmacSecret(
355
+ this.credential.rawCredentialId,
356
+ encryptedData.wrappedSK,
357
+ encryptedData.wrappingIV,
358
+ encryptedData.salt,
359
+ window.location.hostname
360
+ );
361
+ }
362
+
363
+ // Decrypt keystore private key
364
+ const privateKeyBytes = await KeystoreEncryption.decryptWithAESGCM(
365
+ encryptedData.ciphertext,
366
+ sk,
367
+ encryptedData.iv
368
+ );
369
+
370
+ const publicKeyBytes = encryptedData.publicKey instanceof Uint8Array
371
+ ? encryptedData.publicKey
372
+ : new Uint8Array(encryptedData.publicKey);
373
+ // Store unlocked keypair in memory for session
374
+ this.unlockedKeypair = {
375
+ privateKey: privateKeyBytes,
376
+ publicKey: publicKeyBytes,
377
+ keyType: encryptedData.keyType || this.keystoreKeyType
378
+ };
379
+ try {
380
+ this.unlockedPrivateKey = privateKeyFromRaw(privateKeyBytes);
381
+ } catch (error) {
382
+ identityLog.error('Failed to unmarshal encrypted keystore private key: %s', error.message);
383
+ this.unlockedPrivateKey = null;
384
+ }
385
+
386
+ identityLog('Encrypted keystore unlocked successfully');
387
+
388
+ return this.unlockedKeypair;
389
+
390
+ } catch (error) {
391
+ identityLog.error('Failed to unlock encrypted keystore: %s', error.message);
392
+ throw new Error(`Failed to unlock encrypted keystore: ${error.message}`);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Sign data for OrbitDB identity operations.
398
+ * @param {string|Uint8Array} data - Payload to sign.
399
+ * @returns {Promise<string>} Signature envelope.
400
+ */
401
+ signIdentity(data) {
402
+ const dataLength = typeof data === 'string' ? data.length : data.byteLength;
403
+ identityLog('signIdentity() called with data length: %d', dataLength);
404
+ identityLog('Signer context: %o', {
405
+ useKeystoreDID: this.useKeystoreDID,
406
+ encryptKeystore: this.encryptKeystore,
407
+ keystoreEncryptionMethod: this.keystoreEncryptionMethod,
408
+ hasKeystore: Boolean(this.keystore),
409
+ hasUnlockedKeypair: Boolean(this.unlockedKeypair)
410
+ });
411
+
412
+ const signerSelection =
413
+ this.encryptKeystore && this.unlockedKeypair ? 'encrypted-keystore' : 'webauthn';
414
+ identityLog('Signer selection: %s', signerSelection);
415
+
416
+ // If using encrypted keystore and it's unlocked, use unlocked key
417
+ if (this.encryptKeystore && this.unlockedKeypair) {
418
+ identityLog('Using unlocked encrypted keystore for signing');
419
+ // TODO: Implement signing with unlocked keypair
420
+ // For now, fall back to WebAuthn
421
+ }
422
+
423
+ return this.webauthnProvider.sign(data);
424
+ }
425
+
426
+ /**
427
+ * Verify identity signature.
428
+ * @param {string} signature - Signature envelope.
429
+ * @param {string|Uint8Array} data - Payload that was signed.
430
+ * @param {Object} [publicKey] - Optional public key override.
431
+ * @returns {Promise<boolean>} True if valid.
432
+ */
433
+ verifyIdentity(signature, data, publicKey) {
434
+ identityLog('verifyIdentity() called');
435
+ return this.webauthnProvider.verify(signature, data, publicKey || this.credential.publicKey);
436
+ }
437
+
438
+ /**
439
+ * Create OrbitDB identity using WebAuthn
440
+ * @param {Object} options - Provider options.
441
+ * @returns {Promise<Object>} OrbitDB identity object.
442
+ */
443
+ static async createIdentity(options) {
444
+ const {
445
+ webauthnCredential,
446
+ useKeystoreDID = false,
447
+ keystore = null,
448
+ keystoreKeyType = 'secp256k1',
449
+ encryptKeystore = false,
450
+ keystoreEncryptionMethod = 'prf'
451
+ } = options;
452
+
453
+ identityLog('createIdentity() called with useKeystoreDID: %s, keystoreKeyType: %s, encryptKeystore: %s',
454
+ useKeystoreDID, keystoreKeyType, encryptKeystore);
455
+
456
+ const provider = new OrbitDBWebAuthnIdentityProvider({
457
+ webauthnCredential,
458
+ useKeystoreDID,
459
+ keystore,
460
+ keystoreKeyType,
461
+ encryptKeystore,
462
+ keystoreEncryptionMethod
463
+ });
464
+
465
+ // If encryption is enabled, create and unlock encrypted keystore
466
+ if (encryptKeystore && keystore) {
467
+ try {
468
+ console.log('🔐 Creating encrypted keystore with', keystoreEncryptionMethod, '...');
469
+ await provider.createEncryptedKeystore();
470
+ console.log('🔓 Unlocking encrypted keystore...');
471
+ await provider.unlockEncryptedKeystore();
472
+ console.log('✅ Encrypted keystore created and unlocked successfully');
473
+ identityLog('Encrypted keystore created and unlocked');
474
+ } catch (error) {
475
+ // Log error visibly so users know encryption failed
476
+ console.error('❌ Failed to setup encrypted keystore:', error.message);
477
+ console.error(' Full error:', error);
478
+ identityLog.error('Failed to setup encrypted keystore: %s', error.message);
479
+ // Continue anyway - encryption is optional but user should know it failed
480
+ }
481
+ }
482
+
483
+ const id = await provider.getId();
484
+
485
+ identityLog('Identity created successfully: %o', {
486
+ id: id.substring(0, 32) + '...',
487
+ type: 'webauthn',
488
+ didType: useKeystoreDID ? 'Ed25519 (from keystore)' : 'P-256 (from WebAuthn)',
489
+ encrypted: encryptKeystore,
490
+ hasPublicKey: !!webauthnCredential.publicKey
491
+ });
492
+
493
+ return {
494
+ id,
495
+ publicKey: webauthnCredential.publicKey,
496
+ type: 'webauthn',
497
+ sign: (identity, data) => {
498
+ identityLog('identity.sign() called from OrbitDB');
499
+ return provider.signIdentity(data);
500
+ },
501
+ verify: (signature, data) => {
502
+ identityLog('identity.verify() called from OrbitDB');
503
+ return provider.verifyIdentity(signature, data, webauthnCredential.publicKey);
504
+ }
505
+ };
506
+ }
507
+ }
508
+
509
+ /**
510
+ * WebAuthn Identity Provider Function for OrbitDB
511
+ * This follows the same pattern as OrbitDBIdentityProviderDID
512
+ * Returns a function that returns a promise resolving to the provider instance
513
+ *
514
+ * @param {Object} options - Configuration options
515
+ * @param {Object} options.webauthnCredential - WebAuthn credential for authentication
516
+ * @param {boolean} options.useKeystoreDID - If true, creates DID from keystore instead of P-256 DID from WebAuthn
517
+ * @param {Object} options.keystore - OrbitDB keystore instance (required if useKeystoreDID is true)
518
+ * @param {string} options.keystoreKeyType - Key type for keystore: 'secp256k1' (default) or 'Ed25519'
519
+ * @param {boolean} options.encryptKeystore - If true, encrypts the keystore with WebAuthn-protected secret
520
+ * @param {string} options.keystoreEncryptionMethod - Encryption method: 'largeBlob' or 'hmac-secret'
521
+ * @returns {Function} Provider factory for OrbitDB.
522
+ */
523
+ export function OrbitDBWebAuthnIdentityProviderFunction(options = {}) {
524
+ // Return a function that returns a promise (as expected by OrbitDB)
525
+ return async () => {
526
+ return new OrbitDBWebAuthnIdentityProvider(options);
527
+ };
528
+ }
529
+
530
+ // Add static methods and properties that OrbitDB expects
531
+ OrbitDBWebAuthnIdentityProviderFunction.type = 'webauthn';
532
+ OrbitDBWebAuthnIdentityProviderFunction.verifyIdentity = async function(identity) {
533
+ try {
534
+ // For WebAuthn identities, we need to store the credential info in the identity
535
+ // Since WebAuthn verification requires the original credential, not just the public key,
536
+ // we'll create a simplified verification that checks the proof structure
537
+
538
+
539
+ // For WebAuthn, the identity should have been created with our provider,
540
+ // so we can trust it if it has the right structure
541
+ // Accept both DID format (did:key:...) and hash format (hex string) for backward compatibility
542
+ const isValidDID = identity.id && identity.id.startsWith('did:key:');
543
+ const isValidHash = identity.id && /^[a-f0-9]{64}$/.test(identity.id); // 64-char hex string (legacy)
544
+
545
+ if (identity.type === 'webauthn' && (isValidDID || isValidHash)) {
546
+ return true;
547
+ }
548
+
549
+ return false;
550
+
551
+ } catch (error) {
552
+ console.error('WebAuthn static identity verification failed:', error);
553
+ return false;
554
+ }
555
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Backward-compatible export for keystore encryption.
3
+ * Use `./keystore/encryption.js` going forward.
4
+ */
5
+ export * from './keystore/encryption.js';
6
+ export { default } from './keystore/encryption.js';