From 2d7f0e42b1a532ed92d90e6668544491199fd1ed Mon Sep 17 00:00:00 2001 From: Christian Mouchet Date: Thu, 15 Jun 2023 22:20:59 +0200 Subject: [PATCH] making the evaluation keys independant of the OperandQP type As for the public keys, I think it is better to keep evaluation key types as simple as possible. The "Operand/OperandQP" types are more adapted to the "data" path, i.e. as operands in a circuit. One obvious exemple is that there is no point for keys to have the metadata of a ciphertext.Also, we'll have easier time designing the evaluation logic and evolving the Operand types if the keys do not depend on them. --- drlwe/keygen_gal.go | 4 +- drlwe/keygen_relin.go | 50 ++++++------ rgsw/encryptor.go | 4 +- rgsw/evaluator.go | 68 ++++++++--------- rlwe/evaluator_gadget_product.go | 26 +++---- rlwe/gadgetciphertext.go | 27 +++---- rlwe/keygenerator.go | 2 +- rlwe/keys.go | 126 ++++++++++++++++++++++++------- rlwe/utils.go | 14 ++-- 9 files changed, 194 insertions(+), 127 deletions(-) diff --git a/drlwe/keygen_gal.go b/drlwe/keygen_gal.go index efb8f432..a0449b7e 100644 --- a/drlwe/keygen_gal.go +++ b/drlwe/keygen_gal.go @@ -229,8 +229,8 @@ func (gkg *GaloisKeyGenProtocol) GenGaloisKey(share *GaloisKeyGenShare, crp Galo BITDecomp := len(m[0]) for i := 0; i < RNSDecomp; i++ { for j := 0; j < BITDecomp; j++ { - gk.Value[i][j].Value[0].Copy(&m[i][j]) - gk.Value[i][j].Value[1].Copy(&p[i][j]) + gk.Value[i][j][0].Copy(&m[i][j]) + gk.Value[i][j][1].Copy(&p[i][j]) } } diff --git a/drlwe/keygen_relin.go b/drlwe/keygen_relin.go index acbb78db..9278e644 100644 --- a/drlwe/keygen_relin.go +++ b/drlwe/keygen_relin.go @@ -141,13 +141,13 @@ func (ekg *RelinKeyGenProtocol) GenShareRoundOne(sk *rlwe.SecretKey, crp RelinKe for j := 0; j < BITDecomp; j++ { for i := 0; i < RNSDecomp; i++ { // h = e - ekg.gaussianSamplerQ.Read(shareOut.Value[i][j].Value[0].Q) + ekg.gaussianSamplerQ.Read(shareOut.Value[i][j][0].Q) if hasModulusP { - ringQP.ExtendBasisSmallNormAndCenter(shareOut.Value[i][j].Value[0].Q, levelP, nil, shareOut.Value[i][j].Value[0].P) + ringQP.ExtendBasisSmallNormAndCenter(shareOut.Value[i][j][0].Q, levelP, nil, shareOut.Value[i][j][0].P) } - ringQP.NTT(&shareOut.Value[i][j].Value[0], &shareOut.Value[i][j].Value[0]) + ringQP.NTT(&shareOut.Value[i][j][0], &shareOut.Value[i][j][0]) // h = sk*CrtBaseDecompQi + e for k := 0; k < levelP+1; k++ { @@ -161,7 +161,7 @@ func (ekg *RelinKeyGenProtocol) GenShareRoundOne(sk *rlwe.SecretKey, crp RelinKe qi := ringQ.SubRings[index].Modulus skP := ekg.buf[0].Q.Coeffs[index] - h := shareOut.Value[i][j].Value[0].Q.Coeffs[index] + h := shareOut.Value[i][j][0].Q.Coeffs[index] for w := 0; w < N; w++ { h[w] = ring.CRed(h[w]+skP[w], qi) @@ -169,19 +169,19 @@ func (ekg *RelinKeyGenProtocol) GenShareRoundOne(sk *rlwe.SecretKey, crp RelinKe } // h = sk*CrtBaseDecompQi + -u*a + e - ringQP.MulCoeffsMontgomeryThenSub(&ephSkOut.Value, &c[i][j], &shareOut.Value[i][j].Value[0]) + ringQP.MulCoeffsMontgomeryThenSub(&ephSkOut.Value, &c[i][j], &shareOut.Value[i][j][0]) // Second Element // e_2i - ekg.gaussianSamplerQ.Read(shareOut.Value[i][j].Value[1].Q) + ekg.gaussianSamplerQ.Read(shareOut.Value[i][j][1].Q) if hasModulusP { - ringQP.ExtendBasisSmallNormAndCenter(shareOut.Value[i][j].Value[1].Q, levelP, nil, shareOut.Value[i][j].Value[1].P) + ringQP.ExtendBasisSmallNormAndCenter(shareOut.Value[i][j][1].Q, levelP, nil, shareOut.Value[i][j][1].P) } - ringQP.NTT(&shareOut.Value[i][j].Value[1], &shareOut.Value[i][j].Value[1]) + ringQP.NTT(&shareOut.Value[i][j][1], &shareOut.Value[i][j][1]) // s*a + e_2i - ringQP.MulCoeffsMontgomeryThenAdd(&sk.Value, &c[i][j], &shareOut.Value[i][j].Value[1]) + ringQP.MulCoeffsMontgomeryThenAdd(&sk.Value, &c[i][j], &shareOut.Value[i][j][1]) } ringQ.MulScalar(ekg.buf[0].Q, 1< -1 { - ringQP.ExtendBasisSmallNormAndCenter(shareOut.Value[i][j].Value[1].Q, levelP, nil, shareOut.Value[i][j].Value[1].P) + ringQP.ExtendBasisSmallNormAndCenter(shareOut.Value[i][j][1].Q, levelP, nil, shareOut.Value[i][j][1].P) } - ringQP.NTT(&shareOut.Value[i][j].Value[1], &shareOut.Value[i][j].Value[1]) - ringQP.MulCoeffsMontgomeryThenAdd(ekg.buf[0], &round1.Value[i][j].Value[1], &shareOut.Value[i][j].Value[1]) + ringQP.NTT(&shareOut.Value[i][j][1], &shareOut.Value[i][j][1]) + ringQP.MulCoeffsMontgomeryThenAdd(ekg.buf[0], &round1.Value[i][j][1], &shareOut.Value[i][j][1]) } } } @@ -249,8 +249,8 @@ func (ekg *RelinKeyGenProtocol) GenShareRoundTwo(ephSk, sk *rlwe.SecretKey, roun // AggregateShares combines two RelinKeyGen shares into a single one. func (ekg *RelinKeyGenProtocol) AggregateShares(share1, share2, shareOut *RelinKeyGenShare) { - levelQ := share1.Value[0][0].LevelQ() - levelP := share1.Value[0][0].LevelP() + levelQ := share1.Value[0][0][0].LevelQ() + levelP := share1.Value[0][0][0].LevelP() ringQP := ekg.params.RingQP().AtLevel(levelQ, levelP) @@ -258,8 +258,8 @@ func (ekg *RelinKeyGenProtocol) AggregateShares(share1, share2, shareOut *RelinK BITDecomp := len(shareOut.Value[0]) for i := 0; i < RNSDecomp; i++ { for j := 0; j < BITDecomp; j++ { - ringQP.Add(&share1.Value[i][j].Value[0], &share2.Value[i][j].Value[0], &shareOut.Value[i][j].Value[0]) - ringQP.Add(&share1.Value[i][j].Value[1], &share2.Value[i][j].Value[1], &shareOut.Value[i][j].Value[1]) + ringQP.Add(&share1.Value[i][j][0], &share2.Value[i][j][0], &shareOut.Value[i][j][0]) + ringQP.Add(&share1.Value[i][j][1], &share2.Value[i][j][1], &shareOut.Value[i][j][1]) } } } @@ -277,8 +277,8 @@ func (ekg *RelinKeyGenProtocol) AggregateShares(share1, share2, shareOut *RelinK // = [s * b + P * s^2 + s*e0 + u*e1 + e2 + e3, b] func (ekg *RelinKeyGenProtocol) GenRelinearizationKey(round1 *RelinKeyGenShare, round2 *RelinKeyGenShare, evalKeyOut *rlwe.RelinearizationKey) { - levelQ := round1.Value[0][0].LevelQ() - levelP := round1.Value[0][0].LevelP() + levelQ := round1.Value[0][0][0].LevelQ() + levelP := round1.Value[0][0][0].LevelP() ringQP := ekg.params.RingQP().AtLevel(levelQ, levelP) @@ -286,10 +286,10 @@ func (ekg *RelinKeyGenProtocol) GenRelinearizationKey(round1 *RelinKeyGenShare, BITDecomp := len(round1.Value[0]) for i := 0; i < RNSDecomp; i++ { for j := 0; j < BITDecomp; j++ { - ringQP.Add(&round2.Value[i][j].Value[0], &round2.Value[i][j].Value[1], &evalKeyOut.Value[i][j].Value[0]) - evalKeyOut.Value[i][j].Value[1].Copy(&round1.Value[i][j].Value[1]) - ringQP.MForm(&evalKeyOut.Value[i][j].Value[0], &evalKeyOut.Value[i][j].Value[0]) - ringQP.MForm(&evalKeyOut.Value[i][j].Value[1], &evalKeyOut.Value[i][j].Value[1]) + ringQP.Add(&round2.Value[i][j][0], &round2.Value[i][j][1], &evalKeyOut.Value[i][j][0]) + evalKeyOut.Value[i][j][1].Copy(&round1.Value[i][j][1]) + ringQP.MForm(&evalKeyOut.Value[i][j][0], &evalKeyOut.Value[i][j][0]) + ringQP.MForm(&evalKeyOut.Value[i][j][1], &evalKeyOut.Value[i][j][1]) } } } diff --git a/rgsw/encryptor.go b/rgsw/encryptor.go index 324c22f8..1c32b930 100644 --- a/rgsw/encryptor.go +++ b/rgsw/encryptor.go @@ -69,8 +69,8 @@ func (enc *Encryptor) EncryptZero(ct interface{}) { for j := 0; j < decompPw2; j++ { for i := 0; i < decompRNS; i++ { - enc.EncryptorInterface.EncryptZero(rgswCt.Value[0].Value[i][j]) - enc.EncryptorInterface.EncryptZero(rgswCt.Value[1].Value[i][j]) + enc.EncryptorInterface.EncryptZero(&rlwe.OperandQP{MetaData: rlwe.MetaData{IsNTT: true, IsMontgomery: true}, Value: rgswCt.Value[0].Value[i][j][:]}) + enc.EncryptorInterface.EncryptZero(&rlwe.OperandQP{MetaData: rlwe.MetaData{IsNTT: true, IsMontgomery: true}, Value: rgswCt.Value[1].Value[i][j][:]}) } } } diff --git a/rgsw/evaluator.go b/rgsw/evaluator.go index 43243287..9fd13b36 100644 --- a/rgsw/evaluator.go +++ b/rgsw/evaluator.go @@ -103,12 +103,12 @@ func (eval *Evaluator) externalProduct32Bit(ct0 *rlwe.Ciphertext, rgsw *Cipherte ring.MaskVec(eval.BuffInvNTT.Coeffs[0], j*pw2, mask, cw) if j == 0 && i == 0 { subRing.NTTLazy(cw, cwNTT) - subRing.MulCoeffsLazy(el.Value[0][j].Value[0].Q.Coeffs[0], cwNTT, acc0) - subRing.MulCoeffsLazy(el.Value[0][j].Value[1].Q.Coeffs[0], cwNTT, acc1) + subRing.MulCoeffsLazy(el.Value[0][j][0].Q.Coeffs[0], cwNTT, acc0) + subRing.MulCoeffsLazy(el.Value[0][j][1].Q.Coeffs[0], cwNTT, acc1) } else { subRing.NTTLazy(cw, cwNTT) - subRing.MulCoeffsLazyThenAddLazy(el.Value[0][j].Value[0].Q.Coeffs[0], cwNTT, acc0) - subRing.MulCoeffsLazyThenAddLazy(el.Value[0][j].Value[1].Q.Coeffs[0], cwNTT, acc1) + subRing.MulCoeffsLazyThenAddLazy(el.Value[0][j][0].Q.Coeffs[0], cwNTT, acc0) + subRing.MulCoeffsLazyThenAddLazy(el.Value[0][j][1].Q.Coeffs[0], cwNTT, acc1) } } } @@ -148,15 +148,15 @@ func (eval *Evaluator) externalProductInPlaceSinglePAndBitDecomp(ct0 *rlwe.Ciphe for u, s := range ringQ.SubRings[:levelQ+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomery(el.Value[i][j].Value[0].Q.Coeffs[u], cwNTT, c0QP.Q.Coeffs[u]) - s.MulCoeffsMontgomery(el.Value[i][j].Value[1].Q.Coeffs[u], cwNTT, c1QP.Q.Coeffs[u]) + s.MulCoeffsMontgomery(el.Value[i][j][0].Q.Coeffs[u], cwNTT, c0QP.Q.Coeffs[u]) + s.MulCoeffsMontgomery(el.Value[i][j][1].Q.Coeffs[u], cwNTT, c1QP.Q.Coeffs[u]) } if ringP != nil { for u, s := range ringP.SubRings[:levelP+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomery(el.Value[i][j].Value[0].P.Coeffs[u], cwNTT, c0QP.P.Coeffs[u]) - s.MulCoeffsMontgomery(el.Value[i][j].Value[1].P.Coeffs[u], cwNTT, c1QP.P.Coeffs[u]) + s.MulCoeffsMontgomery(el.Value[i][j][0].P.Coeffs[u], cwNTT, c0QP.P.Coeffs[u]) + s.MulCoeffsMontgomery(el.Value[i][j][1].P.Coeffs[u], cwNTT, c1QP.P.Coeffs[u]) } } @@ -164,15 +164,15 @@ func (eval *Evaluator) externalProductInPlaceSinglePAndBitDecomp(ct0 *rlwe.Ciphe for u, s := range ringQ.SubRings[:levelQ+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomeryThenAdd(el.Value[i][j].Value[0].Q.Coeffs[u], cwNTT, c0QP.Q.Coeffs[u]) - s.MulCoeffsMontgomeryThenAdd(el.Value[i][j].Value[1].Q.Coeffs[u], cwNTT, c1QP.Q.Coeffs[u]) + s.MulCoeffsMontgomeryThenAdd(el.Value[i][j][0].Q.Coeffs[u], cwNTT, c0QP.Q.Coeffs[u]) + s.MulCoeffsMontgomeryThenAdd(el.Value[i][j][1].Q.Coeffs[u], cwNTT, c1QP.Q.Coeffs[u]) } if ringP != nil { for u, s := range ringP.SubRings[:levelP+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomeryThenAdd(el.Value[i][j].Value[0].P.Coeffs[u], cwNTT, c0QP.P.Coeffs[u]) - s.MulCoeffsMontgomeryThenAdd(el.Value[i][j].Value[1].P.Coeffs[u], cwNTT, c1QP.P.Coeffs[u]) + s.MulCoeffsMontgomeryThenAdd(el.Value[i][j][0].P.Coeffs[u], cwNTT, c0QP.P.Coeffs[u]) + s.MulCoeffsMontgomeryThenAdd(el.Value[i][j][1].P.Coeffs[u], cwNTT, c1QP.P.Coeffs[u]) } } } @@ -218,11 +218,11 @@ func (eval *Evaluator) externalProductInPlaceMultipleP(levelQ, levelP int, ct0 * eval.DecomposeSingleNTT(levelQ, levelP, levelP+1, i, c2NTT, c2InvNTT, c2QP.Q, c2QP.P) if k == 0 && i == 0 { - ringQP.MulCoeffsMontgomeryLazy(&el.Value[i][0].Value[0], &c2QP, &c0QP) - ringQP.MulCoeffsMontgomeryLazy(&el.Value[i][0].Value[1], &c2QP, &c1QP) + ringQP.MulCoeffsMontgomeryLazy(&el.Value[i][0][0], &c2QP, &c0QP) + ringQP.MulCoeffsMontgomeryLazy(&el.Value[i][0][1], &c2QP, &c1QP) } else { - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el.Value[i][0].Value[0], &c2QP, &c0QP) - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el.Value[i][0].Value[1], &c2QP, &c1QP) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el.Value[i][0][0], &c2QP, &c0QP) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el.Value[i][0][1], &c2QP, &c1QP) } if reduce%QiOverF == QiOverF-1 { @@ -271,18 +271,18 @@ func AddLazy(op interface{}, ringQP ringqp.Ring, ctOut *Ciphertext) { end = nQ } for k := start; k < end; k++ { - s.AddLazy(ctOut.Value[0].Value[i][j].Value[0].Q.Coeffs[k], el.Value[j].Coeffs[k], ctOut.Value[0].Value[i][j].Value[0].Q.Coeffs[k]) - s.AddLazy(ctOut.Value[1].Value[i][j].Value[1].Q.Coeffs[k], el.Value[j].Coeffs[k], ctOut.Value[1].Value[i][j].Value[1].Q.Coeffs[k]) + s.AddLazy(ctOut.Value[0].Value[i][j][0].Q.Coeffs[k], el.Value[j].Coeffs[k], ctOut.Value[0].Value[i][j][0].Q.Coeffs[k]) + s.AddLazy(ctOut.Value[1].Value[i][j][1].Q.Coeffs[k], el.Value[j].Coeffs[k], ctOut.Value[1].Value[i][j][1].Q.Coeffs[k]) } } } case *Ciphertext: for i := range el.Value[0].Value { for j := range el.Value[0].Value[i] { - ringQP.AddLazy(&ctOut.Value[0].Value[i][j].Value[0], &el.Value[0].Value[i][j].Value[0], &ctOut.Value[0].Value[i][j].Value[0]) - ringQP.AddLazy(&ctOut.Value[0].Value[i][j].Value[1], &el.Value[0].Value[i][j].Value[1], &ctOut.Value[0].Value[i][j].Value[1]) - ringQP.AddLazy(&ctOut.Value[1].Value[i][j].Value[0], &el.Value[1].Value[i][j].Value[0], &ctOut.Value[1].Value[i][j].Value[0]) - ringQP.AddLazy(&ctOut.Value[1].Value[i][j].Value[1], &el.Value[1].Value[i][j].Value[1], &ctOut.Value[1].Value[i][j].Value[1]) + ringQP.AddLazy(&ctOut.Value[0].Value[i][j][0], &el.Value[0].Value[i][j][0], &ctOut.Value[0].Value[i][j][0]) + ringQP.AddLazy(&ctOut.Value[0].Value[i][j][1], &el.Value[0].Value[i][j][1], &ctOut.Value[0].Value[i][j][1]) + ringQP.AddLazy(&ctOut.Value[1].Value[i][j][0], &el.Value[1].Value[i][j][0], &ctOut.Value[1].Value[i][j][0]) + ringQP.AddLazy(&ctOut.Value[1].Value[i][j][1], &el.Value[1].Value[i][j][1], &ctOut.Value[1].Value[i][j][1]) } } default: @@ -294,10 +294,10 @@ func AddLazy(op interface{}, ringQP ringqp.Ring, ctOut *Ciphertext) { func Reduce(ctIn *Ciphertext, ringQP ringqp.Ring, ctOut *Ciphertext) { for i := range ctIn.Value[0].Value { for j := range ctIn.Value[0].Value[i] { - ringQP.Reduce(&ctIn.Value[0].Value[i][j].Value[0], &ctOut.Value[0].Value[i][j].Value[0]) - ringQP.Reduce(&ctIn.Value[0].Value[i][j].Value[1], &ctOut.Value[0].Value[i][j].Value[1]) - ringQP.Reduce(&ctIn.Value[1].Value[i][j].Value[0], &ctOut.Value[1].Value[i][j].Value[0]) - ringQP.Reduce(&ctIn.Value[1].Value[i][j].Value[1], &ctOut.Value[1].Value[i][j].Value[1]) + ringQP.Reduce(&ctIn.Value[0].Value[i][j][0], &ctOut.Value[0].Value[i][j][0]) + ringQP.Reduce(&ctIn.Value[0].Value[i][j][1], &ctOut.Value[0].Value[i][j][1]) + ringQP.Reduce(&ctIn.Value[1].Value[i][j][0], &ctOut.Value[1].Value[i][j][0]) + ringQP.Reduce(&ctIn.Value[1].Value[i][j][1], &ctOut.Value[1].Value[i][j][1]) } } } @@ -306,10 +306,10 @@ func Reduce(ctIn *Ciphertext, ringQP ringqp.Ring, ctOut *Ciphertext) { func MulByXPowAlphaMinusOneLazy(ctIn *Ciphertext, powXMinusOne ringqp.Poly, ringQP ringqp.Ring, ctOut *Ciphertext) { for i := range ctIn.Value[0].Value { for j := range ctIn.Value[0].Value[i] { - ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[0].Value[i][j].Value[0], &powXMinusOne, &ctOut.Value[0].Value[i][j].Value[0]) - ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[0].Value[i][j].Value[1], &powXMinusOne, &ctOut.Value[0].Value[i][j].Value[1]) - ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[1].Value[i][j].Value[0], &powXMinusOne, &ctOut.Value[1].Value[i][j].Value[0]) - ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[1].Value[i][j].Value[1], &powXMinusOne, &ctOut.Value[1].Value[i][j].Value[1]) + ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[0].Value[i][j][0], &powXMinusOne, &ctOut.Value[0].Value[i][j][0]) + ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[0].Value[i][j][1], &powXMinusOne, &ctOut.Value[0].Value[i][j][1]) + ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[1].Value[i][j][0], &powXMinusOne, &ctOut.Value[1].Value[i][j][0]) + ringQP.MulCoeffsMontgomeryLazy(&ctIn.Value[1].Value[i][j][1], &powXMinusOne, &ctOut.Value[1].Value[i][j][1]) } } } @@ -318,10 +318,10 @@ func MulByXPowAlphaMinusOneLazy(ctIn *Ciphertext, powXMinusOne ringqp.Poly, ring func MulByXPowAlphaMinusOneThenAddLazy(ctIn *Ciphertext, powXMinusOne ringqp.Poly, ringQP ringqp.Ring, ctOut *Ciphertext) { for i := range ctIn.Value[0].Value { for j := range ctIn.Value[0].Value[i] { - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[0].Value[i][j].Value[0], &powXMinusOne, &ctOut.Value[0].Value[i][j].Value[0]) - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[0].Value[i][j].Value[1], &powXMinusOne, &ctOut.Value[0].Value[i][j].Value[1]) - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[1].Value[i][j].Value[0], &powXMinusOne, &ctOut.Value[1].Value[i][j].Value[0]) - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[1].Value[i][j].Value[1], &powXMinusOne, &ctOut.Value[1].Value[i][j].Value[1]) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[0].Value[i][j][0], &powXMinusOne, &ctOut.Value[0].Value[i][j][0]) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[0].Value[i][j][1], &powXMinusOne, &ctOut.Value[0].Value[i][j][1]) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[1].Value[i][j][0], &powXMinusOne, &ctOut.Value[1].Value[i][j][0]) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&ctIn.Value[1].Value[i][j][1], &powXMinusOne, &ctOut.Value[1].Value[i][j][1]) } } } diff --git a/rlwe/evaluator_gadget_product.go b/rlwe/evaluator_gadget_product.go index 136a7730..09dbf7e2 100644 --- a/rlwe/evaluator_gadget_product.go +++ b/rlwe/evaluator_gadget_product.go @@ -141,11 +141,11 @@ func (eval *Evaluator) gadgetProductMultiplePLazy(levelQ int, cx *ring.Poly, gad eval.DecomposeSingleNTT(levelQ, levelP, levelP+1, i, cxNTT, cxInvNTT, c2QP.Q, c2QP.P) if i == 0 { - ringQP.MulCoeffsMontgomeryLazy(&el[i][0].Value[0], &c2QP, &ct.Value[0]) - ringQP.MulCoeffsMontgomeryLazy(&el[i][0].Value[1], &c2QP, &ct.Value[1]) + ringQP.MulCoeffsMontgomeryLazy(&el[i][0][0], &c2QP, &ct.Value[0]) + ringQP.MulCoeffsMontgomeryLazy(&el[i][0][1], &c2QP, &ct.Value[1]) } else { - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el[i][0].Value[0], &c2QP, &ct.Value[0]) - ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el[i][0].Value[1], &c2QP, &ct.Value[1]) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el[i][0][0], &c2QP, &ct.Value[0]) + ringQP.MulCoeffsMontgomeryLazyThenAddLazy(&el[i][0][1], &c2QP, &ct.Value[1]) } if reduce%QiOverF == QiOverF-1 { @@ -218,30 +218,30 @@ func (eval *Evaluator) gadgetProductSinglePAndBitDecompLazy(levelQ int, cx *ring if i == 0 && j == 0 { for u, s := range ringQ.SubRings[:levelQ+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomeryLazy(el[i][j].Value[0].Q.Coeffs[u], cwNTT, ct.Value[0].Q.Coeffs[u]) - s.MulCoeffsMontgomeryLazy(el[i][j].Value[1].Q.Coeffs[u], cwNTT, ct.Value[1].Q.Coeffs[u]) + s.MulCoeffsMontgomeryLazy(el[i][j][0].Q.Coeffs[u], cwNTT, ct.Value[0].Q.Coeffs[u]) + s.MulCoeffsMontgomeryLazy(el[i][j][1].Q.Coeffs[u], cwNTT, ct.Value[1].Q.Coeffs[u]) } if ringP != nil { for u, s := range ringP.SubRings[:levelP+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomeryLazy(el[i][j].Value[0].P.Coeffs[u], cwNTT, ct.Value[0].P.Coeffs[u]) - s.MulCoeffsMontgomeryLazy(el[i][j].Value[1].P.Coeffs[u], cwNTT, ct.Value[1].P.Coeffs[u]) + s.MulCoeffsMontgomeryLazy(el[i][j][0].P.Coeffs[u], cwNTT, ct.Value[0].P.Coeffs[u]) + s.MulCoeffsMontgomeryLazy(el[i][j][1].P.Coeffs[u], cwNTT, ct.Value[1].P.Coeffs[u]) } } } else { for u, s := range ringQ.SubRings[:levelQ+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j].Value[0].Q.Coeffs[u], cwNTT, ct.Value[0].Q.Coeffs[u]) - s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j].Value[1].Q.Coeffs[u], cwNTT, ct.Value[1].Q.Coeffs[u]) + s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][0].Q.Coeffs[u], cwNTT, ct.Value[0].Q.Coeffs[u]) + s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][1].Q.Coeffs[u], cwNTT, ct.Value[1].Q.Coeffs[u]) } if ringP != nil { for u, s := range ringP.SubRings[:levelP+1] { s.NTTLazy(cw, cwNTT) - s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j].Value[0].P.Coeffs[u], cwNTT, ct.Value[0].P.Coeffs[u]) - s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j].Value[1].P.Coeffs[u], cwNTT, ct.Value[1].P.Coeffs[u]) + s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][0].P.Coeffs[u], cwNTT, ct.Value[0].P.Coeffs[u]) + s.MulCoeffsMontgomeryLazyThenAddLazy(el[i][j][1].P.Coeffs[u], cwNTT, ct.Value[1].P.Coeffs[u]) } } } @@ -323,7 +323,7 @@ func (eval *Evaluator) GadgetProductHoistedLazy(levelQ int, BuffQPDecompQP []rin var reduce int for i := 0; i < decompRNS; i++ { - gct := gadgetCt.Value[i][0].Value + gct := gadgetCt.Value[i][0] if i == 0 { ringQP.MulCoeffsMontgomeryLazy(&gct[0], &BuffQPDecompQP[i], c0QP) diff --git a/rlwe/gadgetciphertext.go b/rlwe/gadgetciphertext.go index 08a841af..03b613cf 100644 --- a/rlwe/gadgetciphertext.go +++ b/rlwe/gadgetciphertext.go @@ -12,24 +12,19 @@ import ( // GadgetCiphertext is a struct for storing an encrypted // plaintext times the gadget power matrix. type GadgetCiphertext struct { - Value structs.Matrix[OperandQP] + Value structs.Matrix[tupleQP] } // NewGadgetCiphertext returns a new Ciphertext key with pre-allocated zero-value. // Ciphertext is always in the NTT domain. func NewGadgetCiphertext(params ParametersInterface, levelQ, levelP, decompRNS, decompBIT int) *GadgetCiphertext { - m := make([][]OperandQP, decompRNS) + m := make(structs.Matrix[tupleQP], decompRNS) for i := 0; i < decompRNS; i++ { - v := make([]OperandQP, decompBIT) - - for j := range v { - v[j] = *NewOperandQP(params, 1, levelQ, levelP) - v[j].IsNTT = true - v[j].IsMontgomery = true + m[i] = make([]tupleQP, decompBIT) + for j := range m[i] { + m[i][j] = newTupleQPAtLevel(params, levelQ, levelP) } - - m[i] = v } return &GadgetCiphertext{Value: m} @@ -37,12 +32,12 @@ func NewGadgetCiphertext(params ParametersInterface, levelQ, levelP, decompRNS, // LevelQ returns the level of the modulus Q of the target Ciphertext. func (ct GadgetCiphertext) LevelQ() int { - return ct.Value[0][0].LevelQ() + return ct.Value[0][0][0].LevelQ() } // LevelP returns the level of the modulus P of the target Ciphertext. func (ct GadgetCiphertext) LevelP() int { - return ct.Value[0][0].LevelP() + return ct.Value[0][0][0].LevelP() } // Equal checks two Ciphertexts for equality. @@ -55,11 +50,11 @@ func (ct *GadgetCiphertext) CopyNew() (ctCopy *GadgetCiphertext) { if ct == nil || len(ct.Value) == 0 { return nil } - v := make([][]OperandQP, len(ct.Value)) + v := make(structs.Matrix[tupleQP], len(ct.Value)) for i := range ct.Value { - v[i] = make([]OperandQP, len(ct.Value[0])) + v[i] = make([]tupleQP, len(ct.Value[0])) for j, el := range ct.Value[i] { - v[i][j] = *el.CopyNew() + v[i][j] = el.CopyNew() } } return &GadgetCiphertext{Value: v} @@ -162,7 +157,7 @@ func AddPolyTimesGadgetVectorToGadgetCiphertext(pt *ring.Poly, cts []GadgetCiphe p0tmp := buff.Coeffs[index] for u, ct := range cts { - p1tmp := ct.Value[i][j].Value[u].Q.Coeffs[index] + p1tmp := ct.Value[i][j][u].Q.Coeffs[index] for w := 0; w < N; w++ { p1tmp[w] = ring.CRed(p1tmp[w]+p0tmp[w], qi) } diff --git a/rlwe/keygenerator.go b/rlwe/keygenerator.go index cf2e23d9..d305f49f 100644 --- a/rlwe/keygenerator.go +++ b/rlwe/keygenerator.go @@ -304,7 +304,7 @@ func (kgen *KeyGenerator) genEvaluationKey(skIn *ring.Poly, skOut *SecretKey, ev // Samples an encryption of zero for each element of the EvaluationKey. for i := 0; i < len(evk.Value); i++ { for j := 0; j < len(evk.Value[0]); j++ { - enc.EncryptZero(evk.Value[i][j]) + enc.EncryptZero(&OperandQP{MetaData: MetaData{IsNTT: true, IsMontgomery: true}, Value: evk.Value[i][j][:]}) } } diff --git a/rlwe/keys.go b/rlwe/keys.go index 65e4d2d3..00369098 100644 --- a/rlwe/keys.go +++ b/rlwe/keys.go @@ -95,29 +95,31 @@ func (sk *SecretKey) UnmarshalBinary(p []byte) (err error) { return sk.Value.UnmarshalBinary(p) } -// PublicKey is a type for generic RLWE public keys. -// The Value field stores the polynomials in NTT and Montgomery form. -type PublicKey struct { - Value [2]ringqp.Poly +type tupleQP [2]ringqp.Poly + +// NewPublicKey returns a new PublicKey with zero values. +func newTupleQP(params ParametersInterface) (pk tupleQP) { + return [2]ringqp.Poly{*params.RingQP().NewPoly(), *params.RingQP().NewPoly()} } // NewPublicKey returns a new PublicKey with zero values. -func NewPublicKey(params ParametersInterface) (pk *PublicKey) { - return &PublicKey{Value: [2]ringqp.Poly{*params.RingQP().NewPoly(), *params.RingQP().NewPoly()}} +func newTupleQPAtLevel(params ParametersInterface, levelQ, levelP int) (pk tupleQP) { + rqp := params.RingQP().AtLevel(levelQ, levelP) + return [2]ringqp.Poly{*rqp.NewPoly(), *rqp.NewPoly()} } // CopyNew creates a deep copy of the target PublicKey and returns it. -func (p *PublicKey) CopyNew() *PublicKey { - return &PublicKey{Value: [2]ringqp.Poly{*p.Value[0].CopyNew(), *p.Value[1].CopyNew()}} +func (p *tupleQP) CopyNew() tupleQP { + return [2]ringqp.Poly{*p[0].CopyNew(), *p[1].CopyNew()} } // Equal performs a deep equal. -func (p *PublicKey) Equal(other *PublicKey) bool { - return p.Value[0].Equal(&other.Value[0]) && p.Value[1].Equal(&other.Value[1]) +func (p *tupleQP) Equal(other tupleQP) bool { + return p[0].Equal(&other[0]) && p[1].Equal(&other[1]) } -func (p *PublicKey) BinarySize() int { - return structs.Vector[ringqp.Poly](p.Value[:]).BinarySize() +func (p *tupleQP) BinarySize() int { + return structs.Vector[ringqp.Poly](p[:]).BinarySize() } // WriteTo writes the object on an io.Writer. It implements the io.WriterTo @@ -131,8 +133,8 @@ func (p *PublicKey) BinarySize() int { // io.Writer in a pre-allocated bufio.Writer. // - When writing to a pre-allocated var b []byte, it is preferable to pass // buffer.NewBuffer(b) as w (see lattigo/utils/buffer/buffer.go). -func (p *PublicKey) WriteTo(w io.Writer) (n int64, err error) { - v := structs.Vector[ringqp.Poly](p.Value[:]) +func (p *tupleQP) WriteTo(w io.Writer) (n int64, err error) { + v := structs.Vector[ringqp.Poly](p[:]) return v.WriteTo(w) } @@ -147,8 +149,8 @@ func (p *PublicKey) WriteTo(w io.Writer) (n int64, err error) { // first wrap io.Reader in a pre-allocated bufio.Reader. // - When reading from a var b []byte, it is preferable to pass a buffer.NewBuffer(b) // as w (see lattigo/utils/buffer/buffer.go). -func (p *PublicKey) ReadFrom(r io.Reader) (n int64, err error) { - v := structs.Vector[ringqp.Poly](p.Value[:]) +func (p *tupleQP) ReadFrom(r io.Reader) (n int64, err error) { + v := structs.Vector[ringqp.Poly](p[:]) n, err = v.ReadFrom(r) if len(v) != 2 { return n, fmt.Errorf("bad public key format") @@ -157,15 +159,15 @@ func (p *PublicKey) ReadFrom(r io.Reader) (n int64, err error) { } // MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. -func (p *PublicKey) MarshalBinary() ([]byte, error) { - v := structs.Vector[ringqp.Poly](p.Value[:]) +func (p *tupleQP) MarshalBinary() ([]byte, error) { + v := structs.Vector[ringqp.Poly](p[:]) return v.MarshalBinary() } // UnmarshalBinary decodes a slice of bytes generated by // MarshalBinary or WriteTo on the object. -func (p *PublicKey) UnmarshalBinary(b []byte) error { - v := structs.Vector[ringqp.Poly](p.Value[:]) +func (p *tupleQP) UnmarshalBinary(b []byte) error { + v := structs.Vector[ringqp.Poly](p[:]) err := v.UnmarshalBinary(b) if len(v) != 2 { return fmt.Errorf("bad public key format") @@ -173,6 +175,72 @@ func (p *PublicKey) UnmarshalBinary(b []byte) error { return err } +// PublicKey is a type for generic RLWE public keys. +// The Value field stores the polynomials in NTT and Montgomery form. +type PublicKey struct { + Value tupleQP +} + +// NewPublicKey returns a new PublicKey with zero values. +func NewPublicKey(params ParametersInterface) (pk *PublicKey) { + return &PublicKey{Value: newTupleQP(params)} +} + +// CopyNew creates a deep copy of the target PublicKey and returns it. +func (p *PublicKey) CopyNew() *PublicKey { + return &PublicKey{Value: p.Value.CopyNew()} +} + +// Equal performs a deep equal. +func (p *PublicKey) Equal(other *PublicKey) bool { + return p.Value.Equal(other.Value) +} + +func (p *PublicKey) BinarySize() int { + return p.Value.BinarySize() +} + +// WriteTo writes the object on an io.Writer. It implements the io.WriterTo +// interface, and will write exactly object.BinarySize() bytes on w. +// +// Unless w implements the buffer.Writer interface (see lattigo/utils/buffer/writer.go), +// it will be wrapped into a bufio.Writer. Since this requires allocations, it +// is preferable to pass a buffer.Writer directly: +// +// - When writing multiple times to a io.Writer, it is preferable to first wrap the +// io.Writer in a pre-allocated bufio.Writer. +// - When writing to a pre-allocated var b []byte, it is preferable to pass +// buffer.NewBuffer(b) as w (see lattigo/utils/buffer/buffer.go). +func (p *PublicKey) WriteTo(w io.Writer) (n int64, err error) { + return p.Value.WriteTo(w) +} + +// ReadFrom reads on the object from an io.Writer. It implements the +// io.ReaderFrom interface. +// +// Unless r implements the buffer.Reader interface (see see lattigo/utils/buffer/reader.go), +// it will be wrapped into a bufio.Reader. Since this requires allocation, it +// is preferable to pass a buffer.Reader directly: +// +// - When reading multiple values from a io.Reader, it is preferable to first +// first wrap io.Reader in a pre-allocated bufio.Reader. +// - When reading from a var b []byte, it is preferable to pass a buffer.NewBuffer(b) +// as w (see lattigo/utils/buffer/buffer.go). +func (p *PublicKey) ReadFrom(r io.Reader) (n int64, err error) { + return p.Value.ReadFrom(r) +} + +// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes. +func (p *PublicKey) MarshalBinary() ([]byte, error) { + return p.Value.MarshalBinary() +} + +// UnmarshalBinary decodes a slice of bytes generated by +// MarshalBinary or WriteTo on the object. +func (p *PublicKey) UnmarshalBinary(b []byte) error { + return p.Value.UnmarshalBinary(b) +} + // EvaluationKey is a public key indended to be used during the evaluation phase of a homomorphic circuit. // It provides a one way public and non-interactive re-encryption from a ciphertext encrypted under `skIn` // to a ciphertext encrypted under `skOut`. @@ -192,13 +260,17 @@ type EvaluationKey struct { // NewEvaluationKey returns a new EvaluationKey with pre-allocated zero-value func NewEvaluationKey(params ParametersInterface, levelQ, levelP int) *EvaluationKey { - return &EvaluationKey{GadgetCiphertext: *NewGadgetCiphertext( - params, - levelQ, - levelP, - params.DecompRNS(levelQ, levelP), - params.DecompPw2(levelQ, levelP), - )} + //evk := new(EvaluationKey) + // drns := params.DecompRNS(levelQ, levelP) + // dpw2 := params.DecompPw2(levelQ, levelP) + // evk.Value = make(structs.Matrix[tupleQP], drns) + // for i := range evk.Value { + // evk.Value[i] = make([][2]ringqp.Poly, dpw2) + // for j := range evk.Value[i] { + // evk.Value[i][j] = NewPublicKey(params).Value + // } + // } + return &EvaluationKey{GadgetCiphertext: *NewGadgetCiphertext(params, levelQ, levelP, params.DecompRNS(levelQ, levelP), params.DecompPw2(levelQ, levelP))} } // CopyNew creates a deep copy of the target EvaluationKey and returns it. diff --git a/rlwe/utils.go b/rlwe/utils.go index 8bcef4ea..3e673b19 100644 --- a/rlwe/utils.go +++ b/rlwe/utils.go @@ -74,7 +74,7 @@ func EvaluationKeyIsCorrect(evk *EvaluationKey, skIn, skOut *SecretKey, params P // [-asIn + w*P*sOut + e, a] + [asIn] for i := range evk.Value { for j := range evk.Value[i] { - ringQP.MulCoeffsMontgomeryThenAdd(&evk.Value[i][j].Value[1], &skOut.Value, &evk.Value[i][j].Value[0]) + ringQP.MulCoeffsMontgomeryThenAdd(&evk.Value[i][j][1], &skOut.Value, &evk.Value[i][j][0]) } } @@ -83,7 +83,7 @@ func EvaluationKeyIsCorrect(evk *EvaluationKey, skIn, skOut *SecretKey, params P for i := range evk.Value { // RNS decomp if i > 0 { for j := range evk.Value[i] { // PW2 decomp - ringQP.Add(&evk.Value[0][j].Value[0], &evk.Value[i][j].Value[0], &evk.Value[0][j].Value[0]) + ringQP.Add(&evk.Value[0][j][0], &evk.Value[i][j][0], &evk.Value[0][j][0]) } } } @@ -96,22 +96,22 @@ func EvaluationKeyIsCorrect(evk *EvaluationKey, skIn, skOut *SecretKey, params P for i := 0; i < decompPw2; i++ { // P*s^i + sum(e) - P*s^i = sum(e) - ringQ.Sub(evk.Value[0][i].Value[0].Q, skIn.Value.Q, evk.Value[0][i].Value[0].Q) + ringQ.Sub(evk.Value[0][i][0].Q, skIn.Value.Q, evk.Value[0][i][0].Q) // Checks that the error is below the bound // Worst error bound is N * floor(6*sigma) * #Keys - ringQP.INTT(&evk.Value[0][i].Value[0], &evk.Value[0][i].Value[0]) - ringQP.IMForm(&evk.Value[0][i].Value[0], &evk.Value[0][i].Value[0]) + ringQP.INTT(&evk.Value[0][i][0], &evk.Value[0][i][0]) + ringQP.IMForm(&evk.Value[0][i][0], &evk.Value[0][i][0]) // Worst bound of inner sum // N*#Keys*(N * #Parties * floor(sigma*6) + #Parties * floor(sigma*6) + N * #Parties + #Parties * floor(6*sigma)) - if log2Bound < ringQ.Log2OfStandardDeviation(evk.Value[0][i].Value[0].Q) { + if log2Bound < ringQ.Log2OfStandardDeviation(evk.Value[0][i][0].Q) { return false } if levelP != -1 { - if log2Bound < ringP.Log2OfStandardDeviation(evk.Value[0][i].Value[0].P) { + if log2Bound < ringP.Log2OfStandardDeviation(evk.Value[0][i][0].P) { return false } }