tfhe: Phase 1 real Lagrange-interpolated distributed decryption (issue #20)#25
Open
abhicris wants to merge 1 commit into
Open
tfhe: Phase 1 real Lagrange-interpolated distributed decryption (issue #20)#25abhicris wants to merge 1 commit into
abhicris wants to merge 1 commit into
Conversation
…luxfi#20) Adds RealKeyShare / PartialDecrypt / CombineShares to protocols/tfhe, replacing the HMAC-stub gated by ALLOW_FAKE_TFHE_FOR_TESTING_ONLY with genuine M-of-N threshold decryption that never materialises the master key on any party. Scheme (textbook AJL+12-style RLWE threshold decryption, single-bit): - DealRealKeyShares performs trusted-dealer keygen, then Shamir-shares each LWE-secret-key polynomial coefficient over F_QLWE and zeroises the master polynomial before returning. - RealKeyShare.PartialDecrypt computes <a, s_i> against the LWE half of an fhe.Ciphertext via MulCoeffsMontgomery + INTT, matching the rlwe.Decryptor convention exactly. Optional flood-noise term plumbed through the API (sampler deferred to Phase 2). - CombineShares uses polynomial.LagrangeAtZeroBigInt (added in PR luxfi#24 for this exact purpose) to interpolate the partials at x=0 in F_QLWE, adds the constant term of b, and rounds against the bit encoding. Tests: parametrised M-of-N harness covering N=5/M=3 and N=21/M=11 (the production-validator scale from the acceptance criteria), plus N=3/M=2, N=7/M=4, and a PN9QP28_STD128 spot-check. Additional tests cover insufficient-quorum refusal, cross-ciphertext partial-mix rejection, and a structural check that no two parties share the same first coefficient (catches the legacy master-key-replication regression). Deferred to Phase 2 (documented in package doc + PR body): - Formal noise-growth proof bounds for PN9QP28_STD128 - Active-adversary verification (Feldman VSS on partials) - Public-DKG variant (LP-181/Magnetar track) The existing fake-threshold path in tfhe.go (UnderlyingKey: masterSK, HMAC PartialDecrypt) is preserved for downstream compile compatibility; the ALLOW_FAKE_TFHE_FOR_TESTING_ONLY env-var deletion follows in a later PR once downstream consumers migrate to the real API surface. Refs: luxfi#20 (Phase 1), luxfi#24 (LagrangeAtZeroBigInt precursor)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 of issue #20. Replaces the HMAC-stub
PartialDecrypt/CombineSharesinprotocols/tfhe/tfhe.gowith a real Shamir-over-F_QLWE distributed decryption that never materialises the master key on any party.Builds on the
polynomial.LagrangeAtZeroBigInthelper added in #24 specifically as the precursor for this PR.What lands
New file
protocols/tfhe/partial_decrypt.gowith the real surface:DealRealKeyShares(ctx, params, threshold, parties)— trusted-dealer keygen. Generates the master FHE keypair, Shamir-shares each LWE-secret-key polynomial coefficient overF_QLWE, evaluates the shares at every party's x-coordinate, and zeroises the master coefficient buffer before returning. No party'sRealKeySharecarries the master key.RealKeyShare.PartialDecrypt(ct, flood)— computes<a, s_i>against the LWE half of anfhe.CiphertextbyMulCoeffsMontgomery+INTT, matchingrlwe.Decryptor's convention exactly. Optional flood-noise term plumbed through the API.CombineShares(params, ct, partials, threshold)— usespolynomial.LagrangeAtZeroBigInt(the precursor from pkg/math/polynomial: add LagrangeAtZeroBigInt for tfhe combine (issue #20 precursor) #24) to interpolate the partials atx=0inF_QLWE, adds the constant term ofb, and rounds against the{Q/8, 7Q/8}bit encoding.Plus a binding-hash check so partials produced against ciphertext A cannot be silently combined with partials from ciphertext B.
Scheme (textbook AJL+12-style, single-bit form)
Each LWE-secret-key polynomial coefficient
s[k] ∈ Z_QLWEis Shamir-shared in F_QLWE. By Shamir linearity over the same field, for any authorised subset T of size ≥ threshold:For an LWE ciphertext
(a, b)withb = Δm + e − a·s (mod QLWE), each party returnsp_i = <a, s_i>(the constant term of the ring product, asrlwe.Decryptorextracts it). The combiner runs:and rounds against the bit encoding to recover the plaintext bit. The whole reduction is exact mod QLWE — noise structure is preserved because no operation leaves F_QLWE.
Tests
Parametrised M-of-N harness in
partial_decrypt_test.go:TestPartialDecrypt_3of2TestPartialDecrypt_5of3TestPartialDecrypt_7of4TestPartialDecrypt_21of11TestPartialDecrypt_STD128_5of3PN9QP28_STD128(OpenFHE STD128_LMKCDEY)Plus three structural / negative tests:
TestPartialDecrypt_RejectsInsufficient— combine refuses with< thresholdpartials.TestPartialDecrypt_BindingHashRejectsMisroute— partials bound to one ciphertext cannot be reused against another.TestPartialDecrypt_MasterKeyNotMaterialisedOnParty— structural check that no two parties hold the same first coefficient (catches the legacyUnderlyingKey: masterSKregression).Every test passes locally on Go 1.26.3 with both
PN10QP27andPN9QP28_STD128.Deferred to Phase 2 (NOT in this PR)
Documented in the package doc and tracked under issue #20:
PN9QP28_STD128. The Phase 1 API plumbs thefloodterm throughPartialDecryptbut defaults to no flooding. Phase 2 wires in a χ_flood sampler and adds noise-budget assertions in the test harness against the STD128 parameter set.p_iis not currently detected. Phase 2 adds Feldman/Pedersen VSS commitments on partial shares so the combiner can reject deviating partials before combine.The existing fake-threshold path in
tfhe.go(the one gated byALLOW_FAKE_TFHE_FOR_TESTING_ONLY=1) is intentionally preserved for now so downstream consumers keep compiling. Deletion of that env var + path follows in a later PR once consumers migrate to the new API surface.Refs
polynomial.LagrangeAtZeroBigIntfor exactly this combine)