From 3de057efbe4a629b41a264d5defffc863f5656fb Mon Sep 17 00:00:00 2001 From: Christian Grigis Date: Wed, 27 Nov 2019 16:19:15 +0100 Subject: [PATCH] Added evaluator and encryptor contexts for ckks --- bfv/bfv_benchmark_test.go | 9 +- bfv/bfv_test.go | 14 +- bfv/ciphertext.go | 12 ++ ckks/algorithms.go | 14 +- ckks/chebyshev_evaluation.go | 6 +- ckks/ciphertext.go | 56 +++++- ckks/ckks_test.go | 16 +- ckks/encryptor.go | 193 +++++++++++++++---- ckks/evaluator.go | 338 +++++++++++++++++++++++---------- ckks/operand.go | 4 +- ckks/polynomial_evaluation.go | 8 +- dbfv/dbfv_benchmark_test.go | 9 +- dbfv/dbfv_test.go | 35 +--- dckks/dckks_benchmark_test.go | 4 +- dckks/dckks_test.go | 18 +- examples/ckks/examples_ckks.go | 4 +- examples/dbfv/pir/pir.go | 7 +- examples/dbfv/psi/psi.go | 7 +- 18 files changed, 517 insertions(+), 237 deletions(-) diff --git a/bfv/bfv_benchmark_test.go b/bfv/bfv_benchmark_test.go index b44d5b49..7066fb60 100755 --- a/bfv/bfv_benchmark_test.go +++ b/bfv/bfv_benchmark_test.go @@ -3,8 +3,6 @@ package bfv import ( "fmt" "testing" - - "github.com/ldsec/lattigo/ring" ) func Benchmark_BFV(b *testing.B) { @@ -40,12 +38,7 @@ func Benchmark_BFV(b *testing.B) { pt := bfvContext.NewPlaintext() pt.setCoefficientsUint64(bfvContext, ptcoeffs) - ringCtx := ring.NewContext() - ringCtx.SetParameters(params.N, params.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := NewCiphertextRingContext(¶ms) ctd1 := NewCiphertext(1, ringCtx) b.Run(testString("EncryptFromPk", ¶ms), func(b *testing.B) { diff --git a/bfv/bfv_test.go b/bfv/bfv_test.go index 98e50e43..c40e3716 100644 --- a/bfv/bfv_test.go +++ b/bfv/bfv_test.go @@ -426,12 +426,7 @@ func testEvaluatorSub(t *testing.T) { func testEvaluatorMul(t *testing.T) { for _, parameters := range testParams.bfvParameters { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := NewCiphertextRingContext(parameters) params := genBfvParams(parameters) @@ -543,12 +538,7 @@ func testRotateRows(t *testing.T) { func testRotateCols(t *testing.T) { for _, parameters := range testParams.bfvParameters { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := NewCiphertextRingContext(parameters) params := genBfvParams(parameters) diff --git a/bfv/ciphertext.go b/bfv/ciphertext.go index b396bcd7..09b524c3 100644 --- a/bfv/ciphertext.go +++ b/bfv/ciphertext.go @@ -14,6 +14,18 @@ func NewCiphertextStruct() (ciphertext *Ciphertext) { return &Ciphertext{&bfvElement{}} } +func NewCiphertextRingContext(params *Parameters) *ring.Context { + ringCtx := ring.NewContext() + ringCtx.SetParameters(params.N, params.Qi) + + err := ringCtx.GenNTTParams() + if err != nil { + panic(err) + } + + return ringCtx +} + // NewCiphertext creates a new empty ciphertext of degree degree. func NewCiphertext(degree uint64, ringCtx *ring.Context) *Ciphertext { ciphertext := &Ciphertext{&bfvElement{}} diff --git a/ckks/algorithms.go b/ckks/algorithms.go index 42e9aca3..a043ea6c 100644 --- a/ckks/algorithms.go +++ b/ckks/algorithms.go @@ -19,13 +19,13 @@ func (evaluator *Evaluator) PowerOf2(el0 *Ciphertext, logPow2 uint64, evakey *Ev evaluator.MulRelin(el0.Element(), el0.Element(), evakey, elOut) - evaluator.Rescale(elOut, evaluator.ckkscontext.scale, elOut) + evaluator.Rescale(elOut, evaluator.context.scale, elOut) for i := uint64(1); i < logPow2; i++ { evaluator.MulRelin(elOut.Element(), elOut.Element(), evakey, elOut) - evaluator.Rescale(elOut, evaluator.ckkscontext.scale, elOut) + evaluator.Rescale(elOut, evaluator.context.scale, elOut) } } } @@ -33,7 +33,7 @@ func (evaluator *Evaluator) PowerOf2(el0 *Ciphertext, logPow2 uint64, evakey *Ev // PowerNew compute ct0^degree, consuming log(degree) levels, and returns the result on a new element. Providing an evaluation // key is necessary when degree > 2. func (evaluator *Evaluator) PowerNew(op *Ciphertext, degree uint64, evakey *EvaluationKey) (opOut *Ciphertext) { - opOut = evaluator.ckkscontext.NewCiphertext(1, op.Level(), op.Scale()) + opOut = NewCiphertext(1, op.Level(), op.Scale(), evaluator.context.contextQ) evaluator.Power(op, degree, evakey, opOut) return } @@ -58,13 +58,13 @@ func (evaluator *Evaluator) Power(ct0 *Ciphertext, degree uint64, evakey *Evalua logDegree = uint64(bits.Len64(degree)) - 1 po2Degree = 1 << logDegree - tmp := evaluator.ckkscontext.NewCiphertext(1, tmpct0.Level(), tmpct0.Scale()) + tmp := NewCiphertext(1, tmpct0.Level(), tmpct0.Scale(), evaluator.context.contextQ) evaluator.PowerOf2(tmpct0.Ciphertext(), logDegree, evakey, tmp) evaluator.MulRelin(res.Element(), tmp.Element(), evakey, res) - evaluator.Rescale(res, evaluator.ckkscontext.scale, res) + evaluator.Rescale(res, evaluator.context.scale, res) degree -= po2Degree } @@ -85,13 +85,13 @@ func (evaluator *Evaluator) InverseNew(ct0 *Ciphertext, steps uint64, evakey *Ev evaluator.MulRelin(cbar.Element(), cbar.Element(), evakey, cbar.Ciphertext()) - evaluator.Rescale(cbar, evaluator.ckkscontext.scale, cbar) + evaluator.Rescale(cbar, evaluator.context.scale, cbar) tmp = evaluator.AddConstNew(cbar, 1) evaluator.MulRelin(tmp.Element(), res.Element(), evakey, tmp.Ciphertext()) - evaluator.Rescale(tmp, evaluator.ckkscontext.scale, tmp) + evaluator.Rescale(tmp, evaluator.context.scale, tmp) res = tmp.CopyNew().Ciphertext() } diff --git a/ckks/chebyshev_evaluation.go b/ckks/chebyshev_evaluation.go index fad98770..88638ca9 100644 --- a/ckks/chebyshev_evaluation.go +++ b/ckks/chebyshev_evaluation.go @@ -15,7 +15,7 @@ func (evaluator *Evaluator) EvaluateChebyFast(ct *Ciphertext, cheby *ChebyshevIn evaluator.MultByConst(C[1], 2/(cheby.b-cheby.a), C[1]) evaluator.AddConst(C[1], (-cheby.a-cheby.b)/(cheby.b-cheby.a), C[1]) - evaluator.Rescale(C[1], evaluator.ckkscontext.scale, C[1]) + evaluator.Rescale(C[1], evaluator.context.scale, C[1]) M := uint64(bits.Len64(cheby.degree - 1)) L := uint64(M >> 1) @@ -86,7 +86,7 @@ func computePowerBasisCheby(n uint64, C map[uint64]*Ciphertext, evaluator *Evalu // Computes C[n] = C[a]*C[b] C[n] = evaluator.MulRelinNew(C[a], C[b], evakey) - evaluator.Rescale(C[n], evaluator.ckkscontext.scale, C[n]) + evaluator.Rescale(C[n], evaluator.context.scale, C[n]) // Computes C[n] = 2*C[a]*C[b] evaluator.Add(C[n], C[n], C[n]) @@ -123,7 +123,7 @@ func recurseCheby(maxDegree, L, M uint64, coeffs map[uint64]complex128, C map[ui evaluator.Add(res, tmp, res) - evaluator.Rescale(res, evaluator.ckkscontext.scale, res) + evaluator.Rescale(res, evaluator.context.scale, res) return res diff --git a/ckks/ciphertext.go b/ckks/ciphertext.go index f9e903f4..82dc775a 100644 --- a/ckks/ciphertext.go +++ b/ckks/ciphertext.go @@ -9,18 +9,66 @@ type Ciphertext struct { *ckksElement } -// NewCiphertext returns a new Ciphertext element. -func NewCiphertext() (ciphertext *Ciphertext) { +// NewCiphertextStruct returns a new Ciphertext element. +func NewCiphertextStruct() (ciphertext *Ciphertext) { return &Ciphertext{&ckksElement{}} } +func NewCiphertextRingContext(params *Parameters) *ring.Context { + scalechain := make([]float64, len(params.Modulichain)) + + // Extracts all the different primes bit size and maps their number + primesbitlen := make(map[uint64]uint64) + for i, qi := range params.Modulichain { + + primesbitlen[uint64(qi)]++ + + if uint64(params.Modulichain[i]) > 60 { + panic("provided moduli must be smaller than 61") + } + } + + for _, pj := range params.P { + primesbitlen[uint64(pj)]++ + + if uint64(pj) > 60 { + panic("provided P must be smaller than 61") + } + } + + // For each bitsize, finds that many primes + primes := make(map[uint64][]uint64) + for key, value := range primesbitlen { + primes[key] = GenerateCKKSPrimes(key, uint64(params.LogN), value) + } + + // Assigns the primes to the ckks moduli chain + moduli := make([]uint64, len(params.Modulichain)) + for i, qi := range params.Modulichain { + moduli[i] = primes[uint64(params.Modulichain[i])][0] + primes[uint64(qi)] = primes[uint64(qi)][1:] + + scalechain[i] = float64(moduli[i]) + } + + ringCtx := ring.NewContext() + ringCtx.SetParameters(1< 60 { + panic("provided moduli must be smaller than 61") + } + } + + for _, pj := range params.P { + primesbitlen[uint64(pj)]++ + + if uint64(pj) > 60 { + panic("provided P must be smaller than 61") + } + } + + // For each bitsize, finds that many primes + primes := make(map[uint64][]uint64) + for key, value := range primesbitlen { + primes[key] = GenerateCKKSPrimes(key, uint64(params.LogN), value) + } + + // Assigns the primes to the ckks moduli chain + moduli := make([]uint64, len(params.Modulichain)) + for i, qi := range params.Modulichain { + moduli[i] = primes[uint64(params.Modulichain[i])][0] + primes[uint64(qi)] = primes[uint64(qi)][1:] + + scalechain[i] = float64(moduli[i]) + } + + // Assigns the primes to the special primes list for the the keyscontext + specialPrimes := make([]uint64, len(params.P)) + for i, pj := range params.P { + specialPrimes[i] = primes[uint64(pj)][0] + primes[uint64(pj)] = primes[uint64(pj)][1:] + } + + // Contexts + contextQ := ring.NewContext() + contextQ.SetParameters(1< plaintext not at maximum level") } @@ -97,10 +226,10 @@ func encryptfrompk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe // We sample a R-WLE instance (encryption of zero) over the keys context (ciphertext context + special prime) - contextKeys := encryptor.ckkscontext.contextKeys - contextQ := encryptor.ckkscontext.contextQ + contextKeys := encryptor.context.contextKeys + contextQ := encryptor.context.contextQ - encryptor.ckkscontext.contextKeys.SampleTernaryMontgomeryNTT(encryptor.polypool[2], 0.5) + encryptor.context.contextKeys.SampleTernaryMontgomeryNTT(encryptor.polypool[2], 0.5) // ct0 = u*pk0 contextKeys.MulCoeffsMontgomery(encryptor.polypool[2], encryptor.pk.pk[0], encryptor.polypool[0]) @@ -112,15 +241,15 @@ func encryptfrompk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe contextKeys.InvNTT(encryptor.polypool[1], encryptor.polypool[1]) // ct0 = u*pk0 + e0 - encryptor.ckkscontext.gaussianSampler.SampleAndAdd(encryptor.polypool[0]) + encryptor.context.gaussianSampler.SampleAndAdd(encryptor.polypool[0]) // ct1 = u*pk1 + e1 - encryptor.ckkscontext.gaussianSampler.SampleAndAdd(encryptor.polypool[1]) + encryptor.context.gaussianSampler.SampleAndAdd(encryptor.polypool[1]) // ct0 = (u*pk0 + e0)/P - encryptor.baseconverter.ModDown(contextKeys, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2]) + encryptor.baseconverter.ModDown(contextKeys, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2]) // ct1 = (u*pk1 + e1)/P - encryptor.baseconverter.ModDown(contextKeys, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2]) + encryptor.baseconverter.ModDown(contextKeys, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2]) // 2*#Q NTT contextQ.NTT(ciphertext.value[0], ciphertext.value[0]) @@ -133,9 +262,9 @@ func encryptfrompk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe } func encryptfromsk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphertext) { - contextKeys := encryptor.ckkscontext.contextKeys - contextP := encryptor.ckkscontext.contextP - contextQ := encryptor.ckkscontext.contextQ + contextKeys := encryptor.context.contextKeys + contextP := encryptor.context.contextP + contextQ := encryptor.context.contextQ // ct1 = a contextKeys.UniformPoly(encryptor.polypool[1]) @@ -148,15 +277,15 @@ func encryptfromsk(encryptor *Encryptor, plaintext *Plaintext, ciphertext *Ciphe contextKeys.InvNTT(encryptor.polypool[0], encryptor.polypool[0]) // ct0 = -s*a + e - encryptor.ckkscontext.gaussianSampler.SampleAndAdd(encryptor.polypool[0]) + encryptor.context.gaussianSampler.SampleAndAdd(encryptor.polypool[0]) // We rescal by the special prime, dividing the error by this prime // ct0 = (-s*a + e)/P - encryptor.baseconverter.ModDown(contextKeys, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2]) + encryptor.baseconverter.ModDown(contextKeys, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[0], ciphertext.value[0], encryptor.polypool[2]) // #Q + #P NTT // ct1 = a/P - encryptor.baseconverter.ModDownNTT(contextQ, contextP, encryptor.ckkscontext.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2]) + encryptor.baseconverter.ModDownNTT(contextQ, contextP, encryptor.context.rescaleParamsKeys, plaintext.Level(), encryptor.polypool[1], ciphertext.value[1], encryptor.polypool[2]) // #Q NTT contextQ.NTT(ciphertext.value[0], ciphertext.value[0]) diff --git a/ckks/evaluator.go b/ckks/evaluator.go index fe4bd277..b989fd44 100644 --- a/ckks/evaluator.go +++ b/ckks/evaluator.go @@ -7,10 +7,147 @@ import ( "math" ) +type evaluatorContext struct { + // Context parameters + n uint64 + scale float64 + + // Number of available levels + levels uint64 + + // Moduli chain + moduli []uint64 + scalechain []float64 + + // Contexts + specialprimes []uint64 + alpha uint64 + beta uint64 + contextQ *ring.Context + contextP *ring.Context + contextKeys *ring.Context + + // Pre-computed values for the rescaling + rescaleParamsKeys []uint64 // (P^-1) mod each qi +} + +func newEvaluatorContext(params *Parameters) *evaluatorContext { + n := uint64(1 << uint64(params.LogN)) + scale := params.Scale + levels := uint64(len(params.Modulichain)) + + alpha := uint64(len(params.P)) + beta := uint64(math.Ceil(float64(levels) / float64(alpha))) + + scalechain := make([]float64, len(params.Modulichain)) + + // Extracts all the different primes bit size and maps their number + primesbitlen := make(map[uint64]uint64) + for i, qi := range params.Modulichain { + + primesbitlen[uint64(qi)]++ + + if uint64(params.Modulichain[i]) > 60 { + panic("provided moduli must be smaller than 61") + } + } + + for _, pj := range params.P { + primesbitlen[uint64(pj)]++ + + if uint64(pj) > 60 { + panic("provided P must be smaller than 61") + } + } + // For each bitsize, finds that many primes + primes := make(map[uint64][]uint64) + for key, value := range primesbitlen { + primes[key] = GenerateCKKSPrimes(key, uint64(params.LogN), value) + } + + // Assigns the primes to the ckks moduli chain + moduli := make([]uint64, len(params.Modulichain)) + for i, qi := range params.Modulichain { + moduli[i] = primes[uint64(params.Modulichain[i])][0] + primes[uint64(qi)] = primes[uint64(qi)][1:] + + scalechain[i] = float64(moduli[i]) + } + + // Assigns the primes to the special primes list for the the keyscontext + specialprimes := make([]uint64, len(params.P)) + for i, pj := range params.P { + specialprimes[i] = primes[uint64(pj)][0] + primes[uint64(pj)] = primes[uint64(pj)][1:] + } + + // Contexts + contextQ := ring.NewContext() + contextQ.SetParameters(1< c1.Degree() && tmp0 != ctOut { for i := minDegree + 1; i < maxDegree+1; i++ { - evaluator.ckkscontext.contextQ.CopyLvl(level, tmp0.Value()[i], ctOut.Value()[i]) + evaluator.context.contextQ.CopyLvl(level, tmp0.Value()[i], ctOut.Value()[i]) } } else if c1.Degree() > c0.Degree() && tmp1 != ctOut { for i := minDegree + 1; i < maxDegree+1; i++ { - evaluator.ckkscontext.contextQ.CopyLvl(level, tmp1.Value()[i], ctOut.Value()[i]) + evaluator.context.contextQ.CopyLvl(level, tmp1.Value()[i], ctOut.Value()[i]) } } } @@ -298,13 +436,13 @@ func (evaluator *Evaluator) Neg(ct0 *Ciphertext, ctOut *Ciphertext) { } for i := range ct0.value { - evaluator.ckkscontext.contextQ.NegLvl(level, ct0.value[i], ctOut.Value()[i]) + evaluator.context.contextQ.NegLvl(level, ct0.value[i], ctOut.Value()[i]) } } // NegNew negates ct0 and returns the result on a newly created element. func (evaluator *Evaluator) NegNew(ct0 *Ciphertext) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.Neg(ct0, ctOut) return } @@ -349,7 +487,7 @@ func (evaluator *Evaluator) AddConst(ct0 *Ciphertext, constant interface{}, ctOu var scaledConst, scaledConstReal, scaledConstImag uint64 - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ // Component wise addition of the following vector to the ciphertext : // [a + b*psi_qi^2, ....., a + b*psi_qi^2, a - b*psi_qi^2, ...., a - b*psi_qi^2] mod Qi @@ -376,7 +514,7 @@ func (evaluator *Evaluator) AddConst(ct0 *Ciphertext, constant interface{}, ctOu p1tmp := ctOut.Value()[0].Coeffs[i] p0tmp := ct0.value[0].Coeffs[i] - for j := uint64(0); j < evaluator.ckkscontext.n>>1; j++ { + for j := uint64(0); j < evaluator.context.n>>1; j++ { p1tmp[j] = ring.CRed(p0tmp[j]+scaledConst, qi) } @@ -384,7 +522,7 @@ func (evaluator *Evaluator) AddConst(ct0 *Ciphertext, constant interface{}, ctOu scaledConst = ring.CRed(scaledConstReal+(qi-scaledConstImag), qi) } - for j := evaluator.ckkscontext.n >> 1; j < evaluator.ckkscontext.n; j++ { + for j := evaluator.context.n >> 1; j < evaluator.context.n; j++ { p1tmp[j] = ring.CRed(p0tmp[j]+scaledConst, qi) } } @@ -421,7 +559,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac valueFloat := cReal - float64(valueInt) if valueFloat != 0 { - scale = evaluator.ckkscontext.scale + scale = evaluator.context.scale } } @@ -430,7 +568,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac valueFloat := cImag - float64(valueInt) if valueFloat != 0 { - scale = evaluator.ckkscontext.scale + scale = evaluator.context.scale } } @@ -443,7 +581,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac valueFloat := cReal - float64(valueInt) if valueFloat != 0 { - scale = evaluator.ckkscontext.scale + scale = evaluator.context.scale } } @@ -462,7 +600,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac var scaledConst, scaledConstReal, scaledConstImag uint64 - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ // If a scaling will be required to multiply by the constant, // equalizes scales such that the scales match in the end. @@ -470,19 +608,19 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac // If ctOut scaling is smaller than ct0's scale + the default scaling, // then brings ctOut scale to ct0's scale. - if ctOut.Scale() < ct0.Scale()*evaluator.ckkscontext.scale { + if ctOut.Scale() < ct0.Scale()*evaluator.context.scale { - if uint64((evaluator.ckkscontext.scale*ct0.Scale())/ctOut.Scale()) != 0 { + if uint64((evaluator.context.scale*ct0.Scale())/ctOut.Scale()) != 0 { - evaluator.MultByConst(ctOut, uint64((evaluator.ckkscontext.scale*ct0.Scale())/ctOut.Scale()), ctOut) + evaluator.MultByConst(ctOut, uint64((evaluator.context.scale*ct0.Scale())/ctOut.Scale()), ctOut) } - ctOut.SetScale(evaluator.ckkscontext.scale * ct0.Scale()) + ctOut.SetScale(evaluator.context.scale * ct0.Scale()) // If ctOut.Scale() > ((a+bi)*scale)*ct0(x), then sets the scale to // bring c(x)*scale to the level of ctOut(x) scale - } else if ctOut.Scale() > ct0.Scale()*evaluator.ckkscontext.scale { + } else if ctOut.Scale() > ct0.Scale()*evaluator.context.scale { scale = ctOut.Scale() / ct0.Scale() } @@ -534,7 +672,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac for u := range ct0.Value() { p0tmp := ct0.Value()[u].Coeffs[i] p1tmp := ctOut.Value()[u].Coeffs[i] - for j := uint64(0); j < evaluator.ckkscontext.n>>1; j++ { + for j := uint64(0); j < evaluator.context.n>>1; j++ { p1tmp[j] = ring.CRed(p1tmp[j]+ring.MRed(p0tmp[j], scaledConst, qi, mredParams), qi) } } @@ -547,7 +685,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac for u := range ct0.Value() { p0tmp := ct0.Value()[u].Coeffs[i] p1tmp := ctOut.Value()[u].Coeffs[i] - for j := evaluator.ckkscontext.n >> 1; j < evaluator.ckkscontext.n; j++ { + for j := evaluator.context.n >> 1; j < evaluator.context.n; j++ { p1tmp[j] = ring.CRed(p1tmp[j]+ring.MRed(p0tmp[j], scaledConst, qi, mredParams), qi) } } @@ -558,7 +696,7 @@ func (evaluator *Evaluator) MultByConstAndAdd(ct0 *Ciphertext, constant interfac // The scale of the output element will depend on the scale of the input element and the constant (if the constant // needs to be scaled (its rational part is not zero)). The constant can be an uint64, int64, float64 or complex128. func (evaluator *Evaluator) MultByConstNew(ct0 *Ciphertext, constant interface{}) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.MultByConst(ct0, constant, ctOut) return } @@ -587,7 +725,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c valueFloat := cReal - float64(valueInt) if valueFloat != 0 { - scale = evaluator.ckkscontext.scale + scale = evaluator.context.scale } } @@ -596,7 +734,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c valueFloat := cImag - float64(valueInt) if valueFloat != 0 { - scale = evaluator.ckkscontext.scale + scale = evaluator.context.scale } } @@ -609,7 +747,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c valueFloat := cReal - float64(valueInt) if valueFloat != 0 { - scale = evaluator.ckkscontext.scale + scale = evaluator.context.scale } } @@ -630,7 +768,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c // [a + b*psi_qi^2, ....., a + b*psi_qi^2, a - b*psi_qi^2, ...., a - b*psi_qi^2] mod Qi // [{ N/2 }{ N/2 }] // Which is equivalent outside of the NTT domain of adding a to the first coefficient of ct0 and b to the N/2th coefficient of ct0. - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ var scaledConst, scaledConstReal, scaledConstImag uint64 for i := uint64(0); i < level+1; i++ { @@ -658,7 +796,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c for u := range ct0.Value() { p0tmp := ct0.Value()[u].Coeffs[i] p1tmp := ctOut.Value()[u].Coeffs[i] - for j := uint64(0); j < evaluator.ckkscontext.n>>1; j++ { + for j := uint64(0); j < evaluator.context.n>>1; j++ { p1tmp[j] = ring.MRed(p0tmp[j], scaledConst, qi, mredParams) } } @@ -671,7 +809,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c for u := range ct0.Value() { p0tmp := ct0.Value()[u].Coeffs[i] p1tmp := ctOut.Value()[u].Coeffs[i] - for j := evaluator.ckkscontext.n >> 1; j < evaluator.ckkscontext.n; j++ { + for j := evaluator.context.n >> 1; j < evaluator.context.n; j++ { p1tmp[j] = ring.MRed(p0tmp[j], scaledConst, qi, mredParams) } } @@ -683,7 +821,7 @@ func (evaluator *Evaluator) MultByConst(ct0 *Ciphertext, constant interface{}, c // MultByiNew multiplies ct0 by the imaginary number i, and returns the result on a newly created element. // Does not change the scale. func (evaluator *Evaluator) MultByiNew(ct0 *Ciphertext) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(1, ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.MultByi(ct0, ctOut) return ctOut } @@ -696,7 +834,7 @@ func (evaluator *Evaluator) MultByi(ct0 *Ciphertext, ct1 *Ciphertext) { level = utils.MinUint64(ct0.Level(), ct1.Level()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ var imag uint64 @@ -732,7 +870,7 @@ func (evaluator *Evaluator) MultByi(ct0 *Ciphertext, ct1 *Ciphertext) { // DivByiNew multiplies ct0 by the imaginary number 1/i = -i, and returns the result on a newly created element. // Does not change the scale. func (evaluator *Evaluator) DivByiNew(ct0 *Ciphertext) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(1, ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.DivByi(ct0, ctOut) return } @@ -745,7 +883,7 @@ func (evaluator *Evaluator) DivByi(ct0 *Ciphertext, ct1 *Ciphertext) { level = utils.MinUint64(ct0.Level(), ct1.Level()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ var imag uint64 @@ -780,7 +918,7 @@ func (evaluator *Evaluator) DivByi(ct0 *Ciphertext, ct1 *Ciphertext) { // ScaleUpNew multiplies ct0 by 2^scale and sets its scale to its previous scale // plus 2^n. Returns the result on a newly created element. func (evaluator *Evaluator) ScaleUpNew(ct0 *Ciphertext, scale float64) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.ScaleUp(ct0, scale, ctOut) return } @@ -795,7 +933,7 @@ func (evaluator *Evaluator) ScaleUp(ct0 *Ciphertext, scale float64, ctOut *Ciphe // MulByPow2New multiplies the ct0 by 2^pow2 and returns the result on a newly created element. func (evaluator *Evaluator) MulByPow2New(ct0 *Ciphertext, pow2 uint64) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.MulByPow2(ct0.Element(), pow2, ctOut.Element()) return } @@ -805,7 +943,7 @@ func (evaluator *Evaluator) MulByPow2(ct0 *ckksElement, pow2 uint64, ctOut *ckks var level uint64 level = utils.MinUint64(ct0.Level(), ctOut.Level()) for i := range ctOut.Value() { - evaluator.ckkscontext.contextQ.MulByPow2Lvl(level, ct0.value[i], pow2, ctOut.Value()[i]) + evaluator.context.contextQ.MulByPow2Lvl(level, ct0.value[i], pow2, ctOut.Value()[i]) } } @@ -813,7 +951,7 @@ func (evaluator *Evaluator) MulByPow2(ct0 *ckksElement, pow2 uint64, ctOut *ckks // To be used in conjonction with function not applying modular reduction. func (evaluator *Evaluator) ReduceNew(ct0 *Ciphertext) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) _ = evaluator.Reduce(ct0, ctOut) @@ -829,7 +967,7 @@ func (evaluator *Evaluator) Reduce(ct0 *Ciphertext, ctOut *Ciphertext) error { } for i := range ct0.value { - evaluator.ckkscontext.contextQ.ReduceLvl(utils.MinUint64(ct0.Level(), ctOut.Level()), ct0.value[i], ctOut.value[i]) + evaluator.context.contextQ.ReduceLvl(utils.MinUint64(ct0.Level(), ctOut.Level()), ct0.value[i], ctOut.value[i]) } return nil @@ -867,7 +1005,7 @@ func (evaluator *Evaluator) DropLevel(ct0 *Ciphertext, levels uint64) (err error // some error. func (evaluator *Evaluator) RescaleNew(ct0 *Ciphertext, threshold float64) (ctOut *Ciphertext, err error) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) return ctOut, evaluator.Rescale(ct0, threshold, ctOut) } @@ -887,7 +1025,7 @@ func (evaluator *Evaluator) Rescale(ct0 *Ciphertext, threshold float64, c1 *Ciph panic("cannot rescale -> reciever ciphertext does not match input ciphertext level") } - if ct0.Scale() >= (threshold*evaluator.ckkscontext.scalechain[c1.Level()])/2 { + if ct0.Scale() >= (threshold*evaluator.context.scalechain[c1.Level()])/2 { if !ct0.IsNTT() { panic("cannot rescale -> input ciphertext not in NTT") @@ -895,12 +1033,12 @@ func (evaluator *Evaluator) Rescale(ct0 *Ciphertext, threshold float64, c1 *Ciph c1.Copy(ct0.Element()) - for c1.Scale() >= (threshold*evaluator.ckkscontext.scalechain[c1.Level()])/2 && c1.Level() != 0 { + for c1.Scale() >= (threshold*evaluator.context.scalechain[c1.Level()])/2 && c1.Level() != 0 { - c1.DivScale(evaluator.ckkscontext.scalechain[c1.Level()]) + c1.DivScale(evaluator.context.scalechain[c1.Level()]) for i := range c1.Value() { - evaluator.ckkscontext.contextQ.DivRoundByLastModulusNTT(c1.Value()[i]) + evaluator.context.contextQ.DivRoundByLastModulusNTT(c1.Value()[i]) } } @@ -930,11 +1068,11 @@ func (evaluator *Evaluator) RescaleMany(ct0 *Ciphertext, nbRescales uint64, c1 * c1.Copy(ct0.Element()) for i := uint64(0); i < nbRescales; i++ { - c1.DivScale(evaluator.ckkscontext.scalechain[c1.Level()-i]) + c1.DivScale(evaluator.context.scalechain[c1.Level()-i]) } for i := range c1.Value() { - evaluator.ckkscontext.contextQ.DivRoundByLastModulusManyNTT(c1.Value()[i], nbRescales) + evaluator.context.contextQ.DivRoundByLastModulusManyNTT(c1.Value()[i], nbRescales) } return nil @@ -947,7 +1085,7 @@ func (evaluator *Evaluator) RescaleMany(ct0 *Ciphertext, nbRescales uint64, c1 * // the resulting ciphertext will be of degree two. This function only accepts plaintexts (degree zero) and/or ciphertexts of degree one. func (evaluator *Evaluator) MulRelinNew(op0, op1 Operand, evakey *EvaluationKey) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(1, utils.MinUint64(op0.Level(), op1.Level()), op0.Scale()+op1.Scale()) + ctOut = NewCiphertext(1, utils.MinUint64(op0.Level(), op1.Level()), op0.Scale()+op1.Scale(), evaluator.context.contextQ) evaluator.MulRelin(op0, op1, evakey, ctOut) return ctOut @@ -982,7 +1120,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct elOut.SetScale(el0.Scale() * el1.Scale()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ var c00, c01, c0, c1, c2 *ring.Poly @@ -1003,7 +1141,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct // resizes the cipher text to a degree 2 ciphertext if evakey == nil { - elOut.Resize(evaluator.ckkscontext, 2) + elOut.Resize(evaluator.context.contextQ, 2) c2 = elOut.value[2] // If there is however an evaluation key, then @@ -1050,7 +1188,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct } else { // Or copies the result on the output ciphertext if it was one of the inputs if elOut == el0 || elOut == el1 { - elOut.Resize(evaluator.ckkscontext, 2) + elOut.Resize(evaluator.context.contextQ, 2) context.CopyLvl(level, c0, elOut.value[0]) context.CopyLvl(level, c1, elOut.value[1]) context.CopyLvl(level, c2, elOut.value[2]) @@ -1080,7 +1218,7 @@ func (evaluator *Evaluator) MulRelin(op0, op1 Operand, evakey *EvaluationKey, ct // RelinearizeNew applies the relinearization procedure on ct0 and returns the result on a newly // created ciphertext. Requires the input ciphertext to be of degree two. func (evaluator *Evaluator) RelinearizeNew(ct0 *Ciphertext, evakey *EvaluationKey) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(1, ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(1, ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.Relinearize(ct0, evakey, ctOut) return } @@ -1096,21 +1234,21 @@ func (evaluator *Evaluator) Relinearize(ct0 *Ciphertext, evakey *EvaluationKey, } level := utils.MinUint64(ct0.Level(), ctOut.Level()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ context.CopyLvl(level, ct0.value[0], ctOut.value[0]) context.CopyLvl(level, ct0.value[1], ctOut.value[1]) evaluator.switchKeysInPlace(ct0.value[2], evakey.evakey, ctOut) - ctOut.Resize(evaluator.ckkscontext, 1) + ctOut.Resize(evaluator.context.contextQ, 1) } // SwitchKeysNew re-encrypts ct0 under a different key and returns the result on a newly created element. // Requires a switchinkey, which is computed from the key under which the ciphertext is currently encrypted, // and the key under which the ciphertext will be re-encrypted. func (evaluator *Evaluator) SwitchKeysNew(ct0 *Ciphertext, switchingKey *SwitchingKey) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.SwitchKeys(ct0, switchingKey, ctOut) return } @@ -1125,7 +1263,7 @@ func (evaluator *Evaluator) SwitchKeys(ct0 *Ciphertext, switchingKey *SwitchingK } level := utils.MinUint64(ct0.Level(), ctOut.Level()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ context.CopyLvl(level, ct0.value[0], ctOut.value[0]) context.CopyLvl(level, ct0.value[1], ctOut.value[1]) @@ -1136,7 +1274,7 @@ func (evaluator *Evaluator) SwitchKeys(ct0 *Ciphertext, switchingKey *SwitchingK // RotateColumnsNew rotates the columns of ct0 by k position to the left, and returns the result on a newly created element. // If the provided element is a ciphertext, a keyswitching operation is necessary and a rotation key for the specific rotation needs to be provided. func (evaluator *Evaluator) RotateColumnsNew(ct0 *Ciphertext, k uint64, evakey *RotationKeys) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.RotateColumns(ct0, k, evakey, ctOut) return } @@ -1149,7 +1287,7 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot panic("cannot rotate -> input and output ciphertext must be of degree 1") } - k &= ((evaluator.ckkscontext.n >> 1) - 1) + k &= ((evaluator.context.n >> 1) - 1) if k == 0 { @@ -1168,7 +1306,7 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot // If not looks if the left and right pow2 rotations have been generated hasPow2Rotations := true - for i := uint64(1); i < evaluator.ckkscontext.n>>1; i <<= 1 { + for i := uint64(1); i < evaluator.context.n>>1; i <<= 1 { if evakey.evakeyRotColLeft[i] == nil || evakey.evakeyRotColRight[i] == nil { hasPow2Rotations = false break @@ -1178,10 +1316,10 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot // If yes, computes the least amount of rotation between left and right required to apply the demanded rotation if hasPow2Rotations { - if utils.HammingWeight64(k) <= utils.HammingWeight64((evaluator.ckkscontext.n>>1)-k) { + if utils.HammingWeight64(k) <= utils.HammingWeight64((evaluator.context.n>>1)-k) { evaluator.rotateColumnsLPow2(ct0, k, evakey, ctOut) } else { - evaluator.rotateColumnsRPow2(ct0, (evaluator.ckkscontext.n>>1)-k, evakey, ctOut) + evaluator.rotateColumnsRPow2(ct0, (evaluator.context.n>>1)-k, evakey, ctOut) } // Else returns an error indicating that the keys have not been generated @@ -1197,17 +1335,17 @@ func (evaluator *Evaluator) RotateColumns(ct0 *Ciphertext, k uint64, evakey *Rot func (evaluator *Evaluator) RotateHoisted(ctIn *Ciphertext, rotations []uint64, rotkeys *RotationKeys) (cOut map[uint64]*Ciphertext) { // Pre-computation for rotations using hoisting - contextQ := evaluator.ckkscontext.contextQ - contextP := evaluator.ckkscontext.contextP + contextQ := evaluator.context.contextQ + contextP := evaluator.context.contextP c2NTT := ctIn.value[1] c2InvNTT := contextQ.NewPoly() contextQ.InvNTTLvl(ctIn.Level(), c2NTT, c2InvNTT) - c2QiQDecomp := make([]*ring.Poly, evaluator.ckkscontext.beta) - c2QiPDecomp := make([]*ring.Poly, evaluator.ckkscontext.beta) + c2QiQDecomp := make([]*ring.Poly, evaluator.context.beta) + c2QiPDecomp := make([]*ring.Poly, evaluator.context.beta) - alpha := evaluator.ckkscontext.alpha + alpha := evaluator.context.alpha beta := uint64(math.Ceil(float64(ctIn.Level()+1) / float64(alpha))) for i := uint64(0); i < beta; i++ { @@ -1220,12 +1358,12 @@ func (evaluator *Evaluator) RotateHoisted(ctIn *Ciphertext, rotations []uint64, for _, i := range rotations { - i &= ((evaluator.ckkscontext.n >> 1) - 1) + i &= ((evaluator.context.n >> 1) - 1) if i == 0 { cOut[i] = ctIn.CopyNew().Ciphertext() } else { - cOut[i] = evaluator.ckkscontext.NewCiphertext(1, ctIn.Level(), ctIn.Scale()) + cOut[i] = NewCiphertext(1, ctIn.Level(), ctIn.Scale(), evaluator.context.contextQ) evaluator.switchKeyHoisted(ctIn, c2QiQDecomp, c2QiPDecomp, i, rotkeys, cOut[i]) } } @@ -1251,8 +1389,8 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi level = ctOut.Level() - contextQ := evaluator.ckkscontext.contextQ - contextP := evaluator.ckkscontext.contextP + contextQ := evaluator.context.contextQ + contextP := evaluator.context.contextP if ctIn != ctOut { ring.PermuteNTTWithIndex(ctIn.value[0], evakey.permuteNTTLeftIndex[k], evaluator.ringpool[0]) @@ -1283,7 +1421,7 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi reduce = 0 - alpha := evaluator.ckkscontext.alpha + alpha := evaluator.context.alpha beta := uint64(math.Ceil(float64(level+1) / float64(alpha))) // Key switching with crt decomposition for the Qi @@ -1296,7 +1434,7 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi contextQ.MulCoeffsMontgomeryAndAddNoModLvl(level, evakey.evakeyRotColLeft[k].evakey[i][1], c2QiQPermute, pool3Q) // We continue with the keyswitch primes. - for j, keysindex := uint64(0), evaluator.ckkscontext.levels; j < uint64(len(evaluator.ckkscontext.specialprimes)); j, keysindex = j+1, keysindex+1 { + for j, keysindex := uint64(0), evaluator.context.levels; j < uint64(len(evaluator.context.specialprimes)); j, keysindex = j+1, keysindex+1 { pj := contextP.Modulus[j] mredParams := contextP.GetMredParams()[j] @@ -1332,8 +1470,8 @@ func (evaluator *Evaluator) switchKeyHoisted(ctIn *Ciphertext, c2QiQDecomp, c2Qi //Independant of context (parameter : level) // Computes pool2Q = pool2Q/pool2P and pool3Q = pool3Q/pool3P - evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0]) - evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0]) + evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0]) + evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0]) //Independant of context (parameter : level) contextQ.AddLvl(level, ctOut.value[0], pool2Q, ctOut.value[0]) @@ -1355,7 +1493,7 @@ func (evaluator *Evaluator) rotateColumnsPow2(ct0 *Ciphertext, k uint64, permute evakeyIndex = 1 level := utils.MinUint64(ct0.Level(), ctOut.Level()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ context.CopyLvl(level, ct0.value[0], ctOut.value[0]) context.CopyLvl(level, ct0.value[1], ctOut.value[1]) @@ -1376,7 +1514,7 @@ func (evaluator *Evaluator) rotateColumnsPow2(ct0 *Ciphertext, k uint64, permute // created element. If the provided element is a ciphertext, a keyswitching operation is necessary and a rotation key // for the row rotation needs to be provided. func (evaluator *Evaluator) ConjugateNew(ct0 *Ciphertext, evakey *RotationKeys) (ctOut *Ciphertext) { - ctOut = evaluator.ckkscontext.NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale()) + ctOut = NewCiphertext(ct0.Degree(), ct0.Level(), ct0.Scale(), evaluator.context.contextQ) evaluator.Conjugate(ct0, evakey, ctOut) return } @@ -1412,7 +1550,7 @@ func (evaluator *Evaluator) permuteNTT(ct0 *Ciphertext, index []uint64, evakey * ring.PermuteNTTWithIndex(ct0.value[1], index, el1) level := utils.MinUint64(ct0.Level(), ctOut.Level()) - context := evaluator.ckkscontext.contextQ + context := evaluator.context.contextQ context.CopyLvl(level, el0, ctOut.value[0]) context.CopyLvl(level, el1, ctOut.value[1]) @@ -1426,8 +1564,8 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe level = ctOut.Level() - contextQ := evaluator.ckkscontext.contextQ - contextP := evaluator.ckkscontext.contextP + contextQ := evaluator.context.contextQ + contextP := evaluator.context.contextP for i := range evaluator.poolQ { evaluator.poolQ[i].Zero() @@ -1456,7 +1594,7 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe reduce = 0 - alpha := evaluator.ckkscontext.alpha + alpha := evaluator.context.alpha beta := uint64(math.Ceil(float64(level+1) / float64(alpha))) // Key switching with crt decomposition for the Qi @@ -1468,7 +1606,7 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe contextQ.MulCoeffsMontgomeryAndAddNoModLvl(level, evakey.evakey[i][1], c2QiQ, pool3Q) // We continue with the keyswitch primes. - for j, keysindex := uint64(0), evaluator.ckkscontext.levels; j < uint64(len(evaluator.ckkscontext.specialprimes)); j, keysindex = j+1, keysindex+1 { + for j, keysindex := uint64(0), evaluator.context.levels; j < uint64(len(evaluator.context.specialprimes)); j, keysindex = j+1, keysindex+1 { pj := contextP.Modulus[j] mredParams := contextP.GetMredParams()[j] @@ -1505,8 +1643,8 @@ func (evaluator *Evaluator) switchKeysInPlace(cx *ring.Poly, evakey *SwitchingKe //Independant of context (parameter : level) // Computes pool2Q = pool2Q/pool2P and pool3Q = pool3Q/pool3P - evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0]) - evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.ckkscontext.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0]) + evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool2Q, pool2P, pool2Q, evaluator.keyswitchpool[0]) + evaluator.baseconverter.ModDownSplitedNTT(contextQ, contextP, evaluator.context.rescaleParamsKeys, level, pool3Q, pool3P, pool3Q, evaluator.keyswitchpool[0]) //Independant of context (parameter : level) contextQ.AddLvl(level, ctOut.value[0], pool2Q, ctOut.value[0]) @@ -1517,12 +1655,12 @@ func (evaluator *Evaluator) decomposeAndSplitNTT(level, beta uint64, c2NTT, c2In // Decomposes the input polynomial into the target CRT basis. - contextQ := evaluator.ckkscontext.contextQ - contextP := evaluator.ckkscontext.contextP + contextQ := evaluator.context.contextQ + contextP := evaluator.context.contextP evaluator.decomposer.DecomposeAndSplit(level, beta, c2InvNTT, c2QiQ, c2QiP) - p0idxst := beta * evaluator.ckkscontext.alpha + p0idxst := beta * evaluator.context.alpha p0idxed := p0idxst + evaluator.decomposer.Xalpha()[beta] // c2_qi = cx mod qi mod qi diff --git a/ckks/operand.go b/ckks/operand.go index ab01b182..322c7682 100644 --- a/ckks/operand.go +++ b/ckks/operand.go @@ -75,12 +75,12 @@ func (el *ckksElement) DivScale(scale float64) { } // Resize resizes the degree of the target element. -func (el *ckksElement) Resize(ckkscontext *Context, degree uint64) { +func (el *ckksElement) Resize(ringCtx *ring.Context, degree uint64) { if el.Degree() > degree { el.value = el.value[:degree+1] } else if el.Degree() < degree { for el.Degree() < degree { - el.value = append(el.value, []*ring.Poly{ckkscontext.contextQ.NewPolyLvl(el.Level())}...) + el.value = append(el.value, []*ring.Poly{ringCtx.NewPolyLvl(el.Level())}...) } } } diff --git a/ckks/polynomial_evaluation.go b/ckks/polynomial_evaluation.go index 81878299..8cbdd5f1 100644 --- a/ckks/polynomial_evaluation.go +++ b/ckks/polynomial_evaluation.go @@ -88,7 +88,7 @@ func computePowerBasis(n uint64, C map[uint64]*Ciphertext, evaluator *Evaluator, // Computes C[n] = C[a]*C[b] C[n] = evaluator.MulRelinNew(C[a], C[b], evakey) - evaluator.Rescale(C[n], evaluator.ckkscontext.scale, C[n]) + evaluator.Rescale(C[n], evaluator.context.scale, C[n]) } } @@ -133,7 +133,7 @@ func recurse(maxDegree, L, M uint64, coeffs map[uint64]complex128, C map[uint64] evaluator.Add(res, tmp, res) - evaluator.Rescale(res, evaluator.ckkscontext.scale, res) + evaluator.Rescale(res, evaluator.context.scale, res) return res @@ -141,7 +141,7 @@ func recurse(maxDegree, L, M uint64, coeffs map[uint64]complex128, C map[uint64] func evaluatePolyFromPowerBasis(coeffs map[uint64]complex128, C map[uint64]*Ciphertext, evaluator *Evaluator, evakey *EvaluationKey) (res *Ciphertext) { - res = evaluator.ckkscontext.NewCiphertext(1, C[1].Level(), C[1].Scale()) + res = NewCiphertext(1, C[1].Level(), C[1].Scale(), evaluator.context.contextQ) if math.Abs(real(coeffs[0])) > 1e-15 || math.Abs(imag(coeffs[0])) > 1e-15 { evaluator.AddConst(res, coeffs[0], res) @@ -153,7 +153,7 @@ func evaluatePolyFromPowerBasis(coeffs map[uint64]complex128, C map[uint64]*Ciph } } - evaluator.Rescale(res, evaluator.ckkscontext.scale, res) + evaluator.Rescale(res, evaluator.context.scale, res) return } diff --git a/dbfv/dbfv_benchmark_test.go b/dbfv/dbfv_benchmark_test.go index 05272a11..38591608 100644 --- a/dbfv/dbfv_benchmark_test.go +++ b/dbfv/dbfv_benchmark_test.go @@ -371,6 +371,8 @@ func benchRefresh(b *testing.B) { contextKeys := bfvContext.ContextKeys() sk0Shards := params.sk0Shards + ringCtx := bfv.NewCiphertextRingContext(¶meters) + type Party struct { *RefreshProtocol s *ring.Poly @@ -403,13 +405,6 @@ func benchRefresh(b *testing.B) { }) b.Run(testString("Finalize", ¶meters), func(b *testing.B) { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } - ctOut := bfv.NewCiphertext(1, ringCtx) for i := 0; i < b.N; i++ { p.Finalize(ciphertext, crp, p.share, ctOut) // TODO: why does this fail ? diff --git a/dbfv/dbfv_test.go b/dbfv/dbfv_test.go index 96b2e861..ab684b16 100644 --- a/dbfv/dbfv_test.go +++ b/dbfv/dbfv_test.go @@ -165,12 +165,7 @@ func testRelinKeyGen(t *testing.T) { parties := testParams.parties for _, parameters := range testParams.contexts { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(¶meters) params := genDBFVContext(¶meters) @@ -263,12 +258,7 @@ func testRelinKeyGenNaive(t *testing.T) { parties := testParams.parties for _, parameters := range testParams.contexts { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(¶meters) params := genDBFVContext(¶meters) @@ -344,12 +334,7 @@ func testKeyswitching(t *testing.T) { parties := testParams.parties for _, parameters := range testParams.contexts { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(¶meters) params := genDBFVContext(¶meters) @@ -408,12 +393,7 @@ func testPublicKeySwitching(t *testing.T) { parties := testParams.parties for _, parameters := range testParams.contexts { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(¶meters) params := genDBFVContext(¶meters) @@ -529,12 +509,7 @@ func testRotKeyGenRotCols(t *testing.T) { parties := testParams.parties for _, parameters := range testParams.contexts { - ringCtx := ring.NewContext() - ringCtx.SetParameters(parameters.N, parameters.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(¶meters) params := genDBFVContext(¶meters) diff --git a/dckks/dckks_benchmark_test.go b/dckks/dckks_benchmark_test.go index 16a1d623..581286d0 100644 --- a/dckks/dckks_benchmark_test.go +++ b/dckks/dckks_benchmark_test.go @@ -373,7 +373,9 @@ func benchRefresh(b *testing.B) { crpGenerator.Seed([]byte{}) crp := crpGenerator.ClockNew() - ciphertext := ckksContext.NewCiphertext(1, levelStart, ckksContext.Scale()) + ringCtx := ckks.NewCiphertextRingContext(parameters) + + ciphertext := ckks.NewCiphertext(1, levelStart, ckksContext.Scale(), ringCtx) ckksContext.ContextQ().UniformPoly(ciphertext.Value()[0]) ckksContext.ContextQ().UniformPoly(ciphertext.Value()[1]) diff --git a/dckks/dckks_test.go b/dckks/dckks_test.go index c85389e8..d61692b6 100644 --- a/dckks/dckks_test.go +++ b/dckks/dckks_test.go @@ -78,7 +78,7 @@ func genDCKKSContext(contextParameters *ckks.Parameters) (params *dckksContext) params.ckksContext = ckks.NewContext(contextParameters) params.encoder = ckks.NewEncoder(contextParameters) - params.evaluator = params.ckksContext.NewEvaluator() + params.evaluator = ckks.NewEvaluator(contextParameters) kgen := params.ckksContext.NewKeyGenerator() @@ -105,7 +105,7 @@ func genDCKKSContext(contextParameters *ckks.Parameters) (params *dckksContext) params.pk0 = kgen.NewPublicKey(params.sk0) params.pk1 = kgen.NewPublicKey(params.sk1) - params.encryptorPk0 = params.ckksContext.NewEncryptorFromPk(params.pk0) + params.encryptorPk0 = ckks.NewEncryptorFromPk(params.pk0, contextParameters) params.decryptorSk0 = params.ckksContext.NewDecryptor(params.sk0) @@ -166,7 +166,7 @@ func testPublicKeyGen(t *testing.T) { P0.GenPublicKey(P0.s1, crp, pk) // Verifies that decrypt((encryptp(collectiveSk, m), collectivePk) = m - encryptorTest := ckksContext.NewEncryptorFromPk(pk) + encryptorTest := ckks.NewEncryptorFromPk(pk, parameters) coeffs, _, ciphertext := newTestVectors(params, encryptorTest, 1, t) @@ -369,6 +369,8 @@ func testKeyswitching(t *testing.T) { sk0Shards := params.sk0Shards sk1Shards := params.sk1Shards + ringCtx := ckks.NewCiphertextRingContext(parameters) + t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/scale=%f", parties, ckksContext.LogN(), @@ -405,7 +407,7 @@ func testKeyswitching(t *testing.T) { } } - ksCiphertext := ckksContext.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale()) + ksCiphertext := ckks.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale(), ringCtx) P0.KeySwitch(P0.share, ciphertext, ksCiphertext) @@ -433,6 +435,8 @@ func testPublicKeySwitching(t *testing.T) { sk0Shards := params.sk0Shards pk1 := params.pk1 + ringCtx := ckks.NewCiphertextRingContext(parameters) + t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/scale=%f", parties, ckksContext.LogN(), @@ -461,7 +465,7 @@ func testPublicKeySwitching(t *testing.T) { } P0 := pcksParties[0] - ciphertextSwitched := ckksContext.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale()) + ciphertextSwitched := ckks.NewCiphertext(1, ciphertext.Level(), ciphertext.Scale(), ringCtx) for i, p := range pcksParties { p.GenShare(p.s, pk1, ciphertext, p.share) @@ -559,6 +563,8 @@ func testRotKeyGenCols(t *testing.T) { decryptorSk0 := params.decryptorSk0 sk0Shards := params.sk0Shards + ringCtx := ckks.NewCiphertextRingContext(parameters) + t.Run(fmt.Sprintf("parties=%d/logN=%d/logQ=%d/levels=%d/scale=%f", parties, ckksContext.LogN(), ckksContext.LogQ(), ckksContext.Levels(), ckksContext.Scale()), func(t *testing.T) { type Party struct { @@ -590,7 +596,7 @@ func testRotKeyGenCols(t *testing.T) { coeffs, _, ciphertext := newTestVectors(params, encryptorPk0, 1, t) - receiver := ckksContext.NewCiphertext(ciphertext.Degree(), ciphertext.Level(), ciphertext.Scale()) + receiver := ckks.NewCiphertext(ciphertext.Degree(), ciphertext.Level(), ciphertext.Scale(), ringCtx) for k := uint64(1); k < contextKeys.N>>1; k <<= 1 { diff --git a/examples/ckks/examples_ckks.go b/examples/ckks/examples_ckks.go index 033fd200..b0039c6a 100644 --- a/examples/ckks/examples_ckks.go +++ b/examples/ckks/examples_ckks.go @@ -47,7 +47,7 @@ func chebyshevinterpolation() { // Encryptor var encryptor *ckks.Encryptor - encryptor = ckkscontext.NewEncryptorFromPk(pk) + encryptor = ckks.NewEncryptorFromPk(pk, params) // Decryptor var decryptor *ckks.Decryptor @@ -55,7 +55,7 @@ func chebyshevinterpolation() { // Evaluator var evaluator *ckks.Evaluator - evaluator = ckkscontext.NewEvaluator() + evaluator = ckks.NewEvaluator(params) // Values to encrypt values := make([]complex128, ckkscontext.Slots()) diff --git a/examples/dbfv/pir/pir.go b/examples/dbfv/pir/pir.go index 85c971ab..919a518b 100644 --- a/examples/dbfv/pir/pir.go +++ b/examples/dbfv/pir/pir.go @@ -231,12 +231,7 @@ func main() { plainMask := make([]*bfv.Plaintext, N, N) encPartial := make([]*bfv.Ciphertext, N, N) - ringCtx := ring.NewContext() - ringCtx.SetParameters(params.N, params.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(params) // Ciphertexts to be retrieved. for i := range encInputs { diff --git a/examples/dbfv/psi/psi.go b/examples/dbfv/psi/psi.go index ebd9e48e..5fb62dc7 100644 --- a/examples/dbfv/psi/psi.go +++ b/examples/dbfv/psi/psi.go @@ -172,12 +172,7 @@ func main() { l.Printf("\tSetup done (cloud: %s, party: %s)\n", elapsedRKGCloud+elapsedCKGCloud, elapsedRKGParty+elapsedCKGParty) - ringCtx := ring.NewContext() - ringCtx.SetParameters(params.N, params.Qi) - err = ringCtx.GenNTTParams() - if err != nil { - panic(err) - } + ringCtx := bfv.NewCiphertextRingContext(params) // Pre-loading memory l.Println("> Memory alloc Phase")