BFV : params revamp

This commit is contained in:
Jean-Philippe Bossuat
2019-11-29 13:49:07 +01:00
parent a35b4d0bc7
commit b926532a8d
12 changed files with 221 additions and 120 deletions

View File

@@ -21,7 +21,6 @@ type Context struct {
t uint64
logQ uint64
logP uint64
// floor(Q/T) mod each Qi in Montgomery form
deltaMont []uint64
@@ -88,11 +87,11 @@ func NewContext(params *Parameters) (newContext *Context) {
// - sigma : the variance of the gaussian sampling.
func (context *Context) SetParameters(params *Parameters) {
N := params.N
LogN := params.LogN
N := uint64(1 << LogN)
t := params.T
ModuliQ1 := params.Q1
ModuliQ2 := params.Q2
ModuliP := params.P
ModuliQ1, ModuliP, ModuliQ2 := genModuli(params)
sigma := params.Sigma
context.n = N
@@ -146,8 +145,7 @@ func (context *Context) SetParameters(params *Parameters) {
context.qHalf = new(big.Int).Rsh(context.contextQ1.ModulusBigint, 1)
context.pHalf = new(big.Int).Rsh(context.contextQ2.ModulusBigint, 1)
context.logQ = uint64(context.contextP.ModulusBigint.BitLen())
context.logP = uint64(context.contextQ2.ModulusBigint.BitLen())
context.logQ = uint64(context.contextQ1P.ModulusBigint.BitLen())
delta := new(big.Int).Quo(context.contextQ1.ModulusBigint, ring.NewUint(t))
tmpBig := new(big.Int)
@@ -177,11 +175,6 @@ func (context *Context) LogQ() uint64 {
return context.logQ
}
// LogP returns logQ which is the total bitzise of the secondary ciphertext modulus.
func (context *Context) LogP() uint64 {
return context.logP
}
// T returns the plaintext modulus of the target context.
func (context *Context) T() uint64 {
return context.t

View File

@@ -11,24 +11,24 @@ func Benchmark_BFV(b *testing.B) {
for _, params := range paramSets {
bfvContext := NewContext(&params)
bfvContext := NewContext(params)
var sk *SecretKey
var pk *PublicKey
var err error
kgen := NewKeyGenerator(&params)
kgen := NewKeyGenerator(params)
encoder := NewEncoder(&params)
encoder := NewEncoder(params)
coeffs := bfvContext.contextT.NewUniformPoly()
plaintext := NewPlaintextFromParams(&params)
plaintext := NewPlaintextFromParams(params)
encoder.EncodeUint(coeffs.Coeffs[0], plaintext)
// Public Key Generation
b.Run(testString("KeyGen", &params), func(b *testing.B) {
b.Run(testString("KeyGen", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
sk, pk = kgen.NewKeyPair()
if err != nil {
@@ -38,61 +38,61 @@ func Benchmark_BFV(b *testing.B) {
})
// Encryption
encryptorPk := NewEncryptorFromPk(&params, pk)
encryptorSk := NewEncryptorFromSk(&params, sk)
encryptorPk := NewEncryptorFromPk(params, pk)
encryptorSk := NewEncryptorFromSk(params, sk)
ctd1 := NewCiphertextFromParams(&params, 1)
b.Run(testString("EncryptFromPk", &params), func(b *testing.B) {
ctd1 := NewCiphertextFromParams(params, 1)
b.Run(testString("EncryptFromPk", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
encryptorPk.Encrypt(plaintext, ctd1)
}
})
b.Run(testString("EncryptFromSk", &params), func(b *testing.B) {
b.Run(testString("EncryptFromSk", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
encryptorSk.Encrypt(plaintext, ctd1)
}
})
// Decryption
decryptor := NewDecryptor(&params, sk)
ptp := NewPlaintextFromParams(&params)
b.Run(testString("Decrypt", &params), func(b *testing.B) {
decryptor := NewDecryptor(params, sk)
ptp := NewPlaintextFromParams(params)
b.Run(testString("Decrypt", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
decryptor.Decrypt(ctd1, ptp)
}
_ = ptp
})
evaluator := NewEvaluator(&params)
evaluator := NewEvaluator(params)
ct1 := NewRandomCiphertextFromParams(&params, 1)
ct2 := NewRandomCiphertextFromParams(&params, 1)
ct1 := NewRandomCiphertextFromParams(params, 1)
ct2 := NewRandomCiphertextFromParams(params, 1)
// Addition
b.Run(testString("Add", &params), func(b *testing.B) {
b.Run(testString("Add", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.Add(ct1, ct2, ctd1)
}
})
// Subtraction
b.Run(testString("Sub", &params), func(b *testing.B) {
b.Run(testString("Sub", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.Sub(ct1, ct2, ctd1)
}
})
// Multiplication
receiver := NewCiphertextFromParams(&params, 2)
b.Run(testString("Multiply", &params), func(b *testing.B) {
receiver := NewCiphertextFromParams(params, 2)
b.Run(testString("Multiply", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.Mul(ct1, ct2, receiver)
}
})
// Square is Mul(ct, ct) for now
b.Run(testString("Square", &params), func(b *testing.B) {
b.Run(testString("Square", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.Mul(ct1, ct1, receiver)
}
@@ -102,7 +102,7 @@ func Benchmark_BFV(b *testing.B) {
rlk := kgen.NewRelinKey(sk, 2)
// Relinearization
b.Run(testString("Relin", &params), func(b *testing.B) {
b.Run(testString("Relin", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.Relinearize(receiver, rlk, ctd1)
}
@@ -112,14 +112,14 @@ func Benchmark_BFV(b *testing.B) {
rtk := kgen.NewRotationKeysPow2(sk)
// Rotation Rows
b.Run(testString("RotateRows", &params), func(b *testing.B) {
b.Run(testString("RotateRows", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.RotateRows(ct1, rtk, ctd1)
}
})
// Rotation Cols
b.Run(testString("RotateCols", &params), func(b *testing.B) {
b.Run(testString("RotateCols", params), func(b *testing.B) {
for i := 0; i < b.N; i++ {
evaluator.RotateColumns(ct1, 1, rtk, ctd1)
}
@@ -129,5 +129,5 @@ func Benchmark_BFV(b *testing.B) {
}
func testString(opname string, params *Parameters) string {
return fmt.Sprintf("%s/params=%d", opname, params.N)
return fmt.Sprintf("%s/params=%d", opname, params.LogN)
}

View File

@@ -18,7 +18,7 @@ func check(t *testing.T, err error) {
}
func testString2(opname string, params *bfvParams) string {
return fmt.Sprintf("%sparams=%d", opname, params.bfvContext.N())
return fmt.Sprintf("%sparams=%d/logQ=%d", opname, params.bfvContext.n, params.bfvContext.logQ)
}
type bfvParams struct {
@@ -32,7 +32,6 @@ type bfvParams struct {
encryptorSk *Encryptor
decryptor *Decryptor
evaluator *Evaluator
ringCtx *ring.Context
}
type bfvTestParameters struct {
@@ -46,9 +45,10 @@ func init() {
rand.Seed(time.Now().UnixNano())
testParams.bfvParameters = []*Parameters{
&DefaultParams[0],
&DefaultParams[1],
&DefaultParams[2],
DefaultParams[12],
DefaultParams[13],
DefaultParams[14],
DefaultParams[15],
}
}

View File

@@ -20,7 +20,7 @@ func NewCiphertextFromParams(params *Parameters, degree uint64) (ciphertext *Cip
ciphertext.value = make([]*ring.Poly, degree+1)
for i := uint64(0); i < degree+1; i++ {
ciphertext.value[i] = ring.NewPoly(params.N, uint64(len(params.Q1)))
ciphertext.value[i] = ring.NewPoly(1<<params.LogN, uint64(len(params.Q1)))
}
ciphertext.isNTT = true
@@ -34,7 +34,7 @@ func NewRandomCiphertextFromParams(params *Parameters, degree uint64) (ciphertex
ciphertext.value = make([]*ring.Poly, degree+1)
for i := uint64(0); i < degree+1; i++ {
ciphertext.value[i] = ring.NewPolyUniform(params.N, uint64(len(params.Q1)))
ciphertext.value[i] = ring.NewPolyUniform(1<<params.LogN, uint64(len(params.Q1)))
}
ciphertext.isNTT = true

View File

@@ -15,7 +15,7 @@ type Decryptor struct {
// NewDecryptor creates a new Decryptor from the target context with the secret-key given as input.
func NewDecryptor(params *Parameters, sk *SecretKey) (decryptor *Decryptor) {
if sk.sk.GetDegree() != int(params.N) {
if sk.sk.GetDegree() != int(1<<params.LogN) {
panic("error : secret_key degree must match context degree")
}

View File

@@ -29,11 +29,11 @@ func NewEncryptorFromSk(params *Parameters, sk *SecretKey) *Encryptor {
func newEncryptor(params *Parameters, pk *PublicKey, sk *SecretKey) (encryptor *Encryptor) {
if pk != nil && (uint64(pk.pk[0].GetDegree()) != params.N || uint64(pk.pk[1].GetDegree()) != params.N) {
if pk != nil && (uint64(pk.pk[0].GetDegree()) != uint64(1<<params.LogN) || uint64(pk.pk[1].GetDegree()) != uint64(1<<params.LogN)) {
panic("error : pk ring degree doesn't match bfvcontext ring degree")
}
if sk != nil && uint64(sk.sk.GetDegree()) != params.N {
if sk != nil && uint64(sk.sk.GetDegree()) != uint64(1<<params.LogN) {
panic("error : sk ring degree doesn't match bfvcontext ring degree")
}

View File

@@ -79,7 +79,7 @@ func (keygen *KeyGenerator) NewSecretkeyWithDistrib(p float64) (sk *SecretKey) {
// NewSecretKey generates a new SecretKey with zero values.
func NewSecretKey(params *Parameters) *SecretKey {
sk := new(SecretKey)
sk.sk = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
sk.sk = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
return sk
}
@@ -115,8 +115,8 @@ func (keygen *KeyGenerator) NewPublicKey(sk *SecretKey) (pk *PublicKey) {
func NewPublicKey(params *Parameters) (pk *PublicKey) {
pk = new(PublicKey)
pk.pk[0] = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
pk.pk[1] = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
pk.pk[0] = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
pk.pk[1] = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
return
}
@@ -178,8 +178,8 @@ func NewRelinKey(params *Parameters, maxDegree uint64) (evakey *EvaluationKey) {
for i := uint64(0); i < beta; i++ {
evakey.evakey[w].evakey[i][0] = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
evakey.evakey[w].evakey[i][1] = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
evakey.evakey[w].evakey[i][0] = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
evakey.evakey[w].evakey[i][1] = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
}
}
@@ -230,8 +230,8 @@ func NewSwitchingKey(params *Parameters) (evakey *SwitchingKey) {
evakey.evakey = make([][2]*ring.Poly, beta)
for i := uint64(0); i < beta; i++ {
evakey.evakey[i][0] = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
evakey.evakey[i][1] = ring.NewPoly(params.N, uint64(len(params.Q1)+len(params.P)))
evakey.evakey[i][0] = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
evakey.evakey[i][1] = ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)+len(params.P)))
}
return

View File

@@ -48,7 +48,7 @@ func (el *bfvElement) Resize(params *Parameters, degree uint64) {
el.value = append(el.value, []*ring.Poly{new(ring.Poly)}...)
el.value[el.Degree()].Coeffs = make([][]uint64, len(params.Q1))
for i := 0; i < len(params.Q1); i++ {
el.value[el.Degree()].Coeffs[i] = make([]uint64, params.N)
el.value[el.Degree()].Coeffs[i] = make([]uint64, uint64(1<<params.LogN))
}
}
}

View File

@@ -5,45 +5,16 @@ import (
"fmt"
"github.com/ldsec/lattigo/utils"
"math"
"math/bits"
)
// MaxN is the largest supported polynomial modulus degree
const MaxN = 1 << 16
const MaxLogN = 16
// MaxModuliCount is the largest supported number of 60 moduli in the RNS representation
const MaxModuliCount = 34
// Modulies for 128 security according to http://homomorphicencryption.org/white_papers/security_homomorphic_encryption_white_paper.pdf
var logN13Q218 = []uint64{0x7ffffffffb4001, 0x3fffffffef8001, 0x3fffffffeb8001}
var logN14Q438 = []uint64{0x7fffffffe90001, 0x7fffffffd58001, 0x7fffffffbf0001, 0x7fffffffbd0001,
0x7fffffffba0001, 0x7fffffffb58001}
var logN15Q881 = []uint64{0x7ffffffffe70001, 0x7ffffffffe10001, 0x7ffffffffcc0001, 0x7ffffffffba0001,
0x7ffffffffb00001, 0x7ffffffff630001, 0x7ffffffff510001, 0x7ffffffff3f0001,
0x7ffffffff350001, 0x7ffffffff320001, 0x7ffffffff2c0001, 0x7fffffffe90001}
var logN16Q1770 = []uint64{0x7ffffffffcc0001, 0x7ffffffffba0001, 0x7ffffffffb00001, 0x7ffffffff320001,
0x7ffffffff2c0001, 0x7ffffffff240001, 0x7fffffffefa0001, 0x7fffffffede0001,
0x7fffffffe900001, 0x7fffffffe3c0001, 0x7fffffffe240001, 0x7fffffffddc0001,
0x7fffffffdbe0001, 0x7fffffffd740001, 0x7fffffffd640001, 0x7fffffffd1a0001,
0x7fffffffd0a0001, 0x7fffffffd080001, 0x7fffffffcda0001, 0x7fffffffccc0001,
0x7fffffffcbc0001, 0x7fffffffcae0001, 0x7fffffffc980001, 0x7fffffffc480001,
0x7fffffffc020001, 0x7fffffffbcc0001}
var Q60 = []uint64{0xffffffffe400001, 0xffffffffd000001, 0xffffffffa200001, 0xffffffff9600001,
0xfffffffeb200001, 0xfffffffea400001, 0xfffffffe8000001, 0xfffffffe3e00001,
0xfffffffe2200001, 0xfffffffe0800001, 0xfffffffdd400001, 0xfffffffd9000001,
0xfffffffcea00001, 0xfffffffcdc00001, 0xfffffffc7200001, 0xfffffffc5a00001,
0xfffffffc5400001, 0xfffffffc4200001, 0xfffffffc2e00001, 0xfffffffbfa00001,
0xfffffffbf200001, 0xfffffffbce00001, 0xfffffffba400001, 0xfffffffba000001,
0xfffffffb8c00001, 0xfffffffb1400001, 0xfffffffafc00001, 0xfffffffaf800001,
0xfffffffa6600001, 0xfffffffa5000001, 0xfffffff9ee00001, 0xfffffff9d600001,
0xfffffff9ba00001, 0xfffffff99a00001, 0xfffffff94800001, 0xfffffff91000001,
0xfffffff90600001, 0xfffffff8e600001, 0xfffffff8a400001, 0xfffffff88200001}
// Power of 2 plaintext modulus
var tPow2 = []uint64{2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144,
524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912,
@@ -63,11 +34,11 @@ var tBatching = map[uint64][]uint64{
// Parameters represents a given parameter set for the BFV cryptosystem.
type Parameters struct {
N uint64
LogN uint8
T uint64
Q1 []uint64
P []uint64
Q2 []uint64
Q1 []uint8
P []uint8
Q2 []uint8
Sigma float64
}
@@ -75,20 +46,20 @@ type Parameters struct {
func (p *Parameters) Copy() (paramsCopy *Parameters) {
paramsCopy = new(Parameters)
paramsCopy.N = p.N
paramsCopy.LogN = p.LogN
paramsCopy.T = p.T
paramsCopy.Q1 = make([]uint64, len(p.Q1))
paramsCopy.Q1 = make([]uint8, len(p.Q1))
for i := range p.Q1 {
paramsCopy.Q1[i] = p.Q1[i]
}
paramsCopy.P = make([]uint64, len(p.P))
paramsCopy.P = make([]uint8, len(p.P))
for i := range p.P {
paramsCopy.P[i] = p.P[i]
}
paramsCopy.Q2 = make([]uint64, len(p.Q2))
paramsCopy.Q2 = make([]uint8, len(p.Q2))
for i := range p.Q2 {
paramsCopy.Q2[i] = p.Q2[i]
}
@@ -98,13 +69,40 @@ func (p *Parameters) Copy() (paramsCopy *Parameters) {
return
}
// DefaultParams is an array default parameters with increasing homomorphic capacity.
// These parameters correspond to 128 bit security level for secret keys in the ternary distribution
// (see //https://projects.csail.mit.edu/HEWorkshop/HomomorphicEncryptionStandard2018.pdf).
var DefaultParams = []Parameters{
{8192, 65537, logN13Q218, []uint64{0x7fffffffeac001}, Q60[len(Q60)-len(logN13Q218):], 3.19},
{16384, 65537, logN14Q438, []uint64{0x3fffffffeb8001, 0x3fffffffef8001}, Q60[len(Q60)-len(logN14Q438):], 3.19},
{32768, 65537, logN15Q881, []uint64{0x7ffffffff240001, 0x7ffffffff230001, 0x7fffffffefa0001}, Q60[len(Q60)-len(logN15Q881):], 3.19},
// DefaultParams is a set of default BFV parameters ensuring 128 bit security.
var DefaultParams = map[uint64]*Parameters{
//logQ = 109
12: {LogN: 12,
T: 65537,
Q1: []uint8{39, 39},
P: []uint8{30},
Q2: []uint8{60, 60},
Sigma: 3.2},
//logQ = 218
13: {LogN: 13,
T: 65537,
Q1: []uint8{54, 54, 54},
P: []uint8{55},
Q2: []uint8{60, 60, 60},
Sigma: 3.2},
//logQ = 438
14: {LogN: 14,
T: 65537,
Q1: []uint8{56, 55, 55, 54, 54, 54},
P: []uint8{55, 55},
Q2: []uint8{60, 60, 60, 60, 60, 60},
Sigma: 3.2},
//logQ = 880
15: {LogN: 15,
T: 65537,
Q1: []uint8{59, 59, 59, 58, 58, 58, 58, 58, 58, 58, 58, 58},
P: []uint8{60, 60, 60},
Q2: []uint8{60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60},
Sigma: 3.2},
}
// Equals compares two sets of parameters for equality
@@ -112,24 +110,24 @@ func (p *Parameters) Equals(other *Parameters) bool {
if p == other {
return true
}
return p.N == other.N && utils.EqualSliceUint64(p.Q1, other.Q1) && utils.EqualSliceUint64(p.Q2, other.Q2) && p.Sigma == other.Sigma
return p.LogN == other.LogN && p.T == other.T && utils.EqualSliceUint8(p.Q1, other.Q1) && utils.EqualSliceUint8(p.P, other.P) && utils.EqualSliceUint8(p.Q2, other.Q2) && p.Sigma == other.Sigma
}
// MarshalBinary returns a []byte representation of the parameter set
func (p *Parameters) MarshalBinary() ([]byte, error) {
if p.N == 0 { // if N is 0, then p is the zero value
if p.LogN == 0 { // if N is 0, then p is the zero value
return []byte{}, nil
}
b := utils.NewBuffer(make([]byte, 0, 4+((3+len(p.Q1)+len(p.Q2)+len(p.P))<<3)))
b.WriteUint8(uint8(bits.Len64(p.N) - 1))
b.WriteUint8(uint8(p.LogN))
b.WriteUint8(uint8(len(p.Q1)))
b.WriteUint8(uint8(len(p.P)))
b.WriteUint8(uint8(len(p.Q2)))
b.WriteUint64(p.T)
b.WriteUint64(uint64(p.Sigma * (1 << 32)))
b.WriteUint64Slice(p.Q1)
b.WriteUint64Slice(p.P)
b.WriteUint64Slice(p.Q2)
b.WriteUint8Slice(p.Q1)
b.WriteUint8Slice(p.P)
b.WriteUint8Slice(p.Q2)
return b.Bytes(), nil
}
@@ -140,34 +138,34 @@ func (p *Parameters) UnmarshalBinary(data []byte) error {
}
b := utils.NewBuffer(data)
p.N = 1 << uint64(b.ReadUint8())
if p.N > MaxN {
p.LogN = b.ReadUint8()
if p.LogN > MaxLogN {
return errors.New("polynomial degree is too large")
}
lenQ1 := uint64(b.ReadUint8())
lenQ1 := b.ReadUint8()
if lenQ1 > MaxModuliCount {
return fmt.Errorf("len(Q1) is larger than %d", MaxModuliCount)
}
lenP := uint64(b.ReadUint8())
lenP := b.ReadUint8()
if lenP > MaxModuliCount {
return fmt.Errorf("len(lenP) is larger than %d", MaxModuliCount)
}
lenQ2 := uint64(b.ReadUint8())
lenQ2 := b.ReadUint8()
if lenQ2 > MaxModuliCount {
return fmt.Errorf("len(Q2) is larger than %d", MaxModuliCount)
}
p.T = b.ReadUint64()
p.Sigma = math.Round((float64(b.ReadUint64())/float64(1<<32))*100) / 100
p.Q1 = make([]uint64, lenQ1, lenQ1)
p.P = make([]uint64, lenP, lenP)
p.Q2 = make([]uint64, lenQ2, lenQ2)
p.Q1 = make([]uint8, lenQ1, lenQ1)
p.P = make([]uint8, lenP, lenP)
p.Q2 = make([]uint8, lenQ2, lenQ2)
b.ReadUint64Slice(p.Q1)
b.ReadUint64Slice(p.P)
b.ReadUint64Slice(p.Q2)
b.ReadUint8Slice(p.Q1)
b.ReadUint8Slice(p.P)
b.ReadUint8Slice(p.Q2)
return nil
}

View File

@@ -10,7 +10,7 @@ func TestParams_BinaryMarshaller(t *testing.T) {
bytes, err := (&Parameters{}).MarshalBinary()
assert.Nil(t, err)
assert.Equal(t, []byte{}, bytes)
var p Parameters
p := new(Parameters)
err = p.UnmarshalBinary(bytes)
assert.NotNil(t, err)
})
@@ -18,7 +18,7 @@ func TestParams_BinaryMarshaller(t *testing.T) {
for _, params := range DefaultParams {
bytes, err := params.MarshalBinary()
assert.Nil(t, err)
var p Parameters
p := new(Parameters)
err = p.UnmarshalBinary(bytes)
assert.Nil(t, err)
assert.Equal(t, params, p)

View File

@@ -14,7 +14,7 @@ type Plaintext struct {
func NewPlaintextFromParams(params *Parameters) *Plaintext {
plaintext := &Plaintext{&bfvElement{}, nil}
plaintext.bfvElement.value = []*ring.Poly{ring.NewPoly(params.N, uint64(len(params.Q1)))}
plaintext.bfvElement.value = []*ring.Poly{ring.NewPoly(uint64(1<<params.LogN), uint64(len(params.Q1)))}
plaintext.value = plaintext.bfvElement.value[0]
plaintext.isNTT = false

110
bfv/utils.go Normal file
View File

@@ -0,0 +1,110 @@
package bfv
import (
"github.com/ldsec/lattigo/ring"
)
// genModuli generates the appropriate primes from the parameters using generateCKKSPrimes such that all primes are different.
func genModuli(params *Parameters) (Q1 []uint64, P []uint64, Q2 []uint64) {
// Extracts all the different primes bit size and maps their number
primesbitlen := make(map[uint64]uint64)
for i, qi := range params.Q1 {
primesbitlen[uint64(qi)]++
if uint64(params.Q1[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 i, qi := range params.Q2 {
primesbitlen[uint64(qi)]++
if uint64(params.Q2[i]) > 60 {
panic("provided moduli must be smaller than 61")
}
}
// For each bitsize, finds that many primes
primes := make(map[uint64][]uint64)
for key, value := range primesbitlen {
primes[key] = generateNTTPrimes(key, uint64(params.LogN), value)
}
// Assigns the primes to the ckks moduli chain
Q1 = make([]uint64, len(params.Q1))
for i, qi := range params.Q1 {
Q1[i] = primes[uint64(params.Q1[i])][0]
primes[uint64(qi)] = primes[uint64(qi)][1:]
}
// Assigns the primes to the special primes list for the the keyscontext
P = make([]uint64, len(params.P))
for i, pj := range params.P {
P[i] = primes[uint64(pj)][0]
primes[uint64(pj)] = primes[uint64(pj)][1:]
}
Q2 = make([]uint64, len(params.Q2))
for i, qi := range params.Q2 {
Q2[i] = primes[uint64(params.Q2[i])][0]
primes[uint64(qi)] = primes[uint64(qi)][1:]
}
return Q1, P, Q2
}
func generateNTTPrimes(logQ, logN, levels uint64) (primes []uint64) {
// generateCKKSPrimes generates primes given logQ = size of the primes, logN = size of N and level, the number
// of levels required. Will return all the appropriate primes, up to the number of level, with the
// best avaliable deviation from the base power of 2 for the given level.
if logQ > 60 {
panic("logQ must be between 1 and 60")
}
var x, y, Qpow2, _2N uint64
primes = []uint64{}
Qpow2 = 1 << logQ
_2N = 2 << logN
x = Qpow2 + 1
y = Qpow2 + 1
for true {
if ring.IsPrime(y) {
primes = append(primes, y)
if uint64(len(primes)) == levels {
return primes
}
}
y -= _2N
if ring.IsPrime(x) {
primes = append(primes, x)
if uint64(len(primes)) == levels {
return primes
}
}
x += _2N
}
return
}