FHE
Aug 13, 2025
Generalized BFV in Practice
Discover how the generalized BFV scheme overcomes classical fully homomorphic encryption (FHE) limits, enabling high data throughput and dee...
FHE
Aug 26, 2025

Understanding the MPC Baseline
Security Model
The MHE Paradigm
MBFV-based MPC Protocol
Distributed Secret Key Generation
Collective Public Key Generation
Relinearization Key Generation
Collective Key Switching
Public Key Switching
Bridging Between Representations
Collective Bootstrapping
Example
Conclusion
Acknowledgements
Secure multiparty computation (MPC) delivers on cryptography's ambitious promise: computing on private inputs while maintaining correctness and distributing trust. Yet this promise comes at a steep price in communication overhead. Every multiplication in a secret-sharing protocol demands an interactive round, meaning a circuit with multiplicative depth requires at least synchronized exchanges between all parties. When you factor in the common broadcast patterns where message count per layer scales as , the bottleneck becomes clear, throughput is limited not by arithmetic speed but by network latency. The liveness requirements make things even more fragile, as all parties must remain online throughout computation, and a single straggler can stall everything while a dropout might force a complete restart.
Multiparty Homomorphic Encryption (MHE) tackles these exact pain points, communication overhead and synchronization requirements, without sacrificing the distributed trust model that makes MPC valuable in the first place. The core insight is elegant: instead of sharing the data, we share the decryption power. Parties collaborate to generate a public key while keeping the corresponding secret key additively shared among themselves. This setup phase unlocks something remarkable, anyone can evaluate arbitrary circuits on ciphertexts completely non-interactively, with no parties needing to be online during computation. The only interaction happens at the output boundary, where parties collaborate in a short, bandwidth-efficient step to deliver the final result.
This architectural shift fundamentally transforms the cost structure from "one round per multiplication layer" to "one short round per result," with per-party communication scaling linearly in and supporting asynchronous, wide-area deployments.
MPC protocols are analyzed under several choices:
Adversary behavior.
Corruption pattern.
Threshold / who’s stronger.
Goals (what you prove).
Proof frameworks / composability.
What we assume here. Throughout this post, we work in a dishonest-majority, semi-honest model with static corruptions and no private channels.
To appreciate what we're improving upon, let's briefly examine how traditional MPC handles computation. In additive secret sharing over a field , a secret gets split into shares where . The beauty of this approach lies in its linearity, additions and scalar multiplications become purely local operations that require no communication whatsoever.
Multiplication presents a different challenge entirely. While parties can locally compute , the result isn't a proper sharing of . The standard solution relies on Beaver triples, authenticated sharings of where . Given sharings of and along with a triple, the online multiplication protocol opens the masked values and through synchronized broadcasts, then computes the result locally using the identity .
This preprocessing paradigm, exemplified by protocols like SPDZ, splits the work into an offline phase that produces authenticated triples using somewhat homomorphic encryption, followed by a lightweight online phase that consumes these triples. While this separation enables fast online computation even in malicious settings, it doesn't eliminate the fundamental bottleneck: you still need one interactive opening per multiplication layer.
The practical costs add up quickly. Each opening typically involves broadcasts, creating messages per layer unless you deploy more sophisticated network primitives. A single straggler delays the entire round, dropouts require resharing or abort protocols, and in malicious settings each opening carries additional MAC checks and consistency verification overhead. Even with vectorized batch operations, the core constraint remains unchanged, additions are free, but multiplications demand interaction.
This is the baseline we're working to improve: replacing those interactive multiplication layers with non-interactive evaluation while preserving the "no single party can decrypt alone" trust model.

Our goal is straightforward yet ambitious: keep computation non-interactive like standard homomorphic encryption while keeping decryption power distributed like MPC. The setup phase has parties run a distributed key generation (DKG) for an RLWE/BFV scheme, publishing a joint public key along with the necessary evaluation keys for relinearization, Galois operations, and bootstrapping. The secret key is never assembled, instead, it remains additively shared as , with party holding only .
Once setup completes, the workflow becomes remarkably simple. Any data owner can encrypt their input as , and from that point forward, anyone, even a completely untrusted server, can compute arbitrary functions by evaluating without any interaction with the parties or learning anything about the inputs. This eliminates the "one round per multiplication layer" bottleneck entirely.
The only remaining interaction happens at output time, where parties collaborate in a collective key switch (CKS) to move from the shared secret to a target key . Often this target is for collective decryption, or it might be another party's public key through a public key switch (PCKS) protocol. The crucial point is that this heavy computation, the entire circuit evaluation, runs offline and non-interactively, with parties only coming online once for a single short transcript whose size per party scales linearly in .
The contrast with traditional MPC is illuminating. Where MPC shares the data itself, replacing each secret with additive shares and running computation directly on those shares, MHE shares the decryption power instead. Inputs get encrypted once under a joint public key while the secret key remains additively shared. Anyone can evaluate on ciphertexts non-interactively, and only at the end do parties collaborate to decrypt.

