import { isInitialized } from '../init.js'; import type { WasmSource } from '../wasm-source.js'; import type { EcdsaP256KeyPair } from './types.js'; /** * Initialise the p256 WASM module. Loads the underlying binary * (scalar, no SIMD) into the `p256` slot. */ export declare function ecdsaP256Init(source: WasmSource): Promise; export type { WasmSource }; export type { EcdsaP256KeyPair, EcdsaP256Exports } from './types.js'; export { isInitialized }; export { encodeEcPrivateKey, decodeEcPrivateKey } from './ecprivatekey-der.js'; /** * Decompress a 33-byte SEC 1 §2.3.3 compressed P-256 public key to the * 65-byte SEC 1 §2.3.4 uncompressed encoding `0x04 || X || Y`. * * The compressed form encodes only the affine x coordinate plus a * single parity bit (in the prefix byte: 0x02 even-y, 0x03 odd-y). * Recovery of y solves the curve equation * `y² = x³ - 3x + b mod p` (SP 800-186 §3.2.1.3, P-256 has a = -3) * and selects the y root whose parity matches the prefix. The * substrate runs the modular square root inside the p256 WASM * (`feSqrt` via the p ≡ 3 (mod 4) shortcut, x^((p+1)/4)); rejecting * invalid inputs that have no square root or whose recovered (x, y) * lies off-curve. * * Rejection cases (all throw `SigningError('sig-malformed-input')`): * - prefix byte not in {0x02, 0x03} * - x coordinate is not the x of any on-curve point (no quadratic * residue exists for `x³ - 3x + b mod p`) * * Length / shape rejections throw `TypeError` / `RangeError` per the * usual leviathan-crypto contract-violation posture. * * Requires `init({ p256: ... })`. Uses the same p256 module singleton * as `EcdsaP256`; concurrency-safe alongside non-stateful uses (the * `_assertNotOwned` check fires if a stateful instance is holding * the module). * * @param pk33 33-byte compressed pk per SEC 1 §2.3.3 * @returns 65-byte uncompressed pk per SEC 1 §2.3.4 (0x04 || X || Y) */ export declare function pointDecompress(pk33: Uint8Array): Uint8Array; export declare class EcdsaP256 { constructor(); private get mx(); /** * Deterministic ECDSA-P256 key generation from a 32-byte seed. * d = seed mod n per FIPS 186-5 §A.4.2 (testing-candidates style, * single candidate). pk = [d]G compressed to 33 bytes per SEC 1 * §2.3.3. The vanishingly rare seed mod n == 0 case traps in the * WASM and surfaces as a SigningError here. * * @param seed 32-byte BE input * @returns 33-byte compressed pk and a fresh 32-byte copy of the * secret scalar d (sk === seed for this derivation, the * caller may use either as the private value). */ keygenDerand(seed: Uint8Array): EcdsaP256KeyPair; /** Random ECDSA-P256 key generation, wraps `keygenDerand` with `randomBytes(32)`. */ keygen(): EcdsaP256KeyPair; /** * Key generation that returns the public key in the 65-byte SEC 1 * §2.3.4 uncompressed encoding `0x04 || X || Y`, rather than the * 33-byte compressed form `keygen` / `keygenDerand` return. The * secret-key half is the same 32-byte raw scalar `d`. * * Internally runs `keygen` (or `keygenDerand` if a seed is supplied) * to obtain the compressed pk, then `pointDecompress` to expand it. * The compressed intermediate is wiped before return. * * @param seed Optional 32-byte seed; passes through to `keygenDerand` * when present, falls back to `keygen` (CSPRNG seed) when * omitted. */ keygenUncompressed(seed?: Uint8Array): EcdsaP256KeyPair; /** * Hedged-or-deterministic ECDSA-P256 sign per FIPS 186-5 §6.4 with * RFC 6979 §3.5 low-S normalisation. The K nonce is derived per * RFC 6979 §3.2 (deterministic) when `rnd` is all-zero, or per * draft-irtf-cfrg-det-sigs-with-noise-05 (hedged) otherwise. The * hedged path is the recommended default; pass `randomBytes(32)`. * * The WASM re-derives pk = [d]G internally and compares it against * the caller-supplied `pk`. A mismatch traps via `unreachable` and * is rethrown as `SigningError('sig-malformed-input')`. This * defends against fault injection that would bias the per-signature * randomness derivation by forcing the caller to also know pk. * * @param sk 32-byte secret scalar d * @param pk 33-byte compressed or 65-byte uncompressed pk; * cross-checked by WASM after derivation * @param msgHash 32-byte SHA-256(M) digest (caller-computed) * @param rnd 32-byte per-call entropy Z; all-zero selects * deterministic RFC 6979 §3.2, non-zero selects * the hedged path * @returns 64-byte raw r || s signature, low-S normalised * @throws SigningError('sig-malformed-input') on pk-mismatch * (fault-injection trap) */ sign(sk: Uint8Array, pk: Uint8Array, msgHash: Uint8Array, rnd: Uint8Array): Uint8Array; /** * Suite-only: hedged-or-deterministic sign that derives pk * internally and skips the fault-injection cross-check. See * AGENTS.md "SignatureSuite lifecycle". Underscore-prefixed, * not part of the public API. */ _signInternalPk(sk: Uint8Array, msgHash: Uint8Array, rnd: Uint8Array): Uint8Array; /** * Strict ECDSA-P256 verify per FIPS 186-5 §6.5 with low-S * enforcement (RFC 6979 §3.5). Returns `true` on success, `false` * on every signature failure mode: off-curve / identity pk, r or * s out of [1, n-1], high-S, or the signature equation failing. * Throws only on caller-side contract violations (wrong-length * inputs). * * @param pk 33-byte compressed or 65-byte uncompressed pk * @param msgHash 32-byte SHA-256(M) digest * @param sig 64-byte raw r || s (use `ecdsaSignatureFromDer` * to convert DER-encoded signatures first) */ verify(pk: Uint8Array, msgHash: Uint8Array, sig: Uint8Array): boolean; dispose(): void; }