@soropass/core 0.1.2 → 0.2.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 +93 -0
- package/dist/{checkAuth-C2AcXqFe.d.cts → checkAuth-B5ObsEsP.d.cts} +11 -7
- package/dist/{checkAuth-C2AcXqFe.d.ts → checkAuth-B5ObsEsP.d.ts} +11 -7
- package/dist/{chunk-AHFGTIBY.js → chunk-2KLSTE4E.js} +16 -4
- package/dist/chunk-2KLSTE4E.js.map +1 -0
- package/dist/{chunk-KDBIRRF7.js → chunk-42ETVY6V.js} +3 -3
- package/dist/{chunk-KDBIRRF7.js.map → chunk-42ETVY6V.js.map} +1 -1
- package/dist/{chunk-ZQRFIL4P.js → chunk-ODXQCLZL.js} +18 -6
- package/dist/chunk-ODXQCLZL.js.map +1 -0
- package/dist/{chunk-MMB4ZIW3.js → chunk-OHKPNVB3.js} +13 -5
- package/dist/chunk-OHKPNVB3.js.map +1 -0
- package/dist/{chunk-BLFCRWCT.js → chunk-QREICNIW.js} +3 -3
- package/dist/{chunk-BLFCRWCT.js.map → chunk-QREICNIW.js.map} +1 -1
- package/dist/{chunk-W75OQKHR.js → chunk-RFMHMWTY.js} +5 -3
- package/dist/chunk-RFMHMWTY.js.map +1 -0
- package/dist/{chunk-FIRTUNWF.js → chunk-XTLWH3VQ.js} +26 -5
- package/dist/chunk-XTLWH3VQ.js.map +1 -0
- package/dist/connect.cjs +14 -2
- package/dist/connect.cjs.map +1 -1
- package/dist/connect.d.cts +11 -1
- package/dist/connect.d.ts +11 -1
- package/dist/connect.js +1 -1
- package/dist/create.cjs +14 -4
- package/dist/create.cjs.map +1 -1
- package/dist/create.d.cts +20 -12
- package/dist/create.d.ts +20 -12
- package/dist/create.js +2 -2
- package/dist/index.cjs +77 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +20 -11
- package/dist/index.js.map +1 -1
- package/dist/{recover-0wbOXDjR.d.cts → recover-BOq0DQaU.d.cts} +1 -1
- package/dist/{recover-COnsxhYk.d.ts → recover-CL_jZ2kN.d.ts} +1 -1
- package/dist/recover.cjs +24 -3
- package/dist/recover.cjs.map +1 -1
- package/dist/recover.d.cts +4 -4
- package/dist/recover.d.ts +4 -4
- package/dist/recover.js +3 -3
- package/dist/{sign-B4JV2Inn.d.ts → sign-O5d7Moma.d.cts} +15 -0
- package/dist/{sign-B4JV2Inn.d.cts → sign-O5d7Moma.d.ts} +15 -0
- package/dist/sign.cjs +39 -6
- package/dist/sign.cjs.map +1 -1
- package/dist/sign.d.cts +27 -14
- package/dist/sign.d.ts +27 -14
- package/dist/sign.js +3 -3
- package/dist/{submit-BsKXOCkL.d.cts → submit-BKogb6Y8.d.cts} +2 -2
- package/dist/{submit-CZt8fNRf.d.ts → submit-CKJp7XgZ.d.ts} +2 -2
- package/dist/testing.cjs +46 -8
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +8 -5
- package/dist/testing.d.ts +8 -5
- package/dist/testing.js +8 -5
- package/dist/testing.js.map +1 -1
- package/dist/{types-C3VIw5Iu.d.ts → types-BJsL-q2B.d.ts} +3 -3
- package/dist/{types-DLcXrud8.d.cts → types-B_pVAg-T.d.cts} +3 -3
- package/dist/{types--WNl-Sda.d.cts → types-CJluKV7s.d.cts} +8 -0
- package/dist/{types--WNl-Sda.d.ts → types-CJluKV7s.d.ts} +8 -0
- package/dist/types.d.cts +4 -4
- package/dist/types.d.ts +4 -4
- package/package.json +23 -2
- package/dist/chunk-AHFGTIBY.js.map +0 -1
- package/dist/chunk-FIRTUNWF.js.map +0 -1
- package/dist/chunk-MMB4ZIW3.js.map +0 -1
- package/dist/chunk-W75OQKHR.js.map +0 -1
- package/dist/chunk-ZQRFIL4P.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# @soropass/core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@soropass/core)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
6
|
+
Minimal, headless, ES256-only passkey SDK for Stellar smart accounts. It turns a WebAuthn passkey (Face ID, Touch ID, a security key) into the signer of a Soroban smart account, and handles the parts that are easy to get wrong: ES256 enforcement, DER-to-compact low-S conversion, the `__check_auth` authorization-entry wire shape, deterministic C-address derivation, and multi-device recovery.
|
|
7
|
+
|
|
8
|
+
It ships no UI and no framework opinions, and keeps `@stellar/stellar-sdk` as a peer dependency so it is never bundled twice. For drop-in create/sign/recover screens, see [`@soropass/ui`](https://www.npmjs.com/package/@soropass/ui). To register a passkey wallet in [Stellar Wallets Kit](https://github.com/Creit-Tech/Stellar-Wallets-Kit), see the [integration guide](https://docs.soropass.dev/docs/wallets-kit).
|
|
9
|
+
|
|
10
|
+
## Why a passkey needs a smart account
|
|
11
|
+
|
|
12
|
+
A passkey signs with secp256r1 (ES256); a classic Stellar account (`G...`) only verifies Ed25519. So a passkey controls a Soroban **smart-account contract** (`C...`) whose `__check_auth` verifies the WebAuthn assertion on-chain via the host `secp256r1_verify`. This SDK owns that `C` side: create, sign, recover, add a signer.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @soropass/core "@stellar/stellar-sdk@>=12 <17"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`@stellar/stellar-sdk` is a required peer dependency. Pin it to `>=12 <17`: version 17 reworked the XDR API this release builds against. `@soropass/core` publishes ESM, CommonJS, and type declarations.
|
|
21
|
+
|
|
22
|
+
## Quick start (no browser)
|
|
23
|
+
|
|
24
|
+
Reproduce a create + sign in Node or CI with the built-in mock authenticator, no WebAuthn hardware and no network. Save as `verify.mjs` and run `node verify.mjs`:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import { createPasskeyKit, sampleAuthEntry } from '@soropass/core/testing';
|
|
28
|
+
import { referenceCheckAuth } from '@soropass/core';
|
|
29
|
+
import { xdr } from '@stellar/stellar-sdk';
|
|
30
|
+
|
|
31
|
+
const NETWORK = 'Test SDF Network ; September 2015';
|
|
32
|
+
|
|
33
|
+
// Create a passkey and deploy its smart account (in-memory).
|
|
34
|
+
const kit = createPasskeyKit({ mode: 'mock', rpId: 'example.com', rpName: 'Example' });
|
|
35
|
+
const account = await kit.createPasskey({ userName: 'alice' });
|
|
36
|
+
|
|
37
|
+
// Sign a demo auth entry, then verify it the way the on-chain __check_auth does.
|
|
38
|
+
const signed = xdr.SorobanAuthorizationEntry.fromXDR(
|
|
39
|
+
await kit.signAuthEntry(sampleAuthEntry(account.contractId)),
|
|
40
|
+
'base64',
|
|
41
|
+
);
|
|
42
|
+
console.log(referenceCheckAuth(signed, account.publicKey, NETWORK).success ? 'PASS' : 'FAIL');
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Create a real account in the browser
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { createPasskey, factoryDeployer } from '@soropass/core';
|
|
49
|
+
import { Networks } from '@stellar/stellar-sdk';
|
|
50
|
+
|
|
51
|
+
const account = await createPasskey({
|
|
52
|
+
rpId: location.hostname,
|
|
53
|
+
rpName: 'My Stellar App',
|
|
54
|
+
userName: 'alice',
|
|
55
|
+
deployer: factoryDeployer({
|
|
56
|
+
rpcUrl: 'https://soroban-testnet.stellar.org',
|
|
57
|
+
networkPassphrase: Networks.TESTNET,
|
|
58
|
+
factoryContractId: 'C...', // your AccountFactory
|
|
59
|
+
sourceSecret, // a funded account that pays the one-time deploy fee
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
// account.contractId · account.credentialId · account.publicKey
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Sign a transaction with `signTransaction` / `browserPasskeySigner`, resolve a returning user offline with `deriveAccountAddress`, and add a backup device with `registerPasskey` + `add_signer`. The full flows are in the [SDK reference](https://docs.soropass.dev/docs/sdk).
|
|
66
|
+
|
|
67
|
+
## What you get
|
|
68
|
+
|
|
69
|
+
- **ES256-only.** Registration pins `pubKeyCredParams` to alg `-7`; anything else throws `KitError('ES256_NOT_SUPPORTED')`.
|
|
70
|
+
- **Always low-S.** Roughly half of Apple passkeys emit high-S signatures; the SDK normalizes every signature to canonical low-S so it verifies on-chain.
|
|
71
|
+
- **Tree-shakeable subpaths.** Import only what you use; the heavy crypto is pulled only by `/create` and `/sign`.
|
|
72
|
+
- **Pluggable adapters** for submission (direct, Launchtube, OpenZeppelin Relayer) and indexing (on-chain events, Mercury).
|
|
73
|
+
- **One typed error taxonomy.** Every throw is a `KitError` with a code from a frozen 10-code set.
|
|
74
|
+
|
|
75
|
+
| Subpath | Contents |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `.` | Full public surface |
|
|
78
|
+
| `@soropass/core/create` | `createPasskey`, `registerPasskey`, `deriveAccountAddress`, public-key extraction |
|
|
79
|
+
| `@soropass/core/sign` | `signTransaction`, `signAuthEntry`, `browserPasskeySigner`, low-S helpers |
|
|
80
|
+
| `@soropass/core/connect` | `connect` |
|
|
81
|
+
| `@soropass/core/recover` | `recover`, `addSigner`, `removeSigner` |
|
|
82
|
+
| `@soropass/core/types` | `KitError`, `KIT_ERROR_CODES`, `isKitError` |
|
|
83
|
+
| `@soropass/core/testing` | `createPasskeyKit` mock mode, `sampleAuthEntry` (dev only) |
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
- Full docs and API reference: [docs.soropass.dev](https://docs.soropass.dev)
|
|
88
|
+
- Quickstart (three integration paths): [docs.soropass.dev/docs/quickstart](https://docs.soropass.dev/docs/quickstart)
|
|
89
|
+
- Compatibility matrix (device / browser / hardware): [docs.soropass.dev/docs/compatibility](https://docs.soropass.dev/docs/compatibility)
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
Apache-2.0
|
|
@@ -79,13 +79,17 @@ interface CheckAuthResult {
|
|
|
79
79
|
reason?: string;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* A faithful JavaScript model of the on-chain `__check_auth`
|
|
83
|
-
* `webauthn-
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* (
|
|
87
|
-
*
|
|
88
|
-
*
|
|
82
|
+
* A faithful JavaScript model of the on-chain v0.2 `__check_auth`
|
|
83
|
+
* (`contracts/webauthn-account`): read the inline `public_key`, require it to be
|
|
84
|
+
* an enrolled signer (here, equal to `publicKey`), reconstruct
|
|
85
|
+
* `SHA256(authenticator_data ‖ SHA256(client_data_json))`, run `secp256r1_verify`
|
|
86
|
+
* (which REQUIRES low-S and rejects high-S at decode, so the model verifies with
|
|
87
|
+
* lowS:true — the client-side normalization is what makes real assertions pass),
|
|
88
|
+
* and assert challenge-binding (`clientDataJSON.challenge ===
|
|
89
|
+
* base64url(signature_payload)`). Lets us prove the assembly without a live
|
|
90
|
+
* Soroban RPC; the real on-chain run is exercised by the kit integration and the
|
|
91
|
+
* demo. A 3-field struct with no inline key still verifies against `publicKey`
|
|
92
|
+
* for back-compat with the smart-wallet reference.
|
|
89
93
|
*/
|
|
90
94
|
declare function referenceCheckAuth(entry: xdr.SorobanAuthorizationEntry, publicKey: Uint8Array, networkPassphrase: string): CheckAuthResult;
|
|
91
95
|
interface SmartWalletCheckAuthResult {
|
|
@@ -79,13 +79,17 @@ interface CheckAuthResult {
|
|
|
79
79
|
reason?: string;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* A faithful JavaScript model of the on-chain `__check_auth`
|
|
83
|
-
* `webauthn-
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* (
|
|
87
|
-
*
|
|
88
|
-
*
|
|
82
|
+
* A faithful JavaScript model of the on-chain v0.2 `__check_auth`
|
|
83
|
+
* (`contracts/webauthn-account`): read the inline `public_key`, require it to be
|
|
84
|
+
* an enrolled signer (here, equal to `publicKey`), reconstruct
|
|
85
|
+
* `SHA256(authenticator_data ‖ SHA256(client_data_json))`, run `secp256r1_verify`
|
|
86
|
+
* (which REQUIRES low-S and rejects high-S at decode, so the model verifies with
|
|
87
|
+
* lowS:true — the client-side normalization is what makes real assertions pass),
|
|
88
|
+
* and assert challenge-binding (`clientDataJSON.challenge ===
|
|
89
|
+
* base64url(signature_payload)`). Lets us prove the assembly without a live
|
|
90
|
+
* Soroban RPC; the real on-chain run is exercised by the kit integration and the
|
|
91
|
+
* demo. A 3-field struct with no inline key still verifies against `publicKey`
|
|
92
|
+
* for back-compat with the smart-wallet reference.
|
|
89
93
|
*/
|
|
90
94
|
declare function referenceCheckAuth(entry: xdr.SorobanAuthorizationEntry, publicKey: Uint8Array, networkPassphrase: string): CheckAuthResult;
|
|
91
95
|
interface SmartWalletCheckAuthResult {
|
|
@@ -27,10 +27,22 @@ async function connect(options) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
const accounts = await options.indexer.resolveByCredential(credentialId);
|
|
30
|
-
const
|
|
31
|
-
return
|
|
30
|
+
const selected = selectAccount(accounts, options.publicKey);
|
|
31
|
+
return selected ? { contractId: selected.contractId, credentialId } : null;
|
|
32
|
+
}
|
|
33
|
+
function bytesEqual(a, b) {
|
|
34
|
+
if (a.length !== b.length) return false;
|
|
35
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
function selectAccount(accounts, publicKey) {
|
|
39
|
+
if (publicKey) {
|
|
40
|
+
const match = accounts.find((a) => a.publicKey && bytesEqual(a.publicKey, publicKey));
|
|
41
|
+
return match ? { contractId: match.contractId } : null;
|
|
42
|
+
}
|
|
43
|
+
return accounts[0] ?? null;
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
export { connect };
|
|
35
|
-
//# sourceMappingURL=chunk-
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
47
|
+
//# sourceMappingURL=chunk-2KLSTE4E.js.map
|
|
48
|
+
//# sourceMappingURL=chunk-2KLSTE4E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ceremonies/connect.ts"],"names":[],"mappings":";;;AA4BA,eAAe,yBAAA,GAA8C;AAC3D,EAAA,MAAM,MACJ,UAAA,CAGA,mBAAA;AACF,EAAA,IAAI,CAAC,GAAA,EAAK,+BAAA,EAAiC,OAAO,KAAA;AAClD,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,IAAI,+BAAA,EAAgC;AAAA,EACnD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AASA,eAAsB,QAAQ,OAAA,EAAwD;AACpF,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,IAAW,wBAAA,EAAyB;AAC5D,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA;AAC7C,EAAA,IAAI,CAAC,cAAc,OAAO,IAAA;AAE1B,EAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,wBAAA,IAA6B,MAAM,yBAAA,EAA0B;AAC7F,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,QAAA,IAAY,qBAAA,EAAsB;AAC3D,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,GAAA,CAAI;AAAA,QACjB,MAAM,OAAA,CAAQ,IAAA;AAAA,QACd,gBAAA,EAAkB,CAAC,YAAY,CAAA;AAAA,QAC/B,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,OAAA,CAAQ,oBAAoB,YAAY,CAAA;AACvE,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,QAAA,EAAU,OAAA,CAAQ,SAAS,CAAA;AAC1D,EAAA,OAAO,WAAW,EAAE,UAAA,EAAY,QAAA,CAAS,UAAA,EAAY,cAAa,GAAI,IAAA;AACxE;AAGA,SAAS,UAAA,CAAW,GAAe,CAAA,EAAwB;AACzD,EAAA,IAAI,CAAA,CAAE,MAAA,KAAW,CAAA,CAAE,MAAA,EAAQ,OAAO,KAAA;AAClC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,CAAE,MAAA,EAAQ,CAAA,EAAA,EAAK,IAAI,CAAA,CAAE,CAAC,CAAA,KAAM,CAAA,CAAE,CAAC,GAAG,OAAO,KAAA;AAC7D,EAAA,OAAO,IAAA;AACT;AAQA,SAAS,aAAA,CACP,UACA,SAAA,EAC+B;AAC/B,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAA,IAAa,UAAA,CAAW,CAAA,CAAE,SAAA,EAAW,SAAS,CAAC,CAAA;AACpF,IAAA,OAAO,KAAA,GAAQ,EAAE,UAAA,EAAY,KAAA,CAAM,YAAW,GAAI,IAAA;AAAA,EACpD;AACA,EAAA,OAAO,QAAA,CAAS,CAAC,CAAA,IAAK,IAAA;AACxB","file":"chunk-2KLSTE4E.js","sourcesContent":["import type { IndexerAdapter } from '../adapters/types';\nimport { browserWebAuthnClient, defaultCredentialStorage } from './browserClient';\nimport type { CredentialStorage, WebAuthnClient } from './types';\n\nexport interface ConnectOptions {\n rpId: string;\n indexer: IndexerAdapter;\n webauthn?: WebAuthnClient;\n storage?: CredentialStorage;\n /** Override silent-mediation detection (default: probe isConditionalMediationAvailable). */\n silentMediationSupported?: boolean;\n /**\n * SEC-1 (65-byte) public key of this passkey. When set, `connect` returns the\n * account whose factory `deployed` event names this exact key, ignoring any\n * other candidate for the same credential id. Because `deploy` is\n * permissionless, a credential id can resolve to more than one account; pass\n * the founding key (persisted at create time) so a poisoned candidate is never\n * selected. When omitted, `connect` returns the first resolved account and you\n * MUST verify enrollment yourself before trusting it with funds.\n */\n publicKey?: Uint8Array;\n}\n\nexport interface ConnectResult {\n contractId: string;\n credentialId: string;\n}\n\nasync function probeConditionalMediation(): Promise<boolean> {\n const pkc = (\n globalThis as {\n PublicKeyCredential?: { isConditionalMediationAvailable?: () => Promise<boolean> };\n }\n ).PublicKeyCredential;\n if (!pkc?.isConditionalMediationAvailable) return false;\n try {\n return await pkc.isConditionalMediationAvailable();\n } catch {\n return false;\n }\n}\n\n/**\n * `connect` — silent reconnect using the stored credential id. Where conditional\n * mediation is available, does a best-effort `mediation:'silent'` liveness check;\n * the C-address is resolved via the IndexerAdapter regardless, so connect\n * degrades gracefully where silent mediation is unsupported. Returns null when\n * there is no stored credential (the caller should `recover`) or no account.\n */\nexport async function connect(options: ConnectOptions): Promise<ConnectResult | null> {\n const storage = options.storage ?? defaultCredentialStorage();\n const credentialId = storage.get(options.rpId);\n if (!credentialId) return null;\n\n const silentSupported = options.silentMediationSupported ?? (await probeConditionalMediation());\n if (silentSupported) {\n const webauthn = options.webauthn ?? browserWebAuthnClient();\n try {\n await webauthn.get({\n rpId: options.rpId,\n allowCredentials: [credentialId],\n mediation: 'silent',\n });\n } catch {\n // A silent get may no-op or reject; resolution is via the indexer.\n }\n }\n\n const accounts = await options.indexer.resolveByCredential(credentialId);\n const selected = selectAccount(accounts, options.publicKey);\n return selected ? { contractId: selected.contractId, credentialId } : null;\n}\n\n/** True if two byte arrays are equal. */\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\n/**\n * Pick the resolved account. With an expected public key, return only the\n * candidate whose factory-reported founding key matches it (poison-resistant);\n * without one, fall back to the first candidate (see the `publicKey` doc on\n * ConnectOptions for why that is a trust decision the caller must back up).\n */\nfunction selectAccount(\n accounts: { contractId: string; publicKey?: Uint8Array }[],\n publicKey?: Uint8Array,\n): { contractId: string } | null {\n if (publicKey) {\n const match = accounts.find((a) => a.publicKey && bytesEqual(a.publicKey, publicKey));\n return match ? { contractId: match.contractId } : null;\n }\n return accounts[0] ?? null;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildAddSignerOperation, sendSmartWalletTx, buildRemoveSignerOperation } from './chunk-
|
|
1
|
+
import { buildAddSignerOperation, sendSmartWalletTx, buildRemoveSignerOperation } from './chunk-QREICNIW.js';
|
|
2
2
|
import { decodeChallenge } from './chunk-JNJ2FY45.js';
|
|
3
3
|
|
|
4
4
|
// src/ceremonies/addDevice.ts
|
|
@@ -28,5 +28,5 @@ async function removeSigner(options) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export { addSigner, removeSigner };
|
|
31
|
-
//# sourceMappingURL=chunk-
|
|
32
|
-
//# sourceMappingURL=chunk-
|
|
31
|
+
//# sourceMappingURL=chunk-42ETVY6V.js.map
|
|
32
|
+
//# sourceMappingURL=chunk-42ETVY6V.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ceremonies/addDevice.ts"],"names":[],"mappings":";;;;AA+CA,SAAS,kBAAkB,EAAA,EAAqC;AAC9D,EAAA,OAAO,OAAO,EAAA,KAAO,QAAA,GAAW,eAAA,CAAgB,EAAE,CAAA,GAAI,EAAA;AACxD;AAOA,eAAsB,UAAU,OAAA,EAAkD;AAChF,EAAA,MAAM,EAAE,gBAAA,EAAkB,SAAA,EAAW,GAAG,QAAO,GAAI,OAAA;AACnD,EAAA,MAAM,YAAY,uBAAA,CAAwB;AAAA,IACxC,gBAAA;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,YAAA,EAAc,iBAAA,CAAkB,SAAA,CAAU,YAAY,CAAA;AAAA,MACtD,WAAW,SAAA,CAAU,SAAA;AAAA,MACrB,YAAY,SAAA,CAAU,UAAA;AAAA,MACtB,SAAS,SAAA,CAAU;AAAA;AACrB,GACD,CAAA;AACD,EAAA,OAAO,iBAAA,CAAkB,EAAE,GAAG,MAAA,EAAQ,WAAW,CAAA;AACnD;AAGA,eAAsB,aAAa,OAAA,EAAqD;AACtF,EAAA,MAAM,EAAE,gBAAA,EAAkB,YAAA,EAAc,GAAG,QAAO,GAAI,OAAA;AACtD,EAAA,MAAM,YAAY,0BAAA,CAA2B;AAAA,IAC3C,gBAAA;AAAA,IACA,YAAA,EAAc,kBAAkB,YAAY;AAAA,GAC7C,CAAA;AACD,EAAA,OAAO,iBAAA,CAAkB,EAAE,GAAG,MAAA,EAAQ,WAAW,CAAA;AACnD","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/ceremonies/addDevice.ts"],"names":[],"mappings":";;;;AA+CA,SAAS,kBAAkB,EAAA,EAAqC;AAC9D,EAAA,OAAO,OAAO,EAAA,KAAO,QAAA,GAAW,eAAA,CAAgB,EAAE,CAAA,GAAI,EAAA;AACxD;AAOA,eAAsB,UAAU,OAAA,EAAkD;AAChF,EAAA,MAAM,EAAE,gBAAA,EAAkB,SAAA,EAAW,GAAG,QAAO,GAAI,OAAA;AACnD,EAAA,MAAM,YAAY,uBAAA,CAAwB;AAAA,IACxC,gBAAA;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,YAAA,EAAc,iBAAA,CAAkB,SAAA,CAAU,YAAY,CAAA;AAAA,MACtD,WAAW,SAAA,CAAU,SAAA;AAAA,MACrB,YAAY,SAAA,CAAU,UAAA;AAAA,MACtB,SAAS,SAAA,CAAU;AAAA;AACrB,GACD,CAAA;AACD,EAAA,OAAO,iBAAA,CAAkB,EAAE,GAAG,MAAA,EAAQ,WAAW,CAAA;AACnD;AAGA,eAAsB,aAAa,OAAA,EAAqD;AACtF,EAAA,MAAM,EAAE,gBAAA,EAAkB,YAAA,EAAc,GAAG,QAAO,GAAI,OAAA;AACtD,EAAA,MAAM,YAAY,0BAAA,CAA2B;AAAA,IAC3C,gBAAA;AAAA,IACA,YAAA,EAAc,kBAAkB,YAAY;AAAA,GAC7C,CAAA;AACD,EAAA,OAAO,iBAAA,CAAkB,EAAE,GAAG,MAAA,EAAQ,WAAW,CAAA;AACnD","file":"chunk-42ETVY6V.js","sourcesContent":["import { decodeChallenge } from '../webauthn/clientData';\nimport type { SubmitResult } from '../adapters/types';\nimport {\n buildAddSignerOperation,\n buildRemoveSignerOperation,\n type SignerStorage,\n} from '../soroban/signer';\nimport { sendSmartWalletTx, type SendSmartWalletTxOptions } from '../soroban/submit';\n\n/**\n * Multi-device recovery: add (or remove) a passkey signer on an existing\n * smart-wallet, on-chain. Adding a signer from a NEW device — while an existing\n * device is still available — is what keeps a lost device from locking the user\n * out. The new device produces a `{credentialId, publicKey}` via `registerPasskey`;\n * an EXISTING device authorizes `add_signer` here.\n *\n * These are thin wrappers over {@link sendSmartWalletTx}: build the wallet-\n * authorized `add_signer` / `remove_signer` invocation, then sign it with the\n * existing passkey and submit (recording → sign → enforcing re-sim → submit).\n */\n\n/** The new device's signer to enroll. `credentialId` may be base64url or raw bytes. */\nexport interface NewDeviceSigner {\n credentialId: string | Uint8Array;\n /** SEC-1 uncompressed public key (65 bytes) from `registerPasskey`. */\n publicKey: Uint8Array;\n /** Optional UNIX-seconds expiry (v1). Omit for a non-expiring recovery signer. */\n expiration?: number | bigint;\n /** SignerStorage; defaults to `Persistent`. */\n storage?: SignerStorage;\n}\n\n/** Shared wiring for a wallet-authorized signer change — the submit options minus the operation. */\nexport interface WalletCallOptions extends Omit<SendSmartWalletTxOptions, 'operation'> {\n /** The smart-wallet C-address being modified. */\n walletContractId: string;\n}\n\nexport interface AddSignerOptions extends WalletCallOptions {\n newSigner: NewDeviceSigner;\n}\n\nexport interface RemoveSignerOptions extends WalletCallOptions {\n /** Credential id of the signer to remove (base64url or raw bytes). */\n credentialId: string | Uint8Array;\n}\n\nfunction toRawCredentialId(id: string | Uint8Array): Uint8Array {\n return typeof id === 'string' ? decodeChallenge(id) : id;\n}\n\n/**\n * Add a new passkey signer to a smart-wallet on-chain (multi-device recovery),\n * authorized by an existing device. Returns the submission result (tx hash on\n * success). Throws a typed {@link KitError} on any failure.\n */\nexport async function addSigner(options: AddSignerOptions): Promise<SubmitResult> {\n const { walletContractId, newSigner, ...wiring } = options;\n const operation = buildAddSignerOperation({\n walletContractId,\n signer: {\n credentialId: toRawCredentialId(newSigner.credentialId),\n publicKey: newSigner.publicKey,\n expiration: newSigner.expiration,\n storage: newSigner.storage,\n },\n });\n return sendSmartWalletTx({ ...wiring, operation });\n}\n\n/** Remove a passkey signer from a smart-wallet on-chain (authorized by an existing device). */\nexport async function removeSigner(options: RemoveSignerOptions): Promise<SubmitResult> {\n const { walletContractId, credentialId, ...wiring } = options;\n const operation = buildRemoveSignerOperation({\n walletContractId,\n credentialId: toRawCredentialId(credentialId),\n });\n return sendSmartWalletTx({ ...wiring, operation });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { authEntryChallengeBytes } from './chunk-
|
|
1
|
+
import { authEntryChallengeBytes } from './chunk-XTLWH3VQ.js';
|
|
2
2
|
import { sha256, concatBytes } from './chunk-JNJ2FY45.js';
|
|
3
3
|
import { browserWebAuthnClient, base64UrlToBytes, bytesToUtf8, bytesToBase64Url } from './chunk-LB4HOTLV.js';
|
|
4
4
|
import '@stellar/stellar-sdk';
|
|
@@ -12,13 +12,16 @@ function browserPasskeySigner(options) {
|
|
|
12
12
|
rpId: options.rpId,
|
|
13
13
|
challenge: base64UrlToBytes(challenge),
|
|
14
14
|
allowCredentials: options.allowCredentials ?? [],
|
|
15
|
-
|
|
15
|
+
// The v0.2 account requires the User-Verified flag, so default to
|
|
16
|
+
// 'required'; a signature without UV fails on-chain.
|
|
17
|
+
userVerification: options.userVerification ?? "required"
|
|
16
18
|
});
|
|
17
19
|
return {
|
|
18
20
|
authenticatorData: assertion.authenticatorData,
|
|
19
21
|
clientDataJSON: assertion.clientDataJSON,
|
|
20
22
|
signature: assertion.signature,
|
|
21
|
-
credentialId: assertion.rawId
|
|
23
|
+
credentialId: assertion.rawId,
|
|
24
|
+
publicKey: options.publicKey
|
|
22
25
|
};
|
|
23
26
|
};
|
|
24
27
|
}
|
|
@@ -53,17 +56,26 @@ function verifySecp256r1StructMap(structMap, publicKey, signaturePayload) {
|
|
|
53
56
|
}
|
|
54
57
|
const digest = sha256(concatBytes(authenticatorData, sha256(clientDataJSON)));
|
|
55
58
|
const signatureValid = p256.verify(signature, digest, publicKey, {
|
|
56
|
-
lowS:
|
|
59
|
+
lowS: true,
|
|
57
60
|
format: "compact"
|
|
58
61
|
});
|
|
59
62
|
return { ok: challengeBound && signatureValid, challengeBound, signatureValid };
|
|
60
63
|
}
|
|
64
|
+
function bytesEqual(a, b) {
|
|
65
|
+
if (a.length !== b.length) return false;
|
|
66
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
61
69
|
function referenceCheckAuth(entry, publicKey, networkPassphrase) {
|
|
62
70
|
if (entry.credentials().switch().name !== "sorobanCredentialsAddress") {
|
|
63
71
|
return fail("no address credentials");
|
|
64
72
|
}
|
|
65
73
|
const structMap = entry.credentials().address().signature().map();
|
|
66
74
|
if (!structMap) return fail("signature is not a Secp256r1Signature map");
|
|
75
|
+
const inline = structMap.find((x) => x.key().sym().toString() === "public_key");
|
|
76
|
+
if (inline && !bytesEqual(new Uint8Array(inline.val().bytes()), publicKey)) {
|
|
77
|
+
return fail("unknown signer: inline public_key is not the enrolled key");
|
|
78
|
+
}
|
|
67
79
|
const signaturePayload = authEntryChallengeBytes(entry, networkPassphrase);
|
|
68
80
|
const r = verifySecp256r1StructMap(structMap, publicKey, signaturePayload);
|
|
69
81
|
return { success: r.ok, challengeBound: r.challengeBound, signatureValid: r.signatureValid };
|
|
@@ -104,5 +116,5 @@ function referenceSmartWalletCheckAuth(entry, publicKeyFor, networkPassphrase) {
|
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
export { browserPasskeySigner, referenceCheckAuth, referenceSmartWalletCheckAuth };
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
119
|
+
//# sourceMappingURL=chunk-ODXQCLZL.js.map
|
|
120
|
+
//# sourceMappingURL=chunk-ODXQCLZL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ceremonies/browserSigner.ts","../src/soroban/checkAuth.ts"],"names":[],"mappings":"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","file":"chunk-ODXQCLZL.js","sourcesContent":["import { base64UrlToBytes } from '../internal/encoding';\nimport type { AssertionResult, WebAuthnSigner } from '../soroban/sign';\nimport { browserWebAuthnClient } from './browserClient';\nimport type { WebAuthnClient } from './types';\n\nexport interface BrowserPasskeySignerOptions {\n /** The Relying Party ID (your site's registrable domain). */\n rpId: string;\n /**\n * Restrict the prompt to specific credential ids (base64url) — pass the\n * `credentialId` from `create` / `connect` so the OS offers the right passkey.\n * Omit (or leave empty) for a discoverable prompt over any resident passkey.\n */\n allowCredentials?: string[];\n userVerification?: 'discouraged' | 'preferred' | 'required';\n /** Inject a custom client (tests / non-browser). Defaults to `browserWebAuthnClient()`. */\n webauthn?: WebAuthnClient;\n /**\n * SEC-1 (65-byte) public key of this passkey (from `create` / `connect`).\n * WebAuthn assertions do not return the public key, so pass it here to sign\n * for the v0.2 single-signer account; it is echoed onto the `AssertionResult`.\n * You can instead supply it once via `SorobanSignOptions.publicKey`.\n */\n publicKey?: Uint8Array;\n}\n\n/**\n * Adapt `navigator.credentials.get` into the `WebAuthnSigner` that\n * `signTransaction` / `signAuthEntry` expect — so wiring a passkey is one line:\n *\n * ```ts\n * const sign = browserPasskeySigner({ rpId, allowCredentials: [credentialId] });\n * const signedXdr = await signTransaction(txXdr, { networkPassphrase, sign });\n * ```\n *\n * The SDK hands the signer a base64url challenge (the Soroban auth preimage);\n * this decodes it to the raw bytes WebAuthn signs over, runs the assertion, and\n * returns the fields the auth assembler needs. The DER signature is low-S\n * normalized downstream (invariant #2) — nothing here weakens that.\n */\nexport function browserPasskeySigner(options: BrowserPasskeySignerOptions): WebAuthnSigner {\n const webauthn = options.webauthn ?? browserWebAuthnClient();\n return async (challenge: string): Promise<AssertionResult> => {\n const assertion = await webauthn.get({\n rpId: options.rpId,\n challenge: base64UrlToBytes(challenge),\n allowCredentials: options.allowCredentials ?? [],\n // The v0.2 account requires the User-Verified flag, so default to\n // 'required'; a signature without UV fails on-chain.\n userVerification: options.userVerification ?? 'required',\n });\n return {\n authenticatorData: assertion.authenticatorData,\n clientDataJSON: assertion.clientDataJSON,\n signature: assertion.signature,\n credentialId: assertion.rawId,\n publicKey: options.publicKey,\n };\n };\n}\n","import { xdr } from '@stellar/stellar-sdk';\nimport { p256 } from '@noble/curves/nist';\nimport { sha256 } from '../internal/sha256';\nimport { concatBytes } from '../internal/bytes';\nimport { bytesToBase64Url, bytesToUtf8 } from '../internal/encoding';\nimport { authEntryChallengeBytes } from './preimage';\n\nexport interface CheckAuthResult {\n success: boolean;\n /** clientDataJSON.challenge === base64url(preimage hash). */\n challengeBound: boolean;\n /** secp256r1_verify over SHA256(authData ‖ SHA256(cdj)) passed. */\n signatureValid: boolean;\n reason?: string;\n}\n\nfunction fail(reason: string): CheckAuthResult {\n return { success: false, challengeBound: false, signatureValid: false, reason };\n}\n\n/**\n * Verify a bare `Secp256r1Signature` ScMap ({ authenticator_data,\n * client_data_json, signature }) against a public key + the expected signature\n * payload. Shared by the single-signer and smart-wallet reference verifiers.\n */\nfunction verifySecp256r1StructMap(\n structMap: xdr.ScMapEntry[],\n publicKey: Uint8Array,\n signaturePayload: Uint8Array,\n): { ok: boolean; challengeBound: boolean; signatureValid: boolean; reason?: string } {\n const field = (name: string): Uint8Array | undefined => {\n const e = structMap.find((x) => x.key().sym().toString() === name);\n return e ? new Uint8Array(e.val().bytes()) : undefined;\n };\n const authenticatorData = field('authenticator_data');\n const clientDataJSON = field('client_data_json');\n const signature = field('signature');\n if (!authenticatorData || !clientDataJSON || !signature) {\n return { ok: false, challengeBound: false, signatureValid: false, reason: 'missing fields' };\n }\n if (signature.length !== 64) {\n return {\n ok: false,\n challengeBound: false,\n signatureValid: false,\n reason: 'not 64-byte compact',\n };\n }\n let challengeBound = false;\n try {\n const cdj = JSON.parse(bytesToUtf8(clientDataJSON)) as { challenge?: string };\n challengeBound = cdj.challenge === bytesToBase64Url(signaturePayload);\n } catch {\n challengeBound = false;\n }\n // Host fn semantics: the Soroban secp256r1 host REQUIRES low-S. It rejects a\n // high-S signature at decode, so the reference model must too (lowS:true).\n // Modeling it as lowS:false would accept signatures the chain rejects and\n // could mask a missing client-side normalization in a signer path.\n const digest = sha256(concatBytes(authenticatorData, sha256(clientDataJSON)));\n const signatureValid = p256.verify(signature, digest, publicKey, {\n lowS: true,\n format: 'compact',\n });\n return { ok: challengeBound && signatureValid, challengeBound, signatureValid };\n}\n\nfunction bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\n/**\n * A faithful JavaScript model of the on-chain v0.2 `__check_auth`\n * (`contracts/webauthn-account`): read the inline `public_key`, require it to be\n * an enrolled signer (here, equal to `publicKey`), reconstruct\n * `SHA256(authenticator_data ‖ SHA256(client_data_json))`, run `secp256r1_verify`\n * (which REQUIRES low-S and rejects high-S at decode, so the model verifies with\n * lowS:true — the client-side normalization is what makes real assertions pass),\n * and assert challenge-binding (`clientDataJSON.challenge ===\n * base64url(signature_payload)`). Lets us prove the assembly without a live\n * Soroban RPC; the real on-chain run is exercised by the kit integration and the\n * demo. A 3-field struct with no inline key still verifies against `publicKey`\n * for back-compat with the smart-wallet reference.\n */\nexport function referenceCheckAuth(\n entry: xdr.SorobanAuthorizationEntry,\n publicKey: Uint8Array,\n networkPassphrase: string,\n): CheckAuthResult {\n if (entry.credentials().switch().name !== 'sorobanCredentialsAddress') {\n return fail('no address credentials');\n }\n // The signature is the Secp256r1Signature struct directly: a sorted ScMap\n // { authenticator_data, client_data_json, public_key, signature } (assemble.ts).\n const structMap = entry.credentials().address().signature().map();\n if (!structMap) return fail('signature is not a Secp256r1Signature map');\n\n // Enrollment: the contract rejects a key it does not hold before any crypto.\n const inline = structMap.find((x) => x.key().sym().toString() === 'public_key');\n if (inline && !bytesEqual(new Uint8Array(inline.val().bytes()), publicKey)) {\n return fail('unknown signer: inline public_key is not the enrolled key');\n }\n\n const signaturePayload = authEntryChallengeBytes(entry, networkPassphrase);\n const r = verifySecp256r1StructMap(structMap, publicKey, signaturePayload);\n return { success: r.ok, challengeBound: r.challengeBound, signatureValid: r.signatureValid };\n}\n\nexport interface SmartWalletCheckAuthResult {\n success: boolean;\n signers: { credentialId: string; challengeBound: boolean; signatureValid: boolean }[];\n reason?: string;\n}\n\n/**\n * A reference model of passkey-kit's smart-wallet `__check_auth`: unwrap the\n * `Signatures(Map<SignerKey, Signature>)` value (`ScVal::Vec([ScVal::Map(...)])`),\n * and for each `SignerKey::Secp256r1(id) => Signature::Secp256r1(struct)` entry,\n * look up that credential id's SEC-1 public key and verify the struct. Succeeds\n * only when the map is non-empty and every signer verifies + is challenge-bound.\n * `publicKeyFor` receives the lowercase-hex credential id.\n */\nexport function referenceSmartWalletCheckAuth(\n entry: xdr.SorobanAuthorizationEntry,\n publicKeyFor: (credentialIdHex: string) => Uint8Array | undefined,\n networkPassphrase: string,\n): SmartWalletCheckAuthResult {\n if (entry.credentials().switch().name !== 'sorobanCredentialsAddress') {\n return { success: false, signers: [], reason: 'no address credentials' };\n }\n const sig = entry.credentials().address().signature();\n const map = sig.switch().name === 'scvVec' ? sig.vec()?.[0]?.map() : undefined;\n if (!map) {\n return { success: false, signers: [], reason: 'signature is not a Signatures(Map) vec' };\n }\n if (map.length === 0) return { success: false, signers: [], reason: 'empty signatures map' };\n\n const signaturePayload = authEntryChallengeBytes(entry, networkPassphrase);\n const signers: SmartWalletCheckAuthResult['signers'] = [];\n let allOk = true;\n\n for (const e of map) {\n const keyVec = e.key().vec();\n const idBytes = keyVec?.[1]?.bytes();\n const credentialId = idBytes ? Buffer.from(idBytes).toString('hex') : '';\n // Signature::Secp256r1(struct) → scvVec([Symbol, structMap]).\n const structMap = e.val().vec()?.[1]?.map();\n const publicKey = publicKeyFor(credentialId);\n if (!structMap || !publicKey) {\n allOk = false;\n signers.push({ credentialId, challengeBound: false, signatureValid: false });\n continue;\n }\n const r = verifySecp256r1StructMap(structMap, publicKey, signaturePayload);\n if (!r.ok) allOk = false;\n signers.push({\n credentialId,\n challengeBound: r.challengeBound,\n signatureValid: r.signatureValid,\n });\n }\n return { success: allOk, signers };\n}\n"]}
|
|
@@ -24,14 +24,22 @@ function contractIdFromDeployerSalt(deployer, salt, networkPassphrase) {
|
|
|
24
24
|
return StrKey.encodeContract(hash(preimage.toXDR()));
|
|
25
25
|
}
|
|
26
26
|
function deriveAccountAddress(options) {
|
|
27
|
-
const { factoryContractId, credentialId, networkPassphrase } = options;
|
|
27
|
+
const { factoryContractId, credentialId, publicKey, networkPassphrase } = options;
|
|
28
28
|
if (credentialId.length === 0) {
|
|
29
29
|
throw new KitError("CONTRACT_AUTH_FAILED", "deriveAccountAddress: empty credential id");
|
|
30
30
|
}
|
|
31
|
+
if (publicKey.length !== 65) {
|
|
32
|
+
throw new KitError(
|
|
33
|
+
"CONTRACT_AUTH_FAILED",
|
|
34
|
+
`deriveAccountAddress: public key must be 65-byte SEC-1 (got ${String(publicKey.length)})`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const saltInput = new Uint8Array(credentialId.length + publicKey.length);
|
|
38
|
+
saltInput.set(credentialId, 0);
|
|
39
|
+
saltInput.set(publicKey, credentialId.length);
|
|
31
40
|
return contractIdFromDeployerSalt(
|
|
32
41
|
factoryContractId,
|
|
33
|
-
hash(Buffer.from(
|
|
34
|
-
// e.crypto().sha256(credential_id)
|
|
42
|
+
hash(Buffer.from(saltInput)),
|
|
35
43
|
networkPassphrase
|
|
36
44
|
);
|
|
37
45
|
}
|
|
@@ -44,5 +52,5 @@ function deriveSmartWalletAddress(options) {
|
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export { deriveAccountAddress, deriveSmartWalletAddress };
|
|
47
|
-
//# sourceMappingURL=chunk-
|
|
48
|
-
//# sourceMappingURL=chunk-
|
|
55
|
+
//# sourceMappingURL=chunk-OHKPNVB3.js.map
|
|
56
|
+
//# sourceMappingURL=chunk-OHKPNVB3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/soroban/address.ts"],"names":[],"mappings":";;;;;AAMA,SAAS,0BAAA,CACP,QAAA,EACA,IAAA,EACA,iBAAA,EACQ;AACR,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA,CAAE,WAAA,EAAY;AAAA,EACrD,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAI,SAAS,sBAAA,EAAwB,CAAA,0BAAA,EAA6B,QAAQ,CAAA,CAAA,CAAA,EAAK,EAAE,OAAO,CAAA;AAAA,EAChG;AACA,EAAA,MAAM,QAAA,GAAW,IAAI,cAAA,CAAe,sBAAA;AAAA,IAClC,IAAI,IAAI,wBAAA,CAAyB;AAAA,MAC/B,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAC,CAAA;AAAA,MAC9C,kBAAA,EAAoB,IAAI,kBAAA,CAAmB,6BAAA;AAAA,QACzC,IAAI,IAAI,6BAAA,CAA8B;AAAA,UACpC,OAAA;AAAA,UACA,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,IAAI;AAAA,SACvB;AAAA;AACH,KACD;AAAA,GACH;AACA,EAAA,OAAO,OAAO,cAAA,CAAe,IAAA,CAAK,QAAA,CAAS,KAAA,EAAO,CAAC,CAAA;AACrD;AA0BO,SAAS,qBAAqB,OAAA,EAA8C;AACjF,EAAA,MAAM,EAAE,iBAAA,EAAmB,YAAA,EAAc,SAAA,EAAW,mBAAkB,GAAI,OAAA;AAC1E,EAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,QAAA,CAAS,sBAAA,EAAwB,2CAA2C,CAAA;AAAA,EACxF;AACA,EAAA,IAAI,SAAA,CAAU,WAAW,EAAA,EAAI;AAC3B,IAAA,MAAM,IAAI,QAAA;AAAA,MACR,sBAAA;AAAA,MACA,CAAA,4DAAA,EAA+D,MAAA,CAAO,SAAA,CAAU,MAAM,CAAC,CAAA,CAAA;AAAA,KACzF;AAAA,EACF;AAEA,EAAA,MAAM,YAAY,IAAI,UAAA,CAAW,YAAA,CAAa,MAAA,GAAS,UAAU,MAAM,CAAA;AACvE,EAAA,SAAA,CAAU,GAAA,CAAI,cAAc,CAAC,CAAA;AAC7B,EAAA,SAAA,CAAU,GAAA,CAAI,SAAA,EAAW,YAAA,CAAa,MAAM,CAAA;AAC5C,EAAA,OAAO,0BAAA;AAAA,IACL,iBAAA;AAAA,IACA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,IAC3B;AAAA,GACF;AACF;AAyBO,SAAS,yBAAyB,OAAA,EAAkD;AACzF,EAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,OAAA,CAAQ,YAAY,CAAA;AAChD,EAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AACpB,IAAA,MAAM,IAAI,QAAA,CAAS,sBAAA,EAAwB,+CAA+C,CAAA;AAAA,EAC5F;AACA,EAAA,OAAO,2BAA2B,OAAA,CAAQ,QAAA,EAAU,OAAO,GAAG,CAAA,EAAG,QAAQ,iBAAiB,CAAA;AAC5F","file":"chunk-OHKPNVB3.js","sourcesContent":["import { Address, StrKey, hash, xdr } from '@stellar/stellar-sdk';\nimport { sha256 } from '@noble/hashes/sha256';\nimport { KitError } from '../errors';\nimport { decodeChallenge } from '../webauthn/clientData';\n\n/** The createCustomContract C-address from a deployer address + 32-byte salt (no network round-trip). */\nfunction contractIdFromDeployerSalt(\n deployer: string,\n salt: Uint8Array,\n networkPassphrase: string,\n): string {\n let address: xdr.ScAddress;\n try {\n address = Address.fromString(deployer).toScAddress();\n } catch (cause) {\n throw new KitError('CONTRACT_AUTH_FAILED', `invalid deployer address \"${deployer}\"`, { cause });\n }\n const preimage = xdr.HashIdPreimage.envelopeTypeContractId(\n new xdr.HashIdPreimageContractId({\n networkId: hash(Buffer.from(networkPassphrase)),\n contractIdPreimage: xdr.ContractIdPreimage.contractIdPreimageFromAddress(\n new xdr.ContractIdPreimageFromAddress({\n address,\n salt: Buffer.from(salt),\n }),\n ),\n }),\n );\n return StrKey.encodeContract(hash(preimage.toXDR()));\n}\n\nexport interface DeriveAccountAddressOptions {\n /** The AccountFactory contract C-address — the deployer. */\n factoryContractId: string;\n /** The credential-id bytes the factory salts (utf-8 of the base64url id in our factory). */\n credentialId: Uint8Array;\n /**\n * SEC-1 (65-byte) public key of the founding passkey. The v0.2 factory binds\n * it into the deploy salt (`sha256(credential_id ‖ public_key)`) so a public\n * credential id alone cannot be used to squat a derived address.\n */\n publicKey: Uint8Array;\n networkPassphrase: string;\n}\n\n/**\n * Derive the deterministic C-address of the `webauthn-account` our AccountFactory\n * deploys for a passkey — with NO network round-trip. The v0.2 factory salts by\n * `sha256(credential_id ‖ public_key)` and deploys with\n * `deployer().with_current_contract(salt)`. Binding the public key is the F1\n * fix: a credential id is public (the factory emits it), so salting by it alone\n * let anyone pre-deploy at a victim's address with their own key. For the\n * passkey-kit v1 smart-wallet, use {@link deriveSmartWalletAddress} instead\n * (different deployer + salt-input scheme).\n */\nexport function deriveAccountAddress(options: DeriveAccountAddressOptions): string {\n const { factoryContractId, credentialId, publicKey, networkPassphrase } = options;\n if (credentialId.length === 0) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'deriveAccountAddress: empty credential id');\n }\n if (publicKey.length !== 65) {\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n `deriveAccountAddress: public key must be 65-byte SEC-1 (got ${String(publicKey.length)})`,\n );\n }\n // salt = sha256(credential_id ‖ public_key), matching account-factory deploy().\n const saltInput = new Uint8Array(credentialId.length + publicKey.length);\n saltInput.set(credentialId, 0);\n saltInput.set(publicKey, credentialId.length);\n return contractIdFromDeployerSalt(\n factoryContractId,\n hash(Buffer.from(saltInput)),\n networkPassphrase,\n );\n}\n\nexport interface DeriveSmartWalletAddressOptions {\n /**\n * The account that deploys the wallet via `createCustomContract` — a classic\n * G-address (or a factory C-address). MUST be stable for offline `getAddress`.\n */\n deployer: string;\n /** base64url WebAuthn credential id of the FOUNDING passkey — its RAW bytes are salted. */\n credentialId: string;\n networkPassphrase: string;\n}\n\n/**\n * Derive the deterministic C-address of a passkey-kit **v1 smart-wallet** — offline,\n * no network. Mirrors passkey-kit's scheme exactly: `salt = sha256(rawCredentialId)`\n * (the base64url-decoded credential-id bytes), deployed by a fixed `deployer`\n * account via `createCustomContract`. Proven on-chain (`scripts/v1-events-probe.ts`:\n * derived id === deployed id).\n *\n * This only recovers the address for the wallet's FOUNDING credential (the one used\n * as the deploy salt). Signers added later via `add_signer` do not change the\n * address and cannot be reversed into it — resolve those through the events indexer\n * ({@link smartWalletV1Indexer}).\n */\nexport function deriveSmartWalletAddress(options: DeriveSmartWalletAddressOptions): string {\n const raw = decodeChallenge(options.credentialId);\n if (raw.length === 0) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'deriveSmartWalletAddress: empty credential id');\n }\n return contractIdFromDeployerSalt(options.deployer, sha256(raw), options.networkPassphrase);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildSignerKeyScVal, signTransaction } from './chunk-
|
|
1
|
+
import { buildSignerKeyScVal, signTransaction } from './chunk-XTLWH3VQ.js';
|
|
2
2
|
import { KitError } from './chunk-AMYFJQAC.js';
|
|
3
3
|
import { xdr, rpc, TransactionBuilder, Keypair, nativeToScVal, Contract } from '@stellar/stellar-sdk';
|
|
4
4
|
|
|
@@ -171,5 +171,5 @@ async function sendSmartWalletTx(options) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
export { buildAddSignerOperation, buildRemoveSignerOperation, buildSecp256r1Signer, directSubmission, sendSmartWalletTx };
|
|
174
|
-
//# sourceMappingURL=chunk-
|
|
175
|
-
//# sourceMappingURL=chunk-
|
|
174
|
+
//# sourceMappingURL=chunk-QREICNIW.js.map
|
|
175
|
+
//# sourceMappingURL=chunk-QREICNIW.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/soroban/signer.ts","../src/adapters/direct.ts","../src/soroban/submit.ts"],"names":["rpc","TransactionBuilder"],"mappings":"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","file":"chunk-BLFCRWCT.js","sourcesContent":["import { Contract, nativeToScVal, xdr } from '@stellar/stellar-sdk';\nimport { KitError } from '../errors';\nimport { buildSignerKeyScVal } from './smartWallet';\n\n/**\n * passkey-kit v1 (v0.13.0, Protocol 27) smart-wallet SIGNER encoders — the WRITE\n * path (what `add_signer` / `remove_signer` consume), complementing\n * `smartWallet.ts` (the signature READ path the wallet's `__check_auth` reads).\n *\n * A signer is added on-chain as\n *\n * Signer::Secp256r1(Bytes id, BytesN<65> public_key,\n * SignerExpiration, SignerLimits, SignerStorage)\n *\n * which `process_signer` reduces to `SignerKey::Secp256r1(id) ->\n * SignerVal::Secp256r1(public_key, ..)`: the raw credential id is the key, the\n * SEC-1 public key lives in the value. Shape + field order confirmed by kalepail\n * (passkey-kit author) in issue #32 and proven on testnet against the audited v1\n * wasm (`scripts/smartwallet-v1-e2e.ts`).\n *\n * v1 note: `SignerExpiration` is a UNIX-seconds `u64` timestamp (Protocol 27),\n * NOT a ledger sequence as in the pre-v1 kit.\n */\n\n/** `SignerStorage` — where the contract persists the signer entry. */\nexport type SignerStorage = 'Persistent' | 'Temporary';\n\nexport interface Secp256r1SignerSpec {\n /** Raw WebAuthn credential-id bytes (base64url-decoded `rawId`) — becomes the SignerKey. */\n credentialId: Uint8Array;\n /** SEC-1 uncompressed public key (65 bytes, `0x04 ‖ X ‖ Y`) — stored in SignerVal. */\n publicKey: Uint8Array;\n /**\n * `SignerExpiration` — a UNIX-seconds timestamp (v1 / Protocol 27) after which\n * the signer stops being valid. Omit for `None` (a non-expiring signer, the\n * usual choice for a recovery / second device).\n */\n expiration?: number | bigint;\n /** `SignerStorage` — defaults to `Persistent`. */\n storage?: SignerStorage;\n}\n\n/** A tuple-struct `Option` field set to `None` → `scvVec([Void])`. */\nfunction noneTuple(): xdr.ScVal {\n return xdr.ScVal.scvVec([xdr.ScVal.scvVoid()]);\n}\n\n/** `SignerExpiration(Option<u64>)` — `None` or `Some(unixSeconds)`. */\nfunction expirationScVal(expiration?: number | bigint): xdr.ScVal {\n if (expiration === undefined) return noneTuple();\n const ts = BigInt(expiration);\n if (ts < 0n) {\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n 'signer expiration must be a non-negative UNIX-seconds timestamp (v1)',\n );\n }\n return xdr.ScVal.scvVec([nativeToScVal(ts, { type: 'u64' })]);\n}\n\n/** `SignerStorage::Persistent | Temporary` → `scvVec([Symbol(variant)])`. */\nfunction storageScVal(storage: SignerStorage = 'Persistent'): xdr.ScVal {\n return xdr.ScVal.scvVec([xdr.ScVal.scvSymbol(storage)]);\n}\n\n/**\n * Encode a `Signer::Secp256r1(..)` for `add_signer`. `SignerLimits` is always\n * `None` (unrestricted) — policy/limited signers are out of scope for this\n * minimal passkey SDK. Validates the credential id and the SEC-1 public key so a\n * malformed signer can never be handed to the contract.\n */\nexport function buildSecp256r1Signer(spec: Secp256r1SignerSpec): xdr.ScVal {\n if (spec.credentialId.length === 0) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'buildSecp256r1Signer: empty credential id');\n }\n if (spec.publicKey.length !== 65 || spec.publicKey[0] !== 0x04) {\n throw new KitError(\n 'INVALID_PUBLIC_KEY',\n 'buildSecp256r1Signer: publicKey must be a 65-byte SEC-1 uncompressed point (0x04-prefixed)',\n );\n }\n return xdr.ScVal.scvVec([\n xdr.ScVal.scvSymbol('Secp256r1'),\n xdr.ScVal.scvBytes(Buffer.from(spec.credentialId)),\n xdr.ScVal.scvBytes(Buffer.from(spec.publicKey)),\n expirationScVal(spec.expiration), // SignerExpiration\n noneTuple(), // SignerLimits(None)\n storageScVal(spec.storage), // SignerStorage\n ]);\n}\n\nfunction contractCall(contractId: string, method: string, ...args: xdr.ScVal[]): xdr.Operation {\n let contract: Contract;\n try {\n contract = new Contract(contractId);\n } catch (cause) {\n throw new KitError('CONTRACT_AUTH_FAILED', `invalid smart-wallet contract id \"${contractId}\"`, {\n cause,\n });\n }\n return contract.call(method, ...args);\n}\n\nexport interface BuildAddSignerOptions {\n /** The smart-wallet C-address the signer is being added to. */\n walletContractId: string;\n /** The signer to add — a `Secp256r1SignerSpec`, or a pre-built `Signer` ScVal. */\n signer: Secp256r1SignerSpec | xdr.ScVal;\n}\n\n/**\n * Build the `wallet.add_signer(signer)` invocation. The resulting invoke-host\n * operation requires the wallet's OWN auth: an EXISTING signer authorizes adding\n * the new one, which is what keeps a lost device from locking the user out.\n * Sign the produced transaction's auth entry with\n * `signTransaction(.., { target: 'smart-wallet' })`.\n */\nexport function buildAddSignerOperation(options: BuildAddSignerOptions): xdr.Operation {\n const signerScVal =\n options.signer instanceof xdr.ScVal ? options.signer : buildSecp256r1Signer(options.signer);\n return contractCall(options.walletContractId, 'add_signer', signerScVal);\n}\n\nexport interface BuildRemoveSignerOptions {\n walletContractId: string;\n /** Raw credential-id bytes of the signer to remove (its `SignerKey`). */\n credentialId: Uint8Array;\n}\n\n/** Build the `wallet.remove_signer(SignerKey::Secp256r1(id))` invocation (wallet-authorized). */\nexport function buildRemoveSignerOperation(options: BuildRemoveSignerOptions): xdr.Operation {\n if (options.credentialId.length === 0) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'buildRemoveSignerOperation: empty credential id');\n }\n return contractCall(\n options.walletContractId,\n 'remove_signer',\n buildSignerKeyScVal(options.credentialId),\n );\n}\n","import { rpc, TransactionBuilder } from '@stellar/stellar-sdk';\nimport { KitError } from '../errors';\nimport type { SubmissionAdapter, SubmitResult } from './types';\n\nexport interface DirectSubmissionOptions {\n rpcUrl: string;\n networkPassphrase: string;\n allowHttp?: boolean;\n /** Max confirmation-poll attempts before failing with NETWORK_ERROR (default 30). */\n pollAttempts?: number;\n /** Delay between confirmation polls, in ms (default 1000). */\n pollIntervalMs?: number;\n}\n\n/**\n * Default, zero-infra submission: send straight to soroban-rpc and poll. No\n * relayer, no API key. Swappable with `launchtube`/`openzeppelinRelayer` with\n * zero ceremony changes (same `SubmissionAdapter` shape).\n */\nexport function directSubmission(options: DirectSubmissionOptions): SubmissionAdapter {\n const server = new rpc.Server(options.rpcUrl, {\n allowHttp: options.allowHttp ?? options.rpcUrl.startsWith('http://'),\n });\n const attempts = options.pollAttempts ?? 30;\n const intervalMs = options.pollIntervalMs ?? 1000;\n return {\n async send(signedTxXdr: string): Promise<SubmitResult> {\n const tx = TransactionBuilder.fromXDR(signedTxXdr, options.networkPassphrase);\n let sent;\n try {\n sent = await server.sendTransaction(tx);\n } catch (cause) {\n throw new KitError('NETWORK_ERROR', 'failed to submit transaction to soroban-rpc', {\n cause,\n });\n }\n if (sent.status === 'ERROR') {\n return {\n status: 'FAILED',\n hash: sent.hash,\n errorResultXdr: sent.errorResult?.toXDR('base64'),\n };\n }\n // Bounded polling: give up (NETWORK_ERROR) instead of hanging forever.\n let final;\n try {\n final = await server.pollTransaction(sent.hash, {\n attempts,\n sleepStrategy: () => intervalMs,\n });\n } catch (cause) {\n throw new KitError(\n 'NETWORK_ERROR',\n `transaction ${sent.hash} was not confirmed after ${String(attempts)} polls`,\n { cause },\n );\n }\n if (final.status === rpc.Api.GetTransactionStatus.NOT_FOUND) {\n throw new KitError(\n 'NETWORK_ERROR',\n `transaction ${sent.hash} still NOT_FOUND after ${String(attempts)} polls`,\n );\n }\n const success = final.status === rpc.Api.GetTransactionStatus.SUCCESS;\n return {\n status: success ? 'SUCCESS' : 'FAILED',\n hash: sent.hash,\n returnValue: success ? (final as { returnValue?: unknown }).returnValue : undefined,\n };\n },\n };\n}\n","import { Keypair, TransactionBuilder, rpc, xdr } from '@stellar/stellar-sdk';\nimport { KitError } from '../errors';\nimport { directSubmission } from '../adapters/direct';\nimport type { SubmissionAdapter, SubmitResult } from '../adapters/types';\nimport { signTransaction, type SignVerifyOptions, type WebAuthnSigner } from './sign';\n\nexport interface SendSmartWalletTxOptions {\n /** The single wallet-authorized invocation to run (SAC transfer, contract call, add_signer, …). */\n operation: xdr.Operation;\n networkPassphrase: string;\n rpcUrl: string;\n /**\n * Secret of the funded classic account that pays the fee + sequences the tx. The\n * passkey provides the wallet's AUTH; this only sponsors the envelope\n * (ephemeral/friendbot-funded in a demo; a relayer key otherwise).\n */\n sourceSecret: string;\n /** The wallet's passkey signer that authorizes the invocation. */\n sign: WebAuthnSigner;\n /** Where the signed tx is submitted. Defaults to `direct` (soroban-rpc). */\n submission?: SubmissionAdapter;\n /** Opt-in pre-flight validation of the authorizing assertion. */\n verify?: SignVerifyOptions;\n fee?: string;\n timeoutSeconds?: number;\n /** Ledgers ahead of `latestLedger` to expire the auth signature at (default 60). */\n signatureExpirationLedgerOffset?: number;\n allowHttp?: boolean;\n}\n\nconst DEFAULT_EXPIRATION_OFFSET = 60;\n\n/**\n * Sign and submit a single passkey-authorized smart-wallet transaction — the\n * general primitive behind every smart-wallet action (payment, contract call,\n * add/remove signer). The full dance:\n *\n * 1. recording sim → assemble (discovers the wallet's auth requirement);\n * 2. sign the wallet auth entry with the passkey (smart-wallet Signatures map),\n * binding the expiration ledger the contract re-derives;\n * 3. enforcing re-sim of the SIGNED tx — recording auth never runs the custom\n * account's `__check_auth`, so it misses the signer-storage reads and\n * under-counts instructions; the enforcing pass yields the true footprint +\n * resources, and doubles as a client-side proof the auth is accepted;\n * 4. pay the fee from the classic source and submit via the pluggable adapter.\n *\n * tx.fee + sorobanData are outside the auth preimage, so re-assembling with the\n * enforcing resources never invalidates the signature. Throws a typed KitError on\n * any simulation/validation failure.\n */\nexport async function sendSmartWalletTx(options: SendSmartWalletTxOptions): Promise<SubmitResult> {\n const server = new rpc.Server(options.rpcUrl, {\n allowHttp: options.allowHttp ?? options.rpcUrl.startsWith('http://'),\n });\n let source: Keypair;\n try {\n source = Keypair.fromSecret(options.sourceSecret);\n } catch (cause) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'invalid fee-source secret', { cause });\n }\n\n const account = await server.getAccount(source.publicKey());\n const tx = new TransactionBuilder(account, {\n fee: options.fee ?? '2000000',\n networkPassphrase: options.networkPassphrase,\n })\n .addOperation(options.operation)\n .setTimeout(options.timeoutSeconds ?? 120)\n .build();\n\n const recSim = await server.simulateTransaction(tx);\n if (!rpc.Api.isSimulationSuccess(recSim)) {\n const detail = 'error' in recSim ? String(recSim.error) : 'unknown simulation error';\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n `smart-wallet tx simulation failed: ${detail.slice(0, 300)}`,\n );\n }\n const prepared = rpc.assembleTransaction(tx, recSim).build();\n\n const signatureExpirationLedger =\n (await server.getLatestLedger()).sequence +\n (options.signatureExpirationLedgerOffset ?? DEFAULT_EXPIRATION_OFFSET);\n const signedAuthXdr = await signTransaction(prepared.toXDR(), {\n networkPassphrase: options.networkPassphrase,\n sign: options.sign,\n target: 'smart-wallet',\n signatureExpirationLedger,\n verify: options.verify,\n });\n\n const signedTx = TransactionBuilder.fromXDR(signedAuthXdr, options.networkPassphrase);\n const enfSim = await server.simulateTransaction(signedTx);\n if (!rpc.Api.isSimulationSuccess(enfSim)) {\n const detail = 'error' in enfSim ? String(enfSim.error) : 'unknown simulation error';\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n `signed smart-wallet tx failed enforcing simulation (auth rejected?): ${detail.slice(0, 300)}`,\n );\n }\n\n const finalTx = rpc.assembleTransaction(signedTx, enfSim).build();\n finalTx.sign(source);\n const submission =\n options.submission ??\n directSubmission({\n rpcUrl: options.rpcUrl,\n networkPassphrase: options.networkPassphrase,\n allowHttp: options.allowHttp,\n });\n return submission.send(finalTx.toXDR());\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/soroban/signer.ts","../src/adapters/direct.ts","../src/soroban/submit.ts"],"names":["rpc","TransactionBuilder"],"mappings":"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","file":"chunk-QREICNIW.js","sourcesContent":["import { Contract, nativeToScVal, xdr } from '@stellar/stellar-sdk';\nimport { KitError } from '../errors';\nimport { buildSignerKeyScVal } from './smartWallet';\n\n/**\n * passkey-kit v1 (v0.13.0, Protocol 27) smart-wallet SIGNER encoders — the WRITE\n * path (what `add_signer` / `remove_signer` consume), complementing\n * `smartWallet.ts` (the signature READ path the wallet's `__check_auth` reads).\n *\n * A signer is added on-chain as\n *\n * Signer::Secp256r1(Bytes id, BytesN<65> public_key,\n * SignerExpiration, SignerLimits, SignerStorage)\n *\n * which `process_signer` reduces to `SignerKey::Secp256r1(id) ->\n * SignerVal::Secp256r1(public_key, ..)`: the raw credential id is the key, the\n * SEC-1 public key lives in the value. Shape + field order confirmed by kalepail\n * (passkey-kit author) in issue #32 and proven on testnet against the audited v1\n * wasm (`scripts/smartwallet-v1-e2e.ts`).\n *\n * v1 note: `SignerExpiration` is a UNIX-seconds `u64` timestamp (Protocol 27),\n * NOT a ledger sequence as in the pre-v1 kit.\n */\n\n/** `SignerStorage` — where the contract persists the signer entry. */\nexport type SignerStorage = 'Persistent' | 'Temporary';\n\nexport interface Secp256r1SignerSpec {\n /** Raw WebAuthn credential-id bytes (base64url-decoded `rawId`) — becomes the SignerKey. */\n credentialId: Uint8Array;\n /** SEC-1 uncompressed public key (65 bytes, `0x04 ‖ X ‖ Y`) — stored in SignerVal. */\n publicKey: Uint8Array;\n /**\n * `SignerExpiration` — a UNIX-seconds timestamp (v1 / Protocol 27) after which\n * the signer stops being valid. Omit for `None` (a non-expiring signer, the\n * usual choice for a recovery / second device).\n */\n expiration?: number | bigint;\n /** `SignerStorage` — defaults to `Persistent`. */\n storage?: SignerStorage;\n}\n\n/** A tuple-struct `Option` field set to `None` → `scvVec([Void])`. */\nfunction noneTuple(): xdr.ScVal {\n return xdr.ScVal.scvVec([xdr.ScVal.scvVoid()]);\n}\n\n/** `SignerExpiration(Option<u64>)` — `None` or `Some(unixSeconds)`. */\nfunction expirationScVal(expiration?: number | bigint): xdr.ScVal {\n if (expiration === undefined) return noneTuple();\n const ts = BigInt(expiration);\n if (ts < 0n) {\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n 'signer expiration must be a non-negative UNIX-seconds timestamp (v1)',\n );\n }\n return xdr.ScVal.scvVec([nativeToScVal(ts, { type: 'u64' })]);\n}\n\n/** `SignerStorage::Persistent | Temporary` → `scvVec([Symbol(variant)])`. */\nfunction storageScVal(storage: SignerStorage = 'Persistent'): xdr.ScVal {\n return xdr.ScVal.scvVec([xdr.ScVal.scvSymbol(storage)]);\n}\n\n/**\n * Encode a `Signer::Secp256r1(..)` for `add_signer`. `SignerLimits` is always\n * `None` (unrestricted) — policy/limited signers are out of scope for this\n * minimal passkey SDK. Validates the credential id and the SEC-1 public key so a\n * malformed signer can never be handed to the contract.\n */\nexport function buildSecp256r1Signer(spec: Secp256r1SignerSpec): xdr.ScVal {\n if (spec.credentialId.length === 0) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'buildSecp256r1Signer: empty credential id');\n }\n if (spec.publicKey.length !== 65 || spec.publicKey[0] !== 0x04) {\n throw new KitError(\n 'INVALID_PUBLIC_KEY',\n 'buildSecp256r1Signer: publicKey must be a 65-byte SEC-1 uncompressed point (0x04-prefixed)',\n );\n }\n return xdr.ScVal.scvVec([\n xdr.ScVal.scvSymbol('Secp256r1'),\n xdr.ScVal.scvBytes(Buffer.from(spec.credentialId)),\n xdr.ScVal.scvBytes(Buffer.from(spec.publicKey)),\n expirationScVal(spec.expiration), // SignerExpiration\n noneTuple(), // SignerLimits(None)\n storageScVal(spec.storage), // SignerStorage\n ]);\n}\n\nfunction contractCall(contractId: string, method: string, ...args: xdr.ScVal[]): xdr.Operation {\n let contract: Contract;\n try {\n contract = new Contract(contractId);\n } catch (cause) {\n throw new KitError('CONTRACT_AUTH_FAILED', `invalid smart-wallet contract id \"${contractId}\"`, {\n cause,\n });\n }\n return contract.call(method, ...args);\n}\n\nexport interface BuildAddSignerOptions {\n /** The smart-wallet C-address the signer is being added to. */\n walletContractId: string;\n /** The signer to add — a `Secp256r1SignerSpec`, or a pre-built `Signer` ScVal. */\n signer: Secp256r1SignerSpec | xdr.ScVal;\n}\n\n/**\n * Build the `wallet.add_signer(signer)` invocation. The resulting invoke-host\n * operation requires the wallet's OWN auth: an EXISTING signer authorizes adding\n * the new one, which is what keeps a lost device from locking the user out.\n * Sign the produced transaction's auth entry with\n * `signTransaction(.., { target: 'smart-wallet' })`.\n */\nexport function buildAddSignerOperation(options: BuildAddSignerOptions): xdr.Operation {\n const signerScVal =\n options.signer instanceof xdr.ScVal ? options.signer : buildSecp256r1Signer(options.signer);\n return contractCall(options.walletContractId, 'add_signer', signerScVal);\n}\n\nexport interface BuildRemoveSignerOptions {\n walletContractId: string;\n /** Raw credential-id bytes of the signer to remove (its `SignerKey`). */\n credentialId: Uint8Array;\n}\n\n/** Build the `wallet.remove_signer(SignerKey::Secp256r1(id))` invocation (wallet-authorized). */\nexport function buildRemoveSignerOperation(options: BuildRemoveSignerOptions): xdr.Operation {\n if (options.credentialId.length === 0) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'buildRemoveSignerOperation: empty credential id');\n }\n return contractCall(\n options.walletContractId,\n 'remove_signer',\n buildSignerKeyScVal(options.credentialId),\n );\n}\n","import { rpc, TransactionBuilder } from '@stellar/stellar-sdk';\nimport { KitError } from '../errors';\nimport type { SubmissionAdapter, SubmitResult } from './types';\n\nexport interface DirectSubmissionOptions {\n rpcUrl: string;\n networkPassphrase: string;\n allowHttp?: boolean;\n /** Max confirmation-poll attempts before failing with NETWORK_ERROR (default 30). */\n pollAttempts?: number;\n /** Delay between confirmation polls, in ms (default 1000). */\n pollIntervalMs?: number;\n}\n\n/**\n * Default, zero-infra submission: send straight to soroban-rpc and poll. No\n * relayer, no API key. Swappable with `launchtube`/`openzeppelinRelayer` with\n * zero ceremony changes (same `SubmissionAdapter` shape).\n */\nexport function directSubmission(options: DirectSubmissionOptions): SubmissionAdapter {\n const server = new rpc.Server(options.rpcUrl, {\n allowHttp: options.allowHttp ?? options.rpcUrl.startsWith('http://'),\n });\n const attempts = options.pollAttempts ?? 30;\n const intervalMs = options.pollIntervalMs ?? 1000;\n return {\n async send(signedTxXdr: string): Promise<SubmitResult> {\n const tx = TransactionBuilder.fromXDR(signedTxXdr, options.networkPassphrase);\n let sent;\n try {\n sent = await server.sendTransaction(tx);\n } catch (cause) {\n throw new KitError('NETWORK_ERROR', 'failed to submit transaction to soroban-rpc', {\n cause,\n });\n }\n if (sent.status === 'ERROR') {\n return {\n status: 'FAILED',\n hash: sent.hash,\n errorResultXdr: sent.errorResult?.toXDR('base64'),\n };\n }\n // Bounded polling: give up (NETWORK_ERROR) instead of hanging forever.\n let final;\n try {\n final = await server.pollTransaction(sent.hash, {\n attempts,\n sleepStrategy: () => intervalMs,\n });\n } catch (cause) {\n throw new KitError(\n 'NETWORK_ERROR',\n `transaction ${sent.hash} was not confirmed after ${String(attempts)} polls`,\n { cause },\n );\n }\n if (final.status === rpc.Api.GetTransactionStatus.NOT_FOUND) {\n throw new KitError(\n 'NETWORK_ERROR',\n `transaction ${sent.hash} still NOT_FOUND after ${String(attempts)} polls`,\n );\n }\n const success = final.status === rpc.Api.GetTransactionStatus.SUCCESS;\n return {\n status: success ? 'SUCCESS' : 'FAILED',\n hash: sent.hash,\n returnValue: success ? (final as { returnValue?: unknown }).returnValue : undefined,\n };\n },\n };\n}\n","import { Keypair, TransactionBuilder, rpc, xdr } from '@stellar/stellar-sdk';\nimport { KitError } from '../errors';\nimport { directSubmission } from '../adapters/direct';\nimport type { SubmissionAdapter, SubmitResult } from '../adapters/types';\nimport { signTransaction, type SignVerifyOptions, type WebAuthnSigner } from './sign';\n\nexport interface SendSmartWalletTxOptions {\n /** The single wallet-authorized invocation to run (SAC transfer, contract call, add_signer, …). */\n operation: xdr.Operation;\n networkPassphrase: string;\n rpcUrl: string;\n /**\n * Secret of the funded classic account that pays the fee + sequences the tx. The\n * passkey provides the wallet's AUTH; this only sponsors the envelope\n * (ephemeral/friendbot-funded in a demo; a relayer key otherwise).\n */\n sourceSecret: string;\n /** The wallet's passkey signer that authorizes the invocation. */\n sign: WebAuthnSigner;\n /** Where the signed tx is submitted. Defaults to `direct` (soroban-rpc). */\n submission?: SubmissionAdapter;\n /** Opt-in pre-flight validation of the authorizing assertion. */\n verify?: SignVerifyOptions;\n fee?: string;\n timeoutSeconds?: number;\n /** Ledgers ahead of `latestLedger` to expire the auth signature at (default 60). */\n signatureExpirationLedgerOffset?: number;\n allowHttp?: boolean;\n}\n\nconst DEFAULT_EXPIRATION_OFFSET = 60;\n\n/**\n * Sign and submit a single passkey-authorized smart-wallet transaction — the\n * general primitive behind every smart-wallet action (payment, contract call,\n * add/remove signer). The full dance:\n *\n * 1. recording sim → assemble (discovers the wallet's auth requirement);\n * 2. sign the wallet auth entry with the passkey (smart-wallet Signatures map),\n * binding the expiration ledger the contract re-derives;\n * 3. enforcing re-sim of the SIGNED tx — recording auth never runs the custom\n * account's `__check_auth`, so it misses the signer-storage reads and\n * under-counts instructions; the enforcing pass yields the true footprint +\n * resources, and doubles as a client-side proof the auth is accepted;\n * 4. pay the fee from the classic source and submit via the pluggable adapter.\n *\n * tx.fee + sorobanData are outside the auth preimage, so re-assembling with the\n * enforcing resources never invalidates the signature. Throws a typed KitError on\n * any simulation/validation failure.\n */\nexport async function sendSmartWalletTx(options: SendSmartWalletTxOptions): Promise<SubmitResult> {\n const server = new rpc.Server(options.rpcUrl, {\n allowHttp: options.allowHttp ?? options.rpcUrl.startsWith('http://'),\n });\n let source: Keypair;\n try {\n source = Keypair.fromSecret(options.sourceSecret);\n } catch (cause) {\n throw new KitError('CONTRACT_AUTH_FAILED', 'invalid fee-source secret', { cause });\n }\n\n const account = await server.getAccount(source.publicKey());\n const tx = new TransactionBuilder(account, {\n fee: options.fee ?? '2000000',\n networkPassphrase: options.networkPassphrase,\n })\n .addOperation(options.operation)\n .setTimeout(options.timeoutSeconds ?? 120)\n .build();\n\n const recSim = await server.simulateTransaction(tx);\n if (!rpc.Api.isSimulationSuccess(recSim)) {\n const detail = 'error' in recSim ? String(recSim.error) : 'unknown simulation error';\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n `smart-wallet tx simulation failed: ${detail.slice(0, 300)}`,\n );\n }\n const prepared = rpc.assembleTransaction(tx, recSim).build();\n\n const signatureExpirationLedger =\n (await server.getLatestLedger()).sequence +\n (options.signatureExpirationLedgerOffset ?? DEFAULT_EXPIRATION_OFFSET);\n const signedAuthXdr = await signTransaction(prepared.toXDR(), {\n networkPassphrase: options.networkPassphrase,\n sign: options.sign,\n target: 'smart-wallet',\n signatureExpirationLedger,\n verify: options.verify,\n });\n\n const signedTx = TransactionBuilder.fromXDR(signedAuthXdr, options.networkPassphrase);\n const enfSim = await server.simulateTransaction(signedTx);\n if (!rpc.Api.isSimulationSuccess(enfSim)) {\n const detail = 'error' in enfSim ? String(enfSim.error) : 'unknown simulation error';\n throw new KitError(\n 'CONTRACT_AUTH_FAILED',\n `signed smart-wallet tx failed enforcing simulation (auth rejected?): ${detail.slice(0, 300)}`,\n );\n }\n\n const finalTx = rpc.assembleTransaction(signedTx, enfSim).build();\n finalTx.sign(source);\n const submission =\n options.submission ??\n directSubmission({\n rpcUrl: options.rpcUrl,\n networkPassphrase: options.networkPassphrase,\n allowHttp: options.allowHttp,\n });\n return submission.send(finalTx.toXDR());\n}\n"]}
|
|
@@ -19,7 +19,9 @@ function buildCreateOptions(input) {
|
|
|
19
19
|
authenticatorSelection: {
|
|
20
20
|
residentKey,
|
|
21
21
|
requireResidentKey: residentKey === "required",
|
|
22
|
-
|
|
22
|
+
// The v0.2 account requires the User-Verified flag at sign time, so
|
|
23
|
+
// register with 'required' to enroll authenticators that can produce it.
|
|
24
|
+
userVerification: input.userVerification ?? "required"
|
|
23
25
|
},
|
|
24
26
|
attestation: "none",
|
|
25
27
|
timeout: input.timeout ?? 6e4
|
|
@@ -129,5 +131,5 @@ async function createPasskey(options) {
|
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
export { buildCreateOptions, coseKeyToSec1, createPasskey, extractPublicKeyFromAttestationObject, extractPublicKeyFromAuthData, registerPasskey };
|
|
132
|
-
//# sourceMappingURL=chunk-
|
|
133
|
-
//# sourceMappingURL=chunk-
|
|
134
|
+
//# sourceMappingURL=chunk-RFMHMWTY.js.map
|
|
135
|
+
//# sourceMappingURL=chunk-RFMHMWTY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/webauthn/createOptions.ts","../src/webauthn/publicKey.ts","../src/ceremonies/create.ts"],"names":[],"mappings":"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","file":"chunk-RFMHMWTY.js","sourcesContent":["import { bytesToBase64Url } from '../internal/encoding';\nimport { utf8ToBytes } from '../internal/encoding';\n\nexport type ResidentKeyRequirement = 'discouraged' | 'preferred' | 'required';\nexport type UserVerificationRequirement = 'discouraged' | 'preferred' | 'required';\n\nexport interface BuildCreateOptionsInput {\n rpId: string;\n rpName: string;\n userName: string;\n /** Stable, opaque user handle. Defaults to UTF-8 bytes of `userName`. */\n userId?: Uint8Array;\n userDisplayName?: string;\n /** Raw challenge bytes (base64url-encoded into the options). */\n challenge: Uint8Array;\n residentKey?: ResidentKeyRequirement;\n userVerification?: UserVerificationRequirement;\n timeout?: number;\n}\n\n/** ES256-only credential parameter. The `alg: -7` literal is non-negotiable. */\nexport interface PubKeyCredParam {\n type: 'public-key';\n alg: -7;\n}\n\nexport interface PublicKeyCredentialCreationOptionsJSON {\n rp: { id: string; name: string };\n user: { id: string; name: string; displayName: string };\n challenge: string;\n pubKeyCredParams: PubKeyCredParam[];\n authenticatorSelection: {\n residentKey: ResidentKeyRequirement;\n requireResidentKey: boolean;\n userVerification: UserVerificationRequirement;\n };\n attestation: 'none';\n timeout: number;\n}\n\n/**\n * Build WebAuthn registration options that offer ES256 *and only* ES256\n * (`pubKeyCredParams: [{ type: 'public-key', alg: -7 }]`) — invariant #1.\n * Soroban verifies only secp256r1, so we never advertise any other algorithm.\n */\nexport function buildCreateOptions(\n input: BuildCreateOptionsInput,\n): PublicKeyCredentialCreationOptionsJSON {\n const residentKey = input.residentKey ?? 'required';\n return {\n rp: { id: input.rpId, name: input.rpName },\n user: {\n id: bytesToBase64Url(input.userId ?? utf8ToBytes(input.userName)),\n name: input.userName,\n displayName: input.userDisplayName ?? input.userName,\n },\n challenge: bytesToBase64Url(input.challenge),\n pubKeyCredParams: [{ type: 'public-key', alg: -7 }],\n authenticatorSelection: {\n residentKey,\n requireResidentKey: residentKey === 'required',\n // The v0.2 account requires the User-Verified flag at sign time, so\n // register with 'required' to enroll authenticators that can produce it.\n userVerification: input.userVerification ?? 'required',\n },\n attestation: 'none',\n timeout: input.timeout ?? 60_000,\n };\n}\n","import { p256 } from '@noble/curves/nist';\nimport { KitError } from '../errors';\nimport { concatBytes } from '../internal/bytes';\nimport { decodeCborFirst, type CborMap } from '../internal/cbor';\nimport { parseAuthenticatorData } from './authData';\nimport { assertES256 } from '../anchors';\n\n// COSE key common parameters (RFC 9052) and EC2 key parameters.\nconst COSE_KTY = 1;\nconst COSE_ALG = 3;\nconst COSE_CRV = -1;\nconst COSE_X = -2;\nconst COSE_Y = -3;\n\nconst KTY_EC2 = 2;\nconst CRV_P256 = 1;\n\n/**\n * Convert a CBOR-encoded COSE EC2 public key to the 65-byte SEC-1 uncompressed\n * point `0x04 ‖ X ‖ Y`. Enforces ES256/P-256 — anything else (e.g. RS256)\n * throws `ES256_NOT_SUPPORTED` (invariant #1).\n */\nexport function coseKeyToSec1(coseKey: Uint8Array): Uint8Array {\n let decoded: ReturnType<typeof decodeCborFirst>;\n try {\n decoded = decodeCborFirst(coseKey);\n } catch (cause) {\n throw new KitError('INVALID_PUBLIC_KEY', 'failed to CBOR-decode COSE key', { cause });\n }\n if (!(decoded instanceof Map)) {\n throw new KitError('INVALID_PUBLIC_KEY', 'COSE key is not a CBOR map');\n }\n const map = decoded as CborMap;\n const alg = map.get(COSE_ALG);\n if (typeof alg !== 'number') {\n throw new KitError('ES256_NOT_SUPPORTED', `COSE alg ${String(alg)} is not ES256 (-7)`);\n }\n assertES256(alg); // anchor RS256_HARD_FAIL\n if (map.get(COSE_KTY) !== KTY_EC2) {\n throw new KitError(\n 'INVALID_PUBLIC_KEY',\n `COSE kty ${String(map.get(COSE_KTY))} is not EC2 (2)`,\n );\n }\n if (map.get(COSE_CRV) !== CRV_P256) {\n throw new KitError(\n 'INVALID_PUBLIC_KEY',\n `COSE crv ${String(map.get(COSE_CRV))} is not P-256 (1)`,\n );\n }\n const x = map.get(COSE_X);\n const y = map.get(COSE_Y);\n if (!(x instanceof Uint8Array) || x.length !== 32) {\n throw new KitError('INVALID_PUBLIC_KEY', 'COSE key X coordinate is not 32 bytes');\n }\n if (!(y instanceof Uint8Array) || y.length !== 32) {\n throw new KitError('INVALID_PUBLIC_KEY', 'COSE key Y coordinate is not 32 bytes');\n }\n const sec1 = concatBytes(new Uint8Array([0x04]), x, y);\n // Reject off-curve / invalid points so an invalid key can never be persisted.\n try {\n p256.Point.fromHex(sec1);\n } catch (cause) {\n throw new KitError('INVALID_PUBLIC_KEY', 'public key is not a valid P-256 point', { cause });\n }\n return sec1;\n}\n\n/** Extract the SEC-1 public key from raw authenticatorData (attested data). */\nexport function extractPublicKeyFromAuthData(authData: Uint8Array): Uint8Array {\n const parsed = parseAuthenticatorData(authData);\n if (!parsed.credentialPublicKey) {\n throw new KitError(\n 'INVALID_PUBLIC_KEY',\n 'authenticatorData has no attested credential public key',\n );\n }\n return coseKeyToSec1(parsed.credentialPublicKey);\n}\n\n/** Extract the SEC-1 public key from a CBOR attestationObject (create ceremony). */\nexport function extractPublicKeyFromAttestationObject(attestationObject: Uint8Array): Uint8Array {\n let decoded: ReturnType<typeof decodeCborFirst>;\n try {\n decoded = decodeCborFirst(attestationObject);\n } catch (cause) {\n throw new KitError('INVALID_PUBLIC_KEY', 'failed to CBOR-decode attestationObject', { cause });\n }\n if (!(decoded instanceof Map)) {\n throw new KitError('INVALID_PUBLIC_KEY', 'attestationObject is not a CBOR map');\n }\n const authData = (decoded as CborMap).get('authData');\n if (!(authData instanceof Uint8Array)) {\n throw new KitError('INVALID_PUBLIC_KEY', 'attestationObject has no authData byte string');\n }\n return extractPublicKeyFromAuthData(authData);\n}\n","import { assertUserActivation } from '../anchors';\nimport { buildCreateOptions } from '../webauthn/createOptions';\nimport { extractPublicKeyFromAttestationObject } from '../webauthn/publicKey';\nimport type { PasskeyCredential } from '../types';\nimport { browserWebAuthnClient, defaultCredentialStorage } from './browserClient';\nimport { randomBytes } from './random';\nimport type { AccountDeployer, CredentialStorage, WebAuthnClient } from './types';\n\nexport interface CreatePasskeyOptions {\n rpId: string;\n rpName: string;\n userName: string;\n /** Deploys the smart account for the new passkey (contract-specific; see AccountDeployer). */\n deployer: AccountDeployer;\n webauthn?: WebAuthnClient;\n storage?: CredentialStorage;\n userId?: Uint8Array;\n challenge?: Uint8Array;\n residentKey?: 'discouraged' | 'preferred' | 'required';\n userVerification?: 'discouraged' | 'preferred' | 'required';\n /** Pass navigator.userActivation to enforce the Safari gesture requirement (S04). */\n userActivation?: { isActive: boolean };\n}\n\n/** Options for {@link registerPasskey} — a create WITHOUT deploy (see below). */\nexport type RegisterPasskeyOptions = Omit<CreatePasskeyOptions, 'deployer' | 'storage'>;\n\n/** A freshly-registered passkey credential (no account deployed yet). */\nexport interface RegisteredPasskey {\n /** base64url credential id returned by the authenticator. */\n credentialId: string;\n /** SEC-1 uncompressed public key (65 bytes). */\n publicKey: Uint8Array;\n}\n\n/**\n * `registerPasskey` — register an ES256-only passkey and extract its SEC-1 public\n * key (RS256 hard-fails), but do NOT deploy an account or persist anything. This\n * is the \"new device\" primitive for multi-device recovery: a fresh device\n * produces a `{credentialId, publicKey}` signer spec that an existing device then\n * authorizes on-chain via `addSigner`. (`createPasskey` is this plus a deploy.)\n */\nexport async function registerPasskey(options: RegisterPasskeyOptions): Promise<RegisteredPasskey> {\n assertUserActivation(options.userActivation); // anchor: apple-user-gesture (S04)\n const webauthn = options.webauthn ?? browserWebAuthnClient();\n\n const creationOptions = buildCreateOptions({\n rpId: options.rpId,\n rpName: options.rpName,\n userName: options.userName,\n userId: options.userId,\n challenge: options.challenge ?? randomBytes(32),\n residentKey: options.residentKey,\n userVerification: options.userVerification,\n });\n\n const credential = await webauthn.create(creationOptions);\n // ES256-only enforcement happens here: a non-ES256 credential throws ES256_NOT_SUPPORTED.\n const publicKey = extractPublicKeyFromAttestationObject(credential.attestationObject);\n return { credentialId: credential.id, publicKey };\n}\n\n/**\n * `createPasskey` — register an ES256-only passkey (see {@link registerPasskey}),\n * deploy a smart account for it via the factory, persist the credential id, and\n * return the account.\n */\nexport async function createPasskey(options: CreatePasskeyOptions): Promise<PasskeyCredential> {\n const storage = options.storage ?? defaultCredentialStorage();\n const { credentialId, publicKey } = await registerPasskey(options);\n const { contractId } = await options.deployer.deploy({ publicKey, credentialId });\n storage.set(options.rpId, credentialId);\n return { contractId, credentialId, publicKey };\n}\n"]}
|
|
@@ -67,10 +67,24 @@ function authEntryChallengeBytes(entry, networkPassphrase) {
|
|
|
67
67
|
function authEntryChallenge(entry, networkPassphrase) {
|
|
68
68
|
return bytesToBase64Url(authEntryChallengeBytes(entry, networkPassphrase));
|
|
69
69
|
}
|
|
70
|
-
function applyAssertionToEntry(entry, assertion) {
|
|
71
|
-
entry.credentials().address().signature(
|
|
70
|
+
function applyAssertionToEntry(entry, assertion, publicKey) {
|
|
71
|
+
entry.credentials().address().signature(buildSingleSignerSignatureScVal(assertion, publicKey));
|
|
72
72
|
return entry;
|
|
73
73
|
}
|
|
74
|
+
function buildSingleSignerSignatureScVal(assertion, publicKey) {
|
|
75
|
+
if (publicKey.length !== 65) {
|
|
76
|
+
throw new KitError(
|
|
77
|
+
"CONTRACT_AUTH_FAILED",
|
|
78
|
+
`single-signer signature needs a 65-byte SEC-1 public key (got ${String(publicKey.length)})`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return xdr.ScVal.scvMap([
|
|
82
|
+
bytesField("authenticator_data", assertion.authenticatorData),
|
|
83
|
+
bytesField("client_data_json", assertion.clientDataJSON),
|
|
84
|
+
bytesField("public_key", publicKey),
|
|
85
|
+
bytesField("signature", assertion.signature)
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
74
88
|
function buildSignatureScVal(assertion) {
|
|
75
89
|
return xdr.ScVal.scvMap([
|
|
76
90
|
bytesField("authenticator_data", assertion.authenticatorData),
|
|
@@ -221,7 +235,14 @@ async function signEntryInPlace(entry, options) {
|
|
|
221
235
|
if (options.target === "smart-wallet") {
|
|
222
236
|
applyAssertionToSmartWalletEntry(entry, normalized);
|
|
223
237
|
} else {
|
|
224
|
-
|
|
238
|
+
const publicKey = options.publicKey ?? assertion.publicKey;
|
|
239
|
+
if (publicKey === void 0) {
|
|
240
|
+
throw new KitError(
|
|
241
|
+
"CONTRACT_AUTH_FAILED",
|
|
242
|
+
"single-signer signing requires the signer public key: set SorobanSignOptions.publicKey or return publicKey from the signer"
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
applyAssertionToEntry(entry, normalized, publicKey);
|
|
225
246
|
}
|
|
226
247
|
}
|
|
227
248
|
async function signAuthEntry(entryXdr, options) {
|
|
@@ -264,5 +285,5 @@ async function signTransaction(txXdr, options) {
|
|
|
264
285
|
}
|
|
265
286
|
|
|
266
287
|
export { applyAssertionToEntry, applyAssertionToSmartWalletEntry, authEntryChallenge, authEntryChallengeBytes, buildSignerKeyScVal, buildSmartWalletSignatureVariant, compareSignerKeyScVal, derToCompact, derToCompactLowS, isLowS, normalizeLowS, reconstructSignedPayload, signAuthEntry, signTransaction, verifyAssertionSignature };
|
|
267
|
-
//# sourceMappingURL=chunk-
|
|
268
|
-
//# sourceMappingURL=chunk-
|
|
288
|
+
//# sourceMappingURL=chunk-XTLWH3VQ.js.map
|
|
289
|
+
//# sourceMappingURL=chunk-XTLWH3VQ.js.map
|