Files

293 lines
13 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 TwoRound 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