primitives

package
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 4, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package primitives provides the curve abstraction Lens uses for all elliptic-curve-based threshold operations.

The package is deliberately small. Three concrete curves are shipped:

  • Ed25519 — RFC 8032 / FIPS 186-5 EdDSA curve (filippo.io/edwards25519)
  • secp256k1 — SEC 2 / Bitcoin curve (decred/dcrd/dcrec/secp256k1)
  • Ristretto255 — RFC 9496 prime-order group over Curve25519 (gtank/ristretto255)

All three implement the Curve interface below. Two curves never share Scalar / Point types — each curve owns its concrete types and casts internally; the interfaces exist so generic threshold code can treat them uniformly.

Why a separate curve package

Lens deliberately does NOT depend on github.com/luxfi/threshold for curve operations: the Lens kernel must be importable by the LSS-Lens adapter inside the threshold module without creating a circular dependency. The curve interface is small enough that re-implementing it costs less than the dependency tangle.

No bias, no leakage

Scalar sampling rejects until the drawn bytes interpret to a value in [0, order). Point operations never panic on invalid inputs — they return errors that propagate to the protocol layer.

Index

Constants

This section is empty.

Variables

View Source
var ErrCommitMismatch = errors.New("lens/primitives: Pedersen commitment verification failed")

ErrCommitMismatch is returned when a (share, blind) pair fails the Pedersen verification equation.

View Source
var ErrShamirInsufficient = errors.New("lens/primitives: insufficient shares for Shamir recovery")

ErrShamirInsufficient is returned when fewer than `threshold` shares are supplied to a reconstruction routine.

Functions

func HashPoint

func HashPoint(tag string, p Point) [32]byte

HashPoint returns BLAKE3-32 over a domain-separated tag and the canonical encoding of `p`. Used as a stable identifier for a curve-point in transcript hashing.

func HashPoints

func HashPoints(tag string, ps ...Point) [32]byte

HashPoints returns BLAKE3-32 over a domain-separated tag and the canonical encoding of every point in `ps`, length-prefixed for unambiguous parsing.

func Lagrange

func Lagrange(curve Curve, ids []int) (map[int]Scalar, error)

Lagrange returns the Lagrange basis coefficients λ_i^T evaluated at X = 0 for the set of one-indexed party IDs T:

λ_i = Π_{j ∈ T, j ≠ i} (-x_j) / (x_i - x_j)

where x_k is the scalar representation of party ID k. Used to reconstruct s = Σ_i λ_i · s_i over the curve.

IDs MUST be distinct, non-zero, and < group order.

func LagrangeAt

func LagrangeAt(curve Curve, ids []int, x Scalar) (map[int]Scalar, error)

LagrangeAt returns Lagrange coefficients evaluated at an arbitrary scalar `x` (rather than at 0). Used during VSR to evaluate the composite polynomial G(x_j) = Σ_i λ_i · g_i(x_j).

func SortedInts

func SortedInts(ids []int) []int

SortedInts returns a copy of `ids` sorted ascending.

func VerifyPedersenShare

func VerifyPedersenShare(
	curve Curve,
	share, blind Scalar,
	commits []Point,
	x Scalar,
) error

VerifyPedersenShare checks the commitment-equation

share·G + blind·H ?= Σ_{k=0..t-1} x^k · C_k

where (share, blind) is what the recipient at evaluation point x received from the dealer, and the C_k are the dealer's broadcast Pedersen commits to its (secretCoeffs, blindCoeffs). Returns nil if the share is consistent; ErrCommitMismatch otherwise.

Types

type Curve

