Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Knowing the maths without knowing the ecosystem leaves you stuck — every HE project is a choice of library, parameter set, and standards body to follow, and that choice constrains what you can build. In 2024, four library families dominate: Microsoft SEAL (BFV/BGV/CKKS, C++, mature), OpenFHE (the IBM/HElib + PALISADE merger, all major schemes, Apache 2.0, broad scheme support), TFHE-rs (Zama's Rust TFHE for boolean circuits, very fast bootstrapping), and Concrete (Zama's higher-level compiler that targets TFHE-rs). The HomomorphicEncryption.org consortium publishes parameter standards (the basis for FIPS-style guidance), and NIST's post-quantum process indirectly drives lattice parameter choices. Adoption is real: Apple ships HE in iOS for private federated learning, Microsoft uses it for password monitor, several blockchain projects (FHENIX, Mind Network) are building HE-as-L2. None of this is a fairy tale anymore — it's libraries you can pip install today. This is the map you need before picking a project.
The four-axis decision is: scheme family (BFV/BGV for exact integer, CKKS for approximate real, TFHE for fast boolean), library (SEAL, OpenFHE, TFHE-rs, Concrete), language binding (C++, Python, Rust, Go), and parameter set (security level , ring dimension , modulus ). The HE Standard tables tell you which pairs hit 128-bit security; the libraries enforce them as defaults.
scheme='bfv' with scheme='ckks' and encrypting floats instead of ints. CKKS gives you approximate real arithmetic — note the small error in the decrypted result. When is that acceptable, and when would you stick with BFV?// main.go
// A real HE 'hello world' using lattigo (pure-Go BFV implementation).
// go get github.com/tuneinsight/lattigo/v5 — then run with: go run main.go
package main
import (
"fmt"
"github.com/tuneinsight/lattigo/v5/core/rlwe"
"github.com/tuneinsight/lattigo/v5/schemes/bfv"
)
func main() {
// BFV parameters: ring degree n=8192 (≈2^13), plaintext modulus ~2^20.
params, _ := bfv.NewParametersFromLiteral(bfv.ParametersLiteral{
LogN: 13,
LogQ: []int{56, 55, 55},
PlainModulus: 786433, // prime just above 2^20, satisfies t ≡ 1 mod 2n
})
kgen := rlwe.NewKeyGenerator(params)
sk, pk := kgen.GenKeyPairNew()
rlk := kgen.GenRelinearizationKeyNew(sk)
encoder := bfv.NewEncoder(params)
encryptor := rlwe.NewEncryptor(params, pk)
decryptor := rlwe.NewDecryptor(params, sk)
evaluator := bfv.NewEvaluator(params, rlwe.NewMemEvaluationKeySet(rlk))
x, y := uint64(137), uint64(246)
// Encode scalars into plaintext slots then encrypt.
ptX := bfv.NewPlaintext(params, params.MaxLevel())
ptY := bfv.NewPlaintext(params, params.MaxLevel())
encoder.Encode([]uint64{x}, ptX)
encoder.Encode([]uint64{y}, ptY)
ctX, _ := encryptor.EncryptNew(ptX)
ctY, _ := encryptor.EncryptNew(ptY)
// Homomorphic addition and multiplication.
ctSum, _ := evaluator.AddNew(ctX, ctY)
ctProd, _ := evaluator.MulNew(ctX, ctY)
evaluator.Relinearize(ctProd, ctProd) // tame the noise after multiplication
// Decrypt and decode.
ptSum := decryptor.DecryptNew(ctSum)
ptProd := decryptor.DecryptNew(ctProd)
decSum := make([]uint64, params.N())
decProd := make([]uint64, params.N())
encoder.Decode(ptSum, decSum)
encoder.Decode(ptProd, decProd)
fmt.Println("decrypted sum :", decSum[0])
fmt.Println("decrypted prod :", decProd[0])
fmt.Println("plaintext sum :", x+y)
fmt.Println("plaintext prod :", x*y)
// Inspect the parameters — these are the real knobs from the HE Standard.
fmt.Println("ring n :", params.N())
fmt.Println("plain modulus:", params.PlainModulus())
fmt.Println("log coeff Q :", params.LogQ())
}
go run main.go