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

128 lines
3.1 KiB
Go

// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package hash
// LensBLAKE3 is a NON-NORMATIVE legacy suite kept for byte-equality
// checks against transcripts produced before the Lens-SHA3 profile
// was pinned as canonical. NEW deployments MUST use Lens-SHA3.
import (
"encoding/binary"
"github.com/zeebo/blake3"
)
// lensBLAKE3 implements HashSuite using BLAKE3 primitives.
type lensBLAKE3 struct{}
// NewLensBLAKE3 returns the legacy BLAKE3 suite. NOT for production.
func NewLensBLAKE3() HashSuite { return lensBLAKE3{} }
func (lensBLAKE3) ID() string { return "Lens-BLAKE3" }
func (lensBLAKE3) Hc(transcript []byte) []byte {
h := blake3.New()
_, _ = h.Write([]byte(tagHC))
_, _ = h.Write(transcript)
return h.Sum(nil)[:32]
}
func (lensBLAKE3) Hu(transcript []byte, outLen int) []byte {
h := blake3.New()
_, _ = h.Write([]byte(tagHU))
_, _ = h.Write(transcript)
out := make([]byte, outLen)
_, _ = h.Digest().Read(out)
return out
}
func (lensBLAKE3) TranscriptHash(parts ...[]byte) [32]byte {
h := blake3.New()
_, _ = h.Write([]byte(tagTranscript))
for _, p := range parts {
var lenBuf [4]byte
binary.BigEndian.PutUint32(lenBuf[:], uint32(len(p)))
_, _ = h.Write(lenBuf[:])
_, _ = h.Write(p)
}
var out [32]byte
copy(out[:], h.Sum(nil)[:32])
return out
}
func (lensBLAKE3) PRF(key, msg []byte, outLen int) []byte {
keyArr := blake3SizedKey(key)
h, _ := blake3.NewKeyed(keyArr[:])
_, _ = h.Write([]byte(tagPRF))
_, _ = h.Write(msg)
out := make([]byte, outLen)
_, _ = h.Digest().Read(out)
return out
}
func (lensBLAKE3) MAC(key, msg []byte, outLen int) []byte {
keyArr := blake3SizedKey(key)
h, _ := blake3.NewKeyed(keyArr[:])
_, _ = h.Write([]byte(tagMAC))
_, _ = h.Write(msg)
out := make([]byte, outLen)
_, _ = h.Digest().Read(out)
return out
}
func (lensBLAKE3) DerivePairwise(
kex []byte,
chainID, groupID []byte,
eraID, generation uint64,
i, j int,
outLen int,
) []byte {
a, b := i, j
if a > b {
a, b = b, a
}
keyArr := blake3SizedKey(kex)
h, _ := blake3.NewKeyed(keyArr[:])
_, _ = h.Write([]byte(tagPairwise))
writeLen(h, chainID)
writeLen(h, groupID)
var u8 [8]byte
binary.BigEndian.PutUint64(u8[:], eraID)
_, _ = h.Write(u8[:])
binary.BigEndian.PutUint64(u8[:], generation)
_, _ = h.Write(u8[:])
var u4 [4]byte
binary.BigEndian.PutUint32(u4[:], uint32(a))
_, _ = h.Write(u4[:])
binary.BigEndian.PutUint32(u4[:], uint32(b))
_, _ = h.Write(u4[:])
out := make([]byte, outLen)
_, _ = h.Digest().Read(out)
return out
}
// blake3SizedKey compresses arbitrary keying material into the 32-byte
// key BLAKE3.NewKeyed requires.
func blake3SizedKey(kex []byte) [32]byte {
if len(kex) == 32 {
var out [32]byte
copy(out[:], kex)
return out
}
h := blake3.New()
_, _ = h.Write([]byte("LENS-KDF-KEY-v1"))
_, _ = h.Write(kex)
var out [32]byte
copy(out[:], h.Sum(nil)[:32])
return out
}
// writeLen writes a length-prefixed byte slice to the hash writer.
func writeLen(h interface{ Write(p []byte) (int, error) }, data []byte) {
var lenBuf [4]byte
binary.BigEndian.PutUint32(lenBuf[:], uint32(len(data)))
_, _ = h.Write(lenBuf[:])
_, _ = h.Write(data)
}