@kingdomchain/wallet-client 0.6.2 → 0.8.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 +25 -1
- package/dist/index.d.ts +321 -12
- package/dist/index.js +763 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1940,6 +1940,7 @@ var EMBEDDED_WALLET_VAULT_VERSION = 1;
|
|
|
1940
1940
|
var EMBEDDED_WALLET_KDF_PRF = "webauthn-prf";
|
|
1941
1941
|
var EMBEDDED_WALLET_KDF_PBKDF2 = "pbkdf2-passkey";
|
|
1942
1942
|
var EMBEDDED_WALLET_KDF_PIN = "pbkdf2-pin";
|
|
1943
|
+
var EMBEDDED_WALLET_KDF_DEVICE_PIN = "device-pin";
|
|
1943
1944
|
var EMBEDDED_WALLET_KEY_HD_DERIVED = "hd-derived";
|
|
1944
1945
|
var EMBEDDED_WALLET_KEY_DIRECT_ENTROPY = "direct-entropy";
|
|
1945
1946
|
var EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING = {
|
|
@@ -1994,6 +1995,23 @@ var EmbeddedWalletWeakPasscodeError = class extends Error {
|
|
|
1994
1995
|
this.reason = reason;
|
|
1995
1996
|
}
|
|
1996
1997
|
};
|
|
1998
|
+
var EmbeddedWalletPasskeyUnsupportedError = class extends EmbeddedWalletVaultError {
|
|
1999
|
+
code = "passkey_prf_unsupported";
|
|
2000
|
+
constructor(message = "This device cannot protect the wallet with a passkey; keep using the passcode") {
|
|
2001
|
+
super(message);
|
|
2002
|
+
this.name = "EmbeddedWalletPasskeyUnsupportedError";
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
var EmbeddedWalletQuickUnlockError = class extends Error {
|
|
2006
|
+
code;
|
|
2007
|
+
attemptsLeft;
|
|
2008
|
+
constructor(code, message, attemptsLeft) {
|
|
2009
|
+
super(message);
|
|
2010
|
+
this.name = "EmbeddedWalletQuickUnlockError";
|
|
2011
|
+
this.code = code;
|
|
2012
|
+
this.attemptsLeft = attemptsLeft;
|
|
2013
|
+
}
|
|
2014
|
+
};
|
|
1997
2015
|
var DEFAULT_HD_PATH = "m/44'/118'/0'/0/0";
|
|
1998
2016
|
async function embeddedPlaintextToSigner(plaintext, prefix = BECH32_PREFIX) {
|
|
1999
2017
|
if (plaintext.privateKey.length !== 32) {
|
|
@@ -2321,6 +2339,30 @@ function assertVaultPermitsTransaction(vault) {
|
|
|
2321
2339
|
throw new EmbeddedWalletBackupRequiredError();
|
|
2322
2340
|
}
|
|
2323
2341
|
}
|
|
2342
|
+
async function encryptPlaintextWithKek(payload, kek) {
|
|
2343
|
+
const iv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES));
|
|
2344
|
+
const aesKey = await importAesKey(kek);
|
|
2345
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
2346
|
+
{ name: "AES-GCM", iv },
|
|
2347
|
+
aesKey,
|
|
2348
|
+
new Uint8Array(serializePlaintext(payload))
|
|
2349
|
+
);
|
|
2350
|
+
return { iv: toBase64(iv), ciphertext: toBase64(new Uint8Array(ciphertext)) };
|
|
2351
|
+
}
|
|
2352
|
+
async function decryptPlaintextWithKek(iv, ciphertext, kek) {
|
|
2353
|
+
const aesKey = await importAesKey(kek);
|
|
2354
|
+
let plaintextBytes;
|
|
2355
|
+
try {
|
|
2356
|
+
plaintextBytes = await crypto.subtle.decrypt(
|
|
2357
|
+
{ name: "AES-GCM", iv: new Uint8Array(fromBase64(iv)) },
|
|
2358
|
+
aesKey,
|
|
2359
|
+
new Uint8Array(fromBase64(ciphertext))
|
|
2360
|
+
);
|
|
2361
|
+
} catch (err) {
|
|
2362
|
+
throw new EmbeddedWalletDecryptError(err);
|
|
2363
|
+
}
|
|
2364
|
+
return deserializePlaintext(new Uint8Array(plaintextBytes));
|
|
2365
|
+
}
|
|
2324
2366
|
|
|
2325
2367
|
// src/embeddedWallet/prf.ts
|
|
2326
2368
|
function detectWebAuthnPrfCapabilities() {
|
|
@@ -2331,15 +2373,6 @@ function detectWebAuthnPrfCapabilities() {
|
|
|
2331
2373
|
prfFallbackReason: "WebAuthn is not available in this environment"
|
|
2332
2374
|
};
|
|
2333
2375
|
}
|
|
2334
|
-
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
2335
|
-
const isSafari = /Safari/i.test(ua) && !/Chrome|Chromium|CriOS|FxiOS/i.test(ua);
|
|
2336
|
-
if (isSafari) {
|
|
2337
|
-
return {
|
|
2338
|
-
webAuthnAvailable: true,
|
|
2339
|
-
prfLikelySupported: false,
|
|
2340
|
-
prfFallbackReason: "Safari does not support WebAuthn PRF for cross-device QR flows; use PBKDF2 fallback"
|
|
2341
|
-
};
|
|
2342
|
-
}
|
|
2343
2376
|
const getCaps = PublicKeyCredential.getClientCapabilities;
|
|
2344
2377
|
if (typeof getCaps !== "function") {
|
|
2345
2378
|
return {
|
|
@@ -2370,6 +2403,11 @@ function selectEmbeddedWalletKdf(caps, prfOutput) {
|
|
|
2370
2403
|
|
|
2371
2404
|
// src/embeddedWallet/backup.ts
|
|
2372
2405
|
function exportEncryptedBackup(vault) {
|
|
2406
|
+
if (vault.kdf === "device-pin") {
|
|
2407
|
+
throw new Error(
|
|
2408
|
+
"A PIN wallet is bound to this device and has no backup file; its backup is the 24-word recovery phrase"
|
|
2409
|
+
);
|
|
2410
|
+
}
|
|
2373
2411
|
return {
|
|
2374
2412
|
format: "kingdom-os-embedded-wallet-backup",
|
|
2375
2413
|
version: EMBEDDED_WALLET_VAULT_VERSION,
|
|
@@ -2519,9 +2557,11 @@ function decodeCredentialId(encoded) {
|
|
|
2519
2557
|
|
|
2520
2558
|
// src/embeddedWallet/storage.ts
|
|
2521
2559
|
var DB_NAME = "kingdom-os-wallet";
|
|
2522
|
-
var DB_VERSION =
|
|
2560
|
+
var DB_VERSION = 4;
|
|
2523
2561
|
var VAULT_STORE_NAME = "vault";
|
|
2524
2562
|
var LOCKOUT_STORE_NAME = "passcode-lockout";
|
|
2563
|
+
var SESSION_STORE_NAME = "session-cache";
|
|
2564
|
+
var QUICK_UNLOCK_STORE_NAME = "quick-unlock";
|
|
2525
2565
|
var VAULT_LEGACY_KEY = "primary";
|
|
2526
2566
|
function resolveVaultStorageKey(scopeId) {
|
|
2527
2567
|
const trimmed = scopeId?.trim();
|
|
@@ -2539,6 +2579,12 @@ function openDb() {
|
|
|
2539
2579
|
if (!db.objectStoreNames.contains(LOCKOUT_STORE_NAME)) {
|
|
2540
2580
|
db.createObjectStore(LOCKOUT_STORE_NAME);
|
|
2541
2581
|
}
|
|
2582
|
+
if (!db.objectStoreNames.contains(SESSION_STORE_NAME)) {
|
|
2583
|
+
db.createObjectStore(SESSION_STORE_NAME);
|
|
2584
|
+
}
|
|
2585
|
+
if (!db.objectStoreNames.contains(QUICK_UNLOCK_STORE_NAME)) {
|
|
2586
|
+
db.createObjectStore(QUICK_UNLOCK_STORE_NAME);
|
|
2587
|
+
}
|
|
2542
2588
|
};
|
|
2543
2589
|
request.onsuccess = () => resolve(request.result);
|
|
2544
2590
|
});
|
|
@@ -2613,6 +2659,149 @@ async function clearPasscodeLockoutRecord(publicAddress) {
|
|
|
2613
2659
|
});
|
|
2614
2660
|
db.close();
|
|
2615
2661
|
}
|
|
2662
|
+
async function saveSessionCacheRecord(record, options = {}) {
|
|
2663
|
+
const key = resolveVaultStorageKey(options.scopeId);
|
|
2664
|
+
const db = await openDb();
|
|
2665
|
+
await new Promise((resolve, reject) => {
|
|
2666
|
+
const tx = db.transaction(SESSION_STORE_NAME, "readwrite");
|
|
2667
|
+
tx.objectStore(SESSION_STORE_NAME).put(record, key);
|
|
2668
|
+
tx.oncomplete = () => resolve();
|
|
2669
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB write failed"));
|
|
2670
|
+
});
|
|
2671
|
+
db.close();
|
|
2672
|
+
}
|
|
2673
|
+
async function loadSessionCacheRecord(options = {}) {
|
|
2674
|
+
const key = resolveVaultStorageKey(options.scopeId);
|
|
2675
|
+
const db = await openDb();
|
|
2676
|
+
const record = await new Promise((resolve, reject) => {
|
|
2677
|
+
const tx = db.transaction(SESSION_STORE_NAME, "readonly");
|
|
2678
|
+
const request = tx.objectStore(SESSION_STORE_NAME).get(key);
|
|
2679
|
+
request.onsuccess = () => resolve(request.result ?? null);
|
|
2680
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB read failed"));
|
|
2681
|
+
});
|
|
2682
|
+
db.close();
|
|
2683
|
+
return record;
|
|
2684
|
+
}
|
|
2685
|
+
async function clearSessionCacheRecord(options = {}) {
|
|
2686
|
+
const key = resolveVaultStorageKey(options.scopeId);
|
|
2687
|
+
const db = await openDb();
|
|
2688
|
+
await new Promise((resolve, reject) => {
|
|
2689
|
+
const tx = db.transaction(SESSION_STORE_NAME, "readwrite");
|
|
2690
|
+
tx.objectStore(SESSION_STORE_NAME).delete(key);
|
|
2691
|
+
tx.oncomplete = () => resolve();
|
|
2692
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB delete failed"));
|
|
2693
|
+
});
|
|
2694
|
+
db.close();
|
|
2695
|
+
}
|
|
2696
|
+
async function saveQuickUnlockRecord(record, options = {}) {
|
|
2697
|
+
const key = resolveVaultStorageKey(options.scopeId);
|
|
2698
|
+
const db = await openDb();
|
|
2699
|
+
await new Promise((resolve, reject) => {
|
|
2700
|
+
const tx = db.transaction(QUICK_UNLOCK_STORE_NAME, "readwrite");
|
|
2701
|
+
tx.objectStore(QUICK_UNLOCK_STORE_NAME).put(record, key);
|
|
2702
|
+
tx.oncomplete = () => resolve();
|
|
2703
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB write failed"));
|
|
2704
|
+
});
|
|
2705
|
+
db.close();
|
|
2706
|
+
}
|
|
2707
|
+
async function loadQuickUnlockRecord(options = {}) {
|
|
2708
|
+
const key = resolveVaultStorageKey(options.scopeId);
|
|
2709
|
+
const db = await openDb();
|
|
2710
|
+
const record = await new Promise((resolve, reject) => {
|
|
2711
|
+
const tx = db.transaction(QUICK_UNLOCK_STORE_NAME, "readonly");
|
|
2712
|
+
const request = tx.objectStore(QUICK_UNLOCK_STORE_NAME).get(key);
|
|
2713
|
+
request.onsuccess = () => resolve(request.result ?? null);
|
|
2714
|
+
request.onerror = () => reject(request.error ?? new Error("IndexedDB read failed"));
|
|
2715
|
+
});
|
|
2716
|
+
db.close();
|
|
2717
|
+
return record;
|
|
2718
|
+
}
|
|
2719
|
+
async function clearQuickUnlockRecord(options = {}) {
|
|
2720
|
+
const key = resolveVaultStorageKey(options.scopeId);
|
|
2721
|
+
const db = await openDb();
|
|
2722
|
+
await new Promise((resolve, reject) => {
|
|
2723
|
+
const tx = db.transaction(QUICK_UNLOCK_STORE_NAME, "readwrite");
|
|
2724
|
+
tx.objectStore(QUICK_UNLOCK_STORE_NAME).delete(key);
|
|
2725
|
+
tx.oncomplete = () => resolve();
|
|
2726
|
+
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB delete failed"));
|
|
2727
|
+
});
|
|
2728
|
+
db.close();
|
|
2729
|
+
}
|
|
2730
|
+
var EMBEDDED_WALLET_SESSION_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
2731
|
+
var AES_GCM_IV_BYTES2 = 12;
|
|
2732
|
+
function serializeSessionPlaintext(plaintext) {
|
|
2733
|
+
const wire = {
|
|
2734
|
+
privateKey: toBase64(plaintext.privateKey),
|
|
2735
|
+
hdPath: plaintext.hdPath,
|
|
2736
|
+
createdAt: plaintext.createdAt,
|
|
2737
|
+
keyType: plaintext.keyType ?? EMBEDDED_WALLET_KEY_DIRECT_ENTROPY
|
|
2738
|
+
};
|
|
2739
|
+
if (plaintext.mnemonic) {
|
|
2740
|
+
wire.mnemonic = plaintext.mnemonic;
|
|
2741
|
+
}
|
|
2742
|
+
return new TextEncoder().encode(JSON.stringify(wire));
|
|
2743
|
+
}
|
|
2744
|
+
function deserializeSessionPlaintext(bytes) {
|
|
2745
|
+
const wire = JSON.parse(new TextDecoder().decode(bytes));
|
|
2746
|
+
const plaintext = {
|
|
2747
|
+
privateKey: fromBase64(wire.privateKey),
|
|
2748
|
+
hdPath: wire.hdPath,
|
|
2749
|
+
createdAt: wire.createdAt,
|
|
2750
|
+
keyType: wire.keyType ?? EMBEDDED_WALLET_KEY_DIRECT_ENTROPY
|
|
2751
|
+
};
|
|
2752
|
+
if (wire.mnemonic) {
|
|
2753
|
+
plaintext.mnemonic = wire.mnemonic;
|
|
2754
|
+
}
|
|
2755
|
+
return plaintext;
|
|
2756
|
+
}
|
|
2757
|
+
async function generateNonExtractableSessionKey() {
|
|
2758
|
+
return crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
|
|
2759
|
+
"encrypt",
|
|
2760
|
+
"decrypt"
|
|
2761
|
+
]);
|
|
2762
|
+
}
|
|
2763
|
+
async function cacheUnlockedSession(options) {
|
|
2764
|
+
const { plaintext, ttlMs, ...storageOptions } = options;
|
|
2765
|
+
const key = await generateNonExtractableSessionKey();
|
|
2766
|
+
const iv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES2));
|
|
2767
|
+
const material = serializeSessionPlaintext(plaintext);
|
|
2768
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
2769
|
+
{ name: "AES-GCM", iv },
|
|
2770
|
+
key,
|
|
2771
|
+
new Uint8Array(material)
|
|
2772
|
+
);
|
|
2773
|
+
const record = {
|
|
2774
|
+
key,
|
|
2775
|
+
iv: toBase64(iv),
|
|
2776
|
+
ciphertext: toBase64(new Uint8Array(ciphertext)),
|
|
2777
|
+
expiresAt: Date.now() + (ttlMs ?? EMBEDDED_WALLET_SESSION_TTL_MS)
|
|
2778
|
+
};
|
|
2779
|
+
await saveSessionCacheRecord(record, storageOptions);
|
|
2780
|
+
}
|
|
2781
|
+
async function restoreCachedSessionPlaintext(options = {}) {
|
|
2782
|
+
const record = await loadSessionCacheRecord(options);
|
|
2783
|
+
if (!record) {
|
|
2784
|
+
return null;
|
|
2785
|
+
}
|
|
2786
|
+
if (typeof record.expiresAt !== "number" || Date.now() >= record.expiresAt) {
|
|
2787
|
+
await clearSessionCacheRecord(options).catch(() => void 0);
|
|
2788
|
+
return null;
|
|
2789
|
+
}
|
|
2790
|
+
try {
|
|
2791
|
+
const plaintextBytes = await crypto.subtle.decrypt(
|
|
2792
|
+
{ name: "AES-GCM", iv: new Uint8Array(fromBase64(record.iv)) },
|
|
2793
|
+
record.key,
|
|
2794
|
+
new Uint8Array(fromBase64(record.ciphertext))
|
|
2795
|
+
);
|
|
2796
|
+
return deserializeSessionPlaintext(new Uint8Array(plaintextBytes));
|
|
2797
|
+
} catch {
|
|
2798
|
+
await clearSessionCacheRecord(options).catch(() => void 0);
|
|
2799
|
+
return null;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
async function clearUnlockedSession(options = {}) {
|
|
2803
|
+
await clearSessionCacheRecord(options);
|
|
2804
|
+
}
|
|
2616
2805
|
|
|
2617
2806
|
// src/embeddedWallet/lockout.ts
|
|
2618
2807
|
var PASSCODE_LOCKOUT_FAILURE_THRESHOLD = 5;
|
|
@@ -2777,10 +2966,264 @@ function assertPasscodeAllowedForCreation(passcode) {
|
|
|
2777
2966
|
throw new EmbeddedWalletWeakPasscodeError(strength.blockReason ?? "too_weak");
|
|
2778
2967
|
}
|
|
2779
2968
|
}
|
|
2969
|
+
var QUICK_UNLOCK_PIN_LENGTH = 6;
|
|
2970
|
+
var QUICK_UNLOCK_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2971
|
+
var QUICK_UNLOCK_MAX_ATTEMPTS = 5;
|
|
2972
|
+
var QUICK_UNLOCK_PBKDF2_ITERATIONS = 6e5;
|
|
2973
|
+
var AES_GCM_IV_BYTES3 = 12;
|
|
2974
|
+
function assertQuickUnlockPinAllowed(pin) {
|
|
2975
|
+
if (typeof pin !== "string" || !new RegExp(`^\\d{${QUICK_UNLOCK_PIN_LENGTH}}$`).test(pin)) {
|
|
2976
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
2977
|
+
"invalid_pin",
|
|
2978
|
+
`PIN must be exactly ${QUICK_UNLOCK_PIN_LENGTH} digits`
|
|
2979
|
+
);
|
|
2980
|
+
}
|
|
2981
|
+
if (isTrivialPin(pin)) {
|
|
2982
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
2983
|
+
"invalid_pin",
|
|
2984
|
+
"PIN is too easy to guess; avoid repeats and sequences like 123456"
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
function isTrivialPin(pin) {
|
|
2989
|
+
const digits = pin.split("").map(Number);
|
|
2990
|
+
const steps = digits.slice(1).map((d, i) => d - digits[i]);
|
|
2991
|
+
if (steps.every((s) => s === 0)) return true;
|
|
2992
|
+
if (steps.every((s) => s === 1) || steps.every((s) => s === -1)) return true;
|
|
2993
|
+
for (const period of [2, 3]) {
|
|
2994
|
+
if (pin === pin.slice(0, period).repeat(pin.length / period)) return true;
|
|
2995
|
+
}
|
|
2996
|
+
return false;
|
|
2997
|
+
}
|
|
2998
|
+
async function derivePinKey(pin, salt, iterations) {
|
|
2999
|
+
const material = await crypto.subtle.importKey(
|
|
3000
|
+
"raw",
|
|
3001
|
+
new TextEncoder().encode(pin),
|
|
3002
|
+
"PBKDF2",
|
|
3003
|
+
false,
|
|
3004
|
+
["deriveKey"]
|
|
3005
|
+
);
|
|
3006
|
+
return crypto.subtle.deriveKey(
|
|
3007
|
+
{ name: "PBKDF2", salt: new Uint8Array(salt), iterations, hash: "SHA-256" },
|
|
3008
|
+
material,
|
|
3009
|
+
{ name: "AES-GCM", length: 256 },
|
|
3010
|
+
false,
|
|
3011
|
+
["encrypt", "decrypt"]
|
|
3012
|
+
);
|
|
3013
|
+
}
|
|
3014
|
+
async function createQuickUnlockRecord(options) {
|
|
3015
|
+
const { plaintext, publicAddress, pin, ttlMs, iterations, ...storageOptions } = options;
|
|
3016
|
+
assertQuickUnlockPinAllowed(pin);
|
|
3017
|
+
const rounds = iterations ?? QUICK_UNLOCK_PBKDF2_ITERATIONS;
|
|
3018
|
+
const salt = crypto.getRandomValues(new Uint8Array(32));
|
|
3019
|
+
const pinKey = await derivePinKey(pin, salt, rounds);
|
|
3020
|
+
const deviceKey = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
|
|
3021
|
+
"encrypt",
|
|
3022
|
+
"decrypt"
|
|
3023
|
+
]);
|
|
3024
|
+
const innerIv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES3));
|
|
3025
|
+
const inner = await crypto.subtle.encrypt(
|
|
3026
|
+
{ name: "AES-GCM", iv: innerIv },
|
|
3027
|
+
pinKey,
|
|
3028
|
+
new Uint8Array(serializePlaintext(plaintext))
|
|
3029
|
+
);
|
|
3030
|
+
const outerIv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES3));
|
|
3031
|
+
const outer = await crypto.subtle.encrypt({ name: "AES-GCM", iv: outerIv }, deviceKey, inner);
|
|
3032
|
+
const record = {
|
|
3033
|
+
deviceKey,
|
|
3034
|
+
publicAddress,
|
|
3035
|
+
salt: toBase64(salt),
|
|
3036
|
+
iterations: rounds,
|
|
3037
|
+
innerIv: toBase64(innerIv),
|
|
3038
|
+
outerIv: toBase64(outerIv),
|
|
3039
|
+
ciphertext: toBase64(new Uint8Array(outer)),
|
|
3040
|
+
expiresAt: Date.now() + (ttlMs ?? QUICK_UNLOCK_TTL_MS),
|
|
3041
|
+
failedAttempts: 0
|
|
3042
|
+
};
|
|
3043
|
+
await saveQuickUnlockRecord(record, storageOptions);
|
|
3044
|
+
return record;
|
|
3045
|
+
}
|
|
3046
|
+
async function openQuickUnlockRecord(options) {
|
|
3047
|
+
const { pin, ...storageOptions } = options;
|
|
3048
|
+
if (typeof pin !== "string" || !/^\d+$/.test(pin) || pin.length !== QUICK_UNLOCK_PIN_LENGTH) {
|
|
3049
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
3050
|
+
"invalid_pin",
|
|
3051
|
+
`PIN must be exactly ${QUICK_UNLOCK_PIN_LENGTH} digits`
|
|
3052
|
+
);
|
|
3053
|
+
}
|
|
3054
|
+
const record = await loadQuickUnlockRecord(storageOptions);
|
|
3055
|
+
if (!record) {
|
|
3056
|
+
throw new EmbeddedWalletQuickUnlockError("not_set", "No PIN is set up on this device");
|
|
3057
|
+
}
|
|
3058
|
+
if (typeof record.expiresAt !== "number" || Date.now() >= record.expiresAt) {
|
|
3059
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
3060
|
+
"expired",
|
|
3061
|
+
"PIN expired; unlock with Face ID or your passcode"
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
const attempt = (record.failedAttempts ?? 0) + 1;
|
|
3065
|
+
await saveQuickUnlockRecord({ ...record, failedAttempts: attempt }, storageOptions);
|
|
3066
|
+
let inner;
|
|
3067
|
+
try {
|
|
3068
|
+
inner = await crypto.subtle.decrypt(
|
|
3069
|
+
{ name: "AES-GCM", iv: new Uint8Array(fromBase64(record.outerIv)) },
|
|
3070
|
+
record.deviceKey,
|
|
3071
|
+
new Uint8Array(fromBase64(record.ciphertext))
|
|
3072
|
+
);
|
|
3073
|
+
} catch {
|
|
3074
|
+
await clearQuickUnlockRecord(storageOptions).catch(() => void 0);
|
|
3075
|
+
throw new EmbeddedWalletQuickUnlockError("not_set", "PIN record is unusable on this device");
|
|
3076
|
+
}
|
|
3077
|
+
const pinKey = await derivePinKey(pin, fromBase64(record.salt), record.iterations);
|
|
3078
|
+
let plaintextBytes;
|
|
3079
|
+
try {
|
|
3080
|
+
plaintextBytes = await crypto.subtle.decrypt(
|
|
3081
|
+
{ name: "AES-GCM", iv: new Uint8Array(fromBase64(record.innerIv)) },
|
|
3082
|
+
pinKey,
|
|
3083
|
+
inner
|
|
3084
|
+
);
|
|
3085
|
+
} catch {
|
|
3086
|
+
if (attempt >= QUICK_UNLOCK_MAX_ATTEMPTS) {
|
|
3087
|
+
await clearQuickUnlockRecord(storageOptions).catch(() => void 0);
|
|
3088
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
3089
|
+
"wiped",
|
|
3090
|
+
"Too many wrong PINs; unlock with Face ID or your passcode"
|
|
3091
|
+
);
|
|
3092
|
+
}
|
|
3093
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
3094
|
+
"wrong_pin",
|
|
3095
|
+
"Wrong PIN",
|
|
3096
|
+
QUICK_UNLOCK_MAX_ATTEMPTS - attempt
|
|
3097
|
+
);
|
|
3098
|
+
}
|
|
3099
|
+
await saveQuickUnlockRecord({ ...record, failedAttempts: 0 }, storageOptions);
|
|
3100
|
+
return {
|
|
3101
|
+
plaintext: deserializePlaintext(new Uint8Array(plaintextBytes)),
|
|
3102
|
+
publicAddress: record.publicAddress
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
async function getQuickUnlockStatus(options = {}) {
|
|
3106
|
+
const record = await loadQuickUnlockRecord(options);
|
|
3107
|
+
if (!record) {
|
|
3108
|
+
return {
|
|
3109
|
+
enabled: false,
|
|
3110
|
+
expired: false,
|
|
3111
|
+
expiresAt: null,
|
|
3112
|
+
attemptsLeft: QUICK_UNLOCK_MAX_ATTEMPTS,
|
|
3113
|
+
publicAddress: null
|
|
3114
|
+
};
|
|
3115
|
+
}
|
|
3116
|
+
return {
|
|
3117
|
+
enabled: true,
|
|
3118
|
+
expired: typeof record.expiresAt !== "number" || Date.now() >= record.expiresAt,
|
|
3119
|
+
expiresAt: record.expiresAt,
|
|
3120
|
+
attemptsLeft: Math.max(0, QUICK_UNLOCK_MAX_ATTEMPTS - (record.failedAttempts ?? 0)),
|
|
3121
|
+
publicAddress: record.publicAddress
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
async function refreshQuickUnlockExpiry(options) {
|
|
3125
|
+
const { publicAddress, ttlMs, ...storageOptions } = options;
|
|
3126
|
+
const record = await loadQuickUnlockRecord(storageOptions);
|
|
3127
|
+
if (!record) return;
|
|
3128
|
+
if (record.publicAddress !== publicAddress) {
|
|
3129
|
+
await clearQuickUnlockRecord(storageOptions);
|
|
3130
|
+
return;
|
|
3131
|
+
}
|
|
3132
|
+
await saveQuickUnlockRecord(
|
|
3133
|
+
{ ...record, expiresAt: Date.now() + (ttlMs ?? QUICK_UNLOCK_TTL_MS), failedAttempts: 0 },
|
|
3134
|
+
storageOptions
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
async function clearQuickUnlock(options = {}) {
|
|
3138
|
+
await clearQuickUnlockRecord(options);
|
|
3139
|
+
}
|
|
3140
|
+
var DEVICE_PIN_PBKDF2_ITERATIONS = 6e5;
|
|
3141
|
+
var AES_GCM_IV_BYTES4 = 12;
|
|
3142
|
+
function vaultUsesPinUnlock(vault) {
|
|
3143
|
+
return vault.kdf === EMBEDDED_WALLET_KDF_DEVICE_PIN;
|
|
3144
|
+
}
|
|
3145
|
+
async function encryptPlaintextWithDevicePin(plaintext, pin, options) {
|
|
3146
|
+
assertQuickUnlockPinAllowed(pin);
|
|
3147
|
+
const iterations = options.iterations ?? DEVICE_PIN_PBKDF2_ITERATIONS;
|
|
3148
|
+
const salt = crypto.getRandomValues(new Uint8Array(32));
|
|
3149
|
+
const pinKey = await derivePinKey(pin, salt, iterations);
|
|
3150
|
+
const deviceKey = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
|
|
3151
|
+
"encrypt",
|
|
3152
|
+
"decrypt"
|
|
3153
|
+
]);
|
|
3154
|
+
const iv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES4));
|
|
3155
|
+
const inner = await crypto.subtle.encrypt(
|
|
3156
|
+
{ name: "AES-GCM", iv },
|
|
3157
|
+
pinKey,
|
|
3158
|
+
new Uint8Array(serializePlaintext(plaintext))
|
|
3159
|
+
);
|
|
3160
|
+
const deviceIv = crypto.getRandomValues(new Uint8Array(AES_GCM_IV_BYTES4));
|
|
3161
|
+
const outer = await crypto.subtle.encrypt({ name: "AES-GCM", iv: deviceIv }, deviceKey, inner);
|
|
3162
|
+
const acknowledged = options.backupAcknowledged ?? false;
|
|
3163
|
+
return {
|
|
3164
|
+
version: EMBEDDED_WALLET_VAULT_VERSION,
|
|
3165
|
+
kdf: EMBEDDED_WALLET_KDF_DEVICE_PIN,
|
|
3166
|
+
credentialId: "",
|
|
3167
|
+
publicAddress: options.publicAddress,
|
|
3168
|
+
iv: toBase64(iv),
|
|
3169
|
+
ciphertext: toBase64(new Uint8Array(outer)),
|
|
3170
|
+
kdfSalt: toBase64(salt),
|
|
3171
|
+
kdfIterations: iterations,
|
|
3172
|
+
prfSalt: "",
|
|
3173
|
+
backupAcknowledged: acknowledged,
|
|
3174
|
+
backupAcknowledgedAt: acknowledged ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
|
|
3175
|
+
createdAt: plaintext.createdAt,
|
|
3176
|
+
deviceKey,
|
|
3177
|
+
deviceIv: toBase64(deviceIv)
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
async function decryptDevicePinVault(vault, pin) {
|
|
3181
|
+
if (!vaultUsesPinUnlock(vault)) {
|
|
3182
|
+
throw new EmbeddedWalletVaultError("Vault does not use PIN unlock");
|
|
3183
|
+
}
|
|
3184
|
+
if (typeof pin !== "string" || !new RegExp(`^\\d{${QUICK_UNLOCK_PIN_LENGTH}}$`).test(pin)) {
|
|
3185
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
3186
|
+
"invalid_pin",
|
|
3187
|
+
`PIN must be exactly ${QUICK_UNLOCK_PIN_LENGTH} digits`
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
if (!vault.deviceKey || !vault.deviceIv) {
|
|
3191
|
+
throw new EmbeddedWalletVaultError(
|
|
3192
|
+
"This wallet's device key is missing; recover it with your 24 words"
|
|
3193
|
+
);
|
|
3194
|
+
}
|
|
3195
|
+
let inner;
|
|
3196
|
+
try {
|
|
3197
|
+
inner = await crypto.subtle.decrypt(
|
|
3198
|
+
{ name: "AES-GCM", iv: new Uint8Array(fromBase64(vault.deviceIv)) },
|
|
3199
|
+
vault.deviceKey,
|
|
3200
|
+
new Uint8Array(fromBase64(vault.ciphertext))
|
|
3201
|
+
);
|
|
3202
|
+
} catch (err) {
|
|
3203
|
+
throw new EmbeddedWalletVaultError(
|
|
3204
|
+
"This wallet's device layer is damaged; recover it with your 24 words",
|
|
3205
|
+
err
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
const pinKey = await derivePinKey(pin, fromBase64(vault.kdfSalt), vault.kdfIterations);
|
|
3209
|
+
let plaintext;
|
|
3210
|
+
try {
|
|
3211
|
+
plaintext = await crypto.subtle.decrypt(
|
|
3212
|
+
{ name: "AES-GCM", iv: new Uint8Array(fromBase64(vault.iv)) },
|
|
3213
|
+
pinKey,
|
|
3214
|
+
inner
|
|
3215
|
+
);
|
|
3216
|
+
} catch (err) {
|
|
3217
|
+
throw new EmbeddedWalletDecryptError(err);
|
|
3218
|
+
}
|
|
3219
|
+
return deserializePlaintext(new Uint8Array(plaintext));
|
|
3220
|
+
}
|
|
2780
3221
|
|
|
2781
3222
|
// src/embeddedWallet/wallet.ts
|
|
2782
3223
|
var activeSession = null;
|
|
2783
3224
|
var activeScopeId;
|
|
3225
|
+
var activePlaintext = null;
|
|
3226
|
+
var activeUnlockWasStrong = false;
|
|
2784
3227
|
function resolveScopeId(scopeId) {
|
|
2785
3228
|
const trimmed = scopeId?.trim();
|
|
2786
3229
|
return trimmed || void 0;
|
|
@@ -2797,6 +3240,12 @@ function vaultUsesPasskeyUnlock(vault) {
|
|
|
2797
3240
|
function vaultUsesPasscodeUnlock(vault) {
|
|
2798
3241
|
return vault.kdf === EMBEDDED_WALLET_KDF_PIN;
|
|
2799
3242
|
}
|
|
3243
|
+
function vaultHasPasskeyUnlock(vault) {
|
|
3244
|
+
return Boolean(vault.passkeyWrap) || vaultUsesPasskeyUnlock(vault);
|
|
3245
|
+
}
|
|
3246
|
+
function vaultPersistsSessionCache(vault) {
|
|
3247
|
+
return !(vaultUsesPinUnlock(vault) || Boolean(vault.passkeyWrap));
|
|
3248
|
+
}
|
|
2800
3249
|
function isPasscodeDecryptFailure(err) {
|
|
2801
3250
|
return err instanceof EmbeddedWalletDecryptError;
|
|
2802
3251
|
}
|
|
@@ -2819,6 +3268,28 @@ async function decryptVaultWithPasscodeProtected(vault, passcode) {
|
|
|
2819
3268
|
throw err;
|
|
2820
3269
|
}
|
|
2821
3270
|
}
|
|
3271
|
+
async function decryptVaultWithPinProtected(vault, pin) {
|
|
3272
|
+
if (!pin) {
|
|
3273
|
+
throw new EmbeddedWalletVaultError("PIN is required to unlock this wallet");
|
|
3274
|
+
}
|
|
3275
|
+
await assertPasscodeUnlockAllowed(vault.publicAddress);
|
|
3276
|
+
try {
|
|
3277
|
+
const plaintext = await decryptDevicePinVault(vault, pin);
|
|
3278
|
+
await recordPasscodeUnlockSuccess(vault.publicAddress);
|
|
3279
|
+
return plaintext;
|
|
3280
|
+
} catch (err) {
|
|
3281
|
+
if (isPasscodeDecryptFailure(err)) {
|
|
3282
|
+
await recordPasscodeUnlockFailure(vault.publicAddress);
|
|
3283
|
+
}
|
|
3284
|
+
throw err;
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
async function decryptVaultWithSecret(vault, secret) {
|
|
3288
|
+
if (vaultUsesPinUnlock(vault)) {
|
|
3289
|
+
return decryptVaultWithPinProtected(vault, secret.pin);
|
|
3290
|
+
}
|
|
3291
|
+
return decryptVaultWithPasscodeProtected(vault, secret.passcode);
|
|
3292
|
+
}
|
|
2822
3293
|
async function deriveKekFromPasskeyMaterial(vault, prfOutput, authenticatorData, clientDataJSON) {
|
|
2823
3294
|
const caps = detectWebAuthnPrfCapabilities();
|
|
2824
3295
|
const kdf = selectEmbeddedWalletKdf(caps, prfOutput);
|
|
@@ -2874,55 +3345,61 @@ async function encryptPlaintextWithPasscode(plaintext, passcode, backupAcknowled
|
|
|
2874
3345
|
});
|
|
2875
3346
|
return { address: account.address, vault };
|
|
2876
3347
|
}
|
|
2877
|
-
async function
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
kek = await deriveKekFromPrf(registration.prfOutput);
|
|
2887
|
-
prfSalt = registration.prfSalt;
|
|
2888
|
-
} else {
|
|
2889
|
-
kdfSalt = createPbkdf2Salt();
|
|
2890
|
-
const material = createPasskeyMaterialFromAssertion(
|
|
2891
|
-
registration.authenticatorData,
|
|
2892
|
-
registration.clientDataJSON
|
|
2893
|
-
);
|
|
2894
|
-
kek = await deriveKekFromPbkdf2(material, kdfSalt);
|
|
3348
|
+
async function encryptPlaintextWithSecret(plaintext, secret, backupAcknowledged = false) {
|
|
3349
|
+
if (secret.pin != null) {
|
|
3350
|
+
const signer = await embeddedPlaintextToSigner(plaintext);
|
|
3351
|
+
const [account] = await signer.getAccounts();
|
|
3352
|
+
const vault = await encryptPlaintextWithDevicePin(plaintext, secret.pin, {
|
|
3353
|
+
publicAddress: account.address,
|
|
3354
|
+
backupAcknowledged
|
|
3355
|
+
});
|
|
3356
|
+
return { address: account.address, vault };
|
|
2895
3357
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
kek,
|
|
2901
|
-
kdfSalt,
|
|
2902
|
-
prfSalt,
|
|
2903
|
-
backupAcknowledged
|
|
2904
|
-
});
|
|
2905
|
-
return { address: account.address, vault };
|
|
3358
|
+
if (secret.passcode == null) {
|
|
3359
|
+
throw new EmbeddedWalletVaultError("A PIN (or legacy passcode) is required");
|
|
3360
|
+
}
|
|
3361
|
+
return encryptPlaintextWithPasscode(plaintext, secret.passcode, backupAcknowledged);
|
|
2906
3362
|
}
|
|
2907
3363
|
async function unlockVaultToSession(vault, options = {}) {
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3364
|
+
const scopeId = scopeFromOptions(options);
|
|
3365
|
+
let plaintext;
|
|
3366
|
+
if (vaultUsesPinUnlock(vault)) {
|
|
3367
|
+
plaintext = await decryptVaultWithPinProtected(vault, options.pin);
|
|
3368
|
+
} else if (vaultUsesPasscodeUnlock(vault)) {
|
|
3369
|
+
plaintext = await decryptVaultWithPasscodeProtected(vault, options.passcode);
|
|
3370
|
+
} else {
|
|
3371
|
+
const credentialId = decodeCredentialId(vault.credentialId);
|
|
3372
|
+
const prfSalt = vault.prfSalt ? decodeCredentialId(vault.prfSalt) : void 0;
|
|
3373
|
+
const assertion = await assertPasskey({ credentialId, rpId: void 0, prfSalt });
|
|
3374
|
+
plaintext = await decryptVaultWithUnlockMaterial(vault, void 0, assertion);
|
|
3375
|
+
}
|
|
3376
|
+
return establishSession(vault, plaintext, scopeId, { strong: true });
|
|
3377
|
+
}
|
|
3378
|
+
async function establishSession(vault, plaintext, scopeId, options) {
|
|
2921
3379
|
const signer = await embeddedPlaintextToSigner(plaintext);
|
|
2922
3380
|
const [account] = await signer.getAccounts();
|
|
2923
3381
|
const session = { address: account.address, vault, signer };
|
|
2924
3382
|
activeSession = session;
|
|
2925
|
-
activeScopeId =
|
|
3383
|
+
activeScopeId = scopeId;
|
|
3384
|
+
activePlaintext = plaintext;
|
|
3385
|
+
activeUnlockWasStrong = options.strong;
|
|
3386
|
+
if (vaultPersistsSessionCache(vault)) {
|
|
3387
|
+
try {
|
|
3388
|
+
await cacheUnlockedSession({ scopeId, plaintext });
|
|
3389
|
+
} catch {
|
|
3390
|
+
}
|
|
3391
|
+
} else {
|
|
3392
|
+
try {
|
|
3393
|
+
await clearUnlockedSession({ scopeId });
|
|
3394
|
+
} catch {
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
if (options.strong) {
|
|
3398
|
+
try {
|
|
3399
|
+
await refreshQuickUnlockExpiry({ scopeId, publicAddress: account.address });
|
|
3400
|
+
} catch {
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
2926
3403
|
return session;
|
|
2927
3404
|
}
|
|
2928
3405
|
async function hasEmbeddedWallet(options = {}) {
|
|
@@ -2962,7 +3439,7 @@ async function createEmbeddedWallet(options) {
|
|
|
2962
3439
|
}
|
|
2963
3440
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2964
3441
|
const plaintext = await createHdDerivedPlaintext(createdAt);
|
|
2965
|
-
const { address, vault } = await
|
|
3442
|
+
const { address, vault } = await encryptPlaintextWithSecret(plaintext, options);
|
|
2966
3443
|
await saveVaultToIndexedDb(vault, { scopeId });
|
|
2967
3444
|
lockEmbeddedWallet();
|
|
2968
3445
|
activeScopeId = scopeId;
|
|
@@ -2975,7 +3452,11 @@ async function revealEmbeddedRecoveryPhrase(options = {}) {
|
|
|
2975
3452
|
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
2976
3453
|
}
|
|
2977
3454
|
let plaintext;
|
|
2978
|
-
if (vaultUsesPasscodeUnlock(vault)) {
|
|
3455
|
+
if ((vaultUsesPasscodeUnlock(vault) || vaultUsesPinUnlock(vault)) && !options.passcode && !options.pin && vault.passkeyWrap) {
|
|
3456
|
+
plaintext = await decryptPasskeyWrap(vault);
|
|
3457
|
+
} else if (vaultUsesPinUnlock(vault)) {
|
|
3458
|
+
plaintext = await decryptVaultWithPinProtected(vault, options.pin);
|
|
3459
|
+
} else if (vaultUsesPasscodeUnlock(vault)) {
|
|
2979
3460
|
plaintext = await decryptVaultWithPasscodeProtected(vault, options.passcode);
|
|
2980
3461
|
} else {
|
|
2981
3462
|
const credentialId = decodeCredentialId(vault.credentialId);
|
|
@@ -3031,7 +3512,7 @@ async function recoverEmbeddedWalletFromMnemonic(options) {
|
|
|
3031
3512
|
"Recovered wallet address does not match expectedAddress"
|
|
3032
3513
|
);
|
|
3033
3514
|
}
|
|
3034
|
-
const result = await
|
|
3515
|
+
const result = await encryptPlaintextWithSecret(plaintext, options);
|
|
3035
3516
|
if (result.address !== address) {
|
|
3036
3517
|
throw new EmbeddedWalletVaultError("Internal error: recovered address mismatch");
|
|
3037
3518
|
}
|
|
@@ -3040,41 +3521,220 @@ async function recoverEmbeddedWalletFromMnemonic(options) {
|
|
|
3040
3521
|
activeScopeId = scopeId;
|
|
3041
3522
|
return { address: result.address, vault: result.vault, keyType };
|
|
3042
3523
|
}
|
|
3524
|
+
function activePlaintextFor(scopeId, vault) {
|
|
3525
|
+
if (!activePlaintext || !activeSession) return null;
|
|
3526
|
+
if (activeScopeId !== scopeId) return null;
|
|
3527
|
+
if (activeSession.address !== vault.publicAddress) return null;
|
|
3528
|
+
return activePlaintext;
|
|
3529
|
+
}
|
|
3043
3530
|
async function enableEmbeddedPasskeyUnlock(options) {
|
|
3044
3531
|
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3045
3532
|
const vault = await loadVaultForScope(scopeId);
|
|
3046
3533
|
if (!vault) {
|
|
3047
3534
|
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
3048
3535
|
}
|
|
3049
|
-
if (
|
|
3536
|
+
if (vaultHasPasskeyUnlock(vault)) {
|
|
3050
3537
|
throw new EmbeddedWalletVaultError("Passkey unlock is already enabled for this wallet");
|
|
3051
3538
|
}
|
|
3052
|
-
if (!vaultUsesPasscodeUnlock(vault)) {
|
|
3539
|
+
if (!vaultUsesPasscodeUnlock(vault) && !vaultUsesPinUnlock(vault)) {
|
|
3053
3540
|
throw new EmbeddedWalletVaultError("Unsupported vault unlock method for passkey upgrade");
|
|
3054
3541
|
}
|
|
3055
|
-
const
|
|
3056
|
-
|
|
3057
|
-
|
|
3542
|
+
const plaintext = options.passcode != null || options.pin != null ? await decryptVaultWithSecret(vault, options) : activePlaintextFor(scopeId, vault);
|
|
3543
|
+
if (!plaintext) {
|
|
3544
|
+
throw new EmbeddedWalletVaultError("Unlock the wallet (or pass the passcode) before adding Face ID");
|
|
3545
|
+
}
|
|
3546
|
+
const prfSalt = crypto.getRandomValues(new Uint8Array(32));
|
|
3058
3547
|
const registration = await registerPasskey({
|
|
3059
3548
|
userId: options.userId,
|
|
3060
3549
|
userName: options.userName,
|
|
3061
3550
|
displayName: options.displayName,
|
|
3062
|
-
rpId: options.rpId
|
|
3551
|
+
rpId: options.rpId,
|
|
3552
|
+
prfSalt
|
|
3063
3553
|
});
|
|
3064
|
-
|
|
3554
|
+
let prfOutput = registration.prfOutput;
|
|
3555
|
+
if (!prfOutput) {
|
|
3556
|
+
try {
|
|
3557
|
+
const assertion = await assertPasskey({
|
|
3558
|
+
credentialId: registration.credentialId,
|
|
3559
|
+
rpId: options.rpId,
|
|
3560
|
+
prfSalt
|
|
3561
|
+
});
|
|
3562
|
+
prfOutput = assertion.prfOutput;
|
|
3563
|
+
} catch {
|
|
3564
|
+
prfOutput = null;
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
if (!prfOutput) {
|
|
3568
|
+
throw new EmbeddedWalletPasskeyUnsupportedError();
|
|
3569
|
+
}
|
|
3570
|
+
const previewSigner = await embeddedPlaintextToSigner(plaintext);
|
|
3571
|
+
const [account] = await previewSigner.getAccounts();
|
|
3572
|
+
if (account.address !== vault.publicAddress) {
|
|
3573
|
+
throw new EmbeddedWalletVaultError("Passkey upgrade must not change wallet address");
|
|
3574
|
+
}
|
|
3575
|
+
const kek = await deriveKekFromPrf(prfOutput);
|
|
3576
|
+
const wrapped = await encryptPlaintextWithKek(plaintext, kek);
|
|
3577
|
+
const updated = {
|
|
3578
|
+
...vault,
|
|
3579
|
+
passkeyWrap: {
|
|
3580
|
+
credentialId: encodeCredentialId(registration.credentialId),
|
|
3581
|
+
prfSalt: encodeCredentialId(prfSalt),
|
|
3582
|
+
rpId: options.rpId,
|
|
3583
|
+
iv: wrapped.iv,
|
|
3584
|
+
ciphertext: wrapped.ciphertext,
|
|
3585
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3586
|
+
}
|
|
3587
|
+
};
|
|
3588
|
+
await saveVaultToIndexedDb(updated, { scopeId });
|
|
3589
|
+
if (activeSession && activeSession.address === updated.publicAddress) {
|
|
3590
|
+
activeSession = { ...activeSession, vault: updated };
|
|
3591
|
+
}
|
|
3592
|
+
return { address: updated.publicAddress, vault: updated };
|
|
3593
|
+
}
|
|
3594
|
+
async function setEmbeddedWalletPin(options) {
|
|
3595
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3596
|
+
const vault = await loadVaultForScope(scopeId);
|
|
3597
|
+
if (!vault) {
|
|
3598
|
+
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
3599
|
+
}
|
|
3600
|
+
if (!vaultUsesPasscodeUnlock(vault) && !vaultUsesPinUnlock(vault)) {
|
|
3601
|
+
throw new EmbeddedWalletVaultError("This wallet type cannot switch to a PIN");
|
|
3602
|
+
}
|
|
3603
|
+
let plaintext = null;
|
|
3604
|
+
if (options.currentPin != null || options.passcode != null) {
|
|
3605
|
+
plaintext = await decryptVaultWithSecret(vault, {
|
|
3606
|
+
pin: options.currentPin,
|
|
3607
|
+
passcode: options.passcode
|
|
3608
|
+
});
|
|
3609
|
+
} else if (activeUnlockWasStrong) {
|
|
3610
|
+
plaintext = activePlaintextFor(scopeId, vault);
|
|
3611
|
+
}
|
|
3612
|
+
if (!plaintext) {
|
|
3613
|
+
throw new EmbeddedWalletVaultError("Unlock the wallet before changing the PIN");
|
|
3614
|
+
}
|
|
3615
|
+
const result = await encryptPlaintextWithSecret(
|
|
3065
3616
|
plaintext,
|
|
3066
|
-
|
|
3067
|
-
backupAcknowledged
|
|
3617
|
+
{ pin: options.pin },
|
|
3618
|
+
vault.backupAcknowledged
|
|
3068
3619
|
);
|
|
3069
|
-
if (result.address !==
|
|
3070
|
-
throw new EmbeddedWalletVaultError("
|
|
3620
|
+
if (result.address !== vault.publicAddress) {
|
|
3621
|
+
throw new EmbeddedWalletVaultError("Changing the PIN must not change the wallet address");
|
|
3622
|
+
}
|
|
3623
|
+
const updated = {
|
|
3624
|
+
...result.vault,
|
|
3625
|
+
backupAcknowledgedAt: vault.backupAcknowledgedAt,
|
|
3626
|
+
createdAt: vault.createdAt,
|
|
3627
|
+
passkeyWrap: vault.passkeyWrap
|
|
3628
|
+
};
|
|
3629
|
+
if (!updated.passkeyWrap) delete updated.passkeyWrap;
|
|
3630
|
+
await saveVaultToIndexedDb(updated, { scopeId });
|
|
3631
|
+
await clearQuickUnlock({ scopeId }).catch(() => void 0);
|
|
3632
|
+
if (activeSession && activeSession.address === updated.publicAddress) {
|
|
3633
|
+
activeSession = { ...activeSession, vault: updated };
|
|
3071
3634
|
}
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3635
|
+
return { address: updated.publicAddress, vault: updated };
|
|
3636
|
+
}
|
|
3637
|
+
async function disableEmbeddedPasskeyUnlock(options = {}) {
|
|
3638
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3639
|
+
const vault = await loadVaultForScope(scopeId);
|
|
3640
|
+
if (!vault) {
|
|
3641
|
+
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
3075
3642
|
}
|
|
3076
|
-
|
|
3077
|
-
|
|
3643
|
+
if (!vault.passkeyWrap) {
|
|
3644
|
+
return vault;
|
|
3645
|
+
}
|
|
3646
|
+
const updated = { ...vault };
|
|
3647
|
+
delete updated.passkeyWrap;
|
|
3648
|
+
await saveVaultToIndexedDb(updated, { scopeId });
|
|
3649
|
+
if (activeSession && activeSession.address === updated.publicAddress) {
|
|
3650
|
+
activeSession = { ...activeSession, vault: updated };
|
|
3651
|
+
}
|
|
3652
|
+
return updated;
|
|
3653
|
+
}
|
|
3654
|
+
async function decryptPasskeyWrap(vault) {
|
|
3655
|
+
const wrap = vault.passkeyWrap;
|
|
3656
|
+
if (!wrap) {
|
|
3657
|
+
throw new EmbeddedWalletVaultError("Face ID is not set up for this wallet");
|
|
3658
|
+
}
|
|
3659
|
+
const prfSalt = decodeCredentialId(wrap.prfSalt);
|
|
3660
|
+
const assertion = await assertPasskey({
|
|
3661
|
+
credentialId: decodeCredentialId(wrap.credentialId),
|
|
3662
|
+
rpId: wrap.rpId,
|
|
3663
|
+
prfSalt
|
|
3664
|
+
});
|
|
3665
|
+
if (!assertion.prfOutput) {
|
|
3666
|
+
throw new EmbeddedWalletPasskeyUnsupportedError(
|
|
3667
|
+
"This browser did not return the passkey secret; unlock with your passcode"
|
|
3668
|
+
);
|
|
3669
|
+
}
|
|
3670
|
+
const kek = await deriveKekFromPrf(assertion.prfOutput);
|
|
3671
|
+
const plaintext = await decryptPlaintextWithKek(wrap.iv, wrap.ciphertext, kek);
|
|
3672
|
+
const signer = await embeddedPlaintextToSigner(plaintext);
|
|
3673
|
+
const [account] = await signer.getAccounts();
|
|
3674
|
+
if (account.address !== vault.publicAddress) {
|
|
3675
|
+
throw new EmbeddedWalletVaultError("Passkey opened a key for a different address");
|
|
3676
|
+
}
|
|
3677
|
+
return plaintext;
|
|
3678
|
+
}
|
|
3679
|
+
async function unlockEmbeddedWalletWithPasskey(options = {}) {
|
|
3680
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3681
|
+
const vault = await loadVaultForScope(scopeId);
|
|
3682
|
+
if (!vault) {
|
|
3683
|
+
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
3684
|
+
}
|
|
3685
|
+
if (vaultUsesPasskeyUnlock(vault)) {
|
|
3686
|
+
return unlockVaultToSession(vault, { scopeId });
|
|
3687
|
+
}
|
|
3688
|
+
const plaintext = await decryptPasskeyWrap(vault);
|
|
3689
|
+
return establishSession(vault, plaintext, scopeId, { strong: true });
|
|
3690
|
+
}
|
|
3691
|
+
async function setEmbeddedQuickUnlockPin(options) {
|
|
3692
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3693
|
+
const vault = await loadVaultForScope(scopeId);
|
|
3694
|
+
if (!vault) {
|
|
3695
|
+
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
3696
|
+
}
|
|
3697
|
+
const plaintext = activePlaintextFor(scopeId, vault);
|
|
3698
|
+
if (!plaintext || !activeUnlockWasStrong) {
|
|
3699
|
+
throw new EmbeddedWalletQuickUnlockError(
|
|
3700
|
+
"no_session",
|
|
3701
|
+
"Unlock with Face ID or your passcode before choosing a PIN"
|
|
3702
|
+
);
|
|
3703
|
+
}
|
|
3704
|
+
await createQuickUnlockRecord({
|
|
3705
|
+
scopeId,
|
|
3706
|
+
plaintext,
|
|
3707
|
+
publicAddress: vault.publicAddress,
|
|
3708
|
+
pin: options.pin
|
|
3709
|
+
});
|
|
3710
|
+
return getQuickUnlockStatus({ scopeId });
|
|
3711
|
+
}
|
|
3712
|
+
async function unlockEmbeddedWalletWithPin(options) {
|
|
3713
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3714
|
+
const vault = await loadVaultForScope(scopeId);
|
|
3715
|
+
if (!vault) {
|
|
3716
|
+
throw new EmbeddedWalletVaultError("No embedded wallet found on this device");
|
|
3717
|
+
}
|
|
3718
|
+
const opened = await openQuickUnlockRecord({ scopeId, pin: options.pin });
|
|
3719
|
+
if (opened.publicAddress !== vault.publicAddress) {
|
|
3720
|
+
await clearQuickUnlock({ scopeId }).catch(() => void 0);
|
|
3721
|
+
throw new EmbeddedWalletQuickUnlockError("not_set", "PIN belongs to a different wallet");
|
|
3722
|
+
}
|
|
3723
|
+
const signer = await embeddedPlaintextToSigner(opened.plaintext);
|
|
3724
|
+
const [account] = await signer.getAccounts();
|
|
3725
|
+
if (account.address !== vault.publicAddress) {
|
|
3726
|
+
await clearQuickUnlock({ scopeId }).catch(() => void 0);
|
|
3727
|
+
throw new EmbeddedWalletQuickUnlockError("not_set", "PIN record does not match this wallet");
|
|
3728
|
+
}
|
|
3729
|
+
return establishSession(vault, opened.plaintext, scopeId, { strong: false });
|
|
3730
|
+
}
|
|
3731
|
+
async function getEmbeddedQuickUnlockStatus(options = {}) {
|
|
3732
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3733
|
+
return getQuickUnlockStatus({ scopeId });
|
|
3734
|
+
}
|
|
3735
|
+
async function clearEmbeddedQuickUnlockPin(options = {}) {
|
|
3736
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3737
|
+
await clearQuickUnlock({ scopeId });
|
|
3078
3738
|
}
|
|
3079
3739
|
async function unlockEmbeddedWallet(options = {}) {
|
|
3080
3740
|
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
@@ -3084,9 +3744,37 @@ async function unlockEmbeddedWallet(options = {}) {
|
|
|
3084
3744
|
}
|
|
3085
3745
|
return unlockVaultToSession(vault, options);
|
|
3086
3746
|
}
|
|
3747
|
+
async function tryRestoreUnlockedSession(options = {}) {
|
|
3748
|
+
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3749
|
+
const plaintext = await restoreCachedSessionPlaintext({ scopeId });
|
|
3750
|
+
if (!plaintext) {
|
|
3751
|
+
return null;
|
|
3752
|
+
}
|
|
3753
|
+
const vault = await loadVaultForScope(scopeId);
|
|
3754
|
+
if (!vault) {
|
|
3755
|
+
await clearUnlockedSession({ scopeId }).catch(() => void 0);
|
|
3756
|
+
return null;
|
|
3757
|
+
}
|
|
3758
|
+
const signer = await embeddedPlaintextToSigner(plaintext);
|
|
3759
|
+
const [account] = await signer.getAccounts();
|
|
3760
|
+
if (account.address !== vault.publicAddress) {
|
|
3761
|
+
await clearUnlockedSession({ scopeId }).catch(() => void 0);
|
|
3762
|
+
return null;
|
|
3763
|
+
}
|
|
3764
|
+
const session = { address: account.address, vault, signer };
|
|
3765
|
+
activeSession = session;
|
|
3766
|
+
activeScopeId = scopeId;
|
|
3767
|
+
activePlaintext = plaintext;
|
|
3768
|
+
activeUnlockWasStrong = false;
|
|
3769
|
+
return session;
|
|
3770
|
+
}
|
|
3087
3771
|
function lockEmbeddedWallet() {
|
|
3772
|
+
const scopeId = activeScopeId;
|
|
3088
3773
|
activeSession = null;
|
|
3089
3774
|
activeScopeId = void 0;
|
|
3775
|
+
activePlaintext = null;
|
|
3776
|
+
activeUnlockWasStrong = false;
|
|
3777
|
+
void clearUnlockedSession({ scopeId }).catch(() => void 0);
|
|
3090
3778
|
}
|
|
3091
3779
|
function getEmbeddedWalletSession() {
|
|
3092
3780
|
return activeSession;
|
|
@@ -3113,7 +3801,7 @@ async function acknowledgeEmbeddedBackup(options = {}) {
|
|
|
3113
3801
|
if (activeSession) {
|
|
3114
3802
|
activeSession = { ...activeSession, vault: updated };
|
|
3115
3803
|
}
|
|
3116
|
-
return exportEncryptedBackup(updated);
|
|
3804
|
+
return vaultUsesPinUnlock(updated) ? null : exportEncryptedBackup(updated);
|
|
3117
3805
|
}
|
|
3118
3806
|
function downloadEmbeddedBackupFile(backup, filename) {
|
|
3119
3807
|
const blob = new Blob([serializeBackupFile(backup)], { type: "application/json" });
|
|
@@ -3141,6 +3829,8 @@ async function importEmbeddedBackup(json, options = {}) {
|
|
|
3141
3829
|
async function clearEmbeddedWallet(options = {}) {
|
|
3142
3830
|
const scopeId = scopeFromOptions(options) ?? activeScopeId;
|
|
3143
3831
|
lockEmbeddedWallet();
|
|
3832
|
+
await clearUnlockedSession({ scopeId });
|
|
3833
|
+
await clearQuickUnlock({ scopeId }).catch(() => void 0);
|
|
3144
3834
|
await clearVaultFromIndexedDb({ scopeId });
|
|
3145
3835
|
}
|
|
3146
3836
|
|
|
@@ -3423,6 +4113,6 @@ function sleep(ms) {
|
|
|
3423
4113
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3424
4114
|
}
|
|
3425
4115
|
|
|
3426
|
-
export { ATTESTATION_TYPE_CITIZENSHIP_APPLICATION, ATTESTATION_TYPE_RECOVERY, ApplicationStatus, BECH32_PREFIX, CHAIN_COIN_TYPE, CitizenTxError, DEFAULT_CHAIN_ID, DEFAULT_REST_URL, DEFAULT_RPC_URL, EMBEDDED_WALLET_KDF_PBKDF2, EMBEDDED_WALLET_KDF_PIN, EMBEDDED_WALLET_KDF_PRF, EMBEDDED_WALLET_KEY_DIRECT_ENTROPY, EMBEDDED_WALLET_KEY_HD_DERIVED, EMBEDDED_WALLET_PASSCODE_MAX_LENGTH, EMBEDDED_WALLET_PASSCODE_MIN_LENGTH, EMBEDDED_WALLET_PASSKEY_PBKDF2_ITERATIONS, EMBEDDED_WALLET_PIN_PBKDF2_ITERATIONS, EMBEDDED_WALLET_RECOVERY_PHRASE_DIRECT_ENTROPY_WARNING, EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING, EMBEDDED_WALLET_VAULT_VERSION, EmbeddedWalletBackupRequiredError, EmbeddedWalletDecryptError, EmbeddedWalletLockoutError, EmbeddedWalletVaultError, EmbeddedWalletWeakPasscodeError, InvalidChainConfigError, MsgClaimReward, MsgSubmitApplication, MsgSubmitRecoveryAttestation, PASSCODE_LOCKOUT_FAILURE_THRESHOLD, PASSCODE_LOCKOUT_INITIAL_MS, PASSCODE_LOCKOUT_MAX_MS, RecoveryAttestationStatus, RestQueryError, RpcConnectionError, UKDR_DECIMALS, UKDR_DISPLAY_DENOM, UKDR_MINIMAL_DENOM, VAULT_LEGACY_KEY, WALLETCONNECT_COSMOS_METHODS, WALLETCONNECT_COSMOS_NAMESPACE, WEBAUTHN_RP_NAME, WalletAccountError, WalletConnectNotInitializedError, WalletConnectSessionError, WalletConnectSigningError, WalletConnectionError, WalletConnectionRejectedError, WalletNotInstalledError, WebAuthnUnavailableError, WebAuthnUserCancelledError, acknowledgeEmbeddedBackup, addressFromHdMnemonic, addressFromPrivateKey, assertPasscodeAllowedForCreation, assertPasscodeUnlockAllowed, assertPasskey, assertValidPasscode, assertValidRecoveryMnemonic, assertVaultPermitsTransaction, attestationSignPayload, broadcastAndConfirmTx, buildClaimRewardMsg, buildKingdomChainInfo, buildRequiredCosmosNamespaces, buildSendUkdrMsg, buildSubmitApplicationMsg, citizenTxErrorMessage, clearEmbeddedWallet, clearPasscodeLockoutRecord, clearVaultFromIndexedDb, computePasscodeLockoutDurationMs, connectCosmostation, connectKeplr, connectLeap, connectWalletConnect, createBrowserSigningClient, createEmbeddedWallet, createHdDerivedPlaintext, createKingdomChainRegistry, createPasskeyMaterialFromAssertion, createPbkdf2Salt, createWalletConnectOfflineSigner, decodeCredentialId, decodeDirectEntropyMnemonic, decryptWalletPayload, defaultEmbeddedHdPath, defaultUkdrTxFee, deriveKekFromPasscode, deriveKekFromPbkdf2, deriveKekFromPrf, derivePrivateKeyFromHdMnemonic, detectRecoveryMnemonicKeyType, detectWebAuthnPrfCapabilities, disconnectWalletConnect, downloadEmbeddedBackupFile, embeddedPlaintextToSigner, enableEmbeddedPasskeyUnlock, encodeCredentialId, encodeDirectEntropyMnemonic, encryptWalletPayload, evaluatePasscodeStrength, exportEncryptedBackup, fetchFeeGrantGranter, fetchUkdrBalance, fetchUnclaimedFinalizedRewards, formatUkdrAsKdr, getCosmostationKeplrApi, getEmbeddedOfflineSigner, getEmbeddedWalletSession, getPasscodeLockoutState, hasEmbeddedWallet, hasLegacyEmbeddedWallet, hasMixedCharacterClasses, importEmbeddedBackup, initWalletConnectClient, isCosmostationAvailable, isKeplrAvailable, isLeapAvailable, loadPasscodeLockoutRecord, loadVaultFromIndexedDb, lockEmbeddedWallet, markVaultBackupAcknowledged, migrateLegacyEmbeddedWallet, normalizePasscode, normalizeRecoveryMnemonic, parseEncryptedBackup, parseKdrToUkdr, parseWalletConnectAccount, plaintextFromRecoveryMnemonic, providerSeedFromHex, publicKeyFromPrivateSeed, readPrfOutput, recordPasscodeUnlockFailure, recordPasscodeUnlockSuccess, recoverEmbeddedWalletFromMnemonic, recoveryMnemonicAddress, recoveryMnemonicFromPlaintext, registerPasskey, resolvePlaintextKeyType, resolveVaultStorageKey, restoreWalletConnectSession, revealEmbeddedRecoveryPhrase, savePasscodeLockoutRecord, saveVaultToIndexedDb, selectEmbeddedWalletKdf, serializeBackupFile, signAttestationPayload, signCitizenshipAttestation, suggestAndConnect, toWalletConnectCosmosChainId, ukdrCoin, unlockEmbeddedWallet, validateChainConfig, vaultUsesPasscodeUnlock, vaultUsesPasskeyUnlock };
|
|
4116
|
+
export { ATTESTATION_TYPE_CITIZENSHIP_APPLICATION, ATTESTATION_TYPE_RECOVERY, ApplicationStatus, BECH32_PREFIX, CHAIN_COIN_TYPE, CitizenTxError, DEFAULT_CHAIN_ID, DEFAULT_REST_URL, DEFAULT_RPC_URL, DEVICE_PIN_PBKDF2_ITERATIONS, EMBEDDED_WALLET_KDF_DEVICE_PIN, EMBEDDED_WALLET_KDF_PBKDF2, EMBEDDED_WALLET_KDF_PIN, EMBEDDED_WALLET_KDF_PRF, EMBEDDED_WALLET_KEY_DIRECT_ENTROPY, EMBEDDED_WALLET_KEY_HD_DERIVED, EMBEDDED_WALLET_PASSCODE_MAX_LENGTH, EMBEDDED_WALLET_PASSCODE_MIN_LENGTH, EMBEDDED_WALLET_PASSKEY_PBKDF2_ITERATIONS, EMBEDDED_WALLET_PIN_PBKDF2_ITERATIONS, EMBEDDED_WALLET_RECOVERY_PHRASE_DIRECT_ENTROPY_WARNING, EMBEDDED_WALLET_RECOVERY_PHRASE_GENERAL_WARNING, EMBEDDED_WALLET_SESSION_TTL_MS, EMBEDDED_WALLET_VAULT_VERSION, EmbeddedWalletBackupRequiredError, EmbeddedWalletDecryptError, EmbeddedWalletLockoutError, EmbeddedWalletPasskeyUnsupportedError, EmbeddedWalletQuickUnlockError, EmbeddedWalletVaultError, EmbeddedWalletWeakPasscodeError, InvalidChainConfigError, MsgClaimReward, MsgSubmitApplication, MsgSubmitRecoveryAttestation, PASSCODE_LOCKOUT_FAILURE_THRESHOLD, PASSCODE_LOCKOUT_INITIAL_MS, PASSCODE_LOCKOUT_MAX_MS, QUICK_UNLOCK_MAX_ATTEMPTS, QUICK_UNLOCK_PBKDF2_ITERATIONS, QUICK_UNLOCK_PIN_LENGTH, QUICK_UNLOCK_TTL_MS, RecoveryAttestationStatus, RestQueryError, RpcConnectionError, UKDR_DECIMALS, UKDR_DISPLAY_DENOM, UKDR_MINIMAL_DENOM, VAULT_LEGACY_KEY, WALLETCONNECT_COSMOS_METHODS, WALLETCONNECT_COSMOS_NAMESPACE, WEBAUTHN_RP_NAME, WalletAccountError, WalletConnectNotInitializedError, WalletConnectSessionError, WalletConnectSigningError, WalletConnectionError, WalletConnectionRejectedError, WalletNotInstalledError, WebAuthnUnavailableError, WebAuthnUserCancelledError, acknowledgeEmbeddedBackup, addressFromHdMnemonic, addressFromPrivateKey, assertPasscodeAllowedForCreation, assertPasscodeUnlockAllowed, assertPasskey, assertQuickUnlockPinAllowed, assertValidPasscode, assertValidRecoveryMnemonic, assertVaultPermitsTransaction, attestationSignPayload, broadcastAndConfirmTx, buildClaimRewardMsg, buildKingdomChainInfo, buildRequiredCosmosNamespaces, buildSendUkdrMsg, buildSubmitApplicationMsg, cacheUnlockedSession, citizenTxErrorMessage, clearEmbeddedQuickUnlockPin, clearEmbeddedWallet, clearPasscodeLockoutRecord, clearQuickUnlock, clearQuickUnlockRecord, clearSessionCacheRecord, clearUnlockedSession, clearVaultFromIndexedDb, computePasscodeLockoutDurationMs, connectCosmostation, connectKeplr, connectLeap, connectWalletConnect, createBrowserSigningClient, createEmbeddedWallet, createHdDerivedPlaintext, createKingdomChainRegistry, createPasskeyMaterialFromAssertion, createPbkdf2Salt, createQuickUnlockRecord, createWalletConnectOfflineSigner, decodeCredentialId, decodeDirectEntropyMnemonic, decryptDevicePinVault, decryptPlaintextWithKek, decryptWalletPayload, defaultEmbeddedHdPath, defaultUkdrTxFee, deriveKekFromPasscode, deriveKekFromPbkdf2, deriveKekFromPrf, derivePinKey, derivePrivateKeyFromHdMnemonic, deserializePlaintext, detectRecoveryMnemonicKeyType, detectWebAuthnPrfCapabilities, disableEmbeddedPasskeyUnlock, disconnectWalletConnect, downloadEmbeddedBackupFile, embeddedPlaintextToSigner, enableEmbeddedPasskeyUnlock, encodeCredentialId, encodeDirectEntropyMnemonic, encryptPlaintextWithDevicePin, encryptPlaintextWithKek, encryptWalletPayload, evaluatePasscodeStrength, exportEncryptedBackup, fetchFeeGrantGranter, fetchUkdrBalance, fetchUnclaimedFinalizedRewards, formatUkdrAsKdr, getCosmostationKeplrApi, getEmbeddedOfflineSigner, getEmbeddedQuickUnlockStatus, getEmbeddedWalletSession, getPasscodeLockoutState, getQuickUnlockStatus, hasEmbeddedWallet, hasLegacyEmbeddedWallet, hasMixedCharacterClasses, importEmbeddedBackup, initWalletConnectClient, isCosmostationAvailable, isKeplrAvailable, isLeapAvailable, isTrivialPin, loadPasscodeLockoutRecord, loadQuickUnlockRecord, loadSessionCacheRecord, loadVaultFromIndexedDb, lockEmbeddedWallet, markVaultBackupAcknowledged, migrateLegacyEmbeddedWallet, normalizePasscode, normalizeRecoveryMnemonic, openQuickUnlockRecord, parseEncryptedBackup, parseKdrToUkdr, parseWalletConnectAccount, plaintextFromRecoveryMnemonic, providerSeedFromHex, publicKeyFromPrivateSeed, readPrfOutput, recordPasscodeUnlockFailure, recordPasscodeUnlockSuccess, recoverEmbeddedWalletFromMnemonic, recoveryMnemonicAddress, recoveryMnemonicFromPlaintext, refreshQuickUnlockExpiry, registerPasskey, resolvePlaintextKeyType, resolveVaultStorageKey, restoreCachedSessionPlaintext, restoreWalletConnectSession, revealEmbeddedRecoveryPhrase, savePasscodeLockoutRecord, saveQuickUnlockRecord, saveSessionCacheRecord, saveVaultToIndexedDb, selectEmbeddedWalletKdf, serializeBackupFile, serializePlaintext, setEmbeddedQuickUnlockPin, setEmbeddedWalletPin, signAttestationPayload, signCitizenshipAttestation, suggestAndConnect, toWalletConnectCosmosChainId, tryRestoreUnlockedSession, ukdrCoin, unlockEmbeddedWallet, unlockEmbeddedWalletWithPasskey, unlockEmbeddedWalletWithPin, validateChainConfig, vaultHasPasskeyUnlock, vaultUsesPasscodeUnlock, vaultUsesPasskeyUnlock, vaultUsesPinUnlock };
|
|
3427
4117
|
//# sourceMappingURL=index.js.map
|
|
3428
4118
|
//# sourceMappingURL=index.js.map
|