From 053ae45b59bef06f1ec0f92c6d46945470c863e1 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 28 Jun 2018 14:28:05 +0200 Subject: [PATCH 1/4] Let to pass an arbitrary number of cyphers to PublicKey.Add `PublicKey.Add` accepts now an arbitrary number of `*Cypher`s. Previously, it was possible to add only two `*Cypher*` instances and it was quite unwieldy for a lot of use cases. The current implementation is backward compatible. It's still possible to pass just two `*Cypher` arguments, so every project referencing the library will observe no compilation issues. --- paillier.go | 20 +++++++++++++++----- paillier_test.go | 20 ++++++++++++-------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/paillier.go b/paillier.go index 9b6fba2..62d3e35 100644 --- a/paillier.go +++ b/paillier.go @@ -40,14 +40,24 @@ func (pub *PublicKey) Encrypt(m *big.Int, random io.Reader) (*Cypher, error) { return &Cypher{new(big.Int).Mod(new(big.Int).Mul(rn, gm), nSquare)}, nil } -// Takes two cypher texts and returns a 3rd one that encode -// the sum of the two plain texts. +// Add takes an arbitrary number of cyphertexts and returns one that encodes +// their sum. // // It's possible because Paillier is a homomorphic encryption scheme, where // E(m1) * E(m2) = E(m1 + m2) -func (this *PublicKey) Add(cypher1, cypher2 *Cypher) *Cypher { - m := new(big.Int).Mul(cypher1.C, cypher2.C) - return &Cypher{new(big.Int).Mod(m, this.GetNSquare())} +func (pub *PublicKey) Add(cypher ...*Cypher) *Cypher { + accumulator := big.NewInt(1) + + for _, c := range cypher { + accumulator = new(big.Int).Mod( + new(big.Int).Mul(accumulator, c.C), + pub.GetNSquare(), + ) + } + + return &Cypher{ + C: accumulator, + } } type PrivateKey struct { diff --git a/paillier_test.go b/paillier_test.go index 5e9fe9b..8918e27 100644 --- a/paillier_test.go +++ b/paillier_test.go @@ -64,13 +64,17 @@ func TestEncryptDecryptSmall(t *testing.T) { } } -func TestAddCypher(t *testing.T) { - privateKey := CreatePrivateKey(big.NewInt(13), big.NewInt(11)) - cypher1, _ := privateKey.Encrypt(big.NewInt(12), rand.Reader) - cypher2, _ := privateKey.Encrypt(big.NewInt(13), rand.Reader) - cypher3 := privateKey.Add(cypher1, cypher2) - m := privateKey.Decrypt(cypher3) - if !reflect.DeepEqual(m, big.NewInt(25)) { - t.Error(m) +func TestAddCyphers(t *testing.T) { + privateKey := CreatePrivateKey(big.NewInt(17), big.NewInt(13)) + + cypher1, _ := privateKey.Encrypt(big.NewInt(5), rand.Reader) + cypher2, _ := privateKey.Encrypt(big.NewInt(6), rand.Reader) + cypher3, _ := privateKey.Encrypt(big.NewInt(7), rand.Reader) + cypher4, _ := privateKey.Encrypt(big.NewInt(8), rand.Reader) + cypher5 := privateKey.Add(cypher1, cypher2, cypher3, cypher4) + + m := privateKey.Decrypt(cypher5) + if !reflect.DeepEqual(m, big.NewInt(26)) { + t.Errorf("Unexpected decrypted value [%v]", m) } } From 5631d7cca57ef7c2cb684e3c037a4f2dc96d8fc2 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 28 Jun 2018 15:46:45 +0200 Subject: [PATCH 2/4] Better explanation of an additively homomorphic property --- paillier.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/paillier.go b/paillier.go index 62d3e35..d324248 100644 --- a/paillier.go +++ b/paillier.go @@ -44,7 +44,10 @@ func (pub *PublicKey) Encrypt(m *big.Int, random io.Reader) (*Cypher, error) { // their sum. // // It's possible because Paillier is a homomorphic encryption scheme, where -// E(m1) * E(m2) = E(m1 + m2) +// the product of two ciphertexts will decrypt to the sum of their corresponding +// plaintexts: +// +// D( (E(m1) * E(m2) mod n^2) ) = m1 + m2 mod n func (pub *PublicKey) Add(cypher ...*Cypher) *Cypher { accumulator := big.NewInt(1) From b7abc00044a1942bb8ac7eef55ad73d2fbc53e72 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 28 Jun 2018 15:47:41 +0200 Subject: [PATCH 3/4] Test of an additively homomorphic property for a small modulus --- paillier_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/paillier_test.go b/paillier_test.go index 8918e27..e157ec2 100644 --- a/paillier_test.go +++ b/paillier_test.go @@ -78,3 +78,17 @@ func TestAddCyphers(t *testing.T) { t.Errorf("Unexpected decrypted value [%v]", m) } } + +func TestAddCypherWithSmallKeyModulus(t *testing.T) { + privateKey := CreatePrivateKey(big.NewInt(7), big.NewInt(5)) + + cypher1, _ := privateKey.Encrypt(big.NewInt(41), rand.Reader) + cypher2, _ := privateKey.Encrypt(big.NewInt(219), rand.Reader) + cypher3, _ := privateKey.Encrypt(big.NewInt(54), rand.Reader) + cypher4 := privateKey.Add(cypher1, cypher2, cypher3) + + m := privateKey.Decrypt(cypher4) + if !reflect.DeepEqual(m, big.NewInt(34)) { + t.Errorf("Unexpected decrypted value [%v]", m) + } +} From d439831ffd09f48f52d5ed771aa635818d9e3bb7 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Thu, 28 Jun 2018 15:52:26 +0200 Subject: [PATCH 4/4] pub self-reference renamed to pk We want to use a noun-ish name, not an adjective. --- paillier.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/paillier.go b/paillier.go index d324248..9eca2ab 100644 --- a/paillier.go +++ b/paillier.go @@ -10,11 +10,11 @@ type PublicKey struct { N *big.Int } -func (this *PublicKey) GetNSquare() *big.Int { - return new(big.Int).Mul(this.N, this.N) +func (pk *PublicKey) GetNSquare() *big.Int { + return new(big.Int).Mul(pk.N, pk.N) } -// Encode a plaintext in a cypher one. The plain text must be smaller that +// Encrypt a plaintext into a cypher one. The plain text must be smaller that // N and bigger or equal than zero. random is usually rand.Reader from the // package crypto/rand. // @@ -24,19 +24,19 @@ func (this *PublicKey) GetNSquare() *big.Int { // See [KL 08] construction 11.32, page 414. // // Returns an error if an error has be returned by io.Reader. -func (pub *PublicKey) Encrypt(m *big.Int, random io.Reader) (*Cypher, error) { - r, err := GetRandomNumberInMultiplicativeGroup(pub.N, random) +func (pk *PublicKey) Encrypt(m *big.Int, random io.Reader) (*Cypher, error) { + r, err := GetRandomNumberInMultiplicativeGroup(pk.N, random) if err != nil { return nil, err } - nSquare := pub.GetNSquare() + nSquare := pk.GetNSquare() // g is _always_ equal n+1 // Threshold encryption is safe only for g=n+1 choice. // See [DJN 10], section 5.1 - g := new(big.Int).Add(pub.N, big.NewInt(1)) + g := new(big.Int).Add(pk.N, big.NewInt(1)) gm := new(big.Int).Exp(g, m, nSquare) - rn := new(big.Int).Exp(r, pub.N, nSquare) + rn := new(big.Int).Exp(r, pk.N, nSquare) return &Cypher{new(big.Int).Mod(new(big.Int).Mul(rn, gm), nSquare)}, nil } @@ -48,13 +48,13 @@ func (pub *PublicKey) Encrypt(m *big.Int, random io.Reader) (*Cypher, error) { // plaintexts: // // D( (E(m1) * E(m2) mod n^2) ) = m1 + m2 mod n -func (pub *PublicKey) Add(cypher ...*Cypher) *Cypher { +func (pk *PublicKey) Add(cypher ...*Cypher) *Cypher { accumulator := big.NewInt(1) for _, c := range cypher { accumulator = new(big.Int).Mod( new(big.Int).Mul(accumulator, c.C), - pub.GetNSquare(), + pk.GetNSquare(), ) }