mirror of
https://github.com/tuneinsight/lattigo.git
synced 2025-09-13 03:27:14 +00:00
-
This commit is contained in:
@@ -184,7 +184,7 @@ func (eval *evaluator) getElemAndCheckUnary(op0, opOut Operand, opOutMinDegree u
|
||||
panic("receiver operand degree is too small")
|
||||
}
|
||||
el0, elOut = op0.El(), opOut.El()
|
||||
return // TODO: more checks on elements
|
||||
return
|
||||
}
|
||||
|
||||
// evaluateInPlaceBinary applies the provided function in place on el0 and el1 and returns the result in elOut.
|
||||
|
||||
@@ -166,8 +166,7 @@ func (keygen *keyGenerator) GenPublicKey(sk *SecretKey) (pk *PublicKey) {
|
||||
ringQP.NTT(pk.pk[0], pk.pk[0])
|
||||
pk.pk[1] = keygen.uniformSampler.ReadNew()
|
||||
|
||||
ringQP.MulCoeffsMontgomeryAndAdd(sk.sk, pk.pk[1], pk.pk[0])
|
||||
ringQP.Neg(pk.pk[0], pk.pk[0])
|
||||
ringQP.MulCoeffsMontgomeryAndSub(sk.sk, pk.pk[1], pk.pk[0])
|
||||
|
||||
return pk
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import (
|
||||
// MaxLogN is the log2 of the largest supported polynomial modulus degree.
|
||||
const MaxLogN = 16
|
||||
|
||||
// MinLogN is the log2 of the smallest supported polynomial modulus degree.
|
||||
const MinLogN = 4
|
||||
|
||||
// MaxModuliCount is the largest supported number of moduli in the RNS representation.
|
||||
const MaxModuliCount = 34
|
||||
|
||||
@@ -178,7 +181,7 @@ func NewParametersFromModuli(logN uint64, m *Moduli, t uint64) (p *Parameters, e
|
||||
|
||||
p = new(Parameters)
|
||||
|
||||
if logN < 0 || logN > MaxLogN {
|
||||
if logN < MinLogN || logN > MaxLogN {
|
||||
return nil, fmt.Errorf("invalid polynomial ring log degree: %d", logN)
|
||||
}
|
||||
|
||||
@@ -431,13 +434,19 @@ func (p *Parameters) MarshalBinary() ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
// data : 19 byte + len(QPi) * 8 byte
|
||||
// 1 byte : logN
|
||||
// 1 byte : #pi
|
||||
// 1 byte : #pi
|
||||
// 8 byte : t
|
||||
// 8 byte : sigma
|
||||
b := utils.NewBuffer(make([]byte, 0, 19+(len(p.qi)+len(p.pi))<<3))
|
||||
|
||||
b.WriteUint8(uint8(p.logN))
|
||||
b.WriteUint8(uint8(len(p.qi)))
|
||||
b.WriteUint8(uint8(len(p.pi)))
|
||||
b.WriteUint64(p.t)
|
||||
b.WriteUint64(uint64(p.sigma * (1 << 32)))
|
||||
b.WriteUint64(math.Float64bits(p.sigma))
|
||||
b.WriteUint64Slice(p.qi)
|
||||
b.WriteUint64Slice(p.pi)
|
||||
|
||||
@@ -446,7 +455,7 @@ func (p *Parameters) MarshalBinary() ([]byte, error) {
|
||||
|
||||
// UnmarshalBinary decodes a []byte into a parameter set struct.
|
||||
func (p *Parameters) UnmarshalBinary(data []byte) error {
|
||||
if len(data) < 3 {
|
||||
if len(data) < 19 {
|
||||
return errors.New("invalid parameters encoding")
|
||||
}
|
||||
b := utils.NewBuffer(data)
|
||||
@@ -461,14 +470,14 @@ func (p *Parameters) UnmarshalBinary(data []byte) error {
|
||||
lenPi := b.ReadUint8()
|
||||
|
||||
p.t = b.ReadUint64()
|
||||
p.sigma = math.Round((float64(b.ReadUint64())/float64(1<<32))*100) / 100
|
||||
p.sigma = math.Float64frombits(b.ReadUint64())
|
||||
p.qi = make([]uint64, lenQi, lenQi)
|
||||
p.pi = make([]uint64, lenPi, lenPi)
|
||||
|
||||
b.ReadUint64Slice(p.qi)
|
||||
b.ReadUint64Slice(p.pi)
|
||||
|
||||
err := checkModuli(p.Moduli(), p.logN) // TODO: check more than moduli.
|
||||
err := checkModuli(p.Moduli(), p.logN)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -518,7 +527,7 @@ func checkLogModuli(lm *LogModuli) (err error) {
|
||||
|
||||
// Checks if the parameters are empty
|
||||
if lm.LogQi == nil || len(lm.LogQi) == 0 {
|
||||
return fmt.Errorf("nil or empty slice provided as LogModuli.LogQi") // TODO: are our algorithm working with empty mult basis ?
|
||||
return fmt.Errorf("nil or empty slice provided as LogModuli.LogQi")
|
||||
}
|
||||
|
||||
if len(lm.LogQi) > MaxModuliCount {
|
||||
|
||||
@@ -398,13 +398,13 @@ func (eval *evaluator) rotateHoistedNoModDown(ct0 *Ciphertext, rotations []uint6
|
||||
ringP := eval.ringP
|
||||
|
||||
c2NTT := ct0.value[1]
|
||||
c2InvNTT := ringQ.NewPoly() // TODO : maybe have a pre-allocated memory pool ?
|
||||
c2InvNTT := ringQ.NewPoly() // IMPROVEMENT: maybe have a pre-allocated memory pool ?
|
||||
ringQ.InvNTTLvl(ct0.Level(), c2NTT, c2InvNTT)
|
||||
|
||||
alpha := eval.params.Alpha()
|
||||
beta := uint64(math.Ceil(float64(ct0.Level()+1) / float64(alpha)))
|
||||
|
||||
// TODO : maybe have a pre-allocated memory pool ?
|
||||
// IMPROVEMENT: maybe have a pre-allocated memory pool ?
|
||||
c2QiQDecomp := make([]*ring.Poly, beta)
|
||||
c2QiPDecomp := make([]*ring.Poly, beta)
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ type evaluator struct {
|
||||
poolQ [4]*ring.Poly // Memory pool in order : Decomp(c2), for NTT^-1(c2), res(c0', c1')
|
||||
poolP [3]*ring.Poly // Memory pool in order : Decomp(c2), res(c0', c1')
|
||||
|
||||
// TODO use the other pools
|
||||
ctxpool *Ciphertext // Memory pool for ciphertext that need to be scaled up (to be removed eventually)
|
||||
|
||||
baseconverter *ring.FastBasisExtender
|
||||
@@ -136,7 +135,7 @@ func (eval *evaluator) getElemAndCheckBinary(op0, op1, opOut Operand, opOutMinDe
|
||||
panic("receiver operand degree is too small")
|
||||
}
|
||||
el0, el1, elOut = op0.El(), op1.El(), opOut.El()
|
||||
return // TODO: more checks on elements
|
||||
return
|
||||
}
|
||||
|
||||
func (eval *evaluator) getElemAndCheckUnary(op0, opOut Operand, opOutMinDegree uint64) (el0, elOut *Element) {
|
||||
@@ -152,7 +151,7 @@ func (eval *evaluator) getElemAndCheckUnary(op0, opOut Operand, opOutMinDegree u
|
||||
panic("receiver operand degree is too small")
|
||||
}
|
||||
el0, elOut = op0.El(), opOut.El()
|
||||
return // TODO: more checks on elements
|
||||
return
|
||||
}
|
||||
|
||||
func (eval *evaluator) newCiphertextBinary(op0, op1 Operand) (ctOut *Ciphertext) {
|
||||
@@ -240,7 +239,7 @@ func (eval *evaluator) SubNoModNew(op0, op1 Operand) (ctOut *Ciphertext) {
|
||||
|
||||
func (eval *evaluator) evaluateInPlace(c0, c1, ctOut *Element, evaluate func(uint64, *ring.Poly, *ring.Poly, *ring.Poly)) {
|
||||
|
||||
var tmp0, tmp1 *Element // TODO : use eval mem pool
|
||||
var tmp0, tmp1 *Element
|
||||
|
||||
level := utils.MinUint64(utils.MinUint64(c0.Level(), c1.Level()), ctOut.Level())
|
||||
|
||||
|
||||
@@ -192,8 +192,8 @@ func (keygen *keyGenerator) GenPublicKey(sk *SecretKey) (pk *PublicKey) {
|
||||
ringQP.NTT(pk.pk[0], pk.pk[0])
|
||||
pk.pk[1] = keygen.uniformSampler.ReadNew()
|
||||
|
||||
ringQP.MulCoeffsMontgomeryAndAdd(sk.sk, pk.pk[1], pk.pk[0])
|
||||
ringQP.Neg(pk.pk[0], pk.pk[0])
|
||||
ringQP.MulCoeffsMontgomeryAndSub(sk.sk, pk.pk[1], pk.pk[0])
|
||||
|
||||
return pk
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ import (
|
||||
|
||||
// GetDataLen returns the length in bytes of the target Ciphertext.
|
||||
func (ciphertext *Ciphertext) GetDataLen(WithMetaData bool) (dataLen uint64) {
|
||||
// MetaData is :
|
||||
// 1 byte : Degree
|
||||
// 9 byte : Scale
|
||||
// 1 byte : isNTT
|
||||
if WithMetaData {
|
||||
dataLen += 11
|
||||
}
|
||||
@@ -55,7 +59,7 @@ func (ciphertext *Ciphertext) MarshalBinary() (data []byte, err error) {
|
||||
// The target Ciphertext must be of the appropriate format and size, it can be created with the
|
||||
// method NewCiphertext(uint64).
|
||||
func (ciphertext *Ciphertext) UnmarshalBinary(data []byte) (err error) {
|
||||
if len(data) < 11 {
|
||||
if len(data) < 11 { // cf. ciphertext.GetDataLen()
|
||||
return errors.New("too small bytearray")
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ import (
|
||||
// MaxLogN is the log2 of the largest supported polynomial modulus degree.
|
||||
const MaxLogN = 16
|
||||
|
||||
// MinLogN is the log2 of the smallest supported polynomial modulus degree
|
||||
const MinLogN = 4
|
||||
|
||||
// MaxModuliCount is the largest supported number of moduli in the RNS representation.
|
||||
const MaxModuliCount = 34
|
||||
|
||||
@@ -239,7 +242,7 @@ type Parameters struct {
|
||||
func NewParametersFromModuli(logN uint64, m *Moduli) (p *Parameters, err error) {
|
||||
p = new(Parameters)
|
||||
|
||||
if (logN < 3) || (logN > MaxLogN) {
|
||||
if (logN < MinLogN) || (logN > MaxLogN) {
|
||||
return nil, fmt.Errorf("invalid polynomial ring log degree: %d", logN)
|
||||
}
|
||||
|
||||
@@ -546,7 +549,14 @@ func (p *Parameters) MarshalBinary() ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
b := utils.NewBuffer(make([]byte, 0, 21+(p.QPiCount())<<3))
|
||||
// Data 21 byte + QPiCount * 8 byte:
|
||||
// 1 byte : logN
|
||||
// 1 byte : logSlots
|
||||
// 8 byte : scale
|
||||
// 8 byte : sigma
|
||||
// 1 byte : #qi
|
||||
// 1 byte : #pi
|
||||
b := utils.NewBuffer(make([]byte, 0, 20+(p.QPiCount())<<3))
|
||||
|
||||
b.WriteUint8(uint8(p.logN))
|
||||
b.WriteUint8(uint8(p.logSlots))
|
||||
@@ -563,7 +573,7 @@ func (p *Parameters) MarshalBinary() ([]byte, error) {
|
||||
// UnmarshalBinary decodes a []byte into a parameter set struct
|
||||
func (p *Parameters) UnmarshalBinary(data []byte) (err error) {
|
||||
|
||||
if len(data) < 3 {
|
||||
if len(data) < 20 {
|
||||
return errors.New("invalid parameters encoding")
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ type PCKSShare [2]*ring.Poly
|
||||
|
||||
// MarshalBinary encodes a PCKS share on a slice of bytes.
|
||||
func (share *PCKSShare) MarshalBinary() ([]byte, error) {
|
||||
//TODO discuss choice here.
|
||||
//Discuss choice here.
|
||||
//Maybe not worth it to have the metadata separated. we "lose" two bytes but complexity of the code would be higher in Unmarshalling.
|
||||
lenR1 := share[0].GetDataLen(true)
|
||||
lenR2 := share[1].GetDataLen(true)
|
||||
|
||||
@@ -127,7 +127,7 @@ func (rfp *RefreshProtocol) GenShares(sk *ring.Poly, ciphertext *bfv.Ciphertext,
|
||||
ringQ.MulScalarBigint(share.RefreshShareDecrypt, rfp.context.ringP.ModulusBigint, share.RefreshShareDecrypt)
|
||||
|
||||
// h0 = s*ct[1]*P + e
|
||||
rfp.gaussianSampler.ReadLvl(uint64(len(ringQP.Modulus)-1), rfp.tmp1) // TODO : add smudging noise
|
||||
rfp.gaussianSampler.ReadLvl(uint64(len(ringQP.Modulus)-1), rfp.tmp1)
|
||||
ringQ.Add(share.RefreshShareDecrypt, rfp.tmp1, share.RefreshShareDecrypt)
|
||||
|
||||
for x, i := 0, uint64(len(ringQ.Modulus)); i < uint64(len(rfp.context.ringQP.Modulus)); x, i = x+1, i+1 {
|
||||
|
||||
@@ -177,7 +177,7 @@ func (ekg *RKGProtocol) GenShareRoundOne(u, sk *ring.Poly, crp []*ring.Poly, sha
|
||||
ringQP.MulCoeffsMontgomeryAndAdd(sk, crp[i], shareOut[i][1])
|
||||
}
|
||||
|
||||
ekg.polypool.Zero() // TODO: check if we can remove this one
|
||||
ekg.polypool.Zero()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ type CKSProtocol struct {
|
||||
|
||||
sigmaSmudging float64
|
||||
|
||||
tmp *ring.Poly
|
||||
tmpQ *ring.Poly
|
||||
tmpP *ring.Poly
|
||||
tmpDelta *ring.Poly
|
||||
hP *ring.Poly
|
||||
|
||||
@@ -34,7 +35,8 @@ func NewCKSProtocol(params *ckks.Parameters, sigmaSmudging float64) (cks *CKSPro
|
||||
|
||||
cks.dckksContext = dckksContext
|
||||
|
||||
cks.tmp = dckksContext.ringQP.NewPoly()
|
||||
cks.tmpQ = dckksContext.ringQ.NewPoly()
|
||||
cks.tmpP = dckksContext.ringP.NewPoly()
|
||||
cks.tmpDelta = dckksContext.ringQ.NewPoly()
|
||||
cks.hP = dckksContext.ringP.NewPoly()
|
||||
|
||||
@@ -75,24 +77,18 @@ func (cks *CKSProtocol) genShareDelta(skDelta *ring.Poly, ct *ckks.Ciphertext, s
|
||||
|
||||
ringQ.MulScalarBigintLvl(ct.Level(), shareOut, ringP.ModulusBigint, shareOut)
|
||||
|
||||
// TODO : improve by only computing the NTT for the required primes
|
||||
cks.gaussianSampler.Read(cks.tmp)
|
||||
cks.dckksContext.ringQP.NTT(cks.tmp, cks.tmp)
|
||||
cks.gaussianSampler.ReadLvl(ct.Level(), cks.tmpQ)
|
||||
extendBasisSmallNormAndCenter(ringQ, ringP, cks.tmpQ, cks.tmpP)
|
||||
|
||||
ringQ.AddLvl(ct.Level(), shareOut, cks.tmp, shareOut)
|
||||
ringQ.NTTLvl(ct.Level(), cks.tmpQ, cks.tmpQ)
|
||||
ringP.NTT(cks.tmpP, cks.tmpP)
|
||||
|
||||
for x, i := 0, uint64(len(ringQ.Modulus)); i < uint64(len(cks.dckksContext.ringQP.Modulus)); x, i = x+1, i+1 {
|
||||
tmp0 := cks.tmp.Coeffs[i]
|
||||
tmp1 := cks.hP.Coeffs[x]
|
||||
for j := uint64(0); j < ringQ.N; j++ {
|
||||
tmp1[j] += tmp0[j]
|
||||
}
|
||||
}
|
||||
ringQ.AddLvl(ct.Level(), shareOut, cks.tmpQ, shareOut)
|
||||
|
||||
cks.baseconverter.ModDownSplitNTTPQ(ct.Level(), shareOut, cks.hP, shareOut)
|
||||
cks.baseconverter.ModDownSplitNTTPQ(ct.Level(), shareOut, cks.tmpP, shareOut)
|
||||
|
||||
cks.hP.Zero()
|
||||
cks.tmp.Zero()
|
||||
cks.tmpQ.Zero()
|
||||
cks.tmpP.Zero()
|
||||
}
|
||||
|
||||
// AggregateShares is the second part of the unique round of the CKSProtocol protocol. Upon receiving the j-1 elements each party computes :
|
||||
|
||||
@@ -64,6 +64,8 @@ func (pcks *PCKSProtocol) AllocateShares(level uint64) (s PCKSShare) {
|
||||
// and broadcasts the result to the other j-1 parties.
|
||||
func (pcks *PCKSProtocol) GenShare(sk *ring.Poly, pk *ckks.PublicKey, ct *ckks.Ciphertext, shareOut PCKSShare) {
|
||||
|
||||
// Planned improvement : adapt share size to ct.Level() to improve efficiency.
|
||||
|
||||
ringQ := pcks.dckksContext.ringQ
|
||||
ringQP := pcks.dckksContext.ringQP
|
||||
|
||||
|
||||
@@ -176,8 +176,6 @@ func (ekg *RKGProtocol) GenShareRoundOne(u, sk *ring.Poly, crp []*ring.Poly, sha
|
||||
ringQP.MulCoeffsMontgomeryAndAdd(sk, crp[i], shareOut[i][1])
|
||||
}
|
||||
|
||||
ekg.polypool.Zero() // TODO: check if we can remove this one
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
26
dckks/utils.go
Normal file
26
dckks/utils.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package dckks
|
||||
|
||||
import (
|
||||
"github.com/ldsec/lattigo/v2/ring"
|
||||
)
|
||||
|
||||
func extendBasisSmallNormAndCenter(ringQ, ringP *ring.Ring, polQ, polP *ring.Poly) {
|
||||
var coeff, Q, QHalf, sign uint64
|
||||
Q = ringQ.Modulus[0]
|
||||
QHalf = Q >> 1
|
||||
|
||||
for j := uint64(0); j < ringQ.N; j++ {
|
||||
|
||||
coeff = polQ.Coeffs[0][j]
|
||||
|
||||
sign = 1
|
||||
if coeff > QHalf {
|
||||
coeff = Q - coeff
|
||||
sign = 0
|
||||
}
|
||||
|
||||
for i, pi := range ringP.Modulus {
|
||||
polP.Coeffs[i][j] = (coeff * sign) | (pi-coeff)*(sign^1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,69 +3,11 @@ package ring
|
||||
import (
|
||||
"fmt"
|
||||
"math/bits"
|
||||
|
||||
"github.com/ldsec/lattigo/v2/utils"
|
||||
)
|
||||
|
||||
// IsPrime applies a Miller-Rabin test on the given uint64 variable, returning true if the input is probably prime, and false otherwise.
|
||||
func IsPrime(num uint64) bool {
|
||||
|
||||
if num < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, smallPrime := range smallPrimes {
|
||||
if num == smallPrime {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, smallPrime := range smallPrimes {
|
||||
if num%smallPrime == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
s := num - 1
|
||||
k := 0
|
||||
for (s & 1) == 0 {
|
||||
s >>= 1
|
||||
k++
|
||||
}
|
||||
|
||||
bredParams := BRedParams(num)
|
||||
var mask, b uint64
|
||||
mask = (1 << uint64(bits.Len64(num))) - 1
|
||||
|
||||
prng, err := utils.NewPRNG()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for trial := 0; trial < 50; trial++ {
|
||||
|
||||
b = RandUniform(prng, num-1, mask)
|
||||
|
||||
for b < 2 {
|
||||
b = RandUniform(prng, num-1, mask)
|
||||
}
|
||||
|
||||
x := ModExp(b, s, num)
|
||||
|
||||
if x != 1 {
|
||||
i := 0
|
||||
for x != num-1 {
|
||||
|
||||
if i == k-1 {
|
||||
return false
|
||||
}
|
||||
|
||||
i++
|
||||
x = BRed(x, x, num, bredParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
// IsPrime applies the Baillie-PSW, which is 100% accurate for numbers bellow 2^64
|
||||
func IsPrime(x uint64) bool {
|
||||
return NewUint(x).ProbablyPrime(0)
|
||||
}
|
||||
|
||||
// GenerateNTTPrimes generates n NthRoot NTT friendly primes given logQ = size of the primes.
|
||||
|
||||
@@ -855,7 +855,7 @@ func (r *Ring) MulPolyNaiveMontgomery(p1, p2, p3 *Poly) {
|
||||
}
|
||||
|
||||
// Exp raises p1 to p1^e and writes the result on p2.
|
||||
// TODO : implement Montgomery ladder
|
||||
// IMPROVEMENT : implement Montgomery ladder
|
||||
func (r *Ring) Exp(p1 *Poly, e uint64, p2 *Poly) {
|
||||
|
||||
r.NTT(p1, p1)
|
||||
|
||||
@@ -21,7 +21,11 @@ func Test_PRNG(t *testing.T) {
|
||||
sum1 := make([]byte, 512)
|
||||
|
||||
Ha.SetClock(sum0, 256)
|
||||
Hb.SetClock(sum1, 256)
|
||||
Hb.SetClock(sum1, 128)
|
||||
|
||||
for i := 0; i < 128; i++ {
|
||||
Hb.Clock(sum1)
|
||||
}
|
||||
|
||||
Ha.Clock(sum0)
|
||||
Hb.Clock(sum1)
|
||||
|
||||
Reference in New Issue
Block a user