mirror of
https://github.com/luxfi/lens.git
synced 2026-10-30 16:21:16 +00:00
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.
95 lines
2.6 KiB
Go
95 lines
2.6 KiB
Go
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package hash
|
|
|
|
// LensSHA3 is the production hash suite for Lens. Built on
|
|
// cSHAKE256 / KMAC256 / TupleHash256 from FIPS 202 and NIST SP 800-185.
|
|
//
|
|
// Customization tags pin every operation to the Lens protocol:
|
|
//
|
|
// Hc "LENS-HC-v1"
|
|
// Hu "LENS-HU-v1"
|
|
// TranscriptHash "LENS-TRANSCRIPT-v1"
|
|
// PRF "LENS-PRF-v1" (KMAC256)
|
|
// MAC "LENS-MAC-v1" (KMAC256)
|
|
// DerivePairwise "LENS-PAIRWISE-v1" (KMAC256)
|
|
//
|
|
// Distinct customization strings are essential — same primitive +
|
|
// different tag = independent oracle. Bumping any tag invalidates
|
|
// every transcript / activation cert / pairwise material in flight.
|
|
//
|
|
// All operations are stateless and goroutine-safe.
|
|
|
|
import (
|
|
"encoding/binary"
|
|
)
|
|
|
|
const (
|
|
tagHC = "LENS-HC-v1"
|
|
tagHU = "LENS-HU-v1"
|
|
tagTranscript = "LENS-TRANSCRIPT-v1"
|
|
tagPRF = "LENS-PRF-v1"
|
|
tagMAC = "LENS-MAC-v1"
|
|
tagPairwise = "LENS-PAIRWISE-v1"
|
|
)
|
|
|
|
// lensSHA3 implements HashSuite using the SP 800-185 primitives.
|
|
type lensSHA3 struct{}
|
|
|
|
// NewLensSHA3 returns the production hash suite.
|
|
func NewLensSHA3() HashSuite { return lensSHA3{} }
|
|
|
|
func (lensSHA3) ID() string { return "Lens-SHA3" }
|
|
|
|
func (lensSHA3) Hc(transcript []byte) []byte {
|
|
return cshake256Stream(tagHC, transcript, 32)
|
|
}
|
|
|
|
func (lensSHA3) Hu(transcript []byte, outLen int) []byte {
|
|
return cshake256Stream(tagHU, transcript, outLen)
|
|
}
|
|
|
|
func (lensSHA3) TranscriptHash(parts ...[]byte) [32]byte {
|
|
out := tupleHash256(parts, 32, tagTranscript)
|
|
var fixed [32]byte
|
|
copy(fixed[:], out)
|
|
return fixed
|
|
}
|
|
|
|
func (lensSHA3) PRF(key, msg []byte, outLen int) []byte {
|
|
return kmac256(key, msg, outLen, tagPRF)
|
|
}
|
|
|
|
func (lensSHA3) MAC(key, msg []byte, outLen int) []byte {
|
|
return kmac256(key, msg, outLen, tagMAC)
|
|
}
|
|
|
|
func (lensSHA3) 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
|
|
}
|
|
var msg []byte
|
|
msg = append(msg, encodeString(chainID)...)
|
|
msg = append(msg, encodeString(groupID)...)
|
|
var u8 [8]byte
|
|
binary.BigEndian.PutUint64(u8[:], eraID)
|
|
msg = append(msg, encodeString(u8[:])...)
|
|
binary.BigEndian.PutUint64(u8[:], generation)
|
|
msg = append(msg, encodeString(u8[:])...)
|
|
var u4 [4]byte
|
|
binary.BigEndian.PutUint32(u4[:], uint32(a))
|
|
msg = append(msg, encodeString(u4[:])...)
|
|
binary.BigEndian.PutUint32(u4[:], uint32(b))
|
|
msg = append(msg, encodeString(u4[:])...)
|
|
|
|
return kmac256(kex, msg, outLen, tagPairwise)
|
|
}
|