docs(threshold): decomplect the threshold-FHE package — one canonical path

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.
This commit is contained in:
zeekay
2026-06-28 07:20:08 -07:00
parent bc34033354
commit 9624abb0d2
3 changed files with 75 additions and 3 deletions
+55
View File
@@ -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
+14 -3
View File
@@ -40,9 +40,20 @@ type KeyGenResult struct {
Shares []*KeyShare // One per party Shares []*KeyShare // One per party
} }
// GenerateSharedKey performs distributed key generation. // GenerateSharedKey is a TRUSTED-DEALER key-COMMITMENT facility, NOT the
// In a real implementation, this would be done via MPC. // threshold-FHE key generator. It generates a keypair in one process and
// For now, we generate keys and split them using LSSS. // 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) { func GenerateSharedKey(params fhe.Parameters, threshold, total int, sessionID string) (*KeyGenResult, error) {
if threshold > total { if threshold > total {
return nil, fmt.Errorf("threshold %d exceeds total %d", threshold, total) return nil, fmt.Errorf("threshold %d exceeds total %d", threshold, total)
+6
View File
@@ -241,6 +241,12 @@ type LWEPartialDecryption struct {
// ShareLWESecretKey Shamir-splits the LWE secret key coefficient-by- // ShareLWESecretKey Shamir-splits the LWE secret key coefficient-by-
// coefficient over Z_q where q is the LWE modulus. // 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: // Pipeline:
// //
// 1. Extract the secret-key polynomial from NTT+Montgomery form back to // 1. Extract the secret-key polynomial from NTT+Montgomery form back to