type Curve interface {
	// Name returns the curve identifier (e.g. "ed25519",
	// "secp256k1", "ristretto255"). Used in transcript binding and
	// activation cert serialization. MUST be unique across curves.
	Name() string

	// ScalarBytes returns the canonical byte length of an encoded
	// Scalar.
	ScalarBytes() int

	// PointBytes returns the canonical byte length of an encoded Point.
	PointBytes() int

	// NewScalar returns the additive identity (zero) Scalar.
	NewScalar() Scalar

	// NewPoint returns the identity Point.
	NewPoint() Point

	// BasePoint returns the canonical generator G of the prime-order
	// subgroup of the curve.
	BasePoint() Point

	// SecondaryGenerator returns the canonical Pedersen-binding
	// generator H. H is independent of G — neither is a known scalar
	// multiple of the other. Derived deterministically per curve via
	// hash-to-curve from a domain-separated nothing-up-my-sleeve tag
	// (see lens.<curve>.h-base.v1 in the per-curve implementation).
	SecondaryGenerator() Point

	// SampleScalar draws a uniform Scalar in [0, order) from rand.
	// Uses rejection sampling so the distribution is unbiased.
	SampleScalar(rand io.Reader) (Scalar, error)

	// HashToScalar maps the byte string `data` to a Scalar via a
	// curve-specific domain-separated wide-reduction hash. Output
	// distribution is statistically indistinguishable from uniform on
	// [0, order).
	HashToScalar(data []byte) Scalar
}

Curve is the abstract elliptic-curve group Lens operates on. Each concrete implementation pins one curve identity (Ed25519, secp256k1, Ristretto255) and carries the corresponding Scalar / Point types.

func NewEd25519

func NewEd25519() Curve

NewEd25519 returns the singleton Ed25519 curve.

func NewRistretto255

func NewRistretto255() Curve

NewRistretto255 returns the singleton Ristretto255 curve.

func NewSecp256k1

func NewSecp256k1() Curve

NewSecp256k1 returns the singleton Secp256k1 curve.

type Ed25519

type Ed25519 struct{}

Ed25519 is the RFC 8032 / FIPS 186-5 Edwards-form curve. The prime-order subgroup has order ℓ = 2^252 + 27742317777372353535851937790883648493.

G is the conventional Ed25519 base point. H is derived deterministically via hash-to-curve from a Lens-specific nothing-up-my-sleeve tag, then cleared to the prime-order subgroup via multiplication by the cofactor.

func (Ed25519) BasePoint

func (Ed25519) BasePoint() Point

func (Ed25519) HashToScalar

func (Ed25519) HashToScalar(data []byte) Scalar

HashToScalar maps a byte string to a uniform scalar via SHA-512 + SetUniformBytes (the standard reduction for ℓ).

func (Ed25519) Name

func (Ed25519) Name() string

Ed25519 implements Curve.

func (Ed25519) NewPoint

func (Ed25519) NewPoint() Point

func (Ed25519) NewScalar

func (Ed25519) NewScalar() Scalar

func (Ed25519) PointBytes

func (Ed25519) PointBytes() int

func (Ed25519) SampleScalar

func (Ed25519) SampleScalar(rand io.Reader) (Scalar, error)

SampleScalar draws a uniform scalar from a 64-byte read which is reduced mod ℓ via SetUniformBytes (statistically unbiased).

func (Ed25519) ScalarBytes

func (Ed25519) ScalarBytes() int

func (Ed25519) SecondaryGenerator

func (c Ed25519) SecondaryGenerator() Point

type Point

type Point interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler

	// Curve returns the curve this point belongs to.
	Curve() Curve

	// Add returns receiver + other (group operation).
	Add(other Point) Point

	// Sub returns receiver - other.
	Sub(other Point) Point

	// Negate returns -receiver.
	Negate() Point

	// IsIdentity returns true iff this is the identity element.
	IsIdentity() bool

	// Equal returns true iff receiver == other.
	Equal(other Point) bool
}

Point is an element of the curve group. Point operations are non-mutating — every method returns a fresh point.

func PedersenCommit

func PedersenCommit(curve Curve, secret, blind Scalar) Point

PedersenCommit returns C = secret·G + blind·H over the supplied curve. G is the curve's base point; H is the curve's secondary generator (returned by SecondaryGenerator).

The commitment is hiding (when blind is uniform random) and binding (under the discrete log assumption between G and H).

func PedersenVectorCommit

