From 9624abb0d26c0dc327386e1d6227ce5ef37118b8 Mon Sep 17 00:00:00 2001 From: zeekay Date: Sun, 28 Jun 2026 07:16:11 -0700 Subject: [PATCH] =?UTF-8?q?docs(threshold):=20decomplect=20the=20threshold?= =?UTF-8?q?-FHE=20package=20=E2=80=94=20one=20canonical=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Establish exactly one way to do each thing and fence off the legacy primitives: - doc.go (new): package overview. DealerlessKeyGen is THE production keygen (dealerless); PartialDecryptLWE/CombineLWE is THE threshold decrypt (no-reconstruct); ShareLWESecretKey is a trusted-dealer test oracle only. Documents the F ⊥ M boundary (this package is FHE-only; MPC signing lives in luxfi/threshold) and the dealerless-bootstrap-key residual (homomorphic compute), stated honestly, not faked. - keygen.go: re-document GenerateSharedKey. It is a TRUSTED-DEALER key-COMMITMENT VSS that shares the SHA-256 hash of the key — useful for verifiable-sharing audit, NOT usable for threshold decryption. Marked Deprecated for the keygen role; points to DealerlessKeyGen. Removes the misleading 'distributed key generation … for now we generate keys and split them' framing. - partial_decrypt.go: label ShareLWESecretKey as TRUSTED-DEALER (retained as a KAT oracle); point to DealerlessKeyGen for production. No behaviour change; kernel tests green. --- pkg/threshold/doc.go | 55 ++++++++++++++++++++++++++++++++ pkg/threshold/keygen.go | 17 ++++++++-- pkg/threshold/partial_decrypt.go | 6 ++++ 3 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 pkg/threshold/doc.go diff --git a/pkg/threshold/doc.go b/pkg/threshold/doc.go new file mode 100644 index 0000000..73afff7 --- /dev/null +++ b/pkg/threshold/doc.go @@ -0,0 +1,55 @@ +// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +// Package threshold is the ONE threshold-FHE implementation for Lux — the +// F-Chain confidential-decrypt lane. It is held to the trustless-by-default law: +// no trusted dealer (dealerless key generation) and no reconstruction of the FHE +// secret key at decrypt time, the same standard Corona / Pulsar / Magnetar meet. +// +// There is exactly one way to do each thing here. The canonical trustless flow: +// +// pub, shares := DealerlessKeyGen(params, t, n, crsSeed) // dealerless keygen +// ct := encrypt(pub, m) // collective public key +// d_j := PartialDecryptLWE(shares[j], ct, …) // per-party, no s formed +// m := CombineLWE(ct, {d_j}_{|S|≥t}, …) // s masked behind c_1, never recovered +// +// File map (decomplected — each file is one concern, no overlap): +// +// - dealerless_dkg.go — THE production key generation. Dealerless (GJKR/Pedersen +// DKG composed with the audited Lattigo multiparty CKG): each committee +// member samples only its own contribution s_j, the collective secret +// s = Σ s_j is NEVER formed, and the output is the collective LWE public key +// plus one t-of-n LWEShare per party. +// +// - partial_decrypt.go — THE threshold decryption (AJL+12 noise-flooding with +// Bendlin-Damgård denominator clearing). PartialDecryptLWE / CombineLWE are +// no-reconstruct: the secret s appears only as the coefficient of the public +// c_1 inside the masked aggregate and is never materialised. ShareLWESecretKey +// in this file is a TRUSTED-DEALER split of a whole key — retained only as a +// test/KAT oracle for the same LWEShare type; production uses DealerlessKeyGen. +// +// - lsss.go + keygen.go — a SEPARATE primitive: large-prime Shamir VSS with +// Feldman commitments, and GenerateSharedKey, which shares the SHA-256 *hash* +// of a serialized key. This is a key-COMMITMENT / verifiable-secret-sharing +// facility (audit, dealer-equivocation checks). Its KeyShare values are shares +// of a hash and are NOT usable for threshold DECRYPTION — do not mistake +// GenerateSharedKey for the threshold-FHE keygen; that is DealerlessKeyGen. +// +// Scope and residual. This package realises the trustless confidential-DECRYPT +// lane completely: dealerless generation of the encryption key + t-of-n shares, +// and no-reconstruct threshold decryption. It deliberately does NOT generate the +// FHEW blind-rotation / bootstrap (evaluation) key, which homomorphic COMPUTE on +// committee ciphertexts requires. Dealerless bootstrap-key generation is a +// distinct multiparty-FHEW protocol (collaborative RGSW key generation) and is +// the documented residual — it is not faked here. Until it lands, the committee +// can trustlessly ENCRYPT (collective public key) and trustlessly threshold- +// DECRYPT (the launch-critical confidentiality property); homomorphic compute on +// the committee's own ciphertexts still depends on a bootstrap key generated by a +// dealer and is therefore out of the strict trustless lane. +// +// Decomplecting boundary (F ⊥ M). This package is FHE only (confidential +// decrypt). It shares nothing with threshold MPC SIGNING (M-Chain: CGGMP21 / +// FROST for bridge custody), which lives in github.com/luxfi/threshold. The two +// are orthogonal primitives — different math, different purpose — and must never +// be conflated. +package threshold diff --git a/pkg/threshold/keygen.go b/pkg/threshold/keygen.go index 9a2284b..805881b 100644 --- a/pkg/threshold/keygen.go +++ b/pkg/threshold/keygen.go @@ -40,9 +40,20 @@ type KeyGenResult struct { Shares []*KeyShare // One per party } -// GenerateSharedKey performs distributed key generation. -// In a real implementation, this would be done via MPC. -// For now, we generate keys and split them using LSSS. +// GenerateSharedKey is a TRUSTED-DEALER key-COMMITMENT facility, NOT the +// threshold-FHE key generator. It generates a keypair in one process and +// large-prime Shamir-splits the SHA-256 HASH of each secret-key component +// (hashToFieldElement) — i.e. it shares a COMMITMENT to the key, for verifiable- +// secret-sharing / dealer-equivocation auditing. The resulting KeyShare values +// are shares of a hash and CANNOT perform threshold decryption. +// +// For the trustless threshold-FHE lane use DealerlessKeyGen (dealerless_dkg.go), +// which is dealerless (no party ever holds the FHE secret key) and produces +// decrypt-capable LWEShare values consumed by PartialDecryptLWE / CombineLWE. See +// the package doc for the canonical flow. +// +// Deprecated: not part of the threshold-decrypt path; retained only for the +// commitment-VSS use case. func GenerateSharedKey(params fhe.Parameters, threshold, total int, sessionID string) (*KeyGenResult, error) { if threshold > total { return nil, fmt.Errorf("threshold %d exceeds total %d", threshold, total) diff --git a/pkg/threshold/partial_decrypt.go b/pkg/threshold/partial_decrypt.go index 78efe8e..3b3e7fa 100644 --- a/pkg/threshold/partial_decrypt.go +++ b/pkg/threshold/partial_decrypt.go @@ -241,6 +241,12 @@ type LWEPartialDecryption struct { // ShareLWESecretKey Shamir-splits the LWE secret key coefficient-by- // coefficient over Z_q where q is the LWE modulus. // +// TRUSTED-DEALER. This takes a WHOLE secret key, so the caller momentarily holds +// the full FHE secret — a single point of compromise. It is retained as a +// test/KAT oracle for the LWEShare type; the trustless PRODUCTION key generation +// is DealerlessKeyGen (dealerless_dkg.go), where no party ever holds the secret. +// Both produce the same LWEShare via the same dealing helpers (dealCoeffSubShares). +// // Pipeline: // // 1. Extract the secret-key polynomial from NTT+Montgomery form back to