@zkp-auth/core 0.1.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,293 @@
1
+ /**
2
+ * Generates a fresh `(privateKey, publicKey)` pair for the ZKP-auth
3
+ * scheme.
4
+ *
5
+ * The `privateKey` is a uniform 32-byte little-endian encoding of a
6
+ * scalar `n ∈ [1, L)`, drawn via bounded rejection sampling against
7
+ * the CSPRNG chokepoint `randomBytes32()`. The `publicKey` is the
8
+ * canonical 32-byte encoding of `n · G`, where `G` is the Ed25519
9
+ * base point.
10
+ *
11
+ * The two outputs are returned as fresh `Uint8Array` instances —
12
+ * `privateKey` is the accepted CSPRNG draw itself (a copy detached
13
+ * from Node's internal buffer pool, see `rng.ts`'s `Uint8Array.from`
14
+ * step), and `publicKey` is produced by `@noble/curves`'s `toBytes()`
15
+ * which allocates a fresh array. Callers may zero-fill the returned
16
+ * `privateKey` after use without affecting any other observer
17
+ * (Requirement 6.4 hygiene; not enforced by this function but
18
+ * permitted by its allocation contract).
19
+ *
20
+ * Failure modes — both surface as `RandomnessError` with
21
+ * `code === 'RNG_FAILURE'`, never as a partial or zero-padded result
22
+ * (Requirement 1.5):
23
+ *
24
+ * - The underlying `randomBytes32()` throws (CSPRNG anomaly or short
25
+ * read). Any error — whether a `RandomnessError` already produced
26
+ * by `rng.ts`, or a raw `Error` injected by tests via `vi.mock` —
27
+ * is caught and, if not already a `RandomnessError`, re-wrapped as
28
+ * one. This mirrors the defense-in-depth pattern in `compute-proof.ts`.
29
+ * - 256 successive draws all decode to scalars outside `[1, L)`. This
30
+ * is statistically impossible under a healthy CSPRNG (≈ `2^-252`
31
+ * per draw), so exhaustion is reported as an RNG failure rather
32
+ * than as a separate exhaustion-specific error class.
33
+ *
34
+ * @returns An object with `privateKey` (32 bytes, encoding a scalar
35
+ * in `[1, L)`) and `publicKey` (32 bytes, encoding `privateKey · G`).
36
+ * @throws RandomnessError When the CSPRNG fails or rejection sampling
37
+ * exhausts its 256-iteration bound.
38
+ */
39
+ declare function generateKeyPair(): {
40
+ privateKey: Uint8Array;
41
+ publicKey: Uint8Array;
42
+ };
43
+
44
+ /**
45
+ * Generates a fresh 32-byte challenge for a Schnorr-proof
46
+ * authentication session.
47
+ *
48
+ * The returned bytes are drawn from the OS CSPRNG via the library's
49
+ * single chokepoint (`randomBytes32`) and are statistically
50
+ * independent of `sessionId` — the parameter exists only so the
51
+ * caller's wire protocol can validate session-handle shape at a
52
+ * single, well-defined entry point. See the file-header comment for
53
+ * the security rationale (Requirement 2.5 / Property 4).
54
+ *
55
+ * The returned `Uint8Array` is a fresh allocation, detached from any
56
+ * internal CSPRNG buffer pool (see `rng.ts`'s `Uint8Array.from` step),
57
+ * so the caller may zero-fill it after use without affecting any
58
+ * other observer.
59
+ *
60
+ * Failure modes:
61
+ *
62
+ * - `InvalidInputError` with `code === 'INVALID_SESSION_ID'` — thrown
63
+ * when `sessionId` is not a `Uint8Array`, has length 0, or has
64
+ * length greater than 256 bytes. The 1..256-byte inclusive window
65
+ * is enforced by `assertUint8ArrayLengthBetween`.
66
+ * - `RandomnessError` with `code === 'RNG_FAILURE'` — thrown when
67
+ * `randomBytes32()` throws (any underlying CSPRNG fault), or when
68
+ * the returned buffer is not exactly 32 bytes. In production,
69
+ * `rng.ts` wraps both cases before they reach this function; the
70
+ * extra length guard and try/catch here are defense-in-depth for
71
+ * test-injected mocks (property-13, `vi.mock`). No partial or
72
+ * zero-padded challenge is ever returned (Requirement 2.4).
73
+ *
74
+ * @param sessionId Caller-supplied session handle. Validated for
75
+ * `Uint8Array` shape and a length in the inclusive range
76
+ * `[1, 256]`; never read after validation.
77
+ * @returns A fresh 32-byte CSPRNG-derived `Uint8Array`, independent
78
+ * of `sessionId`.
79
+ * @throws InvalidInputError When `sessionId` fails shape or length
80
+ * validation.
81
+ * @throws RandomnessError When the underlying CSPRNG throws or
82
+ * returns a short read.
83
+ */
84
+ declare function generateChallenge(sessionId: Uint8Array): Uint8Array;
85
+
86
+ /**
87
+ * Computes a 64-byte Schnorr proof of knowledge of `privateKey` over
88
+ * a verifier-chosen `challenge`, with `password` carried as opaque
89
+ * (and currently unused) metadata for forward-compatibility.
90
+ *
91
+ * The returned proof is `R_bytes || s_bytes` (32 bytes each), where
92
+ * `R = r · G` is the commitment to a fresh CSPRNG-drawn nonce
93
+ * `r ∈ [1, L)`, and `s = (r + c · x) mod L` is the response, with
94
+ * `c = int_LE(SHA-512(R || X || challenge)) mod L` and
95
+ * `x = int_LE(privateKey)` (Requirement 11.1: `x` is derived from
96
+ * `privateKey` only; `password` does NOT participate).
97
+ *
98
+ * The proof verifies under `verify-proof.ts`'s
99
+ * `s · G == R + c · publicKey` equation when invoked with the
100
+ * matching `publicKey = x · G` (Property 6 round-trip).
101
+ *
102
+ * `password` is validated for shape (Requirement 3.7) but is then
103
+ * treated as opaque bytes — it is NOT mixed into the scalar `x`, NOT
104
+ * folded into the Fiat-Shamir transcript, and NOT touched in any
105
+ * computation past validation (Requirements 3.3, 11.1; Property 10).
106
+ *
107
+ * Failure modes:
108
+ *
109
+ * - `InvalidInputError` with `code === 'INVALID_PRIVATE_KEY'` —
110
+ * `privateKey` is not a `Uint8Array(32)`, OR its little-endian
111
+ * decoding is `0`, OR its little-endian decoding is `≥ L`
112
+ * (Requirements 3.5, 11.4). The `≥ L` and `=== 0` checks are
113
+ * performed on the RAW decoding, not on `reduceScalar`'s output:
114
+ * `generateKeyPair` always produces in-range keys, so any
115
+ * out-of-range input is an integration error and we surface it
116
+ * verbatim rather than silently reduce.
117
+ * - `InvalidInputError` with `code === 'INVALID_PASSWORD'` —
118
+ * `password` is not a `Uint8Array`, or its length exceeds 4096
119
+ * bytes (Requirement 3.7). The bound is wide enough to admit any
120
+ * reasonable user-supplied password yet rejects payloads large
121
+ * enough to suggest accidental data-passing or a DoS attempt.
122
+ * - `InvalidInputError` with `code === 'INVALID_CHALLENGE'` —
123
+ * `challenge` is not a `Uint8Array(32)` (Requirement 3.6).
124
+ * - `RandomnessError` with `code === 'RNG_FAILURE'` — the underlying
125
+ * `randomBytes32()` threw or short-read, OR rejection sampling
126
+ * exhausted its 256-iteration bound (Requirement 3.10). No
127
+ * partial or zero-padded proof is emitted on this failure path.
128
+ *
129
+ * @param privateKey 32-byte little-endian encoding of a scalar in
130
+ * `[1, L)`. Never read after `x` is derived; the buffer is not
131
+ * wiped by this function (the caller owns its lifecycle).
132
+ * @param password Opaque bytes, length `[0, 4096]`. Validated for
133
+ * shape and then ignored.
134
+ * @param challenge 32-byte verifier-chosen challenge, ideally
135
+ * produced by `generateChallenge`.
136
+ * @returns A fresh 64-byte `Uint8Array` carrying `R_bytes || s_bytes`.
137
+ * @throws InvalidInputError When any input fails shape or range
138
+ * validation.
139
+ * @throws RandomnessError When the CSPRNG throws, returns a short
140
+ * read, or rejection sampling exhausts its iteration bound.
141
+ */
142
+ declare function computeProof(privateKey: Uint8Array, password: Uint8Array, challenge: Uint8Array): Uint8Array;
143
+
144
+ /**
145
+ * Verifies a 64-byte Schnorr proof against the registered `publicKey`
146
+ * and the verifier-chosen `challenge`.
147
+ *
148
+ * Returns `true` iff `proof = R_bytes || s_bytes` satisfies the
149
+ * non-interactive Schnorr equation
150
+ *
151
+ * s · G == R + c · publicKey
152
+ *
153
+ * with `c = int_LE(SHA-512(R_bytes || publicKey || challenge)) mod L`
154
+ * (the Fiat-Shamir scalar pinned in `transcript.ts`, identical to the
155
+ * one used by `compute-proof.ts`).
156
+ *
157
+ * Failure modes:
158
+ *
159
+ * - `InvalidInputError` with `code === 'INVALID_PUBLIC_KEY'` —
160
+ * `publicKey` is not a `Uint8Array(32)` (Requirement 4.5), OR it
161
+ * fails to decode as an Edwards point, OR it decodes to the
162
+ * identity point `O = (0, 1)`. The identity-point rejection is the
163
+ * one Requirement 4.5 specifically calls out: with `publicKey = O`,
164
+ * the verification equation collapses to `s · G == R`, which any
165
+ * forger can satisfy by picking any `s` and setting `R = s · G`.
166
+ * - `InvalidInputError` with `code === 'INVALID_CHALLENGE'` —
167
+ * `challenge` is not a `Uint8Array(32)` (Requirement 4.6).
168
+ * - `InvalidInputError` with `code === 'INVALID_PROOF'` — `proof` is
169
+ * not a `Uint8Array(64)` (Requirement 4.6, applied to the proof
170
+ * shape).
171
+ * - Returns `false` (does NOT throw) when:
172
+ * • `R_bytes` does not decode to a valid Edwards point
173
+ * (Requirement 4.7), OR
174
+ * • `s = int_LE(s_bytes) >= L` (Requirement 4.8), OR
175
+ * • the verification equation `s · G != R + c · publicKey` does
176
+ * not hold (Requirement 4.9, the standard "wrong proof"
177
+ * rejection).
178
+ *
179
+ * The silent-`false` returns are deliberate (design "Key design
180
+ * decisions → 5–6"): the verify path must NOT distinguish between
181
+ * "malformed proof material" and "well-formed but mathematically
182
+ * invalid proof" via thrown errors, since that would expose an
183
+ * oracle to a prover-side adversary.
184
+ *
185
+ * @param publicKey 32-byte Ed25519 point encoding of the registered
186
+ * public key. Must decode to a non-identity point.
187
+ * @param challenge 32-byte verifier-chosen challenge, ideally
188
+ * produced by `generateChallenge`.
189
+ * @param proof 64-byte proof `R_bytes || s_bytes` produced by
190
+ * `compute-proof.ts`.
191
+ * @returns `true` iff the proof satisfies the Schnorr verification
192
+ * equation under `(publicKey, challenge)`; `false` for any
193
+ * well-typed-but-invalid proof or attacker-tampered proof material.
194
+ * @throws InvalidInputError When any caller-supplied input fails
195
+ * shape, length, decoding, or identity-point validation.
196
+ */
197
+ declare function verifyProof(publicKey: Uint8Array, challenge: Uint8Array, proof: Uint8Array): boolean;
198
+
199
+ /**
200
+ * Stable, machine-readable identifiers attached to every thrown error.
201
+ *
202
+ * Callers are expected to pattern-match on `.code` rather than inspect
203
+ * `.message`, which is for human readers only. The set is closed: adding a
204
+ * new code is a breaking change to the public API surface.
205
+ *
206
+ * - `INVALID_PRIVATE_KEY` — privateKey shape, length, or scalar range invalid.
207
+ * - `INVALID_PUBLIC_KEY` — publicKey shape, length, decode, or identity-point.
208
+ * - `INVALID_CHALLENGE` — challenge shape or length invalid.
209
+ * - `INVALID_PROOF` — proof shape or length invalid (NOT verification failure).
210
+ * - `INVALID_PASSWORD` — password shape or length invalid.
211
+ * - `INVALID_SESSION_ID` — sessionId shape, empty, or oversize.
212
+ * - `RNG_FAILURE` — CSPRNG threw, returned short, or rejection-sampling exhausted.
213
+ * - `CURVE_ERROR` — `@noble/curves` raised an unexpected internal error.
214
+ */
215
+ type ErrorCode = 'INVALID_PRIVATE_KEY' | 'INVALID_PUBLIC_KEY' | 'INVALID_CHALLENGE' | 'INVALID_PROOF' | 'INVALID_PASSWORD' | 'INVALID_SESSION_ID' | 'RNG_FAILURE' | 'CURVE_ERROR';
216
+ /**
217
+ * Thrown when a public function receives an input that fails shape, length,
218
+ * encoding, or range validation. The accompanying `.code` indicates which
219
+ * input was invalid.
220
+ *
221
+ * `.name` is set as a readonly class field so it cannot be silently shadowed
222
+ * by user-land subclasses or by `Error`'s default `'Error'` value.
223
+ *
224
+ * @example
225
+ * try {
226
+ * computeProof(privateKey, password, challenge);
227
+ * } catch (e) {
228
+ * if (e instanceof InvalidInputError && e.code === 'INVALID_CHALLENGE') {
229
+ * // handle challenge-shape failure
230
+ * }
231
+ * }
232
+ */
233
+ declare class InvalidInputError extends Error {
234
+ /** Class name; fixed for all instances. */
235
+ readonly name = "InvalidInputError";
236
+ /** Stable, machine-readable identifier for the failing input. */
237
+ readonly code: ErrorCode;
238
+ /**
239
+ * @param code Stable identifier for the failing input.
240
+ * @param message Human-readable description; not part of the stable API.
241
+ */
242
+ constructor(code: ErrorCode, message: string);
243
+ }
244
+ /**
245
+ * Thrown when the underlying CSPRNG throws, returns a short read, or when
246
+ * bounded rejection sampling exhausts its iteration cap (treated as an RNG
247
+ * anomaly). The library MUST NOT emit a partial output on this failure path.
248
+ *
249
+ * `.code` is fixed to `'RNG_FAILURE'`. The optional `cause` carries the
250
+ * underlying error (e.g. the `node:crypto.randomBytes` throw) for diagnostics.
251
+ *
252
+ * `cause` is attached via a structural cast so the assignment works under
253
+ * any tsconfig `lib` selection that may or may not include
254
+ * `lib.es2022.error.d.ts` (per design.md).
255
+ */
256
+ declare class RandomnessError extends Error {
257
+ /** Class name; fixed for all instances. */
258
+ readonly name = "RandomnessError";
259
+ /** Stable, machine-readable identifier; fixed for this class. */
260
+ readonly code: ErrorCode;
261
+ /**
262
+ * @param message Human-readable description; not part of the stable API.
263
+ * @param options Optional bag carrying the underlying `cause`.
264
+ */
265
+ constructor(message: string, options?: {
266
+ cause?: unknown;
267
+ });
268
+ }
269
+ /**
270
+ * Thrown when an internal `@noble/curves` operation raises an unexpected
271
+ * error (e.g. invalid point encoding) on a code path where the library's
272
+ * contract is to throw rather than return `false`. The verification path's
273
+ * silent-`false` returns for malformed `R` and out-of-range `s` are
274
+ * deliberate exceptions to this rule (see design.md, Requirements 4.7, 4.8).
275
+ *
276
+ * `.code` is fixed to `'CURVE_ERROR'`. The optional `cause` carries the
277
+ * underlying noble error for diagnostics.
278
+ */
279
+ declare class CryptoError extends Error {
280
+ /** Class name; fixed for all instances. */
281
+ readonly name = "CryptoError";
282
+ /** Stable, machine-readable identifier; fixed for this class. */
283
+ readonly code: ErrorCode;
284
+ /**
285
+ * @param message Human-readable description; not part of the stable API.
286
+ * @param options Optional bag carrying the underlying `cause`.
287
+ */
288
+ constructor(message: string, options?: {
289
+ cause?: unknown;
290
+ });
291
+ }
292
+
293
+ export { CryptoError, type ErrorCode, InvalidInputError, RandomnessError, computeProof, generateChallenge, generateKeyPair, verifyProof };
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Generates a fresh `(privateKey, publicKey)` pair for the ZKP-auth
3
+ * scheme.
4
+ *
5
+ * The `privateKey` is a uniform 32-byte little-endian encoding of a
6
+ * scalar `n ∈ [1, L)`, drawn via bounded rejection sampling against
7
+ * the CSPRNG chokepoint `randomBytes32()`. The `publicKey` is the
8
+ * canonical 32-byte encoding of `n · G`, where `G` is the Ed25519
9
+ * base point.
10
+ *
11
+ * The two outputs are returned as fresh `Uint8Array` instances —
12
+ * `privateKey` is the accepted CSPRNG draw itself (a copy detached
13
+ * from Node's internal buffer pool, see `rng.ts`'s `Uint8Array.from`
14
+ * step), and `publicKey` is produced by `@noble/curves`'s `toBytes()`
15
+ * which allocates a fresh array. Callers may zero-fill the returned
16
+ * `privateKey` after use without affecting any other observer
17
+ * (Requirement 6.4 hygiene; not enforced by this function but
18
+ * permitted by its allocation contract).
19
+ *
20
+ * Failure modes — both surface as `RandomnessError` with
21
+ * `code === 'RNG_FAILURE'`, never as a partial or zero-padded result
22
+ * (Requirement 1.5):
23
+ *
24
+ * - The underlying `randomBytes32()` throws (CSPRNG anomaly or short
25
+ * read). Any error — whether a `RandomnessError` already produced
26
+ * by `rng.ts`, or a raw `Error` injected by tests via `vi.mock` —
27
+ * is caught and, if not already a `RandomnessError`, re-wrapped as
28
+ * one. This mirrors the defense-in-depth pattern in `compute-proof.ts`.
29
+ * - 256 successive draws all decode to scalars outside `[1, L)`. This
30
+ * is statistically impossible under a healthy CSPRNG (≈ `2^-252`
31
+ * per draw), so exhaustion is reported as an RNG failure rather
32
+ * than as a separate exhaustion-specific error class.
33
+ *
34
+ * @returns An object with `privateKey` (32 bytes, encoding a scalar
35
+ * in `[1, L)`) and `publicKey` (32 bytes, encoding `privateKey · G`).
36
+ * @throws RandomnessError When the CSPRNG fails or rejection sampling
37
+ * exhausts its 256-iteration bound.
38
+ */
39
+ declare function generateKeyPair(): {
40
+ privateKey: Uint8Array;
41
+ publicKey: Uint8Array;
42
+ };
43
+
44
+ /**
45
+ * Generates a fresh 32-byte challenge for a Schnorr-proof
46
+ * authentication session.
47
+ *
48
+ * The returned bytes are drawn from the OS CSPRNG via the library's
49
+ * single chokepoint (`randomBytes32`) and are statistically
50
+ * independent of `sessionId` — the parameter exists only so the
51
+ * caller's wire protocol can validate session-handle shape at a
52
+ * single, well-defined entry point. See the file-header comment for
53
+ * the security rationale (Requirement 2.5 / Property 4).
54
+ *
55
+ * The returned `Uint8Array` is a fresh allocation, detached from any
56
+ * internal CSPRNG buffer pool (see `rng.ts`'s `Uint8Array.from` step),
57
+ * so the caller may zero-fill it after use without affecting any
58
+ * other observer.
59
+ *
60
+ * Failure modes:
61
+ *
62
+ * - `InvalidInputError` with `code === 'INVALID_SESSION_ID'` — thrown
63
+ * when `sessionId` is not a `Uint8Array`, has length 0, or has
64
+ * length greater than 256 bytes. The 1..256-byte inclusive window
65
+ * is enforced by `assertUint8ArrayLengthBetween`.
66
+ * - `RandomnessError` with `code === 'RNG_FAILURE'` — thrown when
67
+ * `randomBytes32()` throws (any underlying CSPRNG fault), or when
68
+ * the returned buffer is not exactly 32 bytes. In production,
69
+ * `rng.ts` wraps both cases before they reach this function; the
70
+ * extra length guard and try/catch here are defense-in-depth for
71
+ * test-injected mocks (property-13, `vi.mock`). No partial or
72
+ * zero-padded challenge is ever returned (Requirement 2.4).
73
+ *
74
+ * @param sessionId Caller-supplied session handle. Validated for
75
+ * `Uint8Array` shape and a length in the inclusive range
76
+ * `[1, 256]`; never read after validation.
77
+ * @returns A fresh 32-byte CSPRNG-derived `Uint8Array`, independent
78
+ * of `sessionId`.
79
+ * @throws InvalidInputError When `sessionId` fails shape or length
80
+ * validation.
81
+ * @throws RandomnessError When the underlying CSPRNG throws or
82
+ * returns a short read.
83
+ */
84
+ declare function generateChallenge(sessionId: Uint8Array): Uint8Array;
85
+
86
+ /**
87
+ * Computes a 64-byte Schnorr proof of knowledge of `privateKey` over
88
+ * a verifier-chosen `challenge`, with `password` carried as opaque
89
+ * (and currently unused) metadata for forward-compatibility.
90
+ *
91
+ * The returned proof is `R_bytes || s_bytes` (32 bytes each), where
92
+ * `R = r · G` is the commitment to a fresh CSPRNG-drawn nonce
93
+ * `r ∈ [1, L)`, and `s = (r + c · x) mod L` is the response, with
94
+ * `c = int_LE(SHA-512(R || X || challenge)) mod L` and
95
+ * `x = int_LE(privateKey)` (Requirement 11.1: `x` is derived from
96
+ * `privateKey` only; `password` does NOT participate).
97
+ *
98
+ * The proof verifies under `verify-proof.ts`'s
99
+ * `s · G == R + c · publicKey` equation when invoked with the
100
+ * matching `publicKey = x · G` (Property 6 round-trip).
101
+ *
102
+ * `password` is validated for shape (Requirement 3.7) but is then
103
+ * treated as opaque bytes — it is NOT mixed into the scalar `x`, NOT
104
+ * folded into the Fiat-Shamir transcript, and NOT touched in any
105
+ * computation past validation (Requirements 3.3, 11.1; Property 10).
106
+ *
107
+ * Failure modes:
108
+ *
109
+ * - `InvalidInputError` with `code === 'INVALID_PRIVATE_KEY'` —
110
+ * `privateKey` is not a `Uint8Array(32)`, OR its little-endian
111
+ * decoding is `0`, OR its little-endian decoding is `≥ L`
112
+ * (Requirements 3.5, 11.4). The `≥ L` and `=== 0` checks are
113
+ * performed on the RAW decoding, not on `reduceScalar`'s output:
114
+ * `generateKeyPair` always produces in-range keys, so any
115
+ * out-of-range input is an integration error and we surface it
116
+ * verbatim rather than silently reduce.
117
+ * - `InvalidInputError` with `code === 'INVALID_PASSWORD'` —
118
+ * `password` is not a `Uint8Array`, or its length exceeds 4096
119
+ * bytes (Requirement 3.7). The bound is wide enough to admit any
120
+ * reasonable user-supplied password yet rejects payloads large
121
+ * enough to suggest accidental data-passing or a DoS attempt.
122
+ * - `InvalidInputError` with `code === 'INVALID_CHALLENGE'` —
123
+ * `challenge` is not a `Uint8Array(32)` (Requirement 3.6).
124
+ * - `RandomnessError` with `code === 'RNG_FAILURE'` — the underlying
125
+ * `randomBytes32()` threw or short-read, OR rejection sampling
126
+ * exhausted its 256-iteration bound (Requirement 3.10). No
127
+ * partial or zero-padded proof is emitted on this failure path.
128
+ *
129
+ * @param privateKey 32-byte little-endian encoding of a scalar in
130
+ * `[1, L)`. Never read after `x` is derived; the buffer is not
131
+ * wiped by this function (the caller owns its lifecycle).
132
+ * @param password Opaque bytes, length `[0, 4096]`. Validated for
133
+ * shape and then ignored.
134
+ * @param challenge 32-byte verifier-chosen challenge, ideally
135
+ * produced by `generateChallenge`.
136
+ * @returns A fresh 64-byte `Uint8Array` carrying `R_bytes || s_bytes`.
137
+ * @throws InvalidInputError When any input fails shape or range
138
+ * validation.
139
+ * @throws RandomnessError When the CSPRNG throws, returns a short
140
+ * read, or rejection sampling exhausts its iteration bound.
141
+ */
142
+ declare function computeProof(privateKey: Uint8Array, password: Uint8Array, challenge: Uint8Array): Uint8Array;
143
+
144
+ /**
145
+ * Verifies a 64-byte Schnorr proof against the registered `publicKey`
146
+ * and the verifier-chosen `challenge`.
147
+ *
148
+ * Returns `true` iff `proof = R_bytes || s_bytes` satisfies the
149
+ * non-interactive Schnorr equation
150
+ *
151
+ * s · G == R + c · publicKey
152
+ *
153
+ * with `c = int_LE(SHA-512(R_bytes || publicKey || challenge)) mod L`
154
+ * (the Fiat-Shamir scalar pinned in `transcript.ts`, identical to the
155
+ * one used by `compute-proof.ts`).
156
+ *
157
+ * Failure modes:
158
+ *
159
+ * - `InvalidInputError` with `code === 'INVALID_PUBLIC_KEY'` —
160
+ * `publicKey` is not a `Uint8Array(32)` (Requirement 4.5), OR it
161
+ * fails to decode as an Edwards point, OR it decodes to the
162
+ * identity point `O = (0, 1)`. The identity-point rejection is the
163
+ * one Requirement 4.5 specifically calls out: with `publicKey = O`,
164
+ * the verification equation collapses to `s · G == R`, which any
165
+ * forger can satisfy by picking any `s` and setting `R = s · G`.
166
+ * - `InvalidInputError` with `code === 'INVALID_CHALLENGE'` —
167
+ * `challenge` is not a `Uint8Array(32)` (Requirement 4.6).
168
+ * - `InvalidInputError` with `code === 'INVALID_PROOF'` — `proof` is
169
+ * not a `Uint8Array(64)` (Requirement 4.6, applied to the proof
170
+ * shape).
171
+ * - Returns `false` (does NOT throw) when:
172
+ * • `R_bytes` does not decode to a valid Edwards point
173
+ * (Requirement 4.7), OR
174
+ * • `s = int_LE(s_bytes) >= L` (Requirement 4.8), OR
175
+ * • the verification equation `s · G != R + c · publicKey` does
176
+ * not hold (Requirement 4.9, the standard "wrong proof"
177
+ * rejection).
178
+ *
179
+ * The silent-`false` returns are deliberate (design "Key design
180
+ * decisions → 5–6"): the verify path must NOT distinguish between
181
+ * "malformed proof material" and "well-formed but mathematically
182
+ * invalid proof" via thrown errors, since that would expose an
183
+ * oracle to a prover-side adversary.
184
+ *
185
+ * @param publicKey 32-byte Ed25519 point encoding of the registered
186
+ * public key. Must decode to a non-identity point.
187
+ * @param challenge 32-byte verifier-chosen challenge, ideally
188
+ * produced by `generateChallenge`.
189
+ * @param proof 64-byte proof `R_bytes || s_bytes` produced by
190
+ * `compute-proof.ts`.
191
+ * @returns `true` iff the proof satisfies the Schnorr verification
192
+ * equation under `(publicKey, challenge)`; `false` for any
193
+ * well-typed-but-invalid proof or attacker-tampered proof material.
194
+ * @throws InvalidInputError When any caller-supplied input fails
195
+ * shape, length, decoding, or identity-point validation.
196
+ */
197
+ declare function verifyProof(publicKey: Uint8Array, challenge: Uint8Array, proof: Uint8Array): boolean;
198
+
199
+ /**
200
+ * Stable, machine-readable identifiers attached to every thrown error.
201
+ *
202
+ * Callers are expected to pattern-match on `.code` rather than inspect
203
+ * `.message`, which is for human readers only. The set is closed: adding a
204
+ * new code is a breaking change to the public API surface.
205
+ *
206
+ * - `INVALID_PRIVATE_KEY` — privateKey shape, length, or scalar range invalid.
207
+ * - `INVALID_PUBLIC_KEY` — publicKey shape, length, decode, or identity-point.
208
+ * - `INVALID_CHALLENGE` — challenge shape or length invalid.
209
+ * - `INVALID_PROOF` — proof shape or length invalid (NOT verification failure).
210
+ * - `INVALID_PASSWORD` — password shape or length invalid.
211
+ * - `INVALID_SESSION_ID` — sessionId shape, empty, or oversize.
212
+ * - `RNG_FAILURE` — CSPRNG threw, returned short, or rejection-sampling exhausted.
213
+ * - `CURVE_ERROR` — `@noble/curves` raised an unexpected internal error.
214
+ */
215
+ type ErrorCode = 'INVALID_PRIVATE_KEY' | 'INVALID_PUBLIC_KEY' | 'INVALID_CHALLENGE' | 'INVALID_PROOF' | 'INVALID_PASSWORD' | 'INVALID_SESSION_ID' | 'RNG_FAILURE' | 'CURVE_ERROR';
216
+ /**
217
+ * Thrown when a public function receives an input that fails shape, length,
218
+ * encoding, or range validation. The accompanying `.code` indicates which
219
+ * input was invalid.
220
+ *
221
+ * `.name` is set as a readonly class field so it cannot be silently shadowed
222
+ * by user-land subclasses or by `Error`'s default `'Error'` value.
223
+ *
224
+ * @example
225
+ * try {
226
+ * computeProof(privateKey, password, challenge);
227
+ * } catch (e) {
228
+ * if (e instanceof InvalidInputError && e.code === 'INVALID_CHALLENGE') {
229
+ * // handle challenge-shape failure
230
+ * }
231
+ * }
232
+ */
233
+ declare class InvalidInputError extends Error {
234
+ /** Class name; fixed for all instances. */
235
+ readonly name = "InvalidInputError";
236
+ /** Stable, machine-readable identifier for the failing input. */
237
+ readonly code: ErrorCode;
238
+ /**
239
+ * @param code Stable identifier for the failing input.
240
+ * @param message Human-readable description; not part of the stable API.
241
+ */
242
+ constructor(code: ErrorCode, message: string);
243
+ }
244
+ /**
245
+ * Thrown when the underlying CSPRNG throws, returns a short read, or when
246
+ * bounded rejection sampling exhausts its iteration cap (treated as an RNG
247
+ * anomaly). The library MUST NOT emit a partial output on this failure path.
248
+ *
249
+ * `.code` is fixed to `'RNG_FAILURE'`. The optional `cause` carries the
250
+ * underlying error (e.g. the `node:crypto.randomBytes` throw) for diagnostics.
251
+ *
252
+ * `cause` is attached via a structural cast so the assignment works under
253
+ * any tsconfig `lib` selection that may or may not include
254
+ * `lib.es2022.error.d.ts` (per design.md).
255
+ */
256
+ declare class RandomnessError extends Error {
257
+ /** Class name; fixed for all instances. */
258
+ readonly name = "RandomnessError";
259
+ /** Stable, machine-readable identifier; fixed for this class. */
260
+ readonly code: ErrorCode;
261
+ /**
262
+ * @param message Human-readable description; not part of the stable API.
263
+ * @param options Optional bag carrying the underlying `cause`.
264
+ */
265
+ constructor(message: string, options?: {
266
+ cause?: unknown;
267
+ });
268
+ }
269
+ /**
270
+ * Thrown when an internal `@noble/curves` operation raises an unexpected
271
+ * error (e.g. invalid point encoding) on a code path where the library's
272
+ * contract is to throw rather than return `false`. The verification path's
273
+ * silent-`false` returns for malformed `R` and out-of-range `s` are
274
+ * deliberate exceptions to this rule (see design.md, Requirements 4.7, 4.8).
275
+ *
276
+ * `.code` is fixed to `'CURVE_ERROR'`. The optional `cause` carries the
277
+ * underlying noble error for diagnostics.
278
+ */
279
+ declare class CryptoError extends Error {
280
+ /** Class name; fixed for all instances. */
281
+ readonly name = "CryptoError";
282
+ /** Stable, machine-readable identifier; fixed for this class. */
283
+ readonly code: ErrorCode;
284
+ /**
285
+ * @param message Human-readable description; not part of the stable API.
286
+ * @param options Optional bag carrying the underlying `cause`.
287
+ */
288
+ constructor(message: string, options?: {
289
+ cause?: unknown;
290
+ });
291
+ }
292
+
293
+ export { CryptoError, type ErrorCode, InvalidInputError, RandomnessError, computeProof, generateChallenge, generateKeyPair, verifyProof };