func PedersenVectorCommit(curve Curve, coeffs, blinds []Scalar) ([]Point, error)

PedersenVectorCommit commits to the t coefficients of a polynomial f(X) = c_0 + c_1·X + ... + c_{t-1}·X^{t-1} together with matching blinding coefficients r_0, ..., r_{t-1}. Returns the t commitments C_k = c_k·G + r_k·H.

type Polynomial

type Polynomial struct {
	// contains filtered or unexported fields
}

Polynomial represents f(X) = a_0 + a_1·X + ... + a_t·X^t with coefficients in the curve's scalar field.

func NewPolynomial

func NewPolynomial(curve Curve, degree int, constant Scalar, rand io.Reader) (*Polynomial, error)

NewPolynomial constructs a polynomial of the given degree with the supplied constant term and (degree) random higher-order coefficients drawn from rand.

If constant == nil, the constant term is set to zero (used by HJKY97 zero-poly Refresh).

func PolynomialFromCoefficients

func PolynomialFromCoefficients(curve Curve, coeffs []Scalar) *Polynomial

PolynomialFromCoefficients wraps an explicit coefficient slice as a polynomial. The slice is shared, not copied — the caller is responsible for not mutating it externally.

func Shamir

func Shamir(
	curve Curve,
	secret Scalar,
	threshold, n int,
	rand io.Reader,
) (*Polynomial, map[int]Scalar, error)

Shamir produces a (t, n)-Shamir secret sharing of `secret` over the curve's scalar field. Returns:

  • The polynomial f with f(0) = secret and (t-1) random higher- order coefficients drawn from `rand`.
  • A map partyID → f(partyID) for partyID in [1, n].

The threshold t is the minimum number of shares needed to recover the secret (so the polynomial degree is t-1).

Note that party IDs are 1-indexed; ID 0 is reserved (evaluating at 0 reveals the secret).

func (*Polynomial) Coefficients

func (p *Polynomial) Coefficients() []Scalar

Coefficients returns the underlying coefficient slice (not a copy). Used by the commitment layer to compute Pedersen commits per term.

func (*Polynomial) Constant

func (p *Polynomial) Constant() Scalar

Constant returns a copy of the constant term.

func (*Polynomial) Degree

func (p *Polynomial) Degree() int

Degree returns the polynomial's degree.

func (*Polynomial) Evaluate

func (p *Polynomial) Evaluate(x Scalar) Scalar

Evaluate computes f(x) via Horner's method. Panics if x is zero, to avoid leaking the secret constant term.

func (*Polynomial) EvaluateUnchecked

func (p *Polynomial) EvaluateUnchecked(x Scalar) Scalar

EvaluateUnchecked is like Evaluate but allows x = 0. Used in test/KAT paths to recover f(0) (the secret) for verification.

type Ristretto255

type Ristretto255 struct{}

Ristretto255 is the prime-order group from RFC 9496, built on Curve25519 via the Ristretto encoding. Group order is identical to Ed25519's ℓ.

func (Ristretto255) BasePoint

func (Ristretto255) BasePoint() Point

func (Ristretto255) HashToScalar

func (Ristretto255) HashToScalar(data []byte) Scalar

HashToScalar produces a uniform scalar via SHA-512 + reduction.

func (Ristretto255) Name

func (Ristretto255) Name() string

Ristretto255 implements Curve.

func (Ristretto255) NewPoint

func (Ristretto255) NewPoint() Point

func (Ristretto255) NewScalar

func (Ristretto255) NewScalar() Scalar

func (Ristretto255) PointBytes

func (Ristretto255) PointBytes() int

func (Ristretto255) SampleScalar

func (Ristretto255) SampleScalar(rand io.Reader) (Scalar, error)

SampleScalar draws a uniform scalar via 64-byte read + reduction.

func (Ristretto255) ScalarBytes

func (Ristretto255) ScalarBytes() int

func (Ristretto255) SecondaryGenerator

func (Ristretto255) SecondaryGenerator() Point

