mirror of
https://github.com/tuneinsight/lattigo.git
synced 2025-09-13 03:27:14 +00:00
BFV : params revamp
This commit is contained in:
17
bfv/bfv.go
17
bfv/bfv.go
@@ -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
|
||||
|
||||
@@ -11,24 +11,24 @@ func Benchmark_BFV(b *testing.B) {
|
||||
|
||||
for _, params := range paramSets {
|
||||
|
||||
bfvContext := NewContext(¶ms)
|
||||
bfvContext := NewContext(params)
|
||||
|
||||
var sk *SecretKey
|
||||
var pk *PublicKey
|
||||
var err error
|
||||
|
||||
kgen := NewKeyGenerator(¶ms)
|
||||
kgen := NewKeyGenerator(params)
|
||||
|
||||
encoder := NewEncoder(¶ms)
|
||||
encoder := NewEncoder(params)
|
||||
|
||||
coeffs := bfvContext.contextT.NewUniformPoly()
|
||||
|
||||
plaintext := NewPlaintextFromParams(¶ms)
|
||||
plaintext := NewPlaintextFromParams(params)
|
||||
|
||||
encoder.EncodeUint(coeffs.Coeffs[0], plaintext)
|
||||
|
||||
// Public Key Generation
|
||||
b.Run(testString("KeyGen", ¶ms), 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(¶ms, pk)
|
||||
encryptorSk := NewEncryptorFromSk(¶ms, sk)
|
||||
encryptorPk := NewEncryptorFromPk(params, pk)
|
||||
encryptorSk := NewEncryptorFromSk(params, sk)
|
||||
|
||||
ctd1 := NewCiphertextFromParams(¶ms, 1)
|
||||
b.Run(testString("EncryptFromPk", ¶ms), 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", ¶ms), 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(¶ms, sk)
|
||||
ptp := NewPlaintextFromParams(¶ms)
|
||||
b.Run(testString("Decrypt", ¶ms), 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(¶ms)
|
||||
evaluator := NewEvaluator(params)
|
||||
|
||||
ct1 := NewRandomCiphertextFromParams(¶ms, 1)
|
||||
ct2 := NewRandomCiphertextFromParams(¶ms, 1)
|
||||
ct1 := NewRandomCiphertextFromParams(params, 1)
|
||||
ct2 := NewRandomCiphertextFromParams(params, 1)
|
||||
|
||||
// Addition
|
||||
b.Run(testString("Add", ¶ms), 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", ¶ms), 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(¶ms, 2)
|
||||
b.Run(testString("Multiply", ¶ms), 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", ¶ms), 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", ¶ms), 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", ¶ms), 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", ¶ms), 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)
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
122
bfv/params.go
122
bfv/params.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
110
bfv/utils.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user