├── LOGO.png
├── mktfhe
├── mktfhe.go
├── lwe.go
├── binary_decryptor.go
├── bootstrap_key.go
├── bootstrap_key_marshal.go
├── bootstrap_keygen.go
├── binary_encryptor_public.go
├── lwe_ops.go
├── glwe_enc.go
├── binary_encryptor.go
├── fft_glwe_enc.go
├── fft_glwe.go
├── params_list.go
├── glwe.go
└── fft_glwe_conv.go
├── tfhe
├── tfhe.go
├── asm_decompose_stub_amd64.go
├── keyswitch_keygen.go
├── asm_decompose.go
├── fft_glwe_enc_public.go
├── binary_encoder.go
├── asm_decompose_amd64.go
├── glwe_enc_public.go
├── lwe_enc_public.go
├── binary_encryptor_public.go
├── keyswitch.go
├── lwe_ops.go
├── asm_decompose_amd64.s
├── bootstrap_key.go
├── encryptor_public.go
├── binary_encryptor.go
├── keyswitch_key.go
├── bootstrap_lut.go
├── lwe_enc.go
├── binary_tfhe_test.go
├── encryptor_key_marshal.go
└── encryptor_key.go
├── xtfhe
├── xtfhe.go
├── bfv_params_list.go
├── fhew_test.go
├── sanitization_test.go
├── fhew_params_list.go
├── manylut_test.go
├── circuit_bootstrap_test.go
├── sanitization_params_list.go
├── bfv_test.go
├── manylut_params.go
├── circuit_bootstrap_key.go
├── circuit_bootstrap_params_list.go
├── fhew_enc.go
├── fhew_params.go
├── circuit_bootstrap_params.go
├── sanitization_sampler.go
├── manylut_params_list.go
├── manylut_evaluator.go
├── fhew_evaluator.go
├── sanitization_params.go
├── bfv_keygen.go
└── fhew_bootstrap_keygen.go
├── math
├── csprng
│ ├── csprng.go
│ ├── gaussian_sampler_test.go
│ ├── binary_sampler.go
│ └── uniform_sampler.go
├── poly
│ ├── asm_fft_stub_amd64.go
│ ├── asm_fold_stub_amd64.go
│ ├── asm_vec_cmplx_stub_amd64.go
│ ├── asm_fft_test.go
│ ├── poly_test.go
│ ├── poly.go
│ └── asm_vec_cmplx_test.go
└── vec
│ ├── asm_vec_stub_amd64.go
│ └── asm_vec_test.go
├── internal
└── asmgen
│ ├── go.mod
│ ├── go.sum
│ ├── fft_butterfly.go
│ ├── asmgen.go
│ └── decompose.go
├── go.mod
├── go.sum
├── .github
└── workflows
│ └── ci.yml
├── SECURITY.md
└── .gitignore
/LOGO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sp301415/tfhe-go/HEAD/LOGO.png
--------------------------------------------------------------------------------
/mktfhe/mktfhe.go:
--------------------------------------------------------------------------------
1 | // Package mktfhe implements multi-key variant of TFHE scheme.
2 | package mktfhe
3 |
--------------------------------------------------------------------------------
/tfhe/tfhe.go:
--------------------------------------------------------------------------------
1 | // Package tfhe implements TFHE(Fully Homomorphic Encryption over the Torus) scheme.
2 | package tfhe
3 |
--------------------------------------------------------------------------------
/xtfhe/xtfhe.go:
--------------------------------------------------------------------------------
1 | // Package xtfhe implements experimental or advanced functionalities of the TFHE scheme.
2 | package xtfhe
3 |
--------------------------------------------------------------------------------
/math/csprng/csprng.go:
--------------------------------------------------------------------------------
1 | // Package csprng implements various samplers used throughout TFHE.
2 | // All samplers in this package are CSPRNG(Cryptographically Secure Pseudo Random Generator),
3 | // hence the name.
4 | package csprng
5 |
--------------------------------------------------------------------------------
/internal/asmgen/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/sp301415/tfhe-go/internal/asmgen
2 |
3 | go 1.18
4 |
5 | require github.com/mmcloughlin/avo v0.6.0
6 |
7 | require (
8 | golang.org/x/mod v0.20.0 // indirect
9 | golang.org/x/sync v0.8.0 // indirect
10 | golang.org/x/tools v0.24.1 // indirect
11 | )
12 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/sp301415/tfhe-go
2 |
3 | go 1.18
4 |
5 | require (
6 | github.com/stretchr/testify v1.10.0
7 | golang.org/x/sys v0.30.0
8 | )
9 |
10 | require (
11 | github.com/davecgh/go-spew v1.1.1 // indirect
12 | github.com/pmezard/go-difflib v1.0.0 // indirect
13 | gopkg.in/yaml.v3 v3.0.1 // indirect
14 | )
15 |
--------------------------------------------------------------------------------
/xtfhe/bfv_params_list.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | var (
6 | // ParamsBFVKeySwitchLogN11 is a gadget parameter set for keyswitching keys in BFV type operations
7 | // where N = 2^11 = 2048.
8 | ParamsBFVKeySwitchLogN11 = tfhe.GadgetParametersLiteral[uint64]{
9 | Base: 1 << 9,
10 | Level: 4,
11 | }
12 | )
13 |
--------------------------------------------------------------------------------
/math/poly/asm_fft_stub_amd64.go:
--------------------------------------------------------------------------------
1 | // Code generated by command: go run asmgen.go -fft -out ../../math/poly/asm_fft_amd64.s -stubs ../../math/poly/asm_fft_stub_amd64.go -pkg=poly. DO NOT EDIT.
2 |
3 | //go:build amd64 && !purego
4 |
5 | package poly
6 |
7 | //go:noescape
8 | func fwdFFTInPlaceAVX2(coeffs []float64, tw []complex128)
9 |
10 | //go:noescape
11 | func invFFTInPlaceAVX2(coeffs []float64, twInv []complex128, scale float64)
12 |
--------------------------------------------------------------------------------
/tfhe/asm_decompose_stub_amd64.go:
--------------------------------------------------------------------------------
1 | // Code generated by command: go run asmgen.go -decompose -out ../../tfhe/asm_decompose_amd64.s -stubs ../../tfhe/asm_decompose_stub_amd64.go -pkg=tfhe. DO NOT EDIT.
2 |
3 | //go:build amd64 && !purego
4 |
5 | package tfhe
6 |
7 | //go:noescape
8 | func decomposePolyToUint32AVX2(dcmpOut [][]uint32, p []uint32, base uint32, logBase uint32, logLastBaseQ uint32)
9 |
10 | //go:noescape
11 | func decomposePolyToUint64AVX2(dcmpOut [][]uint64, p []uint64, base uint64, logBase uint64, logLastBaseQ uint64)
12 |
--------------------------------------------------------------------------------
/xtfhe/fhew_test.go:
--------------------------------------------------------------------------------
1 | package xtfhe_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/sp301415/tfhe-go/xtfhe"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | var (
11 | fhewParams = xtfhe.ParamsFHEWBinary.Compile()
12 | fhewEnc = xtfhe.NewFHEWEncryptor(fhewParams)
13 | fhewEval = xtfhe.NewFHEWEvaluator(fhewParams, fhewEnc.GenEvalKeyParallel())
14 | )
15 |
16 | func TestFHEW(t *testing.T) {
17 | for _, msg := range []int{0, 1} {
18 | ct := fhewEnc.EncryptLWE(msg)
19 | ctOut := fhewEval.BootstrapFunc(ct, func(x int) int { return x ^ 1 })
20 | assert.Equal(t, fhewEnc.DecryptLWE(ctOut), msg^1)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/internal/asmgen/go.sum:
--------------------------------------------------------------------------------
1 | github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
2 | github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
3 | golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
4 | golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
5 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
6 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
7 | golang.org/x/tools v0.24.1 h1:vxuHLTNS3Np5zrYoPRpcheASHX/7KiGo+8Y4ZM1J2O8=
8 | golang.org/x/tools v0.24.1/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
9 |
--------------------------------------------------------------------------------
/xtfhe/sanitization_test.go:
--------------------------------------------------------------------------------
1 | package xtfhe_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/sp301415/tfhe-go/tfhe"
7 | "github.com/sp301415/tfhe-go/xtfhe"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | var (
12 | sanitizationParams = xtfhe.ParamsSanitizeBinary.Compile()
13 | sanitizationEnc = tfhe.NewEncryptor(sanitizationParams.BaseParams())
14 | sanitizationEval = xtfhe.NewSanitizer(sanitizationParams, sanitizationEnc.GenPublicKey(), sanitizationEnc.GenEvalKeyParallel())
15 | )
16 |
17 | func TestSanitization(t *testing.T) {
18 | for _, m := range []int{0, 1} {
19 | ct := sanitizationEnc.EncryptLWE(m)
20 | ctOut := sanitizationEval.SanitizeFunc(ct, func(i int) int { return i })
21 |
22 | assert.Equal(t, sanitizationEnc.DecryptLWE(ctOut), m)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tfhe/keyswitch_keygen.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // GenLWEKeySwitchKey samples a new keyswitch key skIn -> LWEKey.
4 | func (e *Encryptor[T]) GenLWEKeySwitchKey(skIn LWESecretKey[T], gadgetParams GadgetParameters[T]) LWEKeySwitchKey[T] {
5 | ksk := NewLWEKeySwitchKey(e.Params, len(skIn.Value), gadgetParams)
6 |
7 | for i := 0; i < ksk.InputLWEDimension(); i++ {
8 | e.EncryptLevScalarTo(ksk.Value[i], skIn.Value[i])
9 | }
10 |
11 | return ksk
12 | }
13 |
14 | // GenGLWEKeySwitchKey samples a new keyswitch key skIn -> GLWEKey.
15 | func (e *Encryptor[T]) GenGLWEKeySwitchKey(skIn GLWESecretKey[T], gadgetParams GadgetParameters[T]) GLWEKeySwitchKey[T] {
16 | ksk := NewGLWEKeySwitchKey(e.Params, len(skIn.Value), gadgetParams)
17 |
18 | for i := 0; i < ksk.InputGLWERank(); i++ {
19 | e.EncryptFFTGLevPolyTo(ksk.Value[i], skIn.Value[i])
20 | }
21 |
22 | return ksk
23 | }
24 |
--------------------------------------------------------------------------------
/xtfhe/fhew_params_list.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | var (
6 | // ParamsFHEWBinary is a default parameter set for binary FHEW.
7 | ParamsFHEWBinary = FHEWParametersLiteral[uint64]{
8 | BaseParams: tfhe.ParametersLiteral[uint64]{
9 | LWEDimension: 447,
10 | GLWERank: 1,
11 | PolyRank: 1024,
12 |
13 | LWEStdDev: 0.000143051147460938,
14 | GLWEStdDev: 0.00000000393811205889363,
15 |
16 | MessageModulus: 1 << 1,
17 |
18 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
19 | Base: 1 << 10,
20 | Level: 3,
21 | },
22 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
23 | Base: 1 << 3,
24 | Level: 4,
25 | },
26 |
27 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
28 | },
29 |
30 | SecretKeyStdDev: 0.00000000000000000017347234759768072,
31 |
32 | WindowSize: 10,
33 | }
34 | )
35 |
--------------------------------------------------------------------------------
/tfhe/asm_decompose.go:
--------------------------------------------------------------------------------
1 | //go:build !(amd64 && !purego)
2 |
3 | package tfhe
4 |
5 | import (
6 | "github.com/sp301415/tfhe-go/math/num"
7 | "github.com/sp301415/tfhe-go/math/poly"
8 | )
9 |
10 | // decomposePolyTo decomposes p with respect to gadgetParams and writes it to dcmpOut.
11 | func decomposePolyTo[T TorusInt](dcmpOut []poly.Poly[T], p poly.Poly[T], gadgetParams GadgetParameters[T]) {
12 | logLastBaseQ := gadgetParams.LogLastBaseQ()
13 | for i := 0; i < p.Rank(); i++ {
14 | c := num.DivRoundBits(p.Coeffs[i], logLastBaseQ)
15 | for j := gadgetParams.level - 1; j >= 1; j-- {
16 | dcmpOut[j].Coeffs[i] = c & (gadgetParams.base - 1)
17 | c >>= gadgetParams.logBase
18 | c += dcmpOut[j].Coeffs[i] >> (gadgetParams.logBase - 1)
19 | dcmpOut[j].Coeffs[i] -= (dcmpOut[j].Coeffs[i] & (gadgetParams.base >> 1)) << 1
20 | }
21 | dcmpOut[0].Coeffs[i] = c & (gadgetParams.base - 1)
22 | dcmpOut[0].Coeffs[i] -= (dcmpOut[0].Coeffs[i] & (gadgetParams.base >> 1)) << 1
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/math/poly/asm_fold_stub_amd64.go:
--------------------------------------------------------------------------------
1 | // Code generated by command: go run asmgen.go -fold -out ../../math/poly/asm_fold_amd64.s -stubs ../../math/poly/asm_fold_stub_amd64.go -pkg=poly. DO NOT EDIT.
2 |
3 | //go:build amd64 && !purego
4 |
5 | package poly
6 |
7 | //go:noescape
8 | func foldPolyToUint32AVX2(fpOut []float64, p []uint32)
9 |
10 | //go:noescape
11 | func foldPolyToUint64AVX2(fpOut []float64, p []uint64)
12 |
13 | //go:noescape
14 | func floatModQInPlaceAVX2(coeffs []float64, q float64, qInv float64)
15 |
16 | //go:noescape
17 | func unfoldPolyToUint32AVX2(pOut []uint32, fp []float64)
18 |
19 | //go:noescape
20 | func unfoldPolyAddToUint32AVX2(pOut []uint32, fp []float64)
21 |
22 | //go:noescape
23 | func unfoldPolySubToUint32AVX2(pOut []uint32, fp []float64)
24 |
25 | //go:noescape
26 | func unfoldPolyToUint64AVX2(pOut []uint64, fp []float64)
27 |
28 | //go:noescape
29 | func unfoldPolyAddToUint64AVX2(pOut []uint64, fp []float64)
30 |
31 | //go:noescape
32 | func unfoldPolySubToUint64AVX2(pOut []uint64, fp []float64)
33 |
--------------------------------------------------------------------------------
/xtfhe/manylut_test.go:
--------------------------------------------------------------------------------
1 | package xtfhe_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/sp301415/tfhe-go/math/num"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | "github.com/sp301415/tfhe-go/xtfhe"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | var (
13 | manyLUTParams = xtfhe.ParamsUint2LUT4.Compile()
14 | manyLUTEnc = tfhe.NewEncryptor(manyLUTParams.BaseParams())
15 | manyLUTEval = xtfhe.NewManyLUTEvaluator(manyLUTParams, manyLUTEnc.GenEvalKeyParallel())
16 | )
17 |
18 | func TestManyLUT(t *testing.T) {
19 | m := int(num.Sqrt(manyLUTParams.BaseParams().MessageModulus()))
20 | fs := make([]func(int) int, manyLUTParams.LUTCount())
21 | for i := 0; i < manyLUTParams.LUTCount(); i++ {
22 | j := i
23 | fs[i] = func(x int) int { return 2*x + j }
24 | }
25 |
26 | ct := manyLUTEnc.EncryptLWE(m)
27 | ctOut := manyLUTEval.BootstrapFunc(ct, fs)
28 |
29 | for i := 0; i < manyLUTParams.LUTCount(); i++ {
30 | assert.Equal(t, manyLUTEnc.DecryptLWE(ctOut[i]), fs[i](m)%int(manyLUTParams.BaseParams().MessageModulus()))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
8 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
9 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
11 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
12 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
13 |
--------------------------------------------------------------------------------
/xtfhe/circuit_bootstrap_test.go:
--------------------------------------------------------------------------------
1 | package xtfhe_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/sp301415/tfhe-go/math/vec"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | "github.com/sp301415/tfhe-go/xtfhe"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | var (
13 | cbParams = xtfhe.ParamsCircuitBootstrapMedium.Compile()
14 | cbEnc = tfhe.NewEncryptor(cbParams.Params())
15 | cbKeyGen = xtfhe.NewCircuitBootstrapKeyGenerator(cbParams, cbEnc.SecretKey)
16 | cbEval = xtfhe.NewCircuitBootstrapper(cbParams, cbEnc.GenEvalKeyParallel(), cbKeyGen.GenCircuitBootstrapKey())
17 | )
18 |
19 | func TestCircuitBootstrap(t *testing.T) {
20 | msgGLWE := []int{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}
21 | ctGLWE := cbEnc.EncryptGLWE(msgGLWE)
22 |
23 | for _, c := range []int{0, 1} {
24 | ctLWE := cbEnc.EncryptLWE(c)
25 | ctFFTGGSW := cbEval.CircuitBootstrap(ctLWE)
26 | ctGLWEOut := cbEval.ExternalProdGLWE(ctFFTGGSW, ctGLWE)
27 |
28 | msgGLWEOut := cbEnc.DecryptGLWE(ctGLWEOut)[:len(msgGLWE)]
29 | assert.Equal(t, msgGLWEOut, vec.ScalarMul(msgGLWE, c))
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/xtfhe/sanitization_params_list.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | var (
6 | // ParamsSanitizeBinary is the default parameters for sanitizing binary TFHE ciphertexts.
7 | ParamsSanitizeBinary = SanitizationParametersLiteral[uint64]{
8 | BaseParams: tfhe.ParametersLiteral[uint64]{
9 | LWEDimension: 612,
10 | GLWERank: 1,
11 | PolyRank: 2048,
12 |
13 | BlockSize: 1,
14 |
15 | MessageModulus: 1 << 1,
16 |
17 | LWEStdDev: 0.00007905932600864546,
18 | GLWEStdDev: 0.0000000000000003472576015484159,
19 |
20 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
21 | Base: 1 << 11,
22 | Level: 3,
23 | },
24 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
25 | Base: 1 << 3,
26 | Level: 4,
27 | },
28 |
29 | BootstrapOrder: tfhe.OrderKeySwitchBlindRotate,
30 | },
31 |
32 | RandSigma: 0.00000000000000008449279779330043,
33 | RandTau: 0.000000000570793820594853,
34 | LinEvalSigma: 0.000000000000000012106655611491527,
35 | LinEvalTau: 0.008864348118988643,
36 | }
37 | )
38 |
--------------------------------------------------------------------------------
/tfhe/fft_glwe_enc_public.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // EncryptFFTGLWE encodes and encrypts integer messages to FFTGLWE ciphertext.
4 | func (e *PublicEncryptor[T]) EncryptFFTGLWE(messages []int) FFTGLWECiphertext[T] {
5 | return e.EncryptFFTGLWEPlaintext(e.EncodeGLWE(messages))
6 | }
7 |
8 | // EncryptFFTGLWETo encrypts and encrypts integer messages to FFTGLWE ciphertext and writes it to ctOut.
9 | func (e *PublicEncryptor[T]) EncryptFFTGLWETo(ctOut FFTGLWECiphertext[T], messages []int) {
10 | e.EncryptFFTGLWEPlaintextTo(ctOut, e.EncodeGLWE(messages))
11 | }
12 |
13 | // EncryptFFTGLWEPlaintext encrypts GLWE plaintext to FFTGLWE ciphertext.
14 | func (e *PublicEncryptor[T]) EncryptFFTGLWEPlaintext(pt GLWEPlaintext[T]) FFTGLWECiphertext[T] {
15 | ctOut := NewFFTGLWECiphertext(e.Params)
16 | e.EncryptFFTGLWEPlaintextTo(ctOut, pt)
17 | return ctOut
18 | }
19 |
20 | // EncryptFFTGLWEPlaintextTo encrypts GLWE plaintext to FFTGLWE ciphertext and writes it to ctOut.
21 | func (e *PublicEncryptor[T]) EncryptFFTGLWEPlaintextTo(ctOut FFTGLWECiphertext[T], pt GLWEPlaintext[T]) {
22 | e.EncryptGLWEPlaintextTo(e.buf.ctGLWE, pt)
23 | e.FwdFFTGLWECiphertextTo(ctOut, e.buf.ctGLWE)
24 | }
25 |
--------------------------------------------------------------------------------
/math/csprng/gaussian_sampler_test.go:
--------------------------------------------------------------------------------
1 | package csprng_test
2 |
3 | import (
4 | "math"
5 | "testing"
6 |
7 | "github.com/sp301415/tfhe-go/math/csprng"
8 | "github.com/sp301415/tfhe-go/math/vec"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | func meanStdDev(v []float64) (mean, stdDev float64) {
13 | sum := 0.0
14 | for _, x := range v {
15 | sum += x
16 | }
17 |
18 | mean = sum / float64(len(v))
19 |
20 | variance := 0.0
21 | for _, x := range v {
22 | variance += (x - mean) * (x - mean)
23 | }
24 | stdDev = math.Sqrt(variance / float64(len(v)))
25 |
26 | return
27 | }
28 |
29 | func TestGaussianSampler(t *testing.T) {
30 | mean := 0.0
31 | sigma := math.Exp2(16)
32 |
33 | gs := csprng.NewGaussianSampler[int64]()
34 | samples := make([]int64, 1024)
35 | gs.SampleVecTo(samples, sigma)
36 | samplesFloat := vec.Cast[float64](samples)
37 | meanSample, stdDevSample := meanStdDev(samplesFloat)
38 |
39 | k := 3.29 // From the GLITCH test suite
40 | N := float64(len(samples))
41 | meanBound := meanSample + k*stdDevSample/math.Sqrt(N)
42 | stdDevBound := stdDevSample + k*stdDevSample/math.Sqrt(2*(N-1))
43 |
44 | assert.GreaterOrEqual(t, meanBound, mean)
45 | assert.GreaterOrEqual(t, stdDevBound, sigma)
46 | }
47 |
--------------------------------------------------------------------------------
/tfhe/binary_encoder.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // BinaryEncoder encodes boolean messages to TFHE plaintexts.
4 | // Encoder is embedded in Encryptor and Evaluator,
5 | // so usually manual instantiation isn't needed.
6 | //
7 | // BinaryEncoder is safe for concurrent use.
8 | type BinaryEncoder[T TorusInt] struct {
9 | // Params is parameters for this Encoder.
10 | Params Parameters[T]
11 | // Encoder is a generic Encoder for this BinaryEncoder.
12 | Encoder *Encoder[T]
13 | }
14 |
15 | // NewBinaryEncoder returns a initialized BinaryEncoder with given parameters.
16 | func NewBinaryEncoder[T TorusInt](params Parameters[T]) *BinaryEncoder[T] {
17 | return &BinaryEncoder[T]{
18 | Params: params,
19 | Encoder: NewEncoder(params),
20 | }
21 | }
22 |
23 | // EncodeLWEBool encodes boolean message to LWE plaintext.
24 | //
25 | // Note that this is different from calling EncodeLWE with 0 or 1.
26 | func (e *BinaryEncoder[T]) EncodeLWEBool(message bool) LWEPlaintext[T] {
27 | if message {
28 | return LWEPlaintext[T]{Value: 1 << (e.Params.logQ - 3)}
29 | }
30 | return LWEPlaintext[T]{Value: 7 << (e.Params.logQ - 3)}
31 | }
32 |
33 | // DecodeLWEBool decodes LWE plaintext to boolean message.
34 | func (e *BinaryEncoder[T]) DecodeLWEBool(pt LWEPlaintext[T]) bool {
35 | return pt.Value < (1 << (e.Params.logQ - 1))
36 | }
37 |
--------------------------------------------------------------------------------
/math/poly/asm_vec_cmplx_stub_amd64.go:
--------------------------------------------------------------------------------
1 | // Code generated by command: go run asmgen.go -vec_cmplx -out ../../math/poly/asm_vec_cmplx_amd64.s -stubs ../../math/poly/asm_vec_cmplx_stub_amd64.go -pkg=poly. DO NOT EDIT.
2 |
3 | //go:build amd64 && !purego
4 |
5 | package poly
6 |
7 | //go:noescape
8 | func addCmplxToAVX2(vOut []float64, v0 []float64, v1 []float64)
9 |
10 | //go:noescape
11 | func subCmplxToAVX2(vOut []float64, v0 []float64, v1 []float64)
12 |
13 | //go:noescape
14 | func negCmplxToAVX2(vOut []float64, v []float64)
15 |
16 | //go:noescape
17 | func floatMulCmplxToAVX2(vOut []float64, v []float64, c float64)
18 |
19 | //go:noescape
20 | func floatMulAddCmplxToAVX2(vOut []float64, v []float64, c float64)
21 |
22 | //go:noescape
23 | func floatMulSubCmplxToAVX2(vOut []float64, v []float64, c float64)
24 |
25 | //go:noescape
26 | func cmplxMulCmplxToAVX2(vOut []float64, v []float64, c complex128)
27 |
28 | //go:noescape
29 | func cmplxMulAddCmplxToAVX2(vOut []float64, v []float64, c complex128)
30 |
31 | //go:noescape
32 | func cmplxMulSubCmplxToAVX2(vOut []float64, v []float64, c complex128)
33 |
34 | //go:noescape
35 | func mulCmplxToAVX2(vOut []float64, v0 []float64, v1 []float64)
36 |
37 | //go:noescape
38 | func mulAddCmplxToAVX2(vOut []float64, v0 []float64, v1 []float64)
39 |
40 | //go:noescape
41 | func mulSubCmplxToAVX2(vOut []float64, v0 []float64, v1 []float64)
42 |
--------------------------------------------------------------------------------
/internal/asmgen/fft_butterfly.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | . "github.com/mmcloughlin/avo/build"
5 | "github.com/mmcloughlin/avo/reg"
6 | )
7 |
8 | func FwdButterflyAVX2(uR, uI, vR, vI, wR, wI reg.VecVirtual) {
9 | vwR := YMM()
10 | VMULPD(wR, vR, vwR)
11 | VFNMADD231PD(wI, vI, vwR)
12 |
13 | vwI := YMM()
14 | VMULPD(wI, vR, vwI)
15 | VFMADD231PD(wR, vI, vwI)
16 |
17 | VSUBPD(vwR, uR, vR)
18 | VSUBPD(vwI, uI, vI)
19 | VADDPD(vwR, uR, uR)
20 | VADDPD(vwI, uI, uI)
21 | }
22 |
23 | func FwdButterflyAVX2XMM(uR, uI, vR, vI, wR, wI reg.VecVirtual) {
24 | vwR := XMM()
25 | VMULPD(wR, vR, vwR)
26 | VFNMADD231PD(wI, vI, vwR)
27 |
28 | vwI := XMM()
29 | VMULPD(wI, vR, vwI)
30 | VFMADD231PD(wR, vI, vwI)
31 |
32 | VSUBPD(vwR, uR, vR)
33 | VSUBPD(vwI, uI, vI)
34 | VADDPD(vwR, uR, uR)
35 | VADDPD(vwI, uI, uI)
36 | }
37 |
38 | func InvButterflyAVX2(uR, uI, vR, vI, wR, wI reg.VecVirtual) {
39 | vuR, vuI := YMM(), YMM()
40 |
41 | VSUBPD(vR, uR, vuR)
42 | VADDPD(vR, uR, uR)
43 |
44 | VSUBPD(vI, uI, vuI)
45 | VADDPD(vI, uI, uI)
46 |
47 | VMULPD(wR, vuR, vR)
48 | VFNMADD231PD(wI, vuI, vR)
49 |
50 | VMULPD(wI, vuR, vI)
51 | VFMADD231PD(wR, vuI, vI)
52 | }
53 |
54 | func InvButterflyAVX2XMM(uR, uI, vR, vI, wR, wI reg.VecVirtual) {
55 | vuR, vuI := XMM(), XMM()
56 |
57 | VSUBPD(vR, uR, vuR)
58 | VADDPD(vR, uR, uR)
59 |
60 | VSUBPD(vI, uI, vuI)
61 | VADDPD(vI, uI, uI)
62 |
63 | VMULPD(wR, vuR, vR)
64 | VFNMADD231PD(wI, vuI, vR)
65 |
66 | VMULPD(wI, vuR, vI)
67 | VFMADD231PD(wR, vuI, vI)
68 | }
69 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI Tests
2 | on:
3 | push:
4 |
5 | jobs:
6 | tests:
7 | name: Run Go ${{ matrix.go }} Tests
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | go: [ '1.18', '1.25' ]
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Setup Go
17 | uses: actions/setup-go@v5
18 | with:
19 | go-version: ${{ matrix.go }}
20 |
21 | - name: Test math/csprng
22 | run: |
23 | go test ./math/csprng -timeout=0 -count=1
24 |
25 | - name: Test math/poly
26 | run: |
27 | go test ./math/poly -timeout=0 -count=1
28 | go test ./math/poly -timeout=0 -count=1 -tags=purego
29 | GODEBUG=cpu.all=off go test ./math/poly -count=1
30 |
31 | - name: Test math/vec
32 | run: |
33 | go test ./math/vec -timeout=0 -count=1
34 | go test ./math/vec -timeout=0 -count=1 -tags=purego
35 | GODEBUG=cpu.all=off go test ./math/vec -count=1
36 |
37 | - name: Test tfhe
38 | run: |
39 | go test ./tfhe -timeout=0 -count=1
40 | go test ./tfhe -timeout=0 -count=1 -tags=purego
41 | GODEBUG=cpu.all=off go test ./tfhe -count=1
42 |
43 | - name: Test mktfhe
44 | run: |
45 | go test ./mktfhe -timeout=0 -count=1
46 | go test ./mktfhe -timeout=0 -count=1 -tags=purego
47 | GODEBUG=cpu.all=off go test ./mktfhe -count=1
48 |
49 | - name: Test xtfhe
50 | run: |
51 | go test ./xtfhe -timeout=0 -count=1
52 | go test ./xtfhe -timeout=0 -count=1 -tags=purego
53 | GODEBUG=cpu.all=off go test ./xtfhe -count=1
54 |
--------------------------------------------------------------------------------
/math/vec/asm_vec_stub_amd64.go:
--------------------------------------------------------------------------------
1 | // Code generated by command: go run asmgen.go -vec -out ../../math/vec/asm_vec_amd64.s -stubs ../../math/vec/asm_vec_stub_amd64.go -pkg=vec. DO NOT EDIT.
2 |
3 | //go:build amd64 && !purego
4 |
5 | package vec
6 |
7 | //go:noescape
8 | func addToUint32AVX2(vOut []uint32, v0 []uint32, v1 []uint32)
9 |
10 | //go:noescape
11 | func subToUint32AVX2(vOut []uint32, v0 []uint32, v1 []uint32)
12 |
13 | //go:noescape
14 | func addToUint64AVX2(vOut []uint64, v0 []uint64, v1 []uint64)
15 |
16 | //go:noescape
17 | func subToUint64AVX2(vOut []uint64, v0 []uint64, v1 []uint64)
18 |
19 | //go:noescape
20 | func scalarMulToUint32AVX2(vOut []uint32, v []uint32, c uint32)
21 |
22 | //go:noescape
23 | func scalarMulAddToUint32AVX2(vOut []uint32, v []uint32, c uint32)
24 |
25 | //go:noescape
26 | func scalarMulSubToUint32AVX2(vOut []uint32, v []uint32, c uint32)
27 |
28 | //go:noescape
29 | func scalarMulToUint64AVX2(vOut []uint64, v []uint64, c uint64)
30 |
31 | //go:noescape
32 | func scalarMulAddToUint64AVX2(vOut []uint64, v []uint64, c uint64)
33 |
34 | //go:noescape
35 | func scalarMulSubToUint64AVX2(vOut []uint64, v []uint64, c uint64)
36 |
37 | //go:noescape
38 | func mulToUint32AVX2(vOut []uint32, v0 []uint32, v1 []uint32)
39 |
40 | //go:noescape
41 | func mulAddToUint32AVX2(vOut []uint32, v0 []uint32, v1 []uint32)
42 |
43 | //go:noescape
44 | func mulSubToUint32AVX2(vOut []uint32, v0 []uint32, v1 []uint32)
45 |
46 | //go:noescape
47 | func mulToUint64AVX2(vOut []uint64, v0 []uint64, v1 []uint64)
48 |
49 | //go:noescape
50 | func mulAddToUint64AVX2(vOut []uint64, v0 []uint64, v1 []uint64)
51 |
52 | //go:noescape
53 | func mulSubToUint64AVX2(vOut []uint64, v0 []uint64, v1 []uint64)
54 |
--------------------------------------------------------------------------------
/tfhe/asm_decompose_amd64.go:
--------------------------------------------------------------------------------
1 | //go:build amd64 && !purego
2 |
3 | package tfhe
4 |
5 | import (
6 | "unsafe"
7 |
8 | "github.com/sp301415/tfhe-go/math/num"
9 | "github.com/sp301415/tfhe-go/math/poly"
10 | "golang.org/x/sys/cpu"
11 | )
12 |
13 | // decomposePolyTo decomposes p with respect to gadgetParams and writes it to dcmpOut.
14 | func decomposePolyTo[T TorusInt](dcmpOut []poly.Poly[T], p poly.Poly[T], gadgetParams GadgetParameters[T]) {
15 | if cpu.X86.HasAVX && cpu.X86.HasAVX2 {
16 | var z T
17 | switch any(z).(type) {
18 | case uint32:
19 | decomposePolyToUint32AVX2(
20 | *(*[][]uint32)(unsafe.Pointer(&dcmpOut)),
21 | *(*[]uint32)(unsafe.Pointer(&p)),
22 | uint32(gadgetParams.base),
23 | uint32(gadgetParams.logBase),
24 | uint32(gadgetParams.LogLastBaseQ()),
25 | )
26 | return
27 | case uint64:
28 | decomposePolyToUint64AVX2(
29 | *(*[][]uint64)(unsafe.Pointer(&dcmpOut)),
30 | *(*[]uint64)(unsafe.Pointer(&p)),
31 | uint64(gadgetParams.base),
32 | uint64(gadgetParams.logBase),
33 | uint64(gadgetParams.LogLastBaseQ()),
34 | )
35 | return
36 | }
37 | }
38 |
39 | logLastBaseQ := gadgetParams.LogLastBaseQ()
40 | for i := 0; i < p.Rank(); i++ {
41 | c := num.DivRoundBits(p.Coeffs[i], logLastBaseQ)
42 | for j := gadgetParams.level - 1; j >= 1; j-- {
43 | dcmpOut[j].Coeffs[i] = c & (gadgetParams.base - 1)
44 | c >>= gadgetParams.logBase
45 | c += dcmpOut[j].Coeffs[i] >> (gadgetParams.logBase - 1)
46 | dcmpOut[j].Coeffs[i] -= (dcmpOut[j].Coeffs[i] & (gadgetParams.base >> 1)) << 1
47 | }
48 | dcmpOut[0].Coeffs[i] = c & (gadgetParams.base - 1)
49 | dcmpOut[0].Coeffs[i] -= (dcmpOut[0].Coeffs[i] & (gadgetParams.base >> 1)) << 1
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/mktfhe/lwe.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/vec"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // LWECiphertext is a multi-key variant of [tfhe.LWECiphertext].
9 | type LWECiphertext[T tfhe.TorusInt] struct {
10 | // Value has length DefaultDimension + 1.
11 | Value []T
12 | }
13 |
14 | // NewLWECiphertext creates a new LWE ciphertext.
15 | func NewLWECiphertext[T tfhe.TorusInt](params Parameters[T]) LWECiphertext[T] {
16 | return LWECiphertext[T]{Value: make([]T, params.DefaultLWEDimension()+1)}
17 | }
18 |
19 | // NewLWECiphertextCustom creates a new LWE ciphertext with given dimension.
20 | func NewLWECiphertextCustom[T tfhe.TorusInt](lweDimension int) LWECiphertext[T] {
21 | return LWECiphertext[T]{Value: make([]T, lweDimension+1)}
22 | }
23 |
24 | // Copy returns a copy of the ciphertext.
25 | func (ct LWECiphertext[T]) Copy() LWECiphertext[T] {
26 | return LWECiphertext[T]{Value: vec.Copy(ct.Value)}
27 | }
28 |
29 | // CopyFrom copies values from the ciphertext.
30 | func (ct *LWECiphertext[T]) CopyFrom(ctIn LWECiphertext[T]) {
31 | copy(ct.Value, ctIn.Value)
32 | }
33 |
34 | // CopyFromSubKey copies values from the single-key ciphertext.
35 | //
36 | // Panics if the dimension of the ciphertext is not a multiple of the dimension of the single-key ciphertext.
37 | func (ct *LWECiphertext[T]) CopyFromSubKey(ctIn tfhe.LWECiphertext[T], idx int) {
38 | if (len(ct.Value)-1)%(len(ctIn.Value)-1) != 0 {
39 | panic("LWE Dimension mismatch")
40 | }
41 |
42 | ct.Clear()
43 | singleKeyLWEDimension := len(ctIn.Value) - 1
44 | copy(ct.Value[1+idx*singleKeyLWEDimension:1+(idx+1)*singleKeyLWEDimension], ctIn.Value[1:])
45 | ct.Value[0] = ctIn.Value[0]
46 | }
47 |
48 | // Clear clears the ciphertext.
49 | func (ct *LWECiphertext[T]) Clear() {
50 | vec.Fill(ct.Value, 0)
51 | }
52 |
--------------------------------------------------------------------------------
/mktfhe/binary_decryptor.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | // BinaryDecryptor is a multi-key TFHE binary decryptor.
6 | type BinaryDecryptor[T tfhe.TorusInt] struct {
7 | // BinaryEncoder is an embedded encoder for this BinaryDecryptor.
8 | *tfhe.BinaryEncoder[T]
9 | // Params is parameters for this BinaryDecryptor.
10 | Params Parameters[T]
11 | // Decryptor is a generic Decryptor for this BinaryDecryptor.
12 | Decryptor *Decryptor[T]
13 | }
14 |
15 | // NewBinaryDecryptor creates a new BinaryDecryptor.
16 | func NewBinaryDecryptor[T tfhe.TorusInt](params Parameters[T], sk map[int]tfhe.SecretKey[T]) *BinaryDecryptor[T] {
17 | return &BinaryDecryptor[T]{
18 | BinaryEncoder: tfhe.NewBinaryEncoder(params.subParams),
19 | Params: params,
20 | Decryptor: NewDecryptor(params, sk),
21 | }
22 | }
23 |
24 | // SafeCopy returns a thread-safe copy.
25 | func (d *BinaryDecryptor[T]) SafeCopy() *BinaryDecryptor[T] {
26 | return &BinaryDecryptor[T]{
27 | BinaryEncoder: d.BinaryEncoder,
28 | Params: d.Params,
29 | Decryptor: d.Decryptor.SafeCopy(),
30 | }
31 | }
32 |
33 | // DecryptLWEBool decrypts LWE ciphertext to boolean message.
34 | // Like most languages, false == 0, and true == 1.
35 | //
36 | // Note that this is different from calling DecryptLWE and comparing with 0.
37 | func (d *BinaryDecryptor[T]) DecryptLWEBool(ct LWECiphertext[T]) bool {
38 | return d.DecodeLWEBool(d.Decryptor.DecryptLWEPlaintext(ct))
39 | }
40 |
41 | // DecryptLWEBits decrypts a slice of binary LWE ciphertext
42 | // to integer message.
43 | // The order of bits of LWE ciphertexts are assumed to be little-endian.
44 | func (d *BinaryDecryptor[T]) DecryptLWEBits(ct []LWECiphertext[T]) int {
45 | var message int
46 | for i := len(ct) - 1; i >= 0; i-- {
47 | message <<= 1
48 | if d.DecryptLWEBool(ct[i]) {
49 | message += 1
50 | }
51 | }
52 | return message
53 | }
54 |
--------------------------------------------------------------------------------
/xtfhe/bfv_test.go:
--------------------------------------------------------------------------------
1 | package xtfhe_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/sp301415/tfhe-go/math/num"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | "github.com/sp301415/tfhe-go/xtfhe"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | var (
13 | bfvParams = tfhe.ParamsUint3.Compile()
14 | bfvKeySwitchParams = xtfhe.ParamsBFVKeySwitchLogN11.Compile()
15 | bfvEnc = tfhe.NewEncryptor(bfvParams)
16 | bfvKeyGen = xtfhe.NewBFVKeyGenerator(bfvParams, bfvEnc.SecretKey)
17 | bfvEval = xtfhe.NewBFVEvaluator(bfvParams, xtfhe.BFVEvaluationKey[uint64]{
18 | RelinKey: bfvKeyGen.GenRelinKey(bfvKeySwitchParams),
19 | GaloisKeys: bfvKeyGen.GenGaloisKeysForPack(bfvKeySwitchParams),
20 | })
21 | )
22 |
23 | func TestBFVMul(t *testing.T) {
24 | m0 := int(num.Sqrt(bfvParams.MessageModulus()) - 1)
25 | m1 := int(num.Sqrt(bfvParams.MessageModulus()) - 2)
26 |
27 | ct0 := bfvEnc.EncryptGLWE([]int{m0})
28 | ct1 := bfvEnc.EncryptGLWE([]int{m1})
29 |
30 | ctMul := bfvEval.Mul(ct0, ct1)
31 |
32 | assert.Equal(t, bfvEnc.DecryptGLWE(ctMul)[0], m0*m1)
33 | }
34 |
35 | func TestBFVPermute(t *testing.T) {
36 | m0 := 1
37 | m1 := 1
38 | d := 1<<5 + 1
39 |
40 | ct0 := bfvEnc.EncryptGLWE([]int{m0, m1})
41 | ctAut := bfvEval.Permute(ct0, d)
42 |
43 | assert.Equal(t, bfvEnc.DecryptGLWE(ctAut)[0], m0)
44 | assert.Equal(t, bfvEnc.DecryptGLWE(ctAut)[d], m1)
45 | }
46 |
47 | func TestLWEToGLWECiphertext(t *testing.T) {
48 | m := 3
49 | ctLWE := bfvEnc.EncryptLWE(m)
50 | ctGLWE := bfvEval.Pack(ctLWE)
51 |
52 | assert.Equal(t, bfvEnc.DecryptGLWE(ctGLWE)[0], m)
53 | }
54 |
55 | func BenchmarkBFVMul(b *testing.B) {
56 | ct0 := bfvEnc.EncryptGLWE(nil)
57 | ct1 := bfvEnc.EncryptGLWE(nil)
58 | ctMul := bfvEnc.EncryptGLWE(nil)
59 |
60 | b.ResetTimer()
61 | for i := 0; i < b.N; i++ {
62 | bfvEval.MulTo(ctMul, ct0, ct1)
63 | }
64 | }
65 |
66 | func BenchmarkBFVRingPack(b *testing.B) {
67 | ctLWE := bfvEnc.EncryptLWE(0)
68 | ctGLWE := bfvEnc.EncryptGLWE(nil)
69 |
70 | b.ResetTimer()
71 | for i := 0; i < b.N; i++ {
72 | bfvEval.PackTo(ctGLWE, ctLWE)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/mktfhe/bootstrap_key.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | // EvaluationKey is a multi-key variant of [tfhe.EvaluationKey].
6 | type EvaluationKey[T tfhe.TorusInt] struct {
7 | // EvaluationKey is an embedded single-key EvaluationKey.
8 | tfhe.EvaluationKey[T]
9 | // CRSPublicKey is a public key from the common reference string.
10 | CRSPublicKey tfhe.FFTGLevCiphertext[T]
11 | // RelinKey is a relinearization key.
12 | RelinKey FFTUniEncryption[T]
13 | }
14 |
15 | // NewEvaluationKey creates a new EvaluationKey.
16 | func NewEvaluationKey[T tfhe.TorusInt](params Parameters[T]) EvaluationKey[T] {
17 | return EvaluationKey[T]{
18 | EvaluationKey: tfhe.NewEvaluationKey(params.subParams),
19 | CRSPublicKey: tfhe.NewFFTGLevCiphertext(params.subParams, params.relinKeyParams),
20 | RelinKey: NewFFTUniEncryption(params, params.relinKeyParams),
21 | }
22 | }
23 |
24 | // NewEvaluationKeyCustom creates a new EvaluationKey with custom parameters.
25 | func NewEvaluationKeyCustom[T tfhe.TorusInt](lweDimension, polyRank int, blindRotateParams, keySwitchParams, relinParams tfhe.GadgetParameters[T]) EvaluationKey[T] {
26 | return EvaluationKey[T]{
27 | EvaluationKey: tfhe.NewEvaluationKeyCustom(lweDimension, 1, polyRank, blindRotateParams, keySwitchParams),
28 | CRSPublicKey: tfhe.NewFFTGLevCiphertextCustom(1, polyRank, relinParams),
29 | RelinKey: NewFFTUniEncryptionCustom(polyRank, relinParams),
30 | }
31 | }
32 |
33 | // Copy returns a copy of the key.
34 | func (evk EvaluationKey[T]) Copy() EvaluationKey[T] {
35 | return EvaluationKey[T]{
36 | EvaluationKey: evk.EvaluationKey.Copy(),
37 | CRSPublicKey: evk.CRSPublicKey.Copy(),
38 | RelinKey: evk.RelinKey.Copy(),
39 | }
40 | }
41 |
42 | // CopyFrom copies values from key.
43 | func (evk *EvaluationKey[T]) CopyFrom(evkIn EvaluationKey[T]) {
44 | evk.EvaluationKey.CopyFrom(evkIn.EvaluationKey)
45 | evk.CRSPublicKey.CopyFrom(evkIn.CRSPublicKey)
46 | evk.RelinKey.CopyFrom(evkIn.RelinKey)
47 | }
48 |
49 | // Clear clears the key.
50 | func (evk *EvaluationKey[T]) Clear() {
51 | evk.EvaluationKey.Clear()
52 | evk.CRSPublicKey.Clear()
53 | evk.RelinKey.Clear()
54 | }
55 |
--------------------------------------------------------------------------------
/xtfhe/manylut_params.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/num"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // ManyLUTParametersLiteral is a structure for PBSManyLUT Parameters.
9 | //
10 | // PolyRank does not equal LUTSize.
11 | type ManyLUTParametersLiteral[T tfhe.TorusInt] struct {
12 | // BaseParams is a base parameters for this ManyLUTParametersLiteral.
13 | BaseParams tfhe.ParametersLiteral[T]
14 |
15 | // LUTCount is the number of LUTs that can be evaluated at once.
16 | // LUTCount must be a power of 2.
17 | LUTCount int
18 | }
19 |
20 | // Compile transforms ParametersLiteral to read-only Parameters.
21 | // If there is any invalid parameter in the literal, it panics.
22 | // Default parameters are guaranteed to be compiled without panics.
23 | func (p ManyLUTParametersLiteral[T]) Compile() ManyLUTParameters[T] {
24 | baseParams := p.BaseParams.Compile()
25 |
26 | switch {
27 | case baseParams.PolyRank() != baseParams.LUTSize():
28 | panic("PolyRank does not equal LUTSize")
29 | case !num.IsPowerOfTwo(p.LUTCount):
30 | panic("lutCount not power of two")
31 | }
32 |
33 | return ManyLUTParameters[T]{
34 | baseParams: baseParams,
35 |
36 | lutCount: p.LUTCount,
37 | logLUTCount: num.Log2(p.LUTCount),
38 | }
39 | }
40 |
41 | // ManyLUTParameters is a parameter set for PBSManyLUT.
42 | type ManyLUTParameters[T tfhe.TorusInt] struct {
43 | // baseParams is a base parameters for this ManyLUTParameters.
44 | baseParams tfhe.Parameters[T]
45 |
46 | // LUTCount is the number of LUTs that can be evaluated at once.
47 | // LUTCount must be a power of 2.
48 | lutCount int
49 | // LogLUTCount equals log(LUTCount).
50 | logLUTCount int
51 | }
52 |
53 | // BaseParams returns the base parameters for this ManyLUTParameters.
54 | func (p ManyLUTParameters[T]) BaseParams() tfhe.Parameters[T] {
55 | return p.baseParams
56 | }
57 |
58 | // LUTCount returns the number of LUTs that can be evaluated at once.
59 | func (p ManyLUTParameters[T]) LUTCount() int {
60 | return p.lutCount
61 | }
62 |
63 | // LogLUTCount returns log(LUTCount).
64 | func (p ManyLUTParameters[T]) LogLUTCount() int {
65 | return p.logLUTCount
66 | }
67 |
--------------------------------------------------------------------------------
/mktfhe/bootstrap_key_marshal.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "bytes"
5 | "io"
6 | )
7 |
8 | // ByteSize returns the size of the key in bytes.
9 | func (evk EvaluationKey[T]) ByteSize() int {
10 | return evk.EvaluationKey.ByteSize() + evk.CRSPublicKey.ByteSize() + evk.RelinKey.ByteSize()
11 | }
12 |
13 | // WriteTo implements the [io.WriterTo] interface.
14 | //
15 | // The encoded form is as follows:
16 | //
17 | // EvaluationKey
18 | // CRSPublicKey
19 | // RelinKey
20 | func (evk EvaluationKey[T]) WriteTo(w io.Writer) (n int64, err error) {
21 | var nWrite int64
22 |
23 | if nWrite, err = evk.EvaluationKey.WriteTo(w); err != nil {
24 | return n + nWrite, err
25 | }
26 | n += nWrite
27 |
28 | if nWrite, err = evk.CRSPublicKey.WriteTo(w); err != nil {
29 | return n + nWrite, err
30 | }
31 | n += nWrite
32 |
33 | if nWrite, err = evk.RelinKey.WriteTo(w); err != nil {
34 | return n + nWrite, err
35 | }
36 | n += nWrite
37 |
38 | if n < int64(evk.ByteSize()) {
39 | return n, io.ErrShortWrite
40 | }
41 |
42 | return
43 | }
44 |
45 | // ReadFrom implements the [io.ReaderFrom] interface.
46 | func (evk *EvaluationKey[T]) ReadFrom(r io.Reader) (n int64, err error) {
47 | var nRead int64
48 |
49 | if nRead, err = evk.EvaluationKey.ReadFrom(r); err != nil {
50 | return n + nRead, err
51 | }
52 | n += nRead
53 |
54 | if nRead, err = evk.CRSPublicKey.ReadFrom(r); err != nil {
55 | return n + nRead, err
56 | }
57 | n += nRead
58 |
59 | if nRead, err = evk.RelinKey.ReadFrom(r); err != nil {
60 | return n + nRead, err
61 | }
62 | n += nRead
63 |
64 | if n < int64(evk.ByteSize()) {
65 | return n, io.ErrShortWrite
66 | }
67 |
68 | return
69 | }
70 |
71 | // MarshalBinary implements the [encoding.BinaryMarshaler] interface.
72 | func (evk EvaluationKey[T]) MarshalBinary() (data []byte, err error) {
73 | buf := bytes.NewBuffer(make([]byte, 0, evk.ByteSize()))
74 | _, err = evk.WriteTo(buf)
75 | return buf.Bytes(), err
76 | }
77 |
78 | // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.
79 | func (evk *EvaluationKey[T]) UnmarshalBinary(data []byte) error {
80 | buf := bytes.NewBuffer(data)
81 | _, err := evk.ReadFrom(buf)
82 | return err
83 | }
84 |
--------------------------------------------------------------------------------
/tfhe/glwe_enc_public.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // EncryptGLWE encodes and encrypts integer messages to GLWE ciphertext.
4 | func (e *PublicEncryptor[T]) EncryptGLWE(messages []int) GLWECiphertext[T] {
5 | ctOut := NewGLWECiphertext(e.Params)
6 | e.EncryptGLWETo(ctOut, messages)
7 | return ctOut
8 | }
9 |
10 | // EncryptGLWETo encodes and encrypts integer messages to GLWE ciphertext and writes it to ctOut.
11 | func (e *PublicEncryptor[T]) EncryptGLWETo(ctOut GLWECiphertext[T], messages []int) {
12 | e.EncodeGLWETo(e.buf.ptGLWE, messages)
13 | e.EncryptGLWEPlaintextTo(ctOut, e.buf.ptGLWE)
14 | }
15 |
16 | // EncryptGLWEPlaintext encrypts GLWE plaintext to GLWE ciphertext.
17 | func (e *PublicEncryptor[T]) EncryptGLWEPlaintext(pt GLWEPlaintext[T]) GLWECiphertext[T] {
18 | ctOut := NewGLWECiphertext(e.Params)
19 | e.EncryptGLWEPlaintextTo(ctOut, pt)
20 | return ctOut
21 | }
22 |
23 | // EncryptGLWEPlaintextTo encrypts GLWE plaintext to GLWE ciphertext and writes it to ctOut.
24 | func (e *PublicEncryptor[T]) EncryptGLWEPlaintextTo(ctOut GLWECiphertext[T], pt GLWEPlaintext[T]) {
25 | ctOut.Value[0].CopyFrom(pt.Value)
26 | e.EncryptGLWEBody(ctOut)
27 | }
28 |
29 | // EncryptGLWEBody encrypts the value in the body of GLWE ciphertext and overrides it.
30 | // This avoids the need for most buffers.
31 | func (e *PublicEncryptor[T]) EncryptGLWEBody(ct GLWECiphertext[T]) {
32 | for i := 0; i < e.Params.glweRank; i++ {
33 | e.BinarySampler.SamplePolyTo(e.buf.auxKey.Value[i])
34 | }
35 | e.FwdFFTGLWESecretKeyTo(e.buf.auxFourierKey, e.buf.auxKey)
36 |
37 | e.PolyEvaluator.ShortFFTPolyMulAddPolyTo(ct.Value[0], e.PublicKey.GLWEKey.Value[0].Value[0], e.buf.auxFourierKey.Value[0])
38 | for j := 1; j < e.Params.glweRank+1; j++ {
39 | e.PolyEvaluator.ShortFFTPolyMulAddPolyTo(ct.Value[j], e.PublicKey.GLWEKey.Value[0].Value[j], e.buf.auxFourierKey.Value[0])
40 | }
41 | for i := 1; i < e.Params.glweRank; i++ {
42 | for j := 0; j < e.Params.glweRank+1; j++ {
43 | e.PolyEvaluator.ShortFFTPolyMulAddPolyTo(ct.Value[j], e.PublicKey.GLWEKey.Value[i].Value[j], e.buf.auxFourierKey.Value[i])
44 | }
45 | }
46 |
47 | for j := 0; j < e.Params.glweRank+1; j++ {
48 | e.GaussianSampler.SamplePolyAddTo(ct.Value[j], e.Params.GLWEStdDevQ())
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/math/csprng/binary_sampler.go:
--------------------------------------------------------------------------------
1 | package csprng
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/num"
5 | "github.com/sp301415/tfhe-go/math/poly"
6 | "github.com/sp301415/tfhe-go/math/vec"
7 | )
8 |
9 | // BinarySampler samples values from uniform and block binary distribution.
10 | type BinarySampler[T num.Integer] struct {
11 | baseSampler *UniformSampler[uint64]
12 | }
13 |
14 | // NewBinarySampler creates a new BinarySampler.
15 | //
16 | // Panics when read from crypto/rand or AES initialization fails.
17 | func NewBinarySampler[T num.Integer]() *BinarySampler[T] {
18 | return &BinarySampler[T]{
19 | baseSampler: NewUniformSampler[uint64](),
20 | }
21 | }
22 |
23 | // NewBinarySamplerWithSeed creates a new BinarySampler, with user supplied seed.
24 | //
25 | // Panics when AES initialization fails.
26 | func NewBinarySamplerWithSeed[T num.Integer](seed []byte) *BinarySampler[T] {
27 | return &BinarySampler[T]{
28 | baseSampler: NewUniformSamplerWithSeed[uint64](seed),
29 | }
30 | }
31 |
32 | // Sample uniformly samples a random binary integer.
33 | func (s *BinarySampler[T]) Sample() T {
34 | return T(s.baseSampler.Sample() & 1)
35 | }
36 |
37 | // SampleVecTo samples uniform binary values to vOut.
38 | func (s *BinarySampler[T]) SampleVecTo(vOut []T) {
39 | var buf uint64
40 | for i := 0; i < len(vOut); i++ {
41 | if i&63 == 0 {
42 | buf = s.baseSampler.Sample()
43 | }
44 | vOut[i] = T(buf & 1)
45 | buf >>= 1
46 | }
47 | }
48 |
49 | // SamplePolyTo samples uniform binary values to pOut.
50 | func (s *BinarySampler[T]) SamplePolyTo(pOut poly.Poly[T]) {
51 | s.SampleVecTo(pOut.Coeffs)
52 | }
53 |
54 | // SampleBlockVecTo samples block binary values to vOut.
55 | func (s *BinarySampler[T]) SampleBlockVecTo(vOut []T, blockSize int) {
56 | if len(vOut)%blockSize != 0 {
57 | panic("length not multiple of blocksize")
58 | }
59 |
60 | for i := 0; i < len(vOut); i += blockSize {
61 | vec.Fill(vOut[i:i+blockSize], 0)
62 | offset := int(s.baseSampler.SampleN(uint64(blockSize) + 1))
63 | if offset == blockSize {
64 | continue
65 | }
66 | vOut[i+offset] = 1
67 | }
68 | }
69 |
70 | // SampleBlockPolyTo samples block binary values to pOut.
71 | func (s *BinarySampler[T]) SampleBlockPolyTo(pOut poly.Poly[T], blockSize int) {
72 | s.SampleBlockVecTo(pOut.Coeffs, blockSize)
73 | }
74 |
--------------------------------------------------------------------------------
/xtfhe/circuit_bootstrap_key.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | // CircuitBootstrapKey is a key for Circuit Bootstrapping.
6 | type CircuitBootstrapKey[T tfhe.TorusInt] struct {
7 | // SchemeSwitchKey is a key for scheme switching.
8 | // It has length GLWERank,
9 | // with i-th element being an FFTGGSWCiphertext of i-th secret key.
10 | SchemeSwitchKey []tfhe.FFTGGSWCiphertext[T]
11 | // TraceKeys is a map of galois keys used for LWE to GLWE packing.
12 | TraceKeys map[int]tfhe.GLWEKeySwitchKey[T]
13 | }
14 |
15 | // CircuitBootstrapKeyGenerator generates a key for Circuit Bootstrapping.
16 | type CircuitBootstrapKeyGenerator[T tfhe.TorusInt] struct {
17 | // BFVKeyGenerator is a BFVKeyGenerator for this CircuitBootstrapKeyGenerator.
18 | *BFVKeyGenerator[T]
19 |
20 | // Params is parameters for this CircuitBootstrapKeyGenerator.
21 | Params CircuitBootstrapParameters[T]
22 | }
23 |
24 | // NewCircuitBootstrapKeyGenerator creates a new CircuitBootstrapKeyGenerator.
25 | func NewCircuitBootstrapKeyGenerator[T tfhe.TorusInt](params CircuitBootstrapParameters[T], sk tfhe.SecretKey[T]) *CircuitBootstrapKeyGenerator[T] {
26 | return &CircuitBootstrapKeyGenerator[T]{
27 | BFVKeyGenerator: NewBFVKeyGenerator(params.Params(), sk),
28 | Params: params,
29 | }
30 | }
31 |
32 | // SafeCopy creates a shallow copy of this CircuitBootstrapKeyGenerator.
33 | // Returned CircuitBootstrapKeyGenerator is safe for concurrent use.
34 | func (kg *CircuitBootstrapKeyGenerator[T]) SafeCopy() *CircuitBootstrapKeyGenerator[T] {
35 | return &CircuitBootstrapKeyGenerator[T]{
36 | BFVKeyGenerator: kg.BFVKeyGenerator.SafeCopy(),
37 | Params: kg.Params,
38 | }
39 | }
40 |
41 | // GenCircuitBootstrapKey generates a key for Circuit Bootstrapping.
42 | func (kg *CircuitBootstrapKeyGenerator[T]) GenCircuitBootstrapKey() CircuitBootstrapKey[T] {
43 | schemeSwitchKey := make([]tfhe.FFTGGSWCiphertext[T], kg.Params.Params().GLWERank())
44 | for i := 0; i < kg.Params.Params().GLWERank(); i++ {
45 | schemeSwitchKey[i] = kg.Encryptor.EncryptFFTGGSWPoly(kg.Encryptor.SecretKey.GLWEKey.Value[i], kg.Params.schemeSwitchParameters)
46 | }
47 |
48 | return CircuitBootstrapKey[T]{
49 | SchemeSwitchKey: schemeSwitchKey,
50 | TraceKeys: kg.GenGaloisKeysForPack(kg.Params.traceKeySwitchParameters),
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tfhe/lwe_enc_public.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/math/vec"
6 | )
7 |
8 | // EncryptLWE encodes and encrypts integer message to LWE ciphertext.
9 | func (e *PublicEncryptor[T]) EncryptLWE(message int) LWECiphertext[T] {
10 | return e.EncryptLWEPlaintext(e.EncodeLWE(message))
11 | }
12 |
13 | // EncryptLWETo encodes and encrypts integer message to LWE ciphertext and writes it to ctOut.
14 | func (e *PublicEncryptor[T]) EncryptLWETo(ctOut LWECiphertext[T], message int) {
15 | e.EncryptLWEPlaintextTo(ctOut, e.EncodeLWE(message))
16 | }
17 |
18 | // EncryptLWEPlaintext encrypts LWE plaintext to LWE ciphertext.
19 | func (e *PublicEncryptor[T]) EncryptLWEPlaintext(pt LWEPlaintext[T]) LWECiphertext[T] {
20 | ctOut := NewLWECiphertext(e.Params)
21 | e.EncryptLWEPlaintextTo(ctOut, pt)
22 | return ctOut
23 | }
24 |
25 | // EncryptLWEPlaintextTo encrypts LWE plaintext to LWE ciphertext and writes it to ctOut.
26 | func (e *PublicEncryptor[T]) EncryptLWEPlaintextTo(ctOut LWECiphertext[T], pt LWEPlaintext[T]) {
27 | ctOut.Value[0] = pt.Value
28 | e.EncryptLWEBody(ctOut)
29 | }
30 |
31 | // EncryptLWEBody encrypts the value in the body of LWE ciphertext and overrides it.
32 | // This avoids the need for most buffers.
33 | func (e *PublicEncryptor[T]) EncryptLWEBody(ct LWECiphertext[T]) {
34 | for i := 0; i < e.Params.glweRank; i++ {
35 | e.BinarySampler.SamplePolyTo(e.buf.auxKey.Value[i])
36 | ct.Value[0] += vec.Dot(e.buf.auxKey.Value[i].Coeffs, e.PublicKey.LWEKey.Value[i].Value[0].Coeffs)
37 | }
38 | ct.Value[0] += e.GaussianSampler.Sample(e.Params.GLWEStdDevQ())
39 |
40 | for i := 0; i < e.Params.glweRank; i++ {
41 | vec.ReverseInPlace(e.buf.auxKey.Value[i].Coeffs)
42 | }
43 | e.FwdFFTGLWESecretKeyTo(e.buf.auxFourierKey, e.buf.auxKey)
44 |
45 | ctGLWE := make([]poly.Poly[T], e.Params.glweRank)
46 | for i := 0; i < e.Params.glweRank; i++ {
47 | ctGLWE[i] = poly.Poly[T]{Coeffs: ct.Value[1+i*e.Params.polyRank : 1+(i+1)*e.Params.polyRank]}
48 | }
49 |
50 | for i := 0; i < e.Params.glweRank; i++ {
51 | for j := 0; j < e.Params.glweRank; j++ {
52 | e.PolyEvaluator.ShortFFTPolyMulAddPolyTo(ctGLWE[j], e.PublicKey.LWEKey.Value[i].Value[j+1], e.buf.auxFourierKey.Value[i])
53 | }
54 | }
55 |
56 | for j := 0; j < e.Params.glweRank; j++ {
57 | e.GaussianSampler.SamplePolyAddTo(ctGLWE[j], e.Params.GLWEStdDevQ())
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/mktfhe/bootstrap_keygen.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/tfhe"
5 | )
6 |
7 | // GenEvalKey samples a new evaluation key for bootstrapping.
8 | //
9 | // This can take a long time.
10 | // Use [*Encryptor.GenEvalKeyParallel] for better key generation performance.
11 | func (e *Encryptor[T]) GenEvalKey() EvaluationKey[T] {
12 | return EvaluationKey[T]{
13 | EvaluationKey: e.SubEncryptor.GenEvalKey(),
14 | CRSPublicKey: e.GenCRSPublicKey(),
15 | RelinKey: e.GenRelinKey(),
16 | }
17 | }
18 |
19 | // GenEvalKeyParallel samples a new evaluation key for bootstrapping in parallel.
20 | func (e *Encryptor[T]) GenEvalKeyParallel() EvaluationKey[T] {
21 | return EvaluationKey[T]{
22 | EvaluationKey: e.SubEncryptor.GenEvalKeyParallel(),
23 | CRSPublicKey: e.GenCRSPublicKey(),
24 | RelinKey: e.GenRelinKey(),
25 | }
26 | }
27 |
28 | // GenDefaultKeySwitchKey samples a new keyswitch key LWELargeKey -> LWEKey,
29 | // used for bootstrapping.
30 | //
31 | // This can take a long time.
32 | // Use [*Encryptor.GenDefaultKeySwitchKeyParallel] for better key generation performance.
33 | func (e *Encryptor[T]) GenDefaultKeySwitchKey() tfhe.LWEKeySwitchKey[T] {
34 | return e.SubEncryptor.GenDefaultKeySwitchKey()
35 | }
36 |
37 | // GenDefaultKeySwitchKeyParallel samples a new keyswitch key LWELargeKey -> LWEKey in parallel,
38 | // used for bootstrapping.
39 | func (e *Encryptor[T]) GenDefaultKeySwitchKeyParallel() tfhe.LWEKeySwitchKey[T] {
40 | return e.SubEncryptor.GenDefaultKeySwitchKeyParallel()
41 | }
42 |
43 | // GenCRSPublicKey samples a new public key from the common reference string.
44 | func (e *Encryptor[T]) GenCRSPublicKey() tfhe.FFTGLevCiphertext[T] {
45 | pk := tfhe.NewFFTGLevCiphertext(e.Params.subParams, e.Params.relinKeyParams)
46 | for i := 0; i < e.Params.relinKeyParams.Level(); i++ {
47 | e.buf.ctSubGLWE.Value[1].CopyFrom(e.CRS[i])
48 |
49 | e.SubEncryptor.GaussianSampler.SamplePolyTo(e.buf.ctSubGLWE.Value[0], e.Params.GLWEStdDevQ())
50 | e.SubEncryptor.PolyEvaluator.ShortFFTPolyMulSubPolyTo(e.buf.ctSubGLWE.Value[0], e.buf.ctSubGLWE.Value[1], e.SecretKey.FFTGLWEKey.Value[0])
51 |
52 | e.SubEncryptor.FwdFFTGLWECiphertextTo(pk.Value[i], e.buf.ctSubGLWE)
53 | }
54 | return pk
55 | }
56 |
57 | // GenRelinKey samples a new relinearization key.
58 | func (e *Encryptor[T]) GenRelinKey() FFTUniEncryption[T] {
59 | return e.FFTUniEncryptPoly(e.SubEncryptor.SecretKey.GLWEKey.Value[0], e.Params.relinKeyParams)
60 | }
61 |
--------------------------------------------------------------------------------
/xtfhe/circuit_bootstrap_params_list.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | var (
6 | // ParamsCircuitBootstrapMedium is a default circuit bootstrapping parameter set for binary TFHE
7 | // with max depth 1194.
8 | ParamsCircuitBootstrapMedium = CircuitBootstrapParametersLiteral[uint64]{
9 | ManyLUTParams: ManyLUTParametersLiteral[uint64]{
10 | BaseParams: tfhe.ParametersLiteral[uint64]{
11 | LWEDimension: 571,
12 | GLWERank: 1,
13 | PolyRank: 2048,
14 |
15 | LWEStdDev: 0.00016996595057126976,
16 | GLWEStdDev: 0.0000000000000003472576015484159,
17 |
18 | MessageModulus: 1 << 1,
19 |
20 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
21 | Base: 1 << 15,
22 | Level: 2,
23 | },
24 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
25 | Base: 1 << 2,
26 | Level: 5,
27 | },
28 |
29 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
30 | },
31 |
32 | LUTCount: 4,
33 | },
34 |
35 | SchemeSwitchParams: tfhe.GadgetParametersLiteral[uint64]{
36 | Base: 1 << 17,
37 | Level: 2,
38 | },
39 | TraceKeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
40 | Base: 1 << 13,
41 | Level: 3,
42 | },
43 | OutputParams: tfhe.GadgetParametersLiteral[uint64]{
44 | Base: 1 << 4,
45 | Level: 4,
46 | },
47 | }
48 |
49 | // ParamsCircuitBootstrapLarge is a default circuit bootstrapping parameter set for binary TFHE
50 | // with max depth 13410.
51 | ParamsCircuitBootstrapLarge = CircuitBootstrapParametersLiteral[uint64]{
52 | ManyLUTParams: ManyLUTParametersLiteral[uint64]{
53 | BaseParams: tfhe.ParametersLiteral[uint64]{
54 | LWEDimension: 571,
55 | GLWERank: 1,
56 | PolyRank: 2048,
57 |
58 | LWEStdDev: 0.00016996595057126976,
59 | GLWEStdDev: 0.0000000000000003472576015484159,
60 |
61 | MessageModulus: 1 << 1,
62 |
63 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
64 | Base: 1 << 15,
65 | Level: 2,
66 | },
67 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
68 | Base: 1 << 2,
69 | Level: 5,
70 | },
71 |
72 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
73 | },
74 |
75 | LUTCount: 4,
76 | },
77 |
78 | SchemeSwitchParams: tfhe.GadgetParametersLiteral[uint64]{
79 | Base: 1 << 17,
80 | Level: 2,
81 | },
82 | TraceKeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
83 | Base: 1 << 8,
84 | Level: 6,
85 | },
86 | OutputParams: tfhe.GadgetParametersLiteral[uint64]{
87 | Base: 1 << 5,
88 | Level: 4,
89 | },
90 | }
91 | )
92 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security
2 | Theoretical security of TFHE is based on hardness of [LWE](https://en.wikipedia.org/wiki/Learning_with_errors) and [RLWE](https://en.wikipedia.org/wiki/Ring_learning_with_errors) problem, which are the foundations of lattice-based cryptography.
3 |
4 | ## Implementation-related Attacks
5 | TFHE-go has not been audited or reviewed by security experts. It is highly likely that it contains critical vulnerabilities to implementation-related attacks, such as side-channel attacks. Use at your own risk.
6 |
7 | ## Parameter Selection
8 | Single-Key parameters of TFHE-go ensure at least 128 bits of security, with bootstrapping failure rate less than 2-64. Multi-Key parameters are based on [[KMS22](https://eprint.iacr.org/2022/1460)], which ensure at least 110 bits of security. They were validated using latest commit([5ba00f5](https://github.com/malb/lattice-estimator/commit/5ba00f56dd1086c3a42b98fc596c64907adb96ff)) of [lattice-estimator](https://github.com/malb/lattice-estimator). Attack costs were estimated with two strategies, `primal_usvp` and `dual_hybrid`, with `red_cost_model = RC.BDGL16`.
9 |
10 | ### Block Binary Keys
11 |
12 | TFHE-go implements block binary distribution [[LMSS23](https://eprint.iacr.org/2023/958)] for sampling secret keys, which may lower the security level. Impacted parameters were carefully adjusted following the authors' security estimation to support 128 bit security. To use uniform binary secret keys like the original TFHE scheme, you can set `BlockSize` to 1.
13 |
14 | ### IND-CPAD Security
15 | Recently, [[CCP+24](https://eprint.iacr.org/2024/127)] proposed an attack against TFHE over IND-CPAD security model. This attack may be effective, often resulting in full key recovery, if bootstrapping failure proabability is high enough. TFHE-go only considers IND-CPA security, and assumes that decrypted plaintexts are not shared with any third parties. If you need such functionality, you must use parameters with lower bootstrapping failure rate.
16 |
17 | ## Distributed Decryption
18 | In multi-key FHE schemes, decrypting a ciphertext requires all parties to engage in a distributed decryption protocol, which allows parties to obtain decrypted messages without any information leak. However, in multi-key TFHE, this protocol is typically expensive, requring multi-party garbling [[Ben18](https://eprint.iacr.org/2017/1186)] or modified noise flooding [[DDK+23](https://eprint.iacr.org/2023/815)]. For simplicity, TFHE-go assumes the presence of a trusted third party (known as the *Decryptor*) who possesses the secret keys of all parties to decrypt ciphertexts.
19 |
--------------------------------------------------------------------------------
/mktfhe/binary_encryptor_public.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | // BinaryPublicEncryptor is a multi-key variant of [tfhe.BinaryPublicEncryptor].
6 | type BinaryPublicEncryptor[T tfhe.TorusInt] struct {
7 | // BinaryEncoder is an embedded encoder for this BinaryPublicEncryptor.
8 | *tfhe.BinaryEncoder[T]
9 | // Params is parameters for this BinaryPublicEncryptor.
10 | Params Parameters[T]
11 | // Encryptor is a generic PublicEncryptor for this BinaryPublicEncryptor.
12 | Encryptor *PublicEncryptor[T]
13 | }
14 |
15 | // NewBinaryPublicEncryptor creates a new BinaryPublicEncryptor.
16 | func NewBinaryPublicEncryptor[T tfhe.TorusInt](params Parameters[T], idx int, pk tfhe.PublicKey[T]) *BinaryPublicEncryptor[T] {
17 | return &BinaryPublicEncryptor[T]{
18 | BinaryEncoder: tfhe.NewBinaryEncoder(params.subParams),
19 | Params: params,
20 | Encryptor: NewPublicEncryptor(params, idx, pk),
21 | }
22 | }
23 |
24 | // SafeCopy returns a thread-safe copy.
25 | func (e *BinaryPublicEncryptor[T]) SafeCopy() *BinaryPublicEncryptor[T] {
26 | return &BinaryPublicEncryptor[T]{
27 | BinaryEncoder: e.BinaryEncoder,
28 | Params: e.Params,
29 | Encryptor: e.Encryptor.SafeCopy(),
30 | }
31 | }
32 |
33 | // EncryptLWEBool encrypts boolean message to LWE ciphertexts.
34 | // Like most languages, false == 0, and true == 1.
35 | //
36 | // Note that this is different from calling EncryptLWE with 0 or 1.
37 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBool(message bool) LWECiphertext[T] {
38 | return e.Encryptor.EncryptLWEPlaintext(e.EncodeLWEBool(message))
39 | }
40 |
41 | // EncryptLWEBoolTo encrypts boolean message to LWE ciphertexts.
42 | // Like most languages, false == 0, and true == 1.
43 | //
44 | // Note that this is different from calling EncryptLWE with 0 or 1.
45 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBoolTo(ctOut LWECiphertext[T], message bool) {
46 | e.Encryptor.EncryptLWEPlaintextTo(ctOut, e.EncodeLWEBool(message))
47 | }
48 |
49 | // EncryptLWEBits encrypts each bits of an integer message.
50 | // The order of the bits are little-endian.
51 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBits(message, bits int) []LWECiphertext[T] {
52 | ctOut := make([]LWECiphertext[T], bits)
53 | e.EncryptLWEBitsTo(ctOut, message)
54 | return ctOut
55 | }
56 |
57 | // EncryptLWEBitsTo encrypts each bits of an integer message.
58 | // The order of the bits are little-endian,
59 | // and will be cut by the length of ctOut.
60 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBitsTo(ctOut []LWECiphertext[T], message int) {
61 | for i := 0; i < len(ctOut); i++ {
62 | ctOut[i] = e.EncryptLWEBool(message&1 == 1)
63 | message >>= 1
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tfhe/binary_encryptor_public.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // BinaryPublicEncryptor encrypts binary TFHE ciphertexts with public key.
4 | // This is meant to be public, usually for servers.
5 | //
6 | // We use compact public key, explained in https://eprint.iacr.org/2023/603.
7 | // This means that not all parameters support public key encryption.
8 | //
9 | // BinaryEncryptor is not safe for concurrent use.
10 | // Use [*BinaryPublicEncryptor.SafeCopy] to get a safe copy.
11 | type BinaryPublicEncryptor[T TorusInt] struct {
12 | // BinaryEncoder is an embedded encoder for this BinaryPublicEncryptor.
13 | *BinaryEncoder[T]
14 | // Params is parameters for this BinaryPublicEncryptor.
15 | Params Parameters[T]
16 | // Encryptor is a generic PublicEncryptor for this BinaryPublicEncryptor.
17 | Encryptor *PublicEncryptor[T]
18 | }
19 |
20 | // NewBinaryPublicEncryptor creates a new BinaryPublicEncryptor.
21 | func NewBinaryPublicEncryptor[T TorusInt](params Parameters[T], pk PublicKey[T]) *BinaryPublicEncryptor[T] {
22 | return &BinaryPublicEncryptor[T]{
23 | BinaryEncoder: NewBinaryEncoder(params),
24 | Params: params,
25 | Encryptor: NewPublicEncryptor(params, pk),
26 | }
27 | }
28 |
29 | // SafeCopy returns a thread-safe copy.
30 | func (e *BinaryPublicEncryptor[T]) SafeCopy() *BinaryPublicEncryptor[T] {
31 | return &BinaryPublicEncryptor[T]{
32 | BinaryEncoder: e.BinaryEncoder,
33 | Params: e.Params,
34 | Encryptor: e.Encryptor.SafeCopy(),
35 | }
36 | }
37 |
38 | // EncryptLWEBool encrypts boolean message to LWE ciphertexts.
39 | //
40 | // Note that this is different from calling EncryptLWE with 0 or 1.
41 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBool(message bool) LWECiphertext[T] {
42 | return e.Encryptor.EncryptLWEPlaintext(e.EncodeLWEBool(message))
43 | }
44 |
45 | // EncryptLWEBoolTo encrypts boolean message to LWE ciphertexts.
46 | //
47 | // Note that this is different from calling EncryptLWE with 0 or 1.
48 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBoolTo(ctOut LWECiphertext[T], message bool) {
49 | e.Encryptor.EncryptLWEPlaintextTo(ctOut, e.EncodeLWEBool(message))
50 | }
51 |
52 | // EncryptLWEBits encrypts each bits of an integer message.
53 | // The order of the bits are little-endian.
54 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBits(message, bits int) []LWECiphertext[T] {
55 | ctOut := make([]LWECiphertext[T], bits)
56 | e.EncryptLWEBitsTo(ctOut, message)
57 | return ctOut
58 | }
59 |
60 | // EncryptLWEBitsTo encrypts each bits of an integer message.
61 | // The order of the bits are little-endian,
62 | // and will be cut by the length of ctOut.
63 | func (e *BinaryPublicEncryptor[T]) EncryptLWEBitsTo(ctOut []LWECiphertext[T], message int) {
64 | for i := 0; i < len(ctOut); i++ {
65 | ctOut[i] = e.EncryptLWEBool(message&1 == 1)
66 | message >>= 1
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/xtfhe/fhew_enc.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // FHEWEncryptor wraps around [tfhe.Encryptor] and implements FHEW encryption algorithm.
9 | type FHEWEncryptor[T tfhe.TorusInt] struct {
10 | // Encryptor is an embedded [tfhe.Encryptor] for this FHEWEncryptor.
11 | *tfhe.Encryptor[T]
12 |
13 | // Params is parameters for this FHEWEncryptor.
14 | Params FHEWParameters[T]
15 |
16 | buf fhewEncryptorBuffer[T]
17 | }
18 |
19 | // fhewEncryptorBuffer is a buffer for FHEWEncryptor.
20 | type fhewEncryptorBuffer[T tfhe.TorusInt] struct {
21 | // skPermute is a permuted secret key.
22 | skPermute tfhe.GLWESecretKey[T]
23 | // ctGLWE is a standard GLWE Ciphertext for Fourier encryption / decryptions.
24 | ctGLWE tfhe.GLWECiphertext[T]
25 | // ptGGSW is GLWEKey * Pt in GGSW encryption.
26 | ptGGSW poly.Poly[T]
27 | }
28 |
29 | // NewFHEWEncryptor creates a new FHEWEncryptor.
30 | func NewFHEWEncryptor[T tfhe.TorusInt](params FHEWParameters[T]) *FHEWEncryptor[T] {
31 | encryptor := FHEWEncryptor[T]{
32 | Encryptor: tfhe.NewEncryptorWithKey(params.BaseParams(), tfhe.SecretKey[T]{}),
33 | Params: params,
34 | buf: newFHEWEncryptorBuffer(params),
35 | }
36 | encryptor.Encryptor.SecretKey = encryptor.GenSecretKey()
37 |
38 | return &encryptor
39 | }
40 |
41 | // NewFHEWEncryptorWithKey creates a new FHEWEncryptor with given parameters and secret key.
42 | func NewFHEWEncryptorWithKey[T tfhe.TorusInt](params FHEWParameters[T], sk tfhe.SecretKey[T]) *FHEWEncryptor[T] {
43 | return &FHEWEncryptor[T]{
44 | Encryptor: tfhe.NewEncryptorWithKey(params.BaseParams(), sk),
45 | Params: params,
46 | buf: newFHEWEncryptorBuffer(params),
47 | }
48 | }
49 |
50 | // newFHEWEncryptorBuffer creates a new fhewEncryptorBuffer.
51 | func newFHEWEncryptorBuffer[T tfhe.TorusInt](params FHEWParameters[T]) fhewEncryptorBuffer[T] {
52 | return fhewEncryptorBuffer[T]{
53 | skPermute: tfhe.NewGLWESecretKey(params.BaseParams()),
54 | ctGLWE: tfhe.NewGLWECiphertext(params.BaseParams()),
55 | ptGGSW: poly.NewPoly[T](params.BaseParams().PolyRank()),
56 | }
57 | }
58 |
59 | // SafeCopy returns a thread-safe copy.
60 | func (e *FHEWEncryptor[T]) SafeCopy() *FHEWEncryptor[T] {
61 | return &FHEWEncryptor[T]{
62 | Encryptor: e.Encryptor.SafeCopy(),
63 | Params: e.Params,
64 | buf: newFHEWEncryptorBuffer(e.Params),
65 | }
66 | }
67 |
68 | // GenSecretKey samples a new SecretKey.
69 | // The SecretKey of the Encryptor is not changed.
70 | func (e *FHEWEncryptor[T]) GenSecretKey() tfhe.SecretKey[T] {
71 | sk := tfhe.NewSecretKey(e.Params.baseParams)
72 |
73 | e.GaussianSampler.SampleVecTo(sk.LWELargeKey.Value, e.Params.SecretKeyStdDevQ())
74 | e.FwdFFTGLWESecretKeyTo(sk.FFTGLWEKey, sk.GLWEKey)
75 |
76 | return sk
77 | }
78 |
--------------------------------------------------------------------------------
/tfhe/keyswitch.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // KeySwitchLWE switches key of ct.
4 | // Input ciphertext should be of length ksk.InputLWEDimension + 1.
5 | func (e *Evaluator[T]) KeySwitchLWE(ct LWECiphertext[T], ksk LWEKeySwitchKey[T]) LWECiphertext[T] {
6 | ctOut := NewLWECiphertext(e.Params)
7 | e.KeySwitchLWETo(ctOut, ct, ksk)
8 | return ctOut
9 | }
10 |
11 | // KeySwitchLWETo switches key of ct and writes it to ctOut.
12 | // Input ciphertext should be of length ksk.InputLWEDimension + 1.
13 | func (e *Evaluator[T]) KeySwitchLWETo(ctOut, ct LWECiphertext[T], ksk LWEKeySwitchKey[T]) {
14 | cDcmp := e.Decomposer.ScalarBuffer(ksk.GadgetParams)
15 |
16 | e.Decomposer.DecomposeScalarTo(cDcmp, ct.Value[1], ksk.GadgetParams)
17 | e.ScalarMulLWETo(e.buf.ctProdLWE, ksk.Value[0].Value[0], cDcmp[0])
18 | for j := 1; j < ksk.GadgetParams.level; j++ {
19 | e.ScalarMulAddLWETo(e.buf.ctProdLWE, ksk.Value[0].Value[j], cDcmp[j])
20 | }
21 |
22 | for i := 1; i < ksk.InputLWEDimension(); i++ {
23 | e.Decomposer.DecomposeScalarTo(cDcmp, ct.Value[i+1], ksk.GadgetParams)
24 | for j := 0; j < ksk.GadgetParams.level; j++ {
25 | e.ScalarMulAddLWETo(e.buf.ctProdLWE, ksk.Value[i].Value[j], cDcmp[j])
26 | }
27 | }
28 |
29 | ctOut.Value[0] = ct.Value[0] + e.buf.ctProdLWE.Value[0]
30 | copy(ctOut.Value[1:], e.buf.ctProdLWE.Value[1:])
31 | }
32 |
33 | // KeySwitchGLWE switches key of ct.
34 | // Input ciphertext should be of length ksk.InputGLWERank + 1.
35 | func (e *Evaluator[T]) KeySwitchGLWE(ct GLWECiphertext[T], ksk GLWEKeySwitchKey[T]) GLWECiphertext[T] {
36 | ctOut := NewGLWECiphertext(e.Params)
37 | e.KeySwitchGLWETo(ctOut, ct, ksk)
38 | return ctOut
39 | }
40 |
41 | // KeySwitchGLWETo switches key of ct and writes it to ctOut.
42 | // Input ciphertext should be of length ksk.InputGLWERank + 1.
43 | func (e *Evaluator[T]) KeySwitchGLWETo(ctOut, ct GLWECiphertext[T], ksk GLWEKeySwitchKey[T]) {
44 | fpDcmp := e.Decomposer.FFTPolyBuffer(ksk.GadgetParameters)
45 |
46 | e.Decomposer.FourierDecomposePolyTo(fpDcmp, ct.Value[1], ksk.GadgetParameters)
47 | e.FFTPolyMulFFTGLWETo(e.buf.ctFFTProdGLWE, ksk.Value[0].Value[0], fpDcmp[0])
48 | for j := 1; j < ksk.GadgetParameters.level; j++ {
49 | e.FFTPolyMulAddFFTGLWETo(e.buf.ctFFTProdGLWE, ksk.Value[0].Value[j], fpDcmp[j])
50 | }
51 |
52 | for i := 1; i < ksk.InputGLWERank(); i++ {
53 | e.Decomposer.FourierDecomposePolyTo(fpDcmp, ct.Value[i+1], ksk.GadgetParameters)
54 | for j := 0; j < ksk.GadgetParameters.level; j++ {
55 | e.FFTPolyMulAddFFTGLWETo(e.buf.ctFFTProdGLWE, ksk.Value[i].Value[j], fpDcmp[j])
56 | }
57 | }
58 |
59 | ctOut.Value[0].CopyFrom(ct.Value[0])
60 | e.PolyEvaluator.InvFFTAddToUnsafe(ctOut.Value[0], e.buf.ctFFTProdGLWE.Value[0])
61 | for i := 0; i < e.Params.glweRank; i++ {
62 | e.PolyEvaluator.InvFFTToUnsafe(ctOut.Value[i+1], e.buf.ctFFTProdGLWE.Value[i+1])
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/go,windows,macos,linux,visualstudiocode
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,windows,macos,linux,visualstudiocode
3 |
4 | ### Go ###
5 | # If you prefer the allow list template instead of the deny list, see community template:
6 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
7 | #
8 | # Binaries for programs and plugins
9 | *.exe
10 | *.exe~
11 | *.dll
12 | *.so
13 | *.dylib
14 |
15 | # Test binary, built with `go test -c`
16 | *.test
17 |
18 | # Output of the go coverage tool, specifically when used with LiteIDE
19 | *.out
20 |
21 | # Dependency directories (remove the comment below to include it)
22 | # vendor/
23 |
24 | # Go workspace file
25 | go.work
26 |
27 | ### Linux ###
28 | *~
29 |
30 | # temporary files which can be created if a process still has a handle open of a deleted file
31 | .fuse_hidden*
32 |
33 | # KDE directory preferences
34 | .directory
35 |
36 | # Linux trash folder which might appear on any partition or disk
37 | .Trash-*
38 |
39 | # .nfs files are created when an open file is removed but is still being accessed
40 | .nfs*
41 |
42 | ### macOS ###
43 | # General
44 | .DS_Store
45 | .AppleDouble
46 | .LSOverride
47 |
48 | # Icon must end with two \r
49 | Icon
50 |
51 |
52 | # Thumbnails
53 | ._*
54 |
55 | # Files that might appear in the root of a volume
56 | .DocumentRevisions-V100
57 | .fseventsd
58 | .Spotlight-V100
59 | .TemporaryItems
60 | .Trashes
61 | .VolumeIcon.icns
62 | .com.apple.timemachine.donotpresent
63 |
64 | # Directories potentially created on remote AFP share
65 | .AppleDB
66 | .AppleDesktop
67 | Network Trash Folder
68 | Temporary Items
69 | .apdisk
70 |
71 | ### macOS Patch ###
72 | # iCloud generated files
73 | *.icloud
74 |
75 | ### VisualStudioCode ###
76 | .vscode/*
77 | !.vscode/settings.json
78 | !.vscode/tasks.json
79 | !.vscode/launch.json
80 | !.vscode/extensions.json
81 | !.vscode/*.code-snippets
82 |
83 | # Local History for Visual Studio Code
84 | .history/
85 |
86 | # Built Visual Studio Code Extensions
87 | *.vsix
88 |
89 | ### VisualStudioCode Patch ###
90 | # Ignore all local history of files
91 | .history
92 | .ionide
93 |
94 | ### Windows ###
95 | # Windows thumbnail cache files
96 | Thumbs.db
97 | Thumbs.db:encryptable
98 | ehthumbs.db
99 | ehthumbs_vista.db
100 |
101 | # Dump file
102 | *.stackdump
103 |
104 | # Folder config file
105 | [Dd]esktop.ini
106 |
107 | # Recycle Bin used on file shares
108 | $RECYCLE.BIN/
109 |
110 | # Windows Installer files
111 | *.cab
112 | *.msi
113 | *.msix
114 | *.msm
115 | *.msp
116 |
117 | # Windows shortcuts
118 | *.lnk
119 |
120 | # End of https://www.toptal.com/developers/gitignore/api/go,windows,macos,linux,visualstudiocode
121 |
122 | scratchpad/
123 |
--------------------------------------------------------------------------------
/tfhe/lwe_ops.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/vec"
5 | )
6 |
7 | // AddLWE returns ct0 + ct1.
8 | func (e *Evaluator[T]) AddLWE(ct0, ct1 LWECiphertext[T]) LWECiphertext[T] {
9 | ctOut := NewLWECiphertext(e.Params)
10 | e.AddLWETo(ctOut, ct0, ct1)
11 | return ctOut
12 | }
13 |
14 | // AddLWETo computes ctOut = ct0 + ct1.
15 | func (e *Evaluator[T]) AddLWETo(ctOut, ct0, ct1 LWECiphertext[T]) {
16 | vec.AddTo(ctOut.Value, ct0.Value, ct1.Value)
17 | }
18 |
19 | // AddPlainLWE returns ct + pt.
20 | func (e *Evaluator[T]) AddPlainLWE(ct LWECiphertext[T], pt LWEPlaintext[T]) LWECiphertext[T] {
21 | ctOut := NewLWECiphertext(e.Params)
22 | e.AddPlainLWETo(ctOut, ct, pt)
23 | return ctOut
24 | }
25 |
26 | // AddPlainLWETo computes ctOut = ct + pt.
27 | func (e *Evaluator[T]) AddPlainLWETo(ctOut, ct LWECiphertext[T], pt LWEPlaintext[T]) {
28 | ctOut.CopyFrom(ct)
29 | ctOut.Value[0] += pt.Value
30 | }
31 |
32 | // SubLWE returns ct0 - ct1.
33 | func (e *Evaluator[T]) SubLWE(ct0, ct1 LWECiphertext[T]) LWECiphertext[T] {
34 | ctOut := NewLWECiphertext(e.Params)
35 | e.SubLWETo(ctOut, ct0, ct1)
36 | return ctOut
37 | }
38 |
39 | // SubLWETo computes ctOut = ct0 - ct1.
40 | func (e *Evaluator[T]) SubLWETo(ctOut, ct0, ct1 LWECiphertext[T]) {
41 | vec.SubTo(ctOut.Value, ct0.Value, ct1.Value)
42 | }
43 |
44 | // SubPlainLWE returns ct - pt.
45 | func (e *Evaluator[T]) SubPlainLWE(ct LWECiphertext[T], pt LWEPlaintext[T]) LWECiphertext[T] {
46 | ctOut := NewLWECiphertext(e.Params)
47 | e.SubPlainLWETo(ctOut, ct, pt)
48 | return ctOut
49 | }
50 |
51 | // SubPlainLWETo computes ctOut = ct0 - pt.
52 | func (e *Evaluator[T]) SubPlainLWETo(ctOut, ct0 LWECiphertext[T], pt LWEPlaintext[T]) {
53 | ctOut.CopyFrom(ct0)
54 | ctOut.Value[0] -= pt.Value
55 | }
56 |
57 | // NegLWE returns -ct.
58 | func (e *Evaluator[T]) NegLWE(ct LWECiphertext[T]) LWECiphertext[T] {
59 | ctOut := NewLWECiphertext(e.Params)
60 | e.NegLWETo(ctOut, ct)
61 | return ctOut
62 | }
63 |
64 | // NegLWETo computes ctOut = -ct.
65 | func (e *Evaluator[T]) NegLWETo(ctOut, ct LWECiphertext[T]) {
66 | vec.NegTo(ctOut.Value, ct.Value)
67 | }
68 |
69 | // ScalarMulLWE returns c * ct.
70 | func (e *Evaluator[T]) ScalarMulLWE(ct LWECiphertext[T], c T) LWECiphertext[T] {
71 | ctOut := NewLWECiphertext(e.Params)
72 | e.ScalarMulLWETo(ctOut, ct, c)
73 | return ctOut
74 | }
75 |
76 | // ScalarMulLWETo computes ctOut = c * ct0.
77 | func (e *Evaluator[T]) ScalarMulLWETo(ctOut, ct LWECiphertext[T], c T) {
78 | vec.ScalarMulTo(ctOut.Value, ct.Value, c)
79 | }
80 |
81 | // ScalarMulAddLWETo computes ctOut += c * ct0.
82 | func (e *Evaluator[T]) ScalarMulAddLWETo(ctOut, ct LWECiphertext[T], c T) {
83 | vec.ScalarMulAddTo(ctOut.Value, ct.Value, c)
84 | }
85 |
86 | // ScalarMulSubLWETo computes ctOut -= c * ct0.
87 | func (e *Evaluator[T]) ScalarMulSubLWETo(ctOut, ct LWECiphertext[T], c T) {
88 | vec.ScalarMulSubTo(ctOut.Value, ct.Value, c)
89 | }
90 |
--------------------------------------------------------------------------------
/math/poly/asm_fft_test.go:
--------------------------------------------------------------------------------
1 | package poly
2 |
3 | import (
4 | "math"
5 | "math/cmplx"
6 | "math/rand"
7 | "testing"
8 |
9 | "github.com/sp301415/tfhe-go/math/vec"
10 | )
11 |
12 | func fwdFFTInPlaceRef(coeffs, tw []complex128) {
13 | N := len(coeffs)
14 |
15 | t := N
16 | for m := 1; m <= N/2; m <<= 1 {
17 | t >>= 1
18 | for i := 0; i < m; i++ {
19 | j1 := i * t << 1
20 | j2 := j1 + t
21 | for j := j1; j < j2; j++ {
22 | U, V := coeffs[j], coeffs[j+t]*tw[i]
23 | coeffs[j], coeffs[j+t] = U+V, U-V
24 | }
25 | }
26 | }
27 | }
28 |
29 | func invFFTInPlaceRef(coeffs, twInv []complex128) {
30 | N := len(coeffs)
31 |
32 | t := 1
33 | for m := N / 2; m >= 1; m >>= 1 {
34 | for i := 0; i < m; i++ {
35 | j1 := i * t << 1
36 | j2 := j1 + t
37 | for j := j1; j < j2; j++ {
38 | U, V := coeffs[j], coeffs[j+t]
39 | coeffs[j], coeffs[j+t] = U+V, (U-V)*twInv[i]
40 | }
41 | }
42 | t <<= 1
43 | }
44 | }
45 |
46 | func TestFFTAssembly(t *testing.T) {
47 | r := rand.New(rand.NewSource(0))
48 |
49 | N := 64
50 | eps := 1e-10
51 |
52 | coeffs := make([]complex128, N)
53 | for i := 0; i < N; i++ {
54 | coeffs[i] = complex(r.Float64(), r.Float64())
55 | }
56 | coeffsAVX2 := vec.CmplxToFloat4(coeffs)
57 | coeffsAVX2Out := make([]complex128, N)
58 |
59 | twRef := make([]complex128, N/2)
60 | twInvRef := make([]complex128, N/2)
61 | for i := 0; i < N/2; i++ {
62 | e := -2 * math.Pi * float64(i) / float64(N)
63 | twRef[i] = cmplx.Exp(complex(0, e))
64 | twInvRef[i] = cmplx.Exp(-complex(0, e))
65 | }
66 | vec.BitReverseInPlace(twRef)
67 | vec.BitReverseInPlace(twInvRef)
68 |
69 | twist := make([]complex128, N)
70 | twistInv := make([]complex128, N)
71 | for i := 0; i < N; i++ {
72 | e := 2 * math.Pi * float64(i) / float64(4*N)
73 | twist[i] = cmplx.Exp(complex(0, e))
74 | twistInv[i] = cmplx.Exp(-complex(0, e)) / complex(float64(N), 0)
75 | }
76 |
77 | tw, twInv := genTwiddleFactors(N)
78 |
79 | t.Run("FwdFFT", func(t *testing.T) {
80 | vec.CmplxToFloat4To(coeffsAVX2, coeffs)
81 | fwdFFTInPlace(coeffsAVX2, tw)
82 | vec.Float4ToCmplxTo(coeffsAVX2Out, coeffsAVX2)
83 |
84 | vec.MulTo(coeffs, coeffs, twist)
85 | fwdFFTInPlaceRef(coeffs, twRef)
86 |
87 | for i := 0; i < N; i++ {
88 | if cmplx.Abs(coeffs[i]-coeffsAVX2Out[i]) > eps {
89 | t.Fatalf("FwdFFT: %v != %v", coeffs[i], coeffsAVX2Out[i])
90 | }
91 | }
92 | })
93 |
94 | t.Run("InvFFT", func(t *testing.T) {
95 | vec.CmplxToFloat4To(coeffsAVX2, coeffs)
96 | invFFTInPlace(coeffsAVX2, twInv)
97 | vec.Float4ToCmplxTo(coeffsAVX2Out, coeffsAVX2)
98 |
99 | invFFTInPlaceRef(coeffs, twInvRef)
100 | vec.MulTo(coeffs, coeffs, twistInv)
101 |
102 | for i := 0; i < N; i++ {
103 | if cmplx.Abs(coeffs[i]-coeffsAVX2Out[i]) > eps {
104 | t.Fatalf("InvFFT: %v != %v", coeffs[i], coeffsAVX2Out[i])
105 | }
106 | }
107 | })
108 | }
109 |
--------------------------------------------------------------------------------
/mktfhe/lwe_ops.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/vec"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // AddLWE returns ct0 + ct1.
9 | func (e *Evaluator[T]) AddLWE(ct0, ct1 LWECiphertext[T]) LWECiphertext[T] {
10 | ctOut := NewLWECiphertext(e.Params)
11 | e.AddLWETo(ctOut, ct0, ct1)
12 | return ctOut
13 | }
14 |
15 | // AddLWETo computes ctOut = ct0 + ct1.
16 | func (e *Evaluator[T]) AddLWETo(ctOut, ct0, ct1 LWECiphertext[T]) {
17 | vec.AddTo(ctOut.Value, ct0.Value, ct1.Value)
18 | }
19 |
20 | // AddPlainLWE returns ct0 + pt.
21 | func (e *Evaluator[T]) AddPlainLWE(ct0 LWECiphertext[T], pt tfhe.LWEPlaintext[T]) LWECiphertext[T] {
22 | ctOut := NewLWECiphertext(e.Params)
23 | e.AddPlainLWETo(ctOut, ct0, pt)
24 | return ctOut
25 | }
26 |
27 | // AddPlainLWETo computes ctOut = ct0 + pt.
28 | func (e *Evaluator[T]) AddPlainLWETo(ctOut LWECiphertext[T], ct0 LWECiphertext[T], pt tfhe.LWEPlaintext[T]) {
29 | ctOut.CopyFrom(ct0)
30 | ctOut.Value[0] += pt.Value
31 | }
32 |
33 | // SubLWE returns ct0 - ct1.
34 | func (e *Evaluator[T]) SubLWE(ct0, ct1 LWECiphertext[T]) LWECiphertext[T] {
35 | ctOut := NewLWECiphertext(e.Params)
36 | e.SubLWETo(ctOut, ct0, ct1)
37 | return ctOut
38 | }
39 |
40 | // SubLWETo computes ctOut = ct0 - ct1.
41 | func (e *Evaluator[T]) SubLWETo(ctOut, ct0, ct1 LWECiphertext[T]) {
42 | vec.SubTo(ctOut.Value, ct0.Value, ct1.Value)
43 | }
44 |
45 | // SubPlainLWE returns ct0 - pt.
46 | func (e *Evaluator[T]) SubPlainLWE(ct0 LWECiphertext[T], pt tfhe.LWEPlaintext[T]) LWECiphertext[T] {
47 | ctOut := NewLWECiphertext(e.Params)
48 | e.SubPlainLWETo(ctOut, ct0, pt)
49 | return ctOut
50 | }
51 |
52 | // SubPlainLWETo computes ctOut = ct0 - pt.
53 | func (e *Evaluator[T]) SubPlainLWETo(ctOut LWECiphertext[T], ct0 LWECiphertext[T], pt tfhe.LWEPlaintext[T]) {
54 | ctOut.CopyFrom(ct0)
55 | ctOut.Value[0] -= pt.Value
56 | }
57 |
58 | // NegLWE returns -ct0.
59 | func (e *Evaluator[T]) NegLWE(ct0 LWECiphertext[T]) LWECiphertext[T] {
60 | ctOut := NewLWECiphertext(e.Params)
61 | e.NegLWETo(ctOut, ct0)
62 | return ctOut
63 | }
64 |
65 | // NegLWETo computes ctOut = -ct0.
66 | func (e *Evaluator[T]) NegLWETo(ctOut, ct0 LWECiphertext[T]) {
67 | vec.NegTo(ctOut.Value, ct0.Value)
68 | }
69 |
70 | // ScalarMulLWE returns c * ct0.
71 | func (e *Evaluator[T]) ScalarMulLWE(ct0 LWECiphertext[T], c T) LWECiphertext[T] {
72 | ctOut := NewLWECiphertext(e.Params)
73 | e.ScalarMulLWETo(ctOut, ct0, c)
74 | return ctOut
75 | }
76 |
77 | // ScalarMulLWETo computes ctOut = c * ct0.
78 | func (e *Evaluator[T]) ScalarMulLWETo(ctOut LWECiphertext[T], ct0 LWECiphertext[T], c T) {
79 | vec.ScalarMulTo(ctOut.Value, ct0.Value, c)
80 | }
81 |
82 | // ScalarMulAddLWETo computes ctOut += c * ct0.
83 | func (e *Evaluator[T]) ScalarMulAddLWETo(ctOut LWECiphertext[T], ct0 LWECiphertext[T], c T) {
84 | vec.ScalarMulAddTo(ctOut.Value, ct0.Value, c)
85 | }
86 |
87 | // ScalarMulSubLWETo computes ctOut -= c * ct0.
88 | func (e *Evaluator[T]) ScalarMulSubLWETo(ctOut LWECiphertext[T], ct0 LWECiphertext[T], c T) {
89 | vec.ScalarMulSubTo(ctOut.Value, ct0.Value, c)
90 | }
91 |
--------------------------------------------------------------------------------
/xtfhe/fhew_params.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "math"
5 |
6 | "github.com/sp301415/tfhe-go/math/poly"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | )
9 |
10 | // FHEWParametersLiteral is a structure for FHEW Parameters.
11 | //
12 | // BlockSize must be 1, and PolyRank does not equal LUTSize.
13 | type FHEWParametersLiteral[T tfhe.TorusInt] struct {
14 | // BaseParams is a base parameters for this FHEWParametersLiteral.
15 | BaseParams tfhe.ParametersLiteral[T]
16 |
17 | // SecretKeyStdDev is the standard deviation of the secret key.
18 | // Must be smaller than [poly.ShortPolyBound] / 8Q.
19 | SecretKeyStdDev float64
20 |
21 | // WindowSize is the window size for the FHEW blind rotation.
22 | WindowSize int
23 | }
24 |
25 | // Compile transforms ParametersLiteral to read-only Parameters.
26 | // If there is any invalid parameter in the literal, it panics.
27 | // Default parameters are guaranteed to be compiled without panics.
28 | func (p FHEWParametersLiteral[T]) Compile() FHEWParameters[T] {
29 | baseParams := p.BaseParams.Compile()
30 |
31 | logQ := float64(baseParams.LogQ())
32 | logTailCut := math.Log2(GaussianTailCut)
33 | switch {
34 | case baseParams.BlockSize() != 1:
35 | panic("BlockSize not 1")
36 | case baseParams.PolyRank() != baseParams.LUTSize():
37 | panic("PolyRank does not equal LUTSize")
38 | case p.SecretKeyStdDev <= 0:
39 | panic("SecretKeyStdDev smaller than or equal to zero")
40 | case math.Log2(p.SecretKeyStdDev)+logQ+logTailCut > poly.ShortLogBound:
41 | panic("SecretKeyStdDev too large")
42 | case p.WindowSize <= 0:
43 | panic("WindowSize smaller than or equal to zero")
44 | }
45 |
46 | return FHEWParameters[T]{
47 | baseParams: baseParams,
48 |
49 | secretKeyStdDev: p.SecretKeyStdDev,
50 | windowSize: p.WindowSize,
51 | }
52 | }
53 |
54 | // FHEWParameters are read-only, compiled parameters for FHEW.
55 | type FHEWParameters[T tfhe.TorusInt] struct {
56 | // BaseParams is a base parameters for this FHEWParameters.
57 | baseParams tfhe.Parameters[T]
58 |
59 | // SecretKeyStdDev is the standard deviation of the secret key.
60 | // Must be smaller than [poly.ShortPolyBound] / 8Q.
61 | secretKeyStdDev float64
62 |
63 | // WindowSize is the window size for the FHEW blind rotation.
64 | windowSize int
65 | }
66 |
67 | // BaseParams returns the base parameters for this FHEWParameters.
68 | func (p FHEWParameters[T]) BaseParams() tfhe.Parameters[T] {
69 | return p.baseParams
70 | }
71 |
72 | // SecretKeyStdDev returns the standard deviation of the secret key.
73 | //
74 | // This is a normalized standard deviation.
75 | // For actual sampling, use [FHEWParameters.SecretKeyStdDevQ].
76 | func (p FHEWParameters[T]) SecretKeyStdDev() float64 {
77 | return p.secretKeyStdDev
78 | }
79 |
80 | // SecretKeyStdDevQ returns SecretKeyStdDev * Q
81 | func (p FHEWParameters[T]) SecretKeyStdDevQ() float64 {
82 | return p.secretKeyStdDev * math.Exp2(float64(p.baseParams.LogQ()))
83 | }
84 |
85 | // WindowSize returns the window size for the FHEW blind rotation.
86 | func (p FHEWParameters[T]) WindowSize() int {
87 | return p.windowSize
88 | }
89 |
--------------------------------------------------------------------------------
/internal/asmgen/asmgen.go:
--------------------------------------------------------------------------------
1 | //go:generate go run . -fold -out ../../math/poly/asm_fold_amd64.s -stubs ../../math/poly/asm_fold_stub_amd64.go -pkg=poly
2 | //go:generate go run . -fft -out ../../math/poly/asm_fft_amd64.s -stubs ../../math/poly/asm_fft_stub_amd64.go -pkg=poly
3 | //go:generate go run . -vec_cmplx -out ../../math/poly/asm_vec_cmplx_amd64.s -stubs ../../math/poly/asm_vec_cmplx_stub_amd64.go -pkg=poly
4 | //go:generate go run . -vec -out ../../math/vec/asm_vec_amd64.s -stubs ../../math/vec/asm_vec_stub_amd64.go -pkg=vec
5 | //go:generate go run . -decompose -out ../../tfhe/asm_decompose_amd64.s -stubs ../../tfhe/asm_decompose_stub_amd64.go -pkg=tfhe
6 | package main
7 |
8 | import (
9 | "flag"
10 |
11 | . "github.com/mmcloughlin/avo/build"
12 | "github.com/mmcloughlin/avo/buildtags"
13 | )
14 |
15 | type OpType int
16 |
17 | const (
18 | OpPure OpType = iota
19 | OpAdd
20 | OpSub
21 | )
22 |
23 | var (
24 | fold = flag.Bool("fold", false, "asm_fold_amd64.s")
25 | fft = flag.Bool("fft", false, "asm_fft_amd64.s")
26 | vecCmplx = flag.Bool("vec_cmplx", false, "asm_vec_cmplx_amd64.s")
27 |
28 | vec = flag.Bool("vec", false, "asm_vec_amd64.s")
29 |
30 | decompose = flag.Bool("decompose", false, "asm_decompose_amd64.s")
31 | )
32 |
33 | func main() {
34 | flag.Parse()
35 |
36 | Constraint(buildtags.Term("amd64"))
37 | Constraint(buildtags.Not("purego"))
38 |
39 | if *fold {
40 | FoldConstants()
41 |
42 | FoldPolyToUint32AVX2()
43 | foldPolyToUint64AVX2()
44 |
45 | FloatModQInPlaceAVX2()
46 |
47 | UnfoldPolyToUint32AVX2(OpPure)
48 | UnfoldPolyToUint32AVX2(OpAdd)
49 | UnfoldPolyToUint32AVX2(OpSub)
50 |
51 | UnfoldPolyToUint64AVX2(OpPure)
52 | UnfoldPolyToUint64AVX2(OpAdd)
53 | UnfoldPolyToUint64AVX2(OpSub)
54 |
55 | }
56 |
57 | if *fft {
58 | FwdFFTInPlaceAVX2()
59 | InvFFTInPlaceAVX2()
60 | }
61 |
62 | if *vecCmplx {
63 | AddSubCmplxToAVX2(OpAdd)
64 | AddSubCmplxToAVX2(OpSub)
65 |
66 | NegCmplxToAVX2()
67 |
68 | FloatMulCmplxToAVX2(OpPure)
69 | FloatMulCmplxToAVX2(OpAdd)
70 | FloatMulCmplxToAVX2(OpSub)
71 |
72 | CmplxMulCmplxToAVX2(OpPure)
73 | CmplxMulCmplxToAVX2(OpAdd)
74 | CmplxMulCmplxToAVX2(OpSub)
75 |
76 | MulCmplxToAVX2(OpPure)
77 | MulCmplxToAVX2(OpAdd)
78 | MulCmplxToAVX2(OpSub)
79 | }
80 |
81 | if *vec {
82 | VecConstants()
83 |
84 | AddSubToUint32AVX2(OpAdd)
85 | AddSubToUint32AVX2(OpSub)
86 |
87 | AddToUint64AVX2(OpAdd)
88 | AddToUint64AVX2(OpSub)
89 |
90 | ScalarMulToUint32AVX2(OpPure)
91 | ScalarMulToUint32AVX2(OpAdd)
92 | ScalarMulToUint32AVX2(OpSub)
93 |
94 | ScalarMulToUint64AVX2(OpPure)
95 | ScalarMulToUint64AVX2(OpAdd)
96 | ScalarMulToUint64AVX2(OpSub)
97 |
98 | MulToUint32AVX2(OpPure)
99 | MulToUint32AVX2(OpAdd)
100 | MulToUint32AVX2(OpSub)
101 |
102 | MulToUint64AVX2(OpPure)
103 | MulToUint64AVX2(OpAdd)
104 | MulToUint64AVX2(OpSub)
105 | }
106 |
107 | if *decompose {
108 | DecomposeConstants()
109 |
110 | DecomposePolyToUint32AVX2()
111 | DecomposePolyToUint64AVX2()
112 | }
113 |
114 | Generate()
115 | }
116 |
--------------------------------------------------------------------------------
/xtfhe/circuit_bootstrap_params.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/tfhe"
5 | )
6 |
7 | // CircuitBootstrapParametersLiteral is a structure for Circuit Bootstrapping Parameters.
8 | type CircuitBootstrapParametersLiteral[T tfhe.TorusInt] struct {
9 | // ManyLUTParams is a base ManyLUTParams for this CircuitBootstrapParametersLiteral.
10 | ManyLUTParams ManyLUTParametersLiteral[T]
11 |
12 | // SchemeSwitchParams is the gadget parameters for scheme switching.
13 | SchemeSwitchParams tfhe.GadgetParametersLiteral[T]
14 | // TraceKeySwitchParams is the gadget parameters for LWE to GLWE packing.
15 | TraceKeySwitchParams tfhe.GadgetParametersLiteral[T]
16 | // OutputParams is the gadget parameters for the output of circuit bootstrapping.
17 | OutputParams tfhe.GadgetParametersLiteral[T]
18 | }
19 |
20 | // Compile transforms ParametersLiteral to read-only Parameters.
21 | // If there is any invalid parameter in the literal, it panics.
22 | // Default parameters are guaranteed to be compiled without panics.
23 | func (p CircuitBootstrapParametersLiteral[T]) Compile() CircuitBootstrapParameters[T] {
24 | return CircuitBootstrapParameters[T]{
25 | manyLUTParameters: p.ManyLUTParams.Compile(),
26 |
27 | schemeSwitchParameters: p.SchemeSwitchParams.Compile(),
28 | traceKeySwitchParameters: p.TraceKeySwitchParams.Compile(),
29 | outputParameters: p.OutputParams.Compile(),
30 | }
31 | }
32 |
33 | // CircuitBootstrapParametersLiteral is a parameter set for Circuit Bootstrapping.
34 | type CircuitBootstrapParameters[T tfhe.TorusInt] struct {
35 | // manyLUTParameters is a base ManyLUTParameters for this CircuitBootstrapParameters.
36 | manyLUTParameters ManyLUTParameters[T]
37 |
38 | // schemeSwitchParameters is the gadget parameters for scheme switching.
39 | schemeSwitchParameters tfhe.GadgetParameters[T]
40 | // traceKeySwitchParameters is the gadget parameters for LWE to GLWE packing.
41 | traceKeySwitchParameters tfhe.GadgetParameters[T]
42 | // outputParameters is the gadget parameters for the output of circuit bootstrapping.
43 | outputParameters tfhe.GadgetParameters[T]
44 | }
45 |
46 | // ManyLUTParams returns the base ManyLUTParams for this CircuitBootstrapParameters.
47 | func (p CircuitBootstrapParameters[T]) ManyLUTParams() ManyLUTParameters[T] {
48 | return p.manyLUTParameters
49 | }
50 |
51 | // Params returns the base parameters for this ManyLUTParameters.
52 | func (p CircuitBootstrapParameters[T]) Params() tfhe.Parameters[T] {
53 | return p.manyLUTParameters.baseParams
54 | }
55 |
56 | // SchemeSwitchParams returns the gadget parameters for scheme switching.
57 | func (p CircuitBootstrapParameters[T]) SchemeSwitchParams() tfhe.GadgetParameters[T] {
58 | return p.schemeSwitchParameters
59 | }
60 |
61 | // TraceKeySwitchParams returns the gadget parameters for LWE to GLWE packing.
62 | func (p CircuitBootstrapParameters[T]) TraceKeySwitchParams() tfhe.GadgetParameters[T] {
63 | return p.traceKeySwitchParameters
64 | }
65 |
66 | // OutputParams returns the gadget parameters for the output of circuit bootstrapping.
67 | func (p CircuitBootstrapParameters[T]) OutputParams() tfhe.GadgetParameters[T] {
68 | return p.outputParameters
69 | }
70 |
--------------------------------------------------------------------------------
/xtfhe/sanitization_sampler.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "math"
5 | "sort"
6 |
7 | "github.com/sp301415/tfhe-go/math/csprng"
8 | "github.com/sp301415/tfhe-go/math/num"
9 | "github.com/sp301415/tfhe-go/math/poly"
10 | )
11 |
12 | const (
13 | // GaussianTailCut is the tail cut for the Gaussian sampler.
14 | // Essentially, for a Gaussian variable x,
15 | // center - tailCut * stdDev <= x <= center + tailCut * stdDev
16 | // with overwhelming probability.
17 | GaussianTailCut = 12
18 | )
19 |
20 | // CDTSampler samples from a discrete Gaussian distribution
21 | // with a fixed center and standard deviation.
22 | type CDTSampler[T num.Integer] struct {
23 | baseSampler *csprng.UniformSampler[uint64]
24 |
25 | center float64
26 | stdDev float64
27 |
28 | table []uint64
29 | offset int64
30 | }
31 |
32 | // NewCDTSampler creates a new CDTSampler.
33 | func NewCDTSampler[T num.Integer](center float64, stdDev float64) *CDTSampler[T] {
34 | cFloor := math.Floor(center)
35 | cFrac := center - cFloor
36 |
37 | tailLo := int64(math.Floor(center - GaussianTailCut*stdDev))
38 | tailHi := int64(math.Ceil(center + GaussianTailCut*stdDev))
39 | tableSize := tailHi - tailLo + 1
40 |
41 | table := make([]uint64, tableSize)
42 | cdf := 0.0
43 | for i, x := 0, tailLo; x <= tailHi; i, x = i+1, x+1 {
44 | xf := float64(x)
45 | cdf += math.Exp(-(xf-cFrac)*(xf-cFrac)/(2*stdDev*stdDev)) / (math.Sqrt(2*math.Pi) * stdDev)
46 | if cdf >= 1 {
47 | table[i] = math.MaxUint64
48 | } else {
49 | table[i] = uint64(cdf * math.MaxUint64)
50 | }
51 | }
52 |
53 | return &CDTSampler[T]{
54 | baseSampler: csprng.NewUniformSampler[uint64](),
55 |
56 | center: center,
57 | stdDev: stdDev,
58 |
59 | table: table,
60 | offset: tailLo + int64(cFloor),
61 | }
62 | }
63 |
64 | // Center returns the center of the Gaussian distribution.
65 | func (s *CDTSampler[T]) Center() float64 {
66 | return s.center
67 | }
68 |
69 | // StdDev returns the standard deviation of the Gaussian distribution.
70 | func (s *CDTSampler[T]) StdDev() float64 {
71 | return s.stdDev
72 | }
73 |
74 | // Sample samples from the discrete Gaussian distribution.
75 | func (s *CDTSampler[T]) Sample() T {
76 | r := s.baseSampler.Sample()
77 |
78 | x := sort.Search(len(s.table), func(i int) bool {
79 | return s.table[i] > r
80 | })
81 |
82 | return T(int64(x) + s.offset)
83 | }
84 |
85 | // SampleVecTo samples Gaussian values and writes it to vOut.
86 | func (s *CDTSampler[T]) SampleVecTo(vOut []T) {
87 | for i := range vOut {
88 | vOut[i] = s.Sample()
89 | }
90 | }
91 |
92 | // SamplePolyTo samples Gaussian values and writes it to pOut.
93 | func (s *CDTSampler[T]) SamplePolyTo(pOut poly.Poly[T]) {
94 | s.SampleVecTo(pOut.Coeffs)
95 | }
96 |
97 | // SamplePolyAddTo samples Gaussian values and adds to pOut.
98 | func (s *CDTSampler[T]) SamplePolyAddTo(pOut poly.Poly[T]) {
99 | for i := range pOut.Coeffs {
100 | pOut.Coeffs[i] += s.Sample()
101 | }
102 | }
103 |
104 | // SamplePolySubTo samples Gaussian values and subtracts from pOut.
105 | func (s *CDTSampler[T]) SamplePolySubTo(pOut poly.Poly[T]) {
106 | for i := range pOut.Coeffs {
107 | pOut.Coeffs[i] -= s.Sample()
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/math/csprng/uniform_sampler.go:
--------------------------------------------------------------------------------
1 | package csprng
2 |
3 | import (
4 | "crypto/aes"
5 | "crypto/cipher"
6 | "crypto/rand"
7 | "crypto/sha512"
8 |
9 | "github.com/sp301415/tfhe-go/math/num"
10 | "github.com/sp301415/tfhe-go/math/poly"
11 | )
12 |
13 | // bufSize is the default buffer size of UniformSampler.
14 | const bufSize = 8192
15 |
16 | // UniformSampler samples values from uniform distribution.
17 | // This uses AES-CTR as a underlying prng.
18 | type UniformSampler[T num.Integer] struct {
19 | prng cipher.Stream
20 |
21 | buf [bufSize]byte
22 | ptr int
23 |
24 | byteSizeT int
25 | maxT T
26 | }
27 |
28 | // NewUniformSampler creates a new UniformSampler.
29 | //
30 | // Panics when read from crypto/rand or AES initialization fails.
31 | func NewUniformSampler[T num.Integer]() *UniformSampler[T] {
32 | var seed [32]byte
33 | if _, err := rand.Read(seed[:]); err != nil {
34 | panic(err)
35 | }
36 |
37 | return NewUniformSamplerWithSeed[T](seed[:])
38 | }
39 |
40 | // NewUniformSamplerWithSeed creates a new UniformSampler, with user supplied seed.
41 | //
42 | // Panics when AES initialization fails.
43 | func NewUniformSamplerWithSeed[T num.Integer](seed []byte) *UniformSampler[T] {
44 | r := sha512.Sum384(seed)
45 |
46 | block, err := aes.NewCipher(r[:32])
47 | if err != nil {
48 | panic(err)
49 | }
50 |
51 | prng := cipher.NewCTR(block, r[32:])
52 |
53 | return &UniformSampler[T]{
54 | prng: prng,
55 |
56 | buf: [bufSize]byte{},
57 | ptr: bufSize,
58 |
59 | byteSizeT: num.ByteSizeT[T](),
60 | maxT: T(num.MaxT[T]()),
61 | }
62 | }
63 |
64 | // Sample uniformly samples a random integer of type T.
65 | func (s *UniformSampler[T]) Sample() T {
66 | if s.ptr == bufSize {
67 | s.prng.XORKeyStream(s.buf[:], s.buf[:])
68 | s.ptr = 0
69 | }
70 |
71 | var res uint64
72 | switch s.byteSizeT {
73 | case 1:
74 | res |= uint64(s.buf[s.ptr+0])
75 | case 2:
76 | res |= uint64(s.buf[s.ptr+0])
77 | res |= uint64(s.buf[s.ptr+1]) << 8
78 | case 4:
79 | res |= uint64(s.buf[s.ptr+0])
80 | res |= uint64(s.buf[s.ptr+1]) << 8
81 | res |= uint64(s.buf[s.ptr+2]) << 16
82 | res |= uint64(s.buf[s.ptr+3]) << 24
83 | case 8:
84 | res |= uint64(s.buf[s.ptr+0])
85 | res |= uint64(s.buf[s.ptr+1]) << 8
86 | res |= uint64(s.buf[s.ptr+2]) << 16
87 | res |= uint64(s.buf[s.ptr+3]) << 24
88 | res |= uint64(s.buf[s.ptr+4]) << 32
89 | res |= uint64(s.buf[s.ptr+5]) << 40
90 | res |= uint64(s.buf[s.ptr+6]) << 48
91 | res |= uint64(s.buf[s.ptr+7]) << 56
92 | }
93 | s.ptr += s.byteSizeT
94 |
95 | return T(res)
96 | }
97 |
98 | // SampleN uniformly samples a random integer of type T in [0, N).
99 | func (s *UniformSampler[T]) SampleN(N T) T {
100 | bound := s.maxT - (s.maxT % N)
101 | for {
102 | res := s.Sample()
103 | if 0 <= res && res < bound {
104 | return res % N
105 | }
106 | }
107 | }
108 |
109 | // SampleVecTo samples uniform values to vOut.
110 | func (s *UniformSampler[T]) SampleVecTo(vOut []T) {
111 | for i := range vOut {
112 | vOut[i] = s.Sample()
113 | }
114 | }
115 |
116 | // SamplePolyTo samples uniform values to p.
117 | func (s *UniformSampler[T]) SamplePolyTo(pOut poly.Poly[T]) {
118 | s.SampleVecTo(pOut.Coeffs)
119 | }
120 |
--------------------------------------------------------------------------------
/math/poly/poly_test.go:
--------------------------------------------------------------------------------
1 | package poly_test
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | "math/rand"
7 | "testing"
8 |
9 | "github.com/sp301415/tfhe-go/math/poly"
10 | )
11 |
12 | var (
13 | LogN = []int{9, 10, 11, 12, 13, 14, 15}
14 | )
15 |
16 | func BenchmarkOps(b *testing.B) {
17 | r := rand.New(rand.NewSource(0))
18 |
19 | for _, logN := range LogN {
20 | b.Run(fmt.Sprintf("LogN=%v", logN), func(b *testing.B) {
21 | N := 1 << logN
22 |
23 | pev := poly.NewEvaluator[uint64](N)
24 |
25 | p0 := pev.NewPoly()
26 | p1 := pev.NewPoly()
27 | pOut := pev.NewPoly()
28 |
29 | for i := 0; i < pev.Rank(); i++ {
30 | p0.Coeffs[i] = r.Uint64()
31 | p1.Coeffs[i] = r.Uint64()
32 | }
33 |
34 | fp := pev.FwdFFTPoly(p0)
35 |
36 | b.Run("Add", func(b *testing.B) {
37 | for i := 0; i < b.N; i++ {
38 | pev.AddPolyTo(pOut, p0, p1)
39 | }
40 | })
41 |
42 | b.Run("Sub", func(b *testing.B) {
43 | for i := 0; i < b.N; i++ {
44 | pev.SubPolyTo(pOut, p0, p1)
45 | }
46 | })
47 |
48 | b.Run("BinaryMul", func(b *testing.B) {
49 | for i := 0; i < b.N; i++ {
50 | pev.ShortFFTPolyMulPolyTo(pOut, p0, fp)
51 | }
52 | })
53 |
54 | b.Run("Mul", func(b *testing.B) {
55 | for i := 0; i < b.N; i++ {
56 | pev.MulPolyTo(pOut, p0, p1)
57 | }
58 | })
59 | })
60 | }
61 | }
62 |
63 | func BenchmarkFFTOps(b *testing.B) {
64 | r := rand.New(rand.NewSource(0))
65 |
66 | for _, logN := range LogN {
67 | b.Run(fmt.Sprintf("LogN=%v", logN), func(b *testing.B) {
68 | N := 1 << logN
69 |
70 | pev := poly.NewEvaluator[uint64](N)
71 |
72 | fp0 := pev.NewFFTPoly()
73 | fp1 := pev.NewFFTPoly()
74 | fpOut := pev.NewFFTPoly()
75 |
76 | for i := 0; i < pev.Rank(); i++ {
77 | fp0.Coeffs[i] = (2*r.Float64() - 1.0) * math.Exp(63)
78 | fp1.Coeffs[i] = (2*r.Float64() - 1.0) * math.Exp(63)
79 | }
80 |
81 | b.Run("Add", func(b *testing.B) {
82 | for i := 0; i < b.N; i++ {
83 | pev.AddFFTPolyTo(fpOut, fp0, fp1)
84 | }
85 | })
86 |
87 | b.Run("Sub", func(b *testing.B) {
88 | for i := 0; i < b.N; i++ {
89 | pev.SubFFTPolyTo(fpOut, fp0, fp1)
90 | }
91 | })
92 |
93 | b.Run("Mul", func(b *testing.B) {
94 | for i := 0; i < b.N; i++ {
95 | pev.MulFFTPolyTo(fpOut, fp0, fp1)
96 | }
97 | })
98 | })
99 | }
100 | }
101 |
102 | func BenchmarkFFT(b *testing.B) {
103 | r := rand.New(rand.NewSource(0))
104 |
105 | for _, logN := range LogN {
106 | b.Run(fmt.Sprintf("LogN=%v", logN), func(b *testing.B) {
107 | N := 1 << logN
108 |
109 | pev := poly.NewEvaluator[uint64](N)
110 |
111 | p := pev.NewPoly()
112 | fp := pev.NewFFTPoly()
113 |
114 | for i := 0; i < pev.Rank(); i++ {
115 | p.Coeffs[i] = r.Uint64()
116 | }
117 |
118 | b.Run("FwdFFT", func(b *testing.B) {
119 | for i := 0; i < b.N; i++ {
120 | pev.FwdFFTPolyTo(fp, p)
121 | }
122 | })
123 |
124 | x := N / 3
125 | b.Run("MonomialFwdFFT", func(b *testing.B) {
126 | for i := 0; i < b.N; i++ {
127 | pev.MonomialFwdFFTTo(fp, x)
128 | }
129 | })
130 |
131 | b.Run("InvFFT", func(b *testing.B) {
132 | for i := 0; i < b.N; i++ {
133 | pev.InvFFTToUnsafe(p, fp)
134 | }
135 | })
136 | })
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/xtfhe/manylut_params_list.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | var (
6 | // ParamsBinaryLUT2 is a default parameter set for binary TFHE with 2 LUTs.
7 | ParamsBinaryLUT2 = ManyLUTParametersLiteral[uint32]{
8 | BaseParams: tfhe.ParametersLiteral[uint32]{
9 | LWEDimension: 687,
10 | GLWERank: 1,
11 | PolyRank: 1024,
12 | LUTSize: 1024,
13 |
14 | LWEStdDev: 0.00002024849643226141,
15 | GLWEStdDev: 0.0000000444843247619562,
16 |
17 | BlockSize: 3,
18 |
19 | MessageModulus: 1 << 1,
20 |
21 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint32]{
22 | Base: 1 << 6,
23 | Level: 3,
24 | },
25 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint32]{
26 | Base: 1 << 3,
27 | Level: 4,
28 | },
29 |
30 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
31 | },
32 |
33 | LUTCount: 2,
34 | }
35 |
36 | // ParamsBinaryCompactLUT2 is a parameter set for binary TFHE
37 | // with OrderKeySwitchBlindRotate with 2 LUTs.
38 | ParamsBinaryCompactLUT2 = ManyLUTParametersLiteral[uint32]{
39 | BaseParams: tfhe.ParametersLiteral[uint32]{
40 | LWEDimension: 687,
41 | GLWERank: 1,
42 | PolyRank: 1024,
43 | LUTSize: 1024,
44 |
45 | LWEStdDev: 0.00002024849643226141,
46 | GLWEStdDev: 0.0000000444843247619562,
47 |
48 | BlockSize: 3,
49 |
50 | MessageModulus: 1 << 1,
51 |
52 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint32]{
53 | Base: 1 << 6,
54 | Level: 3,
55 | },
56 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint32]{
57 | Base: 1 << 4,
58 | Level: 3,
59 | },
60 |
61 | BootstrapOrder: tfhe.OrderKeySwitchBlindRotate,
62 | },
63 |
64 | LUTCount: 2,
65 | }
66 |
67 | // ParamsUint2LUT4 is a parameter set with 2 bits of message space with 4 LUTs.
68 | ParamsUint2LUT4 = ManyLUTParametersLiteral[uint64]{
69 | BaseParams: tfhe.ParametersLiteral[uint64]{
70 | LWEDimension: 804,
71 | GLWERank: 1,
72 | PolyRank: 2048,
73 | LUTSize: 2048,
74 |
75 | LWEStdDev: 0.000002433797421598353,
76 | GLWEStdDev: 0.0000000000000003472576015484159,
77 |
78 | BlockSize: 4,
79 |
80 | MessageModulus: 1 << 2,
81 |
82 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
83 | Base: 1 << 23,
84 | Level: 1,
85 | },
86 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
87 | Base: 1 << 6,
88 | Level: 2,
89 | },
90 |
91 | BootstrapOrder: tfhe.OrderKeySwitchBlindRotate,
92 | },
93 |
94 | LUTCount: 4,
95 | }
96 |
97 | // ParamsUint3LUT2 is a parameter set with 3 bits of message space with 2 LUTs.
98 | ParamsUint3LUT2 = ManyLUTParametersLiteral[uint64]{
99 | BaseParams: tfhe.ParametersLiteral[uint64]{
100 |
101 | LWEDimension: 804,
102 | GLWERank: 1,
103 | PolyRank: 2048,
104 | LUTSize: 2048,
105 |
106 | LWEStdDev: 0.000002433797421598353,
107 | GLWEStdDev: 0.0000000000000003472576015484159,
108 |
109 | BlockSize: 4,
110 |
111 | MessageModulus: 1 << 3,
112 |
113 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
114 | Base: 1 << 23,
115 | Level: 1,
116 | },
117 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
118 | Base: 1 << 6,
119 | Level: 2,
120 | },
121 |
122 | BootstrapOrder: tfhe.OrderKeySwitchBlindRotate,
123 | },
124 |
125 | LUTCount: 2,
126 | }
127 | )
128 |
--------------------------------------------------------------------------------
/math/vec/asm_vec_test.go:
--------------------------------------------------------------------------------
1 | package vec_test
2 |
3 | import (
4 | "math/rand"
5 | "testing"
6 |
7 | "github.com/sp301415/tfhe-go/math/vec"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestVec(t *testing.T) {
12 | r := rand.New(rand.NewSource(0))
13 |
14 | N := 687
15 |
16 | v0 := make([]uint32, N)
17 | v1 := make([]uint32, N)
18 | vOut := make([]uint32, N)
19 | vOutAVX := make([]uint32, N)
20 |
21 | for i := 0; i < N; i++ {
22 | v0[i] = r.Uint32()
23 | v1[i] = r.Uint32()
24 | }
25 |
26 | w0 := make([]uint64, N)
27 | w1 := make([]uint64, N)
28 | wOut := make([]uint64, N)
29 | wOutAVX := make([]uint64, N)
30 |
31 | for i := 0; i < N; i++ {
32 | w0[i] = r.Uint64()
33 | w1[i] = r.Uint64()
34 | }
35 |
36 | t.Run("AddTo", func(t *testing.T) {
37 | for i := 0; i < N; i++ {
38 | vOut[i] = v0[i] + v1[i]
39 | wOut[i] = w0[i] + w1[i]
40 | }
41 | vec.AddTo(vOutAVX, v0, v1)
42 | vec.AddTo(wOutAVX, w0, w1)
43 |
44 | assert.Equal(t, vOut, vOutAVX)
45 | assert.Equal(t, wOut, wOutAVX)
46 | })
47 |
48 | t.Run("SubTo", func(t *testing.T) {
49 | for i := 0; i < N; i++ {
50 | vOut[i] = v0[i] - v1[i]
51 | wOut[i] = w0[i] - w1[i]
52 | }
53 | vec.SubTo(vOutAVX, v0, v1)
54 | vec.SubTo(wOutAVX, w0, w1)
55 |
56 | assert.Equal(t, vOut, vOutAVX)
57 | assert.Equal(t, wOut, wOutAVX)
58 | })
59 |
60 | t.Run("ScalarMulTo", func(t *testing.T) {
61 | cv := vOut[0]
62 | cw := wOut[0]
63 | for i := 0; i < N; i++ {
64 | vOut[i] = cv * v0[i]
65 | wOut[i] = cw * w0[i]
66 | }
67 | vec.ScalarMulTo(vOutAVX, v0, cv)
68 | vec.ScalarMulTo(wOutAVX, w0, cw)
69 |
70 | assert.Equal(t, vOut, vOutAVX)
71 | assert.Equal(t, wOut, wOutAVX)
72 | })
73 |
74 | t.Run("ScalarMulAddTo", func(t *testing.T) {
75 | cv := vOut[0]
76 | cw := wOut[0]
77 | for i := 0; i < N; i++ {
78 | vOut[i] += cv * v0[i]
79 | wOut[i] += cw * w0[i]
80 | }
81 | vec.ScalarMulAddTo(vOutAVX, v0, cv)
82 | vec.ScalarMulAddTo(wOutAVX, w0, cw)
83 |
84 | assert.Equal(t, vOut, vOutAVX)
85 | assert.Equal(t, wOut, wOutAVX)
86 | })
87 |
88 | t.Run("ScalarMulSubTo", func(t *testing.T) {
89 | cv := vOut[0]
90 | cw := wOut[0]
91 | for i := 0; i < N; i++ {
92 | vOut[i] -= cv * v0[i]
93 | wOut[i] -= cw * w0[i]
94 | }
95 | vec.ScalarMulSubTo(vOutAVX, v0, cv)
96 | vec.ScalarMulSubTo(wOutAVX, w0, cw)
97 |
98 | assert.Equal(t, vOut, vOutAVX)
99 | assert.Equal(t, wOut, wOutAVX)
100 | })
101 |
102 | t.Run("MulTo", func(t *testing.T) {
103 | for i := 0; i < N; i++ {
104 | vOut[i] = v0[i] * v1[i]
105 | wOut[i] = w0[i] * w1[i]
106 | }
107 | vec.MulTo(vOutAVX, v0, v1)
108 | vec.MulTo(wOutAVX, w0, w1)
109 |
110 | assert.Equal(t, vOut, vOutAVX)
111 | assert.Equal(t, wOut, wOutAVX)
112 | })
113 |
114 | t.Run("MulAddTo", func(t *testing.T) {
115 | for i := 0; i < N; i++ {
116 | vOut[i] += v0[i] * v1[i]
117 | wOut[i] += w0[i] * w1[i]
118 | }
119 | vec.MulAddTo(vOutAVX, v0, v1)
120 | vec.MulAddTo(wOutAVX, w0, w1)
121 |
122 | assert.Equal(t, vOut, vOutAVX)
123 | assert.Equal(t, wOut, wOutAVX)
124 | })
125 |
126 | t.Run("MulSubTo", func(t *testing.T) {
127 | for i := 0; i < N; i++ {
128 | vOut[i] -= v0[i] * v1[i]
129 | wOut[i] -= w0[i] * w1[i]
130 | }
131 | vec.MulSubTo(vOutAVX, v0, v1)
132 | vec.MulSubTo(wOutAVX, w0, w1)
133 |
134 | assert.Equal(t, vOut, vOutAVX)
135 | assert.Equal(t, wOut, wOutAVX)
136 | })
137 | }
138 |
--------------------------------------------------------------------------------
/xtfhe/manylut_evaluator.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // ManyLUTEvaluator wraps around [tfhe.Evaluator], and implements PBSManyLUT algorithm.
9 | // For more details, see https://eprint.iacr.org/2021/729.
10 | //
11 | // ManyLUTEvaluator is not safe for concurrent use.
12 | // Use [*ManyLUTEvaluator.SafeCopy] to get a safe copy.
13 | type ManyLUTEvaluator[T tfhe.TorusInt] struct {
14 | // Evaluator is an embedded Evaluator for this ManyLUTEvaluator.
15 | *tfhe.Evaluator[T]
16 |
17 | // Params is parameters for this ManyLUTEvaluator.
18 | Params ManyLUTParameters[T]
19 |
20 | buf manyLUTEvaluatorBuffer[T]
21 | }
22 |
23 | // manyLUTEvaluatorBuffer is a buffer for ManyLUTEvaluator.
24 | type manyLUTEvaluatorBuffer[T tfhe.TorusInt] struct {
25 | // ctFFTAcc is a fourier transformed accumulator in Blind Rotation.
26 | ctFFTAcc tfhe.FFTGLWECiphertext[T]
27 | // ctFFTBlockAcc is an auxiliary accumulator in BlindRotateBlock.
28 | ctFFTBlockAcc tfhe.FFTGLWECiphertext[T]
29 | // ctFFTAccDcmp is a decomposed ctAcc in Blind Rotation.
30 | ctFFTAccDcmp [][]poly.FFTPoly
31 | // fMono is a fourier transformed monomial in Blind Rotation.
32 | fMono poly.FFTPoly
33 |
34 | // ctRotate is a blind rotated GLWE ciphertext for bootstrapping.
35 | ctRotate tfhe.GLWECiphertext[T]
36 | // ctExtract is the extracted LWE ciphertext after Blind Rotation.
37 | ctExtract tfhe.LWECiphertext[T]
38 | // ctKeySwitch is the LWEDimension sized ciphertext from keyswitching for bootstrapping.
39 | ctKeySwitch tfhe.LWECiphertext[T]
40 |
41 | // lut is an empty lut, used for BlindRotateFunc.
42 | lut tfhe.LookUpTable[T]
43 | }
44 |
45 | // NewManyLUTEvaluator creates a new ManyLUTEvaluator.
46 | func NewManyLUTEvaluator[T tfhe.TorusInt](params ManyLUTParameters[T], evk tfhe.EvaluationKey[T]) *ManyLUTEvaluator[T] {
47 | return &ManyLUTEvaluator[T]{
48 | Evaluator: tfhe.NewEvaluator(params.baseParams, evk),
49 |
50 | Params: params,
51 |
52 | buf: newManyLUTEvaluatorBuffer(params),
53 | }
54 | }
55 |
56 | // newManyLUTEvaluatorBuffer creates a new manyLUTEvaluatorBuffer.
57 | func newManyLUTEvaluatorBuffer[T tfhe.TorusInt](params ManyLUTParameters[T]) manyLUTEvaluatorBuffer[T] {
58 | ctFFTAccDcmp := make([][]poly.FFTPoly, params.baseParams.GLWERank()+1)
59 | for i := 0; i < params.baseParams.GLWERank()+1; i++ {
60 | ctFFTAccDcmp[i] = make([]poly.FFTPoly, params.baseParams.BlindRotateParams().Level())
61 | for j := 0; j < params.baseParams.BlindRotateParams().Level(); j++ {
62 | ctFFTAccDcmp[i][j] = poly.NewFFTPoly(params.baseParams.PolyRank())
63 | }
64 | }
65 |
66 | return manyLUTEvaluatorBuffer[T]{
67 | ctFFTAcc: tfhe.NewFFTGLWECiphertext(params.baseParams),
68 | ctFFTBlockAcc: tfhe.NewFFTGLWECiphertext(params.baseParams),
69 | ctFFTAccDcmp: ctFFTAccDcmp,
70 | fMono: poly.NewFFTPoly(params.baseParams.PolyRank()),
71 |
72 | ctRotate: tfhe.NewGLWECiphertext(params.baseParams),
73 | ctExtract: tfhe.NewLWECiphertextCustom[T](params.baseParams.GLWEDimension()),
74 | ctKeySwitch: tfhe.NewLWECiphertextCustom[T](params.baseParams.LWEDimension()),
75 |
76 | lut: tfhe.NewLUT(params.baseParams),
77 | }
78 | }
79 |
80 | // SafeCopy returns a thread-safe copy.
81 | func (e *ManyLUTEvaluator[T]) SafeCopy() *ManyLUTEvaluator[T] {
82 | return &ManyLUTEvaluator[T]{
83 | Evaluator: e.Evaluator.SafeCopy(),
84 | Params: e.Params,
85 | buf: newManyLUTEvaluatorBuffer(e.Params),
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tfhe/asm_decompose_amd64.s:
--------------------------------------------------------------------------------
1 | // Code generated by command: go run asmgen.go -decompose -out ../../tfhe/asm_decompose_amd64.s -stubs ../../tfhe/asm_decompose_stub_amd64.go -pkg=tfhe. DO NOT EDIT.
2 |
3 | //go:build amd64 && !purego
4 |
5 | #include "textflag.h"
6 |
7 | DATA ONE<>+0(SB)/8, $0x0000000000000001
8 | GLOBL ONE<>(SB), RODATA|NOPTR, $8
9 |
10 | // func decomposePolyToUint32AVX2(dcmpOut [][]uint32, p []uint32, base uint32, logBase uint32, logLastBaseQ uint32)
11 | // Requires: AVX, AVX2
12 | TEXT ·decomposePolyToUint32AVX2(SB), NOSPLIT, $0-60
13 | MOVQ dcmpOut_base+0(FP), AX
14 | MOVQ p_base+24(FP), CX
15 | MOVQ p_len+32(FP), DX
16 | MOVQ dcmpOut_len+8(FP), BX
17 | VPBROADCASTD base+48(FP), Y0
18 | VPBROADCASTD logBase+52(FP), Y1
19 | VPBROADCASTD logLastBaseQ+56(FP), Y2
20 | VPBROADCASTD ONE<>+0(SB), Y3
21 | VPSUBD Y3, Y0, Y4
22 | VPSRLD $0x01, Y0, Y0
23 | VPSUBD Y3, Y1, Y5
24 | XORQ SI, SI
25 | JMP N_loop_end
26 |
27 | N_loop_body:
28 | VMOVDQU (CX)(SI*4), Y6
29 | VPSRLVD Y2, Y6, Y7
30 | VPSLLD $0x01, Y6, Y6
31 | VPSRLVD Y2, Y6, Y6
32 | VANDPD Y3, Y6, Y6
33 | VPADDD Y6, Y7, Y6
34 | MOVQ BX, DI
35 | SUBQ $0x01, DI
36 | MOVQ DI, R8
37 | ADDQ DI, R8
38 | ADDQ DI, R8
39 | JMP level_loop_end
40 |
41 | level_loop_body:
42 | VANDPD Y4, Y6, Y7
43 | VPSRLVD Y1, Y6, Y6
44 | VPSRLVD Y5, Y7, Y8
45 | VPADDD Y8, Y6, Y6
46 | VANDPD Y0, Y7, Y8
47 | VPSLLD $0x01, Y8, Y8
48 | VPSUBD Y8, Y7, Y7
49 | MOVQ (AX)(R8*8), R9
50 | VMOVDQU Y7, (R9)(SI*4)
51 | SUBQ $0x01, DI
52 | SUBQ $0x03, R8
53 |
54 | level_loop_end:
55 | CMPQ DI, $0x01
56 | JGE level_loop_body
57 | VANDPD Y4, Y6, Y6
58 | VANDPD Y0, Y6, Y7
59 | VPSLLD $0x01, Y7, Y7
60 | VPSUBD Y7, Y6, Y6
61 | MOVQ (AX), DI
62 | VMOVDQU Y6, (DI)(SI*4)
63 | ADDQ $0x08, SI
64 |
65 | N_loop_end:
66 | CMPQ SI, DX
67 | JL N_loop_body
68 | RET
69 |
70 | // func decomposePolyToUint64AVX2(dcmpOut [][]uint64, p []uint64, base uint64, logBase uint64, logLastBaseQ uint64)
71 | // Requires: AVX, AVX2
72 | TEXT ·decomposePolyToUint64AVX2(SB), NOSPLIT, $0-72
73 | MOVQ dcmpOut_base+0(FP), AX
74 | MOVQ p_base+24(FP), CX
75 | MOVQ p_len+32(FP), DX
76 | MOVQ dcmpOut_len+8(FP), BX
77 | VPBROADCASTQ base+48(FP), Y0
78 | VPBROADCASTQ logBase+56(FP), Y1
79 | VPBROADCASTQ logLastBaseQ+64(FP), Y2
80 | VPBROADCASTQ ONE<>+0(SB), Y3
81 | VPSUBQ Y3, Y0, Y4
82 | VPSRLQ $0x01, Y0, Y0
83 | VPSUBQ Y3, Y1, Y5
84 | XORQ SI, SI
85 | JMP N_loop_end
86 |
87 | N_loop_body:
88 | VMOVDQU (CX)(SI*8), Y6
89 | VPSRLVQ Y2, Y6, Y7
90 | VPSLLQ $0x01, Y6, Y6
91 | VPSRLVQ Y2, Y6, Y6
92 | VANDPD Y3, Y6, Y6
93 | VPADDQ Y6, Y7, Y6
94 | MOVQ BX, DI
95 | SUBQ $0x01, DI
96 | MOVQ DI, R8
97 | ADDQ DI, R8
98 | ADDQ DI, R8
99 | JMP level_loop_end
100 |
101 | level_loop_body:
102 | VANDPD Y4, Y6, Y7
103 | VPSRLVQ Y1, Y6, Y6
104 | VPSRLVQ Y5, Y7, Y8
105 | VPADDQ Y8, Y6, Y6
106 | VANDPD Y0, Y7, Y8
107 | VPSLLQ $0x01, Y8, Y8
108 | VPSUBQ Y8, Y7, Y7
109 | MOVQ (AX)(R8*8), R9
110 | VMOVDQU Y7, (R9)(SI*8)
111 | SUBQ $0x01, DI
112 | SUBQ $0x03, R8
113 |
114 | level_loop_end:
115 | CMPQ DI, $0x01
116 | JGE level_loop_body
117 | VANDPD Y4, Y6, Y6
118 | VANDPD Y0, Y6, Y7
119 | VPSLLQ $0x01, Y7, Y7
120 | VPSUBQ Y7, Y6, Y6
121 | MOVQ (AX), DI
122 | VMOVDQU Y6, (DI)(SI*8)
123 | ADDQ $0x04, SI
124 |
125 | N_loop_end:
126 | CMPQ SI, DX
127 | JL N_loop_body
128 | RET
129 |
--------------------------------------------------------------------------------
/tfhe/bootstrap_key.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // EvaluationKey is a public key for Evaluator,
4 | // which consists of BlindRotation Key and KeySwitching Key.
5 | // All keys should be treated as read-only.
6 | // Changing them mid-operation will usually result in wrong results.
7 | type EvaluationKey[T TorusInt] struct {
8 | // BlindRotateKey is a blindrotate key.
9 | BlindRotateKey BlindRotateKey[T]
10 | // KeySwitchKey is a keyswitch key switching LWELargeKey -> LWEKey.
11 | KeySwitchKey LWEKeySwitchKey[T]
12 | }
13 |
14 | // NewEvaluationKey creates a new EvaluationKey.
15 | func NewEvaluationKey[T TorusInt](params Parameters[T]) EvaluationKey[T] {
16 | return EvaluationKey[T]{
17 | BlindRotateKey: NewBlindRotateKey(params),
18 | KeySwitchKey: NewKeySwitchKeyForBootstrap(params),
19 | }
20 | }
21 |
22 | // NewEvaluationKeyCustom creates a new EvaluationKey with custom parameters.
23 | func NewEvaluationKeyCustom[T TorusInt](lweDimension, glweRank, polyRank int, blindRotateParams, keySwitchParams GadgetParameters[T]) EvaluationKey[T] {
24 | return EvaluationKey[T]{
25 | BlindRotateKey: NewBlindRotateKeyCustom(lweDimension, glweRank, polyRank, blindRotateParams),
26 | KeySwitchKey: NewKeySwitchKeyForBootstrapCustom(lweDimension, glweRank, polyRank, keySwitchParams),
27 | }
28 | }
29 |
30 | // Copy returns a copy of the key.
31 | func (evk EvaluationKey[T]) Copy() EvaluationKey[T] {
32 | return EvaluationKey[T]{
33 | BlindRotateKey: evk.BlindRotateKey.Copy(),
34 | KeySwitchKey: evk.KeySwitchKey.Copy(),
35 | }
36 | }
37 |
38 | // CopyFrom copies values from key.
39 | func (evk *EvaluationKey[T]) CopyFrom(evkIn EvaluationKey[T]) {
40 | evk.BlindRotateKey.CopyFrom(evkIn.BlindRotateKey)
41 | evk.KeySwitchKey.CopyFrom(evkIn.KeySwitchKey)
42 | }
43 |
44 | // Clear clears the key.
45 | func (evk *EvaluationKey[T]) Clear() {
46 | evk.BlindRotateKey.Clear()
47 | evk.KeySwitchKey.Clear()
48 | }
49 |
50 | // BlindRotateKey is a key for blind rotation.
51 | // Essentially, this is a GGSW encryption of LWEKey with GLWEKey.
52 | // However, FFT is already applied for fast external product.
53 | type BlindRotateKey[T TorusInt] struct {
54 | GadgetParameters GadgetParameters[T]
55 |
56 | // Value has length LWEDimension.
57 | Value []FFTGGSWCiphertext[T]
58 | }
59 |
60 | // NewBlindRotateKey creates a new BlindRotateKey.
61 | func NewBlindRotateKey[T TorusInt](params Parameters[T]) BlindRotateKey[T] {
62 | brk := make([]FFTGGSWCiphertext[T], params.lweDimension)
63 | for i := 0; i < params.lweDimension; i++ {
64 | brk[i] = NewFFTGGSWCiphertext(params, params.blindRotateParams)
65 | }
66 | return BlindRotateKey[T]{Value: brk, GadgetParameters: params.blindRotateParams}
67 | }
68 |
69 | // NewBlindRotateKeyCustom creates a new BlindRotateKey with custom parameters.
70 | func NewBlindRotateKeyCustom[T TorusInt](lweDimension, glweRank, polyRank int, gadgetParams GadgetParameters[T]) BlindRotateKey[T] {
71 | brk := make([]FFTGGSWCiphertext[T], lweDimension)
72 | for i := 0; i < lweDimension; i++ {
73 | brk[i] = NewFFTGGSWCiphertextCustom(glweRank, polyRank, gadgetParams)
74 | }
75 | return BlindRotateKey[T]{Value: brk, GadgetParameters: gadgetParams}
76 | }
77 |
78 | // Copy returns a copy of the key.
79 | func (brk BlindRotateKey[T]) Copy() BlindRotateKey[T] {
80 | brkCopy := make([]FFTGGSWCiphertext[T], len(brk.Value))
81 | for i := range brk.Value {
82 | brkCopy[i] = brk.Value[i].Copy()
83 | }
84 | return BlindRotateKey[T]{Value: brkCopy, GadgetParameters: brk.GadgetParameters}
85 | }
86 |
87 | // CopyFrom copies values from key.
88 | func (brk *BlindRotateKey[T]) CopyFrom(brkIn BlindRotateKey[T]) {
89 | for i := range brk.Value {
90 | brk.Value[i].CopyFrom(brkIn.Value[i])
91 | }
92 | brk.GadgetParameters = brkIn.GadgetParameters
93 | }
94 |
95 | // Clear clears the key.
96 | func (brk *BlindRotateKey[T]) Clear() {
97 | for i := range brk.Value {
98 | brk.Value[i].Clear()
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/tfhe/encryptor_public.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/csprng"
5 | "github.com/sp301415/tfhe-go/math/poly"
6 | )
7 |
8 | // PublicEncryptor encrypts TFHE ciphertexts with public key.
9 | // This is meant to be public, usually for servers.
10 | //
11 | // We use compact public key, explained in https://eprint.iacr.org/2023/603.
12 | // This means that not all parameters support public key encryption.
13 | //
14 | // PublicEncryptor is not safe for concurrent use.
15 | // Use [*PublicEncryptor.SafeCopy] to get a safe copy.
16 | type PublicEncryptor[T TorusInt] struct {
17 | // Encoder is an embedded encoder for this PublicEncryptor.
18 | *Encoder[T]
19 | // GLWETransformer is an embedded GLWETransformer for this PublicEncryptor.
20 | *GLWETransformer[T]
21 |
22 | // Params is a parameters for this PublicEncryptor.
23 | Params Parameters[T]
24 |
25 | // UniformSampler is used for sampling the mask of encryptions.
26 | UniformSampler *csprng.UniformSampler[T]
27 | // BinarySampler is used for sampling the auxiliary "key" in encryptions.
28 | BinarySampler *csprng.BinarySampler[T]
29 | // GaussianSampler is used for sampling noise in LWE and GLWE encryption.
30 | GaussianSampler *csprng.GaussianSampler[T]
31 |
32 | // PolyEvaluator is a PolyEvaluator for this PublicEncryptor.
33 | PolyEvaluator *poly.Evaluator[T]
34 |
35 | // PublicKey is a public key for this PublicEncryptor.
36 | PublicKey PublicKey[T]
37 |
38 | buf publicEncryptorBuffer[T]
39 | }
40 |
41 | // publicEncryptorBuffer is a buffer for PublicEncryptor.
42 | type publicEncryptorBuffer[T TorusInt] struct {
43 | // ptGLWE is a GLWE plaintext for GLWE encryption / decryptions.
44 | ptGLWE GLWEPlaintext[T]
45 | // ctGLWE is a standard GLWE Ciphertext for Fourier encryption / decryptions.
46 | ctGLWE GLWECiphertext[T]
47 |
48 | // auxKey is an auxiliary key for encryption.
49 | // This must be sampled fresh for each encryption.
50 | auxKey GLWESecretKey[T]
51 | // auxFourierKey is a fourier transform of auxKey.
52 | auxFourierKey FFTGLWESecretKey[T]
53 | }
54 |
55 | // NewPublicEncryptor creates a new PublicEncryptor.
56 | //
57 | // Panics when the parameters do not support public key encryption.
58 | func NewPublicEncryptor[T TorusInt](params Parameters[T], pk PublicKey[T]) *PublicEncryptor[T] {
59 | if !params.IsPublicKeyEncryptable() {
60 | panic("Parameters do not support public key encryption")
61 | }
62 |
63 | return &PublicEncryptor[T]{
64 | Encoder: NewEncoder(params),
65 | GLWETransformer: NewGLWETransformer[T](params.polyRank),
66 |
67 | Params: params,
68 |
69 | UniformSampler: csprng.NewUniformSampler[T](),
70 | BinarySampler: csprng.NewBinarySampler[T](),
71 | GaussianSampler: csprng.NewGaussianSampler[T](),
72 |
73 | PolyEvaluator: poly.NewEvaluator[T](params.polyRank),
74 |
75 | PublicKey: pk,
76 |
77 | buf: newPublicEncryptorBuffer(params),
78 | }
79 | }
80 |
81 | // newPublicEncryptorBuffer creates a new publicEncryptorBuffer.
82 | func newPublicEncryptorBuffer[T TorusInt](params Parameters[T]) publicEncryptorBuffer[T] {
83 | return publicEncryptorBuffer[T]{
84 | ptGLWE: NewGLWEPlaintext(params),
85 | ctGLWE: NewGLWECiphertext(params),
86 |
87 | auxKey: NewGLWESecretKey(params),
88 | auxFourierKey: NewFFTGLWESecretKey(params),
89 | }
90 | }
91 |
92 | // SafeCopy returns a thread-safe copy.
93 | func (e *PublicEncryptor[T]) SafeCopy() *PublicEncryptor[T] {
94 | return &PublicEncryptor[T]{
95 | Encoder: e.Encoder,
96 | GLWETransformer: e.GLWETransformer.SafeCopy(),
97 |
98 | Params: e.Params,
99 |
100 | UniformSampler: csprng.NewUniformSampler[T](),
101 | BinarySampler: csprng.NewBinarySampler[T](),
102 | GaussianSampler: csprng.NewGaussianSampler[T](),
103 |
104 | PolyEvaluator: e.PolyEvaluator.SafeCopy(),
105 |
106 | PublicKey: e.PublicKey,
107 |
108 | buf: newPublicEncryptorBuffer(e.Params),
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/mktfhe/glwe_enc.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/num"
5 | "github.com/sp301415/tfhe-go/math/poly"
6 | "github.com/sp301415/tfhe-go/math/vec"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | )
9 |
10 | // UniEncrypt encrypts integer messages to UniEncryption.
11 | func (e *Encryptor[T]) UniEncrypt(messages []int, gadgetParams tfhe.GadgetParameters[T]) UniEncryption[T] {
12 | ctOut := NewUniEncryption(e.Params, gadgetParams)
13 | e.UniEncryptTo(ctOut, messages, gadgetParams)
14 | return ctOut
15 | }
16 |
17 | // UniEncryptTo encrypts integer messages to UniEncryption and writes it to ctOut.
18 | func (e *Encryptor[T]) UniEncryptTo(ctOut UniEncryption[T], messages []int, gadgetParams tfhe.GadgetParameters[T]) {
19 | length := num.Min(e.Params.PolyRank(), len(messages))
20 | for i := 0; i < length; i++ {
21 | e.buf.ptGLWE.Value.Coeffs[i] = T(messages[i]) % e.Params.MessageModulus()
22 | }
23 | vec.Fill(e.buf.ptGLWE.Value.Coeffs[length:], 0)
24 |
25 | e.UniEncryptPolyTo(ctOut, e.buf.ptGLWE.Value)
26 | }
27 |
28 | // UniEncryptPoly encrypts polynomial to UniEncryption.
29 | func (e *Encryptor[T]) UniEncryptPoly(p poly.Poly[T], gadgetParams tfhe.GadgetParameters[T]) UniEncryption[T] {
30 | ctOut := NewUniEncryption(e.Params, gadgetParams)
31 | e.UniEncryptPolyTo(ctOut, p)
32 | return ctOut
33 | }
34 |
35 | // UniEncryptPolyTo encrypts polynomial to UniEncryption and writes it to ctOut.
36 | func (e *Encryptor[T]) UniEncryptPolyTo(ctOut UniEncryption[T], p poly.Poly[T]) {
37 | e.SubEncryptor.BinarySampler.SamplePolyTo(e.buf.auxKey.Value[0])
38 | e.SubEncryptor.FwdFFTGLWESecretKeyTo(e.buf.auxFourierKey, e.buf.auxKey)
39 |
40 | for i := 0; i < ctOut.GadgetParams.Level(); i++ {
41 | ctOut.Value[0].Value[i].Value[1].CopyFrom(e.CRS[i])
42 | e.SubEncryptor.PolyEvaluator.ScalarMulPolyTo(ctOut.Value[0].Value[i].Value[0], p, ctOut.GadgetParams.BaseQ(i))
43 |
44 | e.SubEncryptor.PolyEvaluator.ShortFFTPolyMulAddPolyTo(ctOut.Value[0].Value[i].Value[0], ctOut.Value[0].Value[i].Value[1], e.buf.auxFourierKey.Value[0])
45 | e.SubEncryptor.GaussianSampler.SamplePolyAddTo(ctOut.Value[0].Value[i].Value[0], e.Params.GLWEStdDevQ())
46 | }
47 |
48 | for i := 0; i < ctOut.GadgetParams.Level(); i++ {
49 | e.SubEncryptor.PolyEvaluator.ScalarMulPolyTo(ctOut.Value[1].Value[i].Value[0], e.buf.auxKey.Value[0], ctOut.GadgetParams.BaseQ(i))
50 | e.SubEncryptor.EncryptGLWEBody(ctOut.Value[1].Value[i])
51 | }
52 | }
53 |
54 | // UniDecrypt decrypts UniEncryption to integer messages.
55 | func (e *Encryptor[T]) UniDecrypt(ct UniEncryption[T]) []int {
56 | messagesOut := make([]int, e.Params.PolyRank())
57 | e.UniDecryptTo(messagesOut, ct)
58 | return messagesOut
59 | }
60 |
61 | // UniDecryptTo decrypts UniEncryption to integer messages and writes it to messagesOut.
62 | func (e *Encryptor[T]) UniDecryptTo(messagesOut []int, ct UniEncryption[T]) {
63 | e.UniDecryptPolyTo(e.buf.ptGLWE.Value, ct)
64 |
65 | length := num.Min(e.Params.PolyRank(), len(messagesOut))
66 | for i := 0; i < length; i++ {
67 | messagesOut[i] = int(e.buf.ptGLWE.Value.Coeffs[i] % e.Params.MessageModulus())
68 | }
69 | }
70 |
71 | // UniDecryptPoly decrypts UniEncryption to polynomial.
72 | func (e *Encryptor[T]) UniDecryptPoly(ct UniEncryption[T]) poly.Poly[T] {
73 | pOut := poly.NewPoly[T](e.Params.PolyRank())
74 | e.UniDecryptPolyTo(pOut, ct)
75 | return pOut
76 | }
77 |
78 | // UniDecryptPolyTo decrypts UniEncryption to GLWE plaintext and writes it to ptOut.
79 | func (e *Encryptor[T]) UniDecryptPolyTo(pOut poly.Poly[T], ct UniEncryption[T]) {
80 | e.SubEncryptor.DecryptGLevPolyTo(e.buf.auxKey.Value[0], ct.Value[1])
81 | e.SubEncryptor.FwdFFTGLWESecretKeyTo(e.buf.auxFourierKey, e.buf.auxKey)
82 |
83 | pOut.CopyFrom(ct.Value[0].Value[0].Value[0])
84 | e.SubEncryptor.PolyEvaluator.ShortFFTPolyMulSubPolyTo(pOut, ct.Value[0].Value[0].Value[1], e.buf.auxFourierKey.Value[0])
85 | for i := 0; i < e.Params.PolyRank(); i++ {
86 | pOut.Coeffs[i] = num.DivRoundBits(pOut.Coeffs[i], ct.GadgetParams.LogFirstBaseQ())
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/xtfhe/fhew_evaluator.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // FHEWEvaluator wraps around [tfhe.Evaluator] and implements FHEW blind rotation.
9 | //
10 | // FHEWEvaluator is not safe for concurrent use.
11 | // Use [*FHEWEvaluator.SafeCopy] to get a safe copy.
12 | type FHEWEvaluator[T tfhe.TorusInt] struct {
13 | // Evaluator is an embedded [tfhe.Evaluator] for this FHEWEvaluator.
14 | *tfhe.Evaluator[T]
15 |
16 | // Params is parameters for this Evaluator.
17 | Params FHEWParameters[T]
18 |
19 | // EvalKey is the evaluation key for this Evaluator.
20 | EvalKey FHEWEvaluationKey[T]
21 |
22 | // autIdxMap holds the map ±5^i -> ±i mod 2N.
23 | autIdxMap []int
24 |
25 | buf fhewEvaluatorBuffer[T]
26 | }
27 |
28 | // fhewEvaluatorBuffer is a buffer for FHEWEvaluator.
29 | type fhewEvaluatorBuffer[T tfhe.TorusInt] struct {
30 | // ctFFTAcc is a fourier transformed accumulator in Blind Rotation.
31 | ctFFTAcc tfhe.FFTGLWECiphertext[T]
32 | // ctFFTAccDcmp is a decomposed ctAcc in Blind Rotation.
33 | ctFFTAccDcmp [][]poly.FFTPoly
34 |
35 | // ctPermute is a permuted accumulator for bootstrapping.
36 | ctPermute tfhe.GLWECiphertext[T]
37 | // ctRotate is a blind rotated GLWE ciphertext for bootstrapping.
38 | ctRotate tfhe.GLWECiphertext[T]
39 | // ctExtract is an extracted LWE ciphertext after Blind Rotation.
40 | ctExtract tfhe.LWECiphertext[T]
41 | // ctKeySwitch is the LWEDimension sized ciphertext from keyswitching for bootstrapping.
42 | ctKeySwitch tfhe.LWECiphertext[T]
43 |
44 | // lut is an empty lut, used for BlindRotateFunc.
45 | lut tfhe.LookUpTable[T]
46 | }
47 |
48 | // NewFHEWEvaluator creates a new FHEWEvaluator.
49 | func NewFHEWEvaluator[T tfhe.TorusInt](params FHEWParameters[T], evk FHEWEvaluationKey[T]) *FHEWEvaluator[T] {
50 | autIdxMap := make([]int, 2*params.baseParams.PolyRank())
51 | autIdx := 1
52 | for i := 0; i < params.baseParams.PolyRank()/2; i++ {
53 | autIdxMap[autIdx] = i
54 | autIdxMap[2*params.baseParams.PolyRank()-autIdx] = -i
55 | autIdx = (autIdx * 5) % (2 * params.baseParams.PolyRank())
56 | }
57 | autIdxMap[2*params.baseParams.PolyRank()-1] = 2 * params.baseParams.PolyRank()
58 |
59 | return &FHEWEvaluator[T]{
60 | Evaluator: tfhe.NewEvaluator(params.baseParams, tfhe.EvaluationKey[T]{KeySwitchKey: evk.KeySwitchKey}),
61 |
62 | Params: params,
63 |
64 | EvalKey: evk,
65 |
66 | autIdxMap: autIdxMap,
67 |
68 | buf: newFHEWEvaluatorBuffer(params),
69 | }
70 | }
71 |
72 | // newFHEWEvaluatorBuffer creates a new fhewEvaluatorBuffer.
73 | func newFHEWEvaluatorBuffer[T tfhe.TorusInt](params FHEWParameters[T]) fhewEvaluatorBuffer[T] {
74 | ctFFTAccDcmp := make([][]poly.FFTPoly, params.baseParams.GLWERank()+1)
75 | for i := 0; i < params.baseParams.GLWERank()+1; i++ {
76 | ctFFTAccDcmp[i] = make([]poly.FFTPoly, params.baseParams.BlindRotateParams().Level())
77 | for j := 0; j < params.baseParams.BlindRotateParams().Level(); j++ {
78 | ctFFTAccDcmp[i][j] = poly.NewFFTPoly(params.baseParams.PolyRank())
79 | }
80 | }
81 |
82 | return fhewEvaluatorBuffer[T]{
83 | ctFFTAcc: tfhe.NewFFTGLWECiphertext(params.baseParams),
84 | ctFFTAccDcmp: ctFFTAccDcmp,
85 |
86 | ctPermute: tfhe.NewGLWECiphertext(params.baseParams),
87 | ctRotate: tfhe.NewGLWECiphertext(params.baseParams),
88 | ctExtract: tfhe.NewLWECiphertextCustom[T](params.baseParams.GLWEDimension()),
89 | ctKeySwitch: tfhe.NewLWECiphertextCustom[T](params.baseParams.LWEDimension()),
90 |
91 | lut: tfhe.NewLUT(params.baseParams),
92 | }
93 | }
94 |
95 | // SafeCopy creates a shallow copy of this FHEWEvaluator.
96 | // Returned FHEWEvaluator is safe for concurrent use.
97 | func (e *FHEWEvaluator[T]) SafeCopy() *FHEWEvaluator[T] {
98 | return &FHEWEvaluator[T]{
99 | Evaluator: e.Evaluator.SafeCopy(),
100 |
101 | Params: e.Params,
102 |
103 | EvalKey: e.EvalKey,
104 |
105 | autIdxMap: e.autIdxMap,
106 |
107 | buf: newFHEWEvaluatorBuffer(e.Params),
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/mktfhe/binary_encryptor.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/tfhe"
5 | )
6 |
7 | // BinaryEncryptor is a multi-key variant of [tfhe.BinaryEncryptor].
8 | type BinaryEncryptor[T tfhe.TorusInt] struct {
9 | // BinaryEncoder is an embedded encoder for this BinaryEncryptor.
10 | *tfhe.BinaryEncoder[T]
11 | // Params is parameters for this BinaryEncryptor.
12 | Params Parameters[T]
13 | // Encryptor is a generic Encryptor for this BinaryEncryptor.
14 | Encryptor *Encryptor[T]
15 | }
16 |
17 | // NewBinaryEncryptor creates a new BinaryEncryptor.
18 | func NewBinaryEncryptor[T tfhe.TorusInt](params Parameters[T], idx int, crsSeed []byte) *BinaryEncryptor[T] {
19 | return &BinaryEncryptor[T]{
20 | BinaryEncoder: tfhe.NewBinaryEncoder(params.subParams),
21 | Params: params,
22 | Encryptor: NewEncryptor(params, idx, crsSeed),
23 | }
24 | }
25 |
26 | // NewBinaryEncryptorWithKey creates a new BinaryEncryptor with a given key.
27 | func NewBinaryEncryptorWithKey[T tfhe.TorusInt](params Parameters[T], idx int, crsSeed []byte, sk tfhe.SecretKey[T]) *BinaryEncryptor[T] {
28 | return &BinaryEncryptor[T]{
29 | BinaryEncoder: tfhe.NewBinaryEncoder(params.subParams),
30 | Params: params,
31 | Encryptor: NewEncryptorWithKey(params, idx, crsSeed, sk),
32 | }
33 | }
34 |
35 | // SafeCopy returns a thread-safe copy.
36 | func (e *BinaryEncryptor[T]) SafeCopy() *BinaryEncryptor[T] {
37 | return &BinaryEncryptor[T]{
38 | BinaryEncoder: e.BinaryEncoder,
39 | Params: e.Params,
40 | Encryptor: e.Encryptor.SafeCopy(),
41 | }
42 | }
43 |
44 | // EncryptLWEBool encrypts boolean message to LWE ciphertexts.
45 | // Like most languages, false == 0, and true == 1.
46 | //
47 | // Note that this is different from calling EncryptLWE with 0 or 1.
48 | func (e *BinaryEncryptor[T]) EncryptLWEBool(message bool) LWECiphertext[T] {
49 | return e.Encryptor.EncryptLWEPlaintext(e.EncodeLWEBool(message))
50 | }
51 |
52 | // EncryptLWEBoolTo encrypts boolean message to LWE ciphertexts.
53 | // Like most languages, false == 0, and true == 1.
54 | //
55 | // Note that this is different from calling EncryptLWE with 0 or 1.
56 | func (e *BinaryEncryptor[T]) EncryptLWEBoolTo(ctOut LWECiphertext[T], message bool) {
57 | e.Encryptor.EncryptLWEPlaintextTo(ctOut, e.EncodeLWEBool(message))
58 | }
59 |
60 | // EncryptLWEBits encrypts each bits of an integer message.
61 | // The order of the bits are little-endian.
62 | func (e *BinaryEncryptor[T]) EncryptLWEBits(message, bits int) []LWECiphertext[T] {
63 | ctOut := make([]LWECiphertext[T], bits)
64 | e.EncryptLWEBitsTo(ctOut, message)
65 | return ctOut
66 | }
67 |
68 | // EncryptLWEBitsTo encrypts each bits of an integer message.
69 | // The order of the bits are little-endian,
70 | // and will be cut by the length of ctOut.
71 | func (e *BinaryEncryptor[T]) EncryptLWEBitsTo(ctOut []LWECiphertext[T], message int) {
72 | for i := 0; i < len(ctOut); i++ {
73 | ctOut[i] = e.EncryptLWEBool(message&1 == 1)
74 | message >>= 1
75 | }
76 | }
77 |
78 | // GenEvalKey samples a new evaluation key for bootstrapping.
79 | //
80 | // This can take a long time.
81 | // Use [*Encryptor.GenEvalKeyParallel] for better key generation performance.
82 | func (e *BinaryEncryptor[T]) GenEvalKey() EvaluationKey[T] {
83 | return e.Encryptor.GenEvalKey()
84 | }
85 |
86 | // GenEvalKeyParallel samples a new evaluation key for bootstrapping in parallel.
87 | func (e *BinaryEncryptor[T]) GenEvalKeyParallel() EvaluationKey[T] {
88 | return e.Encryptor.GenEvalKeyParallel()
89 | }
90 |
91 | // GenPublicKey samples a new public key.
92 | //
93 | // Panics when the parameters do not support public key encryption.
94 | func (e *BinaryEncryptor[T]) GenPublicKey() tfhe.PublicKey[T] {
95 | return e.Encryptor.GenPublicKey()
96 | }
97 |
98 | // PublicEncryptor returns a BinaryPublicEncryptor with the same parameters.
99 | func (e *BinaryEncryptor[T]) PublicEncryptor() *BinaryPublicEncryptor[T] {
100 | return NewBinaryPublicEncryptor(e.Params, e.Encryptor.Index, e.GenPublicKey())
101 | }
102 |
--------------------------------------------------------------------------------
/mktfhe/fft_glwe_enc.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/num"
5 | "github.com/sp301415/tfhe-go/math/poly"
6 | "github.com/sp301415/tfhe-go/math/vec"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | )
9 |
10 | // FFTUniEncrypt encrypts integer messages to FFTUniEncryption.
11 | func (e *Encryptor[T]) FFTUniEncrypt(messages []int, gadgetParams tfhe.GadgetParameters[T]) FFTUniEncryption[T] {
12 | ctOut := NewFFTUniEncryption(e.Params, gadgetParams)
13 | e.FFTUniEncryptTo(ctOut, messages, gadgetParams)
14 | return ctOut
15 | }
16 |
17 | // FFTUniEncryptTo encrypts integer messages to FFTUniEncryption and writes it to ctOut.
18 | func (e *Encryptor[T]) FFTUniEncryptTo(ctOut FFTUniEncryption[T], messages []int, gadgetParams tfhe.GadgetParameters[T]) {
19 | length := num.Min(e.Params.PolyRank(), len(messages))
20 | for i := 0; i < length; i++ {
21 | e.buf.ptGLWE.Value.Coeffs[i] = T(messages[i]) % e.Params.MessageModulus()
22 | }
23 | vec.Fill(e.buf.ptGLWE.Value.Coeffs[length:], 0)
24 |
25 | e.FFTUniEncryptPolyTo(ctOut, e.buf.ptGLWE.Value)
26 | }
27 |
28 | // FFTUniEncryptPoly encrypts polynomial to FFTUniEncryption.
29 | func (e *Encryptor[T]) FFTUniEncryptPoly(p poly.Poly[T], gadgetParams tfhe.GadgetParameters[T]) FFTUniEncryption[T] {
30 | ctOut := NewFFTUniEncryption(e.Params, gadgetParams)
31 | e.FFTUniEncryptPolyTo(ctOut, p)
32 | return ctOut
33 | }
34 |
35 | // FFTUniEncryptPolyTo encrypts polynomial to FFTUniEncryption and writes it to ctOut.
36 | func (e *Encryptor[T]) FFTUniEncryptPolyTo(ctOut FFTUniEncryption[T], p poly.Poly[T]) {
37 | e.SubEncryptor.BinarySampler.SamplePolyTo(e.buf.auxKey.Value[0])
38 | e.SubEncryptor.FwdFFTGLWESecretKeyTo(e.buf.auxFourierKey, e.buf.auxKey)
39 |
40 | for i := 0; i < ctOut.GadgetParams.Level(); i++ {
41 | e.buf.ctSubGLWE.Value[1].CopyFrom(e.CRS[i])
42 | e.SubEncryptor.PolyEvaluator.ScalarMulPolyTo(e.buf.ctSubGLWE.Value[0], p, ctOut.GadgetParams.BaseQ(i))
43 |
44 | e.SubEncryptor.PolyEvaluator.ShortFFTPolyMulAddPolyTo(e.buf.ctSubGLWE.Value[0], e.buf.ctSubGLWE.Value[1], e.buf.auxFourierKey.Value[0])
45 | e.SubEncryptor.GaussianSampler.SamplePolyAddTo(e.buf.ctSubGLWE.Value[0], e.Params.GLWEStdDevQ())
46 |
47 | e.SubEncryptor.FwdFFTGLWECiphertextTo(ctOut.Value[0].Value[i], e.buf.ctSubGLWE)
48 | }
49 |
50 | for i := 0; i < ctOut.GadgetParams.Level(); i++ {
51 | e.SubEncryptor.PolyEvaluator.ScalarMulPolyTo(e.buf.ctSubGLWE.Value[0], e.buf.auxKey.Value[0], ctOut.GadgetParams.BaseQ(i))
52 | e.SubEncryptor.EncryptGLWEBody(e.buf.ctSubGLWE)
53 | e.SubEncryptor.FwdFFTGLWECiphertextTo(ctOut.Value[1].Value[i], e.buf.ctSubGLWE)
54 | }
55 | }
56 |
57 | // FFTUniDecrypt decrypts FFTUniEncryption to integer messages.
58 | func (e *Encryptor[T]) FFTUniDecrypt(ct FFTUniEncryption[T]) []int {
59 | messages := make([]int, e.Params.PolyRank())
60 | e.FFTUniDecryptTo(messages, ct)
61 | return messages
62 | }
63 |
64 | // FFTUniDecryptTo decrypts FFTUniEncryption to integer messages and writes it to messagesOut.
65 | func (e *Encryptor[T]) FFTUniDecryptTo(messagesOut []int, ct FFTUniEncryption[T]) {
66 | e.FFTUniDecryptPolyTo(e.buf.ptGLWE.Value, ct)
67 |
68 | length := num.Min(e.Params.PolyRank(), len(messagesOut))
69 | for i := 0; i < length; i++ {
70 | messagesOut[i] = int(e.buf.ptGLWE.Value.Coeffs[i] % e.Params.MessageModulus())
71 | }
72 | }
73 |
74 | // FFTUniDecryptPoly decrypts FFTUniEncryption to polynomial.
75 | func (e *Encryptor[T]) FFTUniDecryptPoly(ct FFTUniEncryption[T]) poly.Poly[T] {
76 | pOut := poly.NewPoly[T](e.Params.PolyRank())
77 | e.FFTUniDecryptPolyTo(pOut, ct)
78 | return pOut
79 | }
80 |
81 | // FFTUniDecryptPolyTo decrypts FFTUniEncryption to polynomial and writes it to pOut.
82 | func (e *Encryptor[T]) FFTUniDecryptPolyTo(pOut poly.Poly[T], ct FFTUniEncryption[T]) {
83 | e.SubEncryptor.DecryptFFTGLevPolyTo(e.buf.auxKey.Value[0], ct.Value[1])
84 | e.SubEncryptor.FwdFFTGLWESecretKeyTo(e.buf.auxFourierKey, e.buf.auxKey)
85 |
86 | e.SubEncryptor.InvFFTGLWECiphertextTo(e.buf.ctSubGLWE, ct.Value[0].Value[0])
87 | pOut.CopyFrom(e.buf.ctSubGLWE.Value[0])
88 | e.SubEncryptor.PolyEvaluator.ShortFFTPolyMulSubPolyTo(pOut, e.buf.ctSubGLWE.Value[1], e.buf.auxFourierKey.Value[0])
89 | for i := 0; i < e.Params.PolyRank(); i++ {
90 | pOut.Coeffs[i] = num.DivRoundBits(pOut.Coeffs[i], ct.GadgetParams.LogFirstBaseQ())
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/mktfhe/fft_glwe.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // FFTGLWECiphertext is a multi-key variant of [tfhe.FFTGLWECiphertext].
9 | type FFTGLWECiphertext[T tfhe.TorusInt] struct {
10 | // Value has length GLWERank + 1.
11 | Value []poly.FFTPoly
12 | }
13 |
14 | // NewFFTGLWECiphertext creates a new FFT GLWE ciphertext.
15 | func NewFFTGLWECiphertext[T tfhe.TorusInt](params Parameters[T]) FFTGLWECiphertext[T] {
16 | ct := make([]poly.FFTPoly, params.GLWERank()+1)
17 | for i := 0; i < params.GLWERank()+1; i++ {
18 | ct[i] = poly.NewFFTPoly(params.PolyRank())
19 | }
20 | return FFTGLWECiphertext[T]{Value: ct}
21 | }
22 |
23 | // NewFFTGLWECiphertextCustom creates a new FFT GLWE ciphertext with given dimension and polyRank
24 | func NewFFTGLWECiphertextCustom[T tfhe.TorusInt](glweRank, polyRank int) FFTGLWECiphertext[T] {
25 | ct := make([]poly.FFTPoly, glweRank+1)
26 | for i := 0; i < glweRank+1; i++ {
27 | ct[i] = poly.NewFFTPoly(polyRank)
28 | }
29 | return FFTGLWECiphertext[T]{Value: ct}
30 | }
31 |
32 | // Copy returns a copy of the ciphertext.
33 | func (ct FFTGLWECiphertext[T]) Copy() FFTGLWECiphertext[T] {
34 | ctCopy := make([]poly.FFTPoly, len(ct.Value))
35 | for i := range ct.Value {
36 | ctCopy[i] = ct.Value[i].Copy()
37 | }
38 | return FFTGLWECiphertext[T]{Value: ctCopy}
39 | }
40 |
41 | // CopyFrom copies values from the ciphertext.
42 | func (ct *FFTGLWECiphertext[T]) CopyFrom(ctIn FFTGLWECiphertext[T]) {
43 | for i := range ct.Value {
44 | ct.Value[i].CopyFrom(ctIn.Value[i])
45 | }
46 | }
47 |
48 | // CopyFromSubKey copies values from the single-key ciphertext.
49 | //
50 | // Panics if GLWERank of ctIn is not 1.
51 | func (ct *FFTGLWECiphertext[T]) CopyFromSubKey(ctIn tfhe.FFTGLWECiphertext[T], idx int) {
52 | if len(ctIn.Value) != 2 {
53 | panic("GLWERank of ctIn must be 1")
54 | }
55 |
56 | ct.Clear()
57 | ct.Value[0].CopyFrom(ctIn.Value[0])
58 | ct.Value[1+idx].CopyFrom(ctIn.Value[1])
59 | }
60 |
61 | // Clear clears the ciphertext.
62 | func (ct *FFTGLWECiphertext[T]) Clear() {
63 | for i := range ct.Value {
64 | ct.Value[i].Clear()
65 | }
66 | }
67 |
68 | // FFTUniEncryption is a multi-key variant of [tfhe.FFTGGSWCiphertext].
69 | type FFTUniEncryption[T tfhe.TorusInt] struct {
70 | GadgetParams tfhe.GadgetParameters[T]
71 |
72 | // Value has length 2, which equals to single-key GLWERank + 1.
73 | // The first element is always encrypted with the CRS as the mask.
74 | Value []tfhe.FFTGLevCiphertext[T]
75 | }
76 |
77 | // NewFFTUniEncryption creates a new FFT UniEncryption.
78 | func NewFFTUniEncryption[T tfhe.TorusInt](params Parameters[T], gadgetParams tfhe.GadgetParameters[T]) FFTUniEncryption[T] {
79 | return FFTUniEncryption[T]{
80 | GadgetParams: gadgetParams,
81 | Value: []tfhe.FFTGLevCiphertext[T]{
82 | tfhe.NewFFTGLevCiphertextCustom(1, params.PolyRank(), gadgetParams),
83 | tfhe.NewFFTGLevCiphertextCustom(1, params.PolyRank(), gadgetParams),
84 | },
85 | }
86 | }
87 |
88 | // NewFFTUniEncryptionCustom creates a new FFTUniEncryption with given polyRank and partyCount.
89 | func NewFFTUniEncryptionCustom[T tfhe.TorusInt](polyRank int, gadgetParams tfhe.GadgetParameters[T]) FFTUniEncryption[T] {
90 | return FFTUniEncryption[T]{
91 | GadgetParams: gadgetParams,
92 | Value: []tfhe.FFTGLevCiphertext[T]{
93 | tfhe.NewFFTGLevCiphertextCustom(1, polyRank, gadgetParams),
94 | tfhe.NewFFTGLevCiphertextCustom(1, polyRank, gadgetParams),
95 | },
96 | }
97 | }
98 |
99 | // Copy returns a copy of the ciphertext.
100 | func (ct FFTUniEncryption[T]) Copy() FFTUniEncryption[T] {
101 | ctCopy := make([]tfhe.FFTGLevCiphertext[T], len(ct.Value))
102 | for i := range ct.Value {
103 | ctCopy[i] = ct.Value[i].Copy()
104 | }
105 | return FFTUniEncryption[T]{GadgetParams: ct.GadgetParams, Value: ctCopy}
106 | }
107 |
108 | // CopyFrom copies values from the ciphertext.
109 | func (ct *FFTUniEncryption[T]) CopyFrom(ctIn FFTUniEncryption[T]) {
110 | for i := range ct.Value {
111 | ct.Value[i].CopyFrom(ctIn.Value[i])
112 | }
113 | }
114 |
115 | // Clear clears the ciphertext.
116 | func (ct *FFTUniEncryption[T]) Clear() {
117 | for i := range ct.Value {
118 | ct.Value[i].Clear()
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/math/poly/poly.go:
--------------------------------------------------------------------------------
1 | // Package poly implements polynomial and its operations.
2 | package poly
3 |
4 | import (
5 | "math"
6 |
7 | "github.com/sp301415/tfhe-go/math/num"
8 | "github.com/sp301415/tfhe-go/math/vec"
9 | )
10 |
11 | // Poly is a polynomial over Z_Q[X]/(X^N + 1).
12 | type Poly[T num.Integer] struct {
13 | Coeffs []T
14 | }
15 |
16 | // NewPoly creates a polynomial with rank N with empty coefficients.
17 | //
18 | // Panics when N is not a power of two, or when N is smaller than [MinRank].
19 | func NewPoly[T num.Integer](N int) Poly[T] {
20 | switch {
21 | case !num.IsPowerOfTwo(N):
22 | panic("rank not power of two")
23 | case N < MinRank:
24 | panic("rank smaller than MinRank")
25 | }
26 |
27 | return Poly[T]{Coeffs: make([]T, N)}
28 | }
29 |
30 | // From creates a new polynomial from given coefficient slice.
31 | // The given slice is copied, and extended to rank N.
32 | func From[T num.Integer](coeffs []T, N int) Poly[T] {
33 | p := NewPoly[T](N)
34 | copy(p.Coeffs, coeffs)
35 | return p
36 | }
37 |
38 | // Copy returns a copy of the polynomial.
39 | func (p Poly[T]) Copy() Poly[T] {
40 | return Poly[T]{Coeffs: vec.Copy(p.Coeffs)}
41 | }
42 |
43 | // CopyFrom copies p0 to p.
44 | func (p *Poly[T]) CopyFrom(pIn Poly[T]) {
45 | copy(p.Coeffs, pIn.Coeffs)
46 | }
47 |
48 | // Rank returns the rank of the polynomial.
49 | // This is equivalent with length of coefficients.
50 | func (p Poly[T]) Rank() int {
51 | return len(p.Coeffs)
52 | }
53 |
54 | // Clear clears all the coefficients to zero.
55 | func (p Poly[T]) Clear() {
56 | vec.Fill(p.Coeffs, 0)
57 | }
58 |
59 | // Equals checks if p0 is equal with p.
60 | func (p Poly[T]) Equals(p0 Poly[T]) bool {
61 | return vec.Equals(p.Coeffs, p0.Coeffs)
62 | }
63 |
64 | // FFTPoly is a fourier transformed polynomial over C[X]/(X^N/2 + 1).
65 | // This corresponds to a polynomial over Z_Q[X]/(X^N + 1).
66 | type FFTPoly struct {
67 | // Coeffs is represented as float-4 complex vector
68 | // for efficient computation.
69 | //
70 | // Namely,
71 | //
72 | // [(r0, i0), (r1, i1), (r2, i2), (r3, i3), ...]
73 | //
74 | // is represented as
75 | //
76 | // [(r0, r1, r2, r3), (i0, i1, i2, i3), ...]
77 | //
78 | Coeffs []float64
79 | }
80 |
81 | // NewFFTPoly creates a fourier polynomial with rank N/2 with empty coefficients.
82 | //
83 | // Panics when N is not a power of two, or when N is smaller than [MinRank].
84 | func NewFFTPoly(N int) FFTPoly {
85 | switch {
86 | case !num.IsPowerOfTwo(N):
87 | panic("rank not power of two")
88 | case N < MinRank:
89 | panic("rank smaller than MinRank")
90 | }
91 |
92 | return FFTPoly{Coeffs: make([]float64, N)}
93 | }
94 |
95 | // Rank returns the (doubled) rank of the polynomial.
96 | func (p FFTPoly) Rank() int {
97 | return len(p.Coeffs)
98 | }
99 |
100 | // Copy returns a copy of the polynomial.
101 | func (p FFTPoly) Copy() FFTPoly {
102 | return FFTPoly{Coeffs: vec.Copy(p.Coeffs)}
103 | }
104 |
105 | // CopyFrom copies p0 to p.
106 | func (p *FFTPoly) CopyFrom(pIn FFTPoly) {
107 | copy(p.Coeffs, pIn.Coeffs)
108 | }
109 |
110 | // Clear clears all the coefficients to zero.
111 | func (p FFTPoly) Clear() {
112 | vec.Fill(p.Coeffs, 0)
113 | }
114 |
115 | // Equals checks if p0 is equal with p.
116 | // Note that due to floating point errors,
117 | // this function may return false even if p0 and p are equal.
118 | func (p FFTPoly) Equals(p0 FFTPoly) bool {
119 | return vec.Equals(p.Coeffs, p0.Coeffs)
120 | }
121 |
122 | // Approx checks if p0 is approximately equal with p,
123 | // with a difference smaller than eps.
124 | func (p FFTPoly) Approx(p0 FFTPoly, eps float64) bool {
125 | for i := range p.Coeffs {
126 | if math.Abs(p.Coeffs[i]-p0.Coeffs[i]) > eps {
127 | return false
128 | }
129 | }
130 | return true
131 | }
132 |
133 | // checkConsistentPoly checks if [Poly] is consistent with [Evaluator],
134 | // and panics if not.
135 | func checkConsistentPoly[T num.Integer](N int, ps ...Poly[T]) {
136 | if len(ps) == 0 {
137 | return
138 | }
139 |
140 | for i := range ps {
141 | if len(ps[i].Coeffs) != N {
142 | panic("inconsistent inputs")
143 | }
144 | }
145 | }
146 |
147 | // checkConsistentFFTPoly checks if [FFTPoly] is consistent with [Evaluator],
148 | // and panics if not.
149 | func checkConsistentFFTPoly(N int, ps ...FFTPoly) {
150 | if len(ps) == 0 {
151 | return
152 | }
153 |
154 | for i := range ps {
155 | if len(ps[i].Coeffs) != N {
156 | panic("inconsistent inputs")
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/tfhe/binary_encryptor.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // BinaryEncryptor encrypts binary TFHE plaintexts and ciphertexts.
4 | // This is meant to be private, only for clients.
5 | //
6 | // BinaryEncryptor is not safe for concurrent use.
7 | // Use [*BinaryEncryptor.SafeCopy] to get a safe copy.
8 | type BinaryEncryptor[T TorusInt] struct {
9 | // BinaryEncoder is an embedded encoder for this BinaryEncryptor.
10 | *BinaryEncoder[T]
11 | // Params is parameters for this BinaryEncryptor.
12 | Params Parameters[T]
13 | // Encryptor is a generic Encryptor for this BinaryEncryptor.
14 | Encryptor *Encryptor[T]
15 | }
16 |
17 | // NewBinaryEncryptor returns a initialized BinaryEncryptor with given parameters.
18 | // It also automatically samples LWE and GLWE key.
19 | func NewBinaryEncryptor[T TorusInt](params Parameters[T]) *BinaryEncryptor[T] {
20 | return &BinaryEncryptor[T]{
21 | BinaryEncoder: NewBinaryEncoder(params),
22 | Params: params,
23 | Encryptor: NewEncryptor(params),
24 | }
25 | }
26 |
27 | // NewBinaryEncryptorWithKey returns a initialized BinaryEncryptor with given parameters and key.
28 | func NewBinaryEncryptorWithKey[T TorusInt](params Parameters[T], sk SecretKey[T]) *BinaryEncryptor[T] {
29 | return &BinaryEncryptor[T]{
30 | BinaryEncoder: NewBinaryEncoder(params),
31 | Params: params,
32 | Encryptor: NewEncryptorWithKey(params, sk),
33 | }
34 | }
35 |
36 | // SafeCopy returns a thread-safe copy.
37 | func (e *BinaryEncryptor[T]) SafeCopy() *BinaryEncryptor[T] {
38 | return &BinaryEncryptor[T]{
39 | BinaryEncoder: e.BinaryEncoder,
40 | Params: e.Params,
41 | Encryptor: e.Encryptor.SafeCopy(),
42 | }
43 | }
44 |
45 | // EncryptLWEBool encrypts boolean message to LWE ciphertexts.
46 | //
47 | // Note that this is different from calling EncryptLWE with 0 or 1.
48 | func (e *BinaryEncryptor[T]) EncryptLWEBool(message bool) LWECiphertext[T] {
49 | return e.Encryptor.EncryptLWEPlaintext(e.EncodeLWEBool(message))
50 | }
51 |
52 | // EncryptLWEBoolTo encrypts boolean message to LWE ciphertexts and writes to ctOut.
53 | //
54 | // Note that this is different from calling EncryptLWE with 0 or 1.
55 | func (e *BinaryEncryptor[T]) EncryptLWEBoolTo(ctOut LWECiphertext[T], message bool) {
56 | e.Encryptor.EncryptLWEPlaintextTo(ctOut, e.EncodeLWEBool(message))
57 | }
58 |
59 | // DecryptLWEBool decrypts LWE ciphertext to boolean value.
60 | func (e *BinaryEncryptor[T]) DecryptLWEBool(ct LWECiphertext[T]) bool {
61 | return e.DecodeLWEBool(e.Encryptor.DecryptLWEPhase(ct))
62 | }
63 |
64 | // EncryptLWEBits encrypts each bits of an integer message.
65 | // The order of the bits are little-endian.
66 | func (e *BinaryEncryptor[T]) EncryptLWEBits(message, bits int) []LWECiphertext[T] {
67 | ctOut := make([]LWECiphertext[T], bits)
68 | e.EncryptLWEBitsTo(ctOut, message)
69 | return ctOut
70 | }
71 |
72 | // EncryptLWEBitsTo encrypts each bit of an integer message and writes the bits
73 | // into ctOut in little-endian order. The output slice length determines how
74 | // many bits are produced (the message will be truncated to that length).
75 | func (e *BinaryEncryptor[T]) EncryptLWEBitsTo(ctOut []LWECiphertext[T], message int) {
76 | for i := 0; i < len(ctOut); i++ {
77 | ctOut[i] = e.EncryptLWEBool(message&1 == 1)
78 | message >>= 1
79 | }
80 | }
81 |
82 | // DecryptLWEBits decrypts a slice of binary LWE ciphertext
83 | // to integer message.
84 | // The order of bits of LWE ciphertexts are assumed to be little-endian.
85 | func (e *BinaryEncryptor[T]) DecryptLWEBits(ct []LWECiphertext[T]) int {
86 | var message int
87 | for i := len(ct) - 1; i >= 0; i-- {
88 | message <<= 1
89 | if e.DecryptLWEBool(ct[i]) {
90 | message += 1
91 | }
92 | }
93 | return message
94 | }
95 |
96 | // GenPublicKey samples a new public key.
97 | //
98 | // Panics when the parameters do not support public key encryption.
99 | func (e *BinaryEncryptor[T]) GenPublicKey() PublicKey[T] {
100 | return e.Encryptor.GenPublicKey()
101 | }
102 |
103 | // PublicEncryptor returns a BinaryPublicEncryptor with the same parameters.
104 | func (e *BinaryEncryptor[T]) PublicEncryptor() *BinaryPublicEncryptor[T] {
105 | return NewBinaryPublicEncryptor(e.Params, e.GenPublicKey())
106 | }
107 |
108 | // GenEvalKey samples a new evaluation key for bootstrapping.
109 | //
110 | // This can take a long time.
111 | // Use [*BinaryEncryptor.GenEvalKeyParallel] for better key generation performance.
112 | func (e *BinaryEncryptor[T]) GenEvalKey() EvaluationKey[T] {
113 | return e.Encryptor.GenEvalKey()
114 | }
115 |
116 | // GenEvalKeyParallel samples a new evaluation key for bootstrapping in parallel.
117 | func (e *BinaryEncryptor[T]) GenEvalKeyParallel() EvaluationKey[T] {
118 | return e.Encryptor.GenEvalKeyParallel()
119 | }
120 |
--------------------------------------------------------------------------------
/tfhe/keyswitch_key.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | // LWEKeySwitchKey is a LWE keyswitch key from one LWEKey to another LWEKey.
4 | type LWEKeySwitchKey[T TorusInt] struct {
5 | GadgetParams GadgetParameters[T]
6 |
7 | // Value has length InputLWEDimension.
8 | Value []LevCiphertext[T]
9 | }
10 |
11 | // NewLWEKeySwitchKey creates a new LWEKeySwitchingKey.
12 | func NewLWEKeySwitchKey[T TorusInt](params Parameters[T], inputDimension int, gadgetParams GadgetParameters[T]) LWEKeySwitchKey[T] {
13 | ksk := make([]LevCiphertext[T], inputDimension)
14 | for i := 0; i < inputDimension; i++ {
15 | ksk[i] = NewLevCiphertext(params, gadgetParams)
16 | }
17 | return LWEKeySwitchKey[T]{Value: ksk, GadgetParams: gadgetParams}
18 | }
19 |
20 | // NewLWEKeySwitchKeyCustom creates a new LWEKeySwitchingKey with custom parameters.
21 | func NewLWEKeySwitchKeyCustom[T TorusInt](inputDimension, outputDimension int, gadgetParams GadgetParameters[T]) LWEKeySwitchKey[T] {
22 | ksk := make([]LevCiphertext[T], inputDimension)
23 | for i := 0; i < inputDimension; i++ {
24 | ksk[i] = NewLevCiphertextCustom(outputDimension, gadgetParams)
25 | }
26 | return LWEKeySwitchKey[T]{Value: ksk, GadgetParams: gadgetParams}
27 | }
28 |
29 | // NewKeySwitchKeyForBootstrap creates a new LWEKeySwitchingKey for bootstrapping.
30 | func NewKeySwitchKeyForBootstrap[T TorusInt](params Parameters[T]) LWEKeySwitchKey[T] {
31 | return NewLWEKeySwitchKeyCustom(params.glweDimension-params.lweDimension, params.lweDimension, params.keySwitchParams)
32 | }
33 |
34 | // NewKeySwitchKeyForBootstrapCustom creates a new LWEKeySwitchingKey with custom parameters.
35 | func NewKeySwitchKeyForBootstrapCustom[T TorusInt](lweDimension, glweRank, polyRank int, gadgetParams GadgetParameters[T]) LWEKeySwitchKey[T] {
36 | return NewLWEKeySwitchKeyCustom(glweRank*polyRank-lweDimension, lweDimension, gadgetParams)
37 | }
38 |
39 | // InputLWEDimension returns the input LWEDimension of this key.
40 | func (ksk LWEKeySwitchKey[T]) InputLWEDimension() int {
41 | return len(ksk.Value)
42 | }
43 |
44 | // Copy returns a copy of the key.
45 | func (ksk LWEKeySwitchKey[T]) Copy() LWEKeySwitchKey[T] {
46 | kskCopy := make([]LevCiphertext[T], len(ksk.Value))
47 | for i := range ksk.Value {
48 | kskCopy[i] = ksk.Value[i].Copy()
49 | }
50 | return LWEKeySwitchKey[T]{Value: kskCopy, GadgetParams: ksk.GadgetParams}
51 | }
52 |
53 | // CopyFrom copies values from key.
54 | func (ksk *LWEKeySwitchKey[T]) CopyFrom(kskIn LWEKeySwitchKey[T]) {
55 | for i := range ksk.Value {
56 | ksk.Value[i].CopyFrom(kskIn.Value[i])
57 | }
58 | ksk.GadgetParams = kskIn.GadgetParams
59 | }
60 |
61 | // Clear clears the key.
62 | func (ksk *LWEKeySwitchKey[T]) Clear() {
63 | for i := range ksk.Value {
64 | ksk.Value[i].Clear()
65 | }
66 | }
67 |
68 | // GLWEKeySwitchKey is a GLWE keyswitch key from one GLWEKey to another GLWEKey.
69 | type GLWEKeySwitchKey[T TorusInt] struct {
70 | GadgetParameters GadgetParameters[T]
71 |
72 | // Value has length InputGLWERank.
73 | Value []FFTGLevCiphertext[T]
74 | }
75 |
76 | // NewGLWEKeySwitchKey creates a new GLWEKeySwitchingKey.
77 | func NewGLWEKeySwitchKey[T TorusInt](params Parameters[T], inputGLWERank int, gadgetParams GadgetParameters[T]) GLWEKeySwitchKey[T] {
78 | ksk := make([]FFTGLevCiphertext[T], inputGLWERank)
79 | for i := 0; i < inputGLWERank; i++ {
80 | ksk[i] = NewFFTGLevCiphertext(params, gadgetParams)
81 | }
82 | return GLWEKeySwitchKey[T]{Value: ksk, GadgetParameters: gadgetParams}
83 | }
84 |
85 | // NewGLWEKeySwitchKeyCustom creates a new GLWEKeySwitchingKey with custom parameters.
86 | func NewGLWEKeySwitchKeyCustom[T TorusInt](inputGLWERank, outputGLWERank, polyRank int, gadgetParams GadgetParameters[T]) GLWEKeySwitchKey[T] {
87 | ksk := make([]FFTGLevCiphertext[T], inputGLWERank)
88 | for i := 0; i < inputGLWERank; i++ {
89 | ksk[i] = NewFFTGLevCiphertextCustom(outputGLWERank, polyRank, gadgetParams)
90 | }
91 | return GLWEKeySwitchKey[T]{Value: ksk, GadgetParameters: gadgetParams}
92 | }
93 |
94 | // InputGLWERank returns the input GLWERank of this key.
95 | func (ksk GLWEKeySwitchKey[T]) InputGLWERank() int {
96 | return len(ksk.Value)
97 | }
98 |
99 | // Copy returns a copy of the key.
100 | func (ksk GLWEKeySwitchKey[T]) Copy() GLWEKeySwitchKey[T] {
101 | kskCopy := make([]FFTGLevCiphertext[T], len(ksk.Value))
102 | for i := range ksk.Value {
103 | kskCopy[i] = ksk.Value[i].Copy()
104 | }
105 | return GLWEKeySwitchKey[T]{Value: kskCopy, GadgetParameters: ksk.GadgetParameters}
106 | }
107 |
108 | // CopyFrom copies values from key.
109 | func (ksk *GLWEKeySwitchKey[T]) CopyFrom(kskIn GLWEKeySwitchKey[T]) {
110 | for i := range ksk.Value {
111 | ksk.Value[i].CopyFrom(kskIn.Value[i])
112 | }
113 | ksk.GadgetParameters = kskIn.GadgetParameters
114 | }
115 |
116 | // Clear clears the key.
117 | func (ksk *GLWEKeySwitchKey[T]) Clear() {
118 | for i := range ksk.Value {
119 | ksk.Value[i].Clear()
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/mktfhe/params_list.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import "github.com/sp301415/tfhe-go/tfhe"
4 |
5 | var (
6 | // ParamsBinaryParty2 is a parameter set for binary TFHE with 2 parties.
7 | ParamsBinaryParty2 = ParametersLiteral[uint64]{
8 | SubParams: tfhe.ParametersLiteral[uint64]{
9 | LWEDimension: 560,
10 | GLWERank: 1,
11 | PolyRank: 2048,
12 |
13 | LWEStdDev: 0.0000305,
14 | GLWEStdDev: 0.00000000000000000463,
15 |
16 | BlockSize: 2,
17 |
18 | MessageModulus: 1 << 1,
19 |
20 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
21 | Base: 1 << 12,
22 | Level: 3,
23 | },
24 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
25 | Base: 1 << 2,
26 | Level: 8,
27 | },
28 |
29 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
30 | },
31 |
32 | PartyCount: 2,
33 |
34 | AccumulatorParams: tfhe.GadgetParametersLiteral[uint64]{
35 | Base: 1 << 6,
36 | Level: 2,
37 | },
38 | RelinKeyParams: tfhe.GadgetParametersLiteral[uint64]{
39 | Base: 1 << 10,
40 | Level: 3,
41 | },
42 | }
43 |
44 | // ParamsBinaryParty4 is a parameter set for binary TFHE with 4 parties.
45 | ParamsBinaryParty4 = ParametersLiteral[uint64]{
46 | SubParams: tfhe.ParametersLiteral[uint64]{
47 | LWEDimension: 560,
48 | GLWERank: 1,
49 | PolyRank: 2048,
50 |
51 | LWEStdDev: 0.0000305,
52 | GLWEStdDev: 0.00000000000000000463,
53 |
54 | BlockSize: 2,
55 |
56 | MessageModulus: 1 << 1,
57 |
58 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
59 | Base: 1 << 8,
60 | Level: 5,
61 | },
62 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
63 | Base: 1 << 2,
64 | Level: 8,
65 | },
66 |
67 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
68 | },
69 |
70 | PartyCount: 4,
71 |
72 | AccumulatorParams: tfhe.GadgetParametersLiteral[uint64]{
73 | Base: 1 << 8,
74 | Level: 2,
75 | },
76 | RelinKeyParams: tfhe.GadgetParametersLiteral[uint64]{
77 | Base: 1 << 6,
78 | Level: 7,
79 | },
80 | }
81 |
82 | // ParamsBinaryParty8 is a parameter set for binary TFHE with 8 parties.
83 | ParamsBinaryParty8 = ParametersLiteral[uint64]{
84 | SubParams: tfhe.ParametersLiteral[uint64]{
85 | LWEDimension: 560,
86 | GLWERank: 1,
87 | PolyRank: 2048,
88 |
89 | LWEStdDev: 0.0000305,
90 | GLWEStdDev: 0.00000000000000000463,
91 |
92 | BlockSize: 2,
93 |
94 | MessageModulus: 1 << 1,
95 |
96 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
97 | Base: 1 << 9,
98 | Level: 4,
99 | },
100 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
101 | Base: 1 << 2,
102 | Level: 8,
103 | },
104 |
105 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
106 | },
107 |
108 | PartyCount: 8,
109 |
110 | AccumulatorParams: tfhe.GadgetParametersLiteral[uint64]{
111 | Base: 1 << 6,
112 | Level: 3,
113 | },
114 | RelinKeyParams: tfhe.GadgetParametersLiteral[uint64]{
115 | Base: 1 << 4,
116 | Level: 8,
117 | },
118 | }
119 |
120 | // ParamsBinaryParty16 is a parameter set for binary TFHE with 16 parties.
121 | ParamsBinaryParty16 = ParametersLiteral[uint64]{
122 | SubParams: tfhe.ParametersLiteral[uint64]{
123 | LWEDimension: 560,
124 | GLWERank: 1,
125 | PolyRank: 2048,
126 |
127 | LWEStdDev: 0.0000305,
128 | GLWEStdDev: 0.00000000000000000463,
129 |
130 | BlockSize: 2,
131 |
132 | MessageModulus: 1 << 1,
133 |
134 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
135 | Base: 1 << 8,
136 | Level: 5,
137 | },
138 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
139 | Base: 1 << 2,
140 | Level: 8,
141 | },
142 |
143 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
144 | },
145 |
146 | PartyCount: 16,
147 |
148 | AccumulatorParams: tfhe.GadgetParametersLiteral[uint64]{
149 | Base: 1 << 6,
150 | Level: 3,
151 | },
152 | RelinKeyParams: tfhe.GadgetParametersLiteral[uint64]{
153 | Base: 1 << 4,
154 | Level: 9,
155 | },
156 | }
157 |
158 | // ParamsBinaryParty32 is a parameter set for binary TFHE with 32 parties.
159 | ParamsBinaryParty32 = ParametersLiteral[uint64]{
160 | SubParams: tfhe.ParametersLiteral[uint64]{
161 | LWEDimension: 560,
162 | GLWERank: 1,
163 | PolyRank: 2048,
164 |
165 | LWEStdDev: 0.0000305,
166 | GLWEStdDev: 0.00000000000000000463,
167 |
168 | BlockSize: 2,
169 |
170 | MessageModulus: 1 << 1,
171 |
172 | BlindRotateParams: tfhe.GadgetParametersLiteral[uint64]{
173 | Base: 1 << 7,
174 | Level: 6,
175 | },
176 | KeySwitchParams: tfhe.GadgetParametersLiteral[uint64]{
177 | Base: 1 << 2,
178 | Level: 8,
179 | },
180 |
181 | BootstrapOrder: tfhe.OrderBlindRotateKeySwitch,
182 | },
183 |
184 | PartyCount: 32,
185 |
186 | AccumulatorParams: tfhe.GadgetParametersLiteral[uint64]{
187 | Base: 1 << 7,
188 | Level: 3,
189 | },
190 | RelinKeyParams: tfhe.GadgetParametersLiteral[uint64]{
191 | Base: 1 << 2,
192 | Level: 16,
193 | },
194 | }
195 | )
196 |
--------------------------------------------------------------------------------
/tfhe/bootstrap_lut.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/num"
5 | "github.com/sp301415/tfhe-go/math/poly"
6 | "github.com/sp301415/tfhe-go/math/vec"
7 | )
8 |
9 | // LookUpTable is a polynomial that is the lookup table
10 | // for function evaluations during programmable bootstrapping.
11 | type LookUpTable[T TorusInt] struct {
12 | // Value has length lutExtendFactor.
13 | Value []poly.Poly[T]
14 | }
15 |
16 | // NewLUT creates a new lookup table.
17 | func NewLUT[T TorusInt](params Parameters[T]) LookUpTable[T] {
18 | lut := make([]poly.Poly[T], params.lutExtendFactor)
19 | for i := 0; i < params.lutExtendFactor; i++ {
20 | lut[i] = poly.NewPoly[T](params.polyRank)
21 | }
22 |
23 | return LookUpTable[T]{Value: lut}
24 | }
25 |
26 | // NewLUTCustom creates a new lookup table with custom size.
27 | func NewLUTCustom[T TorusInt](extendFactor, polyRank int) LookUpTable[T] {
28 | lut := make([]poly.Poly[T], extendFactor)
29 | for i := 0; i < extendFactor; i++ {
30 | lut[i] = poly.NewPoly[T](polyRank)
31 | }
32 |
33 | return LookUpTable[T]{Value: lut}
34 | }
35 |
36 | // Copy returns a copy of the LUT.
37 | func (lut LookUpTable[T]) Copy() LookUpTable[T] {
38 | lutCopy := make([]poly.Poly[T], len(lut.Value))
39 | for i := 0; i < len(lut.Value); i++ {
40 | lutCopy[i] = lut.Value[i].Copy()
41 | }
42 | return LookUpTable[T]{Value: lutCopy}
43 | }
44 |
45 | // CopyFrom copies values from the LUT.
46 | func (lut *LookUpTable[T]) CopyFrom(lutIn LookUpTable[T]) {
47 | for i := 0; i < len(lut.Value); i++ {
48 | lut.Value[i].CopyFrom(lutIn.Value[i])
49 | }
50 | }
51 |
52 | // Clear clears the LUT.
53 | func (lut *LookUpTable[T]) Clear() {
54 | for i := 0; i < len(lut.Value); i++ {
55 | lut.Value[i].Clear()
56 | }
57 | }
58 |
59 | // GenLUT generates a lookup table based on function f.
60 | // Input and output of f is cut by MessageModulus.
61 | func (e *Evaluator[T]) GenLUT(f func(int) int) LookUpTable[T] {
62 | lutOut := NewLUT(e.Params)
63 | e.GenLUTTo(lutOut, f)
64 | return lutOut
65 | }
66 |
67 | // GenLUTTo generates a lookup table based on function f and writes it to lutOut.
68 | // Input and output of f is cut by MessageModulus.
69 | func (e *Evaluator[T]) GenLUTTo(lutOut LookUpTable[T], f func(int) int) {
70 | e.GenLUTCustomTo(lutOut, f, e.Params.messageModulus, e.Params.scale)
71 | }
72 |
73 | // GenLUTFull generates a lookup table based on function f.
74 | // Output of f is encoded as-is.
75 | func (e *Evaluator[T]) GenLUTFull(f func(int) T) LookUpTable[T] {
76 | lutOut := NewLUT(e.Params)
77 | e.GenLUTFullTo(lutOut, f)
78 | return lutOut
79 | }
80 |
81 | // GenLUTFullTo generates a lookup table based on function f and writes it to lutOut.
82 | // Output of f is encoded as-is.
83 | func (e *Evaluator[T]) GenLUTFullTo(lutOut LookUpTable[T], f func(int) T) {
84 | e.GenLUTCustomFullTo(lutOut, f, e.Params.messageModulus)
85 | }
86 |
87 | // GenLUTCustom generates a lookup table based on function f using custom messageModulus and scale.
88 | // Input and output of f is cut by messageModulus.
89 | func (e *Evaluator[T]) GenLUTCustom(f func(int) int, messageModulus, scale T) LookUpTable[T] {
90 | lutOut := NewLUT(e.Params)
91 | e.GenLUTCustomTo(lutOut, f, messageModulus, scale)
92 | return lutOut
93 | }
94 |
95 | // GenLUTCustomTo generates a lookup table based on function f using custom messageModulus and scale and writes it to lutOut.
96 | // Input and output of f is cut by messageModulus.
97 | func (e *Evaluator[T]) GenLUTCustomTo(lutOut LookUpTable[T], f func(int) int, messageModulus, scale T) {
98 | e.GenLUTCustomFullTo(lutOut, func(x int) T { return e.EncodeLWECustom(f(x), messageModulus, scale).Value }, messageModulus)
99 | }
100 |
101 | // GenLUTCustomFull generates a lookup table based on function f using custom messageModulus and scale.
102 | // Output of f is encoded as-is.
103 | func (e *Evaluator[T]) GenLUTCustomFull(f func(int) T, messageModulus T) LookUpTable[T] {
104 | lutOut := NewLUT(e.Params)
105 | e.GenLUTCustomFullTo(lutOut, f, messageModulus)
106 | return lutOut
107 | }
108 |
109 | // GenLUTCustomFullTo generates a lookup table based on function f using custom messageModulus and scale and writes it to lutOut.
110 | // Output of f is encoded as-is.
111 | func (e *Evaluator[T]) GenLUTCustomFullTo(lutOut LookUpTable[T], f func(int) T, messageModulus T) {
112 | for x := 0; x < int(messageModulus); x++ {
113 | start := num.DivRound(x*e.Params.lutSize, int(messageModulus))
114 | end := num.DivRound((x+1)*e.Params.lutSize, int(messageModulus))
115 | y := f(x)
116 | for xx := start; xx < end; xx++ {
117 | e.buf.lutRaw[xx] = y
118 | }
119 | }
120 |
121 | offset := num.DivRound(e.Params.lutSize, int(2*messageModulus))
122 | vec.RotateInPlace(e.buf.lutRaw, -offset)
123 | for i := e.Params.lutSize - offset; i < e.Params.lutSize; i++ {
124 | e.buf.lutRaw[i] = -e.buf.lutRaw[i]
125 | }
126 |
127 | for i := 0; i < e.Params.lutExtendFactor; i++ {
128 | for j := 0; j < e.Params.polyRank; j++ {
129 | lutOut.Value[i].Coeffs[j] = e.buf.lutRaw[j*e.Params.lutExtendFactor+i]
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/xtfhe/sanitization_params.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "math"
5 |
6 | "github.com/sp301415/tfhe-go/math/num"
7 | "github.com/sp301415/tfhe-go/tfhe"
8 | )
9 |
10 | // SanitizationParametersLiteral is a structure for TFHE Sanitization Parameters.
11 | type SanitizationParametersLiteral[T tfhe.TorusInt] struct {
12 | // BaseParams is a base parameters for this SanitizationParametersLiteral.
13 | BaseParams tfhe.ParametersLiteral[T]
14 |
15 | // RandSigma is the standard deviation used for
16 | // Discrete Gaussian sampling in Rand operation.
17 | RandSigma float64
18 | // RandTau is the standard deviation used for
19 | // Rounded Gaussian sampling in Rand operation.
20 | RandTau float64
21 |
22 | // LinEvalSigma is the standard deviation used for
23 | // Discrete Gaussian sampling in LinEval operation.
24 | LinEvalSigma float64
25 | // LinEvalTau is the standard deviation used for
26 | // Rounded Gaussian sampling in LinEval operation.
27 | LinEvalTau float64
28 | }
29 |
30 | // Compile transforms ParametersLiteral to read-only Parameters.
31 | // If there is any invalid parameter in the literal, it panics.
32 | // Default parameters are guaranteed to be compiled without panics.
33 | func (p SanitizationParametersLiteral[T]) Compile() SanitizationParameters[T] {
34 | baseParameters := p.BaseParams.Compile()
35 |
36 | switch {
37 | case baseParameters.GLWERank() != 1:
38 | panic("GLWERank not 1")
39 | case baseParameters.PolyRank() != baseParameters.LUTSize():
40 | panic("PolyRank does not equal LUTSize")
41 | case baseParameters.BootstrapOrder() != tfhe.OrderKeySwitchBlindRotate:
42 | panic("BootstrapOrder not OrderKeySwitchBlindRotate")
43 | case p.RandSigma <= 0:
44 | panic("RandSigma smaller than zero")
45 | case p.RandTau <= 0:
46 | panic("RandTau smaller than zero")
47 | case p.LinEvalSigma <= 0:
48 | panic("LinEvalSigma smaller than zero")
49 | case p.LinEvalTau <= 0:
50 | panic("LinEvalTau smaller than zero")
51 | }
52 |
53 | return SanitizationParameters[T]{
54 | baseParams: baseParameters,
55 |
56 | floatQ: math.Exp2(float64(num.SizeT[T]())),
57 |
58 | randSigma: p.RandSigma,
59 | randTau: p.RandTau,
60 |
61 | linEvalSigma: p.LinEvalSigma,
62 | linEvalTau: p.LinEvalTau,
63 | }
64 | }
65 |
66 | // SanitizationParameters is a parameter set for TFHE Sanitization.
67 | type SanitizationParameters[T tfhe.TorusInt] struct {
68 | // baseParams is a base parameters for this SanitizationParameters.
69 | baseParams tfhe.Parameters[T]
70 |
71 | // floatQ is the value of Q as float64.
72 | floatQ float64
73 |
74 | // RandSigma is the standard deviation used for
75 | // Discrete Gaussian sampling in Rand operation.
76 | randSigma float64
77 | // RandTau is the standard deviation used for
78 | // Rounded Gaussian sampling in Rand operation.
79 | randTau float64
80 |
81 | // LinEvalSigma is the standard deviation used for
82 | // Discrete Gaussian sampling in LinEval operation.
83 | linEvalSigma float64
84 | // LinEvalTau is the standard deviation used for
85 | // Rounded Gaussian sampling in LinEval operation.
86 | linEvalTau float64
87 | }
88 |
89 | // BaseParams returns the base parameters for this SanitizationParameters.
90 | func (p SanitizationParameters[T]) BaseParams() tfhe.Parameters[T] {
91 | return p.baseParams
92 | }
93 |
94 | // RandSigma returns the standard deviation used for
95 | // Discrete Gaussian sampling in Rand operation.
96 | //
97 | // This is a normalized standard deviation.
98 | // For actual sampling, use [Parameters.RandSigmaQ].
99 | func (p SanitizationParameters[T]) RandSigma() float64 {
100 | return p.randSigma
101 | }
102 |
103 | // RandSigmaQ returns RandSigma * Q.
104 | func (p SanitizationParameters[T]) RandSigmaQ() float64 {
105 | return p.randSigma * p.floatQ
106 | }
107 |
108 | // RandTau returns the standard deviation used for
109 | // Rounded Gaussian sampling in Rand operation.
110 | //
111 | // This is a normalized standard deviation.
112 | // For actual sampling, use [Parameters.RandTauQ].
113 | func (p SanitizationParameters[T]) RandTau() float64 {
114 | return p.randTau
115 | }
116 |
117 | // RandTauQ returns RandTau * Q.
118 | func (p SanitizationParameters[T]) RandTauQ() float64 {
119 | return p.randTau * p.floatQ
120 | }
121 |
122 | // LinEvalSigma returns the standard deviation used for
123 | // Discrete Gaussian sampling in LinEval operation.
124 | //
125 | // This is a normalized standard deviation.
126 | // For actual sampling, use [Parameters.LinEvalSigmaQ].
127 | func (p SanitizationParameters[T]) LinEvalSigma() float64 {
128 | return p.linEvalSigma
129 | }
130 |
131 | // LinEvalSigmaQ returns LinEvalSigma * Q.
132 | func (p SanitizationParameters[T]) LinEvalSigmaQ() float64 {
133 | return p.linEvalSigma * p.floatQ
134 | }
135 |
136 | // LinEvalTau returns the standard deviation used for
137 | // Rounded Gaussian sampling in LinEval operation.
138 | //
139 | // This is a normalized standard deviation.
140 | // For actual sampling, use [Parameters.LinEvalTauQ].
141 | func (p SanitizationParameters[T]) LinEvalTau() float64 {
142 | return p.linEvalTau
143 | }
144 |
145 | // LinEvalTauQ returns LinEvalTau * Q.
146 | func (p SanitizationParameters[T]) LinEvalTauQ() float64 {
147 | return p.linEvalTau * p.floatQ
148 | }
149 |
--------------------------------------------------------------------------------
/tfhe/lwe_enc.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/num"
5 | "github.com/sp301415/tfhe-go/math/vec"
6 | )
7 |
8 | // EncryptLWE encodes and encrypts integer message to LWE ciphertext.
9 | func (e *Encryptor[T]) EncryptLWE(message int) LWECiphertext[T] {
10 | return e.EncryptLWEPlaintext(e.EncodeLWE(message))
11 | }
12 |
13 | // EncryptLWETo encodes and encrypts integer message to LWE ciphertext and writes it to ctOut.
14 | func (e *Encryptor[T]) EncryptLWETo(ctOut LWECiphertext[T], message int) {
15 | e.EncryptLWEPlaintextTo(ctOut, e.EncodeLWE(message))
16 | }
17 |
18 | // EncryptLWEPlaintext encrypts LWE plaintext to LWE ciphertext.
19 | func (e *Encryptor[T]) EncryptLWEPlaintext(pt LWEPlaintext[T]) LWECiphertext[T] {
20 | ctOut := NewLWECiphertext(e.Params)
21 | e.EncryptLWEPlaintextTo(ctOut, pt)
22 | return ctOut
23 | }
24 |
25 | // EncryptLWEPlaintextTo encrypts LWE plaintext to LWE ciphertext and writes it to ctOut.
26 | func (e *Encryptor[T]) EncryptLWEPlaintextTo(ctOut LWECiphertext[T], pt LWEPlaintext[T]) {
27 | ctOut.Value[0] = pt.Value
28 | e.EncryptLWEBody(ctOut)
29 | }
30 |
31 | // EncryptLWEBody encrypts the value in the body of LWE ciphertext and overrides it.
32 | // This avoids the need for most buffers.
33 | func (e *Encryptor[T]) EncryptLWEBody(ct LWECiphertext[T]) {
34 | e.UniformSampler.SampleVecTo(ct.Value[1:])
35 | ct.Value[0] += -vec.Dot(ct.Value[1:], e.DefaultLWESecretKey().Value)
36 | ct.Value[0] += e.GaussianSampler.Sample(e.Params.DefaultLWEStdDevQ())
37 | }
38 |
39 | // DecryptLWE decrypts and decodes LWE ciphertext to integer message.
40 | func (e *Encryptor[T]) DecryptLWE(ct LWECiphertext[T]) int {
41 | return e.DecodeLWE(e.DecryptLWEPhase(ct))
42 | }
43 |
44 | // DecryptLWEPhase decrypts LWE ciphertext to LWE plaintext including errors.
45 | func (e *Encryptor[T]) DecryptLWEPhase(ct LWECiphertext[T]) LWEPlaintext[T] {
46 | ptOut := ct.Value[0] + vec.Dot(ct.Value[1:], e.DefaultLWESecretKey().Value)
47 | return LWEPlaintext[T]{Value: ptOut}
48 | }
49 |
50 | // EncryptLev encrypts integer message to Lev ciphertext.
51 | func (e *Encryptor[T]) EncryptLev(message int, gadgetParams GadgetParameters[T]) LevCiphertext[T] {
52 | return e.EncryptLevScalar(T(message)%e.Params.messageModulus, gadgetParams)
53 | }
54 |
55 | // EncryptLevTo encrypts integer message to Lev ciphertext and writes it to ctOut.
56 | func (e *Encryptor[T]) EncryptLevTo(ctOut LevCiphertext[T], message int) {
57 | e.EncryptLevScalarTo(ctOut, T(message)%e.Params.messageModulus)
58 | }
59 |
60 | // EncryptLevScalar encrypts scalar to Lev ciphertext.
61 | func (e *Encryptor[T]) EncryptLevScalar(c T, gadgetParams GadgetParameters[T]) LevCiphertext[T] {
62 | ctOut := NewLevCiphertext(e.Params, gadgetParams)
63 | e.EncryptLevScalarTo(ctOut, c)
64 | return ctOut
65 | }
66 |
67 | // EncryptLevScalarTo encrypts scalar to Lev ciphertext and writes it to ctOut.
68 | func (e *Encryptor[T]) EncryptLevScalarTo(ctOut LevCiphertext[T], c T) {
69 | for i := 0; i < ctOut.GadgetParams.level; i++ {
70 | ctOut.Value[i].Value[0] = c << ctOut.GadgetParams.LogBaseQ(i)
71 | e.EncryptLWEBody(ctOut.Value[i])
72 | }
73 | }
74 |
75 | // DecryptLev decrypts Lev ciphertext to integer message.
76 | func (e *Encryptor[T]) DecryptLev(ct LevCiphertext[T]) int {
77 | return int(e.DecryptLevScalar(ct) % e.Params.messageModulus)
78 | }
79 |
80 | // DecryptLevScalar decrypts Lev ciphertext to scalar.
81 | func (e *Encryptor[T]) DecryptLevScalar(ct LevCiphertext[T]) T {
82 | ptOut := e.DecryptLWEPhase(ct.Value[0])
83 | return num.DivRoundBits(ptOut.Value, ct.GadgetParams.LogFirstBaseQ()) % ct.GadgetParams.base
84 | }
85 |
86 | // EncryptGSW encrypts integer message to GSW ciphertext.
87 | func (e *Encryptor[T]) EncryptGSW(message int, gadgetParams GadgetParameters[T]) GSWCiphertext[T] {
88 | return e.EncryptGSWScalar(T(message)%e.Params.messageModulus, gadgetParams)
89 | }
90 |
91 | // EncryptGSWTo encrypts integer message to GSW ciphertext and writes it to ctOut.
92 | func (e *Encryptor[T]) EncryptGSWTo(ctOut GSWCiphertext[T], message int) {
93 | e.EncryptGSWScalarTo(ctOut, T(message)%e.Params.messageModulus)
94 | }
95 |
96 | // EncryptGSWScalar encrypts scalar to GSW ciphertext.
97 | func (e *Encryptor[T]) EncryptGSWScalar(c T, gadgetParams GadgetParameters[T]) GSWCiphertext[T] {
98 | ctOut := NewGSWCiphertext(e.Params, gadgetParams)
99 | e.EncryptGSWScalarTo(ctOut, c)
100 | return ctOut
101 | }
102 |
103 | // EncryptGSWScalarTo encrypts LWE plaintext to GSW ciphertext and writes it to ctOut.
104 | func (e *Encryptor[T]) EncryptGSWScalarTo(ctOut GSWCiphertext[T], c T) {
105 | e.EncryptLevScalarTo(ctOut.Value[0], c)
106 |
107 | for i := 0; i < e.Params.DefaultLWEDimension(); i++ {
108 | for j := 0; j < ctOut.GadgetParams.level; j++ {
109 | ctOut.Value[i+1].Value[j].Value[0] = e.DefaultLWESecretKey().Value[i] * c << ctOut.GadgetParams.LogBaseQ(j)
110 | e.EncryptLWEBody(ctOut.Value[i+1].Value[j])
111 | }
112 | }
113 | }
114 |
115 | // DecryptGSW decrypts GSW ciphertext to integer message.
116 | func (e *Encryptor[T]) DecryptGSW(ct GSWCiphertext[T]) int {
117 | return e.DecryptLev(ct.Value[0])
118 | }
119 |
120 | // DecryptGSWScalar decrypts GSW ciphertext to LWE plaintext.
121 | func (e *Encryptor[T]) DecryptGSWScalar(ct GSWCiphertext[T]) T {
122 | return e.DecryptLevScalar(ct.Value[0])
123 | }
124 |
--------------------------------------------------------------------------------
/xtfhe/bfv_keygen.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // BFVEvaluationKey is a keyswitching key for BFV type operations.
9 | // It holds relinearization and automorphism keys.
10 | type BFVEvaluationKey[T tfhe.TorusInt] struct {
11 | // RelinKey is a relinearization key.
12 | RelinKey tfhe.GLWEKeySwitchKey[T]
13 | // GaloisKeys is a map of automorphism keys.
14 | GaloisKeys map[int]tfhe.GLWEKeySwitchKey[T]
15 | }
16 |
17 | // BFVKeyGenerator generates keyswitching keys for BFV type operations.
18 | //
19 | // BFVKeyGenerator is not safe for concurrent use.
20 | // Use [*BFVKeyGenerator.SafeCopy] to get a safe copy.
21 | type BFVKeyGenerator[T tfhe.TorusInt] struct {
22 | // Encryptor is a base encryptor for this BFVKeyGenerator.
23 | Encryptor *tfhe.Encryptor[T]
24 | // PolyEvaluator is a PolyEvaluator for this BFVKeyGenerator.
25 | PolyEvaluator *poly.Evaluator[T]
26 |
27 | // Params is parameters for this BFVKeyGenerator.
28 | Params tfhe.Parameters[T]
29 | }
30 |
31 | // NewBFVKeyGenerator creates a new BFVKeyGenerator.
32 | func NewBFVKeyGenerator[T tfhe.TorusInt](params tfhe.Parameters[T], sk tfhe.SecretKey[T]) *BFVKeyGenerator[T] {
33 | return &BFVKeyGenerator[T]{
34 | Encryptor: tfhe.NewEncryptorWithKey(params, sk),
35 | PolyEvaluator: poly.NewEvaluator[T](params.PolyRank()),
36 | Params: params,
37 | }
38 | }
39 |
40 | // SafeCopy creates a shallow copy of this BFVKeyGenerator.
41 | func (kg *BFVKeyGenerator[T]) SafeCopy() *BFVKeyGenerator[T] {
42 | return &BFVKeyGenerator[T]{
43 | Encryptor: kg.Encryptor.SafeCopy(),
44 | PolyEvaluator: kg.PolyEvaluator.SafeCopy(),
45 | Params: kg.Params,
46 | }
47 | }
48 |
49 | // GenEvalKey generates an evaluation key for BFV type operations.
50 | func (kg *BFVKeyGenerator[T]) GenEvalKey(idx []int, kskParams tfhe.GadgetParameters[T]) BFVEvaluationKey[T] {
51 | return BFVEvaluationKey[T]{
52 | RelinKey: kg.GenRelinKey(kskParams),
53 | GaloisKeys: kg.GenGaloisKeys(idx, kskParams),
54 | }
55 | }
56 |
57 | // GenRelinKey generates a relinearization key for BFV multiplication.
58 | func (kg *BFVKeyGenerator[T]) GenRelinKey(kskParams tfhe.GadgetParameters[T]) tfhe.GLWEKeySwitchKey[T] {
59 | rlkRank := kg.Params.GLWERank() * (kg.Params.GLWERank() + 1) / 2
60 | skOut := tfhe.NewGLWESecretKeyCustom[T](rlkRank, kg.Params.PolyRank())
61 | fskOut := tfhe.NewFFTGLWESecretKeyCustom[T](rlkRank, kg.Params.PolyRank())
62 |
63 | skOutIdx := 0
64 | for i := 0; i < kg.Params.GLWERank(); i++ {
65 | for j := i; j < kg.Params.GLWERank(); j++ {
66 | kg.Encryptor.PolyEvaluator.MulFFTPolyTo(fskOut.Value[skOutIdx], kg.Encryptor.SecretKey.FFTGLWEKey.Value[i], kg.Encryptor.SecretKey.FFTGLWEKey.Value[j])
67 | skOutIdx++
68 | }
69 | }
70 |
71 | for i := range fskOut.Value {
72 | kg.Encryptor.PolyEvaluator.InvFFTToUnsafe(skOut.Value[i], fskOut.Value[i])
73 | }
74 |
75 | return kg.Encryptor.GenGLWEKeySwitchKey(skOut, kskParams)
76 | }
77 |
78 | // GenGaloisKeys generate galois keys for BFV automorphism.
79 | func (kg *BFVKeyGenerator[T]) GenGaloisKeys(idx []int, kskParams tfhe.GadgetParameters[T]) map[int]tfhe.GLWEKeySwitchKey[T] {
80 | galKeys := make(map[int]tfhe.GLWEKeySwitchKey[T], len(idx))
81 | skOut := tfhe.NewGLWESecretKey(kg.Params)
82 |
83 | for _, d := range idx {
84 | for i := 0; i < kg.Params.GLWERank(); i++ {
85 | kg.Encryptor.PolyEvaluator.PermutePolyTo(skOut.Value[i], kg.Encryptor.SecretKey.GLWEKey.Value[i], d)
86 | }
87 | galKeys[d] = kg.Encryptor.GenGLWEKeySwitchKey(skOut, kskParams)
88 | }
89 | return galKeys
90 | }
91 |
92 | // GenGaloisKeysTo generates automorphism keys for BFV automorphism and assigns them to the given map.
93 | // If a key for a given automorphism degree already exists in the map, it will be overwritten.
94 | func (kg *BFVKeyGenerator[T]) GenGaloisKeysTo(galKeysOut map[int]tfhe.GLWEKeySwitchKey[T], idx []int, kskParams tfhe.GadgetParameters[T]) {
95 | skOut := tfhe.NewGLWESecretKey(kg.Params)
96 |
97 | for _, d := range idx {
98 | for i := 0; i < kg.Params.GLWERank(); i++ {
99 | kg.Encryptor.PolyEvaluator.PermutePolyTo(skOut.Value[i], kg.Encryptor.SecretKey.GLWEKey.Value[i], d)
100 | }
101 | galKeysOut[d] = kg.Encryptor.GenGLWEKeySwitchKey(skOut, kskParams)
102 | }
103 | }
104 |
105 | // GenGaloisKeysForPack generates automorphism keys for BFV automorphism for LWE to GLWE packing.
106 | func (kg *BFVKeyGenerator[T]) GenGaloisKeysForPack(kskParams tfhe.GadgetParameters[T]) map[int]tfhe.GLWEKeySwitchKey[T] {
107 | auts := make([]int, kg.Params.LogPolyRank())
108 | for i := range auts {
109 | auts[i] = 1<<(kg.Params.LogPolyRank()-i) + 1
110 | }
111 | return kg.GenGaloisKeys(auts, kskParams)
112 | }
113 |
114 | // GenGaloisKeysForPackTo generates automorphism keys for BFV automorphism for LWE to GLWE packing and assigns them to the given map.
115 | // If a key for a given automorphism degree already exists in the map, it will be overwritten.
116 | func (kg *BFVKeyGenerator[T]) GenGaloisKeysForPackTo(galKeysOut map[int]tfhe.GLWEKeySwitchKey[T], kskParams tfhe.GadgetParameters[T]) {
117 | auts := make([]int, kg.Params.LogPolyRank())
118 | for i := range auts {
119 | auts[i] = 1<<(kg.Params.LogPolyRank()-i) + 1
120 | }
121 | kg.GenGaloisKeysTo(galKeysOut, auts, kskParams)
122 | }
123 |
--------------------------------------------------------------------------------
/mktfhe/glwe.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // GLWECiphertext is a multi-key variant of [tfhe.GLWECiphertext].
9 | // Due to the structure of UniEncryption,
10 | // all multi-key GLWE ciphertexts are actually RLWE ciphertexts.
11 | type GLWECiphertext[T tfhe.TorusInt] struct {
12 | // Value has length GLWERank + 1.
13 | Value []poly.Poly[T]
14 | }
15 |
16 | // NewGLWECiphertext creates a new GLWE ciphertext.
17 | func NewGLWECiphertext[T tfhe.TorusInt](params Parameters[T]) GLWECiphertext[T] {
18 | ct := make([]poly.Poly[T], params.GLWERank()+1)
19 | for i := 0; i < params.GLWERank()+1; i++ {
20 | ct[i] = poly.NewPoly[T](params.PolyRank())
21 | }
22 | return GLWECiphertext[T]{Value: ct}
23 | }
24 |
25 | // NewGLWECiphertextCustom creates a new GLWE ciphertext with given dimension and partyCount.
26 | func NewGLWECiphertextCustom[T tfhe.TorusInt](glweRank, polyRank int) GLWECiphertext[T] {
27 | ct := make([]poly.Poly[T], glweRank+1)
28 | for i := 0; i < glweRank+1; i++ {
29 | ct[i] = poly.NewPoly[T](polyRank)
30 | }
31 | return GLWECiphertext[T]{Value: ct}
32 | }
33 |
34 | // Copy returns a copy of the ciphertext.
35 | func (ct GLWECiphertext[T]) Copy() GLWECiphertext[T] {
36 | ctCopy := make([]poly.Poly[T], len(ct.Value))
37 | for i := range ct.Value {
38 | ctCopy[i] = ct.Value[i].Copy()
39 | }
40 | return GLWECiphertext[T]{Value: ctCopy}
41 | }
42 |
43 | // CopyFrom copies values from the ciphertext.
44 | func (ct *GLWECiphertext[T]) CopyFrom(ctIn GLWECiphertext[T]) {
45 | for i := range ct.Value {
46 | ct.Value[i].CopyFrom(ctIn.Value[i])
47 | }
48 | }
49 |
50 | // CopyFromSubKey copies values from the single-key ciphertext.
51 | //
52 | // Panics if GLWERank of ctIn is not 1.
53 | func (ct *GLWECiphertext[T]) CopyFromSubKey(ctIn tfhe.GLWECiphertext[T], idx int) {
54 | if len(ctIn.Value) != 2 {
55 | panic("GLWERank of ctIn must be 1")
56 | }
57 |
58 | ct.Clear()
59 | ct.Value[0].CopyFrom(ctIn.Value[0])
60 | ct.Value[1+idx].CopyFrom(ctIn.Value[1])
61 | }
62 |
63 | // Clear clears the ciphertext.
64 | func (ct *GLWECiphertext[T]) Clear() {
65 | for i := range ct.Value {
66 | ct.Value[i].Clear()
67 | }
68 | }
69 |
70 | // AsLWECiphertext extracts LWE ciphertext of given index from GLWE ciphertext.
71 | // The output ciphertext will be of length GLWEDimension + 1,
72 | // encrypted with LWELargeKey.
73 | func (ct GLWECiphertext[T]) AsLWECiphertext(idx int) LWECiphertext[T] {
74 | ctOut := NewLWECiphertextCustom[T]((len(ct.Value) - 1) * ct.Value[0].Rank())
75 | ct.AsLWECiphertextTo(ctOut, idx)
76 | return ctOut
77 | }
78 |
79 | // AsLWECiphertextTo extracts LWE ciphertext of given index from GLWE ciphertext and writes it to ctOut.
80 | // The output ciphertext should be of length GLWEDimension + 1,
81 | // and it will be a ciphertext encrypted with LWELargeKey.
82 | func (ct GLWECiphertext[T]) AsLWECiphertextTo(ctOut LWECiphertext[T], idx int) {
83 | ctOut.Value[0] = ct.Value[0].Coeffs[idx]
84 |
85 | for i := 0; i < len(ct.Value)-1; i++ {
86 | for j := 0; j <= idx; j++ {
87 | ctOut.Value[1+i*ct.Value[i+1].Rank()+j] = ct.Value[i+1].Coeffs[idx-j]
88 | }
89 | for j := idx + 1; j < ct.Value[i+1].Rank(); j++ {
90 | ctOut.Value[1+i*ct.Value[i+1].Rank()+j] = -ct.Value[i+1].Coeffs[idx-j+ct.Value[i+1].Rank()]
91 | }
92 | }
93 | }
94 |
95 | // UniEncryption is a multi-key variant of [tfhe.GGSWCiphertext].
96 | type UniEncryption[T tfhe.TorusInt] struct {
97 | GadgetParams tfhe.GadgetParameters[T]
98 |
99 | // Value has length 2, which equals to single-key GLWERank + 1.
100 | // The first element is always encrypted with the CRS as the mask.
101 | Value []tfhe.GLevCiphertext[T]
102 | }
103 |
104 | // NewUniEncryption creates a new UniEncryption.
105 | func NewUniEncryption[T tfhe.TorusInt](params Parameters[T], gadgetParams tfhe.GadgetParameters[T]) UniEncryption[T] {
106 | return UniEncryption[T]{
107 | GadgetParams: gadgetParams,
108 | Value: []tfhe.GLevCiphertext[T]{
109 | tfhe.NewGLevCiphertextCustom(1, params.PolyRank(), gadgetParams),
110 | tfhe.NewGLevCiphertextCustom(1, params.PolyRank(), gadgetParams),
111 | },
112 | }
113 | }
114 |
115 | // NewUniEncryptionCustom creates a new UniEncryption with given polyRank and partyCount.
116 | func NewUniEncryptionCustom[T tfhe.TorusInt](polyRank int, gadgetParams tfhe.GadgetParameters[T]) UniEncryption[T] {
117 | return UniEncryption[T]{
118 | GadgetParams: gadgetParams,
119 | Value: []tfhe.GLevCiphertext[T]{
120 | tfhe.NewGLevCiphertextCustom(1, polyRank, gadgetParams),
121 | tfhe.NewGLevCiphertextCustom(1, polyRank, gadgetParams),
122 | },
123 | }
124 | }
125 |
126 | // Copy returns a copy of the ciphertext.
127 | func (ct UniEncryption[T]) Copy() UniEncryption[T] {
128 | ctCopy := make([]tfhe.GLevCiphertext[T], len(ct.Value))
129 | for i := range ct.Value {
130 | ctCopy[i] = ct.Value[i].Copy()
131 | }
132 | return UniEncryption[T]{GadgetParams: ct.GadgetParams, Value: ctCopy}
133 | }
134 |
135 | // CopyFrom copies values from the ciphertext.
136 | func (ct *UniEncryption[T]) CopyFrom(ctIn UniEncryption[T]) {
137 | for i := range ct.Value {
138 | ct.Value[i].CopyFrom(ctIn.Value[i])
139 | }
140 | }
141 |
142 | // Clear clears the ciphertext.
143 | func (ct *UniEncryption[T]) Clear() {
144 | for i := range ct.Value {
145 | ct.Value[i].Clear()
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/internal/asmgen/decompose.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | . "github.com/mmcloughlin/avo/build"
5 | . "github.com/mmcloughlin/avo/operand"
6 | )
7 |
8 | func DecomposeConstants() {
9 | ConstData("ONE", U64(1))
10 | }
11 |
12 | func DecomposePolyToUint32AVX2() {
13 | TEXT("decomposePolyToUint32AVX2", NOSPLIT, "func(dcmpOut [][]uint32, p []uint32, base uint32, logBase uint32, logLastBaseQ uint32)")
14 | Pragma("noescape")
15 |
16 | dcmpOut := Load(Param("dcmpOut").Base(), GP64())
17 | p := Load(Param("p").Base(), GP64())
18 |
19 | N := Load(Param("p").Len(), GP64())
20 | level := Load(Param("dcmpOut").Len(), GP64())
21 |
22 | // VET: go vet complains about VBROADCASTD on uint32 values.
23 | // See https://github.com/golang/go/issues/47625.
24 | base, logBase, logLastBaseQ := YMM(), YMM(), YMM()
25 | VPBROADCASTD(NewParamAddr("base", 48), base)
26 | VPBROADCASTD(NewParamAddr("logBase", 52), logBase)
27 | VPBROADCASTD(NewParamAddr("logLastBaseQ", 56), logLastBaseQ)
28 |
29 | allOne := YMM()
30 | VPBROADCASTD(NewDataAddr(NewStaticSymbol("ONE"), 0), allOne)
31 |
32 | baseMask, baseHalf, logBaseSubOne := YMM(), YMM(), YMM()
33 | VPSUBD(allOne, base, baseMask)
34 | VPSRLD(Imm(1), base, baseHalf)
35 | VPSUBD(allOne, logBase, logBaseSubOne)
36 |
37 | i := GP64()
38 | XORQ(i, i)
39 | JMP(LabelRef("N_loop_end"))
40 | Label("N_loop_body")
41 |
42 | c := YMM()
43 | VMOVDQU(Mem{Base: p, Index: i, Scale: 4}, c)
44 |
45 | cDiv, cCarry := YMM(), YMM()
46 | VPSRLVD(logLastBaseQ, c, cDiv)
47 | VPSLLD(Imm(1), c, cCarry)
48 | VPSRLVD(logLastBaseQ, cCarry, cCarry)
49 | VANDPD(allOne, cCarry, cCarry)
50 | VPADDD(cCarry, cDiv, c)
51 |
52 | j := GP64()
53 | MOVQ(level, j)
54 | SUBQ(Imm(1), j)
55 |
56 | jj := GP64()
57 | MOVQ(j, jj)
58 | ADDQ(j, jj)
59 | ADDQ(j, jj)
60 | JMP(LabelRef("level_loop_end"))
61 | Label("level_loop_body")
62 |
63 | u := YMM()
64 | VANDPD(baseMask, c, u)
65 | VPSRLVD(logBase, c, c)
66 |
67 | uDiv := YMM()
68 | VPSRLVD(logBaseSubOne, u, uDiv)
69 | VPADDD(uDiv, c, c)
70 |
71 | uCarry := YMM()
72 | VANDPD(baseHalf, u, uCarry)
73 | VPSLLD(Imm(1), uCarry, uCarry)
74 | VPSUBD(uCarry, u, u)
75 |
76 | dcmpOutj := GP64()
77 | MOVQ(Mem{Base: dcmpOut, Index: jj, Scale: 8}, dcmpOutj)
78 | VMOVDQU(u, Mem{Base: dcmpOutj, Index: i, Scale: 4})
79 |
80 | SUBQ(Imm(1), j)
81 | SUBQ(Imm(3), jj)
82 |
83 | Label("level_loop_end")
84 | CMPQ(j, Imm(1))
85 | JGE(LabelRef("level_loop_body"))
86 |
87 | u = YMM()
88 | VANDPD(baseMask, c, u)
89 |
90 | uCarry = YMM()
91 | VANDPD(baseHalf, u, uCarry)
92 | VPSLLD(Imm(1), uCarry, uCarry)
93 | VPSUBD(uCarry, u, u)
94 |
95 | dcmpOut0 := GP64()
96 | MOVQ(Mem{Base: dcmpOut}, dcmpOut0)
97 | VMOVDQU(u, Mem{Base: dcmpOut0, Index: i, Scale: 4})
98 |
99 | ADDQ(Imm(8), i)
100 |
101 | Label("N_loop_end")
102 | CMPQ(i, N)
103 | JL(LabelRef("N_loop_body"))
104 |
105 | RET()
106 | }
107 |
108 | func DecomposePolyToUint64AVX2() {
109 | TEXT("decomposePolyToUint64AVX2", NOSPLIT, "func(dcmpOut [][]uint64, p []uint64, base uint64, logBase uint64, logLastBaseQ uint64)")
110 | Pragma("noescape")
111 |
112 | dcmpOut := Load(Param("dcmpOut").Base(), GP64())
113 | p := Load(Param("p").Base(), GP64())
114 |
115 | N := Load(Param("p").Len(), GP64())
116 | level := Load(Param("dcmpOut").Len(), GP64())
117 |
118 | base, logBase, logLastBaseQ := YMM(), YMM(), YMM()
119 | VPBROADCASTQ(NewParamAddr("base", 48), base)
120 | VPBROADCASTQ(NewParamAddr("logBase", 56), logBase)
121 | VPBROADCASTQ(NewParamAddr("logLastBaseQ", 64), logLastBaseQ)
122 |
123 | one := YMM()
124 | VPBROADCASTQ(NewDataAddr(NewStaticSymbol("ONE"), 0), one)
125 |
126 | baseMask, baseHalf, logBaseSubOne := YMM(), YMM(), YMM()
127 | VPSUBQ(one, base, baseMask)
128 | VPSRLQ(Imm(1), base, baseHalf)
129 | VPSUBQ(one, logBase, logBaseSubOne)
130 |
131 | i := GP64()
132 | XORQ(i, i)
133 | JMP(LabelRef("N_loop_end"))
134 | Label("N_loop_body")
135 |
136 | c := YMM()
137 | VMOVDQU(Mem{Base: p, Index: i, Scale: 8}, c)
138 |
139 | cDiv, cCarry := YMM(), YMM()
140 | VPSRLVQ(logLastBaseQ, c, cDiv)
141 | VPSLLQ(Imm(1), c, cCarry)
142 | VPSRLVQ(logLastBaseQ, cCarry, cCarry)
143 | VANDPD(one, cCarry, cCarry)
144 | VPADDQ(cCarry, cDiv, c)
145 |
146 | j := GP64()
147 | MOVQ(level, j)
148 | SUBQ(Imm(1), j)
149 |
150 | jj := GP64()
151 | MOVQ(j, jj)
152 | ADDQ(j, jj)
153 | ADDQ(j, jj)
154 | JMP(LabelRef("level_loop_end"))
155 | Label("level_loop_body")
156 |
157 | u := YMM()
158 | VANDPD(baseMask, c, u)
159 | VPSRLVQ(logBase, c, c)
160 |
161 | uDiv := YMM()
162 | VPSRLVQ(logBaseSubOne, u, uDiv)
163 | VPADDQ(uDiv, c, c)
164 |
165 | uCarry := YMM()
166 | VANDPD(baseHalf, u, uCarry)
167 | VPSLLQ(Imm(1), uCarry, uCarry)
168 | VPSUBQ(uCarry, u, u)
169 |
170 | dcmpOutj := GP64()
171 | MOVQ(Mem{Base: dcmpOut, Index: jj, Scale: 8}, dcmpOutj)
172 | VMOVDQU(u, Mem{Base: dcmpOutj, Index: i, Scale: 8})
173 |
174 | SUBQ(Imm(1), j)
175 | SUBQ(Imm(3), jj)
176 |
177 | Label("level_loop_end")
178 | CMPQ(j, Imm(1))
179 | JGE(LabelRef("level_loop_body"))
180 |
181 | u = YMM()
182 | VANDPD(baseMask, c, u)
183 |
184 | uCarry = YMM()
185 | VANDPD(baseHalf, u, uCarry)
186 | VPSLLQ(Imm(1), uCarry, uCarry)
187 | VPSUBQ(uCarry, u, u)
188 |
189 | dcmpOut0 := GP64()
190 | MOVQ(Mem{Base: dcmpOut}, dcmpOut0)
191 | VMOVDQU(u, Mem{Base: dcmpOut0, Index: i, Scale: 8})
192 |
193 | ADDQ(Imm(4), i)
194 |
195 | Label("N_loop_end")
196 | CMPQ(i, N)
197 | JL(LabelRef("N_loop_body"))
198 |
199 | RET()
200 | }
201 |
--------------------------------------------------------------------------------
/mktfhe/fft_glwe_conv.go:
--------------------------------------------------------------------------------
1 | package mktfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/tfhe"
6 | )
7 |
8 | // GLWETransformer is a multi-key variant of [tfhe.GLWETransformer].
9 | type GLWETransformer[T tfhe.TorusInt] struct {
10 | // PolyEvaluator is a PolyEvaluator for this GLWETransformer.
11 | PolyEvaluator *poly.Evaluator[T]
12 | }
13 |
14 | // NewGLWETransformer creates a new GLWE transformer with given parameters.
15 | func NewGLWETransformer[T tfhe.TorusInt](N int) *GLWETransformer[T] {
16 | return &GLWETransformer[T]{
17 | PolyEvaluator: poly.NewEvaluator[T](N),
18 | }
19 | }
20 |
21 | // SafeCopy returns a thread-safe copy.
22 | func (e *GLWETransformer[T]) SafeCopy() *GLWETransformer[T] {
23 | return &GLWETransformer[T]{
24 | PolyEvaluator: e.PolyEvaluator.SafeCopy(),
25 | }
26 | }
27 |
28 | // FwdFFTGLWESecretKey transforms GLWE secret key to FFT GLWE secret key.
29 | func (e *GLWETransformer[T]) FwdFFTGLWESecretKey(sk tfhe.GLWESecretKey[T]) tfhe.FFTGLWESecretKey[T] {
30 | skOut := tfhe.NewFFTGLWESecretKeyCustom[T](len(sk.Value), e.PolyEvaluator.Rank())
31 | e.FwdFFTGLWESecretKeyTo(skOut, sk)
32 | return skOut
33 | }
34 |
35 | // FwdFFTGLWESecretKeyTo transforms GLWE secret key to FFT GLWE secret key and writes it to skOut.
36 | func (e *GLWETransformer[T]) FwdFFTGLWESecretKeyTo(skOut tfhe.FFTGLWESecretKey[T], sk tfhe.GLWESecretKey[T]) {
37 | for i := 0; i < len(sk.Value); i++ {
38 | e.PolyEvaluator.FwdFFTPolyTo(skOut.Value[i], sk.Value[i])
39 | }
40 | }
41 |
42 | // InvFFTGLWESecretKey transforms FFT GLWE secret key to GLWE secret key.
43 | func (e *GLWETransformer[T]) InvFFTGLWESecretKey(sk tfhe.FFTGLWESecretKey[T]) tfhe.GLWESecretKey[T] {
44 | skOut := tfhe.NewGLWESecretKeyCustom[T](len(sk.Value), e.PolyEvaluator.Rank())
45 | e.InvFFTGLWESecretKeyTo(skOut, sk)
46 | return skOut
47 | }
48 |
49 | // InvFFTGLWESecretKeyTo transforms FFT GLWE secret key to GLWE secret key and writes it to skOut.
50 | func (e *GLWETransformer[T]) InvFFTGLWESecretKeyTo(skOut tfhe.GLWESecretKey[T], sk tfhe.FFTGLWESecretKey[T]) {
51 | for i := 0; i < len(sk.Value); i++ {
52 | e.PolyEvaluator.InvFFTTo(skOut.Value[i], sk.Value[i])
53 | }
54 | }
55 |
56 | // FwdFFTGLWECiphertext transforms GLWE ciphertext to FFT GLWE ciphertext.
57 | func (e *GLWETransformer[T]) FwdFFTGLWECiphertext(ct GLWECiphertext[T]) FFTGLWECiphertext[T] {
58 | ctOut := NewFFTGLWECiphertextCustom[T](len(ct.Value)-1, e.PolyEvaluator.Rank())
59 | e.FwdFFTGLWECiphertextTo(ctOut, ct)
60 | return ctOut
61 | }
62 |
63 | // FwdFFTGLWECiphertextTo transforms GLWE ciphertext to FFT GLWE ciphertext and writes it to ctOut.
64 | func (e *GLWETransformer[T]) FwdFFTGLWECiphertextTo(ctOut FFTGLWECiphertext[T], ct GLWECiphertext[T]) {
65 | for i := 0; i < len(ct.Value); i++ {
66 | e.PolyEvaluator.FwdFFTPolyTo(ctOut.Value[i], ct.Value[i])
67 | }
68 | }
69 |
70 | // InvGLWECiphertext transforms FFT GLWE ciphertext to GLWE ciphertext.
71 | func (e *GLWETransformer[T]) InvGLWECiphertext(ct FFTGLWECiphertext[T]) GLWECiphertext[T] {
72 | ctOut := NewGLWECiphertextCustom[T](len(ct.Value)-1, e.PolyEvaluator.Rank())
73 | e.InvGLWECiphertextTo(ctOut, ct)
74 | return ctOut
75 | }
76 |
77 | // InvGLWECiphertextTo transforms FFT GLWE ciphertext to GLWE ciphertext and writes it to ctOut.
78 | func (e *GLWETransformer[T]) InvGLWECiphertextTo(ctOut GLWECiphertext[T], ct FFTGLWECiphertext[T]) {
79 | for i := 0; i < len(ct.Value); i++ {
80 | e.PolyEvaluator.InvFFTTo(ctOut.Value[i], ct.Value[i])
81 | }
82 | }
83 |
84 | // FwdFFTUniEncryption transforms UniEncryption to FFT UniEncryption.
85 | func (e *GLWETransformer[T]) FwdFFTUniEncryption(ct UniEncryption[T]) FFTUniEncryption[T] {
86 | ctOut := NewFFTUniEncryptionCustom(e.PolyEvaluator.Rank(), ct.GadgetParams)
87 | e.FwdFFTUniEncryptionTo(ctOut, ct)
88 | return ctOut
89 | }
90 |
91 | // FwdFFTUniEncryptionTo transforms UniEncryption to FFT UniEncryption and writes it to ctOut.
92 | func (e *GLWETransformer[T]) FwdFFTUniEncryptionTo(ctOut FFTUniEncryption[T], ct UniEncryption[T]) {
93 | for j := 0; j < ct.GadgetParams.Level(); j++ {
94 | e.PolyEvaluator.FwdFFTPolyTo(ctOut.Value[0].Value[j].Value[0], ct.Value[0].Value[j].Value[0])
95 | e.PolyEvaluator.FwdFFTPolyTo(ctOut.Value[0].Value[j].Value[1], ct.Value[0].Value[j].Value[1])
96 |
97 | e.PolyEvaluator.FwdFFTPolyTo(ctOut.Value[1].Value[j].Value[0], ct.Value[1].Value[j].Value[0])
98 | e.PolyEvaluator.FwdFFTPolyTo(ctOut.Value[1].Value[j].Value[1], ct.Value[1].Value[j].Value[1])
99 | }
100 | }
101 |
102 | // InvFFTUniEncryption transforms FFT UniEncryption to UniEncryption.
103 | func (e *GLWETransformer[T]) InvFFTUniEncryption(ct FFTUniEncryption[T]) UniEncryption[T] {
104 | ctOut := NewUniEncryptionCustom(e.PolyEvaluator.Rank(), ct.GadgetParams)
105 | e.InvFFTUniEncryptionTo(ctOut, ct)
106 | return ctOut
107 | }
108 |
109 | // InvFFTUniEncryptionTo transforms FFT UniEncryption to UniEncryption and writes it to ctOut.
110 | func (e *GLWETransformer[T]) InvFFTUniEncryptionTo(ctOut UniEncryption[T], ct FFTUniEncryption[T]) {
111 | for j := 0; j < ct.GadgetParams.Level(); j++ {
112 | e.PolyEvaluator.InvFFTTo(ctOut.Value[0].Value[j].Value[0], ct.Value[0].Value[j].Value[0])
113 | e.PolyEvaluator.InvFFTTo(ctOut.Value[0].Value[j].Value[1], ct.Value[0].Value[j].Value[1])
114 |
115 | e.PolyEvaluator.InvFFTTo(ctOut.Value[1].Value[j].Value[0], ct.Value[1].Value[j].Value[0])
116 | e.PolyEvaluator.InvFFTTo(ctOut.Value[1].Value[j].Value[1], ct.Value[1].Value[j].Value[1])
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/math/poly/asm_vec_cmplx_test.go:
--------------------------------------------------------------------------------
1 | package poly
2 |
3 | import (
4 | "math/cmplx"
5 | "math/rand"
6 | "testing"
7 |
8 | "github.com/sp301415/tfhe-go/math/vec"
9 | )
10 |
11 | func TestVecCmplxAssembly(t *testing.T) {
12 | r := rand.New(rand.NewSource(0))
13 |
14 | N := 1 << 10
15 | eps := 1e-10
16 |
17 | v0 := make([]complex128, N)
18 | v1 := make([]complex128, N)
19 | for i := 0; i < N; i++ {
20 | v0[i] = complex(r.Float64(), r.Float64())
21 | v1[i] = complex(r.Float64(), r.Float64())
22 | }
23 | v0Float4 := vec.CmplxToFloat4(v0)
24 | v1Float4 := vec.CmplxToFloat4(v1)
25 |
26 | vOut := make([]complex128, N)
27 | vOutAVX2 := make([]complex128, N)
28 | vOutAVX2Float4 := make([]float64, 2*N)
29 |
30 | t.Run("Add", func(t *testing.T) {
31 | vec.AddTo(vOut, v0, v1)
32 | addCmplxTo(vOutAVX2Float4, v0Float4, v1Float4)
33 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
34 | for i := 0; i < N; i++ {
35 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
36 | t.Fatalf("Add: %v != %v", vOut[i], vOutAVX2[i])
37 | }
38 | }
39 | })
40 |
41 | t.Run("Sub", func(t *testing.T) {
42 | vec.SubTo(vOut, v0, v1)
43 | subCmplxTo(vOutAVX2Float4, v0Float4, v1Float4)
44 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
45 | for i := 0; i < N; i++ {
46 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
47 | t.Fatalf("Sub: %v != %v", vOut[i], vOutAVX2[i])
48 | }
49 | }
50 | })
51 |
52 | t.Run("Neg", func(t *testing.T) {
53 | vec.NegTo(vOut, v0)
54 | negCmplxTo(vOutAVX2Float4, v0Float4)
55 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
56 | for i := 0; i < N; i++ {
57 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
58 | t.Fatalf("Neg: %v != %v", vOut[i], vOutAVX2[i])
59 | }
60 | }
61 | })
62 |
63 | t.Run("FloatMul", func(t *testing.T) {
64 | c := r.Float64()
65 | vec.ScalarMulTo(vOut, v0, complex(c, 0))
66 | floatMulCmplxTo(vOutAVX2Float4, v0Float4, c)
67 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
68 | for i := 0; i < N; i++ {
69 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
70 | t.Fatalf("FloatMul: %v != %v", vOut[i], vOutAVX2[i])
71 | }
72 | }
73 | })
74 |
75 | t.Run("FloatMulAdd", func(t *testing.T) {
76 | vec.Fill(vOut, 0)
77 | vec.Fill(vOutAVX2Float4, 0)
78 |
79 | c := r.Float64()
80 | vec.ScalarMulAddTo(vOut, v0, complex(c, 0))
81 | floatMulAddCmplxTo(vOutAVX2Float4, v0Float4, c)
82 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
83 | for i := 0; i < N; i++ {
84 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
85 | t.Fatalf("FloatMulAdd: %v != %v", vOut[i], vOutAVX2[i])
86 | }
87 | }
88 | })
89 |
90 | t.Run("FloatMulSub", func(t *testing.T) {
91 | vec.Fill(vOut, 0)
92 | vec.Fill(vOutAVX2Float4, 0)
93 |
94 | c := r.Float64()
95 | vec.ScalarMulSubTo(vOut, v0, complex(c, 0))
96 | floatMulSubCmplxTo(vOutAVX2Float4, v0Float4, c)
97 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
98 | for i := 0; i < N; i++ {
99 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
100 | t.Fatalf("FloatMulSub: %v != %v", vOut[i], vOutAVX2[i])
101 | }
102 | }
103 | })
104 |
105 | t.Run("CmplxMul", func(t *testing.T) {
106 | c := complex(r.Float64(), r.Float64())
107 | vec.ScalarMulTo(vOut, v0, c)
108 | cmplxMulCmplxTo(vOutAVX2Float4, v0Float4, c)
109 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
110 | for i := 0; i < N; i++ {
111 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
112 | t.Fatalf("CmplxMul: %v != %v", vOut[i], vOutAVX2[i])
113 | }
114 | }
115 | })
116 |
117 | t.Run("CmplxMulAdd", func(t *testing.T) {
118 | vec.Fill(vOut, 0)
119 | vec.Fill(vOutAVX2Float4, 0)
120 |
121 | c := complex(r.Float64(), r.Float64())
122 | vec.ScalarMulAddTo(vOut, v0, c)
123 | cmplxMulAddCmplxTo(vOutAVX2Float4, v0Float4, c)
124 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
125 | for i := 0; i < N; i++ {
126 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
127 | t.Fatalf("CmplxMulAdd: %v != %v", vOut[i], vOutAVX2[i])
128 | }
129 | }
130 | })
131 |
132 | t.Run("CmplxMulSub", func(t *testing.T) {
133 | vec.Fill(vOut, 0)
134 | vec.Fill(vOutAVX2Float4, 0)
135 |
136 | c := complex(r.Float64(), r.Float64())
137 | vec.ScalarMulSubTo(vOut, v0, c)
138 | cmplxMulSubCmplxTo(vOutAVX2Float4, v0Float4, c)
139 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
140 | for i := 0; i < N; i++ {
141 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
142 | t.Fatalf("CmplxMulSub: %v != %v", vOut[i], vOutAVX2[i])
143 | }
144 | }
145 | })
146 |
147 | t.Run("Mul", func(t *testing.T) {
148 | vec.MulTo(vOut, v0, v1)
149 | mulCmplxTo(vOutAVX2Float4, v0Float4, v1Float4)
150 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
151 | for i := 0; i < N; i++ {
152 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
153 | t.Fatalf("Mul: %v != %v", vOut[i], vOutAVX2[i])
154 | }
155 | }
156 | })
157 |
158 | t.Run("MulAdd", func(t *testing.T) {
159 | vec.Fill(vOut, 0)
160 | vec.Fill(vOutAVX2Float4, 0)
161 |
162 | vec.MulAddTo(vOut, v0, v1)
163 | mulAddCmplxTo(vOutAVX2Float4, v0Float4, v1Float4)
164 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
165 | for i := 0; i < N; i++ {
166 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
167 | t.Fatalf("MulAdd: %v != %v", vOut[i], vOutAVX2[i])
168 | }
169 | }
170 | })
171 |
172 | t.Run("MulSub", func(t *testing.T) {
173 | vec.Fill(vOut, 0)
174 | vec.Fill(vOutAVX2Float4, 0)
175 |
176 | vec.MulSubTo(vOut, v0, v1)
177 | mulSubCmplxTo(vOutAVX2Float4, v0Float4, v1Float4)
178 | vec.Float4ToCmplxTo(vOutAVX2, vOutAVX2Float4)
179 | for i := 0; i < N; i++ {
180 | if cmplx.Abs(vOut[i]-vOutAVX2[i]) > eps {
181 | t.Fatalf("MulSub: %v != %v", vOut[i], vOutAVX2[i])
182 | }
183 | }
184 | })
185 | }
186 |
--------------------------------------------------------------------------------
/tfhe/binary_tfhe_test.go:
--------------------------------------------------------------------------------
1 | package tfhe_test
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | "testing"
7 |
8 | "github.com/sp301415/tfhe-go/tfhe"
9 | "github.com/stretchr/testify/assert"
10 | )
11 |
12 | var (
13 | paramsBinary = tfhe.ParamsBinary.Compile()
14 | encBinary = tfhe.NewBinaryEncryptor(paramsBinary)
15 | evalBinary = tfhe.NewBinaryEvaluator(paramsBinary, encBinary.GenEvalKeyParallel())
16 | )
17 |
18 | func TestBinaryParams(t *testing.T) {
19 | t.Run("Compile/ParamsBinary", func(t *testing.T) {
20 | assert.NotPanics(t, func() { tfhe.ParamsBinary.Compile() })
21 | })
22 |
23 | t.Run("Compile/ParamsBinaryCompact", func(t *testing.T) {
24 | assert.NotPanics(t, func() { tfhe.ParamsBinaryCompact.Compile() })
25 | })
26 |
27 | t.Run("Compile/ParamsBinaryOriginal", func(t *testing.T) {
28 | assert.NotPanics(t, func() { tfhe.ParamsBinaryOriginal.Compile() })
29 | })
30 |
31 | t.Run("FailureProbability/ParamsBinary", func(t *testing.T) {
32 | params := tfhe.ParamsBinary.Compile()
33 |
34 | msStdDev := params.EstimateModSwitchStdDev()
35 | brStdDev := params.EstimateBlindRotateStdDev()
36 | ksStdDev := params.EstimateDefaultKeySwitchStdDev()
37 | maxErrorStdDev := math.Sqrt(msStdDev*msStdDev + 4*brStdDev*brStdDev + 4*ksStdDev*ksStdDev)
38 |
39 | bound := math.Exp2(float64(params.LogQ())) / 16
40 | failureProbability := math.Erfc(bound / (math.Sqrt2 * maxErrorStdDev))
41 | assert.LessOrEqual(t, math.Log2(failureProbability), -64.0)
42 | })
43 |
44 | t.Run("FailureProbability/ParamsBinaryCompact", func(t *testing.T) {
45 | params := tfhe.ParamsBinaryCompact.Compile()
46 |
47 | msStdDev := params.EstimateModSwitchStdDev()
48 | ksStdDev := params.EstimateDefaultKeySwitchStdDev()
49 | brStdDev := params.EstimateBlindRotateStdDev()
50 | maxErrorStdDev := math.Sqrt(msStdDev*msStdDev + ksStdDev*ksStdDev + 4*brStdDev*brStdDev)
51 |
52 | bound := math.Exp2(float64(params.LogQ())) / 16
53 | failureProbability := math.Erfc(bound / (math.Sqrt2 * maxErrorStdDev))
54 | assert.LessOrEqual(t, math.Log2(failureProbability), -64.0)
55 | })
56 | }
57 |
58 | func TestBinaryEvaluator(t *testing.T) {
59 | tests := []struct {
60 | pt0 bool
61 | pt1 bool
62 | ct0 tfhe.LWECiphertext[uint32]
63 | ct1 tfhe.LWECiphertext[uint32]
64 | }{
65 | {true, true, encBinary.EncryptLWEBool(true), encBinary.EncryptLWEBool(true)},
66 | {true, false, encBinary.EncryptLWEBool(true), encBinary.EncryptLWEBool(false)},
67 | {false, true, encBinary.EncryptLWEBool(false), encBinary.EncryptLWEBool(true)},
68 | {false, false, encBinary.EncryptLWEBool(false), encBinary.EncryptLWEBool(false)},
69 | }
70 |
71 | t.Run("AND", func(t *testing.T) {
72 | for _, tc := range tests {
73 | assert.Equal(t, tc.pt0 && tc.pt1, encBinary.DecryptLWEBool(evalBinary.AND(tc.ct0, tc.ct1)))
74 | }
75 | })
76 |
77 | t.Run("NAND", func(t *testing.T) {
78 | for _, tc := range tests {
79 | assert.Equal(t, !(tc.pt0 && tc.pt1), encBinary.DecryptLWEBool(evalBinary.NAND(tc.ct0, tc.ct1)))
80 | }
81 | })
82 |
83 | t.Run("OR", func(t *testing.T) {
84 | for _, tc := range tests {
85 | assert.Equal(t, tc.pt0 || tc.pt1, encBinary.DecryptLWEBool(evalBinary.OR(tc.ct0, tc.ct1)))
86 | }
87 | })
88 |
89 | t.Run("NOR", func(t *testing.T) {
90 | for _, tc := range tests {
91 | assert.Equal(t, !(tc.pt0 || tc.pt1), encBinary.DecryptLWEBool(evalBinary.NOR(tc.ct0, tc.ct1)))
92 | }
93 | })
94 |
95 | t.Run("XOR", func(t *testing.T) {
96 | for _, tc := range tests {
97 | assert.Equal(t, tc.pt0 != tc.pt1, encBinary.DecryptLWEBool(evalBinary.XOR(tc.ct0, tc.ct1)))
98 | }
99 | })
100 |
101 | t.Run("XNOR", func(t *testing.T) {
102 | for _, tc := range tests {
103 | assert.Equal(t, tc.pt0 == tc.pt1, encBinary.DecryptLWEBool(evalBinary.XNOR(tc.ct0, tc.ct1)))
104 | }
105 | })
106 |
107 | t.Run("Bits", func(t *testing.T) {
108 | msg0, msg1 := 0b01, 0b10
109 | ct0 := encBinary.EncryptLWEBits(msg0, 4)
110 | ct1 := encBinary.EncryptLWEBits(msg1, 4)
111 |
112 | ctOut := encBinary.EncryptLWEBits(0, 4)
113 | for i := range ctOut {
114 | evalBinary.XORTo(ctOut[i], ct0[i], ct1[i])
115 | }
116 |
117 | assert.Equal(t, encBinary.DecryptLWEBits(ctOut), msg0^msg1)
118 | })
119 |
120 | t.Run("BootstrapOriginal", func(t *testing.T) {
121 | paramsBinaryOriginal := paramsBinary.Literal().WithBlockSize(1).Compile()
122 |
123 | originalEncryptor := tfhe.NewBinaryEncryptorWithKey(paramsBinaryOriginal, encBinary.Encryptor.SecretKey)
124 | originalEvaluator := tfhe.NewBinaryEvaluator(paramsBinaryOriginal, evalBinary.Evaluator.EvalKey)
125 |
126 | for _, tc := range tests {
127 | assert.Equal(t, tc.pt0 && tc.pt1, originalEncryptor.DecryptLWEBool(originalEvaluator.AND(tc.ct0, tc.ct1)))
128 | }
129 | })
130 | }
131 |
132 | func BenchmarkGateBootstrap(b *testing.B) {
133 | ct0 := encBinary.EncryptLWEBool(true)
134 | ct1 := encBinary.EncryptLWEBool(false)
135 | ctOut := tfhe.NewLWECiphertext(paramsBinary)
136 | b.ResetTimer()
137 |
138 | for i := 0; i < b.N; i++ {
139 | evalBinary.ANDTo(ctOut, ct0, ct1)
140 | }
141 | }
142 |
143 | func ExampleBinaryEvaluator() {
144 | params := tfhe.ParamsBinary.Compile()
145 |
146 | enc := tfhe.NewBinaryEncryptor(params)
147 |
148 | bits := 16
149 | ct0 := enc.EncryptLWEBits(3, bits)
150 | ct1 := enc.EncryptLWEBits(3, bits)
151 |
152 | eval := tfhe.NewBinaryEvaluator(params, enc.GenEvalKeyParallel())
153 |
154 | ctXNOR := tfhe.NewLWECiphertext(params)
155 | ctOut := eval.XNOR(ct0[0], ct1[0])
156 | for i := 1; i < bits; i++ {
157 | eval.XNORTo(ctXNOR, ct0[i], ct1[i])
158 | eval.ANDTo(ctOut, ctXNOR, ctOut)
159 | }
160 |
161 | fmt.Println(enc.DecryptLWEBool(ctOut))
162 | // Output:
163 | // true
164 | }
165 |
--------------------------------------------------------------------------------
/tfhe/encryptor_key_marshal.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "io"
7 |
8 | "github.com/sp301415/tfhe-go/math/num"
9 | )
10 |
11 | // ByteSize returns the size of the key in bytes.
12 | func (sk SecretKey[T]) ByteSize() int {
13 | glweRank := len(sk.GLWEKey.Value)
14 | polyRank := sk.GLWEKey.Value[0].Rank()
15 |
16 | return 24 + glweRank*polyRank*num.ByteSizeT[T]() + glweRank*polyRank*8
17 | }
18 |
19 | // headerWriteTo writes the header.
20 | func (sk SecretKey[T]) headerWriteTo(w io.Writer) (n int64, err error) {
21 | var nWrite int
22 | var buf [8]byte
23 |
24 | lweDimension := len(sk.LWEKey.Value)
25 | binary.BigEndian.PutUint64(buf[:], uint64(lweDimension))
26 | if nWrite, err = w.Write(buf[:]); err != nil {
27 | return n + int64(nWrite), err
28 | }
29 | n += int64(nWrite)
30 |
31 | glweRank := len(sk.GLWEKey.Value)
32 | binary.BigEndian.PutUint64(buf[:], uint64(glweRank))
33 | if nWrite, err = w.Write(buf[:]); err != nil {
34 | return n + int64(nWrite), err
35 | }
36 | n += int64(nWrite)
37 |
38 | polyRank := sk.GLWEKey.Value[0].Rank()
39 | binary.BigEndian.PutUint64(buf[:], uint64(polyRank))
40 | if nWrite, err = w.Write(buf[:]); err != nil {
41 | return n + int64(nWrite), err
42 | }
43 | n += int64(nWrite)
44 |
45 | return
46 | }
47 |
48 | // WriteTo implements the [io.WriterTo] interface.
49 | //
50 | // The encoded form is as follows:
51 | //
52 | // [8] LWEDimension
53 | // [8] GLWERank
54 | // [8] PolyRank
55 | // LWELargeKey
56 | // FFTGLWEKey
57 | func (sk SecretKey[T]) WriteTo(w io.Writer) (n int64, err error) {
58 | var nWrite int64
59 |
60 | if nWrite, err = sk.headerWriteTo(w); err != nil {
61 | return n + nWrite, err
62 | }
63 | n += nWrite
64 |
65 | if nWrite, err = sk.LWELargeKey.valueWriteTo(w); err != nil {
66 | return n + nWrite, err
67 | }
68 | n += nWrite
69 |
70 | if nWrite, err = sk.FFTGLWEKey.valueWriteTo(w); err != nil {
71 | return n + nWrite, err
72 | }
73 | n += nWrite
74 |
75 | if n < int64(sk.ByteSize()) {
76 | return n, io.ErrShortWrite
77 | }
78 |
79 | return
80 | }
81 |
82 | // headerReadFrom reads the header, and initializes the value.
83 | func (sk *SecretKey[T]) headerReadFrom(r io.Reader) (n int64, err error) {
84 | var nRead int
85 | var buf [8]byte
86 |
87 | if nRead, err = io.ReadFull(r, buf[:]); err != nil {
88 | return n + int64(nRead), err
89 | }
90 | n += int64(nRead)
91 | lweDimension := int(binary.BigEndian.Uint64(buf[:]))
92 |
93 | if nRead, err = io.ReadFull(r, buf[:]); err != nil {
94 | return n + int64(nRead), err
95 | }
96 | n += int64(nRead)
97 | glweRank := int(binary.BigEndian.Uint64(buf[:]))
98 |
99 | if nRead, err = io.ReadFull(r, buf[:]); err != nil {
100 | return n + int64(nRead), err
101 | }
102 | n += int64(nRead)
103 | polyRank := int(binary.BigEndian.Uint64(buf[:]))
104 |
105 | *sk = NewSecretKeyCustom[T](lweDimension, glweRank, polyRank)
106 |
107 | return
108 | }
109 |
110 | // ReadFrom implements the [io.ReaderFrom] interface.
111 | func (sk *SecretKey[T]) ReadFrom(r io.Reader) (n int64, err error) {
112 | var nRead int64
113 |
114 | if nRead, err = sk.headerReadFrom(r); err != nil {
115 | return n + nRead, err
116 | }
117 | n += nRead
118 |
119 | if nRead, err = sk.LWELargeKey.valueReadFrom(r); err != nil {
120 | return n + nRead, err
121 | }
122 | n += nRead
123 |
124 | if nRead, err = sk.FFTGLWEKey.valueReadFrom(r); err != nil {
125 | return n + nRead, err
126 | }
127 | n += nRead
128 |
129 | return
130 | }
131 |
132 | // MarshalBinary implements the [encoding.BinaryMarshaler] interface.
133 | func (sk SecretKey[T]) MarshalBinary() (data []byte, err error) {
134 | buf := bytes.NewBuffer(make([]byte, 0, sk.ByteSize()))
135 | _, err = sk.WriteTo(buf)
136 | return buf.Bytes(), err
137 | }
138 |
139 | // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.
140 | func (sk *SecretKey[T]) UnmarshalBinary(data []byte) error {
141 | buf := bytes.NewBuffer(data)
142 | _, err := sk.ReadFrom(buf)
143 | return err
144 | }
145 |
146 | // ByteSize returns the size of the key in bytes.
147 | func (pk PublicKey[T]) ByteSize() int {
148 | return pk.LWEKey.ByteSize() + pk.GLWEKey.ByteSize()
149 | }
150 |
151 | // WriteTo implements the [io.WriterTo] interface.
152 | //
153 | // The encoded form is as follows:
154 | //
155 | // LWEKey
156 | // GLWEKey
157 | func (pk PublicKey[T]) WriteTo(w io.Writer) (n int64, err error) {
158 | var nWrite int64
159 |
160 | if nWrite, err = pk.LWEKey.WriteTo(w); err != nil {
161 | return n + nWrite, err
162 | }
163 | n += nWrite
164 |
165 | if nWrite, err = pk.GLWEKey.WriteTo(w); err != nil {
166 | return n + nWrite, err
167 | }
168 | n += nWrite
169 |
170 | if n < int64(pk.ByteSize()) {
171 | return n, io.ErrShortWrite
172 | }
173 |
174 | return
175 | }
176 |
177 | // ReadFrom implements the [io.ReaderFrom] interface.
178 | func (pk *PublicKey[T]) ReadFrom(r io.Reader) (n int64, err error) {
179 | var nRead int64
180 |
181 | if nRead, err = pk.LWEKey.ReadFrom(r); err != nil {
182 | return n + nRead, err
183 | }
184 | n += nRead
185 |
186 | if nRead, err = pk.GLWEKey.ReadFrom(r); err != nil {
187 | return n + nRead, err
188 | }
189 | n += nRead
190 |
191 | return
192 | }
193 |
194 | // MarshalBinary implements the [encoding.BinaryMarshaler] interface.
195 | func (pk PublicKey[T]) MarshalBinary() (data []byte, err error) {
196 | buf := bytes.NewBuffer(make([]byte, 0, pk.ByteSize()))
197 | _, err = pk.WriteTo(buf)
198 | return buf.Bytes(), err
199 | }
200 |
201 | // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.
202 | func (pk *PublicKey[T]) UnmarshalBinary(data []byte) error {
203 | buf := bytes.NewBuffer(data)
204 | _, err := pk.ReadFrom(buf)
205 | return err
206 | }
207 |
--------------------------------------------------------------------------------
/xtfhe/fhew_bootstrap_keygen.go:
--------------------------------------------------------------------------------
1 | package xtfhe
2 |
3 | import (
4 | "runtime"
5 | "sync"
6 |
7 | "github.com/sp301415/tfhe-go/math/num"
8 | "github.com/sp301415/tfhe-go/tfhe"
9 | )
10 |
11 | // EvaluationKey is a public key for FHEW Evaluator.
12 | type FHEWEvaluationKey[T tfhe.TorusInt] struct {
13 | // BlindRotateKey is the key for blind rotation.
14 | BlindRotateKey tfhe.BlindRotateKey[T]
15 | // KeySwitchKey is the key for key switching.
16 | KeySwitchKey tfhe.LWEKeySwitchKey[T]
17 | // GaloisKey is the key for Galois automorphisms.
18 | // GaloisKey has length WindowSize + 1,
19 | // where the first element is key for X -> X^-5,
20 | // and the next WindowSize elements are keys for X -> X^5^i.
21 | GaloisKey []tfhe.GLWEKeySwitchKey[T]
22 | }
23 |
24 | // GenEvalKey samples a new evaluation key for bootstrapping.
25 | //
26 | // This can take a long time.
27 | // Use [*FHEWEncryptor.GenEvalKeyParallel] for better key generation performance.
28 | func (e *FHEWEncryptor[T]) GenEvalKey() FHEWEvaluationKey[T] {
29 | return FHEWEvaluationKey[T]{
30 | BlindRotateKey: e.GenBlindRotateKey(),
31 | KeySwitchKey: e.GenDefaultKeySwitchKey(),
32 | GaloisKey: e.GenGaloisKey(),
33 | }
34 | }
35 |
36 | // GenEvalKeyParallel samples a new evaluation key for bootstrapping in parallel.
37 | func (e *FHEWEncryptor[T]) GenEvalKeyParallel() FHEWEvaluationKey[T] {
38 | return FHEWEvaluationKey[T]{
39 | BlindRotateKey: e.GenBlindRotateKeyParallel(),
40 | KeySwitchKey: e.GenDefaultKeySwitchKeyParallel(),
41 | GaloisKey: e.GenGaloisKey(),
42 | }
43 | }
44 |
45 | // GenBlindRotateKey samples a new bootstrapping key.
46 | //
47 | // This can take a long time.
48 | // Use [*FHEWEncryptor.GenBlindRotateKeyParallel] for better key generation performance.
49 | func (e *FHEWEncryptor[T]) GenBlindRotateKey() tfhe.BlindRotateKey[T] {
50 | brk := tfhe.NewBlindRotateKey(e.Params.baseParams)
51 |
52 | for i := 0; i < e.Params.baseParams.LWEDimension(); i++ {
53 | var sMonoIdx int
54 |
55 | var z T
56 | switch any(z).(type) {
57 | case uint32:
58 | sMonoIdx = int(int32(e.SecretKey.LWEKey.Value[i]))
59 | case uint64:
60 | sMonoIdx = int(int64(e.SecretKey.LWEKey.Value[i]))
61 | }
62 |
63 | for j := 0; j < e.Params.baseParams.GLWERank()+1; j++ {
64 | if j == 0 {
65 | e.buf.ptGGSW.Clear()
66 | e.buf.ptGGSW.Coeffs[0] = 1
67 | e.PolyEvaluator.MonomialMulPolyInPlace(e.buf.ptGGSW, sMonoIdx)
68 | } else {
69 | e.PolyEvaluator.MonomialMulPolyTo(e.buf.ptGGSW, e.SecretKey.GLWEKey.Value[j-1], sMonoIdx)
70 | }
71 | for k := 0; k < e.Params.baseParams.BlindRotateParams().Level(); k++ {
72 | e.PolyEvaluator.ScalarMulPolyTo(e.buf.ctGLWE.Value[0], e.buf.ptGGSW, e.Params.baseParams.BlindRotateParams().BaseQ(k))
73 | e.EncryptGLWEBody(e.buf.ctGLWE)
74 | e.FwdFFTGLWECiphertextTo(brk.Value[i].Value[j].Value[k], e.buf.ctGLWE)
75 | }
76 | }
77 | }
78 |
79 | return brk
80 | }
81 |
82 | // GenBlindRotateKeyParallel samples a new bootstrapping key in parallel.
83 | func (e *FHEWEncryptor[T]) GenBlindRotateKeyParallel() tfhe.BlindRotateKey[T] {
84 | brk := tfhe.NewBlindRotateKey(e.Params.baseParams)
85 |
86 | workSize := e.Params.baseParams.LWEDimension() * (e.Params.baseParams.GLWERank() + 1)
87 | chunkCount := num.Min(runtime.NumCPU(), num.Sqrt(workSize))
88 |
89 | encryptorPool := make([]*FHEWEncryptor[T], chunkCount)
90 | for i := range encryptorPool {
91 | encryptorPool[i] = e.SafeCopy()
92 | }
93 |
94 | jobs := make(chan [2]int)
95 | go func() {
96 | defer close(jobs)
97 | for i := 0; i < e.Params.baseParams.LWEDimension(); i++ {
98 | for j := 0; j < e.Params.baseParams.GLWERank()+1; j++ {
99 | jobs <- [2]int{i, j}
100 | }
101 | }
102 | }()
103 |
104 | var wg sync.WaitGroup
105 | wg.Add(chunkCount)
106 | for i := 0; i < chunkCount; i++ {
107 | go func(i int) {
108 | eIdx := encryptorPool[i]
109 | for job := range jobs {
110 | i, j := job[0], job[1]
111 |
112 | var sMonoIdx int
113 |
114 | var z T
115 | switch any(z).(type) {
116 | case uint32:
117 | sMonoIdx = int(int32(eIdx.SecretKey.LWEKey.Value[i]))
118 | case uint64:
119 | sMonoIdx = int(int64(eIdx.SecretKey.LWEKey.Value[i]))
120 | }
121 |
122 | if j == 0 {
123 | eIdx.buf.ptGGSW.Clear()
124 | eIdx.buf.ptGGSW.Coeffs[0] = 1
125 | eIdx.PolyEvaluator.MonomialMulPolyInPlace(eIdx.buf.ptGGSW, sMonoIdx)
126 | } else {
127 | eIdx.PolyEvaluator.MonomialMulPolyTo(eIdx.buf.ptGGSW, eIdx.SecretKey.GLWEKey.Value[j-1], sMonoIdx)
128 | }
129 | for k := 0; k < eIdx.Params.baseParams.BlindRotateParams().Level(); k++ {
130 | eIdx.PolyEvaluator.ScalarMulPolyTo(eIdx.buf.ctGLWE.Value[0], eIdx.buf.ptGGSW, eIdx.Params.baseParams.BlindRotateParams().BaseQ(k))
131 | eIdx.EncryptGLWEBody(eIdx.buf.ctGLWE)
132 | eIdx.FwdFFTGLWECiphertextTo(brk.Value[i].Value[j].Value[k], eIdx.buf.ctGLWE)
133 | }
134 | }
135 | wg.Done()
136 | }(i)
137 | }
138 | wg.Wait()
139 |
140 | return brk
141 | }
142 |
143 | // GenGaloisKey samples a new Galois key for bootstrapping.
144 | func (e *FHEWEncryptor[T]) GenGaloisKey() []tfhe.GLWEKeySwitchKey[T] {
145 | glk := make([]tfhe.GLWEKeySwitchKey[T], e.Params.windowSize+1)
146 |
147 | for j := 0; j < e.Params.baseParams.GLWERank(); j++ {
148 | e.PolyEvaluator.PermutePolyTo(e.buf.skPermute.Value[j], e.SecretKey.GLWEKey.Value[j], -5)
149 | }
150 | glk[0] = e.GenGLWEKeySwitchKey(e.buf.skPermute, e.Params.baseParams.BlindRotateParams())
151 |
152 | for i := 1; i < e.Params.windowSize+1; i++ {
153 | d := num.ModExp(5, i, 2*e.Params.baseParams.PolyRank())
154 | for j := 0; j < e.Params.baseParams.GLWERank(); j++ {
155 | e.PolyEvaluator.PermutePolyTo(e.buf.skPermute.Value[j], e.SecretKey.GLWEKey.Value[j], d)
156 | }
157 | glk[i] = e.GenGLWEKeySwitchKey(e.buf.skPermute, e.Params.baseParams.BlindRotateParams())
158 | }
159 |
160 | return glk
161 | }
162 |
--------------------------------------------------------------------------------
/tfhe/encryptor_key.go:
--------------------------------------------------------------------------------
1 | package tfhe
2 |
3 | import (
4 | "github.com/sp301415/tfhe-go/math/poly"
5 | "github.com/sp301415/tfhe-go/math/vec"
6 | )
7 |
8 | // SecretKey is a structure containing LWE and GLWE key.
9 | // All keys should be treated as read-only.
10 | // Changing them mid-operation will usually result in wrong results.
11 | //
12 | // LWEKey and GLWEKey is sampled together, as explained in https://eprint.iacr.org/2023/958.
13 | // As a result, LWEKey and GLWEKey share the same backing slice, so modifying one will affect the other.
14 | type SecretKey[T TorusInt] struct {
15 | // LWELargeKey is a LWE key with length GLWEDimension.
16 | // Essentially, this is same as GLWEKey but parsed differently.
17 | LWELargeKey LWESecretKey[T]
18 | // GLWEKey is a key used for GLWE encryption and decryption.
19 | // Essentially, this is same as LWEKey but parsed differently.
20 | GLWEKey GLWESecretKey[T]
21 | // FFTGLWEKey is a fourier transformed GLWEKey.
22 | // Used for GLWE encryption.
23 | FFTGLWEKey FFTGLWESecretKey[T]
24 | // LWEKey is a LWE key with length LWEDimension.
25 | // Essentially, this is the first LWEDimension elements of LWEKey.
26 | LWEKey LWESecretKey[T]
27 | }
28 |
29 | // NewSecretKey creates a new SecretKey.
30 | // Each key shares the same backing slice, held by LWEKey.
31 | func NewSecretKey[T TorusInt](params Parameters[T]) SecretKey[T] {
32 | lweLargeKey := LWESecretKey[T]{Value: make([]T, params.glweDimension)}
33 |
34 | glweKey := GLWESecretKey[T]{Value: make([]poly.Poly[T], params.glweRank)}
35 | for i := 0; i < params.glweRank; i++ {
36 | glweKey.Value[i].Coeffs = lweLargeKey.Value[i*params.polyRank : (i+1)*params.polyRank]
37 | }
38 | FFTGLWEKey := NewFFTGLWESecretKey(params)
39 |
40 | lweKey := LWESecretKey[T]{Value: lweLargeKey.Value[:params.lweDimension]}
41 |
42 | return SecretKey[T]{
43 | LWELargeKey: lweLargeKey,
44 | GLWEKey: glweKey,
45 | FFTGLWEKey: FFTGLWEKey,
46 | LWEKey: lweKey,
47 | }
48 | }
49 |
50 | // NewSecretKeyCustom creates a new SecretKey with given dimension and polyRank.
51 | // Each key shares the same backing slice, held by LWEKey.
52 | func NewSecretKeyCustom[T TorusInt](lweDimension, glweRank, polyRank int) SecretKey[T] {
53 | lweLargeKey := LWESecretKey[T]{Value: make([]T, glweRank*polyRank)}
54 |
55 | glweKey := GLWESecretKey[T]{Value: make([]poly.Poly[T], glweRank)}
56 | for i := 0; i < glweRank; i++ {
57 | glweKey.Value[i].Coeffs = lweLargeKey.Value[i*polyRank : (i+1)*polyRank]
58 | }
59 | FFTGLWEKey := NewFFTGLWESecretKeyCustom[T](glweRank, polyRank)
60 |
61 | lweKey := LWESecretKey[T]{Value: lweLargeKey.Value[:lweDimension]}
62 |
63 | return SecretKey[T]{
64 | LWELargeKey: lweLargeKey,
65 | GLWEKey: glweKey,
66 | FFTGLWEKey: FFTGLWEKey,
67 | LWEKey: lweKey,
68 | }
69 | }
70 |
71 | // Copy returns a copy of the key.
72 | func (sk SecretKey[T]) Copy() SecretKey[T] {
73 | lweLargeKey := sk.LWELargeKey.Copy()
74 |
75 | glweKey := GLWESecretKey[T]{Value: make([]poly.Poly[T], len(sk.GLWEKey.Value))}
76 | for i := range glweKey.Value {
77 | polyRank := sk.GLWEKey.Value[i].Rank()
78 | glweKey.Value[i].Coeffs = lweLargeKey.Value[i*polyRank : (i+1)*polyRank]
79 | }
80 | FFTGLWEKey := sk.FFTGLWEKey.Copy()
81 |
82 | lweKey := LWESecretKey[T]{Value: lweLargeKey.Value[:len(sk.LWEKey.Value)]}
83 |
84 | return SecretKey[T]{
85 | LWELargeKey: lweLargeKey,
86 | GLWEKey: glweKey,
87 | FFTGLWEKey: FFTGLWEKey,
88 | LWEKey: lweKey,
89 | }
90 | }
91 |
92 | // CopyFrom copies values from the key.
93 | func (sk *SecretKey[T]) CopyFrom(skIn SecretKey[T]) {
94 | copy(sk.LWELargeKey.Value, skIn.LWELargeKey.Value)
95 | sk.FFTGLWEKey.CopyFrom(skIn.FFTGLWEKey)
96 | }
97 |
98 | // Clear clears the key.
99 | func (sk *SecretKey[T]) Clear() {
100 | vec.Fill(sk.LWELargeKey.Value, 0)
101 | sk.FFTGLWEKey.Clear()
102 | }
103 |
104 | // PublicKey is a structure containing LWE and GLWE public key.
105 | // All keys should be treated as read-only.
106 | // Changing them mid-operation will usually result in wrong computation.
107 | //
108 | // We use compact public key, explained in https://eprint.iacr.org/2023/603.
109 | // This means that not all parameters support public key encryption.
110 | type PublicKey[T TorusInt] struct {
111 | // LWEKey is a public key used for LWE encryption.
112 | // It is essentially a GLWE encryption of zero, but with reversed GLWE key,
113 | // as explained in https://eprint.iacr.org/2023/603.
114 | LWEKey LWEPublicKey[T]
115 |
116 | // GLWEKey is a public key used for LWE and GLWE encryption.
117 | // It is essentially a GLWE encryption of zero.
118 | GLWEKey GLWEPublicKey[T]
119 | }
120 |
121 | // NewPublicKey creates a new PublicKey.
122 | //
123 | // Panics when the parameters do not support public key encryption.
124 | func NewPublicKey[T TorusInt](params Parameters[T]) PublicKey[T] {
125 | if !params.IsPublicKeyEncryptable() {
126 | panic("Parameters do not support public key encryption")
127 | }
128 |
129 | return PublicKey[T]{
130 | LWEKey: NewLWEPublicKey(params),
131 | GLWEKey: NewGLWEPublicKey(params),
132 | }
133 | }
134 |
135 | // NewPublicKeyCustom creates a new PublicKey with given dimension and polyRank.
136 | func NewPublicKeyCustom[T TorusInt](glweRank, polyRank int) PublicKey[T] {
137 | return PublicKey[T]{
138 | LWEKey: NewLWEPublicKeyCustom[T](glweRank, polyRank),
139 | GLWEKey: NewGLWEPublicKeyCustom[T](glweRank, polyRank),
140 | }
141 | }
142 |
143 | // Copy returns a copy of the key.
144 | func (pk PublicKey[T]) Copy() PublicKey[T] {
145 | return PublicKey[T]{
146 | LWEKey: pk.LWEKey.Copy(),
147 | GLWEKey: pk.GLWEKey.Copy(),
148 | }
149 | }
150 |
151 | // CopyFrom copies values from the key.
152 | func (pk *PublicKey[T]) CopyFrom(pkIn PublicKey[T]) {
153 | pk.LWEKey.CopyFrom(pkIn.LWEKey)
154 | pk.GLWEKey.CopyFrom(pkIn.GLWEKey)
155 | }
156 |
157 | // Clear clears the key.
158 | func (pk *PublicKey[T]) Clear() {
159 | pk.LWEKey.Clear()
160 | pk.GLWEKey.Clear()
161 | }
162 |
--------------------------------------------------------------------------------