SecondaryGenerator derives H from a fixed nothing-up-my-sleeve tag expanded to 64 uniform bytes via SHA-512, then mapped to the group via the FromUniformBytes Elligator construction (the standard hash-to-curve for Ristretto255).

type Scalar

type Scalar interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler

	// Curve returns the curve this scalar belongs to.
	Curve() Curve

	// Set copies other into the receiver and returns the receiver.
	Set(other Scalar) Scalar

	// SetBytes interprets `bytes` (big-endian, padded to ScalarBytes())
	// as a value mod the group order and assigns it. Returns an error
	// if `bytes` has the wrong length.
	SetBytes(bytes []byte) error

	// SetUint64 assigns the receiver to the value `v`.
	SetUint64(v uint64) Scalar

	// Add computes receiver = receiver + other (mod order) and returns
	// the receiver.
	Add(other Scalar) Scalar

	// Sub computes receiver = receiver - other (mod order) and returns
	// the receiver.
	Sub(other Scalar) Scalar

	// Mul computes receiver = receiver * other (mod order) and returns
	// the receiver.
	Mul(other Scalar) Scalar

	// Negate computes receiver = -receiver (mod order) and returns the
	// receiver.
	Negate() Scalar

	// Invert computes receiver = receiver^-1 (mod order) and returns
	// the receiver. If receiver is zero, the result is undefined and
	// the implementation may panic — callers MUST check IsZero first
	// or guarantee non-zero by construction.
	Invert() Scalar

	// IsZero returns true iff the receiver is the additive identity.
	IsZero() bool

	// Equal returns true iff receiver == other in constant time.
	Equal(other Scalar) bool

	// ActOnBase returns receiver * G (the base point), without
	// mutating the receiver.
	ActOnBase() Point

	// Act returns receiver * P, without mutating either input.
	Act(p Point) Point
}

Scalar is a value modulo the curve's group order. Operations are constant-time where the underlying implementation supports it.

The operations mutate the receiver and return it for chaining, mirroring filippo.io/edwards25519 and threshold's curve.Scalar conventions. To preserve a value across an operation, copy it first via Set on a fresh NewScalar.

func LagrangeRecover

func LagrangeRecover(curve Curve, shares map[int]Scalar, threshold int) (Scalar, error)

LagrangeRecover reconstructs the secret f(0) from the supplied (partyID → share) map, using the smallest-ID t-element subset. Intended for tests and KAT verification — calling this in production gives the caller the master secret.

func SamplePolynomialBlind

func SamplePolynomialBlind(curve Curve, degree int, rand io.Reader) ([]Scalar, error)

SamplePolynomialBlind draws (degree+1) uniform blinding scalars used alongside a Polynomial of the same degree to commit to its coefficients.

type Secp256k1

type Secp256k1 struct{}

Secp256k1 is the SEC2 / Bitcoin curve.

func (Secp256k1) BasePoint

func (Secp256k1) BasePoint() Point

func (Secp256k1) HashToScalar

func (Secp256k1) HashToScalar(data []byte) Scalar

HashToScalar maps a byte string to a scalar via SHA-512 + reduction.

func (Secp256k1) Name

func (Secp256k1) Name() string

Secp256k1 implements Curve.

func (Secp256k1) NewPoint

func (Secp256k1) NewPoint() Point

func (Secp256k1) NewScalar

func (Secp256k1) NewScalar() Scalar

func (Secp256k1) PointBytes

func (Secp256k1) PointBytes() int

func (Secp256k1) SampleScalar

func (Secp256k1) SampleScalar(rand io.Reader) (Scalar, error)

SampleScalar draws a uniform scalar in [0, n) via rejection sampling.

func (Secp256k1) ScalarBytes

func (Secp256k1) ScalarBytes() int

func (Secp256k1) SecondaryGenerator

func (Secp256k1) SecondaryGenerator() Point

SecondaryGenerator returns H, an independent generator for Pedersen commits. Derived deterministically via try-and-increment hash-to-curve over a Lens-specific nothing-up-my-sleeve tag.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL