@dexterai/vault 0.1.3 → 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.
@@ -0,0 +1,205 @@
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
+ signatureDer: derSignature,
106
+ clientDataJSON: new Uint8Array(assertion.clientDataJSON),
107
+ authenticatorData: new Uint8Array(assertion.authenticatorData)
108
+ };
109
+ }
110
+ /**
111
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
112
+ * to type against `PasskeySigner` (e.g. dexter-fe's
113
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
114
+ * that want the explicit name call `.assertOver(challenge)`. Same
115
+ * function, two names.
116
+ */
117
+ sign(challenge) {
118
+ return this.assertOver(challenge);
119
+ }
120
+ };
121
+ function ensureBrowser() {
122
+ if (typeof globalThis === "undefined" || typeof globalThis.navigator === "undefined") {
123
+ throw new WebAuthnAssertionError(
124
+ "not_browser",
125
+ "WebAuthnAssertion requires a browser environment (navigator.credentials)"
126
+ );
127
+ }
128
+ const cred = globalThis.navigator.credentials;
129
+ if (!cred || typeof cred.get !== "function") {
130
+ throw new WebAuthnAssertionError(
131
+ "webauthn_unsupported",
132
+ "this environment does not implement navigator.credentials.get"
133
+ );
134
+ }
135
+ }
136
+ function toBufferSource(bytes) {
137
+ const out = new ArrayBuffer(bytes.length);
138
+ new Uint8Array(out).set(bytes);
139
+ return out;
140
+ }
141
+ var P256_ORDER = BigInt(
142
+ "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
143
+ );
144
+ var P256_HALF_ORDER = P256_ORDER >> BigInt(1);
145
+ function bigintFromBytes(buf) {
146
+ let n = 0n;
147
+ for (const byte of buf) n = n << 8n | BigInt(byte);
148
+ return n;
149
+ }
150
+ function bytesFromBigint(n, length) {
151
+ const out = new Uint8Array(length);
152
+ for (let i = length - 1; i >= 0; i -= 1) {
153
+ out[i] = Number(n & 0xffn);
154
+ n >>= 8n;
155
+ }
156
+ return out;
157
+ }
158
+ function derSignatureToCompactLowS(der) {
159
+ let i = 0;
160
+ if (der[i++] !== 48) {
161
+ throw new WebAuthnAssertionError("bad_signature", "expected DER SEQUENCE");
162
+ }
163
+ i++;
164
+ if (der[i++] !== 2) {
165
+ throw new WebAuthnAssertionError("bad_signature", "expected r INTEGER");
166
+ }
167
+ const rLen = der[i++];
168
+ if (rLen === void 0) {
169
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no r length)");
170
+ }
171
+ let r = der.slice(i, i + rLen);
172
+ i += rLen;
173
+ if (der[i++] !== 2) {
174
+ throw new WebAuthnAssertionError("bad_signature", "expected s INTEGER");
175
+ }
176
+ const sLen = der[i++];
177
+ if (sLen === void 0) {
178
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no s length)");
179
+ }
180
+ let s = der.slice(i, i + sLen);
181
+ i += sLen;
182
+ if (r.length > 32 && r[0] === 0) r = r.slice(1);
183
+ if (s.length > 32 && s[0] === 0) s = s.slice(1);
184
+ if (r.length > 32 || s.length > 32) {
185
+ throw new WebAuthnAssertionError(
186
+ "bad_signature",
187
+ "DER component too large for P-256"
188
+ );
189
+ }
190
+ const rPadded = new Uint8Array(32);
191
+ rPadded.set(r, 32 - r.length);
192
+ let sN = bigintFromBytes(s);
193
+ if (sN > P256_HALF_ORDER) sN = P256_ORDER - sN;
194
+ const sPadded = bytesFromBigint(sN, 32);
195
+ const out = new Uint8Array(64);
196
+ out.set(rPadded, 0);
197
+ out.set(sPadded, 32);
198
+ return out;
199
+ }
200
+ // Annotate the CommonJS export names for ESM import in node:
201
+ 0 && (module.exports = {
202
+ WebAuthnAssertion,
203
+ WebAuthnAssertionError,
204
+ derSignatureToCompactLowS
205
+ });
@@ -0,0 +1,103 @@
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
+ /**
46
+ * Raw DER-encoded ECDSA signature as returned by the authenticator,
47
+ * BEFORE the compact-lowS conversion. Kept so consumers that need to
48
+ * forward the assertion to a WebAuthn server library (which expects
49
+ * DER) don't have to re-run the ceremony. The on-chain bytes are
50
+ * `signature` (compact); DER is for server-side verify legs.
51
+ */
52
+ signatureDer: Uint8Array;
53
+ /** Raw clientDataJSON as returned by the authenticator. */
54
+ clientDataJSON: Uint8Array;
55
+ /** Raw authenticatorData as returned by the authenticator. */
56
+ authenticatorData: Uint8Array;
57
+ }
58
+ declare class WebAuthnAssertionError extends Error {
59
+ readonly code: string;
60
+ constructor(code: string, message: string);
61
+ }
62
+ /**
63
+ * Pure-browser WebAuthn assertion driver. Implements `PasskeySigner` so
64
+ * adapters that compose with server policy can plug straight in.
65
+ */
66
+ declare class WebAuthnAssertion implements PasskeySigner {
67
+ readonly credentialId: Uint8Array;
68
+ readonly publicKeyBase64?: string;
69
+ private readonly rpId?;
70
+ private readonly allowCredentials;
71
+ private readonly timeoutMs;
72
+ private readonly userVerification;
73
+ constructor(config: WebAuthnAssertionConfig);
74
+ /**
75
+ * Run `navigator.credentials.get()` over `challenge` and return the
76
+ * three on-chain-ready buffers.
77
+ *
78
+ * The caller is responsible for what `challenge` *is*. For the vault
79
+ * program, this is typically `sha256(opMessage)` minted server-side
80
+ * with replay defense (see DexterApiBrowserPasskeySigner). The SDK
81
+ * does not impose policy here.
82
+ */
83
+ assertOver(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
84
+ /**
85
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
86
+ * to type against `PasskeySigner` (e.g. dexter-fe's
87
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
88
+ * that want the explicit name call `.assertOver(challenge)`. Same
89
+ * function, two names.
90
+ */
91
+ sign(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
92
+ }
93
+ /**
94
+ * Parse an ASN.1 DER ECDSA signature and return the 64-byte (r||s) form
95
+ * with s normalized to lowS (s ≤ n/2). SIMD-0075 rejects high-S
96
+ * signatures to prevent malleability replay.
97
+ *
98
+ * Exported so byte-parity tests can lock the conversion against the
99
+ * dexter-fe implementation it replaces.
100
+ */
101
+ declare function derSignatureToCompactLowS(der: Uint8Array): Uint8Array;
102
+
103
+ export { WebAuthnAssertion, type WebAuthnAssertionConfig, WebAuthnAssertionError, type WebAuthnAssertionResult, derSignatureToCompactLowS };
@@ -0,0 +1,103 @@
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
+ /**
46
+ * Raw DER-encoded ECDSA signature as returned by the authenticator,
47
+ * BEFORE the compact-lowS conversion. Kept so consumers that need to
48
+ * forward the assertion to a WebAuthn server library (which expects
49
+ * DER) don't have to re-run the ceremony. The on-chain bytes are
50
+ * `signature` (compact); DER is for server-side verify legs.
51
+ */
52
+ signatureDer: Uint8Array;
53
+ /** Raw clientDataJSON as returned by the authenticator. */
54
+ clientDataJSON: Uint8Array;
55
+ /** Raw authenticatorData as returned by the authenticator. */
56
+ authenticatorData: Uint8Array;
57
+ }
58
+ declare class WebAuthnAssertionError extends Error {
59
+ readonly code: string;
60
+ constructor(code: string, message: string);
61
+ }
62
+ /**
63
+ * Pure-browser WebAuthn assertion driver. Implements `PasskeySigner` so
64
+ * adapters that compose with server policy can plug straight in.
65
+ */
66
+ declare class WebAuthnAssertion implements PasskeySigner {
67
+ readonly credentialId: Uint8Array;
68
+ readonly publicKeyBase64?: string;
69
+ private readonly rpId?;
70
+ private readonly allowCredentials;
71
+ private readonly timeoutMs;
72
+ private readonly userVerification;
73
+ constructor(config: WebAuthnAssertionConfig);
74
+ /**
75
+ * Run `navigator.credentials.get()` over `challenge` and return the
76
+ * three on-chain-ready buffers.
77
+ *
78
+ * The caller is responsible for what `challenge` *is*. For the vault
79
+ * program, this is typically `sha256(opMessage)` minted server-side
80
+ * with replay defense (see DexterApiBrowserPasskeySigner). The SDK
81
+ * does not impose policy here.
82
+ */
83
+ assertOver(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
84
+ /**
85
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
86
+ * to type against `PasskeySigner` (e.g. dexter-fe's
87
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
88
+ * that want the explicit name call `.assertOver(challenge)`. Same
89
+ * function, two names.
90
+ */
91
+ sign(challenge: Uint8Array): Promise<WebAuthnAssertionResult>;
92
+ }
93
+ /**
94
+ * Parse an ASN.1 DER ECDSA signature and return the 64-byte (r||s) form
95
+ * with s normalized to lowS (s ≤ n/2). SIMD-0075 rejects high-S
96
+ * signatures to prevent malleability replay.
97
+ *
98
+ * Exported so byte-parity tests can lock the conversion against the
99
+ * dexter-fe implementation it replaces.
100
+ */
101
+ declare function derSignatureToCompactLowS(der: Uint8Array): Uint8Array;
102
+
103
+ export { WebAuthnAssertion, type WebAuthnAssertionConfig, WebAuthnAssertionError, type WebAuthnAssertionResult, derSignatureToCompactLowS };
@@ -0,0 +1,178 @@
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
+ signatureDer: derSignature,
80
+ clientDataJSON: new Uint8Array(assertion.clientDataJSON),
81
+ authenticatorData: new Uint8Array(assertion.authenticatorData)
82
+ };
83
+ }
84
+ /**
85
+ * `PasskeySigner` shape — alias for `assertOver`. Consumers that want
86
+ * to type against `PasskeySigner` (e.g. dexter-fe's
87
+ * `DexterApiBrowserPasskeySigner`) call `.sign(challenge)`; consumers
88
+ * that want the explicit name call `.assertOver(challenge)`. Same
89
+ * function, two names.
90
+ */
91
+ sign(challenge) {
92
+ return this.assertOver(challenge);
93
+ }
94
+ };
95
+ function ensureBrowser() {
96
+ if (typeof globalThis === "undefined" || typeof globalThis.navigator === "undefined") {
97
+ throw new WebAuthnAssertionError(
98
+ "not_browser",
99
+ "WebAuthnAssertion requires a browser environment (navigator.credentials)"
100
+ );
101
+ }
102
+ const cred = globalThis.navigator.credentials;
103
+ if (!cred || typeof cred.get !== "function") {
104
+ throw new WebAuthnAssertionError(
105
+ "webauthn_unsupported",
106
+ "this environment does not implement navigator.credentials.get"
107
+ );
108
+ }
109
+ }
110
+ function toBufferSource(bytes) {
111
+ const out = new ArrayBuffer(bytes.length);
112
+ new Uint8Array(out).set(bytes);
113
+ return out;
114
+ }
115
+ var P256_ORDER = BigInt(
116
+ "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"
117
+ );
118
+ var P256_HALF_ORDER = P256_ORDER >> BigInt(1);
119
+ function bigintFromBytes(buf) {
120
+ let n = 0n;
121
+ for (const byte of buf) n = n << 8n | BigInt(byte);
122
+ return n;
123
+ }
124
+ function bytesFromBigint(n, length) {
125
+ const out = new Uint8Array(length);
126
+ for (let i = length - 1; i >= 0; i -= 1) {
127
+ out[i] = Number(n & 0xffn);
128
+ n >>= 8n;
129
+ }
130
+ return out;
131
+ }
132
+ function derSignatureToCompactLowS(der) {
133
+ let i = 0;
134
+ if (der[i++] !== 48) {
135
+ throw new WebAuthnAssertionError("bad_signature", "expected DER SEQUENCE");
136
+ }
137
+ i++;
138
+ if (der[i++] !== 2) {
139
+ throw new WebAuthnAssertionError("bad_signature", "expected r INTEGER");
140
+ }
141
+ const rLen = der[i++];
142
+ if (rLen === void 0) {
143
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no r length)");
144
+ }
145
+ let r = der.slice(i, i + rLen);
146
+ i += rLen;
147
+ if (der[i++] !== 2) {
148
+ throw new WebAuthnAssertionError("bad_signature", "expected s INTEGER");
149
+ }
150
+ const sLen = der[i++];
151
+ if (sLen === void 0) {
152
+ throw new WebAuthnAssertionError("bad_signature", "truncated DER (no s length)");
153
+ }
154
+ let s = der.slice(i, i + sLen);
155
+ i += sLen;
156
+ if (r.length > 32 && r[0] === 0) r = r.slice(1);
157
+ if (s.length > 32 && s[0] === 0) s = s.slice(1);
158
+ if (r.length > 32 || s.length > 32) {
159
+ throw new WebAuthnAssertionError(
160
+ "bad_signature",
161
+ "DER component too large for P-256"
162
+ );
163
+ }
164
+ const rPadded = new Uint8Array(32);
165
+ rPadded.set(r, 32 - r.length);
166
+ let sN = bigintFromBytes(s);
167
+ if (sN > P256_HALF_ORDER) sN = P256_ORDER - sN;
168
+ const sPadded = bytesFromBigint(sN, 32);
169
+ const out = new Uint8Array(64);
170
+ out.set(rPadded, 0);
171
+ out.set(sPadded, 32);
172
+ return out;
173
+ }
174
+ export {
175
+ WebAuthnAssertion,
176
+ WebAuthnAssertionError,
177
+ derSignatureToCompactLowS
178
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexterai/vault",
3
- "version": "0.1.3",
3
+ "version": "0.2.1",
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": {