ns-auth-sdk 1.11.0 → 1.12.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.
- package/README.md +40 -46
- package/dist/index.cjs +105 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +7 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +75 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -8
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Stoic Identity
|
|
2
2
|
|
|
3
3
|
_The simplest way of doing Auth with seamless and decentralized Key-Management for SSO, Authentication, Membership, and Profile-Management_
|
|
4
4
|
|
|
@@ -75,49 +75,32 @@ relayService.initialize(eventStore);
|
|
|
75
75
|
// Create a passkey (triggers biometric)
|
|
76
76
|
const credentialId = await authService.createPasskey('user@example.com');
|
|
77
77
|
|
|
78
|
-
// Create
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
// Create Key
|
|
79
|
+
const keyInfo = await authService.createKey(credentialId, undefined, {
|
|
80
|
+
username: 'user@example.com',
|
|
81
|
+
recoveryPassword: 'my-recovery-password', // optional
|
|
82
|
+
});
|
|
83
83
|
|
|
84
84
|
// Store keyInfo for later use
|
|
85
85
|
authService.setCurrentKeyInfo(keyInfo);
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
###
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
const event = {
|
|
92
|
-
kind: 1,
|
|
93
|
-
content: 'Hello Nostr!',
|
|
94
|
-
tags: [],
|
|
95
|
-
created_at: Math.floor(Date.now() / 1000),
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// Sign - no password needed if key was cached at creation
|
|
99
|
-
const signed = await authService.signEvent(event);
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Password Fallback
|
|
88
|
+
### Password Fallback (when PRF unavailable)
|
|
103
89
|
|
|
104
|
-
When PRF is not supported
|
|
90
|
+
When PRF is not supported, username is required and password is used to derive the key:
|
|
105
91
|
|
|
106
92
|
```typescript
|
|
107
|
-
import { AuthService, checkPRFSupport } from 'ns-auth-sdk';
|
|
108
|
-
|
|
109
93
|
// Check if password fallback is needed
|
|
110
|
-
const prfSupported = await checkPRFSupport();
|
|
94
|
+
const prfSupported = await authService.checkPRFSupport();
|
|
111
95
|
|
|
112
96
|
if (!prfSupported) {
|
|
113
|
-
|
|
97
|
+
// Username is REQUIRED when PRF unavailable
|
|
98
|
+
const keyInfo = await authService.createKey(undefined, userPassword, {
|
|
99
|
+
username: 'user@example.com',
|
|
100
|
+
});
|
|
114
101
|
}
|
|
115
102
|
|
|
116
|
-
//
|
|
117
|
-
const keyInfo = await authService.createNostrKey(credentialId, password);
|
|
118
|
-
|
|
119
|
-
// First sign: password used to decrypt and cache key
|
|
120
|
-
// Subsequent signs: uses cached key (no password needed)
|
|
103
|
+
// Sign events
|
|
121
104
|
const signedEvent = await authService.signEvent(event);
|
|
122
105
|
```
|
|
123
106
|
|
|
@@ -134,20 +117,23 @@ const signedEvent = await authService.signEvent(event);
|
|
|
134
117
|
- `hasKeyInfo(): boolean` - Check if key info exists
|
|
135
118
|
- `clearStoredKeyInfo(): void` - Clear stored key info
|
|
136
119
|
- `checkPRFSupport(): Promise<boolean>` - Check if PRF is supported
|
|
120
|
+
- `deriveSaltFromUsername(username?: string): Promise<string>` - Derive salt from username (SHA-256)
|
|
137
121
|
|
|
138
122
|
#### Recovery Methods
|
|
139
123
|
|
|
140
124
|
- `addPasswordRecovery(password: string): Promise<KeyInfo>` - Add password recovery to an existing PRF key
|
|
141
125
|
- `activateWithPassword(password: string, newCredentialId: Uint8Array): Promise<KeyInfo>` - Recover using password with a new passkey credential ID from a new device
|
|
142
126
|
- `getRecoveryForKind0(): RecoveryData | null` - Get recovery data for publishing to kind-0
|
|
127
|
+
- `parseRecoveryTag(tags: string[][]): KeyRecovery | null` - Parse recovery tag from event
|
|
128
|
+
- `verifyRecoverySignature(kind0: Event): Promise<boolean>` - Verify recovery signature (async)
|
|
143
129
|
|
|
144
130
|
#### KeyOptions
|
|
145
131
|
|
|
146
132
|
```typescript
|
|
147
133
|
interface KeyOptions {
|
|
148
|
-
username?: string;
|
|
149
|
-
password?: string;
|
|
150
|
-
recoveryPassword?: string;
|
|
134
|
+
username?: string; // Required when PRF unavailable
|
|
135
|
+
password?: string; // Required when PRF unavailable
|
|
136
|
+
recoveryPassword?: string; // Password for recovery (optional)
|
|
151
137
|
}
|
|
152
138
|
```
|
|
153
139
|
|
|
@@ -158,6 +144,7 @@ interface RecoveryData {
|
|
|
158
144
|
recoveryPubkey: string;
|
|
159
145
|
recoverySalt: string;
|
|
160
146
|
createdAt?: number;
|
|
147
|
+
signature?: string; // Schnorr signature from recovery key signing the current pubkey
|
|
161
148
|
}
|
|
162
149
|
```
|
|
163
150
|
|
|
@@ -178,17 +165,7 @@ interface KeyRecovery {
|
|
|
178
165
|
recoveryPubkey: string;
|
|
179
166
|
recoverySalt: string;
|
|
180
167
|
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;
|
|
168
|
+
signature?: string; // Schnorr signature from recovery key signing the current pubkey
|
|
192
169
|
}
|
|
193
170
|
|
|
194
171
|
// Sign options
|
|
@@ -211,7 +188,7 @@ const keyInfo = await authService.createKey(credentialId, undefined, {
|
|
|
211
188
|
});
|
|
212
189
|
|
|
213
190
|
// The recovery data is stored in kind-0 tags:
|
|
214
|
-
// ["r", recoveryPubkey, recoverySalt, createdAt]
|
|
191
|
+
// ["r", recoveryPubkey, recoverySalt, createdAt, signature]
|
|
215
192
|
```
|
|
216
193
|
|
|
217
194
|
**Recovery on a new device:**
|
|
@@ -224,6 +201,23 @@ const newCredentialId = await authService.createPasskey('user@example.com');
|
|
|
224
201
|
const keyInfo = await authService.activateWithPassword('my-recovery-password', newCredentialId);
|
|
225
202
|
```
|
|
226
203
|
|
|
204
|
+
**Verification:**
|
|
205
|
+
|
|
206
|
+
Anyone can verify ownership by fetching the kind-0 and checking the signature:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { parseRecoveryTag, verifyRecoverySignature } from 'ns-auth-sdk';
|
|
210
|
+
|
|
211
|
+
// Fetch kind-0 from relay
|
|
212
|
+
const kind0 = await relayService.fetchProfile(pubkey);
|
|
213
|
+
|
|
214
|
+
// Verify recovery signature (async)
|
|
215
|
+
const isValid = await verifyRecoverySignature(kind0);
|
|
216
|
+
if (isValid) {
|
|
217
|
+
console.log('Recovery key holder controls this identity');
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
227
221
|
#### Configuration Options
|
|
228
222
|
|
|
229
223
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,33 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
1
28
|
let applesauce_core_helpers = require("applesauce-core/helpers");
|
|
29
|
+
let _noble_secp256k1 = require("@noble/secp256k1");
|
|
30
|
+
_noble_secp256k1 = __toESM(_noble_secp256k1);
|
|
2
31
|
|
|
3
32
|
//#region src/utils/utils.ts
|
|
4
33
|
/**
|
|
@@ -391,7 +420,14 @@ async function getPublicKeyFromPassword(password, salt = DEFAULT_SALT) {
|
|
|
391
420
|
* Nosskey class for Passkey-Derived Nostr Identity
|
|
392
421
|
* @packageDocumentation
|
|
393
422
|
*/
|
|
394
|
-
|
|
423
|
+
async function deriveSaltFromUsername(username) {
|
|
424
|
+
if (!username) return "";
|
|
425
|
+
const msgBuffer = new TextEncoder().encode(username.toLowerCase().trim());
|
|
426
|
+
const subtle = globalThis.crypto?.subtle;
|
|
427
|
+
if (!subtle) throw new Error("Web Crypto API not available");
|
|
428
|
+
const hashBuffer = await subtle.digest("SHA-256", msgBuffer);
|
|
429
|
+
return bytesToHex(new Uint8Array(hashBuffer));
|
|
430
|
+
}
|
|
395
431
|
function parseRecoveryTag(tags) {
|
|
396
432
|
const recoveryTag = tags.find((tag) => tag[0] === "r");
|
|
397
433
|
if (!recoveryTag || recoveryTag.length < 3) return null;
|
|
@@ -399,7 +435,8 @@ function parseRecoveryTag(tags) {
|
|
|
399
435
|
return {
|
|
400
436
|
recoveryPubkey: recoveryTag[1],
|
|
401
437
|
recoverySalt: recoveryTag[2],
|
|
402
|
-
createdAt: createdAt || void 0
|
|
438
|
+
createdAt: createdAt || void 0,
|
|
439
|
+
signature: recoveryTag[4] || void 0
|
|
403
440
|
};
|
|
404
441
|
}
|
|
405
442
|
function createRecoveryTag(recovery) {
|
|
@@ -407,9 +444,35 @@ function createRecoveryTag(recovery) {
|
|
|
407
444
|
"r",
|
|
408
445
|
recovery.recoveryPubkey,
|
|
409
446
|
recovery.recoverySalt,
|
|
410
|
-
recovery.createdAt?.toString() || ""
|
|
447
|
+
recovery.createdAt?.toString() || "",
|
|
448
|
+
recovery.signature || ""
|
|
411
449
|
];
|
|
412
450
|
}
|
|
451
|
+
function getRecoverySignature(kind0) {
|
|
452
|
+
if (!parseRecoveryTag(kind0.tags || [])) return null;
|
|
453
|
+
const tag = kind0.tags?.find((t) => t[0] === "r");
|
|
454
|
+
return tag && tag.length > 4 ? tag[4] || null : null;
|
|
455
|
+
}
|
|
456
|
+
async function verifyRecoverySignature(kind0) {
|
|
457
|
+
try {
|
|
458
|
+
if (!parseRecoveryTag(kind0.tags || [])) return false;
|
|
459
|
+
const signature = getRecoverySignature(kind0);
|
|
460
|
+
if (!signature || !kind0.pubkey) return false;
|
|
461
|
+
const messageHash = await sha256(kind0.pubkey);
|
|
462
|
+
const signatureBytes = hexToBytes(signature);
|
|
463
|
+
const pubkeyBytes = hexToBytes(kind0.pubkey);
|
|
464
|
+
return _noble_secp256k1.verify(signatureBytes, messageHash, pubkeyBytes);
|
|
465
|
+
} catch (e) {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async function sha256(message) {
|
|
470
|
+
const msgBuffer = new TextEncoder().encode(message);
|
|
471
|
+
const subtle = globalThis.crypto?.subtle;
|
|
472
|
+
if (!subtle) throw new Error("Web Crypto API not available");
|
|
473
|
+
const hashBuffer = await subtle.digest("SHA-256", msgBuffer);
|
|
474
|
+
return new Uint8Array(hashBuffer);
|
|
475
|
+
}
|
|
413
476
|
/**
|
|
414
477
|
* Nosskey - Passkey-Derived Nostr Keys
|
|
415
478
|
*/
|
|
@@ -601,6 +664,7 @@ var NosskeyManager = class {
|
|
|
601
664
|
if (options.recoveryPassword) keyInfo = await this.addPasswordRecovery(options.recoveryPassword, credentialId);
|
|
602
665
|
} else {
|
|
603
666
|
if (!password) throw new Error("Password is required when PRF is not supported");
|
|
667
|
+
if (!options.username) throw new Error("Username is required when PRF is not supported");
|
|
604
668
|
keyInfo = await this.createPasswordProtectedNostrKey(password, options);
|
|
605
669
|
}
|
|
606
670
|
return keyInfo;
|
|
@@ -613,10 +677,11 @@ var NosskeyManager = class {
|
|
|
613
677
|
if (sk.every((byte) => byte === 0)) throw new Error("Invalid PRF output: all zeros");
|
|
614
678
|
bytesToHex(sk);
|
|
615
679
|
const publicKey = (0, applesauce_core_helpers.getPublicKey)(sk);
|
|
680
|
+
const salt = await deriveSaltFromUsername(options.username);
|
|
616
681
|
const keyInfo = {
|
|
617
682
|
credentialId: bytesToHex(credentialId || responseId),
|
|
618
683
|
pubkey: publicKey,
|
|
619
|
-
salt
|
|
684
|
+
salt,
|
|
620
685
|
...options.username && { username: options.username }
|
|
621
686
|
};
|
|
622
687
|
if (this.#keyCache.isEnabled() && this.#keyCache.getCacheOptions().cacheOnCreation) this.#keyCache.setKey(keyInfo.credentialId, sk);
|
|
@@ -628,11 +693,12 @@ var NosskeyManager = class {
|
|
|
628
693
|
* @param options
|
|
629
694
|
*/
|
|
630
695
|
async createPasswordProtectedNostrKey(password, options = {}) {
|
|
631
|
-
const
|
|
696
|
+
const salt = await deriveSaltFromUsername(options.username);
|
|
697
|
+
const pubkey = await getPublicKeyFromPassword(password, salt);
|
|
632
698
|
return {
|
|
633
699
|
credentialId: bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16))),
|
|
634
700
|
pubkey,
|
|
635
|
-
salt
|
|
701
|
+
salt,
|
|
636
702
|
...options.username && { username: options.username }
|
|
637
703
|
};
|
|
638
704
|
}
|
|
@@ -716,17 +782,29 @@ var NosskeyManager = class {
|
|
|
716
782
|
if (!(currentCredentialId || (keyInfo.credentialId ? hexToBytes(keyInfo.credentialId) : void 0))) throw new Error("Credential ID required");
|
|
717
783
|
const recoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
|
|
718
784
|
const recoveryPubkey = await getPublicKeyFromPassword(password, recoverySalt);
|
|
785
|
+
const recoverySk = await deriveNostrPrivateKey(password, recoverySalt);
|
|
786
|
+
const signature = this.#signWithKey(recoverySk, keyInfo.pubkey);
|
|
787
|
+
this.#clearKey(recoverySk);
|
|
719
788
|
const updatedKeyInfo = {
|
|
720
789
|
...keyInfo,
|
|
721
790
|
recovery: {
|
|
722
791
|
recoveryPubkey,
|
|
723
792
|
recoverySalt,
|
|
724
|
-
createdAt: Date.now()
|
|
793
|
+
createdAt: Date.now(),
|
|
794
|
+
signature
|
|
725
795
|
}
|
|
726
796
|
};
|
|
727
797
|
this.setCurrentKeyInfo(updatedKeyInfo);
|
|
728
798
|
return updatedKeyInfo;
|
|
729
799
|
}
|
|
800
|
+
#signWithKey(sk, message) {
|
|
801
|
+
return (0, applesauce_core_helpers.finalizeEvent)({
|
|
802
|
+
kind: 0,
|
|
803
|
+
content: "",
|
|
804
|
+
tags: [],
|
|
805
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
806
|
+
}, sk).sig;
|
|
807
|
+
}
|
|
730
808
|
/**
|
|
731
809
|
* Activate recovery using password
|
|
732
810
|
* Requires new credential ID from new device
|
|
@@ -745,6 +823,9 @@ var NosskeyManager = class {
|
|
|
745
823
|
this.#clearKey(newSk);
|
|
746
824
|
const newRecoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
|
|
747
825
|
const newRecoveryPubkey = await getPublicKeyFromPassword(password, newRecoverySalt);
|
|
826
|
+
const recoverySk = await deriveNostrPrivateKey(password, recoverySalt);
|
|
827
|
+
const signature = this.#signWithKey(recoverySk, newPubkey);
|
|
828
|
+
this.#clearKey(recoverySk);
|
|
748
829
|
const updatedKeyInfo = {
|
|
749
830
|
credentialId: bytesToHex(newCredentialId),
|
|
750
831
|
pubkey: newPubkey,
|
|
@@ -752,7 +833,8 @@ var NosskeyManager = class {
|
|
|
752
833
|
recovery: {
|
|
753
834
|
recoveryPubkey: newRecoveryPubkey,
|
|
754
835
|
recoverySalt: newRecoverySalt,
|
|
755
|
-
createdAt: Date.now()
|
|
836
|
+
createdAt: Date.now(),
|
|
837
|
+
signature
|
|
756
838
|
}
|
|
757
839
|
};
|
|
758
840
|
this.setCurrentKeyInfo(updatedKeyInfo);
|
|
@@ -767,7 +849,8 @@ var NosskeyManager = class {
|
|
|
767
849
|
return {
|
|
768
850
|
recoveryPubkey: keyInfo.recovery.recoveryPubkey,
|
|
769
851
|
recoverySalt: keyInfo.recovery.recoverySalt,
|
|
770
|
-
createdAt: keyInfo.recovery.createdAt
|
|
852
|
+
createdAt: keyInfo.recovery.createdAt,
|
|
853
|
+
signature: keyInfo.recovery.signature
|
|
771
854
|
};
|
|
772
855
|
}
|
|
773
856
|
/**
|
|
@@ -786,6 +869,11 @@ var NosskeyManager = class {
|
|
|
786
869
|
*/
|
|
787
870
|
const INFO_BYTES = new TextEncoder().encode("nostr-pwk");
|
|
788
871
|
const AES_LENGTH = 256;
|
|
872
|
+
function getSubtle() {
|
|
873
|
+
const subtle = globalThis.crypto?.subtle;
|
|
874
|
+
if (!subtle) throw new Error("Web Crypto API not available");
|
|
875
|
+
return subtle;
|
|
876
|
+
}
|
|
789
877
|
/**
|
|
790
878
|
* PRF AES-GCM
|
|
791
879
|
* @param secret PRF
|
|
@@ -793,8 +881,9 @@ const AES_LENGTH = 256;
|
|
|
793
881
|
* @returns AES-GCM
|
|
794
882
|
*/
|
|
795
883
|
async function deriveAesGcmKey(secret, salt) {
|
|
796
|
-
const
|
|
797
|
-
|
|
884
|
+
const subtle = getSubtle();
|
|
885
|
+
const keyMaterial = await subtle.importKey("raw", secret, "HKDF", false, ["deriveKey"]);
|
|
886
|
+
return subtle.deriveKey({
|
|
798
887
|
name: "HKDF",
|
|
799
888
|
hash: "SHA-256",
|
|
800
889
|
salt,
|
|
@@ -812,7 +901,7 @@ async function deriveAesGcmKey(secret, salt) {
|
|
|
812
901
|
* @returns
|
|
813
902
|
*/
|
|
814
903
|
async function aesGcmEncrypt(key, iv, plaintext) {
|
|
815
|
-
const buf = await
|
|
904
|
+
const buf = await getSubtle().encrypt({
|
|
816
905
|
name: "AES-GCM",
|
|
817
906
|
iv
|
|
818
907
|
}, key, plaintext);
|
|
@@ -831,7 +920,7 @@ async function aesGcmEncrypt(key, iv, plaintext) {
|
|
|
831
920
|
* @returns
|
|
832
921
|
*/
|
|
833
922
|
async function aesGcmDecrypt(key, iv, ct, tag) {
|
|
834
|
-
const buf = await
|
|
923
|
+
const buf = await getSubtle().decrypt({
|
|
835
924
|
name: "AES-GCM",
|
|
836
925
|
iv
|
|
837
926
|
}, key, new Uint8Array([...ct, ...tag]));
|
|
@@ -1416,15 +1505,18 @@ exports.createPasskey = createPasskey;
|
|
|
1416
1505
|
exports.createRecoveryTag = createRecoveryTag;
|
|
1417
1506
|
exports.deriveAesGcmKey = deriveAesGcmKey;
|
|
1418
1507
|
exports.deriveNostrPrivateKey = deriveNostrPrivateKey;
|
|
1508
|
+
exports.deriveSaltFromUsername = deriveSaltFromUsername;
|
|
1419
1509
|
exports.generatePasswordProtectedKey = generatePasswordProtectedKey;
|
|
1420
1510
|
exports.getPrfSecret = getPrfSecret;
|
|
1421
1511
|
exports.getPublicKeyFromPassword = getPublicKeyFromPassword;
|
|
1512
|
+
exports.getRecoverySignature = getRecoverySignature;
|
|
1422
1513
|
exports.hexToBytes = hexToBytes;
|
|
1423
1514
|
exports.importPublicKeyFromBundle = importPublicKeyFromBundle;
|
|
1424
1515
|
exports.isPrfSupported = isPrfSupported;
|
|
1425
1516
|
exports.parseRecoveryTag = parseRecoveryTag;
|
|
1426
1517
|
exports.registerDummyPasskey = registerDummyPasskey;
|
|
1427
1518
|
exports.unwrapPasswordProtectedPrivateKey = unwrapPasswordProtectedPrivateKey;
|
|
1519
|
+
exports.verifyRecoverySignature = verifyRecoverySignature;
|
|
1428
1520
|
var applesauce_core = require("applesauce-core");
|
|
1429
1521
|
Object.keys(applesauce_core).forEach(function (k) {
|
|
1430
1522
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|