UNPKG

@noble/curves

Version:

Audited & minimal JS implementation of elliptic curve cryptography

293 lines 13.1 kB
import { randomBytes, type TArg, type TRet } from '../utils.ts'; import { type CurvePoint, type CurvePointCons } from './curve.ts'; import { type H2CDSTOpts } from './hash-to-curve.ts'; import { type IField } from './modular.ts'; export type RNG = typeof randomBytes; export type Identifier = string; export type Commitment = Uint8Array; export type Coefficient = Uint8Array; export type Signature = Uint8Array; export type Signers = { min: number; max: number; }; export type SecretKey = Uint8Array; export type Bytes = Uint8Array; type Point = Uint8Array; export type DKG_Round1 = { identifier: Identifier; commitment: TRet<Commitment[]>; proofOfKnowledge: TRet<Signature>; }; export type DKG_Round2 = { identifier: Identifier; signingShare: TRet<Bytes>; }; export type DKG_Secret = { identifier: bigint; coefficients?: bigint[]; commitment: TRet<Point[]>; signers: Signers; step?: 1 | 2 | 3; }; export type FrostPublic = { signers: Signers; commitments: TRet<Bytes[]>; verifyingShares: TRet<Record<Identifier, Bytes>>; }; export type FrostSecret = { identifier: Identifier; signingShare: TRet<Bytes>; }; export type Key = { public: FrostPublic; secret: FrostSecret; }; export type DealerShares = { public: FrostPublic; secretShares: Record<Identifier, FrostSecret>; }; export type Nonces = { hiding: TRet<Bytes>; binding: TRet<Bytes>; }; export type NonceCommitments = { identifier: Identifier; hiding: TRet<Bytes>; binding: TRet<Bytes>; }; export type GenNonce = { nonces: Nonces; commitments: NonceCommitments; }; export interface FROSTPoint<T extends CurvePoint<any, T>> extends CurvePoint<any, T> { add(rhs: T): T; multiply(rhs: bigint): T; equals(rhs: T): boolean; toBytes(compressed?: boolean): Bytes; clearCofactor(): T; } export interface FROSTPointConstructor<T extends FROSTPoint<T>> extends CurvePointCons<T> { fromBytes(a: Bytes): T; Fn: IField<bigint>; } export type FrostOpts<P extends FROSTPoint<P>> = { readonly name: string; readonly Point: FROSTPointConstructor<P>; readonly Fn?: IField<bigint>; /** Optional suite hook that tightens canonical decoding with subgroup / identity checks. */ readonly validatePoint?: (p: P) => void; /** Optional public-key parser. Implementations MUST preserve the same subgroup / identity policy * as `validatePoint`, because this bypasses generic canonical decoding in `parsePoint()`. */ readonly parsePublicKey?: (bytes: TArg<Uint8Array>) => P; readonly hash: (msg: TArg<Uint8Array>) => TRet<Uint8Array>; /** Custom scalar hash hook. Implementations MUST treat `msg` and `options` as read-only. */ readonly hashToScalar?: (msg: TArg<Uint8Array>, options?: TArg<H2CDSTOpts>) => bigint; readonly adjustScalar?: (n: bigint) => bigint; readonly adjustPoint?: (n: P) => P; readonly challenge?: (R: P, PK: P, msg: TArg<Uint8Array>) => bigint; readonly adjustNonces?: (PK: P, nonces: TArg<Nonces>) => TRet<Nonces>; readonly adjustSecret?: (secret: TArg<FrostSecret>, pub: TArg<FrostPublic>) => TRet<FrostSecret>; readonly adjustPublic?: (pub: TArg<FrostPublic>) => TRet<FrostPublic>; readonly adjustGroupCommitmentShare?: (GC: P, GCShare: P) => P; readonly adjustTx?: { readonly encode: (tx: TArg<Uint8Array>) => TRet<Uint8Array>; readonly decode: (tx: TArg<Uint8Array>) => TRet<Uint8Array>; }; readonly adjustDKG?: (k: TArg<Key>) => TRet<Key>; readonly H1?: string; readonly H2?: string; readonly H3?: string; readonly H4?: string; readonly H5?: string; readonly HDKG?: string; readonly HID?: string; }; /** * FROST: Threshold Protocol for Two‑Round Schnorr Signatures * from [RFC 9591](https://datatracker.ietf.org/doc/rfc9591/). */ export type FROST = { /** * Methods to construct participant identifiers. */ Identifier: { /** * Constructs an identifier from a numeric index. * @param n - A positive integer. * @returns A canonical serialized Identifier. */ fromNumber(n: number): Identifier; /** * Derives an identifier deterministically from a string (e.g. an email). * @param s - Arbitrary string. * @returns A canonical serialized Identifier. */ derive(s: string): Identifier; }; /** * Distributed Key Generation (DKG) protocol interface. * RFC 9591 leaves DKG out of scope; Appendix C only specifies dealer/VSS key generation. * These helpers follow the split-round API used by frost-rs for interoperable testing. */ DKG: { /** * Generates the first round of DKG. * @param id - Participant's identifier. * @param signers - Set of all participants (min/max threshold). * @param secret - Optional initial secret scalar. * @param rng - Optional RNG for nonce generation. * @returns Public broadcast and private DKG state. The returned `secret` package is mutable * round state that will be consumed by `round2()` and `round3()`. */ round1: (id: Identifier, signers: Signers, secret?: TArg<SecretKey>, rng?: RNG) => { public: DKG_Round1; secret: DKG_Secret; }; /** * Executes DKG round 2 given public round1 data from others. * @param secret - Private DKG state from round1. This mutates `secret.step` in place. * @param others - Public round1 broadcasts from other participants. * @returns A map of round2 messages to be sent to others. */ round2: (secret: TArg<DKG_Secret>, others: TArg<DKG_Round1[]>) => TRet<Record<string, DKG_Round2>>; /** * Finalizes key generation in round3 using received round1 + round2 messages. * @param secret - Private DKG state. This consumes the remaining local polynomial coefficients * and transitions the package to its final post-round3 state. * @param round1 - Public round1 broadcasts from all participants. * @param round2 - Round2 messages received from others. * @returns Final secret/public key information for the participant. * Callers MUST pass the same verified remote `round1` package set that was already * accepted in `round2()`, rather than re-fetching or rebuilding it from the network. */ round3: (secret: TArg<DKG_Secret>, round1: TArg<DKG_Round1[]>, round2: TArg<DKG_Round2[]>) => TRet<Key>; /** * Best-effort erasure of internal secret state. Bigint/JIT copies may still survive outside the * local object even after cleanup. * @param secret - Private DKG state from round1. */ clean(secret: TArg<DKG_Secret>): void; }; /** * Trusted dealer mode: generates key shares from a central trusted authority. * Mirrors RFC 9591 Appendix C and returns one shared VSS commitment package * plus per-participant shares. * @param signers - Threshold parameters (min/max). * @param identifiers - Optional explicit participant list. * @param secret - Optional secret scalar. * @param rng - Optional RNG. * @returns One shared public package plus the participant secret-share packages. */ trustedDealer(signers: Signers, identifiers?: Identifier[], secret?: TArg<SecretKey>, rng?: RNG): TRet<DealerShares>; /** * Validates the consistency of a secret share against the shared public commitments. * This is the RFC 9591 Appendix C.2 `vss_verify` check against the shared dealer/DKG commitment. * It does not relax RFC 9591 Section 3.1: public identity elements are still invalid even when * the scalar/share algebra would otherwise be self-consistent. * Throws if invalid. * @param secret - A FrostSecret containing identifier and signing share. * @param pub - Shared public package containing commitments. */ validateSecret(secret: TArg<FrostSecret>, pub: TArg<FrostPublic>): void; /** * Produces nonces and public commitments used in signing. * RFC 9591 Section 5.1 `commit()`. * @param secret - Participant's secret share. * @param rng - Optional RNG. * @returns Nonce values and their public commitments. * Returned nonces are one-time-use and MUST NOT be reused across signing sessions. * This API does not mutate or zeroize caller-owned nonce objects. */ commit(secret: TArg<FrostSecret>, rng?: RNG): TRet<GenNonce>; /** * Signs a message using the participant's secret and nonce. * @param secret - Participant's secret share. * @param pub - Shared public package containing commitments. * @param nonces - Participant's nonce pair. * @param commitmentList - Commitments from all signing participants. * @param msg - Message to be signed. * @returns Signature share as a byte array. * RFC 9591 Sections 4.1/5.1 require round-one commitments to be one-time-use, and * Section 5.2 signs with the nonce corresponding to that published commitment. * The caller MUST pass fresh nonces from `commit()`. On successful signing, this helper * consumes the caller-owned nonce object by zeroing both nonce byte arrays in place. * Later calls reject an all-zero nonce package, so same-object reuse fails closed and an * accidentally generated zero nonce package is not silently used for signing. */ signShare(secret: TArg<FrostSecret>, pub: TArg<FrostPublic>, nonces: TArg<Nonces>, commitmentList: TArg<NonceCommitments[]>, msg: TArg<Uint8Array>): TRet<Uint8Array>; /** * Verifies a signature share against public commitments. * Matches the coordinator-side individual-share verification from RFC 9591 Section 5.4. * @param pub - Group public key information. * @param commitmentList - Commitments from all signing participants. * @param msg - Message being signed. * @param identifier - Identifier of the signer whose share is being verified. * @param sigShare - Signature share to verify. * @returns True if valid, false otherwise. */ verifyShare(pub: TArg<FrostPublic>, commitmentList: TArg<NonceCommitments[]>, msg: TArg<Uint8Array>, identifier: Identifier, sigShare: TArg<Uint8Array>): boolean; /** * Aggregates signature shares into a full signature. * RFC 9591 Section 5.3 `aggregate()`. * @param pub - Group public key. * @param commitmentList - Nonce commitments from all signers. * @param msg - Message to sign. * @param sigShares - Map from identifier to their signature share. * @returns Final aggregated signature. */ aggregate(pub: TArg<FrostPublic>, commitmentList: TArg<NonceCommitments[]>, msg: TArg<Uint8Array>, sigShares: TArg<Record<Identifier, Uint8Array>>): TRet<Uint8Array>; /** * Signs a message using a raw secret key (e.g. from combineSecret). * @param msg - Message to sign. * @param secretKey - Group secret key as bytes. * @returns Signature bytes. */ sign(msg: TArg<Uint8Array>, secretKey: TArg<Uint8Array>): TRet<Uint8Array>; /** * Verifies a full signature against the group public key. * @param sig - Signature bytes. * @param msg - Message that was signed. * @param publicKey - Group public key. * @returns True if valid, false otherwise. */ verify(sig: TArg<Signature>, msg: TArg<Uint8Array>, publicKey: TArg<Uint8Array>): boolean; /** * Combines multiple secret shares into a single secret key (e.g. for recovery). * @param shares - Set of FrostSecret shares. * @param signers - Threshold parameters. * @returns Group secret key as bytes. */ combineSecret(shares: TArg<FrostSecret[]>, signers: Signers): TRet<Uint8Array>; /** * Low-level helper utilities (field arithmetic and polynomial tools). */ utils: { /** * Finite field used for scalars. */ Fn: IField<bigint>; /** * Generates a random scalar (private key). * @param rng - Optional RNG source. * @returns Scalar as 32-byte Uint8Array. */ randomScalar: (rng?: RNG) => TRet<Uint8Array>; /** * Generates a secret-sharing polynomial and its public commitments. * @param signers - Threshold parameters. * @param secret - Optional initial secret scalar. * @param coeffs - Optional manual coefficients. * @param rng - Optional RNG. * @returns Polynomial coefficients, commitments, and secret value. */ generateSecretPolynomial: (signers: Signers, secret?: TArg<Uint8Array>, coeffs?: bigint[], rng?: RNG) => { coefficients: bigint[]; commitment: TRet<Point[]>; secret: bigint; }; }; }; export declare function createFROST<P extends FROSTPoint<P>>(opts: FrostOpts<P>): TRet<FROST>; export {}; //# sourceMappingURL=frost.d.ts.map