@waaskey/sdk 0.3.0 → 0.3.2
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 +230 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -49
- package/dist/index.d.ts +90 -49
- package/dist/index.js +230 -69
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +5 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -245,7 +245,11 @@ interface DeviceSignParams extends CeremonyParams {
|
|
|
245
245
|
}
|
|
246
246
|
/** Result of the device half of sign. */
|
|
247
247
|
interface DeviceSignResult {
|
|
248
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* The signature as LOW-S–normalized compact `r ‖ s` hex (128 chars, no `0x`) — byte-identical to
|
|
250
|
+
* what the platform's own party produces, so the backend can cross-check the two before embedding
|
|
251
|
+
* one into a transaction, and chain adapters can slice `r`/`s` by offset.
|
|
252
|
+
*/
|
|
249
253
|
signature: string;
|
|
250
254
|
}
|
|
251
255
|
/**
|
|
@@ -570,7 +574,7 @@ declare class WasmMpcCore implements MpcCore {
|
|
|
570
574
|
* exact, optional peer dependency in `package.json` — so an app that installs `@waaskey/client-wasm`
|
|
571
575
|
* alongside the SDK runs the real ed25519 create/send path end to end.
|
|
572
576
|
*/
|
|
573
|
-
declare const CLIENT_WASM_VERSION = "0.2.
|
|
577
|
+
declare const CLIENT_WASM_VERSION = "0.2.2";
|
|
574
578
|
/**
|
|
575
579
|
* Verify the integrity of raw wasm bytes against an expected **SHA-384** hash, in
|
|
576
580
|
* Subresource-Integrity (`sha384-<base64>`) form (issue #40).
|
|
@@ -609,24 +613,6 @@ interface VerifiedWasmLoaderOptions {
|
|
|
609
613
|
* ```
|
|
610
614
|
*/
|
|
611
615
|
declare function createVerifiedClientWasmLoader(options: VerifiedWasmLoaderOptions): ClientWasmLoader;
|
|
612
|
-
/**
|
|
613
|
-
* Convenience loader for the Waaskey client-wasm engine — dynamically imports `@waaskey/client-wasm`
|
|
614
|
-
* (the wasm-pack/bundler build, auto-initialized on import) and returns it as a {@link ClientWasmModule}:
|
|
615
|
-
*
|
|
616
|
-
* ```ts
|
|
617
|
-
* import { Waaskey, WasmMpcCore, loadClientWasm } from '@waaskey/sdk';
|
|
618
|
-
* const waaskey = new Waaskey({ apiKey, mpc: new WasmMpcCore(loadClientWasm), shareStore });
|
|
619
|
-
* ```
|
|
620
|
-
*
|
|
621
|
-
* ⚠️ **No integrity verification (issue #40).** A bundler-target build instantiates the wasm on
|
|
622
|
-
* import, so its bytes cannot be checked first. For production — where a registry compromise or
|
|
623
|
-
* dependency-confusion attack on `@waaskey/client-wasm` could exfiltrate the plaintext device share
|
|
624
|
-
* — use {@link createVerifiedClientWasmLoader} with a vendored `.wasm` and the pinned SHA-384 instead.
|
|
625
|
-
*
|
|
626
|
-
* `@waaskey/client-wasm` is an OPTIONAL peer dependency pinned to {@link CLIENT_WASM_VERSION}: install
|
|
627
|
-
* it alongside the SDK in apps that create wallets. The indirect import specifier keeps it out of
|
|
628
|
-
* static module resolution — so a missing install fails here with an actionable message, not at build.
|
|
629
|
-
*/
|
|
630
616
|
declare const loadClientWasm: ClientWasmLoader;
|
|
631
617
|
|
|
632
618
|
/**
|
|
@@ -1317,24 +1303,33 @@ interface SendResult {
|
|
|
1317
1303
|
* signing quorum roster (`roles` in `participants` order) and the WHOLE `message` bytes (ed25519 signs
|
|
1318
1304
|
* the message, not a digest).
|
|
1319
1305
|
*/
|
|
1320
|
-
interface
|
|
1306
|
+
interface SendSessionResponse {
|
|
1321
1307
|
/** The pending signing-activity row id — echoed back to the ASSEMBLE phase to finalize the signed tx. */
|
|
1322
1308
|
txId: string;
|
|
1323
1309
|
/** Relay websocket URL the device connects to. */
|
|
1324
1310
|
relayUrl: string;
|
|
1325
1311
|
/** Relay session id shared by the device + server parties. */
|
|
1326
1312
|
sessionId: string;
|
|
1327
|
-
/** The
|
|
1313
|
+
/** The wallet's curve — it decides which payload field below is populated and how `participants` is numbered. */
|
|
1314
|
+
curve: WalletCurve;
|
|
1315
|
+
/** The signing quorum's relay roles, in `participants` order (`roles[i]` ↔ `participants[i]`), e.g. `['device','server']`. */
|
|
1328
1316
|
roles: string[];
|
|
1329
|
-
/**
|
|
1317
|
+
/**
|
|
1318
|
+
* The quorum's party identifiers, parallel to {@link roles} — **numbered per curve**: ed25519
|
|
1319
|
+
* (FROST) uses 1-based identifiers (`[1, 2]`), secp256k1 (cggmp24) 0-based keygen indices (`[0, 1]`).
|
|
1320
|
+
*/
|
|
1330
1321
|
participants: number[];
|
|
1331
1322
|
/** This device's own 0-based slot into {@link roles} (its position in the quorum). */
|
|
1332
1323
|
signerPosition: number;
|
|
1333
|
-
/** The raw message bytes to sign, hex
|
|
1334
|
-
message
|
|
1324
|
+
/** The raw message bytes to sign, hex — **ed25519 only** (FROST signs the message, not a hash). */
|
|
1325
|
+
message?: string;
|
|
1326
|
+
/** The 32-byte tx digest to sign, hex — **secp256k1 only** (cggmp24 signs a digest). */
|
|
1327
|
+
digest?: string;
|
|
1335
1328
|
/** Short-lived relay token (JWT) the device presents to join this session; present only when relay auth is enabled. */
|
|
1336
1329
|
relayToken?: string;
|
|
1337
1330
|
}
|
|
1331
|
+
/** @deprecated Use {@link SendSessionResponse} — the shape is curve-agnostic (secp256k1 + ed25519). */
|
|
1332
|
+
type EddsaSendSession = SendSessionResponse;
|
|
1338
1333
|
/** Body of the ASSEMBLE phase (`POST …/send-session/:txId/assemble`) — the aggregated ed25519 signature the backend embeds into the wire tx (backend `AssembleEddsaTxRequest`). */
|
|
1339
1334
|
interface EddsaAssembleRequest {
|
|
1340
1335
|
/** The 64-byte RFC 8032 ed25519 signature (hex) the device + server co-produced. */
|
|
@@ -1542,33 +1537,37 @@ interface OnrampWidgetUrl {
|
|
|
1542
1537
|
/** The recovery factors a wallet enrols (backend `RecoveryFactor`). */
|
|
1543
1538
|
type RecoveryFactor = 'recovery_code' | 'totp' | 'email_otp';
|
|
1544
1539
|
/**
|
|
1545
|
-
* Enrolment of one factor at register time (backend `
|
|
1540
|
+
* Enrolment of one factor at register time (backend `FactorEnrollmentDto`).
|
|
1541
|
+
*
|
|
1542
|
+
* The wire field is `credential` for EVERY factor — the API's DTO declares exactly
|
|
1543
|
+
* `{ type, credential }` and its validation pipe rejects any other property.
|
|
1546
1544
|
*
|
|
1547
1545
|
* Contract A: the recovery code is a client-side **sealing secret** and must never
|
|
1548
|
-
* reach the server, so
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1546
|
+
* reach the server, so for `recovery_code` the `credential` carries the lowercase-hex
|
|
1547
|
+
* SHA-256 of the code, never the code itself (the SDK hashes it in
|
|
1548
|
+
* {@link buildRecoveryRegistration}). `totp` / `email_otp` enrol their non-sealing
|
|
1549
|
+
* credential (base32 secret / email address) verbatim.
|
|
1551
1550
|
*/
|
|
1552
1551
|
interface FactorEnrollment {
|
|
1553
1552
|
type: RecoveryFactor;
|
|
1554
|
-
/** totp → base32 secret; email_otp → email address.
|
|
1555
|
-
credential
|
|
1556
|
-
/** recovery_code → lowercase-hex SHA-256 of the recovery code (the raw code never leaves the client). */
|
|
1557
|
-
credentialHash?: string;
|
|
1553
|
+
/** recovery_code → lowercase-hex SHA-256 of the code; totp → base32 secret; email_otp → email address. */
|
|
1554
|
+
credential: string;
|
|
1558
1555
|
}
|
|
1559
1556
|
/**
|
|
1560
|
-
* One factor's proof at recovery time (backend `
|
|
1557
|
+
* One factor's proof at recovery time (backend `FactorVerificationDto`).
|
|
1561
1558
|
*
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1559
|
+
* The wire field is `token` for EVERY factor — the API's DTO declares exactly
|
|
1560
|
+
* `{ type, token }` and its validation pipe rejects any other property.
|
|
1561
|
+
*
|
|
1562
|
+
* Contract A: a `recovery_code` proof is sent as the same lowercase-hex SHA-256 the
|
|
1563
|
+
* server stored, never the plaintext code — the server compares the hash in constant
|
|
1564
|
+
* time and can never derive the code to unseal the ciphertext. Callers pass the
|
|
1565
|
+
* PLAINTEXT code here; the SDK hashes it before the request leaves the device.
|
|
1565
1566
|
*/
|
|
1566
1567
|
interface FactorVerification {
|
|
1567
1568
|
type: RecoveryFactor;
|
|
1568
|
-
/** totp → current 6-digit OTP; email_otp → the emailed OTP.
|
|
1569
|
-
token
|
|
1570
|
-
/** recovery_code → lowercase-hex SHA-256 of the code (matches the enrolled hash). */
|
|
1571
|
-
credentialHash?: string;
|
|
1569
|
+
/** recovery_code → the plaintext code (the SDK sends its SHA-256); totp → current 6-digit OTP; email_otp → the emailed OTP. */
|
|
1570
|
+
token: string;
|
|
1572
1571
|
}
|
|
1573
1572
|
/** A registered recovery record's metadata (backend `RecoveryShareResponse`). */
|
|
1574
1573
|
interface RecoveryShareInfo {
|
|
@@ -2106,14 +2105,35 @@ declare class Wallet {
|
|
|
2106
2105
|
*/
|
|
2107
2106
|
sign(digest: string, options?: SignOptions): Promise<string>;
|
|
2108
2107
|
/**
|
|
2109
|
-
*
|
|
2110
|
-
*
|
|
2111
|
-
*
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2108
|
+
* The roles that will actually sign — the first `threshold` of the wallet's party list (#292),
|
|
2109
|
+
* mirroring the signer's own derivation. `undefined` when the topology is unknown (a legacy
|
|
2110
|
+
* wallet record), which callers treat as "let the server decide".
|
|
2111
|
+
*/
|
|
2112
|
+
private signQuorum;
|
|
2113
|
+
/**
|
|
2114
|
+
* Everything this device needs to join a ceremony, resolved BEFORE one is started (#89): the MPC
|
|
2115
|
+
* core, the routed-sign capability a >2-party quorum needs, and the stored key share.
|
|
2116
|
+
*
|
|
2117
|
+
* Each failure is a typed error thrown straight to the caller, and the ordering is the point: a
|
|
2118
|
+
* device that cannot co-sign must never leave the platform party waiting on the relay for a
|
|
2119
|
+
* counterpart that will never arrive. That wait ends at the party-runner timeout (~210s) and
|
|
2120
|
+
* reaches the caller as an opaque 5xx — minutes after a knowable, local cause.
|
|
2121
|
+
*/
|
|
2122
|
+
private loadDeviceParty;
|
|
2123
|
+
/**
|
|
2124
|
+
* Normalize a sign-session descriptor into the shared {@link DeviceCeremony}. The 2-party roster
|
|
2125
|
+
* comes from the descriptor's own `role`/`peerRole` (authoritative — the relay token is bound to
|
|
2126
|
+
* that role); a larger quorum carries no server-sent roster, so the wallet's own party slice — the
|
|
2127
|
+
* same slice the signer derives — supplies it.
|
|
2115
2128
|
*/
|
|
2116
|
-
private
|
|
2129
|
+
private toSignCeremony;
|
|
2130
|
+
/**
|
|
2131
|
+
* Run this device's half of a started ceremony. A 2-party quorum uses the plain single-peer
|
|
2132
|
+
* transport; anything larger MUST be roster-routed, or the transport attributes every inbound
|
|
2133
|
+
* message to the one configured peer and the protocol aborts on the third party's first message
|
|
2134
|
+
* (waas-core#131).
|
|
2135
|
+
*/
|
|
2136
|
+
private runDeviceSign;
|
|
2117
2137
|
/**
|
|
2118
2138
|
* Send a transaction from this wallet. The platform builds the chain-specific transaction
|
|
2119
2139
|
* and co-signs it with the 2-of-3 MPC quorum, returning the **signed raw transaction**.
|
|
@@ -2129,13 +2149,34 @@ declare class Wallet {
|
|
|
2129
2149
|
* server-side). Alternatively supply a pre-built assertion via `options.passkeyAssertion`.
|
|
2130
2150
|
*/
|
|
2131
2151
|
send(params: SendParams, options?: SendOptions): Promise<SendResult>;
|
|
2152
|
+
/**
|
|
2153
|
+
* Device-co-signed send for a secp wallet — the cggmp24 counterpart of {@link sendEd25519}:
|
|
2154
|
+
*
|
|
2155
|
+
* 1. **START** (`POST …/send-session`): the platform runs the transfer gates, builds the unsigned
|
|
2156
|
+
* tx, puts its own party on the relay in the background, and returns the 32-byte digest plus
|
|
2157
|
+
* the relay coordination. It does NOT wait for the ceremony.
|
|
2158
|
+
* 2. **CO-SIGN**: this device runs its half over the relay with its stored share; cggmp24 hands
|
|
2159
|
+
* the completed signature to both parties.
|
|
2160
|
+
* 3. **ASSEMBLE** (`POST …/send-session/:txId/assemble`): the platform verifies the signature
|
|
2161
|
+
* (recovers to the wallet key AND equals its own party's) and embeds it into the wire tx.
|
|
2162
|
+
*
|
|
2163
|
+
* Every device-side precondition is resolved BEFORE the START (see {@link loadDeviceParty}), so a
|
|
2164
|
+
* device that cannot co-sign costs nothing: no tx is built and no platform party is left waiting.
|
|
2165
|
+
*
|
|
2166
|
+
* **A co-sign that fails after a successful START rejects with its typed error.** START is not the
|
|
2167
|
+
* commit point — it yields an *unsigned* tx and a pending session, and nothing broadcastable exists
|
|
2168
|
+
* until ASSEMBLE returns a `signedTx` — so there is no result to salvage by swallowing the failure,
|
|
2169
|
+
* and no fallback to `POST /send` (the platform cannot reach the threshold on this wallet alone, so
|
|
2170
|
+
* a retry there would only hang). The backend expires the abandoned session and fails the tx row.
|
|
2171
|
+
*/
|
|
2172
|
+
private sendWithDevice;
|
|
2132
2173
|
/**
|
|
2133
2174
|
* Device-co-signed send for an ed25519 (FROST) wallet (#110) — the browser holds the device FROST
|
|
2134
2175
|
* share and co-signs 2-party with the backend `server` party over the relay:
|
|
2135
2176
|
*
|
|
2136
2177
|
* 1. **START** (`POST …/send-session`): the backend builds the unsigned tx (chain adapter), starts
|
|
2137
2178
|
* its server FROST party on the relay in the background, and returns the raw `message` bytes to
|
|
2138
|
-
* sign + the relay coordination ({@link
|
|
2179
|
+
* sign + the relay coordination ({@link SendSessionResponse}).
|
|
2139
2180
|
* 2. **CO-SIGN**: the device runs `signEddsa` over the relay with its stored `{keyPackage,
|
|
2140
2181
|
* publicKeyPackage}` share; the two parties aggregate the RFC 8032 signature (returned locally).
|
|
2141
2182
|
* 3. **ASSEMBLE** (`POST …/send-session/:txId/assemble`): the backend embeds the aggregated
|
package/dist/index.d.ts
CHANGED
|
@@ -245,7 +245,11 @@ interface DeviceSignParams extends CeremonyParams {
|
|
|
245
245
|
}
|
|
246
246
|
/** Result of the device half of sign. */
|
|
247
247
|
interface DeviceSignResult {
|
|
248
|
-
/**
|
|
248
|
+
/**
|
|
249
|
+
* The signature as LOW-S–normalized compact `r ‖ s` hex (128 chars, no `0x`) — byte-identical to
|
|
250
|
+
* what the platform's own party produces, so the backend can cross-check the two before embedding
|
|
251
|
+
* one into a transaction, and chain adapters can slice `r`/`s` by offset.
|
|
252
|
+
*/
|
|
249
253
|
signature: string;
|
|
250
254
|
}
|
|
251
255
|
/**
|
|
@@ -570,7 +574,7 @@ declare class WasmMpcCore implements MpcCore {
|
|
|
570
574
|
* exact, optional peer dependency in `package.json` — so an app that installs `@waaskey/client-wasm`
|
|
571
575
|
* alongside the SDK runs the real ed25519 create/send path end to end.
|
|
572
576
|
*/
|
|
573
|
-
declare const CLIENT_WASM_VERSION = "0.2.
|
|
577
|
+
declare const CLIENT_WASM_VERSION = "0.2.2";
|
|
574
578
|
/**
|
|
575
579
|
* Verify the integrity of raw wasm bytes against an expected **SHA-384** hash, in
|
|
576
580
|
* Subresource-Integrity (`sha384-<base64>`) form (issue #40).
|
|
@@ -609,24 +613,6 @@ interface VerifiedWasmLoaderOptions {
|
|
|
609
613
|
* ```
|
|
610
614
|
*/
|
|
611
615
|
declare function createVerifiedClientWasmLoader(options: VerifiedWasmLoaderOptions): ClientWasmLoader;
|
|
612
|
-
/**
|
|
613
|
-
* Convenience loader for the Waaskey client-wasm engine — dynamically imports `@waaskey/client-wasm`
|
|
614
|
-
* (the wasm-pack/bundler build, auto-initialized on import) and returns it as a {@link ClientWasmModule}:
|
|
615
|
-
*
|
|
616
|
-
* ```ts
|
|
617
|
-
* import { Waaskey, WasmMpcCore, loadClientWasm } from '@waaskey/sdk';
|
|
618
|
-
* const waaskey = new Waaskey({ apiKey, mpc: new WasmMpcCore(loadClientWasm), shareStore });
|
|
619
|
-
* ```
|
|
620
|
-
*
|
|
621
|
-
* ⚠️ **No integrity verification (issue #40).** A bundler-target build instantiates the wasm on
|
|
622
|
-
* import, so its bytes cannot be checked first. For production — where a registry compromise or
|
|
623
|
-
* dependency-confusion attack on `@waaskey/client-wasm` could exfiltrate the plaintext device share
|
|
624
|
-
* — use {@link createVerifiedClientWasmLoader} with a vendored `.wasm` and the pinned SHA-384 instead.
|
|
625
|
-
*
|
|
626
|
-
* `@waaskey/client-wasm` is an OPTIONAL peer dependency pinned to {@link CLIENT_WASM_VERSION}: install
|
|
627
|
-
* it alongside the SDK in apps that create wallets. The indirect import specifier keeps it out of
|
|
628
|
-
* static module resolution — so a missing install fails here with an actionable message, not at build.
|
|
629
|
-
*/
|
|
630
616
|
declare const loadClientWasm: ClientWasmLoader;
|
|
631
617
|
|
|
632
618
|
/**
|
|
@@ -1317,24 +1303,33 @@ interface SendResult {
|
|
|
1317
1303
|
* signing quorum roster (`roles` in `participants` order) and the WHOLE `message` bytes (ed25519 signs
|
|
1318
1304
|
* the message, not a digest).
|
|
1319
1305
|
*/
|
|
1320
|
-
interface
|
|
1306
|
+
interface SendSessionResponse {
|
|
1321
1307
|
/** The pending signing-activity row id — echoed back to the ASSEMBLE phase to finalize the signed tx. */
|
|
1322
1308
|
txId: string;
|
|
1323
1309
|
/** Relay websocket URL the device connects to. */
|
|
1324
1310
|
relayUrl: string;
|
|
1325
1311
|
/** Relay session id shared by the device + server parties. */
|
|
1326
1312
|
sessionId: string;
|
|
1327
|
-
/** The
|
|
1313
|
+
/** The wallet's curve — it decides which payload field below is populated and how `participants` is numbered. */
|
|
1314
|
+
curve: WalletCurve;
|
|
1315
|
+
/** The signing quorum's relay roles, in `participants` order (`roles[i]` ↔ `participants[i]`), e.g. `['device','server']`. */
|
|
1328
1316
|
roles: string[];
|
|
1329
|
-
/**
|
|
1317
|
+
/**
|
|
1318
|
+
* The quorum's party identifiers, parallel to {@link roles} — **numbered per curve**: ed25519
|
|
1319
|
+
* (FROST) uses 1-based identifiers (`[1, 2]`), secp256k1 (cggmp24) 0-based keygen indices (`[0, 1]`).
|
|
1320
|
+
*/
|
|
1330
1321
|
participants: number[];
|
|
1331
1322
|
/** This device's own 0-based slot into {@link roles} (its position in the quorum). */
|
|
1332
1323
|
signerPosition: number;
|
|
1333
|
-
/** The raw message bytes to sign, hex
|
|
1334
|
-
message
|
|
1324
|
+
/** The raw message bytes to sign, hex — **ed25519 only** (FROST signs the message, not a hash). */
|
|
1325
|
+
message?: string;
|
|
1326
|
+
/** The 32-byte tx digest to sign, hex — **secp256k1 only** (cggmp24 signs a digest). */
|
|
1327
|
+
digest?: string;
|
|
1335
1328
|
/** Short-lived relay token (JWT) the device presents to join this session; present only when relay auth is enabled. */
|
|
1336
1329
|
relayToken?: string;
|
|
1337
1330
|
}
|
|
1331
|
+
/** @deprecated Use {@link SendSessionResponse} — the shape is curve-agnostic (secp256k1 + ed25519). */
|
|
1332
|
+
type EddsaSendSession = SendSessionResponse;
|
|
1338
1333
|
/** Body of the ASSEMBLE phase (`POST …/send-session/:txId/assemble`) — the aggregated ed25519 signature the backend embeds into the wire tx (backend `AssembleEddsaTxRequest`). */
|
|
1339
1334
|
interface EddsaAssembleRequest {
|
|
1340
1335
|
/** The 64-byte RFC 8032 ed25519 signature (hex) the device + server co-produced. */
|
|
@@ -1542,33 +1537,37 @@ interface OnrampWidgetUrl {
|
|
|
1542
1537
|
/** The recovery factors a wallet enrols (backend `RecoveryFactor`). */
|
|
1543
1538
|
type RecoveryFactor = 'recovery_code' | 'totp' | 'email_otp';
|
|
1544
1539
|
/**
|
|
1545
|
-
* Enrolment of one factor at register time (backend `
|
|
1540
|
+
* Enrolment of one factor at register time (backend `FactorEnrollmentDto`).
|
|
1541
|
+
*
|
|
1542
|
+
* The wire field is `credential` for EVERY factor — the API's DTO declares exactly
|
|
1543
|
+
* `{ type, credential }` and its validation pipe rejects any other property.
|
|
1546
1544
|
*
|
|
1547
1545
|
* Contract A: the recovery code is a client-side **sealing secret** and must never
|
|
1548
|
-
* reach the server, so
|
|
1549
|
-
*
|
|
1550
|
-
*
|
|
1546
|
+
* reach the server, so for `recovery_code` the `credential` carries the lowercase-hex
|
|
1547
|
+
* SHA-256 of the code, never the code itself (the SDK hashes it in
|
|
1548
|
+
* {@link buildRecoveryRegistration}). `totp` / `email_otp` enrol their non-sealing
|
|
1549
|
+
* credential (base32 secret / email address) verbatim.
|
|
1551
1550
|
*/
|
|
1552
1551
|
interface FactorEnrollment {
|
|
1553
1552
|
type: RecoveryFactor;
|
|
1554
|
-
/** totp → base32 secret; email_otp → email address.
|
|
1555
|
-
credential
|
|
1556
|
-
/** recovery_code → lowercase-hex SHA-256 of the recovery code (the raw code never leaves the client). */
|
|
1557
|
-
credentialHash?: string;
|
|
1553
|
+
/** recovery_code → lowercase-hex SHA-256 of the code; totp → base32 secret; email_otp → email address. */
|
|
1554
|
+
credential: string;
|
|
1558
1555
|
}
|
|
1559
1556
|
/**
|
|
1560
|
-
* One factor's proof at recovery time (backend `
|
|
1557
|
+
* One factor's proof at recovery time (backend `FactorVerificationDto`).
|
|
1561
1558
|
*
|
|
1562
|
-
*
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
1559
|
+
* The wire field is `token` for EVERY factor — the API's DTO declares exactly
|
|
1560
|
+
* `{ type, token }` and its validation pipe rejects any other property.
|
|
1561
|
+
*
|
|
1562
|
+
* Contract A: a `recovery_code` proof is sent as the same lowercase-hex SHA-256 the
|
|
1563
|
+
* server stored, never the plaintext code — the server compares the hash in constant
|
|
1564
|
+
* time and can never derive the code to unseal the ciphertext. Callers pass the
|
|
1565
|
+
* PLAINTEXT code here; the SDK hashes it before the request leaves the device.
|
|
1565
1566
|
*/
|
|
1566
1567
|
interface FactorVerification {
|
|
1567
1568
|
type: RecoveryFactor;
|
|
1568
|
-
/** totp → current 6-digit OTP; email_otp → the emailed OTP.
|
|
1569
|
-
token
|
|
1570
|
-
/** recovery_code → lowercase-hex SHA-256 of the code (matches the enrolled hash). */
|
|
1571
|
-
credentialHash?: string;
|
|
1569
|
+
/** recovery_code → the plaintext code (the SDK sends its SHA-256); totp → current 6-digit OTP; email_otp → the emailed OTP. */
|
|
1570
|
+
token: string;
|
|
1572
1571
|
}
|
|
1573
1572
|
/** A registered recovery record's metadata (backend `RecoveryShareResponse`). */
|
|
1574
1573
|
interface RecoveryShareInfo {
|
|
@@ -2106,14 +2105,35 @@ declare class Wallet {
|
|
|
2106
2105
|
*/
|
|
2107
2106
|
sign(digest: string, options?: SignOptions): Promise<string>;
|
|
2108
2107
|
/**
|
|
2109
|
-
*
|
|
2110
|
-
*
|
|
2111
|
-
*
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2108
|
+
* The roles that will actually sign — the first `threshold` of the wallet's party list (#292),
|
|
2109
|
+
* mirroring the signer's own derivation. `undefined` when the topology is unknown (a legacy
|
|
2110
|
+
* wallet record), which callers treat as "let the server decide".
|
|
2111
|
+
*/
|
|
2112
|
+
private signQuorum;
|
|
2113
|
+
/**
|
|
2114
|
+
* Everything this device needs to join a ceremony, resolved BEFORE one is started (#89): the MPC
|
|
2115
|
+
* core, the routed-sign capability a >2-party quorum needs, and the stored key share.
|
|
2116
|
+
*
|
|
2117
|
+
* Each failure is a typed error thrown straight to the caller, and the ordering is the point: a
|
|
2118
|
+
* device that cannot co-sign must never leave the platform party waiting on the relay for a
|
|
2119
|
+
* counterpart that will never arrive. That wait ends at the party-runner timeout (~210s) and
|
|
2120
|
+
* reaches the caller as an opaque 5xx — minutes after a knowable, local cause.
|
|
2121
|
+
*/
|
|
2122
|
+
private loadDeviceParty;
|
|
2123
|
+
/**
|
|
2124
|
+
* Normalize a sign-session descriptor into the shared {@link DeviceCeremony}. The 2-party roster
|
|
2125
|
+
* comes from the descriptor's own `role`/`peerRole` (authoritative — the relay token is bound to
|
|
2126
|
+
* that role); a larger quorum carries no server-sent roster, so the wallet's own party slice — the
|
|
2127
|
+
* same slice the signer derives — supplies it.
|
|
2115
2128
|
*/
|
|
2116
|
-
private
|
|
2129
|
+
private toSignCeremony;
|
|
2130
|
+
/**
|
|
2131
|
+
* Run this device's half of a started ceremony. A 2-party quorum uses the plain single-peer
|
|
2132
|
+
* transport; anything larger MUST be roster-routed, or the transport attributes every inbound
|
|
2133
|
+
* message to the one configured peer and the protocol aborts on the third party's first message
|
|
2134
|
+
* (waas-core#131).
|
|
2135
|
+
*/
|
|
2136
|
+
private runDeviceSign;
|
|
2117
2137
|
/**
|
|
2118
2138
|
* Send a transaction from this wallet. The platform builds the chain-specific transaction
|
|
2119
2139
|
* and co-signs it with the 2-of-3 MPC quorum, returning the **signed raw transaction**.
|
|
@@ -2129,13 +2149,34 @@ declare class Wallet {
|
|
|
2129
2149
|
* server-side). Alternatively supply a pre-built assertion via `options.passkeyAssertion`.
|
|
2130
2150
|
*/
|
|
2131
2151
|
send(params: SendParams, options?: SendOptions): Promise<SendResult>;
|
|
2152
|
+
/**
|
|
2153
|
+
* Device-co-signed send for a secp wallet — the cggmp24 counterpart of {@link sendEd25519}:
|
|
2154
|
+
*
|
|
2155
|
+
* 1. **START** (`POST …/send-session`): the platform runs the transfer gates, builds the unsigned
|
|
2156
|
+
* tx, puts its own party on the relay in the background, and returns the 32-byte digest plus
|
|
2157
|
+
* the relay coordination. It does NOT wait for the ceremony.
|
|
2158
|
+
* 2. **CO-SIGN**: this device runs its half over the relay with its stored share; cggmp24 hands
|
|
2159
|
+
* the completed signature to both parties.
|
|
2160
|
+
* 3. **ASSEMBLE** (`POST …/send-session/:txId/assemble`): the platform verifies the signature
|
|
2161
|
+
* (recovers to the wallet key AND equals its own party's) and embeds it into the wire tx.
|
|
2162
|
+
*
|
|
2163
|
+
* Every device-side precondition is resolved BEFORE the START (see {@link loadDeviceParty}), so a
|
|
2164
|
+
* device that cannot co-sign costs nothing: no tx is built and no platform party is left waiting.
|
|
2165
|
+
*
|
|
2166
|
+
* **A co-sign that fails after a successful START rejects with its typed error.** START is not the
|
|
2167
|
+
* commit point — it yields an *unsigned* tx and a pending session, and nothing broadcastable exists
|
|
2168
|
+
* until ASSEMBLE returns a `signedTx` — so there is no result to salvage by swallowing the failure,
|
|
2169
|
+
* and no fallback to `POST /send` (the platform cannot reach the threshold on this wallet alone, so
|
|
2170
|
+
* a retry there would only hang). The backend expires the abandoned session and fails the tx row.
|
|
2171
|
+
*/
|
|
2172
|
+
private sendWithDevice;
|
|
2132
2173
|
/**
|
|
2133
2174
|
* Device-co-signed send for an ed25519 (FROST) wallet (#110) — the browser holds the device FROST
|
|
2134
2175
|
* share and co-signs 2-party with the backend `server` party over the relay:
|
|
2135
2176
|
*
|
|
2136
2177
|
* 1. **START** (`POST …/send-session`): the backend builds the unsigned tx (chain adapter), starts
|
|
2137
2178
|
* its server FROST party on the relay in the background, and returns the raw `message` bytes to
|
|
2138
|
-
* sign + the relay coordination ({@link
|
|
2179
|
+
* sign + the relay coordination ({@link SendSessionResponse}).
|
|
2139
2180
|
* 2. **CO-SIGN**: the device runs `signEddsa` over the relay with its stored `{keyPackage,
|
|
2140
2181
|
* publicKeyPackage}` share; the two parties aggregate the RFC 8032 signature (returned locally).
|
|
2141
2182
|
* 3. **ASSEMBLE** (`POST …/send-session/:txId/assemble`): the backend embeds the aggregated
|