Skip to content

Commit

Permalink
Merge pull request #504 from tuneinsight/integrity-across-versions
Browse files Browse the repository at this point in the history
Serialization inconsistency detection
  • Loading branch information
lehugueni authored Nov 18, 2024
2 parents 661575c + 698dd7b commit 3662ccc
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 0 deletions.
23 changes: 23 additions & 0 deletions core/rlwe/encryptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,29 @@ func newEncryptor(params Parameters) *Encryptor {
}
}

// NewTestEncryptorWithPRNG creates a new [Encryptor] that uses the provided prng for randomness.
// CAUTION: THIS FUNCTION SHOULD BE USED FOR TESTING PURPOSES ONLY.
func NewTestEncryptorWithPRNG(params ParameterProvider, key EncryptionKey, prng sampling.PRNG) *Encryptor {
p := *params.GetRLWEParameters()

enc := NewEncryptor(params, key)
xeSampler, err := ring.NewSampler(prng, p.RingQ(), p.Xe(), false)
if err != nil {
panic(fmt.Errorf("NewEncryptorWithPRNG: cannot create xeSampler %w", err))
}
xsSampler, err := ring.NewSampler(prng, p.RingQ(), p.Xs(), false)
if err != nil {
panic(fmt.Errorf("NewEncryptorWithPRNG: cannot create xsSampler %w", err))
}
uniformSampler := ringqp.NewUniformSampler(prng, *p.RingQP())
enc.prng = prng
enc.xeSampler = xeSampler
enc.xsSampler = xsSampler
enc.uniformSampler = uniformSampler

return enc
}

type encryptorBuffers struct {
buffQP [3]ringqp.Poly
}
Expand Down
117 changes: 117 additions & 0 deletions core/rlwe/rlwe_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package rlwe

import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"math"
"runtime"
"testing"

"golang.org/x/crypto/blake2b"

"github.com/stretchr/testify/require"

"github.com/tuneinsight/lattigo/v6/ring"
Expand All @@ -31,6 +34,86 @@ func testString(params Parameters, levelQ, levelP, bpw2 int, opname string) stri
params.RingType())
}

// TestRLWEConstSerialization test detects (fails) if the serialization of
// [PublicKey], [SecretKey], [Ciphertext], [GaloisKey], [MemEvaluationKeySet] has changed.
// If such a modification is intended, this test must be updated and users notified s.t.
// old serialized objects can be converted to the new format.
func TestRLWEConstSerialization(t *testing.T) {
// Note: changing nbIteration will change the expected value
const nbIteration = 10
const expected = "XRdlwx5vEX9qdGY3CeeAxzGHa0gbXghzpLhV0eIgVk8="
var err error
defaultParamsLiteral := testInsecure
seedKeyGen := []byte{'l', 'a', 't'}
seedEnc := []byte{'t', 'i', 'g', 'o'}
hash, err := blake2b.New(32, nil)
require.Nil(t, err)

for _, paramsLit := range defaultParamsLiteral[:] {

for _, NTTFlag := range []bool{true, false}[:] {

for _, RingType := range []ring.Type{ring.Standard, ring.ConjugateInvariant}[:] {

paramsLit.NTTFlag = NTTFlag
paramsLit.RingType = RingType

var params Parameters
if params, err = NewParametersFromLiteral(paramsLit.ParametersLiteral); err != nil {
t.Fatal(err)
}

detTC, err := NewDeterministicTestContext(params, seedKeyGen, seedEnc)
require.Nil(t, err)
for i := 0; i < nbIteration; i++ {
// Add marshalled (sk, pk) to the hash input
sk, pk := detTC.kgen.GenKeyPairNew()
skBytes, err := sk.MarshalBinary()
hash.Write(skBytes)
require.Nil(t, err)

pkBytes, err := pk.MarshalBinary()
require.Nil(t, err)
hash.Write(pkBytes)

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

// Add marshalled MemEvaluationKeySet to the hash input
relinKey := detTC.kgen.GenRelinearizationKeyNew(sk)
evk := NewMemEvaluationKeySet(relinKey, galKey...)
evkBytes, err := evk.MarshalBinary()
require.Nil(t, err)
hash.Write(evkBytes)

// Add marshalled MemEvaluationKeySet to the hash input
ct := NewCiphertext(params, 1, params.MaxLevel())
pt := genPlaintext(params, params.MaxLevel(), (1<<int(params.LogQ()))-1)
detTC.enc.Encrypt(pt, ct)
ctBytes, err := ct.MarshalBinary()
require.Nil(t, err)
hash.Write(ctBytes)
ctJSON, err := ct.MarshalJSON()
require.Nil(t, err)
hash.Write(ctJSON)
}
}
}
}

digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))

// In case the value expected must be updated, uncomment to print the new expected value:
// fmt.Println(digest)

require.Equal(t, expected, digest)
}

