diff --git a/bfv/bfv.go b/bfv/bfv.go index d95f0003..d755570b 100644 --- a/bfv/bfv.go +++ b/bfv/bfv.go @@ -13,7 +13,7 @@ type BfvContext struct { // Plaintext Modulus t uint64 - // floor(Q/t) mod each Qi in montgomeryform + // floor(Q/T) mod each Qi in montgomeryform DeltaMont []uint64 Delta []uint64 @@ -39,7 +39,7 @@ type BfvContext struct { galElRotColLeft []uint64 galElRotColRight []uint64 - // Checksum of [n, [modulies]] + // Checksum of [N, [modulies]] checksum []byte } diff --git a/bfv/bfv_benchmark_test.go b/bfv/bfv_benchmark_test.go index c6a0d2fd..228cb1af 100755 --- a/bfv/bfv_benchmark_test.go +++ b/bfv/bfv_benchmark_test.go @@ -7,7 +7,7 @@ import ( func BenchmarkBFVScheme(b *testing.B) { - paramSets := ParamSets60 + paramSets := DefaultParams bitDecomps := []uint64{60} diff --git a/bfv/bfv_test.go b/bfv/bfv_test.go index 56faeba2..7a659e52 100644 --- a/bfv/bfv_test.go +++ b/bfv/bfv_test.go @@ -22,7 +22,7 @@ func Test_BFV(t *testing.T) { var err error - paramSets := ParamSets60[1:2] + paramSets := DefaultParams[1:2] bitDecomps := []uint64{16} diff --git a/bfv/params.go b/bfv/params.go index 3cce63d6..b7dc8236 100644 --- a/bfv/params.go +++ b/bfv/params.go @@ -1,19 +1,39 @@ package bfv -import "github.com/lca1/lattigo/ring" +import ( + "errors" + "fmt" + "github.com/lca1/lattigo/ring" + "github.com/lca1/lattigo/utils" + "math" + "math/bits" +) -//https://projects.csail.mit.edu/HEWorkshop/HomomorphicEncryptionStandard2018.pdf +// MaxN is the largest supported polynomial modulus degree +const MaxN = 1 << 15 + +// MaxModuliCount is the largest supported number of 60 moduli in the RNS representation +const MaxModuliCount = 256 // 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, 1073741824, 2147483648, 4294967296} +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, + 1073741824, 2147483648, 4294967296} // Plaintext modulus allowing batching for the corresponding N in ascending bitsize. -var TBatching4096 = []uint64{40961, 114689, 188417, 417793, 1032193, 2056193, 4169729, 8380417, 16760833, 33538049, 67084289, 134176769, 268369921, 536813569, 1073692673, 2147377153, 4294828033} -var TBatching8192 = []uint64{65537, 114689, 163841, 1032193, 1785857, 4079617, 8273921, 16760833, 33538049, 67043329, 133857281, 268369921, 536690689, 1073692673, 2147352577, 4294475777} -var TBatching16384 = []uint64{65537, 163841, 786433, 1769473, 3735553, 8257537, 16580609, 33292289, 67043329, 133857281, 268369921, 536641537, 1073643521, 2147352577, 4294475777} -var TBatching32768 = []uint64{65537, 786433, 1769473, 3735553, 8257537, 16580609, 33292289, 67043329, 132710401, 268369921, 536608769, 1073479681, 2147352577, 4293918721} +var TBatching = map[uint64][]uint64{ + 4096: {40961, 114689, 188417, 417793, 1032193, 2056193, 4169729, 8380417, 16760833, 33538049, 67084289, 134176769, + 268369921, 536813569, 1073692673, 2147377153, 4294828033}, + 8192: {65537, 114689, 163841, 1032193, 1785857, 4079617, 8273921, 16760833, 33538049, 67043329, 133857281, + 268369921, 536690689, 1073692673, 2147352577, 4294475777}, + 16384: {65537, 163841, 786433, 1769473, 3735553, 8257537, 16580609, 33292289, 67043329, 133857281, 268369921, + 536641537, 1073643521, 2147352577, 4294475777}, + 32768: {65537, 786433, 1769473, 3735553, 8257537, 16580609, 33292289, 67043329, 132710401, 268369921, 536608769, + 1073479681, 2147352577, 4293918721}, +} -type Params struct { +// Parameters represents a given parameter set for the BFV cryptosystem. +type Parameters struct { N uint64 T uint64 Qi []uint64 @@ -21,10 +41,63 @@ type Params struct { Sigma float64 } -// ~128 bit security with sigma = 3.19 and secret with ternary distribution -var ParamSets60 = []Params{ +// 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{ {4096, 65537, ring.Qi60[len(ring.Qi60)-2:], ring.Pi60[len(ring.Pi60)-3:], 3.19}, {8192, 65537, ring.Qi60[len(ring.Qi60)-4:], ring.Pi60[len(ring.Pi60)-5:], 3.19}, {16384, 65537, ring.Qi60[len(ring.Qi60)-8:], ring.Pi60[len(ring.Pi60)-9:], 3.19}, {32768, 65537, ring.Qi60[len(ring.Qi60)-16:], ring.Pi60[len(ring.Pi60)-17:], 3.19}, } + +// Equals compares two sets of parameters for equality +func (p *Parameters) Equals(other *Parameters) bool { + if p == other { + return true + } + return p.N == other.N && EqualSlice(p.Qi, other.Qi) && EqualSlice(p.Pi, other.Pi) && 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 + return []byte{}, nil + } + b := utils.NewBuffer(make([]byte, 0, 3+((2+len(p.Qi)+len(p.Pi))<<3))) + b.WriteUint8(uint8(bits.Len64(p.N) - 1)) + b.WriteUint8(uint8(len(p.Qi))) + b.WriteUint8(uint8(len(p.Pi))) + b.WriteUint64(p.T) + b.WriteUint64(uint64(p.Sigma * (1 << 32))) + b.WriteUint64Slice(p.Qi) + b.WriteUint64Slice(p.Pi) + return b.Bytes(), nil +} + +// UnMarshalBinary decodes a []byte into a parameter set struct +func (p *Parameters) UnMarshalBinary(data []byte) error { + if len(data) < 3 { + return errors.New("invalid parameters encoding") + } + b := utils.NewBuffer(data) + p.N = 1 << uint64(b.ReadUint8()) + if p.N > MaxN { + return errors.New("polynomial degree is too large") + } + lenQi := uint64(b.ReadUint8()) + if lenQi > MaxModuliCount { + return fmt.Errorf("len(Qi) is larger than %d", MaxModuliCount) + } + lenPi := uint64(b.ReadUint8()) + if lenPi > MaxModuliCount { + return fmt.Errorf("len(Pi) is larger than %d", MaxModuliCount) + } + p.T = b.ReadUint64() + p.Sigma = math.Round((float64(b.ReadUint64())/float64(1<<32))*100) / 100 + p.Qi = make([]uint64, lenQi, lenQi) + p.Pi = make([]uint64, lenPi, lenPi) + b.ReadUint64Slice(p.Qi) + b.ReadUint64Slice(p.Pi) + return nil +} diff --git a/bfv/params_test.go b/bfv/params_test.go new file mode 100644 index 00000000..7f7a7298 --- /dev/null +++ b/bfv/params_test.go @@ -0,0 +1,27 @@ +package bfv + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestParams_BinaryMarshaller(t *testing.T) { + t.Run("ZeroValue", func(t *testing.T) { + bytes, err := (&Parameters{}).MarshalBinary() + assert.Nil(t, err) + assert.Equal(t, []byte{}, bytes) + var p Parameters + err = p.UnMarshalBinary(bytes) + assert.NotNil(t, err) + }) + t.Run("SupportedParams", func(t *testing.T) { + for _, params := range DefaultParams { + bytes, err := params.MarshalBinary() + assert.Nil(t, err) + var p Parameters + err = p.UnMarshalBinary(bytes) + assert.Nil(t, err) + assert.Equal(t, params, p) + } + }) +} diff --git a/dbfv/dbfv_benchmark_test.go b/dbfv/dbfv_benchmark_test.go index 1977d243..33ce681d 100644 --- a/dbfv/dbfv_benchmark_test.go +++ b/dbfv/dbfv_benchmark_test.go @@ -10,7 +10,7 @@ import ( func Benchmark_DBFVScheme(b *testing.B) { - paramSets := bfv.ParamSets60[3:4] + paramSets := bfv.DefaultParams[3:4] bitDecomps := []uint64{60} nParties := []int{2} diff --git a/dbfv/dbfv_test.go b/dbfv/dbfv_test.go index 8f7cfee3..2932f8d4 100644 --- a/dbfv/dbfv_test.go +++ b/dbfv/dbfv_test.go @@ -10,7 +10,7 @@ import ( func Test_DBFVScheme(t *testing.T) { - paramSets := bfv.ParamSets60[0:1] + paramSets := bfv.DefaultParams[0:1] bitDecomps := []uint64{60} nParties := []int{5} diff --git a/go.mod b/go.mod index 56e767bc..77f37470 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,8 @@ module github.com/lca1/lattigo go 1.12 -require ( - golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 - golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect -) +require golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + +require golang.org/x/lint v0.0.0-20190409202823-959b441ac422 + +require github.com/stretchr/testify v0.0.0-20190311161405-34c6fa2dc709 diff --git a/go.sum b/go.sum index 64a35b6b..2980382d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,14 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v0.0.0-20190311161405-34c6fa2dc709 h1:zN7m1FsHm1PeW8oJ3JvZPC5Cc1lWnEiHtS1i6DpXcm0= +github.com/stretchr/testify v0.0.0-20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -9,5 +16,4 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/utils/buffer.go b/utils/buffer.go new file mode 100644 index 00000000..38f79cb3 --- /dev/null +++ b/utils/buffer.go @@ -0,0 +1,56 @@ +// Package containing helper structures and function +package utils + +// Buffer is a simple wrapper around a []byte to facilitate efficient marshaling of lattigo's objects +type Buffer struct { + buf []byte +} + +// NewBuffer creates a new buffer from the provided backing slice +func NewBuffer(s []byte) *Buffer { + return &Buffer{s} +} + +func (b *Buffer) WriteUint8(c byte) { + b.buf = append(b.buf, c) +} + +func (b *Buffer) WriteUint64(v uint64) { + b.buf = append(b.buf, byte(v>>56), + byte(v>>48), + byte(v>>40), + byte(v>>32), + byte(v>>24), + byte(v>>16), + byte(v>>8), + byte(v)) +} + +func (b *Buffer) WriteUint64Slice(s []uint64) { + for _, v := range s { + b.WriteUint64(v) + } +} + +func (b *Buffer) ReadUint8() byte { + v := b.buf[0] + b.buf = b.buf[1:] + return v +} + +func (b *Buffer) ReadUint64() uint64 { + v := b.buf[:8] + b.buf = b.buf[8:] + return uint64(v[7]) | uint64(v[6])<<8 | uint64(v[5])<<16 | uint64(v[4])<<24 | + uint64(v[3])<<32 | uint64(v[2])<<40 | uint64(v[1])<<48 | uint64(v[0])<<56 +} + +func (b *Buffer) ReadUint64Slice(rec []uint64) { + for i := range rec { + rec[i] = b.ReadUint64() + } +} + +func (b *Buffer) Bytes() []byte { + return b.buf +} diff --git a/utils/buffer_test.go b/utils/buffer_test.go new file mode 100644 index 00000000..c67007e0 --- /dev/null +++ b/utils/buffer_test.go @@ -0,0 +1,39 @@ +// Package containing helper structures and function +package utils + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestNewBuffer(t *testing.T) { + assert.Equal(t, []byte(nil), NewBuffer(nil).Bytes()) + assert.Equal(t, []byte{}, NewBuffer([]byte{}).Bytes()) + assert.Equal(t, []byte{1, 2, 3}, NewBuffer([]byte{1, 2, 3}).Bytes()) +} + +func TestBuffer_WriteReadUint8(t *testing.T) { + b := NewBuffer(make([]byte, 0, 1)) + b.WriteUint8(0xff) + assert.Equal(t, []byte{0xff}, b.Bytes()) + assert.Equal(t, byte(0xff), b.ReadUint8()) + assert.Equal(t, []byte{}, b.Bytes()) +} + +func TestBuffer_WriteReadUint64(t *testing.T) { + b := NewBuffer(make([]byte, 0, 8)) + b.WriteUint64(0x1122334455667788) + assert.Equal(t, []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, b.Bytes()) + assert.Equal(t, uint64(0x1122334455667788), b.ReadUint64()) + assert.Equal(t, []byte{}, b.Bytes()) +} + +func TestBuffer_WriteReadUint64Slice(t *testing.T) { + b := NewBuffer(make([]byte, 0, 8)) + b.WriteUint64Slice([]uint64{0x1122334455667788}) + assert.Equal(t, []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, b.Bytes()) + s := make([]uint64, 1, 1) + b.ReadUint64Slice(s) + assert.Equal(t, []uint64{0x1122334455667788}, s) + assert.Equal(t, []byte{}, b.Bytes()) +}