@noble/curves
Version:
Audited & minimal JS implementation of elliptic curve cryptography
588 lines • 23.6 kB
TypeScript
import { type CHash, type HmacFn, type TArg, type TRet } from '../utils.ts';
import { type AffinePoint, type CurveLengths, type CurvePoint, type CurvePointCons } from './curve.ts';
import { type IField } from './modular.ts';
/** Shared affine point shape used by Weierstrass helpers. */
export type { AffinePoint };
type EndoBasis = [[bigint, bigint], [bigint, bigint]];
/**
* When Weierstrass curve has `a=0`, it becomes Koblitz curve.
* Koblitz curves allow using **efficiently-computable GLV endomorphism ψ**.
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%.
* For precomputed wNAF it trades off 1/2 init time & 1/3 ram for 20% perf hit.
*
* Endomorphism consists of beta, lambda and splitScalar:
*
* 1. GLV endomorphism ψ transforms a point: `P = (x, y) ↦ ψ(P) = (β·x mod p, y)`
* 2. GLV scalar decomposition transforms a scalar: `k ≡ k₁ + k₂·λ (mod n)`
* 3. Then these are combined: `k·P = k₁·P + k₂·ψ(P)`
* 4. Two 128-bit point-by-scalar multiplications + one point addition is faster than
* one 256-bit multiplication.
*
* where
* * beta: β ∈ Fₚ with β³ = 1, β ≠ 1
* * lambda: λ ∈ Fₙ with λ³ = 1, λ ≠ 1
* * splitScalar decomposes k ↦ k₁, k₂, by using reduced basis vectors.
* Gauss lattice reduction calculates them from initial basis vectors `(n, 0), (-λ, 0)`
*
* Check out `test/misc/endomorphism.js` and
* {@link https://gist.github.com/paulmillr/eb670806793e84df628a7c434a873066 | this endomorphism gist}.
*/
export type EndomorphismOpts = {
/** Cube root of unity used by the GLV endomorphism. */
beta: bigint;
/** Reduced lattice basis used for scalar splitting. */
basises?: EndoBasis;
/**
* Optional custom scalar-splitting helper.
* Receives one scalar and returns two half-sized scalar components.
*/
splitScalar?: (k: bigint) => {
k1neg: boolean;
k1: bigint;
k2neg: boolean;
k2: bigint;
};
};
/** Two half-sized scalar components returned by endomorphism splitting. */
export type ScalarEndoParts = {
/** Whether the first split scalar should be negated. */
k1neg: boolean;
/** Absolute value of the first split scalar. */
k1: bigint;
/** Whether the second split scalar should be negated. */
k2neg: boolean;
/** Absolute value of the second split scalar. */
k2: bigint;
};
/** Splits scalar for GLV endomorphism. */
export declare function _splitEndoScalar(k: bigint, basis: EndoBasis, n: bigint): ScalarEndoParts;
/**
* Option to enable hedged signatures with improved security.
*
* * Randomly generated k is bad, because broken CSPRNG would leak private keys.
* * Deterministic k (RFC6979) is better; but is suspectible to fault attacks.
*
* We allow using technique described in RFC6979 3.6: additional k', a.k.a. adding randomness
* to deterministic sig. If CSPRNG is broken & randomness is weak, it would STILL be as secure
* as ordinary sig without ExtraEntropy.
*
* * `true` means "fetch data, from CSPRNG, incorporate it into k generation"
* * `false` means "disable extra entropy, use purely deterministic k"
* * `Uint8Array` passed means "incorporate following data into k generation"
*
* See {@link https://paulmillr.com/posts/deterministic-signatures/ | deterministic signatures}.
*/
export type ECDSAExtraEntropy = boolean | Uint8Array;
/**
* - `compact` is the default format
* - `recovered` is the same as compact, but with an extra byte indicating recovery byte
* - `der` is ASN.1 DER encoding
*/
export type ECDSASignatureFormat = 'compact' | 'recovered' | 'der';
/**
* - `prehash`: (default: true) indicates whether to do sha256(message).
* When a custom hash is used, it must be set to `false`.
*/
export type ECDSARecoverOpts = {
/** Whether to hash the message before signature recovery. */
prehash?: boolean;
};
/**
* - `prehash`: (default: true) indicates whether to do sha256(message).
* When a custom hash is used, it must be set to `false`.
* - `lowS`: (default: true) prohibits signatures with `sig.s >= CURVE.n/2n`.
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
* which is default openssl behavior.
* Non-malleable signatures can still be successfully verified in openssl.
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
*/
export type ECDSAVerifyOpts = {
/** Whether to hash the message before verification. */
prehash?: boolean;
/** Whether to reject high-S signatures. */
lowS?: boolean;
/** Signature encoding to accept. */
format?: ECDSASignatureFormat;
};
/**
* - `prehash`: (default: true) indicates whether to do sha256(message).
* When a custom hash is used, it must be set to `false`.
* - `lowS`: (default: true) prohibits signatures with `sig.s >= CURVE.n/2n`.
* Compatible with BTC/ETH. Setting `lowS: false` allows to create malleable signatures,
* which is default openssl behavior.
* Non-malleable signatures can still be successfully verified in openssl.
* - `format`: (default: 'compact') 'compact' or 'recovered' with recovery byte
* - `extraEntropy`: (default: false) creates signatures with increased
* security, see {@link ECDSAExtraEntropy}
*/
export type ECDSASignOpts = {
/** Whether to hash the message before signing. */
prehash?: boolean;
/** Whether to normalize signatures into the low-S half-order. */
lowS?: boolean;
/** Signature encoding to produce. */
format?: ECDSASignatureFormat;
/** Optional hedging input for deterministic k generation. */
extraEntropy?: ECDSAExtraEntropy;
};
/** Projective XYZ point used by short Weierstrass curves. */
export interface WeierstrassPoint<T> extends CurvePoint<T, WeierstrassPoint<T>> {
/** projective X coordinate. Different from affine x. */
readonly X: T;
/** projective Y coordinate. Different from affine y. */
readonly Y: T;
/** projective z coordinate */
readonly Z: T;
/** affine x coordinate. Different from projective X. */
get x(): T;
/** affine y coordinate. Different from projective Y. */
get y(): T;
/**
* Encode the point into compressed or uncompressed SEC1 bytes.
* @param isCompressed - Whether to use the compressed form.
* @returns Encoded point bytes.
*/
toBytes(isCompressed?: boolean): TRet<Uint8Array>;
/**
* Encode the point into compressed or uncompressed SEC1 hex.
* @param isCompressed - Whether to use the compressed form.
* @returns Encoded point hex.
*/
toHex(isCompressed?: boolean): string;
}
/** Constructor and metadata helpers for Weierstrass points. */
export interface WeierstrassPointCons<T> extends CurvePointCons<WeierstrassPoint<T>> {
/** Does NOT validate if the point is valid. Use `.assertValidity()`. */
new (X: T, Y: T, Z: T): WeierstrassPoint<T>;
/**
* Return the curve parameters captured by this point constructor.
* @returns Curve parameters.
*/
CURVE(): WeierstrassOpts<T>;
}
/**
* Weierstrass curve options.
*
* * p: prime characteristic (order) of finite field, in which arithmetics is done
* * n: order of prime subgroup a.k.a total amount of valid curve points
* * h: cofactor, usually 1. h*n is group order; n is subgroup order
* * a: formula param, must be in field of p
* * b: formula param, must be in field of p
* * Gx: x coordinate of generator point a.k.a. base point
* * Gy: y coordinate of generator point
*/
export type WeierstrassOpts<T> = Readonly<{
/** Base-field modulus. */
p: bigint;
/** Prime subgroup order. */
n: bigint;
/** Curve cofactor. */
h: bigint;
/** Weierstrass curve parameter `a`. */
a: T;
/** Weierstrass curve parameter `b`. */
b: T;
/** Generator x coordinate. */
Gx: T;
/** Generator y coordinate. */
Gy: T;
}>;
/**
* Optional helpers and overrides for a Weierstrass point constructor.
*
* When a cofactor != 1, there can be effective methods to:
* 1. Determine whether a point is torsion-free
* 2. Clear torsion component
*/
export type WeierstrassExtraOpts<T> = Partial<{
/** Optional base-field override. */
Fp: IField<T>;
/** Optional scalar-field override. */
Fn: IField<bigint>;
/** Whether the point constructor accepts infinity points. */
allowInfinityPoint: boolean;
/** Optional GLV endomorphism data. */
endo: EndomorphismOpts;
/** Optional torsion-check override. */
isTorsionFree: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => boolean;
/** Optional cofactor-clearing override. */
clearCofactor: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>) => WeierstrassPoint<T>;
/** Optional custom point decoder. */
fromBytes: (bytes: TArg<Uint8Array>) => AffinePoint<T>;
/** Optional custom point encoder. */
toBytes: (c: WeierstrassPointCons<T>, point: WeierstrassPoint<T>, isCompressed: boolean) => TRet<Uint8Array>;
}>;
/**
* Options for ECDSA signatures over a Weierstrass curve.
*
* * lowS: (default: true) whether produced or verified signatures occupy the
* low half of `ecdsaOpts.n`. Prevents malleability.
* * hmac: (default: noble-hashes hmac) function, would be used to init hmac-drbg for k generation.
* * randomBytes: (default: webcrypto os-level CSPRNG) custom method for fetching secure randomness.
* * bits2int, bits2int_modN: used in sigs, sometimes overridden by curves. Custom hooks are
* treated as pure functions over validated bytes and MUST NOT mutate caller-owned buffers or
* closure-captured option bags. `bits2int_modN` must also return a canonical scalar in
* `[0..Point.Fn.ORDER-1]`.
*/
export type ECDSAOpts = Partial<{
/** Default low-S policy for this ECDSA instance. */
lowS: boolean;
/** HMAC implementation used by RFC6979 DRBG. */
hmac: HmacFn;
/** RNG override used by helper constructors. */
randomBytes: (bytesLength?: number) => TRet<Uint8Array>;
/** Hash-to-integer conversion override. */
bits2int: (bytes: TArg<Uint8Array>) => bigint;
/** Hash-to-integer-mod-n conversion override. Returns a canonical scalar in `[0..Fn.ORDER-1]`. */
bits2int_modN: (bytes: TArg<Uint8Array>) => bigint;
}>;
/** Elliptic Curve Diffie-Hellman helper namespace. */
export interface ECDH {
/**
* Generate a secret/public key pair.
* @param seed - Optional seed material.
* @returns Secret/public key pair.
*/
keygen: (seed?: TArg<Uint8Array>) => {
secretKey: TRet<Uint8Array>;
publicKey: TRet<Uint8Array>;
};
/**
* Derive the public key from a secret key.
* @param secretKey - Secret key bytes.
* @param isCompressed - Whether to emit compressed SEC1 bytes.
* @returns Encoded public key.
*/
getPublicKey: (secretKey: TArg<Uint8Array>, isCompressed?: boolean) => TRet<Uint8Array>;
/**
* Compute the shared secret point from a secret key and peer public key.
* @param secretKeyA - Local secret key bytes.
* @param publicKeyB - Peer public key bytes.
* @param isCompressed - Whether to emit compressed SEC1 bytes.
* @returns Encoded shared point.
*/
getSharedSecret: (secretKeyA: TArg<Uint8Array>, publicKeyB: TArg<Uint8Array>, isCompressed?: boolean) => TRet<Uint8Array>;
/** Point constructor used by this ECDH instance. */
Point: WeierstrassPointCons<bigint>;
/** Validation and random-key helpers. */
utils: {
/** Check whether a secret key has the expected encoding. */
isValidSecretKey: (secretKey: TArg<Uint8Array>) => boolean;
/** Check whether a public key decodes to a valid point. */
isValidPublicKey: (publicKey: TArg<Uint8Array>, isCompressed?: boolean) => boolean;
/** Generate a valid random secret key. */
randomSecretKey: (seed?: TArg<Uint8Array>) => TRet<Uint8Array>;
};
/** Byte lengths for keys and signatures exposed by this curve. */
lengths: CurveLengths;
}
/**
* ECDSA interface.
* Only supported for prime fields, not Fp2 (extension fields).
*/
export interface ECDSA extends ECDH {
/**
* Sign a message with the given secret key.
* @param message - Message bytes.
* @param secretKey - Secret key bytes.
* @param opts - Optional signing tweaks. See {@link ECDSASignOpts}.
* @returns Encoded signature bytes.
*/
sign: (message: TArg<Uint8Array>, secretKey: TArg<Uint8Array>, opts?: TArg<ECDSASignOpts>) => TRet<Uint8Array>;
/**
* Verify a signature against a message and public key.
* @param signature - Encoded signature bytes.
* @param message - Message bytes.
* @param publicKey - Encoded public key.
* @param opts - Optional verification tweaks. See {@link ECDSAVerifyOpts}.
* @returns Whether the signature is valid.
*/
verify: (signature: TArg<Uint8Array>, message: TArg<Uint8Array>, publicKey: TArg<Uint8Array>, opts?: TArg<ECDSAVerifyOpts>) => boolean;
/**
* Recover the public key encoded into a recoverable signature.
* @param signature - Recoverable signature bytes.
* @param message - Message bytes.
* @param opts - Optional recovery tweaks. See {@link ECDSARecoverOpts}.
* @returns Encoded recovered public key.
*/
recoverPublicKey(signature: TArg<Uint8Array>, message: TArg<Uint8Array>, opts?: TArg<ECDSARecoverOpts>): TRet<Uint8Array>;
/** Signature constructor and parser helpers. */
Signature: ECDSASignatureCons;
}
/**
* @param m - Error message.
* @example
* Throw a DER-specific error when signature parsing encounters invalid bytes.
*
* ```ts
* new DERErr('bad der');
* ```
*/
export declare class DERErr extends Error {
constructor(m?: string);
}
/** DER helper namespace used by ECDSA signature parsing and encoding. */
export type IDER = {
/**
* DER-specific error constructor.
* @param m - Error message.
* @returns DER-specific error instance.
*/
Err: typeof DERErr;
/** Low-level tag-length-value helpers used by DER encoders. */
_tlv: {
/**
* Encode one TLV record.
* @param tag - ASN.1 tag byte.
* @param data - Hex-encoded value payload.
* @returns Encoded TLV string.
*/
encode: (tag: number, data: string) => string;
/**
* Decode one TLV record and return the value plus leftover bytes.
* @param tag - Expected ASN.1 tag byte.
* @param data - Remaining DER bytes.
* @returns Parsed value plus leftover bytes.
*/
decode(tag: number, data: TArg<Uint8Array>): TRet<{
v: Uint8Array;
l: Uint8Array;
}>;
};
/** Positive-integer DER helpers used by ECDSA signature encoding. */
_int: {
/**
* Encode one positive bigint as a DER INTEGER.
* @param num - Positive integer to encode.
* @returns Encoded DER INTEGER.
*/
encode(num: bigint): string;
/**
* Decode one DER INTEGER into a bigint.
* @param data - DER INTEGER bytes.
* @returns Decoded bigint.
*/
decode(data: TArg<Uint8Array>): bigint;
};
/**
* Parse a DER signature into `{ r, s }`.
* @param bytes - DER signature bytes.
* @returns Parsed signature components.
*/
toSig(bytes: TArg<Uint8Array>): {
r: bigint;
s: bigint;
};
/**
* Encode `{ r, s }` as a DER signature.
* @param sig - Signature components.
* @returns DER-encoded signature hex.
*/
hexFromSig(sig: {
r: bigint;
s: bigint;
}): string;
};
/**
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
*
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
*
* Docs: {@link https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/ | Let's Encrypt ASN.1 guide} and
* {@link https://luca.ntop.org/Teaching/Appunti/asn1.html | Luca Deri's ASN.1 notes}.
* @example
* ASN.1 DER encoding utilities.
*
* ```ts
* const der = DER.hexFromSig({ r: 1n, s: 2n });
* ```
*/
export declare const DER: IDER;
/**
* Creates weierstrass Point constructor, based on specified curve options.
*
* See {@link WeierstrassOpts}.
* @param params - Curve parameters. See {@link WeierstrassOpts}.
* @param extraOpts - Optional helpers and overrides. See {@link WeierstrassExtraOpts}.
* @returns Weierstrass point constructor.
* @throws If the curve parameters, overrides, or point codecs are invalid. {@link Error}
*
* @example
* Construct a point type from explicit Weierstrass curve parameters.
*
* ```js
* const opts = {
* p: 0xfffffffffffffffffffffffffffffffeffffac73n,
* n: 0x100000000000000000001b8fa16dfab9aca16b6b3n,
* h: 1n,
* a: 0n,
* b: 7n,
* Gx: 0x3b4c382ce37aa192a4019e763036f4f5dd4d7ebbn,
* Gy: 0x938cf935318fdced6bc28286531733c3f03c4feen,
* };
* const secp160k1_Point = weierstrass(opts);
* ```
*/
export declare function weierstrass<T>(params: WeierstrassOpts<T>, extraOpts?: WeierstrassExtraOpts<T>): WeierstrassPointCons<T>;
/** Parsed ECDSA signature with helpers for recovery and re-encoding. */
export interface ECDSASignature {
/** Signature component `r`. */
readonly r: bigint;
/** Signature component `s`. */
readonly s: bigint;
/** Optional recovery bit for recoverable signatures. */
readonly recovery?: number;
/**
* Return a copy of the signature with a recovery bit attached.
* @param recovery - Recovery bit to attach.
* @returns Signature with an attached recovery bit.
*/
addRecoveryBit(recovery: number): ECDSASignature & {
readonly recovery: number;
};
/**
* Check whether the signature uses the high-S half-order.
* @returns Whether the signature uses the high-S half-order.
*/
hasHighS(): boolean;
/**
* Recover the public key from the hashed message and recovery bit.
* @param messageHash - Hashed message bytes.
* @returns Recovered public-key point.
*/
recoverPublicKey(messageHash: TArg<Uint8Array>): WeierstrassPoint<bigint>;
/**
* Encode the signature into bytes.
* @param format - Signature encoding to produce.
* @returns Encoded signature bytes.
*/
toBytes(format?: string): TRet<Uint8Array>;
/**
* Encode the signature into hex.
* @param format - Signature encoding to produce.
* @returns Encoded signature hex.
*/
toHex(format?: string): string;
}
/** Constructor and decoding helpers for ECDSA signatures. */
export type ECDSASignatureCons = {
/** Create a signature from `r`, `s`, and an optional recovery bit. */
new (r: bigint, s: bigint, recovery?: number): ECDSASignature;
/**
* Decode a signature from bytes.
* @param bytes - Encoded signature bytes.
* @param format - Signature encoding to parse.
* @returns Parsed signature.
*/
fromBytes(bytes: TArg<Uint8Array>, format?: ECDSASignatureFormat): ECDSASignature;
/**
* Decode a signature from hex.
* @param hex - Encoded signature hex.
* @param format - Signature encoding to parse.
* @returns Parsed signature.
*/
fromHex(hex: string, format?: ECDSASignatureFormat): ECDSASignature;
};
/**
* Implementation of the Shallue and van de Woestijne method for any weierstrass curve.
* TODO: check if there is a way to merge this with uvRatio in Edwards; move to modular.
* b = True and y = sqrt(u / v) if (u / v) is square in F, and
* b = False and y = sqrt(Z * (u / v)) otherwise.
* RFC 9380 expects callers to provide `v != 0`; this helper does not enforce it.
* @param Fp - Field implementation.
* @param Z - Simplified SWU map parameter.
* @returns Square-root ratio helper.
* @example
* Build the square-root ratio helper used by SWU map implementations.
*
* ```ts
* import { SWUFpSqrtRatio } from '@noble/curves/abstract/weierstrass.js';
* import { Field } from '@noble/curves/abstract/modular.js';
* const Fp = Field(17n);
* const sqrtRatio = SWUFpSqrtRatio(Fp, 3n);
* const out = sqrtRatio(4n, 1n);
* ```
*/
export declare function SWUFpSqrtRatio<T>(Fp: TArg<IField<T>>, Z: T): (u: T, v: T) => {
isValid: boolean;
value: T;
};
/**
* Simplified Shallue-van de Woestijne-Ulas Method
* See {@link https://www.rfc-editor.org/rfc/rfc9380#section-6.6.2 | RFC 9380 section 6.6.2}.
* @param Fp - Field implementation.
* @param opts - SWU parameters:
* - `A`: Curve parameter `A`.
* - `B`: Curve parameter `B`.
* - `Z`: Simplified SWU map parameter.
* @returns Deterministic map-to-curve function.
* @throws If the SWU parameters are invalid or the field lacks the required helpers. {@link Error}
* @example
* Map one field element to a Weierstrass curve point with the SWU recipe.
*
* ```ts
* import { mapToCurveSimpleSWU } from '@noble/curves/abstract/weierstrass.js';
* import { Field } from '@noble/curves/abstract/modular.js';
* const Fp = Field(17n);
* const map = mapToCurveSimpleSWU(Fp, { A: 1n, B: 2n, Z: 3n });
* const point = map(5n);
* ```
*/
export declare function mapToCurveSimpleSWU<T>(Fp: TArg<IField<T>>, opts: {
A: T;
B: T;
Z: T;
}): (u: T) => {
x: T;
y: T;
};
/**
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
* This helper ensures no signature functionality is present. Less code, smaller bundle size.
* @param Point - Weierstrass point constructor.
* @param ecdhOpts - Optional randomness helpers:
* - `randomBytes` (optional): Optional RNG override.
* @returns ECDH helper namespace.
* @example
* Sometimes users only need getPublicKey, getSharedSecret, and secret key handling.
*
* ```ts
* import { ecdh } from '@noble/curves/abstract/weierstrass.js';
* import { p256 } from '@noble/curves/nist.js';
* const dh = ecdh(p256.Point);
* const alice = dh.keygen();
* const shared = dh.getSharedSecret(alice.secretKey, alice.publicKey);
* ```
*/
export declare function ecdh(Point: WeierstrassPointCons<bigint>, ecdhOpts?: TArg<{
randomBytes?: (bytesLength?: number) => TRet<Uint8Array>;
}>): ECDH;
/**
* Creates ECDSA signing interface for given elliptic curve `Point` and `hash` function.
*
* @param Point - created using {@link weierstrass} function
* @param hash - used for 1) message prehash-ing 2) k generation in `sign`, using hmac_drbg(hash)
* @param ecdsaOpts - rarely needed, see {@link ECDSAOpts}:
* - `lowS`: Default low-S policy.
* - `hmac`: HMAC implementation used by RFC6979 DRBG.
* - `randomBytes`: Optional RNG override.
* - `bits2int`: Optional hash-to-int conversion override.
* - `bits2int_modN`: Optional hash-to-int-mod-n conversion override.
*
* @returns ECDSA helper namespace.
* @example
* Create an ECDSA signer/verifier bundle for one curve implementation.
*
* ```ts
* import { ecdsa } from '@noble/curves/abstract/weierstrass.js';
* import { p256 } from '@noble/curves/nist.js';
* import { sha256 } from '@noble/hashes/sha2.js';
* const p256ecdsa = ecdsa(p256.Point, sha256);
* const { secretKey, publicKey } = p256ecdsa.keygen();
* const msg = new TextEncoder().encode('hello noble');
* const sig = p256ecdsa.sign(msg, secretKey);
* const isValid = p256ecdsa.verify(sig, msg, publicKey);
* ```
*/
export declare function ecdsa(Point: WeierstrassPointCons<bigint>, hash: TArg<CHash>, ecdsaOpts?: TArg<ECDSAOpts>): ECDSA;
//# sourceMappingURL=weierstrass.d.ts.map