func TestRLWE(t *testing.T) {

var err error
Expand Down Expand Up @@ -219,6 +302,40 @@ func testUserDefinedParameters(t *testing.T) {

}

func NewDeterministicTestContext(params Parameters, seedKeyGen, seedEnc []byte) (tc *TestContext, err error) {
prngKGen, err := sampling.NewKeyedPRNG(seedKeyGen)
if err != nil {
panic(fmt.Errorf("NewDeterministicTestContext: failed to make prngKGen %w", err))
}
prngEnc, err := sampling.NewKeyedPRNG(seedEnc)
if err != nil {
panic(fmt.Errorf("NewDeterministicTestContext: failed to make prngEnc %w", err))
}
kgen := NewKeyGenerator(params)
kgenEncryptor := NewTestEncryptorWithPRNG(params, nil, prngKGen)
kgen.Encryptor = kgenEncryptor

sk := kgen.GenSecretKeyNew()

pk := kgen.GenPublicKeyNew(sk)

eval := NewEvaluator(params, nil)

enc := NewTestEncryptorWithPRNG(params, sk, prngEnc)

dec := NewDecryptor(params, sk)

return &TestContext{
params: params,
kgen: kgen,
sk: sk,
pk: pk,
enc: enc,
dec: dec,
eval: eval,
}, nil
}

func NewTestContext(params Parameters) (tc *TestContext, err error) {
kgen := NewKeyGenerator(params)
sk := kgen.GenSecretKeyNew()
Expand Down
45 changes: 45 additions & 0 deletions schemes/bgv/bgv_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bgv

import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
Expand All @@ -9,6 +10,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"golang.org/x/crypto/blake2b"

"github.com/tuneinsight/lattigo/v6/core/rlwe"
"github.com/tuneinsight/lattigo/v6/ring"
Expand Down Expand Up @@ -871,6 +873,49 @@ func testEvaluatorBfv(tc *TestContext, t *testing.T) {
})
}

// TestBGVParamsConstSerialization test detects (fails) if the serialization of [Parameters] has changed.
// If such a modification is intended, this test must be updated and users notified s.t.
// old serialized parameters can be converted to the new format.
func TestBGVParamsConstSerialization(t *testing.T) {
const expected = "7aw0pU3xCs2Hu8zHKqkPRUpltHC0+P+UxzMSqKJwSFs="
var err error
distribs := []ring.DistributionParameters{rlwe.DefaultXe, rlwe.DefaultXs, ring.Ternary{H: 192}}
hash, err := blake2b.New(32, nil)
if err != nil {
t.Fatal(err)
}

// Test with different CKKS params, including different plaintext moduli and distributions
for _, paramsLit := range []ParametersLiteral{ExampleParameters128BitLogN14LogQP438, testInsecure}[:] {
for _, ptMod := range testPlaintextModulus[:] {
for _, distXe := range distribs {
for _, distXs := range distribs {

paramsLit.Xe = distXe
paramsLit.Xs = distXs
paramsLit.PlaintextModulus = ptMod
var params Parameters
if params, err = NewParametersFromLiteral(paramsLit); err != nil {
t.Fatal(err)
}
paramsBytes, err := params.MarshalBinary()
require.Nil(t, err)
hash.Write(paramsBytes)
paramsBytes, err = params.MarshalJSON()
require.Nil(t, err)
hash.Write(paramsBytes)
}
}
}
}
digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))

// In case the value expected must be updated, uncomment to print the new expected value:
// fmt.Println(digest)

require.Equal(t, expected, digest)
}

var (
// testInsecure are insecure parameters used for the sole purpose of fast testing.
testInsecure = ParametersLiteral{
Expand Down
45 changes: 45 additions & 0 deletions schemes/ckks/ckks_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ckks

import (
"encoding/base64"
"encoding/json"
"flag"
"fmt"
Expand All @@ -10,6 +11,7 @@ import (
"testing"

"github.com/stretchr/testify/require"
"golang.org/x/crypto/blake2b"

"github.com/tuneinsight/lattigo/v6/core/rlwe"
"github.com/tuneinsight/lattigo/v6/ring"
Expand Down Expand Up @@ -798,6 +800,49 @@ func testBridge(tc *TestContext, t *testing.T) {
})
}

// TestCKKSParamsConstSerialization test detects (fails) if the serialization of [Parameters] has changed.
// If such a modification is intended, this test must be updated and users notified s.t.
// old serialized parameters can be converted to the new format.
func TestCKKSParamsConstSerialization(t *testing.T) {
const expected = "Bo962QjkASlly6oMAojaEYOIGTh5v0nhzWvu93XgVRk="
var err error
distribs := []ring.DistributionParameters{rlwe.DefaultXe, rlwe.DefaultXs, ring.Ternary{H: 192}}
hash, err := blake2b.New(32, nil)
if err != nil {
t.Fatal(err)
}

// Test with different CKKS params, including different ringtypes and distributions
for _, paramsLit := range testParametersLiteral[:] {
for _, ringType := range []ring.Type{ring.Standard, ring.ConjugateInvariant}[:] {
for _, distXe := range distribs {
for _, distXs := range distribs {

paramsLit.RingType = ringType
paramsLit.Xe = distXe
paramsLit.Xs = distXs
var params Parameters
if params, err = NewParametersFromLiteral(paramsLit); err != nil {
t.Fatal(err)
}
paramsBytes, err := params.MarshalBinary()
require.Nil(t, err)
hash.Write(paramsBytes)
paramsBytes, err = params.MarshalJSON()
require.Nil(t, err)
hash.Write(paramsBytes)
}
}
}
}
digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))

// In case the value expected must be updated, uncomment to print the new expected value:
// fmt.Println(digest)

require.Equal(t, expected, digest)
}

func name(opname string, tc *TestContext) string {

var precMode string
Expand Down

0 comments on commit 3662ccc

Please sign in to comment.