Files
zeekay 2c63fd34fc lens: classical curve threshold kernel — sister to Pulsar
Lens is the curve-based threshold signing kernel for Lux. It is the
classical analogue of Pulsar (the lattice kernel at github.com/luxfi/pulsar).
Both kernels share the same lifecycle shape — Bootstrap → Reshare* →
Reanchor — and the same activation circuit-breaker contract. They differ
only in the math field: Pulsar over R_q (Module-LWE), Lens over a
prime-order curve group (discrete log).

Curves shipped: Ed25519, secp256k1, Ristretto255. Each has a
deterministically-derived secondary Pedersen generator H from a
Lens-specific nothing-up-my-sleeve tag.

Sign math: FROST 2-round Schnorr threshold signing per RFC 9591
(Komlo-Goldberg 2020). Verifier checks z·G ?= R + c·X under the
unchanged group public key.

Reshare math: HJKY97 zero-poly Refresh + Desmedt-Jajodia ReshareToNewSet
over the curve scalar field. Master secret s is preserved; only the
share distribution rotates per epoch.

Activation cert message format: QUASAR-LENS-ACTIVATE-v1, byte-equivalent
shape to QUASAR-PULSAR-ACTIVATE-v1.

Hash suite: HashSuite interface with Lens-SHA3 (production, cSHAKE256 /
KMAC256 / TupleHash256) and Lens-BLAKE3 (legacy) profiles.

DKG: distributed FROST DKG with Feldman commitments. No trusted dealer
required at genesis if the chain prefers proper distributed DKG.

KAT oracles: cmd/reshare_oracle and cmd/dkg_oracle emit deterministic
JSON vectors for cross-language byte-equality validation against a
future luxcpp/crypto/lens/ port.

Tests: full unit + integration coverage on every shipped curve. 60
unique tests, 0 failures.
2026-03-03 12:00:00 -08:00

79 lines
2.4 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package reshare
// KeyShare regeneration for Lens.
//
// The Reshare and Refresh kernels operate on bare scalar shares —
// the SkShare field of the production-grade
// `github.com/luxfi/lens/threshold.KeyShare` struct. A KeyShare is
// MORE than just SkShare; it carries:
//
// type KeyShare struct {
// Index int
// PartyID int
// SkShare primitives.Scalar
// VerificationShare primitives.Point
// Lambda primitives.Scalar
// Seeds map[int][][]byte
// MACKeys map[int][]byte
// GroupKey *threshold.GroupKey
// }
//
// All KDF derivations use the canonical Lens HashSuite (KMAC256
// under Lens-SHA3, keyed BLAKE3 under the legacy suite). Domain-
// separation tags:
//
// "lens.reshare.prf-seed.v1" — for Seeds
// "lens.reshare.mac-key.v1" — for MACKeys
import (
"github.com/luxfi/lens/hash"
"github.com/luxfi/lens/primitives"
)
// KDFOutput derives a fixed-length output from keying material under
// the supplied HashSuite's pairwise KDF. suite=nil resolves to the
// production default (Lens-SHA3).
//
// The tag is folded into the chainID label with a `|` separator so
// two callers with distinct tags but the same remaining inputs always
// produce distinct bytes.
func KDFOutput(
suite hash.HashSuite,
tag string,
authKex []byte,
chainID, groupID []byte,
eraID, generation uint64,
partyI, partyJ int,
outLen int,
) []byte {
s := hash.Resolve(suite)
labelledChain := make([]byte, 0, len(tag)+1+len(chainID))
labelledChain = append(labelledChain, []byte(tag)...)
labelledChain = append(labelledChain, '|')
labelledChain = append(labelledChain, chainID...)
return s.DerivePairwise(authKex, labelledChain, groupID, eraID, generation, partyI, partyJ, outLen)
}
// CanonicalPair returns the (i, j) tuple in canonical (smaller-first)
// order. Used as a map key for pairwise material.
func CanonicalPair(i, j int) [2]int {
if i > j {
return [2]int{j, i}
}
return [2]int{i, j}
}
// EraseScalar overwrites the scalar's binary representation with zero
// bytes. After activation, every old share MUST be erased — failure
// to do so undermines the proactive-security guarantee.
func EraseScalar(s primitives.Scalar) {
if s == nil {
return
}
zero := s.Curve().NewScalar()
s.Set(zero)
}