diff --git a/README.md b/README.md index 49b366c..f2ce415 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ > TFHE-go is still under heavy development. There may be backward-incompatible changes at any time. **TFHE-go** is a Go implementation of TFHE[[CGGI16](https://eprint.iacr.org/2016/870)] and Multi-Key TFHE[[KMS22](https://eprint.iacr.org/2022/1460)] scheme. It provides: -- Support for binary and integer TFHE and its multi-key variant +- Support for binary and integer TFHE and its multi-key variant, as well as advanced algorithms such as BFV-style evaluation, PBSManyLUT[[CLOT21](https://eprint.iacr.org/2021/729)], Circuit Bootstrapping[[WHS+24](https://eprint.iacr.org/2024/1318)] and LMKCDEY(FHEW)[[LMK+22](https://eprint.iacr.org/2022/198)]. - Pure Go implementation, along with SIMD-accelerated Go Assembly on amd64 platforms - Comparable performance to state-of-the-art C++/Rust libraries - Readable code and user-friendly API using modern Go features like generics @@ -143,10 +143,10 @@ fmt.Println(dec.DecryptLWEBool(ctOut)) // false ## Benchmarks All benchmarks were measured on a machine equipped with Intel Xeon Platinum 8268 CPU @ 2.90GHz and 384GB of RAM. -|Operation|TFHE-go|TFHE-rs (v0.8.0)| +|Operation|TFHE-go|TFHE-rs (v0.11.0)| |---------|-------|-------| -|Gate Bootstrapping|9.38ms|15.70ms| -|Programmable Bootstrapping (6 bits)|21.52ms|105.05ms| +|Gate Bootstrapping|9.38ms|14.17ms| +|Programmable Bootstrapping (6 bits)|21.52ms|107.96ms| You can use the standard go test tool to reproduce benchmarks: ``` @@ -190,3 +190,6 @@ TFHE-go logo is designed by [@mlgng2010](https://www.instagram.com/mlgng2010/), - New Secret Keys for Enhanced Performance in (T)FHE (https://eprint.iacr.org/2023/979) - TFHE Public-Key Encryption Revisited (https://eprint.iacr.org/2023/603) - Towards Practical Multi-key TFHE: Parallelizable, Key-Compatible, Quasi-linear Complexity (https://eprint.iacr.org/2022/1460) +- Improved Programmable Bootstrapping with Larger Precision and Efficient Arithmetic Circuits for TFHE (https://eprint.iacr.org/2021/729) +- FHEW-like Leveled Homomorphic Evaluation: Refined Workflow and Polished Building Blocks (https://eprint.iacr.org/2024/1318) +- Efficient FHEW Bootstrapping with Small Evaluation Keys, and Applications to Threshold Homomorphic Encryption (https://eprint.iacr.org/2022/198) diff --git a/mktfhe/params.go b/mktfhe/params.go index f887a31..7b2f10e 100644 --- a/mktfhe/params.go +++ b/mktfhe/params.go @@ -115,17 +115,19 @@ func (p ParametersLiteral[T]) WithBootstrapOrder(bootstrapOrder tfhe.BootstrapOr // Unless you are a cryptographic expert, DO NOT set parameters by yourself; // always use the default parameters provided. func (p ParametersLiteral[T]) Compile() Parameters[T] { + singleKeyParameters := p.SingleKeyParametersLiteral.Compile() + switch { case p.PartyCount <= 0: panic("PartyCount smaller than zero") - case p.SingleKeyParametersLiteral.GLWERank != 1: + case singleKeyParameters.GLWERank() != 1: panic("Multi-Key TFHE only supports GLWE dimension 1") - case p.SingleKeyParametersLiteral.LookUpTableSize != 0 && p.SingleKeyParametersLiteral.LookUpTableSize != p.SingleKeyParametersLiteral.PolyDegree: + case singleKeyParameters.LookUpTableSize() != singleKeyParameters.PolyDegree(): panic("Multi-Key TFHE only supports LookUpTableSize equal to PolyDegree") } return Parameters[T]{ - singleKeyParameters: p.SingleKeyParametersLiteral.Compile(), + singleKeyParameters: singleKeyParameters, partyCount: p.PartyCount, diff --git a/tfhe/bootstrap_lut.go b/tfhe/bootstrap_lut.go index 5c993cf..02110c9 100644 --- a/tfhe/bootstrap_lut.go +++ b/tfhe/bootstrap_lut.go @@ -80,9 +80,9 @@ func (e *Evaluator[T]) GenLookUpTableCustomAssign(f func(int) int, messageModulu // GenLookUpTableFullCustom generates a lookup table based on function f using custom messageModulus and scale. // Output of f is encoded as-is. -func (e *Evaluator[T]) GenLookUpTableFullCustom(f func(int) T, messageModulus, scale T) LookUpTable[T] { +func (e *Evaluator[T]) GenLookUpTableFullCustom(f func(int) T, messageModulus T) LookUpTable[T] { lutOut := NewLookUpTable(e.Parameters) - e.GenLookUpTableFullAssign(f, lutOut) + e.GenLookUpTableFullCustomAssign(f, messageModulus, lutOut) return lutOut } diff --git a/xtfhe/bfv_evaluator.go b/xtfhe/bfv_evaluator.go index cece735..03d5808 100644 --- a/xtfhe/bfv_evaluator.go +++ b/xtfhe/bfv_evaluator.go @@ -36,7 +36,7 @@ type bfvEvaluationBuffer[T tfhe.TorusInt] struct { ctTensorFourier tfhe.FourierGLWECiphertext[T] // ctPermute is the permuted ciphertext for BFV automorphism. ctPermute tfhe.GLWECiphertext[T] - // ctPack is the permuted ciphertext for RingPack. + // ctPack is the permuted ciphertext for LWEToGLWECiphertext. ctPack tfhe.GLWECiphertext[T] } diff --git a/xtfhe/bfv_keygen.go b/xtfhe/bfv_keygen.go index 5747a3d..568513c 100644 --- a/xtfhe/bfv_keygen.go +++ b/xtfhe/bfv_keygen.go @@ -5,6 +5,15 @@ import ( "github.com/sp301415/tfhe-go/tfhe" ) +// BFVEvaluationKey is a keyswitching key for BFV type operations. +// It holds relinearization and automorphism keys. +type BFVEvaluationKey[T tfhe.TorusInt] struct { + // RelinKey is a relinearization key. + RelinKey tfhe.GLWEKeySwitchKey[T] + // GaloisKeys is a map of automorphism keys. + GaloisKeys map[int]tfhe.GLWEKeySwitchKey[T] +} + // BFVKeyGenerator generates keyswitching keys for BFV type operations. // // BFVKeyGenerator is not safe for concurrent use. @@ -17,50 +26,36 @@ type BFVKeyGenerator[T tfhe.TorusInt] struct { // Parameters is a parameter set for this BFVKeyGenerator. Parameters tfhe.Parameters[T] - - // KeySwitchParams is a gadget parameter set for keyswitching keys in BFV type operations. - KeySwitchParams tfhe.GadgetParameters[T] } // NewBFVKeyGenerator creates a new BFVKeyGenerator. -func NewBFVKeyGenerator[T tfhe.TorusInt](params tfhe.Parameters[T], keySwitchParams tfhe.GadgetParameters[T], sk tfhe.SecretKey[T]) *BFVKeyGenerator[T] { +func NewBFVKeyGenerator[T tfhe.TorusInt](params tfhe.Parameters[T], sk tfhe.SecretKey[T]) *BFVKeyGenerator[T] { return &BFVKeyGenerator[T]{ - BaseEncryptor: tfhe.NewEncryptorWithKey(params, sk), - PolyEvaluator: poly.NewEvaluator[T](params.PolyDegree()), - Parameters: params, - KeySwitchParams: keySwitchParams, + BaseEncryptor: tfhe.NewEncryptorWithKey(params, sk), + PolyEvaluator: poly.NewEvaluator[T](params.PolyDegree()), + Parameters: params, } } // ShallowCopy creates a shallow copy of this BFVKeyGenerator. func (kg *BFVKeyGenerator[T]) ShallowCopy() *BFVKeyGenerator[T] { return &BFVKeyGenerator[T]{ - BaseEncryptor: kg.BaseEncryptor.ShallowCopy(), - PolyEvaluator: kg.PolyEvaluator.ShallowCopy(), - Parameters: kg.Parameters, - KeySwitchParams: kg.KeySwitchParams, + BaseEncryptor: kg.BaseEncryptor.ShallowCopy(), + PolyEvaluator: kg.PolyEvaluator.ShallowCopy(), + Parameters: kg.Parameters, } } -// BFVEvaluationKey is a keyswitching key for BFV type operations. -// It holds relinearization and automorphism keys. -type BFVEvaluationKey[T tfhe.TorusInt] struct { - // RelinKey is a relinearization key. - RelinKey tfhe.GLWEKeySwitchKey[T] - // GaloisKeys is a map of automorphism keys. - GaloisKeys map[int]tfhe.GLWEKeySwitchKey[T] -} - // GenEvaluationKey generates an evaluation key for BFV type operations. -func (kg *BFVKeyGenerator[T]) GenEvaluationKey(idx []int) BFVEvaluationKey[T] { +func (kg *BFVKeyGenerator[T]) GenEvaluationKey(idx []int, kskParams tfhe.GadgetParameters[T]) BFVEvaluationKey[T] { return BFVEvaluationKey[T]{ - RelinKey: kg.GenRelinKey(), - GaloisKeys: kg.GenGaloisKeys(idx), + RelinKey: kg.GenRelinKey(kskParams), + GaloisKeys: kg.GenGaloisKeys(idx, kskParams), } } // GenRelinKey generates a relinearization key for BFV multiplication. -func (kg *BFVKeyGenerator[T]) GenRelinKey() tfhe.GLWEKeySwitchKey[T] { +func (kg *BFVKeyGenerator[T]) GenRelinKey(kskParams tfhe.GadgetParameters[T]) tfhe.GLWEKeySwitchKey[T] { skOut := tfhe.NewGLWESecretKeyCustom[T](kg.Parameters.GLWERank()*(kg.Parameters.GLWERank()+1)/2, kg.Parameters.PolyDegree()) fskOut := tfhe.NewFourierGLWESecretKeyCustom[T](kg.Parameters.GLWERank()*(kg.Parameters.GLWERank()+1)/2, kg.Parameters.PolyDegree()) @@ -76,11 +71,11 @@ func (kg *BFVKeyGenerator[T]) GenRelinKey() tfhe.GLWEKeySwitchKey[T] { kg.BaseEncryptor.PolyEvaluator.ToPolyAssignUnsafe(fskOut.Value[i], skOut.Value[i]) } - return kg.BaseEncryptor.GenGLWEKeySwitchKey(skOut, kg.KeySwitchParams) + return kg.BaseEncryptor.GenGLWEKeySwitchKey(skOut, kskParams) } // GenGaloisKeys generate galois keys for BFV automorphism. -func (kg *BFVKeyGenerator[T]) GenGaloisKeys(idx []int) map[int]tfhe.GLWEKeySwitchKey[T] { +func (kg *BFVKeyGenerator[T]) GenGaloisKeys(idx []int, kskParams tfhe.GadgetParameters[T]) map[int]tfhe.GLWEKeySwitchKey[T] { galKeys := make(map[int]tfhe.GLWEKeySwitchKey[T], len(idx)) skOut := tfhe.NewGLWESecretKey(kg.Parameters) @@ -88,39 +83,39 @@ func (kg *BFVKeyGenerator[T]) GenGaloisKeys(idx []int) map[int]tfhe.GLWEKeySwitc for i := 0; i < kg.Parameters.GLWERank(); i++ { kg.BaseEncryptor.PolyEvaluator.PermutePolyAssign(kg.BaseEncryptor.SecretKey.GLWEKey.Value[i], d, skOut.Value[i]) } - galKeys[d] = kg.BaseEncryptor.GenGLWEKeySwitchKey(skOut, kg.KeySwitchParams) + galKeys[d] = kg.BaseEncryptor.GenGLWEKeySwitchKey(skOut, kskParams) } return galKeys } // GenGaloisKeysAssign generates automorphism keys for BFV automorphism and assigns them to the given map. // If a key for a given automorphism degree already exists in the map, it will be overwritten. -func (kg *BFVKeyGenerator[T]) GenGaloisKeysAssign(idx []int, galKeysOut map[int]tfhe.GLWEKeySwitchKey[T]) { +func (kg *BFVKeyGenerator[T]) GenGaloisKeysAssign(idx []int, kskParams tfhe.GadgetParameters[T], galKeysOut map[int]tfhe.GLWEKeySwitchKey[T]) { skOut := tfhe.NewGLWESecretKey(kg.Parameters) for _, d := range idx { for i := 0; i < kg.Parameters.GLWERank(); i++ { kg.BaseEncryptor.PolyEvaluator.PermutePolyAssign(kg.BaseEncryptor.SecretKey.GLWEKey.Value[i], d, skOut.Value[i]) } - galKeysOut[d] = kg.BaseEncryptor.GenGLWEKeySwitchKey(skOut, kg.KeySwitchParams) + galKeysOut[d] = kg.BaseEncryptor.GenGLWEKeySwitchKey(skOut, kskParams) } } // GenGaloisKeysForLWEToGLWECiphertext generates automorphism keys for BFV automorphism for LWE to GLWE packing. -func (kg *BFVKeyGenerator[T]) GenGaloisKeysForLWEToGLWECiphertext() map[int]tfhe.GLWEKeySwitchKey[T] { +func (kg *BFVKeyGenerator[T]) GenGaloisKeysForLWEToGLWECiphertext(kskParams tfhe.GadgetParameters[T]) map[int]tfhe.GLWEKeySwitchKey[T] { auts := make([]int, kg.Parameters.LogPolyDegree()) for i := range auts { auts[i] = 1<<(kg.Parameters.LogPolyDegree()-i) + 1 } - return kg.GenGaloisKeys(auts) + return kg.GenGaloisKeys(auts, kskParams) } // GenGaloisKeysForLWEToGLWECiphertextAssign generates automorphism keys for BFV automorphism for LWE to GLWE packing and assigns them to the given map. // If a key for a given automorphism degree already exists in the map, it will be overwritten. -func (kg *BFVKeyGenerator[T]) GenGaloisKeysForLWEToGLWECiphertextAssign(galKeysOut map[int]tfhe.GLWEKeySwitchKey[T]) { +func (kg *BFVKeyGenerator[T]) GenGaloisKeysForLWEToGLWECiphertextAssign(kskParams tfhe.GadgetParameters[T], galKeysOut map[int]tfhe.GLWEKeySwitchKey[T]) { auts := make([]int, kg.Parameters.LogPolyDegree()) for i := range auts { auts[i] = 1<<(kg.Parameters.LogPolyDegree()-i) + 1 } - kg.GenGaloisKeysAssign(auts, galKeysOut) + kg.GenGaloisKeysAssign(auts, kskParams, galKeysOut) } diff --git a/xtfhe/bfv_test.go b/xtfhe/bfv_test.go index 430f4f1..cfb6db0 100644 --- a/xtfhe/bfv_test.go +++ b/xtfhe/bfv_test.go @@ -12,10 +12,10 @@ import ( var ( bfvParams = tfhe.ParamsUint3.Compile() bfvEnc = tfhe.NewEncryptor(bfvParams) - bfvKeyGen = xtfhe.NewBFVKeyGenerator(bfvParams, xtfhe.ParamsBFVKeySwitchLogN11.Compile(), bfvEnc.SecretKey) + bfvKeyGen = xtfhe.NewBFVKeyGenerator(bfvParams, bfvEnc.SecretKey) bfvEval = xtfhe.NewBFVEvaluator(bfvParams, xtfhe.BFVEvaluationKey[uint64]{ - RelinKey: bfvKeyGen.GenRelinKey(), - GaloisKeys: bfvKeyGen.GenGaloisKeysForLWEToGLWECiphertext(), + RelinKey: bfvKeyGen.GenRelinKey(xtfhe.ParamsBFVKeySwitchLogN11.Compile()), + GaloisKeys: bfvKeyGen.GenGaloisKeysForLWEToGLWECiphertext(xtfhe.ParamsBFVKeySwitchLogN11.Compile()), }) ) diff --git a/xtfhe/circuit_bootstrap.go b/xtfhe/circuit_bootstrap.go new file mode 100644 index 0000000..a6dccce --- /dev/null +++ b/xtfhe/circuit_bootstrap.go @@ -0,0 +1,173 @@ +package xtfhe + +import ( + "github.com/sp301415/tfhe-go/math/num" + "github.com/sp301415/tfhe-go/math/poly" + "github.com/sp301415/tfhe-go/tfhe" +) + +// CircuitBootstrapper wraps around [tfhe.Evaluator], and implements Circuit Bootstrapping Algorithm. +// For more details, see https://eprint.iacr.org/2024/1318. +type CircuitBootstrapper[T tfhe.TorusInt] struct { + // Evaluator is an embedded Evaluator for this CircuitBootstrapper. + *ManyLUTEvaluator[T] + // BFVEvaluator is an embedded BFVEvaluator for this CircuitBootstrapper. + *BFVEvaluator[T] + + // Parameters is a parameter set for this CircuitBootstrapper. + Parameters CircuitBootstrapParameters[T] + + // EvaluationKey is a circuit bootstrapping key for this CircuitBootstrapper. + EvaluationKey CircuitBootstrapKey[T] + + buffer circuitBootstrapBuffer[T] +} + +// circuitBootstrapBuffer contains buffer values for CircuitBootstrapper. +type circuitBootstrapBuffer[T tfhe.TorusInt] struct { + // fs are the functions for LUT. + fs []func(x int) T + // lut is an empty lut. + lut tfhe.LookUpTable[T] + + // ctDecomposed are buffer for decomposed ciphertexts during LWE to GLWE. + ctDecomposed []poly.Poly[T] + // ctFourierDecomposed are buffer for decomposed ciphertexts during scheme switching. + ctFourierDecomposed [][]poly.FourierPoly + + // ctKeySwitch is a key-switched LWE ciphertext. + ctKeySwitch tfhe.LWECiphertext[T] + // ctPack is the permuted ciphertext for trace. + ctPack tfhe.GLWECiphertext[T] + // ctRotate is a temporary GLWE ciphertext after Blind Rotation. + ctRotate tfhe.GLWECiphertext[T] + // ctFourierGLWEOut is a temporary Fourier transformed GLWE ciphertext. + ctFourierGLWEOut tfhe.FourierGLWECiphertext[T] + // ctGGSWOut is the output GGSW ciphertext of circuit bootstrapping. + ctGGSWOut tfhe.GGSWCiphertext[T] +} + +// NewCircuitBootstrapper creates a new CircuitBootstrapper. +// For circuit bootstrapping, we need relin key and galois keys for LWE to GLWE. +func NewCircuitBootstrapper[T tfhe.TorusInt](params CircuitBootstrapParameters[T], evk tfhe.EvaluationKey[T], cbk CircuitBootstrapKey[T]) *CircuitBootstrapper[T] { + return &CircuitBootstrapper[T]{ + ManyLUTEvaluator: NewManyLUTEvaluator(params.manyLUTParameters, evk), + BFVEvaluator: NewBFVEvaluator(params.BaseParameters(), BFVEvaluationKey[T]{GaloisKeys: cbk.TraceKeys}), + + Parameters: params, + EvaluationKey: cbk, + + buffer: newCircuitBootstrapBuffer(params), + } +} + +// newCircuitBootstrapBuffer creates a new circuitBootstrapBuffer. +func newCircuitBootstrapBuffer[T tfhe.TorusInt](params CircuitBootstrapParameters[T]) circuitBootstrapBuffer[T] { + fs := make([]func(x int) T, params.manyLUTParameters.lutCount) + for i := 0; i < params.manyLUTParameters.lutCount; i++ { + fs[i] = func(x int) T { return 0 } + } + + ctDecomposed := make([]poly.Poly[T], params.schemeSwitchParameters.Level()) + for i := 0; i < params.schemeSwitchParameters.Level(); i++ { + ctDecomposed[i] = poly.NewPoly[T](params.BaseParameters().PolyDegree()) + } + + ctFourierDecomposed := make([][]poly.FourierPoly, params.BaseParameters().GLWERank()+1) + for i := 0; i < params.BaseParameters().GLWERank()+1; i++ { + ctFourierDecomposed[i] = make([]poly.FourierPoly, params.schemeSwitchParameters.Level()) + for j := 0; j < params.schemeSwitchParameters.Level(); j++ { + ctFourierDecomposed[i][j] = poly.NewFourierPoly(params.BaseParameters().PolyDegree()) + } + } + + return circuitBootstrapBuffer[T]{ + fs: fs, + lut: tfhe.NewLookUpTable(params.BaseParameters()), + + ctDecomposed: ctDecomposed, + ctFourierDecomposed: ctFourierDecomposed, + + ctKeySwitch: tfhe.NewLWECiphertextCustom[T](params.BaseParameters().LWEDimension()), + ctPack: tfhe.NewGLWECiphertext(params.BaseParameters()), + ctRotate: tfhe.NewGLWECiphertext(params.BaseParameters()), + ctFourierGLWEOut: tfhe.NewFourierGLWECiphertext(params.BaseParameters()), + ctGGSWOut: tfhe.NewGGSWCiphertext(params.BaseParameters(), params.outputParameters), + } +} + +// ShallowCopy creates a shallow copy of this CircuitBootstrapper. +// Returned CircuitBootstrapper is safe for concurrent use. +func (e *CircuitBootstrapper[T]) ShallowCopy() *CircuitBootstrapper[T] { + return &CircuitBootstrapper[T]{ + ManyLUTEvaluator: e.ManyLUTEvaluator.ShallowCopy(), + BFVEvaluator: e.BFVEvaluator.ShallowCopy(), + + Parameters: e.Parameters, + EvaluationKey: e.EvaluationKey, + + buffer: newCircuitBootstrapBuffer(e.Parameters), + } +} + +// CircuitBootstrap performs Circuit Bootstrapping and returns the result. +func (e *CircuitBootstrapper[T]) CircuitBootstrap(ct tfhe.LWECiphertext[T]) tfhe.FourierGGSWCiphertext[T] { + ctOut := tfhe.NewFourierGGSWCiphertext(e.Parameters.BaseParameters(), e.Parameters.outputParameters) + e.CircuitBootstrapAssign(ct, ctOut) + return ctOut +} + +// CircuitBootstrapAssign performs Circuit Bootstrapping and writes the result to ctOut. +// The gadget parameters of ctOut should be equal to the output parameters of CircuitBootstrapper. +func (e *CircuitBootstrapper[T]) CircuitBootstrapAssign(ct tfhe.LWECiphertext[T], ctOut tfhe.FourierGGSWCiphertext[T]) { + for i := 0; i < e.Parameters.outputParameters.Level(); i += e.ManyLUTEvaluator.Parameters.lutCount { + start := i + end := num.Min(i+e.ManyLUTEvaluator.Parameters.lutCount, e.Parameters.outputParameters.Level()) + + for j, jj := start, 0; j < end; j, jj = j+1, jj+1 { + logBase := ctOut.GadgetParameters.LogBaseQ(j) + e.buffer.fs[jj] = func(x int) T { + return T(x) << logBase + } + } + + e.GenLookUpTableFullAssign(e.buffer.fs, e.buffer.lut) + switch e.Parameters.BaseParameters().BootstrapOrder() { + case tfhe.OrderBlindRotateKeySwitch: + e.BlindRotateAssign(ct, e.buffer.lut, e.buffer.ctRotate) + case tfhe.OrderKeySwitchBlindRotate: + e.KeySwitchForBootstrapAssign(ct, e.buffer.ctKeySwitch) + e.BlindRotateAssign(e.buffer.ctKeySwitch, e.buffer.lut, e.buffer.ctRotate) + } + + for j := 0; j < e.Parameters.BaseParameters().GLWERank()+1; j++ { + for k := 0; k < e.Parameters.BaseParameters().PolyDegree(); k++ { + e.buffer.ctRotate.Value[j].Coeffs[k] = num.DivRoundBits(e.buffer.ctRotate.Value[j].Coeffs[k], e.Parameters.BaseParameters().LogPolyDegree()) + } + } + + for j, jj := start, 0; j < end; j, jj = j+1, jj+1 { + e.MonomialMulGLWEAssign(e.buffer.ctRotate, -jj, e.buffer.ctGGSWOut.Value[0].Value[j]) + for k := 0; k < e.Parameters.BaseParameters().LogPolyDegree(); k++ { + e.PermuteAssign(e.buffer.ctGGSWOut.Value[0].Value[j], 1<<(e.Parameters.BaseParameters().LogPolyDegree()-k)+1, e.buffer.ctPack) + e.BaseEvaluator.AddGLWEAssign(e.buffer.ctGGSWOut.Value[0].Value[j], e.buffer.ctPack, e.buffer.ctGGSWOut.Value[0].Value[j]) + } + + for k := 0; k < e.Parameters.BaseParameters().GLWERank()+1; k++ { + e.Decomposer.DecomposePolyAssign(e.buffer.ctGGSWOut.Value[0].Value[j].Value[k], e.Parameters.schemeSwitchParameters, e.buffer.ctDecomposed) + for l := 0; l < e.Parameters.schemeSwitchParameters.Level(); l++ { + e.PolyEvaluator.ToFourierPolyAssign(e.buffer.ctDecomposed[l], e.buffer.ctFourierDecomposed[k][l]) + } + } + + for k := 0; k < e.Parameters.BaseParameters().GLWERank(); k++ { + e.ExternalProductFourierDecomposedFourierGLWEAssign(e.EvaluationKey.SchemeSwitchKey[k], e.buffer.ctFourierDecomposed, e.buffer.ctFourierGLWEOut) + for l := 0; l < e.Parameters.BaseParameters().GLWERank()+1; l++ { + e.PolyEvaluator.ToPolyAssignUnsafe(e.buffer.ctFourierGLWEOut.Value[l], e.buffer.ctGGSWOut.Value[k+1].Value[j].Value[l]) + } + } + } + } + + e.ToFourierGGSWCiphertextAssign(e.buffer.ctGGSWOut, ctOut) +} diff --git a/xtfhe/circuit_bootstrap_key.go b/xtfhe/circuit_bootstrap_key.go new file mode 100644 index 0000000..d0d5153 --- /dev/null +++ b/xtfhe/circuit_bootstrap_key.go @@ -0,0 +1,52 @@ +package xtfhe + +import "github.com/sp301415/tfhe-go/tfhe" + +// CircuitBootstrapKey is a key for Circuit Bootstrapping. +type CircuitBootstrapKey[T tfhe.TorusInt] struct { + // SchemeSwitchKey is a key for scheme switching. + // It has length GLWERank, + // with i-th element being an FourierGGSWCiphertext of i-th secret key. + SchemeSwitchKey []tfhe.FourierGGSWCiphertext[T] + // TraceKeys is a map of galois keys used for LWE to GLWE packing. + TraceKeys map[int]tfhe.GLWEKeySwitchKey[T] +} + +// CircuitBootstrapKeyGenerator generates a key for Circuit Bootstrapping. +type CircuitBootstrapKeyGenerator[T tfhe.TorusInt] struct { + // BFVKeyGenerator is a BFVKeyGenerator for this CircuitBootstrapKeyGenerator. + *BFVKeyGenerator[T] + + // Parameters is a parameter set for this CircuitBootstrapKeyGenerator. + Parameters CircuitBootstrapParameters[T] +} + +// NewCircuitBootstrapKeyGenerator creates a new CircuitBootstrapKeyGenerator. +func NewCircuitBootstrapKeyGenerator[T tfhe.TorusInt](params CircuitBootstrapParameters[T], sk tfhe.SecretKey[T]) *CircuitBootstrapKeyGenerator[T] { + return &CircuitBootstrapKeyGenerator[T]{ + BFVKeyGenerator: NewBFVKeyGenerator(params.BaseParameters(), sk), + Parameters: params, + } +} + +// ShallowCopy creates a shallow copy of this CircuitBootstrapKeyGenerator. +// Returned CircuitBootstrapKeyGenerator is safe for concurrent use. +func (kg *CircuitBootstrapKeyGenerator[T]) ShallowCopy() *CircuitBootstrapKeyGenerator[T] { + return &CircuitBootstrapKeyGenerator[T]{ + BFVKeyGenerator: kg.BFVKeyGenerator.ShallowCopy(), + Parameters: kg.Parameters, + } +} + +// GenCircuitBootstrapKey generates a key for Circuit Bootstrapping. +func (kg *CircuitBootstrapKeyGenerator[T]) GenCircuitBootstrapKey() CircuitBootstrapKey[T] { + schemeSwitchKey := make([]tfhe.FourierGGSWCiphertext[T], kg.Parameters.BaseParameters().GLWERank()) + for i := 0; i < kg.Parameters.BaseParameters().GLWERank(); i++ { + schemeSwitchKey[i] = kg.BaseEncryptor.EncryptFourierGGSWPlaintext(tfhe.GLWEPlaintext[T]{Value: kg.BaseEncryptor.SecretKey.GLWEKey.Value[i]}, kg.Parameters.schemeSwitchParameters) + } + + return CircuitBootstrapKey[T]{ + SchemeSwitchKey: schemeSwitchKey, + TraceKeys: kg.GenGaloisKeysForLWEToGLWECiphertext(kg.Parameters.traceKeySwitchParameters), + } +} diff --git a/xtfhe/circuit_bootstrap_params.go b/xtfhe/circuit_bootstrap_params.go new file mode 100644 index 0000000..a7bed6a --- /dev/null +++ b/xtfhe/circuit_bootstrap_params.go @@ -0,0 +1,71 @@ +package xtfhe + +import ( + "github.com/sp301415/tfhe-go/tfhe" +) + +// CircuitBootstrapParametersLiteral is a structure for Circuit Bootstrapping Parameters. +// +// PolyDegree must equal LookUpTableSize. +type CircuitBootstrapParametersLiteral[T tfhe.TorusInt] struct { + // ManyLUTParametersLiteral is a base ManyLUTParametersLiteral for this CircuitBootstrapParametersLiteral. + ManyLUTParametersLiteral ManyLUTParametersLiteral[T] + + // SchemeSwitchParametersLiteral is the gadget parameters for scheme switching. + SchemeSwitchParametersLiteral tfhe.GadgetParametersLiteral[T] + // TraceKeySwitchParametersLiteral is the gadget parameters for LWE to GLWE packing. + TraceKeySwitchParametersLiteral tfhe.GadgetParametersLiteral[T] + // OutputParametersLiteral is the gadget parameters for the output of circuit bootstrapping. + OutputParametersLiteral tfhe.GadgetParametersLiteral[T] +} + +// Compile transforms ParametersLiteral to read-only Parameters. +// If there is any invalid parameter in the literal, it panics. +// Default parameters are guaranteed to be compiled without panics. +func (p CircuitBootstrapParametersLiteral[T]) Compile() CircuitBootstrapParameters[T] { + return CircuitBootstrapParameters[T]{ + manyLUTParameters: p.ManyLUTParametersLiteral.Compile(), + + schemeSwitchParameters: p.SchemeSwitchParametersLiteral.Compile(), + traceKeySwitchParameters: p.TraceKeySwitchParametersLiteral.Compile(), + outputParameters: p.OutputParametersLiteral.Compile(), + } +} + +// CircuitBootstrapParametersLiteral is a parameter set for Circuit Bootstrapping. +type CircuitBootstrapParameters[T tfhe.TorusInt] struct { + // manyLUTParameters is a base ManyLUTParameters for this CircuitBootstrapParameters. + manyLUTParameters ManyLUTParameters[T] + + // schemeSwitchParameters is the gadget parameters for scheme switching. + schemeSwitchParameters tfhe.GadgetParameters[T] + // traceKeySwitchParameters is the gadget parameters for LWE to GLWE packing. + traceKeySwitchParameters tfhe.GadgetParameters[T] + // outputParameters is the gadget parameters for the output of circuit bootstrapping. + outputParameters tfhe.GadgetParameters[T] +} + +// ManyLUTParameters returns the base ManyLUTParameters for this CircuitBootstrapParameters. +func (p CircuitBootstrapParameters[T]) ManyLUTParameters() ManyLUTParameters[T] { + return p.manyLUTParameters +} + +// BaseParameters returns the base parameter set for this ManyLUTParameters. +func (p CircuitBootstrapParameters[T]) BaseParameters() tfhe.Parameters[T] { + return p.manyLUTParameters.baseParameters +} + +// SchemeSwitchParameters returns the gadget parameters for scheme switching. +func (p CircuitBootstrapParameters[T]) SchemeSwitchParameters() tfhe.GadgetParameters[T] { + return p.schemeSwitchParameters +} + +// TraceKeySwitchParameters returns the gadget parameters for LWE to GLWE packing. +func (p CircuitBootstrapParameters[T]) TraceKeySwitchParameters() tfhe.GadgetParameters[T] { + return p.traceKeySwitchParameters +} + +// OutputParameters returns the gadget parameters for the output of circuit bootstrapping. +func (p CircuitBootstrapParameters[T]) OutputParameters() tfhe.GadgetParameters[T] { + return p.outputParameters +} diff --git a/xtfhe/circuit_bootstrap_params_list.go b/xtfhe/circuit_bootstrap_params_list.go new file mode 100644 index 0000000..0434670 --- /dev/null +++ b/xtfhe/circuit_bootstrap_params_list.go @@ -0,0 +1,91 @@ +package xtfhe + +import "github.com/sp301415/tfhe-go/tfhe" + +var ( + // ParamsBinaryCircuitBootstrapMedium is a default circuit bootstrapping parameter set for binary TFHE + // with max depth 1194. + ParamsBinaryCircuitBootstrapMedium = CircuitBootstrapParametersLiteral[uint64]{ + ManyLUTParametersLiteral: ManyLUTParametersLiteral[uint64]{ + BaseParametersLiteral: tfhe.ParametersLiteral[uint64]{ + LWEDimension: 571, + GLWERank: 1, + PolyDegree: 2048, + + LWEStdDev: 0.00016996595057126976, + GLWEStdDev: 0.0000000000000003472576015484159, + + MessageModulus: 1 << 1, + + BlindRotateParameters: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 15, + Level: 2, + }, + KeySwitchParameters: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 2, + Level: 5, + }, + + BootstrapOrder: tfhe.OrderBlindRotateKeySwitch, + }, + + LUTCount: 4, + }, + + SchemeSwitchParametersLiteral: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 17, + Level: 2, + }, + TraceKeySwitchParametersLiteral: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 13, + Level: 3, + }, + OutputParametersLiteral: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 4, + Level: 4, + }, + } + + // ParamsBinaryCircuitBootstrapLarge is a default circuit bootstrapping parameter set for binary TFHE + // with max depth 13410. + ParamsBinaryCircuitBootstrapLarge = CircuitBootstrapParametersLiteral[uint64]{ + ManyLUTParametersLiteral: ManyLUTParametersLiteral[uint64]{ + BaseParametersLiteral: tfhe.ParametersLiteral[uint64]{ + LWEDimension: 571, + GLWERank: 1, + PolyDegree: 2048, + + LWEStdDev: 0.00016996595057126976, + GLWEStdDev: 0.0000000000000003472576015484159, + + MessageModulus: 1 << 1, + + BlindRotateParameters: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 15, + Level: 2, + }, + KeySwitchParameters: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 2, + Level: 5, + }, + + BootstrapOrder: tfhe.OrderBlindRotateKeySwitch, + }, + + LUTCount: 4, + }, + + SchemeSwitchParametersLiteral: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 17, + Level: 2, + }, + TraceKeySwitchParametersLiteral: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 8, + Level: 6, + }, + OutputParametersLiteral: tfhe.GadgetParametersLiteral[uint64]{ + Base: 1 << 5, + Level: 4, + }, + } +) diff --git a/xtfhe/circuit_bootstrap_test.go b/xtfhe/circuit_bootstrap_test.go new file mode 100644 index 0000000..918380e --- /dev/null +++ b/xtfhe/circuit_bootstrap_test.go @@ -0,0 +1,31 @@ +package xtfhe_test + +import ( + "testing" + + "github.com/sp301415/tfhe-go/math/vec" + "github.com/sp301415/tfhe-go/tfhe" + "github.com/sp301415/tfhe-go/xtfhe" + "github.com/stretchr/testify/assert" +) + +var ( + cbParams = xtfhe.ParamsBinaryCircuitBootstrapMedium.Compile() + cbEnc = tfhe.NewEncryptor(cbParams.BaseParameters()) + cbKeyGen = xtfhe.NewCircuitBootstrapKeyGenerator(cbParams, cbEnc.SecretKey) + cbEval = xtfhe.NewCircuitBootstrapper(cbParams, cbEnc.GenEvaluationKeyParallel(), cbKeyGen.GenCircuitBootstrapKey()) +) + +func TestCircuitBootstrap(t *testing.T) { + msgGLWE := []int{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0} + ctGLWE := cbEnc.EncryptGLWE(msgGLWE) + + for _, c := range []int{0, 1} { + ctLWE := cbEnc.EncryptLWE(c) + ctFourierGGSW := cbEval.CircuitBootstrap(ctLWE) + ctGLWEOut := cbEval.ExternalProductGLWE(ctFourierGGSW, ctGLWE) + + msgGLWEOut := cbEnc.DecryptGLWE(ctGLWEOut)[:len(msgGLWE)] + assert.Equal(t, msgGLWEOut, vec.ScalarMul(msgGLWE, c)) + } +} diff --git a/xtfhe/manylut_bootstrap.go b/xtfhe/manylut_bootstrap.go index 80a6438..29733c1 100644 --- a/xtfhe/manylut_bootstrap.go +++ b/xtfhe/manylut_bootstrap.go @@ -71,7 +71,7 @@ func (e *ManyLUTEvaluator[T]) GenLookUpTableCustomAssign(f []func(int) int, mess // Panics if len(f) > LUTCount. func (e *ManyLUTEvaluator[T]) GenLookUpTableFullCustom(f []func(int) T, messageModulus, scale T) tfhe.LookUpTable[T] { lutOut := tfhe.NewLookUpTable(e.Parameters.baseParameters) - e.GenLookUpTableFullAssign(f, lutOut) + e.GenLookUpTableFullCustomAssign(f, messageModulus, lutOut) return lutOut } diff --git a/xtfhe/manylut_params.go b/xtfhe/manylut_params.go index fa18a14..c07d07c 100644 --- a/xtfhe/manylut_params.go +++ b/xtfhe/manylut_params.go @@ -21,15 +21,17 @@ type ManyLUTParametersLiteral[T tfhe.TorusInt] struct { // If there is any invalid parameter in the literal, it panics. // Default parameters are guaranteed to be compiled without panics. func (p ManyLUTParametersLiteral[T]) Compile() ManyLUTParameters[T] { + baseParameters := p.BaseParametersLiteral.Compile() + switch { - case p.BaseParametersLiteral.PolyDegree != p.BaseParametersLiteral.LookUpTableSize: + case baseParameters.PolyDegree() != baseParameters.LookUpTableSize(): panic("PolyDegree must equal LookUpTableSize") case !num.IsPowerOfTwo(p.LUTCount): panic("lutCount not power of two") } return ManyLUTParameters[T]{ - baseParameters: p.BaseParametersLiteral.Compile(), + baseParameters: baseParameters, lutCount: p.LUTCount, logLUTCount: num.Log2(p.LUTCount),