`go build ./...` failed with "missing go.sum entry for module providing
package" (ntt_simd.go) — the go.mod was out of sync with the imports, so
individual `go get`s just walk further down the chain; tidy is the fix.
Also patches the panic-driven denial of service in the AWS SDK for Go v2
EventStream decoder: eventstream v1.7.8, service/s3 v1.97.3 (minimum patched,
so the change carries the fix and nothing else).
Verified: go build ./... goes from exit 1 to exit 0.
go test ./... is 4 ok / 2 FAILING. Those 2 are not a regression here — the
packages could not compile before, so their tests never ran; repairing the
build makes pre-existing failures reachable. Stated explicitly rather than
reporting only the build fix, since a green build with red tests reads as
"done" when it is not.
Several luxfi versions carry TWO different contents across this workspace,
because published tags were moved instead of a new patch being cut:
age@v1.5.0, pq@v1.0.3, threshold@v1.9.4, zap@v0.6.0, zap@v0.8.1.
Adjudicated before editing, since "checksum mismatch / SECURITY ERROR" is also
what a real supply-chain attack looks like. It is not one here: sum.golang.org
holds the OLD hash while proxy.golang.org and a direct fetch BOTH serve the same
NEW bytes. Two independent transports agreeing means nothing is rewriting
content in flight — the tag moved at source. The sumdb entry is a fossil:
GOPRIVATE covers github.com/luxfi/* with GOSUMDB=off, so our own modules never
consult the checksum DB and a moved tag splits consumers silently.
The old bytes are served by nothing now, so a stale pin can never build.
Corrected to the only content that exists, in BOTH line forms (h1: and
/go.mod h1:) — Go reports these one at a time, so a partial fix just relocates
the error.
Deliberately no `go mod tidy`: this changes no selected version, only the
recorded hash of versions already chosen.
Verified: `go list -m all` resolves with no checksum error.
The durable fix is upstream: never move a published tag, cut x.y.z+1 instead.
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.
Closes the one trustless gap in the threshold-FHE stack. partial_decrypt.go
was already NO-RECONSTRUCT (the master secret s only ever appears masked behind
c_1·s), but ShareLWESecretKey assumed a TRUSTED DEALER: one process samples the
whole FHE secret key and Shamir-splits it. That instant of single-party custody
violates the trustless-by-default law (no dealer + no-reconstruct), the same
standard Corona/Pulsar/Magnetar are held to.
DealerlessKeyGen removes the dealer. GJKR/Pedersen DKG structure specialised to
the RLWE/LWE secret, composed with the audited Lattigo multiparty CKG:
- each party samples its OWN contribution s_j; collective secret s = Σ s_j
is never formed by any party or by this package;
- collective LWE public key p = Σ(-a·s_j + e_j) = -a·s + e via lattice CKG
(dealerless, format-identical to the library's pk);
- t-of-n shares: each party coeff-wise Shamir-splits its OWN s_j; recipients
sum sub-shares to a degree-(t-1) Shamir share of s — producing exactly the
LWEShare type the proven PartialDecryptLWE/CombineLWE kernel consumes.
Per-party API (DealerlessParty.DealRound1/Aggregate, SampleDealerlessCRP,
AssembleCollectivePublicKey) for distributed deployment + a one-shot driver.
Wire types (PublicKeyShareMsg/SubShareMsg) carry only public/share data, never
a SecretKey. DRY: ShareLWESecretKey now reuses the shared dealing helpers.
Proven (dealerless_dkg_test.go, all green incl. -race, 3-of-5 PN11QP54):
- E2E trustless lane: dealerless DKG → public-key encrypt → t-of-n partial
decrypt → combine → correct plaintext (2-of-3, 3-of-5);
- below-threshold (t-1) cannot decrypt;
- no party holds s: two t-subsets reconstruct the same secret, no share == s;
- CRP deterministic per consensus seed (validators converge), seed-bound;
- transport carries no secret (reflect gate);
- no-reconstruct structural gate (go/ast) over the keygen+decrypt surface,
with a negative control proving the gate has teeth.
Scope: F-Chain confidential-DECRYPT lane (encryption key + t-of-n shares). The
dealerless FHEW bootstrap/eval key (homomorphic COMPUTE) is a separate
multiparty-FHEW protocol, documented as the residual — not faked here.
require import Bool ('^^' xor scope); reproved chained_pbs_correctness and the
PBS-chain noise-budget induction (last_ind/foldl_rcons); fixed threshold-DKG
correctness rewrites.
Verified: all check under easycrypt (z3 + alt-ergo), 0 admits. Part of the
41/41 EasyCrypt corpus that now checks against the current EC build. Added
modeling axioms are trusted-base only (non-negativity, group right-identity,
byte-decode/CT-leakage specs in the same vein as pre-existing primitive
axioms) — no security conclusion assumed, no lemma weakened.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
All FHE backend infrastructure (Torus compiler, ML SDK, TFHE runtime,
KMS, FHEVM) lives under the luxfhe org. luxfi/* repos are reserved for
the Lux-blockchain integrated surfaces (precompiles, coprocessor, core
FHE Go library).
Removes:
docs/workshop/fhevmjs/ (Zama JS package name; reencrypt + ABI demo)
docs/workshop/pyfhevm/ (Python wrapper around fhevm-tfhe-cli)
Both directories shelled out to upstream tooling that isn't part of
the Lux stack — fhevmjs (Zama's JS SDK) and fhevm-tfhe-cli (Zama's
CLI for TFHE-rs). The clients were also broken on a clean checkout
since neither tool ships with this repo.
Workshop Solidity examples are unchanged (erc20.sol +
eip712-reencrypt.sol — these are stable references). README is
replaced with a placeholder pointing at the Torus compiler /
Lux FHE Coprocessor as the canonical client path; we will re-add
the workshop client when Torus ships.
Naming map followed for replacement copy:
Lux FHE — umbrella product
Lux FHEVM — canonical EVM product
Lux FHE Precompiles, Coprocessor, GPU
Torus — compiler / framework brand
Drops the "Not a wrapper around tfhe-rs, not a binding to Zama, not a port of
OpenFHE" disclaimer in favor of stating what Lux FHE IS: a from-scratch TFHE
implementation in Go on luxfi/lattice, with optional C++ / GPU / FPGA accel
paths. No external library dependency.
Same change applied to the related papers + docs + skills in parallel
commits across lux/papers, lux/docs, lux/skills.
Adds the canonical noise-flooding threshold-decryption primitives that
upstream consumers (notably luxfi/threshold/protocols/tfhe) need to
implement true M-of-N FHE decryption without any party holding the
master key.
- ShareLWESecretKey: Shamir-splits the LWE secret coefficient-by-
coefficient over Z_q. No party ever sees the master.
- PartialDecryptLWE: party j computes d_j = c_1 · s_j + e_j with
fresh smudging noise from a discrete Gaussian.
- CombineLWE: integer-Lagrange (Bendlin-Damgård denominator clearing)
+ Δ^{-1} mod q to recover the plaintext polynomial.
- SmudgingSigma: tight noise calibration against the worst-case
integer Lagrange numerator, so Λ_max · σ · √t · 6 ≤ Q/16.
Decomplecting note: this LSSS-over-Z_q lives next to the existing
LSSS-over-RFC-3526-prime in pkg/threshold. The two are different
primitives — large-prime LSSS shares high-entropy secrets where the
secret space need not equal the FHE modulus; Z_q LSSS composes
directly with lattice arithmetic. Both stay in this package.
Decryption follows Bendlin-Damgård (TCC 2010, §4.2) and Asharov-Jain-
López-Alt-Tromer-Vaikuntanathan-Wichs (EUROCRYPT 2012, §4). Noise
flooding is the simulation-soundness mechanism.
Verified by round-trip tests at (t, n) ∈ {(2, 3), (5, 9), (11, 21)},
below-threshold negative test, share-doesn't-equal-master regression
guard, dedup guard, cross-parameter guard, and combine-determinism
test. All 8 new threshold tests pass.
Also adds public accessors:
- fhe.Parameters.ParamsLWE/ParamsBR — expose underlying rlwe.Parameters
so out-of-tree threshold callers can drive lattice primitives
against the same parameter set the encryptor uses.
- fhe.BitCiphertext.Bits / NewBitCiphertextFromBits — iterate per-bit
for threshold decryption without round-tripping MarshalBinary.
References:
- Bendlin, Damgård. Threshold Decryption and Zero-Knowledge Proofs
for Lattice-Based Cryptosystems. TCC 2010.
- Asharov, Jain, López-Alt, Tromer, Vaikuntanathan, Wichs. Multiparty
Computation with Low Communication, Computation and Interaction
via Threshold FHE. EUROCRYPT 2012.
- Mouchet, Troncoso-Pastoriza, Bossuat, Hubaux. Multiparty
Homomorphic Encryption from Ring-Learning-with-Errors. PETS 2021.
Adds the canonical noise-flooding threshold-decryption primitives that
upstream consumers (notably luxfi/threshold/protocols/tfhe) need to
implement true M-of-N FHE decryption without any party holding the
master key.
- ShareLWESecretKey: Shamir-splits the LWE secret coefficient-by-
coefficient over Z_q. No party ever sees the master.
- PartialDecryptLWE: party j computes d_j = c_1 · s_j + e_j with
fresh smudging noise from a discrete Gaussian.
- CombineLWE: integer-Lagrange (Bendlin-Damgård denominator clearing)
+ Δ^{-1} mod q to recover the plaintext polynomial.
- SmudgingSigma: tight noise calibration against the worst-case
integer Lagrange numerator, so Λ_max · σ · √t · 6 ≤ Q/16.
Decomplecting note: this LSSS-over-Z_q lives next to the existing
LSSS-over-RFC-3526-prime in pkg/threshold. The two are different
primitives — large-prime LSSS shares high-entropy secrets where the
secret space need not equal the FHE modulus; Z_q LSSS composes
directly with lattice arithmetic. Both stay in this package.
Decryption follows Bendlin-Damgård (TCC 2010, §4.2) and Asharov-Jain-
López-Alt-Tromer-Vaikuntanathan-Wichs (EUROCRYPT 2012, §4). Noise
flooding is the simulation-soundness mechanism.
Verified by round-trip tests at (t, n) ∈ {(2, 3), (5, 9), (11, 21)},
below-threshold negative test, share-doesn't-equal-master regression
guard, dedup guard, cross-parameter guard, and combine-determinism
test. All 8 new threshold tests pass.
Also adds public accessors:
- fhe.Parameters.ParamsLWE/ParamsBR — expose underlying rlwe.Parameters
so out-of-tree threshold callers can drive lattice primitives
against the same parameter set the encryptor uses.
- fhe.BitCiphertext.Bits / NewBitCiphertextFromBits — iterate per-bit
for threshold decryption without round-tripping MarshalBinary.
References:
- Bendlin, Damgård. Threshold Decryption and Zero-Knowledge Proofs
for Lattice-Based Cryptosystems. TCC 2010.
- Asharov, Jain, López-Alt, Tromer, Vaikuntanathan, Wichs. Multiparty
Computation with Low Communication, Computation and Interaction
via Threshold FHE. EUROCRYPT 2012.
- Mouchet, Troncoso-Pastoriza, Bossuat, Hubaux. Multiparty
Homomorphic Encryption from Ring-Learning-with-Errors. PETS 2021.
Adds NTTEngine.NTTBatch / INTTBatch that dispatch to luxfi/lattice/v7/gpu
(Metal on darwin, CUDA on linux/NVIDIA) when:
1. CGo is on and a GPU device is reachable (lattice/gpu.GPUAvailable())
2. The engine's (N, Q) has passed a deterministic byte-equality probe
against the CPU subring NTT oracle (cached per (N, Q))
Otherwise falls back to per-polynomial CPU subring NTT. The probe ensures
a GPU context with different convention encoding (Montgomery vs standard,
bit-reversed vs natural) is rejected before it can corrupt ciphertexts.
No `gpu` build tag. CGo is the only compile-time gate.
Tests: ntt_gpu_test.go::TestNTTBatch_ByteEqualToInPlace (probe enforces
byte equality), TestINTTBatch_RoundTrip (INTT(NTT(x)) == x).
The 8 demo mains imported github.com/hanzoai/base (HTTP server scaffold)
which pulled the legacy Google Cloud / temporal / grpc transitive chain.
Demos were the only consumers of that chain in the fhe tree.
One way only: fhe is a cryptographic library, not an app harness. App
authors who want HTTP wiring can vendor hanzoai/base directly in their
own cmd binary — fhe doesn't ship that scaffold.
The 8 demo mains import github.com/hanzoai/base which pulls grpc
transitively via the legacy Google Cloud SDK chain. grpc is opt-in
only across the Lux stack, so demos follow the same discipline:
build with -tags demos.
Default fhe build: 0 grpc / 0 google cloud.
Adds GitHub Actions CI on push to main/dev + PRs:
- go build ./...
- go test -count=1 -short -timeout 300s ./...
- go vet ./...
- govulncheck (non-blocking, reports advisories)
GOWORK=off per repo convention.
PN10QP27 (~128-bit security) drives a full BlindRotate inside
lattice/v7@v7.1.0 — ~22s on Apple silicon. Comment added so future
maintainers don't mistake the test wall-clock for a hang.
Detailed invention disclosures are sensitive pre-filing material. Moved to
lux-private/patents to preserve international filing rights (EU/JP/KR/CN
absolute-novelty rule). Cross-ref: github.com/lux-private/patents/bundles/
CRYPTOGRAPHER-SIGN-OFF.md captures the Tier A artifact review:
- APPROVED WITH GATES for F-Chain TFHE (LP-066/LP-134) and the
threshold-FHE compliance package (LP-019/LP-076/LP-167)
- Verified green: build, dudect bridges build, EC theories 0/0
admits, Lean 0 sorry, bridge map cites each axiom, Jasmin
algorithm shape correct, CT obligations correctly stated
- 4 minor findings, 2 informational
- 4 open gates:
GATE-1: dudect submission-grade run (10^9 samples)
GATE-2: jasminc -checkCT pass on production Jasmin
GATE-3: deployment runbook for F-Chain TFHE
GATE-4: Lean tactic-script transcription of EC proof
None of the gates require algorithm or code change; they are
about empirical confirmation and operational disclosure.
Empirical constant-time analysis harness mirroring the Pulsar
pack at ~/work/lux/pulsar/ct/dudect/:
encrypt_ct.go + dudect_encrypt.c - cgo bridge + C main loop
decrypt_ct.go + dudect_decrypt.c - the CT-critical routine
bootstrap_ct.go + dudect_bootstrap.c - PBS composite
CT population (operational framing):
- Both classes are VALID inputs to the routine under test
- Class A is a fixed input; class B is uniformly drawn from a
pre-built pool of K independent valid inputs
- Any timing difference is a real secret-content signal
All three cgo shared libraries build clean:
GOWORK=off go build -buildmode=c-shared -tags tfhe_encrypt_ct
GOWORK=off go build -buildmode=c-shared -tags tfhe_decrypt_ct
GOWORK=off go build -buildmode=c-shared -tags tfhe_bootstrap_ct
dudect.h fetched on demand via fetch.sh (not committed).
dudect_compat.h supplies _mm_mfence/__rdtsc on AArch64 hosts.
Submission-grade run pending (GATE-1 in CRYPTOGRAPHER-SIGN-OFF.md).
proofs/lean-easycrypt-bridge.md pins the 1:1 correspondence between
the EasyCrypt theory axioms in fhe/proofs/easycrypt/ and the Lean
theorems in ~/work/lux/proofs/lean/Crypto/FHE/TFHE.lean +
Crypto.Threshold.Lagrange.
Maps 5 axioms:
pbs_correctness <-> programmable_bootstrap_correct
shamir_inverse_eval <-> threshold_reconstructs_secret
reshare_preserves_secret <-> reshare_preserves_constant_term
pbs_noise_bound <-> pbs_noise_bound
decrypt_constant_time <-> (operational; no Lean counterpart)
EasyCrypt Tier A theories at fhe/proofs/easycrypt/:
TFHE_Correctness.ec - PBS correctness theorem
TFHE_Threshold_Keygen.ec - threshold-DKG correctness + privacy + reshare
TFHE_Noise_Growth.ec - per-op noise budgets + parameter-set invariant
lemmas/TFHE_CT.ec - CT obligations on hot path
Closure strategy: each top-level theorem reduced to small set of named
functional hypotheses imported from the luxfi/lattice/v7 reference
layer. Admit budget 0/0 across the pack.
Bridge map at proofs/lean-easycrypt-bridge.md pins axiom-to-theorem
correspondence 1:1 to Lean Mathlib (Crypto.Threshold.Lagrange).
Cites existing LaTeX at ~/work/lux/proofs/fhe/correctness.tex,
parameter-security.tex, dual-runtime-equivalence.tex.
Single LICENSING.md file referencing the canonical three-tier IP and
licensing strategy at github.com/luxfi/.github/blob/main/profile/README.md.
LICENSE file is unchanged; this only adds a navigational pointer.
Drop local SecurityProfile, ProfileClassical128, ProfileStrictPQ,
ProfileStrictPQHighValue, IsPostQuantum, String,
ProfileFromPQFlag. The strict-PQ Mode vocabulary lives in
github.com/luxfi/pq as the single source of truth across warp,
dex, evm, fhe, zap.
DefaultParamsForProfile (local enum) → DefaultParamsForMode
(pq.Mode): strict-PQ ⇒ PN9QP27_STD128Q (NIST FIPS 203 / 204
LMKCDEY); classical / hybrid ⇒ PN10QP27 (~128-bit classical,
dev-only). The FHE gate is parameter SELECTION, not refusal — no
classical ciphertext to refuse at the parameter layer, so the
ValidateMode call doesn't apply here.
ProfileStrictPQHighValue dropped: it was a no-op tier returning
the same PQ literal as the base strict-PQ profile. Once
STD192Q_LMKCDEY-aligned ParametersLiterals land, callers pick
them explicitly — the mode vocabulary stays single-purpose.
Single seam every Liquid FHE chain boot routes through to pick a
ParametersLiteral. Direct references to PN10QP27 / PN11QP54 /
PN9QP27_STD128Q in chain-config code are a bug — they bypass the
profile gate and can silently weaken the chain to classical-only
security.
ProfileClassical128 → PN10QP27 (~128 bit *classical*)
ProfileStrictPQ → PN9QP27_STD128Q (128 bit *quantum*,
OpenFHE STD128Q_LMKCDEY-aligned)
ProfileStrictPQHighValue → PN9QP27_STD128Q today; will return
STD192Q_LMKCDEY-aligned params once
published, no consumer churn
ProfileFromPQFlag(pq bool) wires this onto the chain-config "pq"
boolean the liquidity/operator writes into
/data/configs/chains/<id>/config.json. The Liquid FHE plugin reads
"pq":true from that file and asks DefaultParamsForProfile — strict-PQ
chains get STD128Q_LMKCDEY parameters at chain boot, with no
direct PN10QP27 reference anywhere on the boot path.
Tests pin the security guarantee byte-for-byte: ProfileStrictPQ
returns PN9QP27_STD128Q (not "some PQ literal" — the exact one), so a
regression that swaps the underlying literal name requires an
explicit test update.
wire/wire.go — fhe's wire-format hardening boundary. FHE wire formats
nest deeper than pulsar's (CKKS ciphertext = Vec<Poly> over RNS chain
of Vec<u64>) so MaxDepth = 5 + larger MaxFrameBytes (32 MiB) reflect
the deeper polynomial structure.
Tests:
* TestValidateCiphertextFrame_RejectsHugeLength — regression for
lattice issue #4 attack input class. Same substrate, same
rejection.
* TestValidateCiphertextFrame_OverCap — MaxFHESliceLen+1 rejected.
go.mod adds luxfi/math v1.3.0 dependency.
This completes Phase 5 of LP-107: every Lux Go protocol repo
(pulsar, lens, fhe, lattice) now consumes the math substrate for
bounded wire decoding. The same lattice issue #4 attack input is
rejected identically across all four consumers.
Exports NewCmd() returning a cobra.Command with keygen, encrypt,
decrypt, and eval subcommands. Wires directly to fhe.KeyGenerator,
fhe.Encryptor, and fhe.Decryptor -- no stubs.