Skip to content

Commit

Permalink
Merge branch 'main' into 510-bgv-wrong-result-from-innersum
Browse files Browse the repository at this point in the history
  • Loading branch information
lehugueni committed Dec 18, 2024
2 parents dda9b25 + 1d70f2c commit 7c89ca1
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 10 deletions.
7 changes: 4 additions & 3 deletions core/rlwe/keygenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,13 @@ func (kgen KeyGenerator) genEvaluationKey(skIn ring.Poly, skOut ringqp.Poly, evk
// For a compressed evaluation key, a seed is created and stored in the EvaluationKey struct
// struct while an uncompressed key uses an ephemeral seed.
if evk.IsCompressed() {
evk.Seed = make([]byte, 32)
if n, err := kgen.prng.Read(evk.Seed); n != 32 || err != nil {
var seed [32]byte
if n, err := kgen.prng.Read(seed[:]); n != 32 || err != nil {
panic(fmt.Errorf("unable to sample evaluation key seed"))
}
evk.Seed = &seed

sampler, err := sampling.NewKeyedPRNG(evk.Seed)
sampler, err := sampling.NewKeyedPRNG(seed[:])
if err != nil {
panic(fmt.Errorf("sampling.NewKeyedPRNG: %w", err))
}
Expand Down
117 changes: 115 additions & 2 deletions core/rlwe/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (p *PublicKey) isEncryptionKey() {}
// is used to bring it back to its original key.
type EvaluationKey struct {
GadgetCiphertext
Seed []byte
Seed *[32]byte // Must be != nil iff EvaluationKey.IsCompressed() = true
}

type EvaluationKeyParameters struct {
Expand Down Expand Up @@ -362,7 +362,11 @@ func (evk EvaluationKey) Expand(params ParameterProvider, buffer *GadgetCipherte
return fmt.Errorf("evaluation key is not compressed")
}

prng, err := sampling.NewKeyedPRNG(evk.Seed)
if evk.Seed == nil {
return fmt.Errorf("seed is missing")
}

prng, err := sampling.NewKeyedPRNG((*evk.Seed)[:])
if err != nil {
panic(fmt.Errorf("sampling.NewKeyedPRNG: %s", err))
}
Expand Down Expand Up @@ -417,6 +421,115 @@ func (evk EvaluationKey) Expand(params ParameterProvider, buffer *GadgetCipherte
return nil
}

// BinarySize returns the serialized size of the object in bytes.
func (evk EvaluationKey) BinarySize() (size int) {
if evk.Seed != nil {
return evk.GadgetCiphertext.BinarySize() + len(*evk.Seed)
}
return evk.GadgetCiphertext.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 (evk EvaluationKey) WriteTo(w io.Writer) (n int64, err error) {
switch w := w.(type) {
case buffer.Writer:

var inc int64

if inc, err = evk.GadgetCiphertext.WriteTo(w); err != nil {
return n + inc, err
}

n += inc

if evk.IsCompressed() {

// Sanity check, should not happen unless evk has been manually modified
if evk.Seed == nil {
return n + inc, fmt.Errorf("writing compressed evaluation key: the seed is nil")
}

if inc, err = buffer.Write(w, (*evk.Seed)[:]); err != nil {
return n + inc, err
}

n += inc
}

if err = w.Flush(); err != nil {
return n, err
}
return

default:
return evk.WriteTo(bufio.NewWriter(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 (evk *EvaluationKey) ReadFrom(r io.Reader) (n int64, err error) {
switch r := r.(type) {
case buffer.Reader:

var inc int64

if inc, err = evk.GadgetCiphertext.ReadFrom(r); err != nil {
return n + inc, err
}

n += inc

if evk.IsCompressed() {
var seed [32]byte
if inc, err = buffer.Read(r, seed[:]); err != nil {
return n + inc, err
}

evk.Seed = &seed

n += inc
}

return
default:
return evk.ReadFrom(bufio.NewReader(r))
}
}

// MarshalBinary encodes the object into a binary form on a newly allocated slice of bytes.
func (evk EvaluationKey) MarshalBinary() (p []byte, err error) {
buf := buffer.NewBufferSize(evk.BinarySize())
_, err = evk.WriteTo(buf)
return buf.Bytes(), err
}

// UnmarshalBinary decodes a slice of bytes generated by
// [EvaluationKey.MarshalBinary] or [EvaluationKey.WriteTo] on the object.
func (evk *EvaluationKey) UnmarshalBinary(p []byte) (err error) {
_, err = evk.ReadFrom(buffer.NewBuffer(p))
return
}

// CopyNew creates a deep copy of the target [EvaluationKey] and returns it.
func (evk EvaluationKey) CopyNew() *EvaluationKey {
return &EvaluationKey{GadgetCiphertext: *evk.GadgetCiphertext.CopyNew()}
Expand Down
18 changes: 13 additions & 5 deletions core/rlwe/rlwe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func testString(params Parameters, levelQ, levelP, bpw2 int, opname string) stri
func TestRLWEConstSerialization(t *testing.T) {
// Note: changing nbIteration will change the expected value
const nbIteration = 10
const expected = "XRdlwx5vEX9qdGY3CeeAxzGHa0gbXghzpLhV0eIgVk8="
const expected = "/mTt2kB+03NdOMoI1msW+glCZmrF1sxEGQkFsC6P1SA="
var err error
defaultParamsLiteral := testInsecure
seedKeyGen := []byte{'l', 'a', 't'}
Expand Down Expand Up @@ -77,16 +77,20 @@ func TestRLWEConstSerialization(t *testing.T) {
hash.Write(pkBytes)

// Add marshalled GaloisKey to the hash input
galEl := params.GaloisElement(-1)
galEl1 := params.GaloisElement(-1)
galEl2 := params.GaloisElement(3)
galKey := detTC.kgen.GenGaloisKeysNew([]uint64{galEl, galEl2}, sk)
galKeyBytes, err := galKey[0].MarshalBinary()
galKey1 := detTC.kgen.GenGaloisKeyNew(galEl1, sk)
galKey2 := detTC.kgen.GenGaloisKeyNew(galEl2, sk, EvaluationKeyParameters{Compressed: true})
galKeyBytes, err := galKey1.MarshalBinary()
require.Nil(t, err)
hash.Write(galKeyBytes)
galKeyBytes, err = galKey2.MarshalBinary()
require.Nil(t, err)
hash.Write(galKeyBytes)

// Add marshalled MemEvaluationKeySet to the hash input
relinKey := detTC.kgen.GenRelinearizationKeyNew(sk)
evk := NewMemEvaluationKeySet(relinKey, galKey...)
evk := NewMemEvaluationKeySet(relinKey, galKey1, galKey2)
evkBytes, err := evk.MarshalBinary()
require.Nil(t, err)
hash.Write(evkBytes)
Expand Down Expand Up @@ -1239,6 +1243,10 @@ func testWriteAndRead(tc *TestContext, bpw2 int, t *testing.T) {
buffer.RequireSerializerCorrect(t, tc.kgen.GenEvaluationKeyNew(sk, sk))
})

t.Run(testString(params, levelQ, levelP, bpw2, "WriteAndRead/EvaluationKey/Compressed=True"), func(t *testing.T) {
buffer.RequireSerializerCorrect(t, tc.kgen.GenEvaluationKeyNew(sk, sk, EvaluationKeyParameters{Compressed: true}))
})

t.Run(testString(params, levelQ, levelP, bpw2, "WriteAndRead/RelinearizationKey"), func(t *testing.T) {
buffer.RequireSerializerCorrect(t, tc.kgen.GenRelinearizationKeyNew(tc.sk))
})
Expand Down

0 comments on commit 7c89ca1

Please sign in to comment.