ns-auth-sdk 1.9.1 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -13
- package/dist/index.cjs +169 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +60 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +166 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,36 +126,72 @@ const signedEvent = await authService.signEvent(event);
|
|
|
126
126
|
#### Methods
|
|
127
127
|
|
|
128
128
|
- `createPasskey(username?: string): Promise<Uint8Array>` - Create a new passkey
|
|
129
|
-
- `createKey(credentialId?: Uint8Array, password?: string): Promise<KeyInfo>` - Create key from passkey (auto-detects PRF support, uses password fallback if needed)
|
|
129
|
+
- `createKey(credentialId?: Uint8Array, password?: string, options?: KeyOptions): Promise<KeyInfo>` - Create key from passkey (auto-detects PRF support, uses password fallback if needed)
|
|
130
130
|
- `getPublicKey(): Promise<string>` - Get current public key
|
|
131
|
-
- `signEvent(event: Event
|
|
131
|
+
- `signEvent(event: Event): Promise<Event>` - Sign an event
|
|
132
132
|
- `getCurrentKeyInfo(): KeyInfo | null` - Get current key info
|
|
133
133
|
- `setCurrentKeyInfo(keyInfo: KeyInfo): void` - Set current key info
|
|
134
134
|
- `hasKeyInfo(): boolean` - Check if key info exists
|
|
135
135
|
- `clearStoredKeyInfo(): void` - Clear stored key info
|
|
136
|
-
- `checkPRFSupport(): Promise<boolean>` - Check if PRF is supported
|
|
136
|
+
- `checkPRFSupport(): Promise<boolean>` - Check if PRF is supported
|
|
137
137
|
|
|
138
|
-
####
|
|
138
|
+
#### Recovery Methods
|
|
139
|
+
|
|
140
|
+
- `addPasswordRecovery(password: string): Promise<KeyInfo>` - Add password recovery to an existing PRF key
|
|
141
|
+
- `activateWithPassword(password: string, newCredentialId: Uint8Array): Promise<KeyInfo>` - Recover using password with a new passkey credential ID from a new device
|
|
142
|
+
- `getRecoveryForKind0(): RecoveryData | null` - Get recovery data for publishing to kind-0
|
|
143
|
+
|
|
144
|
+
#### KeyOptions
|
|
139
145
|
|
|
140
146
|
```typescript
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
saltBase64: string;
|
|
146
|
-
ivBase64: string;
|
|
147
|
+
interface KeyOptions {
|
|
148
|
+
username?: string;
|
|
149
|
+
password?: string;
|
|
150
|
+
recoveryPassword?: string; // Password for recovery - enables recovery on new device
|
|
147
151
|
}
|
|
152
|
+
```
|
|
148
153
|
|
|
149
|
-
|
|
154
|
+
#### RecoveryData
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
interface RecoveryData {
|
|
158
|
+
recoveryPubkey: string;
|
|
159
|
+
recoverySalt: string;
|
|
160
|
+
createdAt?: number;
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Types
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// KeyInfo with optional recovery
|
|
150
168
|
interface KeyInfo {
|
|
151
169
|
credentialId: string;
|
|
152
170
|
pubkey: string;
|
|
153
171
|
salt: string;
|
|
154
172
|
username?: string;
|
|
155
|
-
|
|
173
|
+
recovery?: KeyRecovery;
|
|
156
174
|
}
|
|
157
175
|
|
|
158
|
-
//
|
|
176
|
+
// Key recovery configuration
|
|
177
|
+
interface KeyRecovery {
|
|
178
|
+
recoveryPubkey: string;
|
|
179
|
+
recoverySalt: string;
|
|
180
|
+
createdAt?: number;
|
|
181
|
+
}
|
|
182
|
+
interface KeyRecovery {
|
|
183
|
+
recoveryPubkey: string;
|
|
184
|
+
recoverySalt: string;
|
|
185
|
+
nextSalt?: string;
|
|
186
|
+
nextRecoverySalt?: string;
|
|
187
|
+
nextPubkey?: string;
|
|
188
|
+
nextCredentialId?: string;
|
|
189
|
+
rotatedAt?: number;
|
|
190
|
+
rotatedBy?: 'passkey' | 'password';
|
|
191
|
+
activatedAt?: number;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Sign options
|
|
159
195
|
interface SignOptions {
|
|
160
196
|
clearMemory?: boolean;
|
|
161
197
|
tags?: string[][];
|
|
@@ -163,6 +199,31 @@ interface SignOptions {
|
|
|
163
199
|
}
|
|
164
200
|
```
|
|
165
201
|
|
|
202
|
+
### Recovery Flow
|
|
203
|
+
|
|
204
|
+
The SDK supports password-based recovery for passkey-protected keys. When creating a key, you can optionally provide a recovery password:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// Create key with recovery enabled
|
|
208
|
+
const keyInfo = await authService.createKey(credentialId, undefined, {
|
|
209
|
+
username: 'user@example.com',
|
|
210
|
+
recoveryPassword: 'my-recovery-password',
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// The recovery data is stored in kind-0 tags:
|
|
214
|
+
// ["r", recoveryPubkey, recoverySalt, createdAt]
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Recovery on a new device:**
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// On new device - create new passkey first
|
|
221
|
+
const newCredentialId = await authService.createPasskey('user@example.com');
|
|
222
|
+
|
|
223
|
+
// Recover using password
|
|
224
|
+
const keyInfo = await authService.activateWithPassword('my-recovery-password', newCredentialId);
|
|
225
|
+
```
|
|
226
|
+
|
|
166
227
|
#### Configuration Options
|
|
167
228
|
|
|
168
229
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -245,6 +245,15 @@ async function getPrfSecret(credentialId, options) {
|
|
|
245
245
|
|
|
246
246
|
//#endregion
|
|
247
247
|
//#region src/utils/prf-password-fallback.ts
|
|
248
|
+
/**
|
|
249
|
+
* PRF support check with a password-protected-key fallback.
|
|
250
|
+
* If the PRF WebAuthn extension is unavailable, callers can fall back to a
|
|
251
|
+
* password-protected private key. The private key is wrapped with a password-
|
|
252
|
+
* derived AES-GCM key and stored alongside the public key (SPKI).
|
|
253
|
+
*
|
|
254
|
+
* This is a minimal, browser-oriented implementation designed to provide a
|
|
255
|
+
* practical alternative while keeping crypto surface area focused and safe.
|
|
256
|
+
*/
|
|
248
257
|
function toBase64(bytes) {
|
|
249
258
|
const arr = new Uint8Array(bytes);
|
|
250
259
|
let binary = "";
|
|
@@ -358,6 +367,23 @@ async function importPublicKeyFromBundle(bundle) {
|
|
|
358
367
|
namedCurve: "P-256"
|
|
359
368
|
}, true, ["verify"]);
|
|
360
369
|
}
|
|
370
|
+
const DEFAULT_SALT = "nostr-key-derivation";
|
|
371
|
+
async function deriveNostrPrivateKey(password, salt = DEFAULT_SALT) {
|
|
372
|
+
const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
|
|
373
|
+
if (!cryptoObj || !cryptoObj.subtle) throw new Error("Web Crypto API not available");
|
|
374
|
+
const encoder = new TextEncoder();
|
|
375
|
+
const passwordKey = await cryptoObj.subtle.importKey("raw", encoder.encode(password), "PBKDF2", false, ["deriveBits"]);
|
|
376
|
+
const derivedBits = await cryptoObj.subtle.deriveBits({
|
|
377
|
+
name: "PBKDF2",
|
|
378
|
+
salt: encoder.encode(salt),
|
|
379
|
+
iterations: 1e5,
|
|
380
|
+
hash: "SHA-256"
|
|
381
|
+
}, passwordKey, 256);
|
|
382
|
+
return new Uint8Array(derivedBits);
|
|
383
|
+
}
|
|
384
|
+
async function getPublicKeyFromPassword(password, salt = DEFAULT_SALT) {
|
|
385
|
+
return (0, applesauce_core_helpers.getPublicKey)(await deriveNostrPrivateKey(password, salt));
|
|
386
|
+
}
|
|
361
387
|
|
|
362
388
|
//#endregion
|
|
363
389
|
//#region src/utils/nosskey.ts
|
|
@@ -366,6 +392,24 @@ async function importPublicKeyFromBundle(bundle) {
|
|
|
366
392
|
* @packageDocumentation
|
|
367
393
|
*/
|
|
368
394
|
const STANDARD_SALT = "6e6f7374722d6b6579";
|
|
395
|
+
function parseRecoveryTag(tags) {
|
|
396
|
+
const recoveryTag = tags.find((tag) => tag[0] === "r");
|
|
397
|
+
if (!recoveryTag || recoveryTag.length < 3) return null;
|
|
398
|
+
const createdAt = recoveryTag[3] ? parseInt(recoveryTag[3], 10) : void 0;
|
|
399
|
+
return {
|
|
400
|
+
recoveryPubkey: recoveryTag[1],
|
|
401
|
+
recoverySalt: recoveryTag[2],
|
|
402
|
+
createdAt: createdAt || void 0
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function createRecoveryTag(recovery) {
|
|
406
|
+
return [
|
|
407
|
+
"r",
|
|
408
|
+
recovery.recoveryPubkey,
|
|
409
|
+
recovery.recoverySalt,
|
|
410
|
+
recovery.createdAt?.toString() || ""
|
|
411
|
+
];
|
|
412
|
+
}
|
|
369
413
|
/**
|
|
370
414
|
* Nosskey - Passkey-Derived Nostr Keys
|
|
371
415
|
*/
|
|
@@ -550,11 +594,16 @@ var NosskeyManager = class {
|
|
|
550
594
|
* @param options
|
|
551
595
|
*/
|
|
552
596
|
async createKey(credentialId, password, options = {}) {
|
|
553
|
-
|
|
554
|
-
|
|
597
|
+
const prfSupported = await this.checkPRFSupport();
|
|
598
|
+
let keyInfo;
|
|
599
|
+
if (prfSupported) {
|
|
600
|
+
keyInfo = await this.createPrfNostrKey(credentialId, options);
|
|
601
|
+
if (options.recoveryPassword) keyInfo = await this.addPasswordRecovery(options.recoveryPassword, credentialId);
|
|
602
|
+
} else {
|
|
555
603
|
if (!password) throw new Error("Password is required when PRF is not supported");
|
|
556
|
-
|
|
604
|
+
keyInfo = await this.createPasswordProtectedNostrKey(password, options);
|
|
557
605
|
}
|
|
606
|
+
return keyInfo;
|
|
558
607
|
}
|
|
559
608
|
/**
|
|
560
609
|
* Create Nostr key using PRF (standard passkey flow)
|
|
@@ -574,24 +623,18 @@ var NosskeyManager = class {
|
|
|
574
623
|
return keyInfo;
|
|
575
624
|
}
|
|
576
625
|
/**
|
|
577
|
-
* Create Nostr key using password-
|
|
578
|
-
* @param password Password to
|
|
626
|
+
* Create Nostr key using password-derived key (fallback when PRF unavailable)
|
|
627
|
+
* @param password Password to derive the private key
|
|
579
628
|
* @param options
|
|
580
629
|
*/
|
|
581
630
|
async createPasswordProtectedNostrKey(password, options = {}) {
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
const publicKeyHex = bytesToHex(await (typeof window !== "undefined" ? window.crypto : globalThis.crypto).subtle.exportKey("spki", publicKey));
|
|
585
|
-
const keyInfo = {
|
|
631
|
+
const pubkey = await getPublicKeyFromPassword(password, STANDARD_SALT);
|
|
632
|
+
return {
|
|
586
633
|
credentialId: bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16))),
|
|
587
|
-
pubkey
|
|
634
|
+
pubkey,
|
|
588
635
|
salt: STANDARD_SALT,
|
|
589
|
-
passwordProtectedBundle: bundle,
|
|
590
636
|
...options.username && { username: options.username }
|
|
591
637
|
};
|
|
592
|
-
const sk = await unwrapPasswordProtectedPrivateKey(bundle, password);
|
|
593
|
-
if (this.#keyCache.isEnabled() && this.#keyCache.getCacheOptions().cacheOnCreation) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
594
|
-
return keyInfo;
|
|
595
638
|
}
|
|
596
639
|
/**
|
|
597
640
|
* @param event Nostr
|
|
@@ -601,9 +644,16 @@ var NosskeyManager = class {
|
|
|
601
644
|
async signEventWithKeyInfo(event, keyInfo, options = {}) {
|
|
602
645
|
const { clearMemory = true, tags, password } = options;
|
|
603
646
|
const shouldUseCache = this.#keyCache.isEnabled();
|
|
604
|
-
const
|
|
647
|
+
const isPasswordDerived = !keyInfo.passwordProtectedBundle && keyInfo.salt;
|
|
605
648
|
let sk;
|
|
606
|
-
if (
|
|
649
|
+
if (isPasswordDerived) {
|
|
650
|
+
if (shouldUseCache) sk = this.#keyCache.getKey(keyInfo.credentialId);
|
|
651
|
+
if (!sk && password) {
|
|
652
|
+
sk = await deriveNostrPrivateKey(password, keyInfo.salt);
|
|
653
|
+
if (shouldUseCache) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
654
|
+
}
|
|
655
|
+
if (!sk) throw new Error("Password required - key not in cache. Provide password to sign.");
|
|
656
|
+
} else if (keyInfo.passwordProtectedBundle) {
|
|
607
657
|
if (shouldUseCache) sk = this.#keyCache.getKey(keyInfo.credentialId);
|
|
608
658
|
if (!sk && password) {
|
|
609
659
|
sk = await unwrapPasswordProtectedPrivateKey(keyInfo.passwordProtectedBundle, password);
|
|
@@ -638,6 +688,10 @@ var NosskeyManager = class {
|
|
|
638
688
|
if (!options.password) throw new Error("Password is required for password-protected keys");
|
|
639
689
|
return bytesToHex(await unwrapPasswordProtectedPrivateKey(keyInfo.passwordProtectedBundle, options.password));
|
|
640
690
|
}
|
|
691
|
+
if (!keyInfo.passwordProtectedBundle && keyInfo.salt) {
|
|
692
|
+
if (!options.password) throw new Error("Password is required for password-derived keys");
|
|
693
|
+
return bytesToHex(await deriveNostrPrivateKey(options.password, keyInfo.salt));
|
|
694
|
+
}
|
|
641
695
|
let usedCredentialId = credentialId;
|
|
642
696
|
if (!usedCredentialId && keyInfo.credentialId) usedCredentialId = hexToBytes(keyInfo.credentialId);
|
|
643
697
|
const { secret: sk } = await getPrfSecret(usedCredentialId, this.#prfOptions);
|
|
@@ -650,6 +704,73 @@ var NosskeyManager = class {
|
|
|
650
704
|
return isPrfSupported();
|
|
651
705
|
}
|
|
652
706
|
/**
|
|
707
|
+
* Add password recovery to an existing PRF key
|
|
708
|
+
* @param password Password for recovery key
|
|
709
|
+
* @param currentCredentialId Current passkey credential ID
|
|
710
|
+
*/
|
|
711
|
+
async addPasswordRecovery(password, currentCredentialId) {
|
|
712
|
+
const keyInfo = this.getCurrentKeyInfo();
|
|
713
|
+
if (!keyInfo) throw new Error("No current KeyInfo set");
|
|
714
|
+
if (keyInfo.passwordProtectedBundle) throw new Error("Password recovery already exists for password-derived key");
|
|
715
|
+
if (keyInfo.recovery) throw new Error("Recovery already configured");
|
|
716
|
+
if (!(currentCredentialId || (keyInfo.credentialId ? hexToBytes(keyInfo.credentialId) : void 0))) throw new Error("Credential ID required");
|
|
717
|
+
const recoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
|
|
718
|
+
const recoveryPubkey = await getPublicKeyFromPassword(password, recoverySalt);
|
|
719
|
+
const updatedKeyInfo = {
|
|
720
|
+
...keyInfo,
|
|
721
|
+
recovery: {
|
|
722
|
+
recoveryPubkey,
|
|
723
|
+
recoverySalt,
|
|
724
|
+
createdAt: Date.now()
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
this.setCurrentKeyInfo(updatedKeyInfo);
|
|
728
|
+
return updatedKeyInfo;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Activate recovery using password
|
|
732
|
+
* Requires new credential ID from new device
|
|
733
|
+
* @param password Password for recovery key
|
|
734
|
+
* @param newCredentialId New passkey credential ID (required)
|
|
735
|
+
*/
|
|
736
|
+
async activateWithPassword(password, newCredentialId) {
|
|
737
|
+
const keyInfo = this.getCurrentKeyInfo();
|
|
738
|
+
if (!keyInfo) throw new Error("No current KeyInfo set");
|
|
739
|
+
if (!keyInfo.recovery) throw new Error("No recovery key configured");
|
|
740
|
+
if (!newCredentialId) throw new Error("New credential ID is required for recovery");
|
|
741
|
+
const { recoveryPubkey, recoverySalt } = keyInfo.recovery;
|
|
742
|
+
if (await getPublicKeyFromPassword(password, recoverySalt) !== recoveryPubkey) throw new Error("Invalid recovery password");
|
|
743
|
+
const { secret: newSk } = await getPrfSecret(newCredentialId, this.#prfOptions);
|
|
744
|
+
const newPubkey = (0, applesauce_core_helpers.getPublicKey)(newSk);
|
|
745
|
+
this.#clearKey(newSk);
|
|
746
|
+
const newRecoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
|
|
747
|
+
const newRecoveryPubkey = await getPublicKeyFromPassword(password, newRecoverySalt);
|
|
748
|
+
const updatedKeyInfo = {
|
|
749
|
+
credentialId: bytesToHex(newCredentialId),
|
|
750
|
+
pubkey: newPubkey,
|
|
751
|
+
salt: keyInfo.salt,
|
|
752
|
+
recovery: {
|
|
753
|
+
recoveryPubkey: newRecoveryPubkey,
|
|
754
|
+
recoverySalt: newRecoverySalt,
|
|
755
|
+
createdAt: Date.now()
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
this.setCurrentKeyInfo(updatedKeyInfo);
|
|
759
|
+
return updatedKeyInfo;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Get the recovery data for publishing to kind-0
|
|
763
|
+
*/
|
|
764
|
+
getRecoveryForKind0() {
|
|
765
|
+
const keyInfo = this.getCurrentKeyInfo();
|
|
766
|
+
if (!keyInfo || !keyInfo.recovery) return null;
|
|
767
|
+
return {
|
|
768
|
+
recoveryPubkey: keyInfo.recovery.recoveryPubkey,
|
|
769
|
+
recoverySalt: keyInfo.recovery.recoverySalt,
|
|
770
|
+
createdAt: keyInfo.recovery.createdAt
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
653
774
|
* @param key
|
|
654
775
|
*/
|
|
655
776
|
#clearKey(key) {
|
|
@@ -821,9 +942,13 @@ var AuthService = class {
|
|
|
821
942
|
/**
|
|
822
943
|
* Create a new Nostr key from a credential ID
|
|
823
944
|
* Automatically uses password fallback if PRF is not supported
|
|
945
|
+
* @param credentialId Passkey credential ID
|
|
946
|
+
* @param password Password (required if PRF not supported)
|
|
947
|
+
* @param options.username Username for the key
|
|
948
|
+
* @param options.recoveryPassword Password for recovery (enables recovery on new device)
|
|
824
949
|
*/
|
|
825
|
-
async createKey(credentialId, password) {
|
|
826
|
-
return await this.getManager().createKey(credentialId, password);
|
|
950
|
+
async createKey(credentialId, password, options) {
|
|
951
|
+
return await this.getManager().createKey(credentialId, password, options);
|
|
827
952
|
}
|
|
828
953
|
/**
|
|
829
954
|
* Check if PRF is supported, otherwise password fallback is needed
|
|
@@ -873,6 +998,28 @@ var AuthService = class {
|
|
|
873
998
|
async isPrfSupported() {
|
|
874
999
|
return this.checkPRFSupport();
|
|
875
1000
|
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Add password recovery to an existing PRF key
|
|
1003
|
+
* @param password Password for recovery key
|
|
1004
|
+
*/
|
|
1005
|
+
async addPasswordRecovery(password) {
|
|
1006
|
+
return await this.getManager().addPasswordRecovery(password);
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Activate recovery using password
|
|
1010
|
+
* Requires new credential ID from new device
|
|
1011
|
+
* @param password Password for recovery key
|
|
1012
|
+
* @param newCredentialId New passkey credential ID (required)
|
|
1013
|
+
*/
|
|
1014
|
+
async activateWithPassword(password, newCredentialId) {
|
|
1015
|
+
return await this.getManager().activateWithPassword(password, newCredentialId);
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Get recovery data for kind-0
|
|
1019
|
+
*/
|
|
1020
|
+
getRecoveryForKind0() {
|
|
1021
|
+
return this.getManager().getRecoveryForKind0();
|
|
1022
|
+
}
|
|
876
1023
|
};
|
|
877
1024
|
|
|
878
1025
|
//#endregion
|
|
@@ -1266,12 +1413,16 @@ exports.aesGcmEncrypt = aesGcmEncrypt;
|
|
|
1266
1413
|
exports.bytesToHex = bytesToHex;
|
|
1267
1414
|
exports.checkPRFSupport = checkPRFSupport;
|
|
1268
1415
|
exports.createPasskey = createPasskey;
|
|
1416
|
+
exports.createRecoveryTag = createRecoveryTag;
|
|
1269
1417
|
exports.deriveAesGcmKey = deriveAesGcmKey;
|
|
1418
|
+
exports.deriveNostrPrivateKey = deriveNostrPrivateKey;
|
|
1270
1419
|
exports.generatePasswordProtectedKey = generatePasswordProtectedKey;
|
|
1271
1420
|
exports.getPrfSecret = getPrfSecret;
|
|
1421
|
+
exports.getPublicKeyFromPassword = getPublicKeyFromPassword;
|
|
1272
1422
|
exports.hexToBytes = hexToBytes;
|
|
1273
1423
|
exports.importPublicKeyFromBundle = importPublicKeyFromBundle;
|
|
1274
1424
|
exports.isPrfSupported = isPrfSupported;
|
|
1425
|
+
exports.parseRecoveryTag = parseRecoveryTag;
|
|
1275
1426
|
exports.registerDummyPasskey = registerDummyPasskey;
|
|
1276
1427
|
exports.unwrapPasswordProtectedPrivateKey = unwrapPasswordProtectedPrivateKey;
|
|
1277
1428
|
var applesauce_core = require("applesauce-core");
|