passkey-kit 0.18.2 → 0.19.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 +1 -0
- package/SECURITY.md +19 -0
- package/dist/constants.d.ts +22 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +34 -0
- package/dist/constants.js.map +1 -1
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +43 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/kit/legacy-ops.d.ts +123 -0
- package/dist/kit/legacy-ops.d.ts.map +1 -0
- package/dist/kit/legacy-ops.js +237 -0
- package/dist/kit/legacy-ops.js.map +1 -0
- package/dist/kit.d.ts +51 -0
- package/dist/kit.d.ts.map +1 -1
- package/dist/kit.js +144 -3
- package/dist/kit.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +21 -29
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy (pre-1.0) wallet upgrade helpers.
|
|
3
|
+
*
|
|
4
|
+
* The kit cannot connect to a pre-1.0 wallet (`connectWallet` throws
|
|
5
|
+
* {@link LegacyWalletError}), but it can still craft the one transaction such
|
|
6
|
+
* a wallet needs: an in-place `update_contract_code` to the legacy-line build
|
|
7
|
+
* that reads both pre-1.0 storage layouts, followed for the bare-layout cohort
|
|
8
|
+
* by a `migrate_signers` call. This module builds those transactions from the
|
|
9
|
+
* wallet's actual on-chain state, so an application only has to sign and
|
|
10
|
+
* submit. See docs/legacy-wallet-upgrade.md for the full procedure.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
import { Address, xdr } from "@stellar/stellar-sdk";
|
|
15
|
+
import { AssembledTransaction } from "@stellar/stellar-sdk/contract";
|
|
16
|
+
import { KNOWN_VULNERABLE_WALLET_WASM_HASHES, LEGACY_UPGRADE_TARGET_WASM_HASH, LEGACY_WALLET_UPGRADE_GUIDE_URL, LEGACY_WALLET_WASM_HASHES, } from "../constants.js";
|
|
17
|
+
import { PasskeyKitErrorCode, SigningError, ValidationError } from "../errors.js";
|
|
18
|
+
import { signerKeyToScVal } from "./auth-payload.js";
|
|
19
|
+
import { assertAdminRootMatchesHostFunction, signAuthEntry, } from "./tx-ops.js";
|
|
20
|
+
import { toContractSignerKey } from "./wallet-ops.js";
|
|
21
|
+
/**
|
|
22
|
+
* Pre-1.0 wallets that store signers in the pre-`6a27d48` (2024-12-13)
|
|
23
|
+
* layout. Every later build fails to decode them, which is why only the
|
|
24
|
+
* legacy-line target is a safe upgrade for them and why `migrate_signers`
|
|
25
|
+
* exists.
|
|
26
|
+
*/
|
|
27
|
+
export const BARE_LAYOUT_WALLET_WASM_HASHES = [
|
|
28
|
+
"0c0a264d4cc0b3e79b8533e2a2e1f0ed21501a5a3f9f2455d2f18c232940b865",
|
|
29
|
+
"19868df3653d427cafa1c30bdb6cec1ca5c8c815eeabab8a8bae6d83efb1fedd",
|
|
30
|
+
];
|
|
31
|
+
/** Classify a code hash without touching the network. */
|
|
32
|
+
export function classifyWasmHash(wasmHash, acceptedWasmHashes) {
|
|
33
|
+
const hash = wasmHash.toLowerCase();
|
|
34
|
+
if (acceptedWasmHashes.includes(hash)) {
|
|
35
|
+
return { status: "current", cohort: null };
|
|
36
|
+
}
|
|
37
|
+
if (KNOWN_VULNERABLE_WALLET_WASM_HASHES.includes(hash)) {
|
|
38
|
+
return {
|
|
39
|
+
status: "vulnerable",
|
|
40
|
+
cohort: BARE_LAYOUT_WALLET_WASM_HASHES.includes(hash) ? "bare" : "wrapped",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (LEGACY_WALLET_WASM_HASHES.includes(hash)) {
|
|
44
|
+
return { status: "legacy", cohort: "wrapped" };
|
|
45
|
+
}
|
|
46
|
+
return { status: "unknown", cohort: null };
|
|
47
|
+
}
|
|
48
|
+
function instanceLedgerKey(contractId) {
|
|
49
|
+
return xdr.LedgerKey.contractData(new xdr.LedgerKeyContractData({
|
|
50
|
+
contract: Address.fromString(contractId).toScAddress(),
|
|
51
|
+
key: xdr.ScVal.scvLedgerKeyContractInstance(),
|
|
52
|
+
durability: xdr.ContractDataDurability.persistent(),
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
function codeLedgerKey(wasmHash) {
|
|
56
|
+
return xdr.LedgerKey.contractCode(new xdr.LedgerKeyContractCode({ hash: Buffer.from(wasmHash, "hex") }));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Read a wallet's code hash and the liveness of the entries an upgrade
|
|
60
|
+
* touches, and say what to do.
|
|
61
|
+
*/
|
|
62
|
+
export async function inspectLegacyWallet(deps, contractId) {
|
|
63
|
+
const wasmHash = (await deps.contractWasmHash(contractId)).toLowerCase();
|
|
64
|
+
const { status, cohort } = classifyWasmHash(wasmHash, deps.acceptedWasmHashes);
|
|
65
|
+
const upgradeTarget = LEGACY_UPGRADE_TARGET_WASM_HASH;
|
|
66
|
+
const keys = [instanceLedgerKey(contractId), codeLedgerKey(wasmHash), codeLedgerKey(upgradeTarget)];
|
|
67
|
+
const response = await deps.rpc.getLedgerEntries(...keys);
|
|
68
|
+
const latest = response.latestLedger;
|
|
69
|
+
const liveByKey = new Map();
|
|
70
|
+
for (const entry of response.entries) {
|
|
71
|
+
const liveUntil = entry.liveUntilLedgerSeq;
|
|
72
|
+
liveByKey.set(entry.key.toXDR("base64"), liveUntil !== undefined && liveUntil > latest);
|
|
73
|
+
}
|
|
74
|
+
const isLive = (key) => liveByKey.get(key.toXDR("base64")) === true;
|
|
75
|
+
const archived = {
|
|
76
|
+
instance: !isLive(keys[0]),
|
|
77
|
+
code: !isLive(keys[1]),
|
|
78
|
+
target: !isLive(keys[2]),
|
|
79
|
+
};
|
|
80
|
+
const upgradeRequired = status === "vulnerable";
|
|
81
|
+
const migrateRequired = cohort === "bare";
|
|
82
|
+
let recommendation;
|
|
83
|
+
switch (status) {
|
|
84
|
+
case "vulnerable":
|
|
85
|
+
recommendation =
|
|
86
|
+
`This wallet runs known-vulnerable code ${wasmHash.slice(0, 8)}…; anyone can overwrite ` +
|
|
87
|
+
`its signers. Upgrade it in place now: ` +
|
|
88
|
+
(archived.instance || archived.code || archived.target
|
|
89
|
+
? `restore the archived entries (${[
|
|
90
|
+
archived.instance && "instance",
|
|
91
|
+
archived.code && "current code",
|
|
92
|
+
archived.target && "target code",
|
|
93
|
+
]
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.join(", ")}), then `
|
|
96
|
+
: "") +
|
|
97
|
+
`call update_contract_code(${upgradeTarget.slice(0, 8)}…) authorized by an existing signer` +
|
|
98
|
+
(migrateRequired
|
|
99
|
+
? `, then call migrate_signers with every signer key (this wallet stores the pre-6a27d48 layout)`
|
|
100
|
+
: "") +
|
|
101
|
+
`. Or move its funds out. Guide: ${LEGACY_WALLET_UPGRADE_GUIDE_URL}`;
|
|
102
|
+
break;
|
|
103
|
+
case "legacy":
|
|
104
|
+
recommendation =
|
|
105
|
+
`This wallet runs patched pre-1.0 code ${wasmHash.slice(0, 8)}…. It is not vulnerable; ` +
|
|
106
|
+
`operate it with the passkey-kit 0.10.20–0.12.x line. Guide: ${LEGACY_WALLET_UPGRADE_GUIDE_URL}`;
|
|
107
|
+
break;
|
|
108
|
+
case "current":
|
|
109
|
+
recommendation = "This wallet runs accepted current code. No upgrade is needed.";
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
recommendation =
|
|
113
|
+
`This wallet runs code ${wasmHash.slice(0, 8)}… that this kit does not recognize. ` +
|
|
114
|
+
`Verify its provenance before acting.`;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
contractId,
|
|
118
|
+
wasmHash,
|
|
119
|
+
status,
|
|
120
|
+
cohort,
|
|
121
|
+
upgradeTarget,
|
|
122
|
+
upgradeRequired,
|
|
123
|
+
migrateRequired,
|
|
124
|
+
archived,
|
|
125
|
+
guideUrl: LEGACY_WALLET_UPGRADE_GUIDE_URL,
|
|
126
|
+
recommendation,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build `wallet.update_contract_code(target)` for a legacy wallet.
|
|
131
|
+
*
|
|
132
|
+
* The transaction's single auth entry is the wallet's own, to be signed by an
|
|
133
|
+
* existing signer through {@link signLegacyUpgradeTx}. The envelope source is
|
|
134
|
+
* the SDK's placeholder account, so submit through `PasskeyServer.send` (a
|
|
135
|
+
* relayer supplies the source and fees) or rebuild with your own funded source.
|
|
136
|
+
*/
|
|
137
|
+
export function buildLegacyUpgradeTx(deps, contractId, target = LEGACY_UPGRADE_TARGET_WASM_HASH) {
|
|
138
|
+
if (!/^[0-9a-f]{64}$/i.test(target)) {
|
|
139
|
+
throw new ValidationError("upgrade target must be a 32-byte hex WASM hash", PasskeyKitErrorCode.INVALID_INPUT, { target });
|
|
140
|
+
}
|
|
141
|
+
return AssembledTransaction.build({
|
|
142
|
+
method: "update_contract_code",
|
|
143
|
+
args: [xdr.ScVal.scvBytes(Buffer.from(target, "hex"))],
|
|
144
|
+
contractId,
|
|
145
|
+
rpcUrl: deps.rpcUrl,
|
|
146
|
+
networkPassphrase: deps.networkPassphrase,
|
|
147
|
+
timeoutInSeconds: deps.timeoutInSeconds,
|
|
148
|
+
parseResultXdr: () => null,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Build `wallet.migrate_signers(keys)` for a wallet already on the legacy-line
|
|
153
|
+
* target. The call needs no wallet authorization (it is a value-preserving
|
|
154
|
+
* re-encoding), so any funded source can submit it. Returns the number of
|
|
155
|
+
* entries rewritten when simulated or executed.
|
|
156
|
+
*
|
|
157
|
+
* Soroban has no storage iteration, so the caller supplies the keys: the
|
|
158
|
+
* hosted passkey indexer (`MercuryIndexer.getSigners` /
|
|
159
|
+
* `PasskeyServer.getSigners`) lists every signer a wallet has ever held.
|
|
160
|
+
*/
|
|
161
|
+
export function buildLegacyMigrateTx(deps, contractId, signerKeys) {
|
|
162
|
+
if (signerKeys.length === 0) {
|
|
163
|
+
throw new ValidationError("migrate_signers needs at least one signer key", PasskeyKitErrorCode.INVALID_INPUT, { contractId });
|
|
164
|
+
}
|
|
165
|
+
const keys = signerKeys.map((key) => signerKeyToScVal(deps.spec, toContractSignerKey(key)));
|
|
166
|
+
return AssembledTransaction.build({
|
|
167
|
+
method: "migrate_signers",
|
|
168
|
+
args: [xdr.ScVal.scvVec(keys)],
|
|
169
|
+
contractId,
|
|
170
|
+
rpcUrl: deps.rpcUrl,
|
|
171
|
+
networkPassphrase: deps.networkPassphrase,
|
|
172
|
+
timeoutInSeconds: deps.timeoutInSeconds,
|
|
173
|
+
parseResultXdr: (value) => value.u32(),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Sign a legacy wallet's `update_contract_code` auth entry with one of its
|
|
178
|
+
* existing signers, without a connected wallet.
|
|
179
|
+
*
|
|
180
|
+
* Passkey and Ed25519 signature entries encode identically on every pre-1.0
|
|
181
|
+
* build, and the signature covers the V2 address-bound payload the host
|
|
182
|
+
* presents to any custom account, so the kit's normal signers work here. The
|
|
183
|
+
* entry root is pinned to the transaction's own host function, as for every
|
|
184
|
+
* wallet-admin write.
|
|
185
|
+
*/
|
|
186
|
+
export async function signLegacyUpgradeTx(deps, tx, signer, options) {
|
|
187
|
+
const built = tx.built;
|
|
188
|
+
const topOp = built?.operations[0];
|
|
189
|
+
const topFunc = topOp?.type === "invokeHostFunction"
|
|
190
|
+
? topOp.func
|
|
191
|
+
: undefined;
|
|
192
|
+
if (!topFunc) {
|
|
193
|
+
throw new ValidationError("the transaction has no invoke-host-function operation to sign", PasskeyKitErrorCode.INVALID_INPUT, { contractId: deps.contractId });
|
|
194
|
+
}
|
|
195
|
+
// Resolve the expiration once here (the kit's own ledger-based default) so
|
|
196
|
+
// the SDK does not fetch it, and pass the same value to every entry.
|
|
197
|
+
const expiration = options?.expiration ?? (await deps.calculateExpiration());
|
|
198
|
+
await tx.signAuthEntries({
|
|
199
|
+
address: deps.contractId,
|
|
200
|
+
expiration,
|
|
201
|
+
authorizeEntry: async (entry) => {
|
|
202
|
+
const clone = xdr.SorobanAuthorizationEntry.fromXDR(entry.toXDR());
|
|
203
|
+
assertRootIsExactlyThisCall(clone, deps.contractId, topFunc);
|
|
204
|
+
assertAdminRootMatchesHostFunction(clone, deps.contractId, topFunc);
|
|
205
|
+
return signAuthEntry(deps, clone, signer, {
|
|
206
|
+
...options,
|
|
207
|
+
expiration,
|
|
208
|
+
allowWalletReentry: true,
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return tx;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The legacy upgrade signs exactly one thing: this wallet's own
|
|
216
|
+
* `update_contract_code` (or `migrate_signers`) as the transaction's
|
|
217
|
+
* top-level call, with no sub-invocations. Anything else is refused before
|
|
218
|
+
* hashing, so a hostile transaction cannot borrow this signing path.
|
|
219
|
+
*/
|
|
220
|
+
function assertRootIsExactlyThisCall(entry, contractId, topFunc) {
|
|
221
|
+
const root = entry.rootInvocation();
|
|
222
|
+
const fn = root.function();
|
|
223
|
+
const expected = topFunc.switch().name === "hostFunctionTypeInvokeContract"
|
|
224
|
+
? topFunc.invokeContract()
|
|
225
|
+
: undefined;
|
|
226
|
+
const actual = fn.switch().name === "sorobanAuthorizedFunctionTypeContractFn" ? fn.contractFn() : undefined;
|
|
227
|
+
const ok = expected !== undefined &&
|
|
228
|
+
actual !== undefined &&
|
|
229
|
+
root.subInvocations().length === 0 &&
|
|
230
|
+
Address.fromScAddress(actual.contractAddress()).toString() === contractId &&
|
|
231
|
+
actual.toXDR("base64") === expected.toXDR("base64");
|
|
232
|
+
if (!ok) {
|
|
233
|
+
throw new SigningError(`Refusing to sign: the auth entry must root at ${contractId}'s own top-level ` +
|
|
234
|
+
`update_contract_code / migrate_signers call with no sub-invocations`, PasskeyKitErrorCode.SIGNING_FAILED, { contractId });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
//# sourceMappingURL=legacy-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-ops.js","sourceRoot":"","sources":["../../src/kit/legacy-ops.ts"],"names":[],"mappings":"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"}
|
package/dist/kit.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { type Signer } from "./signers.js";
|
|
|
22
22
|
import type { WebAuthnClient } from "./kit/webauthn-ops.js";
|
|
23
23
|
import type { CreatedPasskey } from "./kit/webauthn-ops.js";
|
|
24
24
|
import type { SignOptions } from "./kit/tx-ops.js";
|
|
25
|
+
import { type LegacyWalletInspection } from "./kit/legacy-ops.js";
|
|
25
26
|
import { type PolicySignerTxOptions, type WalletTx } from "./kit/wallet-ops.js";
|
|
26
27
|
import type { WalletCandidateLookup } from "./indexer/types.js";
|
|
27
28
|
import { type VerifiedWalletBirth } from "./kit/birth-verification.js";
|
|
@@ -97,6 +98,8 @@ export declare class PasskeyKit {
|
|
|
97
98
|
readonly acceptedWasmHashes: readonly string[];
|
|
98
99
|
/** Accepted immutable birth code identities, lowercase hex. Never empty. */
|
|
99
100
|
readonly acceptedBirthWasmHashes: readonly string[];
|
|
101
|
+
/** Funded key used only for footprint restores, when configured. */
|
|
102
|
+
private readonly restoreKeypair?;
|
|
100
103
|
/** Full-history source for immutable wallet-birth verification. */
|
|
101
104
|
readonly history?: Horizon.Server;
|
|
102
105
|
readonly rpId?: string;
|
|
@@ -143,6 +146,13 @@ export declare class PasskeyKit {
|
|
|
143
146
|
* @throws {WalletOwnershipError} If the keyId is not a signer on the wallet.
|
|
144
147
|
*/
|
|
145
148
|
connectWallet(options?: ConnectOptions): Promise<ConnectWalletResult>;
|
|
149
|
+
/**
|
|
150
|
+
* Reject a wallet whose current code is a pre-1.0 build, with guidance.
|
|
151
|
+
* Known-vulnerable builds get the upgrade instructions; patched legacy
|
|
152
|
+
* builds get the "use the 0.10.20–0.12.x kit" instruction. Accepted hashes
|
|
153
|
+
* are never legacy, so an integrator cannot opt into a vulnerable build.
|
|
154
|
+
*/
|
|
155
|
+
private assertNotLegacyWallet;
|
|
146
156
|
private assertWalletWasmHash;
|
|
147
157
|
private contractWasmHash;
|
|
148
158
|
/** Create the address-bound proof required for a Secp256r1 signer write. */
|
|
@@ -154,6 +164,47 @@ export declare class PasskeyKit {
|
|
|
154
164
|
private assertWebAuthnVerificationConfig;
|
|
155
165
|
/** Disconnect the current wallet. */
|
|
156
166
|
disconnect(): void;
|
|
167
|
+
/**
|
|
168
|
+
* Inspect a wallet this kit cannot connect to and say what it needs:
|
|
169
|
+
* its code status (vulnerable / legacy / current / unknown), its storage
|
|
170
|
+
* cohort, which ledger entries are archived, and a plain recommendation.
|
|
171
|
+
* Read-only.
|
|
172
|
+
*/
|
|
173
|
+
inspectLegacyWallet(contractId: string): Promise<LegacyWalletInspection>;
|
|
174
|
+
private legacyTxDeps;
|
|
175
|
+
/**
|
|
176
|
+
* Restore archived entries a simulated transaction needs, using the
|
|
177
|
+
* configured `restoreSource`, and rebuild. Throws with guidance when no
|
|
178
|
+
* restore source is configured.
|
|
179
|
+
*/
|
|
180
|
+
private withRestore;
|
|
181
|
+
/**
|
|
182
|
+
* Build the in-place upgrade for a pre-1.0 wallet:
|
|
183
|
+
* `update_contract_code(<legacy-line target>)`, authorized by the wallet.
|
|
184
|
+
* Refuses wallets that are not on a known pre-1.0 build. Restores archived
|
|
185
|
+
* entries first when `restoreSource` is configured.
|
|
186
|
+
*
|
|
187
|
+
* Sign it with {@link signLegacyUpgradeTx} and submit through
|
|
188
|
+
* `PasskeyServer.send` (or your own funded source).
|
|
189
|
+
*/
|
|
190
|
+
buildLegacyUpgradeTx(contractId: string): Promise<{
|
|
191
|
+
inspection: LegacyWalletInspection;
|
|
192
|
+
tx: AssembledTransaction<null>;
|
|
193
|
+
}>;
|
|
194
|
+
/**
|
|
195
|
+
* Build `migrate_signers(keys)` for a wallet already upgraded to the
|
|
196
|
+
* legacy-line target. Needs no wallet authorization; any funded source can
|
|
197
|
+
* submit it. Get the keys from `PasskeyServer.getSigners` /
|
|
198
|
+
* `MercuryIndexer.getSigners`.
|
|
199
|
+
*/
|
|
200
|
+
buildLegacyMigrateTx(contractId: string, signerKeys: readonly SignerKey[]): Promise<AssembledTransaction<number>>;
|
|
201
|
+
/**
|
|
202
|
+
* Sign a legacy wallet's upgrade transaction with one of its existing
|
|
203
|
+
* signers, without connecting. Defaults to a discoverable passkey prompt;
|
|
204
|
+
* pass `new PasskeySigner(keyId)` to require a specific credential, or an
|
|
205
|
+
* `Ed25519Signer`.
|
|
206
|
+
*/
|
|
207
|
+
signLegacyUpgradeTx<T>(tx: AssembledTransaction<T>, contractId: string, signer?: Signer, options?: Omit<SignOptions, "allowWalletReentry">): Promise<AssembledTransaction<T>>;
|
|
157
208
|
/** Sign a single auth entry (defaults to the connected passkey signer). */
|
|
158
209
|
signAuthEntry(entry: xdr.SorobanAuthorizationEntry, signer?: Signer, options?: SignOptions): Promise<xdr.SorobanAuthorizationEntry>;
|
|
159
210
|
/** Sign an assembled transaction's wallet auth entries. */
|
package/dist/kit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../src/kit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAqB,GAAG,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAO,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAIL,KAAK,8BAA8B,EACpC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,MAAM,IAAI,aAAa,EAIvB,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,SAAS,EACT,WAAW,EACX,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"kit.d.ts","sourceRoot":"","sources":["../src/kit.ts"],"names":[],"mappings":"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"}
|
package/dist/kit.js
CHANGED
|
@@ -17,14 +17,16 @@ import { startAuthentication, startRegistration, } from "@simplewebauthn/browser
|
|
|
17
17
|
import { Client as PasskeyClient, } from "passkey-kit-sdk";
|
|
18
18
|
import base64url from "./base64url.js";
|
|
19
19
|
import { SignerKey, SignerStore, } from "./types.js";
|
|
20
|
-
import { ConfigurationError, PasskeyKitError, PasskeyKitErrorCode, WalletNotConnectedError, WalletOwnershipError, WalletAmbiguousError, } from "./errors.js";
|
|
20
|
+
import { ConfigurationError, PasskeyKitError, ValidationError, PasskeyKitErrorCode, WalletNotConnectedError, WalletOwnershipError, WalletAmbiguousError, LegacyWalletError, } from "./errors.js";
|
|
21
21
|
import { PasskeyEventEmitter } from "./events.js";
|
|
22
22
|
import { isDefaultDeployer } from "./utils.js";
|
|
23
|
-
import { DEFAULT_TIMEOUT_SECONDS } from "./constants.js";
|
|
23
|
+
import { DEFAULT_TIMEOUT_SECONDS, KNOWN_VULNERABLE_WALLET_WASM_HASHES, LEGACY_UPGRADE_TARGET_WASM_HASH, LEGACY_WALLET_UPGRADE_GUIDE_URL, LEGACY_WALLET_WASM_HASHES, } from "./constants.js";
|
|
24
24
|
import { PasskeySigner } from "./signers.js";
|
|
25
25
|
import { calculateExpiration } from "./kit/tx-ops.js";
|
|
26
26
|
import { CredentialManager, SignerManager, SubmissionManager, } from "./managers/index.js";
|
|
27
27
|
import { resolveDeployer } from "./kit/deploy-ops.js";
|
|
28
|
+
import { buildLegacyMigrateTx, buildLegacyUpgradeTx, inspectLegacyWallet, signLegacyUpgradeTx, } from "./kit/legacy-ops.js";
|
|
29
|
+
import { restoreFootprint } from "./kit/tx-ops.js";
|
|
28
30
|
import { buildSecp256r1Signer, } from "./kit/wallet-ops.js";
|
|
29
31
|
import { verifyWalletBirth, } from "./kit/birth-verification.js";
|
|
30
32
|
import { bindingChallenge, verifyBindingRecord, verifyFreshAssertion, verifyStoredProof, } from "./kit/webauthn-verify.js";
|
|
@@ -44,6 +46,8 @@ export class PasskeyKit {
|
|
|
44
46
|
acceptedWasmHashes;
|
|
45
47
|
/** Accepted immutable birth code identities, lowercase hex. Never empty. */
|
|
46
48
|
acceptedBirthWasmHashes;
|
|
49
|
+
/** Funded key used only for footprint restores, when configured. */
|
|
50
|
+
restoreKeypair;
|
|
47
51
|
/** Full-history source for immutable wallet-birth verification. */
|
|
48
52
|
history;
|
|
49
53
|
rpId;
|
|
@@ -70,6 +74,18 @@ export class PasskeyKit {
|
|
|
70
74
|
if (!config.walletWasmHash) {
|
|
71
75
|
throw new ConfigurationError("walletWasmHash is required", PasskeyKitErrorCode.MISSING_CONFIG);
|
|
72
76
|
}
|
|
77
|
+
for (const configured of [
|
|
78
|
+
config.walletWasmHash,
|
|
79
|
+
...(config.acceptedWasmHashes ?? []),
|
|
80
|
+
...(config.acceptedBirthWasmHashes ?? []),
|
|
81
|
+
]) {
|
|
82
|
+
if (KNOWN_VULNERABLE_WALLET_WASM_HASHES.includes(configured.toLowerCase())) {
|
|
83
|
+
throw new ConfigurationError(`walletWasmHash ${configured.slice(0, 8)}… is a known-vulnerable legacy build ` +
|
|
84
|
+
`(update_signer has no authorization check). Do not deploy from it or accept it. ` +
|
|
85
|
+
`Existing wallets on it must upgrade to ${LEGACY_UPGRADE_TARGET_WASM_HASH}; ` +
|
|
86
|
+
`see ${LEGACY_WALLET_UPGRADE_GUIDE_URL}`, PasskeyKitErrorCode.INVALID_CONFIG, { wasmHash: configured.toLowerCase() });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
73
89
|
this.rpc = new Server(config.rpcUrl);
|
|
74
90
|
this.rpcUrl = config.rpcUrl;
|
|
75
91
|
this.networkPassphrase = config.networkPassphrase;
|
|
@@ -130,6 +146,7 @@ export class PasskeyKit {
|
|
|
130
146
|
getSignerContext: () => this.signerContext(),
|
|
131
147
|
calculateExpiration: () => calculateExpiration({ rpc: this.rpc, timeoutInSeconds: this.timeoutInSeconds }),
|
|
132
148
|
});
|
|
149
|
+
this.restoreKeypair = restoreKeypair;
|
|
133
150
|
this.submissionManager = new SubmissionManager({
|
|
134
151
|
rpc: this.rpc,
|
|
135
152
|
rpcUrl: config.rpcUrl,
|
|
@@ -299,6 +316,9 @@ export class PasskeyKit {
|
|
|
299
316
|
// The last definitive rejection, rethrown verbatim when nothing verifies so
|
|
300
317
|
// its context (e.g. the rejected wasm hash) is not replaced by a summary.
|
|
301
318
|
let lastMismatch;
|
|
319
|
+
// A candidate on pre-1.0 code is a definitive answer with its own guidance.
|
|
320
|
+
// It is reported in preference to any generic mismatch when nothing verifies.
|
|
321
|
+
let legacyMismatch;
|
|
302
322
|
for (const candidate of candidates) {
|
|
303
323
|
const wallet = new PasskeyClient({
|
|
304
324
|
contractId: candidate.contractId,
|
|
@@ -308,6 +328,10 @@ export class PasskeyKit {
|
|
|
308
328
|
this.wallet = wallet;
|
|
309
329
|
this.keyId = keyIdBase64;
|
|
310
330
|
try {
|
|
331
|
+
// Before birth verification: a legacy wallet fails that check for
|
|
332
|
+
// reasons that would hide the real problem (no constructor birth, an
|
|
333
|
+
// unaccepted hash). Name the legacy code and the upgrade path instead.
|
|
334
|
+
await this.assertNotLegacyWallet(candidate.contractId);
|
|
311
335
|
const birth = await verifyWalletBirth({
|
|
312
336
|
rpc: this.rpc,
|
|
313
337
|
history: this.history,
|
|
@@ -358,6 +382,10 @@ export class PasskeyKit {
|
|
|
358
382
|
catch (err) {
|
|
359
383
|
this.wallet = undefined;
|
|
360
384
|
this.keyId = undefined;
|
|
385
|
+
if (err instanceof LegacyWalletError) {
|
|
386
|
+
legacyMismatch = err;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
361
389
|
if (err instanceof WalletOwnershipError) {
|
|
362
390
|
lastMismatch = err;
|
|
363
391
|
continue;
|
|
@@ -368,7 +396,8 @@ export class PasskeyKit {
|
|
|
368
396
|
this.wallet = undefined;
|
|
369
397
|
this.keyId = undefined;
|
|
370
398
|
if (verified.size === 0) {
|
|
371
|
-
throw (
|
|
399
|
+
throw (legacyMismatch ??
|
|
400
|
+
lastMismatch ??
|
|
372
401
|
new WalletOwnershipError("The passkey is not a signer on any resolved wallet", {
|
|
373
402
|
candidates: candidates.map(({ contractId }) => contractId),
|
|
374
403
|
keyId: keyIdBase64,
|
|
@@ -392,6 +421,26 @@ export class PasskeyKit {
|
|
|
392
421
|
this.events.emit("walletConnected", { contractId, keyId: keyIdBase64 });
|
|
393
422
|
return { rawResponse, keyId: keyIdBuffer, keyIdBase64, contractId };
|
|
394
423
|
}
|
|
424
|
+
/**
|
|
425
|
+
* Reject a wallet whose current code is a pre-1.0 build, with guidance.
|
|
426
|
+
* Known-vulnerable builds get the upgrade instructions; patched legacy
|
|
427
|
+
* builds get the "use the 0.10.20–0.12.x kit" instruction. Accepted hashes
|
|
428
|
+
* are never legacy, so an integrator cannot opt into a vulnerable build.
|
|
429
|
+
*/
|
|
430
|
+
async assertNotLegacyWallet(contractId) {
|
|
431
|
+
const wasmHash = await this.contractWasmHash(contractId);
|
|
432
|
+
const vulnerable = KNOWN_VULNERABLE_WALLET_WASM_HASHES.includes(wasmHash);
|
|
433
|
+
if (!vulnerable && !LEGACY_WALLET_WASM_HASHES.includes(wasmHash)) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
throw new LegacyWalletError({
|
|
437
|
+
contractId,
|
|
438
|
+
wasmHash,
|
|
439
|
+
vulnerable,
|
|
440
|
+
upgradeTarget: LEGACY_UPGRADE_TARGET_WASM_HASH,
|
|
441
|
+
guideUrl: LEGACY_WALLET_UPGRADE_GUIDE_URL,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
395
444
|
async assertWalletWasmHash(contractId) {
|
|
396
445
|
const wasmHash = await this.contractWasmHash(contractId);
|
|
397
446
|
if (!this.acceptedWasmHashes.includes(wasmHash)) {
|
|
@@ -512,6 +561,98 @@ export class PasskeyKit {
|
|
|
512
561
|
this.events.emit("walletDisconnected", { contractId });
|
|
513
562
|
}
|
|
514
563
|
}
|
|
564
|
+
// -- Legacy (pre-1.0) wallet upgrade ------------------------------------------
|
|
565
|
+
/**
|
|
566
|
+
* Inspect a wallet this kit cannot connect to and say what it needs:
|
|
567
|
+
* its code status (vulnerable / legacy / current / unknown), its storage
|
|
568
|
+
* cohort, which ledger entries are archived, and a plain recommendation.
|
|
569
|
+
* Read-only.
|
|
570
|
+
*/
|
|
571
|
+
inspectLegacyWallet(contractId) {
|
|
572
|
+
return inspectLegacyWallet({
|
|
573
|
+
rpc: this.rpc,
|
|
574
|
+
acceptedWasmHashes: this.acceptedWasmHashes,
|
|
575
|
+
contractWasmHash: (id) => this.contractWasmHash(id),
|
|
576
|
+
}, contractId);
|
|
577
|
+
}
|
|
578
|
+
legacyTxDeps(contractId) {
|
|
579
|
+
return {
|
|
580
|
+
rpcUrl: this.rpcUrl,
|
|
581
|
+
networkPassphrase: this.networkPassphrase,
|
|
582
|
+
timeoutInSeconds: this.timeoutInSeconds,
|
|
583
|
+
spec: new PasskeyClient({
|
|
584
|
+
contractId,
|
|
585
|
+
rpcUrl: this.rpcUrl,
|
|
586
|
+
networkPassphrase: this.networkPassphrase,
|
|
587
|
+
}).spec,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Restore archived entries a simulated transaction needs, using the
|
|
592
|
+
* configured `restoreSource`, and rebuild. Throws with guidance when no
|
|
593
|
+
* restore source is configured.
|
|
594
|
+
*/
|
|
595
|
+
async withRestore(build, contractId) {
|
|
596
|
+
let tx = await build();
|
|
597
|
+
const simulation = tx.simulation;
|
|
598
|
+
if (simulation && Api.isSimulationRestore(simulation)) {
|
|
599
|
+
if (!this.restoreKeypair) {
|
|
600
|
+
throw new PasskeyKitError(`Wallet ${contractId} has archived ledger entries that must be restored before this ` +
|
|
601
|
+
`call can run. Configure \`restoreSource\` (a funded key) on the kit, or submit a ` +
|
|
602
|
+
`RestoreFootprint operation yourself, then retry.`, PasskeyKitErrorCode.RESTORE_REQUIRED, { context: { contractId } });
|
|
603
|
+
}
|
|
604
|
+
await restoreFootprint({
|
|
605
|
+
rpc: this.rpc,
|
|
606
|
+
networkPassphrase: this.networkPassphrase,
|
|
607
|
+
sourceKeypair: this.restoreKeypair,
|
|
608
|
+
timeoutInSeconds: this.timeoutInSeconds,
|
|
609
|
+
}, simulation.restorePreamble);
|
|
610
|
+
tx = await build();
|
|
611
|
+
}
|
|
612
|
+
return tx;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Build the in-place upgrade for a pre-1.0 wallet:
|
|
616
|
+
* `update_contract_code(<legacy-line target>)`, authorized by the wallet.
|
|
617
|
+
* Refuses wallets that are not on a known pre-1.0 build. Restores archived
|
|
618
|
+
* entries first when `restoreSource` is configured.
|
|
619
|
+
*
|
|
620
|
+
* Sign it with {@link signLegacyUpgradeTx} and submit through
|
|
621
|
+
* `PasskeyServer.send` (or your own funded source).
|
|
622
|
+
*/
|
|
623
|
+
async buildLegacyUpgradeTx(contractId) {
|
|
624
|
+
const inspection = await this.inspectLegacyWallet(contractId);
|
|
625
|
+
if (inspection.status !== "vulnerable" && inspection.status !== "legacy") {
|
|
626
|
+
throw new ValidationError(`Wallet ${contractId} is not on a known pre-1.0 build (${inspection.status}); ` +
|
|
627
|
+
`there is no legacy upgrade to build. ${inspection.recommendation}`, PasskeyKitErrorCode.INVALID_INPUT, { contractId, wasmHash: inspection.wasmHash, status: inspection.status });
|
|
628
|
+
}
|
|
629
|
+
const tx = await this.withRestore(() => buildLegacyUpgradeTx(this.legacyTxDeps(contractId), contractId, inspection.upgradeTarget), contractId);
|
|
630
|
+
return { inspection, tx };
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Build `migrate_signers(keys)` for a wallet already upgraded to the
|
|
634
|
+
* legacy-line target. Needs no wallet authorization; any funded source can
|
|
635
|
+
* submit it. Get the keys from `PasskeyServer.getSigners` /
|
|
636
|
+
* `MercuryIndexer.getSigners`.
|
|
637
|
+
*/
|
|
638
|
+
buildLegacyMigrateTx(contractId, signerKeys) {
|
|
639
|
+
return this.withRestore(() => buildLegacyMigrateTx(this.legacyTxDeps(contractId), contractId, signerKeys), contractId);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Sign a legacy wallet's upgrade transaction with one of its existing
|
|
643
|
+
* signers, without connecting. Defaults to a discoverable passkey prompt;
|
|
644
|
+
* pass `new PasskeySigner(keyId)` to require a specific credential, or an
|
|
645
|
+
* `Ed25519Signer`.
|
|
646
|
+
*/
|
|
647
|
+
signLegacyUpgradeTx(tx, contractId, signer = new PasskeySigner("any"), options) {
|
|
648
|
+
return signLegacyUpgradeTx({
|
|
649
|
+
networkPassphrase: this.networkPassphrase,
|
|
650
|
+
spec: this.legacyTxDeps(contractId).spec,
|
|
651
|
+
signerContext: this.signerContext(),
|
|
652
|
+
calculateExpiration: () => calculateExpiration({ rpc: this.rpc, timeoutInSeconds: this.timeoutInSeconds }),
|
|
653
|
+
contractId,
|
|
654
|
+
}, tx, signer, options);
|
|
655
|
+
}
|
|
515
656
|
// -- Signing -----------------------------------------------------------------
|
|
516
657
|
/** Sign a single auth entry (defaults to the connected passkey signer). */
|
|
517
658
|
signAuthEntry(entry, signer, options) {
|