@anywalletkit/extension-kit 0.3.2 → 0.3.5
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/dist/index.cjs +33 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -3
- package/dist/index.d.ts +14 -3
- package/dist/index.js +33 -10
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.d.cts
CHANGED
|
@@ -81,6 +81,8 @@ interface VaultEnvelopeV1 {
|
|
|
81
81
|
dek: SealedBox;
|
|
82
82
|
/** One box per key source (keyring source id), sealed under the DEK. AAD `forgewallet-vault/secret/<id>`. */
|
|
83
83
|
secrets: Record<string, SealedBox>;
|
|
84
|
+
/** A fixed value sealed under the DEK (AAD `forgewallet-vault/dek-check`): proves a key is this vault's while it holds no secret. Absent in envelopes written before it existed. */
|
|
85
|
+
dekCheck?: SealedBox;
|
|
84
86
|
createdAt: number;
|
|
85
87
|
}
|
|
86
88
|
interface VaultPasskey {
|
|
@@ -106,6 +108,8 @@ interface VaultEnvelopeV2 {
|
|
|
106
108
|
};
|
|
107
109
|
dek: SealedBox;
|
|
108
110
|
secrets: Record<string, SealedBox>;
|
|
111
|
+
/** See {@link VaultEnvelopeV1.dekCheck}. */
|
|
112
|
+
dekCheck?: SealedBox;
|
|
109
113
|
passkeys: VaultPasskey[];
|
|
110
114
|
createdAt: number;
|
|
111
115
|
/** When a v1 envelope was re-sealed as v2. */
|
|
@@ -237,9 +241,12 @@ declare class Vault {
|
|
|
237
241
|
private _random;
|
|
238
242
|
/** A fresh envelope (v2 by default, v1 with `kdf: 'pbkdf2'`) wrapping `dek` under `password`. */
|
|
239
243
|
private _sealEnvelope;
|
|
244
|
+
/** The DEK check box: {@link DEK_CHECK} sealed under `dek`. */
|
|
245
|
+
private _sealDekCheck;
|
|
240
246
|
/**
|
|
241
|
-
* The caller's DEK must open this envelope's
|
|
242
|
-
*
|
|
247
|
+
* The caller's DEK must open this envelope's DEK check (or, in an envelope written before it, its
|
|
248
|
+
* first secret) — it is the key the password protects, not any 32 bytes. An old empty envelope has
|
|
249
|
+
* nothing to prove it with, so it is refused rather than trusted.
|
|
243
250
|
*/
|
|
244
251
|
private _requireDek;
|
|
245
252
|
private _derivePbkdf2;
|
|
@@ -1306,12 +1313,16 @@ interface SwapRecord {
|
|
|
1306
1313
|
checks: number;
|
|
1307
1314
|
}
|
|
1308
1315
|
declare const ACTIVE_STATUSES: ReadonlySet<SwapRecordStatus>;
|
|
1309
|
-
/**
|
|
1316
|
+
/**
|
|
1317
|
+
* `awk1.<provider>.<intentId>[.<sourceTxHash>[.<claimTxHash>[.ok]]]` — the 4th segment is on the id `claimSwap2`
|
|
1318
|
+
* returns, and `.ok` after it when the destination account saw the claim succeed (`destTxConfirmed`).
|
|
1319
|
+
*/
|
|
1310
1320
|
declare function parseSwapId(id: string): {
|
|
1311
1321
|
provider: string;
|
|
1312
1322
|
intentId: string;
|
|
1313
1323
|
sourceTxHash?: string;
|
|
1314
1324
|
destTxHash?: string;
|
|
1325
|
+
destTxConfirmed?: true;
|
|
1315
1326
|
} | null;
|
|
1316
1327
|
declare class SwapsStore {
|
|
1317
1328
|
private readonly storage;
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,8 @@ interface VaultEnvelopeV1 {
|
|
|
81
81
|
dek: SealedBox;
|
|
82
82
|
/** One box per key source (keyring source id), sealed under the DEK. AAD `forgewallet-vault/secret/<id>`. */
|
|
83
83
|
secrets: Record<string, SealedBox>;
|
|
84
|
+
/** A fixed value sealed under the DEK (AAD `forgewallet-vault/dek-check`): proves a key is this vault's while it holds no secret. Absent in envelopes written before it existed. */
|
|
85
|
+
dekCheck?: SealedBox;
|
|
84
86
|
createdAt: number;
|
|
85
87
|
}
|
|
86
88
|
interface VaultPasskey {
|
|
@@ -106,6 +108,8 @@ interface VaultEnvelopeV2 {
|
|
|
106
108
|
};
|
|
107
109
|
dek: SealedBox;
|
|
108
110
|
secrets: Record<string, SealedBox>;
|
|
111
|
+
/** See {@link VaultEnvelopeV1.dekCheck}. */
|
|
112
|
+
dekCheck?: SealedBox;
|
|
109
113
|
passkeys: VaultPasskey[];
|
|
110
114
|
createdAt: number;
|
|
111
115
|
/** When a v1 envelope was re-sealed as v2. */
|
|
@@ -237,9 +241,12 @@ declare class Vault {
|
|
|
237
241
|
private _random;
|
|
238
242
|
/** A fresh envelope (v2 by default, v1 with `kdf: 'pbkdf2'`) wrapping `dek` under `password`. */
|
|
239
243
|
private _sealEnvelope;
|
|
244
|
+
/** The DEK check box: {@link DEK_CHECK} sealed under `dek`. */
|
|
245
|
+
private _sealDekCheck;
|
|
240
246
|
/**
|
|
241
|
-
* The caller's DEK must open this envelope's
|
|
242
|
-
*
|
|
247
|
+
* The caller's DEK must open this envelope's DEK check (or, in an envelope written before it, its
|
|
248
|
+
* first secret) — it is the key the password protects, not any 32 bytes. An old empty envelope has
|
|
249
|
+
* nothing to prove it with, so it is refused rather than trusted.
|
|
243
250
|
*/
|
|
244
251
|
private _requireDek;
|
|
245
252
|
private _derivePbkdf2;
|
|
@@ -1306,12 +1313,16 @@ interface SwapRecord {
|
|
|
1306
1313
|
checks: number;
|
|
1307
1314
|
}
|
|
1308
1315
|
declare const ACTIVE_STATUSES: ReadonlySet<SwapRecordStatus>;
|
|
1309
|
-
/**
|
|
1316
|
+
/**
|
|
1317
|
+
* `awk1.<provider>.<intentId>[.<sourceTxHash>[.<claimTxHash>[.ok]]]` — the 4th segment is on the id `claimSwap2`
|
|
1318
|
+
* returns, and `.ok` after it when the destination account saw the claim succeed (`destTxConfirmed`).
|
|
1319
|
+
*/
|
|
1310
1320
|
declare function parseSwapId(id: string): {
|
|
1311
1321
|
provider: string;
|
|
1312
1322
|
intentId: string;
|
|
1313
1323
|
sourceTxHash?: string;
|
|
1314
1324
|
destTxHash?: string;
|
|
1325
|
+
destTxConfirmed?: true;
|
|
1315
1326
|
} | null;
|
|
1316
1327
|
declare class SwapsStore {
|
|
1317
1328
|
private readonly storage;
|
package/dist/index.js
CHANGED
|
@@ -72,6 +72,8 @@ var aadDekV1 = (kdf) => `forgewallet-vault/1/dek/${kdf.name}/${kdf.iterations}/$
|
|
|
72
72
|
var aadDekV2 = (kdf) => `forgewallet-vault/2/dek/scrypt/${kdf.N}/${kdf.r}/${kdf.p}/${kdf.salt}`;
|
|
73
73
|
var aadPasskey = (id) => `forgewallet-vault/2/passkey/${id}`;
|
|
74
74
|
var aadSecret = (id) => `forgewallet-vault/secret/${id}`;
|
|
75
|
+
var AAD_DEK_CHECK = "forgewallet-vault/dek-check";
|
|
76
|
+
var DEK_CHECK = "forgewallet-vault/dek-check/1";
|
|
75
77
|
var HKDF_INFO = "forgewallet-vault/2/passkey";
|
|
76
78
|
var Vault = class {
|
|
77
79
|
_storage;
|
|
@@ -285,7 +287,7 @@ var Vault = class {
|
|
|
285
287
|
const key = await this._importDek(dek);
|
|
286
288
|
return this._exclusive(async () => {
|
|
287
289
|
const envelope = await this._load();
|
|
288
|
-
if (Object.keys(envelope.secrets).length > 0) await this._requireDek(dek, envelope);
|
|
290
|
+
if (envelope.dekCheck !== void 0 || Object.keys(envelope.secrets).length > 0) await this._requireDek(dek, envelope);
|
|
289
291
|
if (!(sourceId in envelope.secrets) && Object.keys(envelope.secrets).length >= VAULT_LIMITS.secrets) {
|
|
290
292
|
throw new VaultError(`A vault holds at most ${VAULT_LIMITS.secrets} secrets.`, "invalid-input");
|
|
291
293
|
}
|
|
@@ -338,19 +340,31 @@ var Vault = class {
|
|
|
338
340
|
if (this._kdf === "pbkdf2") {
|
|
339
341
|
const kdf2 = { name: "PBKDF2-SHA256", iterations: this._iterations, salt: toBase64(salt) };
|
|
340
342
|
const kek2 = await this._derivePbkdf2(password, salt, this._iterations);
|
|
341
|
-
return { v: 1, kdf: kdf2, dek: await this._seal(kek2, dek, aadDekV1(kdf2)), secrets, createdAt };
|
|
343
|
+
return { v: 1, kdf: kdf2, dek: await this._seal(kek2, dek, aadDekV1(kdf2)), secrets, dekCheck: await this._sealDekCheck(dek), createdAt };
|
|
342
344
|
}
|
|
343
345
|
const kdf = { name: "scrypt", ...this._scrypt, salt: toBase64(salt) };
|
|
344
346
|
const kek = await this._deriveScrypt(password, salt, kdf);
|
|
345
|
-
return { v: 2, kdf, dek: await this._seal(kek, dek, aadDekV2(kdf)), secrets, passkeys, createdAt };
|
|
347
|
+
return { v: 2, kdf, dek: await this._seal(kek, dek, aadDekV2(kdf)), secrets, dekCheck: await this._sealDekCheck(dek), passkeys, createdAt };
|
|
348
|
+
}
|
|
349
|
+
/** The DEK check box: {@link DEK_CHECK} sealed under `dek`. */
|
|
350
|
+
async _sealDekCheck(dek) {
|
|
351
|
+
return this._seal(await this._importDek(dek), new TextEncoder().encode(DEK_CHECK), AAD_DEK_CHECK);
|
|
346
352
|
}
|
|
347
353
|
/**
|
|
348
|
-
* The caller's DEK must open this envelope's
|
|
349
|
-
*
|
|
354
|
+
* The caller's DEK must open this envelope's DEK check (or, in an envelope written before it, its
|
|
355
|
+
* first secret) — it is the key the password protects, not any 32 bytes. An old empty envelope has
|
|
356
|
+
* nothing to prove it with, so it is refused rather than trusted.
|
|
350
357
|
*/
|
|
351
358
|
async _requireDek(dek, envelope) {
|
|
352
359
|
const ids = Object.keys(envelope.secrets);
|
|
353
360
|
const key = await this._importDek(dek);
|
|
361
|
+
if (envelope.dekCheck !== void 0) {
|
|
362
|
+
const check = await this._open(key, envelope.dekCheck, AAD_DEK_CHECK, "locked");
|
|
363
|
+
const ok = new TextDecoder().decode(check) === DEK_CHECK;
|
|
364
|
+
zeroize(check);
|
|
365
|
+
if (!ok) throw new VaultError("The vault key does not open this entry; lock and unlock again.", "locked");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
354
368
|
if (ids.length === 0) throw new VaultError("Add a secret to the vault before a passkey: an empty vault cannot prove the key is its own.", "invalid-input");
|
|
355
369
|
const probe = await this._open(key, envelope.secrets[ids[0]], aadSecret(ids[0]), "locked");
|
|
356
370
|
zeroize(probe);
|
|
@@ -420,13 +434,13 @@ function isEnvelopeV1(x) {
|
|
|
420
434
|
if (typeof x !== "object" || x === null) return false;
|
|
421
435
|
const e = x;
|
|
422
436
|
const kdf = e.kdf;
|
|
423
|
-
return e.v === 1 && typeof kdf === "object" && kdf !== null && kdf.name === "PBKDF2-SHA256" && Number.isInteger(kdf.iterations) && typeof kdf.salt === "string" && isBox(e.dek) && typeof e.secrets === "object" && e.secrets !== null && Object.values(e.secrets).every(isBox);
|
|
437
|
+
return e.v === 1 && typeof kdf === "object" && kdf !== null && kdf.name === "PBKDF2-SHA256" && Number.isInteger(kdf.iterations) && typeof kdf.salt === "string" && isBox(e.dek) && typeof e.secrets === "object" && e.secrets !== null && Object.values(e.secrets).every(isBox) && (e.dekCheck === void 0 || isBox(e.dekCheck));
|
|
424
438
|
}
|
|
425
439
|
function isEnvelopeV2(x) {
|
|
426
440
|
if (typeof x !== "object" || x === null) return false;
|
|
427
441
|
const e = x;
|
|
428
442
|
const kdf = e.kdf;
|
|
429
|
-
return e.v === 2 && typeof kdf === "object" && kdf !== null && kdf.name === "scrypt" && Number.isInteger(kdf.N) && Number.isInteger(kdf.r) && Number.isInteger(kdf.p) && typeof kdf.salt === "string" && isBox(e.dek) && typeof e.secrets === "object" && e.secrets !== null && Object.values(e.secrets).every(isBox) && Array.isArray(e.passkeys) && e.passkeys.every((p) => typeof p === "object" && p !== null && typeof p.id === "string" && typeof p.label === "string" && isBox(p.dek));
|
|
443
|
+
return e.v === 2 && typeof kdf === "object" && kdf !== null && kdf.name === "scrypt" && Number.isInteger(kdf.N) && Number.isInteger(kdf.r) && Number.isInteger(kdf.p) && typeof kdf.salt === "string" && isBox(e.dek) && typeof e.secrets === "object" && e.secrets !== null && Object.values(e.secrets).every(isBox) && (e.dekCheck === void 0 || isBox(e.dekCheck)) && Array.isArray(e.passkeys) && e.passkeys.every((p) => typeof p === "object" && p !== null && typeof p.id === "string" && typeof p.label === "string" && isBox(p.dek));
|
|
430
444
|
}
|
|
431
445
|
function isBox(x) {
|
|
432
446
|
return typeof x === "object" && x !== null && typeof x.iv === "string" && typeof x.data === "string";
|
|
@@ -441,7 +455,7 @@ function envelopeProblem(e) {
|
|
|
441
455
|
if (secrets.length > VAULT_LIMITS.secrets) return `${secrets.length} secrets > ${VAULT_LIMITS.secrets}`;
|
|
442
456
|
const passkeys = e.v === 2 ? e.passkeys : [];
|
|
443
457
|
if (passkeys.length > VAULT_LIMITS.passkeys) return `${passkeys.length} passkeys > ${VAULT_LIMITS.passkeys}`;
|
|
444
|
-
for (const box of [e.dek, ...secrets, ...passkeys.map((p) => p.dek)]) {
|
|
458
|
+
for (const box of [e.dek, ...e.dekCheck ? [e.dekCheck] : [], ...secrets, ...passkeys.map((p) => p.dek)]) {
|
|
445
459
|
if (box.iv.length > VAULT_LIMITS.boxChars || box.data.length > VAULT_LIMITS.boxChars) return `a sealed box above ${VAULT_LIMITS.boxChars} characters`;
|
|
446
460
|
}
|
|
447
461
|
if (JSON.stringify(e).length > VAULT_LIMITS.envelopeChars) return `envelope above ${VAULT_LIMITS.envelopeChars} characters`;
|
|
@@ -1677,6 +1691,12 @@ function outgrown(shown, now) {
|
|
|
1677
1691
|
if ("gasPrice" in now) return shown.gasPrice === void 0 || now.gasPrice > big(shown.gasPrice);
|
|
1678
1692
|
return shown.maxFeePerGas === void 0 || now.base + now.tip > big(shown.maxFeePerGas);
|
|
1679
1693
|
}
|
|
1694
|
+
var STALE_FEE_FACTOR = 4n;
|
|
1695
|
+
function overpriced(shown, fresh) {
|
|
1696
|
+
if (shown.gasPrice !== void 0 && fresh.gasPrice !== void 0) return big(shown.gasPrice) > STALE_FEE_FACTOR * big(fresh.gasPrice);
|
|
1697
|
+
if (shown.maxFeePerGas !== void 0 && fresh.maxFeePerGas !== void 0) return big(shown.maxFeePerGas) > STALE_FEE_FACTOR * big(fresh.maxFeePerGas);
|
|
1698
|
+
return false;
|
|
1699
|
+
}
|
|
1680
1700
|
async function prepareTransaction(deps, network, from, tx) {
|
|
1681
1701
|
const call = { from, to: tx.to, value: hex(BigInt(tx.value)), ...tx.data ? { data: tx.data } : {} };
|
|
1682
1702
|
const [nonceHex, block, gasHex] = await Promise.all([
|
|
@@ -1722,6 +1742,8 @@ function createSignHandler(deps) {
|
|
|
1722
1742
|
await deps.approvals.request({ origin, chain: network.caip2, kind: info.kind, payload: { ...payload, prepared: shown, repriced: true } });
|
|
1723
1743
|
continue;
|
|
1724
1744
|
}
|
|
1745
|
+
const fresh = withFees(network.chainId, nonce, big(shown.gasLimit), fees);
|
|
1746
|
+
if (overpriced(shown, fresh)) shown = fresh;
|
|
1725
1747
|
const handle2 = await deps.signerFor(info.account, network);
|
|
1726
1748
|
let raw;
|
|
1727
1749
|
try {
|
|
@@ -1978,13 +2000,14 @@ var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "action-required", "re
|
|
|
1978
2000
|
function parseSwapId(id) {
|
|
1979
2001
|
if (!id.startsWith(`${SWAP2_ID_PREFIX}.`)) return null;
|
|
1980
2002
|
const parts = id.slice(SWAP2_ID_PREFIX.length + 1).split(".");
|
|
1981
|
-
if (parts.length < 2 || parts.length >
|
|
2003
|
+
if (parts.length < 2 || parts.length > 5 || !parts[0] || !parts[1]) return null;
|
|
2004
|
+
if (parts[4] !== void 0 && (parts[4] !== "ok" || !parts[3])) return null;
|
|
1982
2005
|
const provider = decodeURIComponent(parts[0]);
|
|
1983
2006
|
const intentId = decodeURIComponent(parts[1]);
|
|
1984
2007
|
const sourceTxHash = parts[2] ? decodeURIComponent(parts[2]) : void 0;
|
|
1985
2008
|
const destTxHash = parts[3] ? decodeURIComponent(parts[3]) : void 0;
|
|
1986
2009
|
if (destTxHash && !sourceTxHash) return null;
|
|
1987
|
-
return { provider, intentId, ...sourceTxHash ? { sourceTxHash } : {}, ...destTxHash ? { destTxHash } : {} };
|
|
2010
|
+
return { provider, intentId, ...sourceTxHash ? { sourceTxHash } : {}, ...destTxHash ? { destTxHash } : {}, ...parts[4] !== void 0 ? { destTxConfirmed: true } : {} };
|
|
1988
2011
|
}
|
|
1989
2012
|
var SwapsStore = class {
|
|
1990
2013
|
constructor(storage) {
|