This section presents our concrete instantiation using BFV/RLWE encryption over power-of-two cyclotomic rings and with scaling parameter . The protocol involves parties who jointly hold an additively shared secret key while publishing a joint public key and making the necessary evaluation keys available for relinearization, Galois operations, and bootstrapping.
The workflow follows a clean separation of concerns. Data owners encrypt messages in under the public key , an untrusted evaluator performs all homomorphic work offline without any party interaction, and at the end a qualified set of parties collaborates to release results either collectively or to a designated receiver . Throughout this process we maintain a public-transcript discipline where all interactive steps can be logged on an authenticated broadcast channel without requiring private communication channels.
The foundation of our approach lies in creating a BFV secret key that never gets assembled while still behaving like a standard small-norm key. The process is surprisingly simple and requires no communication whatsoever. Each party locally samples a small share using the standard BFV secret key generation algorithm, and the collective secret key is defined additively as .
This non-interactive approach means no messages get exchanged, no private channels are needed, and the full secret key never exists anywhere in the clear. The trade-off is that , being a sum of small polynomials, has norm that increases with . Parameter selection must account for this growth, but it doesn't create a fundamental bottleneck even for moderately large party counts.
Once parties complete this distributed key generation, they proceed to derive the collective public key and evaluation keys using the public-transcript procedures that follow, all building on the additive structure of the shared secret .
Creating a BFV public key for the collective secret without ever assembling requires careful coordination, but the protocol itself is elegantly straightforward. The key insight is using a common reference string to ensure all parties work with the same random polynomial . In the passive security model, this can be derived deterministically from a shared seed using a pseudorandom function like BLAKE2b, guaranteeing that everyone derives identical values.
Each party samples a small error term from the designated error distribution and publishes their contribution . The aggregation step is straightforward, anyone can sum these contributions to obtain
which gives us the collective public key .
This construction maintains the exact BFV structure with secret and aggregated error . The security argument is clean: since is uniformly distributed in and the aggregated error has small norm, the pair becomes computationally indistinguishable from a standard BFV public key under the decisional RLWE assumption.
While the worst-case norms and grow linearly in , this parameter scaling can be accounted for during setup without creating fundamental scalability barriers for reasonable party counts. The communication profile is highly efficient, requiring only one broadcast per party with a completely public transcript that needs no private channels. With the collective public key established, standard BFV encryption proceeds exactly as in the single-key setting, but now operating under the shared secret .
Generating relinearization keys in the multiparty setting requires more sophistication than public key generation, since we need to "encrypt" under the shared secret without using the public encryption oracle and while maintaining low noise levels.
The following solution uses public coins combined with the additive structure of the shared secret to emulate the quality of centralized BFV relinearization keys. The protocol takes as public input a vector sampled from the common reference string along with a decomposition basis , while each party contributes its private key share .
The protocol proceeds in two coordinated steps. In the first step, each party samples a small masking term along with error vectors , then publishes the contribution
When aggregated, these contributions yield
where , , and .
The second step uses these aggregated values to complete the relinearization key construction. Each party samples fresh error terms and publishes
Aggregating these final contributions produces the relinearization key
The communication requirements are modest, two public broadcasts per party across digits, with no private channels required. This construction successfully uses public coins and the shared secret structure to emulate centralized BFV relinearization key quality, which substantially improves the noise profile of relinearized ciphertexts.
The collective key switching (CKS) protocol provides the crucial bridge between the non-interactive evaluation phase and result delivery. Given a ciphertext encrypted under the collective secret , CKS switches it to an output key known collectively by the parties without revealing any individual share . The target key might be zero for decryption purposes or represent a new shared key after rotating the access set.
The protocol design prioritizes simplicity and efficiency. Each party contributes by sampling fresh smudging noise and publishing , where represents their share of the target key. Any aggregator can then compute with , and output the switched ciphertext where .
To see the correctness run BFV decryption under
provided the post-switch noise remains within the BFV decoding radius . Each contribution gets masked by fresh noise , ensuring that individual terms remain information-theoretically hidden while the transcript reveals only what's implied by the output ciphertext .
The communication profile is excellent, just one broadcast of a ring element per party plus a single aggregation step. This represents the "short boundary round" that concludes an otherwise completely offline evaluation. For the special case of decryption, setting gives us
allowing direct decoding under the zero key to recover the message .
While collective key switching handles cases where parties collectively know the target secret key, public key switching (PCKS) addresses the scenario where results must be delivered to an external receiver who has only exposed a public key for some unknown secret . This capability is essential for "push" delivery of outputs to recipients outside the decryption access set, where CKS would not be applicable.
The protocol maintains the same public-transcript discipline while handling the additional complexity of not knowing the target secret key. Each party samples small terms and fresh noises from the designated error distributions, then publishes
After aggregation, the switched ciphertext becomes where and .
Using and , for , decryption under yields
as long as the total noise stays within the BFV decoding radius, where
Hence guarantees correctness.
The security argument mirrors CKS, each contribution gets masked by fresh noise and public-key terms, making transcripts simulatable given the output ciphertext in the semi-honest model. No party learns the target secret and no private channels are required. The cost profile matches CKS with one broadcast of two ring elements per party plus aggregation, enabling efficient delivery to external receivers while maintaining all security properties.
The ability to move seamlessly between ciphertexts (MHE representation) and additive shares (MPC representation) using single-round protocols with public transcripts opens up powerful hybrid computation strategies. These bridging protocols, Enc2Share and Share2Enc, serve as the fundamental building blocks for our collective bootstrapping approach.
The Enc2Share protocol starts with a BFV ciphertext encrypting message under the collective key and produces additive shares such that . The approach cleverly combines collective key switching with local masking to maintain privacy throughout the conversion.
Each party samples a fresh mask and smudging noise , then computes a masked partial decryption
Parties publish their values while the designated party withholds its own term. The public contributions aggregate as
where and .
Party then adds its secret piece locally and applies the BFV rounding map to obtain its share:
since and rounding removes the bounded noise.
Privacy protection comes from the masks and fresh smudging noise applied to each published , ensuring no party learns the underlying message while maintaining simulatable transcripts. Only combines the public sum with its private term to obtain its share ; no party ever sees itself. The communication cost is minimal, one ring element per party except for the holder of the private residual, plus one decoding operation. The role of is arbitrary and can rotate across different protocol executions.
The Share2Enc protocol reverses this process, starting from additive shares of message and producing a single BFV ciphertext encrypting under the collective key. The core insight treats as a "plaintext ciphertext" under the trivial key zero, then applies collective key switching to move from key zero to the shared key .
Using a public element sampled from the common reference string, each party samples fresh noise and computes a contribution
with the noise providing the usual smudging. All parties publish their values simultaneously in a single round.
The combiner (any party) aggregates these contributions to form the output ciphertext:
where , which has the standard BFV encryption shape. Correctness follows since
and the BFV rounding map recovers with overwhelming probability under the standard decoding bound.
The intuition is that each party contributes its share of the collective secret key term (namely ), its share of the scaled message , and its own fresh noise . Summing these contributions yields the proper BFV ciphertext structure. Alternatively, one can view as a "plaintext ciphertext under key zero," and the protocol realizes a collective key switch from key zero to the shared key .
Privacy protection comes from the fresh smudging noise in each contribution, ensuring that each published pair appears as a standard RLWE sample with secret and embedded message . The communication cost is minimal, one ring element per party in a single round with a public transcript. The combiner requires no secret information, only the public values and the published terms, yet produces a ciphertext that only the parties collectively can decrypt.
BFV ciphertexts inevitably accumulate noise through addition and multiplication operations, and once this noise approaches the decoding bound, some form of refreshing becomes necessary. Traditional bootstrapping evaluates the decryption function homomorphically, which is computationally expensive. Our collective bootstrapping approach trades this heavy computation for a single short interactive round among the parties who already share the secret key, producing a ciphertext that is essentially a fresh encryption of the same plaintext.
The approach leverages the BFV decryption structure where a ciphertext decrypts to plaintext through the formula:
The protocol takes as public input a common reference string element along with the ciphertext that needs refreshing. Each party contributes its secret share where , and the protocol produces a refreshed ciphertext whose noise variance has been reset to approximately .
Each party performs the same local computation during the single communication round. Party begins by sampling a mask share that will be used to hide the underlying plaintext, along with smudging noise and fresh noise . These random elements ensure that no individual party can learn anything about the plaintext being refreshed. The party then publishes two components:
When these contributions are aggregated across all parties, we obtain values that capture the collective secret while maintaining the masking structure:
Here represents the sum of all mask shares, while and are the aggregated noise terms that provide the necessary randomization for security.
The combiner uses these aggregated values to construct a refreshed ciphertext that encrypts the same plaintext with reset noise. The construction follows a specific pattern that ensures the result has the proper BFV structure:
This construction might appear complex, but it follows naturally from the BFV decryption structure and the additive properties of the secret sharing scheme.
The key insight lies in understanding what happens inside the rounding operation. When we compute , we're essentially performing a partial decryption while maintaining the additive structure:
The rounding and scaling operation extracts the plaintext information while removing the old noise:
where the "small" term represents residual effects from and that get controlled by the rounding process. Substituting this back into our construction gives us:
This is precisely the structure of a valid BFV encryption of plaintext under secret key , but now the noise term comes entirely from the fresh randomness rather than the accumulated noise from previous operations.
What makes this protocol qualify as bootstrapping is the complete renewal of the noise profile. The refreshed ciphertext has noise that comes from the sum with variance approximately , plus small contributions from the rounding process. Crucially, this new noise is completely independent of the old accumulated noise that was threatening to breach the decoding bound.
The smudging noises serve a different but equally important purpose, they ensure the protocol transcript can be simulated without knowledge of the plaintext , maintaining privacy throughout the refreshing process. This means the resulting ciphertext behaves exactly like a brand-new encryption of the same plaintext , enabling further homomorphic evaluation without the noise constraints that necessitated bootstrapping in the first place.
The efficiency profile is compelling: the output ciphertext encrypts the same plaintext with noise reset to fresh levels scaling as . Privacy is maintained since no party learns and all transcripts remain simulatable. The communication cost involves just one broadcast round where each party contributes two ring elements, giving overall complexity linear in the number of parties .
The following example demonstrates a 3-party Multiparty Homomorphic Encryption (MHE) workflow using Lattigo’s BGV scheme. Three locally simulated parties run collective public-key generation (CKG), each encrypts one integer, a coordinator adds the ciphertexts, and the result is re-encrypted to a receiver via Public Key Switch (PCKS) and decrypted, so no single party ever holds the joint secret key.
package main
import (
"flag"
"fmt"
"log"
"github.com/tuneinsight/lattigo/v6/core/rlwe"
"github.com/tuneinsight/lattigo/v6/multiparty"
"github.com/tuneinsight/lattigo/v6/ring"
"github.com/tuneinsight/lattigo/v6/schemes/bgv"
"github.com/tuneinsight/lattigo/v6/utils/sampling"
)
type party struct {
sk *rlwe.SecretKey
}
func main() {
// CLI
a := flag.Int("a", 7, "party A integer")
b := flag.Int("b", 12, "party B integer")
c := flag.Int("c", 20, "party C integer")
flag.Parse()
// Parameters: small demo (N=2^12, t=65537). Very short Q-chain for adds only.
params, err := bgv.NewParametersFromLiteral(bgv.ParametersLiteral{
LogN: 12,
PlaintextModulus: 65537,
LogQ: []int{54, 55},
LogP: []int{55},
})
check(err)
encoder := bgv.NewEncoder(params)
// Receiver keypair (for output via PCKS)
tsk, tpk := rlwe.NewKeyGenerator(params).GenKeyPairNew()
// 3 parties with independent secret shares
var parties = make([]party, 3)
for i := range parties {
parties[i].sk = rlwe.NewKeyGenerator(params).GenSecretKeyNew()
}
// Collective Public Key Generation (CKG)
crs, err := sampling.NewKeyedPRNG([]byte("mpbgv-sum-ckg-seed"))
check(err)
pk := execCKG(params, crs, parties)
// Each party: encode & encrypt its scalar under collective pk
ctA := encryptScalar(params, pk, encoder, *a)
ctB := encryptScalar(params, pk, encoder, *b)
ctC := encryptScalar(params, pk, encoder, *c)
// Homomorphic addition
eval := bgv.NewEvaluator(params, nil, false)
tmp := bgv.NewCiphertext(params, 1, params.MaxLevel())
sum := bgv.NewCiphertext(params, 1, params.MaxLevel())
check(eval.Add(ctA, ctB, tmp))
check(eval.Add(tmp, ctC, sum))
// Re-encrypt result to receiver’s pk (PCKS)
encOut := execPCKS(params, tpk, sum, parties)
// Receiver decrypts (Decrypt returns no value in v6)
decryptor := rlwe.NewDecryptor(params, tsk)
pt := bgv.NewPlaintext(params, encOut.Level())
decryptor.Decrypt(encOut, pt)
// Decode first slot
dec := make([]uint64, params.MaxSlots())
check(encoder.Decode(pt, dec))
res := int(dec[0])
fmt.Printf("Inputs: a=%d, b=%d, c=%d\n", *a, *b, *c)
fmt.Printf("Homomorphic sum (decrypted): %d\n", res)
if res == *a+*b+*c {
fmt.Printf("Check: %d+%d+%d = %d ✅\n", *a, *b, *c, res)
} else {
fmt.Printf("Check: %d+%d+%d != %d ❌\n", *a, *b, *c, res)
}
}
// encryptScalar encodes x in slot 0 and encrypts it under the collective pk.
func encryptScalar(params bgv.Parameters, pk *rlwe.PublicKey, enc *bgv.Encoder, x int) *rlwe.Ciphertext {
pt := bgv.NewPlaintext(params, params.MaxLevel())
batch := make([]uint64, params.MaxSlots())
if x < 0 {
t := uint64(params.PlaintextModulus())
batch[0] = (t - uint64(-x)%t) % t
} else {
batch[0] = uint64(x)
}
check(enc.Encode(batch, pt))
encryptor := rlwe.NewEncryptor(params, pk)
ct := bgv.NewCiphertext(params, 1, params.MaxLevel())
check(encryptor.Encrypt(pt, ct))
return ct
}
// execCKG runs multiparty collective public-key generation.
func execCKG(params bgv.Parameters, crs sampling.PRNG, P []party) *rlwe.PublicKey {
ckg := multiparty.NewPublicKeyGenProtocol(params)
crp := ckg.SampleCRP(crs)
shares := make([]multiparty.PublicKeyGenShare, len(P))
for i := range shares {
shares[i] = ckg.AllocateShare()
ckg.GenShare(P[i].sk, crp, &shares[i])
}
combined := ckg.AllocateShare()
for i := range shares {
ckg.AggregateShares(shares[i], combined, &combined)
}
pk := rlwe.NewPublicKey(params)
ckg.GenPublicKey(combined, crp, pk)
return pk
}
// execPCKS re-encrypts ctIn to tpk using the PublicKeySwitch protocol.
func execPCKS(params bgv.Parameters, tpk *rlwe.PublicKey, ctIn *rlwe.Ciphertext, P []party) *rlwe.Ciphertext {
pcks, err := multiparty.NewPublicKeySwitchProtocol(
params,
ring.DiscreteGaussian{Sigma: 1 << 30, Bound: 6 * (1 << 30)},
)
check(err)
shares := make([]multiparty.PublicKeySwitchShare, len(P))
for i := range shares {
shares[i] = pcks.AllocateShare(params.MaxLevel())
pcks.GenShare(P[i].sk, tpk, ctIn, &shares[i])
}
combined := pcks.AllocateShare(params.MaxLevel())
for i := range shares {
check(pcks.AggregateShares(shares[i], combined, &combined))
}
out := bgv.NewCiphertext(params, 1, params.MaxLevel())
pcks.KeySwitch(ctIn, combined, out)
return out
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
Multiparty Homomorphic Encryption fundamentally reshapes collaborative private computation by moving from "share the data" to "share the decryption power." This paradigm shift transforms the cost profile from needing one interactive round per multiplication layer to requiring just one short round per result, with communication scaling linearly in the number of parties.
The MBFV toolkit makes this practical through carefully designed protocols that maintain BFV-level security while enabling distributed operations. Collective key generation produces standard-looking public keys without ever assembling the secret, and the key-switching protocols (both CKS and PCKS) provide efficient output delivery with public transcripts only.
Perhaps most importantly, this approach enables truly asynchronous computation where heavy evaluation happens offline without requiring all parties to be online simultaneously. The only synchronization point occurs at output time, dramatically improving practical deployability compared to traditional MPC protocols that stall on stragglers and struggle with network partitions.
This post is inspired by “Multiparty Homomorphic Encryption from Ring-Learning-with-Errors” by Victor Mouchet, Juan Ramón Troncoso-Pastoriza, Jean-Philippe Bossuat, and Jean-Pierre Hubaux. Big thanks to the maintainers of Lattigo whose implementations helped shape intuition about practical parameterization and costs.