From 2a33f6e35c4e9486e482163bddf7cc9691b1c1f7 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Tue, 31 Jul 2018 16:18:19 +0200 Subject: [PATCH 1/2] Plaintext space check According to [DJN 10], the plaintext space is `Z_n`. We had this information in the `Encrypt` and `EncryptWithR` documentation but sometimes, when encrypting result of more complex equations, it's easy to miss the fact the result is out of the allowed range. Here we have just a safety check making sure that if plaintext is out of allowed space, we report an error. --- paillier.go | 14 ++++++++--- paillier_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/paillier.go b/paillier.go index e507203..a7153e8 100644 --- a/paillier.go +++ b/paillier.go @@ -15,8 +15,8 @@ func (pk *PublicKey) GetNSquare() *big.Int { } // EncryptWithR encrypts a plaintext into a cypher one with random `r` specified -// in the argument. The plain text must be smaller that N and bigger or equal -// than zero. `r` is the randomness used to encrypt the plaintext. `r` must be +// in the argument. The plain text must be smaller that N and bigger than or +// equal zero. `r` is the randomness used to encrypt the plaintext. `r` must be // a random element from a multiplicative group of integers modulo N. // // If you don't need to use the specific `r`, you should use the `Encrypt` @@ -28,6 +28,14 @@ func (pk *PublicKey) GetNSquare() *big.Int { // // See [KL 08] construction 11.32, page 414. func (pk *PublicKey) EncryptWithR(m *big.Int, r *big.Int) (*Cypher, error) { + if m.Cmp(ZERO) == -1 || m.Cmp(pk.N) != -1 { // m < 0 || m >= N ? + return nil, fmt.Errorf( + "%v is out of allowed plaintext space [0, %v)", + m, + pk.N, + ) + } + nSquare := pk.GetNSquare() // g is _always_ equal n+1 @@ -40,7 +48,7 @@ func (pk *PublicKey) EncryptWithR(m *big.Int, r *big.Int) (*Cypher, error) { } // 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 +// N and bigger than or equal zero. random is usually rand.Reader from the // package crypto/rand. // // m - plaintext to encrypt diff --git a/paillier_test.go b/paillier_test.go index bc0af06..558eae3 100644 --- a/paillier_test.go +++ b/paillier_test.go @@ -2,7 +2,9 @@ package paillier import ( "crypto/rand" + "errors" "math/big" + "reflect" "testing" ) @@ -63,6 +65,65 @@ func TestEncryptDecryptSmall(t *testing.T) { } } +func TestCheckPlaintextSpace(t *testing.T) { + p := big.NewInt(13) + q := big.NewInt(11) + + // N = pq = 143 so the plaintext space is [0, 143) + privateKey := CreatePrivateKey(p, q) + + var tests = map[string]struct { + plaintext *big.Int + expectedError error + }{ + "plaintext less than 0": { + plaintext: big.NewInt(-1), + expectedError: errors.New("-1 is out of allowed plaintext space [0, 143)"), + }, + "plaintext equal 0": { + plaintext: big.NewInt(0), + }, + "plaintext equal 1": { + plaintext: big.NewInt(1), + }, + "plaintext equal 142": { + plaintext: big.NewInt(142), + }, + "plaintext equal 143": { + plaintext: big.NewInt(143), + expectedError: errors.New("143 is out of allowed plaintext space [0, 143)"), + }, + "plaintext equal 144": { + plaintext: big.NewInt(144), + expectedError: errors.New("144 is out of allowed plaintext space [0, 143)"), + }, + } + + for testName, test := range tests { + t.Run(testName, func(t *testing.T) { + cypher, err := privateKey.Encrypt(test.plaintext, rand.Reader) + if !reflect.DeepEqual(err, test.expectedError) { + t.Errorf( + "Unexpected error\nExpected: %v\nActual: %v", + test.expectedError, + err, + ) + } + + if test.expectedError == nil { + decrypted := privateKey.Decrypt(cypher) + if test.plaintext.Cmp(decrypted) != 0 { + t.Errorf( + "Unexpected decryption\nExpected: %v\nActual: %v", + test.plaintext, + decrypted, + ) + } + } + }) + } +} + func TestAddCyphers(t *testing.T) { privateKey := CreatePrivateKey(big.NewInt(17), big.NewInt(13)) From edb17e88e7233a7fcbe2588e10e25dab6974b348 Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Tue, 31 Jul 2018 17:06:42 +0200 Subject: [PATCH 2/2] TestAddCypherWithSmallKeyModulus fixed - m can't be bigger than N now For Paillier modulus N=pq, plaintext m space is [0, N). We now report an error if m is not in the allowed range. --- paillier_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/paillier_test.go b/paillier_test.go index 558eae3..5df9256 100644 --- a/paillier_test.go +++ b/paillier_test.go @@ -142,13 +142,13 @@ func TestAddCyphers(t *testing.T) { 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) + cypher1, _ := privateKey.Encrypt(big.NewInt(30), rand.Reader) + cypher2, _ := privateKey.Encrypt(big.NewInt(25), rand.Reader) + cypher3, _ := privateKey.Encrypt(big.NewInt(11), rand.Reader) cypher4 := privateKey.Add(cypher1, cypher2, cypher3) m := privateKey.Decrypt(cypher4) - if m.Cmp(big.NewInt(34)) != 0 { + if m.Cmp(big.NewInt(31)) != 0 { t.Errorf("Unexpected decrypted value [%v]", m) } }