@dexterai/vault 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/signers/browser/index.ts
21
+ var browser_exports = {};
22
+ __export(browser_exports, {
23
+ WebAuthnAssertion: () => WebAuthnAssertion,
24
+ WebAuthnAssertionError: () => WebAuthnAssertionError,
25
+ derSignatureToCompactLowS: () => derSignatureToCompactLowS
26
+ });
27
+ module.exports = __toCommonJS(browser_exports);
28
+ var WebAuthnAssertionError = class extends Error {
29
+ code;
30
+ constructor(code, message) {
31
+ super(message);
32
+ this.code = code;
33
+ this.name = "WebAuthnAssertionError";
34
+ }
35
+ };
36
+ var WebAuthnAssertion = class {
37
+ credentialId;
38
+ publicKeyBase64;
39
+ rpId;
40
+ allowCredentials;
41
+ timeoutMs;
42
+ userVerification;
43
+ constructor(config) {
44
+ if (!(config.credentialId instanceof Uint8Array) || config.credentialId.length === 0) {
45
+ throw new WebAuthnAssertionError(
46
+ "invalid_credential_id",
47
+ "credentialId must be a non-empty Uint8Array"
48
+ );
49
+ }
50
+ this.credentialId = config.credentialId;
51
+ this.publicKeyBase64 = config.publicKeyBase64;
52
+ this.rpId = config.rpId;
53
+ this.allowCredentials = config.allowCredentials && config.allowCredentials.length > 0 ? config.allowCredentials : [{ id: config.credentialId }];
54
+ this.timeoutMs = config.timeoutMs ?? 6e4;
55
+ this.userVerification = config.userVerification ?? "preferred";
56
+ }
57
+ /**
58
+ * Run `navigator.credentials.get()` over `challenge` and return the
59
+ * three on-chain-ready buffers.
60
+ *
61
+ * The caller is responsible for what `challenge` *is*. For the vault
62
+ * program, this is typically `sha256(opMessage)` minted server-side
63
+ * with replay defense (see DexterApiBrowserPasskeySigner). The SDK
64
+ * does not impose policy here.
65
+ */
66
+ async assertOver(challenge) {
67
+ ensureBrowser();
68
+ if (!(challenge instanceof Uint8Array) || challenge.length === 0) {
69
+ throw new WebAuthnAssertionError(
70
+ "invalid_challenge",
71
+ "challenge must be a non-empty Uint8Array"
72
+ );
73
+ }
74
+ const requestOptions = {
75
+ challenge: toBufferSource(challenge),
76
+ allowCredentials: this.allowCredentials.map((c) => ({
77
+ id: toBufferSource(c.id),
78
+ type: "public-key",
79
+ transports: c.transports
80
+ })),
81
+ timeout: this.timeoutMs,
82
+ userVerification: this.userVerification,
83
+ ...this.rpId ? { rpId: this.rpId } : {}
84
+ };
85
+ const credential = await navigator.credentials.get({
86
+ publicKey: requestOptions
87
+ });
88
+ if (!credential) {
89
+ throw new WebAuthnAssertionError(
90
+ "user_cancelled",
91
+ "no assertion returned from authenticator"
92
+ );
93
+ }
94
+ if (credential.type !== "public-key") {
95
+ throw new WebAuthnAssertionError(
96
+ "credential_invalid",
97
+ `unexpected credential type: ${credential.type}`
98
+ );
99
+ }
100
+ const assertion = credential.response;
101
+ const derSignature = new Uint8Array(assertion.signature);
102
+ const compactSignature = derSignatureToCompactLowS(derSignature);
103
+ return {
104
+ signature: compactSignature,
105
+ clientDataJSON: new Uint8Array(assertion.clientDataJSON),
106
+ authenticatorData: new Uint8Array(assertion.authenticatorData)
107
+ };
108
+ }
109
+ /**
110
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
111
+ * to type against `PasskeySigner` (e.g. dexter-fe's
112
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
113
+ * that want the explicit name call `.assertOver(challenge)`. Same
114
+ * function, two names.
115
+ */
116
+ sign(challenge) {
117
+ return this.assertOver(challenge);
118
+ }
119
+ };
120
+ function ensureBrowser() {
121
+ if (typeof globalThis === "undefined" || typeof globalThis.navigator === "undefined") {
122
+ throw new WebAuthnAssertionError(
123
+ "not_browser",
124
+ "WebAuthnAssertion requires a browser environment (navigator.credentials)"
125
+ );
126
+ }
127
+ const cred = globalThis.navigator.credentials;
128
+ if (!cred || typeof cred.get !== "function") {
129
+ throw new WebAuthnAssertionError(
130
+ "webauthn_unsupported",
131
+ "this environment does not implement navigator.credentials.get"
132
+ );
133
+ }
134
+ }
135
+ function toBufferSource(bytes) {
136
+ const out = new ArrayBuffer(bytes.length);
137
+ new Uint8Array(out).set(bytes);
138
+ return out;
139
+ }
140
+ var P256_ORDER = BigInt(
141
+ "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
142
+ );
143
+ var P256_HALF_ORDER = P256_ORDER >> BigInt(1);
144
+ function bigintFromBytes(buf) {
145
+ let n = 0n;
146
+ for (const byte of buf) n = n << 8n | BigInt(byte);
147
+ return n;
148
+ }
149
+ function bytesFromBigint(n, length) {
150
+ const out = new Uint8Array(length);
151
+ for (let i = length - 1; i >= 0; i -= 1) {
152
+ out[i] = Number(n & 0xffn);
153
+ n >>= 8n;
154
+ }
155
+ return out;
156
+ }
157
+ function derSignatureToCompactLowS(der) {
158
+ let i = 0;
159
+ if (der[i++] !== 48) {
160
+ throw new WebAuthnAssertionError("bad_signature", "expected DER SEQUENCE");
161
+ }
162
+ i++;
163
+ if (der[i++] !== 2) {
164
+ throw new WebAuthnAssertionError("bad_signature", "expected r INTEGER");
165
+ }
166
+ const rLen = der[i++];
167
+ if (rLen === void 0) {
168
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no r length)");
169
+ }
170
+ let r = der.slice(i, i + rLen);
171
+ i += rLen;
172
+ if (der[i++] !== 2) {
173
+ throw new WebAuthnAssertionError("bad_signature", "expected s INTEGER");
174
+ }
175
+ const sLen = der[i++];
176
+ if (sLen === void 0) {
177
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no s length)");
178
+ }
179
+ let s = der.slice(i, i + sLen);
180
+ i += sLen;
181
+ if (r.length > 32 && r[0] === 0) r = r.slice(1);
182
+ if (s.length > 32 && s[0] === 0) s = s.slice(1);
183
+ if (r.length > 32 || s.length > 32) {
184
+ throw new WebAuthnAssertionError(
185
+ "bad_signature",
186
+ "DER component too large for P-256"
187
+ );
188
+ }
189
+ const rPadded = new Uint8Array(32);
190
+ rPadded.set(r, 32 - r.length);
191
+ let sN = bigintFromBytes(s);
192
+ if (sN > P256_HALF_ORDER) sN = P256_ORDER - sN;
193
+ const sPadded = bytesFromBigint(sN, 32);
194
+ const out = new Uint8Array(64);
195
+ out.set(rPadded, 0);
196
+ out.set(sPadded, 32);
197
+ return out;
198
+ }
199
+ // Annotate the CommonJS export names for ESM import in node:
200
+ 0 && (module.exports = {
201
+ WebAuthnAssertion,
202
+ WebAuthnAssertionError,
203
+ derSignatureToCompactLowS
204
+ });
@@ -0,0 +1,95 @@
1
+ import { PasskeySigner } from '../types.cjs';
2
+
3
+ /**
4
+ * WebAuthnAssertion — pure-browser P-256 passkey ceremony.
5
+ *
6
+ * Runs `navigator.credentials.get()` over a server-issued challenge and
7
+ * returns the three bytes the on-chain secp256r1 precompile + vault
8
+ * program need:
9
+ *
10
+ * - signature (64-byte compact r||s with low-S enforcement)
11
+ * - clientDataJSON (raw, what the authenticator hashed)
12
+ * - authenticatorData (raw, what the authenticator signed)
13
+ *
14
+ * Zero `fetch` calls. The consumer composes this with whatever server
15
+ * policy they enforce (replay defense, signature counter, AAGUID
16
+ * capture). For Dexter that policy lives in dexter-fe's
17
+ * `DexterApiBrowserPasskeySigner` adapter.
18
+ *
19
+ * The DER → compact lowS conversion is the canonical implementation —
20
+ * it lifts verbatim from dexter-fe/app/lib/passkey.ts (which had it
21
+ * duplicated in passkey-anon.ts). After v0.2 lands and dexter-fe
22
+ * swaps, those two copies go away.
23
+ */
24
+
25
+ interface WebAuthnAssertionConfig {
26
+ /** Raw credential ID bytes (NOT base64-encoded). */
27
+ credentialId: Uint8Array;
28
+ /** 33-byte SEC1 compressed P-256 pubkey, base64. Kept for symmetry / future use; not consumed by `assertOver`. */
29
+ publicKeyBase64?: string;
30
+ /** WebAuthn relying-party identifier. Defaults to omitting the field (browser uses the page's RP ID). */
31
+ rpId?: string;
32
+ /** Optional allow-list. Default: just `credentialId`. */
33
+ allowCredentials?: Array<{
34
+ id: Uint8Array;
35
+ transports?: AuthenticatorTransport[];
36
+ }>;
37
+ /** WebAuthn timeout in milliseconds. Default 60_000. */
38
+ timeoutMs?: number;
39
+ /** UV requirement. Default "preferred". */
40
+ userVerification?: UserVerificationRequirement;
41
+ }
42
+ interface WebAuthnAssertionResult {
43
+ /** 64-byte compact r||s P-256 signature, lowS-normalized (SIMD-0075 requires lowS). */
44
+ signature: Uint8Array;
45
+ /** Raw clientDataJSON as returned by the authenticator. */
46
+ clientDataJSON: Uint8Array;
47
+ /** Raw authenticatorData as returned by the authenticator. */
48
+ authenticatorData: Uint8Array;
49
+ }
50
+ declare class WebAuthnAssertionError extends Error {
51
+ readonly code: string;
52
+ constructor(code: string, message: string);
53
+ }
54
+ /**
55
+ * Pure-browser WebAuthn assertion driver. Implements `PasskeySigner` so
56
+ * adapters that compose with server policy can plug straight in.
57
+ */
58
+ declare class WebAuthnAssertion implements PasskeySigner {
59
+ readonly credentialId: Uint8Array;
60
+ readonly publicKeyBase64?: string;
61
+ private readonly rpId?;
62
+ private readonly allowCredentials;
63
+ private readonly timeoutMs;
64
+ private readonly userVerification;
65
+ constructor(config: WebAuthnAssertionConfig);
66
+ /**
67
+ * Run `navigator.credentials.get()` over `challenge` and return the
68
+ * three on-chain-ready buffers.
69
+ *
70
+ * The caller is responsible for what `challenge` *is*. For the vault
71
+ * program, this is typically `sha256(opMessage)` minted server-side
72
+ * with replay defense (see DexterApiBrowserPasskeySigner). The SDK
73
+ * does not impose policy here.
74
+ */
75
+ assertOver(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
76
+ /**
77
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
78
+ * to type against `PasskeySigner` (e.g. dexter-fe's
79
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
80
+ * that want the explicit name call `.assertOver(challenge)`. Same
81
+ * function, two names.
82
+ */
83
+ sign(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
84
+ }
85
+ /**
86
+ * Parse an ASN.1 DER ECDSA signature and return the 64-byte (r||s) form
87
+ * with s normalized to lowS (s ≤ n/2). SIMD-0075 rejects high-S
88
+ * signatures to prevent malleability replay.
89
+ *
90
+ * Exported so byte-parity tests can lock the conversion against the
91
+ * dexter-fe implementation it replaces.
92
+ */
93
+ declare function derSignatureToCompactLowS(der: Uint8Array): Uint8Array;
94
+
95
+ export { WebAuthnAssertion, type WebAuthnAssertionConfig, WebAuthnAssertionError, type WebAuthnAssertionResult, derSignatureToCompactLowS };
@@ -0,0 +1,95 @@
1
+ import { PasskeySigner } from '../types.js';
2
+
3
+ /**
4
+ * WebAuthnAssertion — pure-browser P-256 passkey ceremony.
5
+ *
6
+ * Runs `navigator.credentials.get()` over a server-issued challenge and
7
+ * returns the three bytes the on-chain secp256r1 precompile + vault
8
+ * program need:
9
+ *
10
+ * - signature (64-byte compact r||s with low-S enforcement)
11
+ * - clientDataJSON (raw, what the authenticator hashed)
12
+ * - authenticatorData (raw, what the authenticator signed)
13
+ *
14
+ * Zero `fetch` calls. The consumer composes this with whatever server
15
+ * policy they enforce (replay defense, signature counter, AAGUID
16
+ * capture). For Dexter that policy lives in dexter-fe's
17
+ * `DexterApiBrowserPasskeySigner` adapter.
18
+ *
19
+ * The DER → compact lowS conversion is the canonical implementation —
20
+ * it lifts verbatim from dexter-fe/app/lib/passkey.ts (which had it
21
+ * duplicated in passkey-anon.ts). After v0.2 lands and dexter-fe
22
+ * swaps, those two copies go away.
23
+ */
24
+
25
+ interface WebAuthnAssertionConfig {
26
+ /** Raw credential ID bytes (NOT base64-encoded). */
27
+ credentialId: Uint8Array;
28
+ /** 33-byte SEC1 compressed P-256 pubkey, base64. Kept for symmetry / future use; not consumed by `assertOver`. */
29
+ publicKeyBase64?: string;
30
+ /** WebAuthn relying-party identifier. Defaults to omitting the field (browser uses the page's RP ID). */
31
+ rpId?: string;
32
+ /** Optional allow-list. Default: just `credentialId`. */
33
+ allowCredentials?: Array<{
34
+ id: Uint8Array;
35
+ transports?: AuthenticatorTransport[];
36
+ }>;
37
+ /** WebAuthn timeout in milliseconds. Default 60_000. */
38
+ timeoutMs?: number;
39
+ /** UV requirement. Default "preferred". */
40
+ userVerification?: UserVerificationRequirement;
41
+ }
42
+ interface WebAuthnAssertionResult {
43
+ /** 64-byte compact r||s P-256 signature, lowS-normalized (SIMD-0075 requires lowS). */
44
+ signature: Uint8Array;
45
+ /** Raw clientDataJSON as returned by the authenticator. */
46
+ clientDataJSON: Uint8Array;
47
+ /** Raw authenticatorData as returned by the authenticator. */
48
+ authenticatorData: Uint8Array;
49
+ }
50
+ declare class WebAuthnAssertionError extends Error {
51
+ readonly code: string;
52
+ constructor(code: string, message: string);
53
+ }
54
+ /**
55
+ * Pure-browser WebAuthn assertion driver. Implements `PasskeySigner` so
56
+ * adapters that compose with server policy can plug straight in.
57
+ */
58
+ declare class WebAuthnAssertion implements PasskeySigner {
59
+ readonly credentialId: Uint8Array;
60
+ readonly publicKeyBase64?: string;
61
+ private readonly rpId?;
62
+ private readonly allowCredentials;
63
+ private readonly timeoutMs;
64
+ private readonly userVerification;
65
+ constructor(config: WebAuthnAssertionConfig);
66
+ /**
67
+ * Run `navigator.credentials.get()` over `challenge` and return the
68
+ * three on-chain-ready buffers.
69
+ *
70
+ * The caller is responsible for what `challenge` *is*. For the vault
71
+ * program, this is typically `sha256(opMessage)` minted server-side
72
+ * with replay defense (see DexterApiBrowserPasskeySigner). The SDK
73
+ * does not impose policy here.
74
+ */
75
+ assertOver(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
76
+ /**
77
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
78
+ * to type against `PasskeySigner` (e.g. dexter-fe's
79
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
80
+ * that want the explicit name call `.assertOver(challenge)`. Same
81
+ * function, two names.
82
+ */
83
+ sign(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
84
+ }
85
+ /**
86
+ * Parse an ASN.1 DER ECDSA signature and return the 64-byte (r||s) form
87
+ * with s normalized to lowS (s ≤ n/2). SIMD-0075 rejects high-S
88
+ * signatures to prevent malleability replay.
89
+ *
90
+ * Exported so byte-parity tests can lock the conversion against the
91
+ * dexter-fe implementation it replaces.
92
+ */
93
+ declare function derSignatureToCompactLowS(der: Uint8Array): Uint8Array;
94
+
95
+ export { WebAuthnAssertion, type WebAuthnAssertionConfig, WebAuthnAssertionError, type WebAuthnAssertionResult, derSignatureToCompactLowS };
@@ -0,0 +1,177 @@
1
+ // src/signers/browser/index.ts
2
+ var WebAuthnAssertionError = class extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = "WebAuthnAssertionError";
8
+ }
9
+ };
10
+ var WebAuthnAssertion = class {
11
+ credentialId;
12
+ publicKeyBase64;
13
+ rpId;
14
+ allowCredentials;
15
+ timeoutMs;
16
+ userVerification;
17
+ constructor(config) {
18
+ if (!(config.credentialId instanceof Uint8Array) || config.credentialId.length === 0) {
19
+ throw new WebAuthnAssertionError(
20
+ "invalid_credential_id",
21
+ "credentialId must be a non-empty Uint8Array"
22
+ );
23
+ }
24
+ this.credentialId = config.credentialId;
25
+ this.publicKeyBase64 = config.publicKeyBase64;
26
+ this.rpId = config.rpId;
27
+ this.allowCredentials = config.allowCredentials && config.allowCredentials.length > 0 ? config.allowCredentials : [{ id: config.credentialId }];
28
+ this.timeoutMs = config.timeoutMs ?? 6e4;
29
+ this.userVerification = config.userVerification ?? "preferred";
30
+ }
31
+ /**
32
+ * Run `navigator.credentials.get()` over `challenge` and return the
33
+ * three on-chain-ready buffers.
34
+ *
35
+ * The caller is responsible for what `challenge` *is*. For the vault
36
+ * program, this is typically `sha256(opMessage)` minted server-side
37
+ * with replay defense (see DexterApiBrowserPasskeySigner). The SDK
38
+ * does not impose policy here.
39
+ */
40
+ async assertOver(challenge) {
41
+ ensureBrowser();
42
+ if (!(challenge instanceof Uint8Array) || challenge.length === 0) {
43
+ throw new WebAuthnAssertionError(
44
+ "invalid_challenge",
45
+ "challenge must be a non-empty Uint8Array"
46
+ );
47
+ }
48
+ const requestOptions = {
49
+ challenge: toBufferSource(challenge),
50
+ allowCredentials: this.allowCredentials.map((c) => ({
51
+ id: toBufferSource(c.id),
52
+ type: "public-key",
53
+ transports: c.transports
54
+ })),
55
+ timeout: this.timeoutMs,
56
+ userVerification: this.userVerification,
57
+ ...this.rpId ? { rpId: this.rpId } : {}
58
+ };
59
+ const credential = await navigator.credentials.get({
60
+ publicKey: requestOptions
61
+ });
62
+ if (!credential) {
63
+ throw new WebAuthnAssertionError(
64
+ "user_cancelled",
65
+ "no assertion returned from authenticator"
66
+ );
67
+ }
68
+ if (credential.type !== "public-key") {
69
+ throw new WebAuthnAssertionError(
70
+ "credential_invalid",
71
+ `unexpected credential type: ${credential.type}`
72
+ );
73
+ }
74
+ const assertion = credential.response;
75
+ const derSignature = new Uint8Array(assertion.signature);
76
+ const compactSignature = derSignatureToCompactLowS(derSignature);
77
+ return {
78
+ signature: compactSignature,
79
+ clientDataJSON: new Uint8Array(assertion.clientDataJSON),
80
+ authenticatorData: new Uint8Array(assertion.authenticatorData)
81
+ };
82
+ }
83
+ /**
84
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
85
+ * to type against `PasskeySigner` (e.g. dexter-fe's
86
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
87
+ * that want the explicit name call `.assertOver(challenge)`. Same
88
+ * function, two names.
89
+ */
90
+ sign(challenge) {
91
+ return this.assertOver(challenge);
92
+ }
93
+ };
94
+ function ensureBrowser() {
95
+ if (typeof globalThis === "undefined" || typeof globalThis.navigator === "undefined") {
96
+ throw new WebAuthnAssertionError(
97
+ "not_browser",
98
+ "WebAuthnAssertion requires a browser environment (navigator.credentials)"
99
+ );
100
+ }
101
+ const cred = globalThis.navigator.credentials;
102
+ if (!cred || typeof cred.get !== "function") {
103
+ throw new WebAuthnAssertionError(
104
+ "webauthn_unsupported",
105
+ "this environment does not implement navigator.credentials.get"
106
+ );
107
+ }
108
+ }
109
+ function toBufferSource(bytes) {
110
+ const out = new ArrayBuffer(bytes.length);
111
+ new Uint8Array(out).set(bytes);
112
+ return out;
113
+ }
114
+ var P256_ORDER = BigInt(
115
+ "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
116
+ );
117
+ var P256_HALF_ORDER = P256_ORDER >> BigInt(1);
118
+ function bigintFromBytes(buf) {
119
+ let n = 0n;
120
+ for (const byte of buf) n = n << 8n | BigInt(byte);
121
+ return n;
122
+ }
123
+ function bytesFromBigint(n, length) {
124
+ const out = new Uint8Array(length);
125
+ for (let i = length - 1; i >= 0; i -= 1) {
126
+ out[i] = Number(n & 0xffn);
127
+ n >>= 8n;
128
+ }
129
+ return out;
130
+ }
131
+ function derSignatureToCompactLowS(der) {
132
+ let i = 0;
133
+ if (der[i++] !== 48) {
134
+ throw new WebAuthnAssertionError("bad_signature", "expected DER SEQUENCE");
135
+ }
136
+ i++;
137
+ if (der[i++] !== 2) {
138
+ throw new WebAuthnAssertionError("bad_signature", "expected r INTEGER");
139
+ }
140
+ const rLen = der[i++];
141
+ if (rLen === void 0) {
142
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no r length)");
143
+ }
144
+ let r = der.slice(i, i + rLen);
145
+ i += rLen;
146
+ if (der[i++] !== 2) {
147
+ throw new WebAuthnAssertionError("bad_signature", "expected s INTEGER");
148
+ }
149
+ const sLen = der[i++];
150
+ if (sLen === void 0) {
151
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no s length)");
152
+ }
153
+ let s = der.slice(i, i + sLen);
154
+ i += sLen;
155
+ if (r.length > 32 && r[0] === 0) r = r.slice(1);
156
+ if (s.length > 32 && s[0] === 0) s = s.slice(1);
157
+ if (r.length > 32 || s.length > 32) {
158
+ throw new WebAuthnAssertionError(
159
+ "bad_signature",
160
+ "DER component too large for P-256"
161
+ );
162
+ }
163
+ const rPadded = new Uint8Array(32);
164
+ rPadded.set(r, 32 - r.length);
165
+ let sN = bigintFromBytes(s);
166
+ if (sN > P256_HALF_ORDER) sN = P256_ORDER - sN;
167
+ const sPadded = bytesFromBigint(sN, 32);
168
+ const out = new Uint8Array(64);
169
+ out.set(rPadded, 0);
170
+ out.set(sPadded, 32);
171
+ return out;
172
+ }
173
+ export {
174
+ WebAuthnAssertion,
175
+ WebAuthnAssertionError,
176
+ derSignatureToCompactLowS
177
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/vault",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Canonical off-chain mirror of the dexter-vault Solana Anchor program — Solana instruction builders, byte-precise message encoders, account decoders, secp256r1/Ed25519 precompile helpers, counterfactual Swig derivation, and signer interfaces. The single source of truth for any TypeScript code that produces bytes the on-chain program will verify.",
5
5
  "author": "Dexter",
6
6
  "license": "MIT",
@@ -15,7 +15,8 @@
15
15
  "./reader": { "types": "./dist/reader/index.d.ts", "import": "./dist/reader/index.js", "require": "./dist/reader/index.cjs" },
16
16
  "./precompile": { "types": "./dist/precompile/index.d.ts", "import": "./dist/precompile/index.js", "require": "./dist/precompile/index.cjs" },
17
17
  "./signers": { "types": "./dist/signers/types.d.ts", "import": "./dist/signers/types.js", "require": "./dist/signers/types.cjs" },
18
- "./signers/node": { "types": "./dist/signers/node/index.d.ts", "import": "./dist/signers/node/index.js", "require": "./dist/signers/node/index.cjs" }
18
+ "./signers/node": { "types": "./dist/signers/node/index.d.ts", "import": "./dist/signers/node/index.js", "require": "./dist/signers/node/index.cjs" },
19
+ "./signers/browser": { "types": "./dist/signers/browser/index.d.ts", "import": "./dist/signers/browser/index.js", "require": "./dist/signers/browser/index.cjs" }
19
20
  },
20
21
  "files": ["dist", "README.md", "LICENSE", "assets"],
21
22
  "scripts": {