From 6fc133ad6af4da8e9eb1ccb0012a21be9a00fa2f Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sat, 28 Oct 2017 11:24:15 +0200 Subject: [PATCH] Run tests on Seal/Open --- internal/crypto/crypto_int_test.go | 12 +- internal/crypto/crypto_test.go | 211 +++++++++++++++++++++++------ 2 files changed, 177 insertions(+), 46 deletions(-) diff --git a/internal/crypto/crypto_int_test.go b/internal/crypto/crypto_int_test.go index a5995a9ad..9473d1382 100644 --- a/internal/crypto/crypto_int_test.go +++ b/internal/crypto/crypto_int_test.go @@ -114,10 +114,10 @@ func TestCrypto(t *testing.T) { } nonce := NewRandomNonce() - ciphertext := k.Seal(msg, nonce, tv.plaintext, nil) + ciphertext := k.Seal(msg[0:], nonce, tv.plaintext, nil) // decrypt message - buf := make([]byte, len(tv.plaintext)) + buf := make([]byte, 0, len(tv.plaintext)) buf, err := k.Open(buf, nonce, ciphertext, nil) if err != nil { t.Fatal(err) @@ -130,7 +130,7 @@ func TestCrypto(t *testing.T) { // change mac, this must fail ciphertext[len(ciphertext)-8] ^= 0x23 - if _, err = k.Open(buf, nonce, ciphertext, nil); err != ErrUnauthenticated { + if _, err = k.Open(buf[:0], nonce, ciphertext, nil); err != ErrUnauthenticated { t.Fatal("wrong MAC value not detected") } // reset mac @@ -138,7 +138,7 @@ func TestCrypto(t *testing.T) { // tamper with nonce, this must fail nonce[2] ^= 0x88 - if _, err = k.Open(buf, nonce, ciphertext, nil); err != ErrUnauthenticated { + if _, err = k.Open(buf[:0], nonce, ciphertext, nil); err != ErrUnauthenticated { t.Fatal("tampered nonce not detected") } // reset nonce @@ -146,14 +146,14 @@ func TestCrypto(t *testing.T) { // tamper with message, this must fail ciphertext[16+5] ^= 0x85 - if _, err = k.Open(buf, nonce, ciphertext, nil); err != ErrUnauthenticated { + if _, err = k.Open(buf[:0], nonce, ciphertext, nil); err != ErrUnauthenticated { t.Fatal("tampered message not detected") } // test decryption p := make([]byte, len(tv.ciphertext)) nonce, ciphertext = tv.ciphertext[:16], tv.ciphertext[16:] - p, err = k.Open(p, nonce, ciphertext, nil) + p, err = k.Open(p[:0], nonce, ciphertext, nil) if err != nil { t.Fatal(err) } diff --git a/internal/crypto/crypto_test.go b/internal/crypto/crypto_test.go index f1a7b7994..94f22588b 100644 --- a/internal/crypto/crypto_test.go +++ b/internal/crypto/crypto_test.go @@ -24,18 +24,17 @@ func TestEncryptDecrypt(t *testing.T) { for _, size := range tests { data := rtest.Random(42, size) - buf := make([]byte, size+crypto.Extension) + buf := make([]byte, 0, size+crypto.Extension) - ciphertext, err := k.Encrypt(buf, data) - rtest.OK(t, err) - rtest.Assert(t, len(ciphertext) == len(data)+crypto.Extension, + nonce := crypto.NewRandomNonce() + ciphertext := k.Seal(buf[:0], nonce, data, nil) + rtest.Assert(t, len(ciphertext) == len(data)+k.Overhead(), "ciphertext length does not match: want %d, got %d", len(data)+crypto.Extension, len(ciphertext)) - plaintext := make([]byte, len(ciphertext)) - n, err := k.Decrypt(plaintext, ciphertext) + plaintext := make([]byte, 0, len(ciphertext)) + plaintext, err := k.Open(plaintext[:0], nonce, ciphertext, nil) rtest.OK(t, err) - plaintext = plaintext[:n] rtest.Assert(t, len(plaintext) == len(data), "plaintext length does not match: want %d, got %d", len(data), len(plaintext)) @@ -52,8 +51,9 @@ func TestSmallBuffer(t *testing.T) { _, err := io.ReadFull(rand.Reader, data) rtest.OK(t, err) - ciphertext := make([]byte, size/2) - ciphertext, err = k.Encrypt(ciphertext, data) + ciphertext := make([]byte, 0, size/2) + nonce := crypto.NewRandomNonce() + ciphertext = k.Seal(ciphertext[:0], nonce, data, nil) // this must extend the slice rtest.Assert(t, cap(ciphertext) > size/2, "expected extended slice, but capacity is only %d bytes", @@ -61,9 +61,8 @@ func TestSmallBuffer(t *testing.T) { // check for the correct plaintext plaintext := make([]byte, len(ciphertext)) - n, err := k.Decrypt(plaintext, ciphertext) + plaintext, err = k.Open(plaintext[:0], nonce, ciphertext, nil) rtest.OK(t, err) - plaintext = plaintext[:n] rtest.Assert(t, bytes.Equal(plaintext, data), "wrong plaintext returned") } @@ -78,37 +77,169 @@ func TestSameBuffer(t *testing.T) { ciphertext := make([]byte, 0, size+crypto.Extension) - ciphertext, err = k.Encrypt(ciphertext, data) - rtest.OK(t, err) + nonce := crypto.NewRandomNonce() + ciphertext = k.Seal(ciphertext, nonce, data, nil) // use the same buffer for decryption - n, err := k.Decrypt(ciphertext, ciphertext) + ciphertext, err = k.Open(ciphertext[:0], nonce, ciphertext, nil) rtest.OK(t, err) - ciphertext = ciphertext[:n] rtest.Assert(t, bytes.Equal(ciphertext, data), "wrong plaintext returned") } -func TestCornerCases(t *testing.T) { +func encrypt(t testing.TB, k *crypto.Key, data, ciphertext, nonce []byte) []byte { + prefixlen := len(ciphertext) + ciphertext = k.Seal(ciphertext, nonce, data, nil) + if len(ciphertext) != len(data)+k.Overhead()+prefixlen { + t.Fatalf("destination slice has wrong length, want %d, got %d", + len(data)+k.Overhead(), len(ciphertext)) + } + + return ciphertext +} + +func decryptNewSliceAndCompare(t testing.TB, k *crypto.Key, data, ciphertext, nonce []byte) { + plaintext := make([]byte, 0, len(ciphertext)) + decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) +} + +func decryptAndCompare(t testing.TB, k *crypto.Key, data, ciphertext, nonce, dst []byte) { + prefix := make([]byte, len(dst)) + copy(prefix, dst) + + plaintext, err := k.Open(dst, nonce, ciphertext, nil) + if err != nil { + t.Fatalf("unable to decrypt ciphertext: %v", err) + } + + if len(data)+len(prefix) != len(plaintext) { + t.Fatalf("wrong plaintext returned, want %d bytes, got %d", len(data)+len(prefix), len(plaintext)) + } + + if !bytes.Equal(plaintext[:len(prefix)], prefix) { + t.Fatal("prefix is wrong") + } + + if !bytes.Equal(plaintext[len(prefix):], data) { + t.Fatal("wrong plaintext returned") + } +} + +func TestAppendSeal(t *testing.T) { + k := crypto.NewRandomKey() + nonce := crypto.NewRandomNonce() + + data := make([]byte, 600) + _, err := io.ReadFull(rand.Reader, data) + rtest.OK(t, err) + ciphertext := encrypt(t, k, data, nil, nonce) + + // we need to test several different cases: + // * destination slice is nil + // * destination slice is empty and has enough capacity + // * destination slice is empty and does not have enough capacity + // * destination slice contains data and has enough capacity + // * destination slice contains data and does not have enough capacity + + // destination slice is nil + t.Run("nil", func(t *testing.T) { + var plaintext []byte + decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) + }) + + // destination slice is empty and has enough capacity + t.Run("empty-large", func(t *testing.T) { + plaintext := make([]byte, 0, len(data)+100) + decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) + }) + + // destination slice is empty and does not have enough capacity + t.Run("empty-small", func(t *testing.T) { + plaintext := make([]byte, 0, len(data)/2) + decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) + }) + + // destination slice contains data and has enough capacity + t.Run("prefix-large", func(t *testing.T) { + plaintext := make([]byte, 0, len(data)+100) + plaintext = append(plaintext, []byte("foobar")...) + decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) + }) + + // destination slice contains data and does not have enough capacity + t.Run("prefix-small", func(t *testing.T) { + plaintext := make([]byte, 0, len(data)/2) + plaintext = append(plaintext, []byte("foobar")...) + decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) + }) +} + +func TestAppendOpen(t *testing.T) { k := crypto.NewRandomKey() - // nil plaintext should encrypt to the empty string - // nil ciphertext should allocate a new slice for the ciphertext - c, err := k.Encrypt(nil, nil) + data := make([]byte, 600) + _, err := io.ReadFull(rand.Reader, data) rtest.OK(t, err) - rtest.Assert(t, len(c) == crypto.Extension, - "wrong length returned for ciphertext, expected 0, got %d", - len(c)) + // we need to test several different cases: + // * destination slice is nil + // * destination slice is empty and has enough capacity + // * destination slice is empty and does not have enough capacity + // * destination slice contains data and has enough capacity + // * destination slice contains data and does not have enough capacity - // this should decrypt to nil - n, err := k.Decrypt(nil, c) - rtest.OK(t, err) - rtest.Equals(t, 0, n) + // destination slice is nil + t.Run("nil", func(t *testing.T) { + nonce := crypto.NewRandomNonce() + var ciphertext []byte - // test encryption for same slice, this should return an error - _, err = k.Encrypt(c, c) - rtest.Equals(t, crypto.ErrInvalidCiphertext, err) + ciphertext = encrypt(t, k, data, ciphertext, nonce) + decryptNewSliceAndCompare(t, k, data, ciphertext, nonce) + }) + + // destination slice is empty and has enough capacity + t.Run("empty-large", func(t *testing.T) { + nonce := crypto.NewRandomNonce() + ciphertext := make([]byte, 0, len(data)+100) + + ciphertext = encrypt(t, k, data, ciphertext, nonce) + decryptNewSliceAndCompare(t, k, data, ciphertext, nonce) + }) + + // destination slice is empty and does not have enough capacity + t.Run("empty-small", func(t *testing.T) { + nonce := crypto.NewRandomNonce() + ciphertext := make([]byte, 0, len(data)/2) + + ciphertext = encrypt(t, k, data, ciphertext, nonce) + decryptNewSliceAndCompare(t, k, data, ciphertext, nonce) + }) + + // destination slice contains data and has enough capacity + t.Run("prefix-large", func(t *testing.T) { + nonce := crypto.NewRandomNonce() + ciphertext := make([]byte, 0, len(data)+100) + ciphertext = append(ciphertext, []byte("foobar")...) + + ciphertext = encrypt(t, k, data, ciphertext, nonce) + if string(ciphertext[:6]) != "foobar" { + t.Errorf("prefix is missing") + } + decryptNewSliceAndCompare(t, k, data, ciphertext[6:], nonce) + }) + + // destination slice contains data and does not have enough capacity + t.Run("prefix-small", func(t *testing.T) { + nonce := crypto.NewRandomNonce() + ciphertext := make([]byte, 0, len(data)/2) + ciphertext = append(ciphertext, []byte("foobar")...) + + ciphertext = encrypt(t, k, data, ciphertext, nonce) + if string(ciphertext[:6]) != "foobar" { + t.Errorf("prefix is missing") + } + decryptNewSliceAndCompare(t, k, data, ciphertext[6:], nonce) + }) } func TestLargeEncrypt(t *testing.T) { @@ -123,10 +254,9 @@ func TestLargeEncrypt(t *testing.T) { _, err := io.ReadFull(rand.Reader, data) rtest.OK(t, err) - ciphertext, err := k.Encrypt(make([]byte, size+crypto.Extension), data) - rtest.OK(t, err) - - plaintext, err := k.Decrypt([]byte{}, ciphertext) + nonce := crypto.NewRandomNonce() + ciphertext := k.Seal(make([]byte, size+k.Overhead()), nonce, data, nil) + plaintext, err := k.Open([]byte{}, nonce, ciphertext, nil) rtest.OK(t, err) rtest.Equals(t, plaintext, data) @@ -139,13 +269,13 @@ func BenchmarkEncrypt(b *testing.B) { k := crypto.NewRandomKey() buf := make([]byte, len(data)+crypto.Extension) + nonce := crypto.NewRandomNonce() b.ResetTimer() b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { - _, err := k.Encrypt(buf, data) - rtest.OK(b, err) + _ = k.Seal(buf, nonce, data, nil) } } @@ -155,17 +285,18 @@ func BenchmarkDecrypt(b *testing.B) { k := crypto.NewRandomKey() - plaintext := make([]byte, size) - ciphertext := make([]byte, size+crypto.Extension) + plaintext := make([]byte, 0, size) + ciphertext := make([]byte, 0, size+crypto.Extension) + nonce := crypto.NewRandomNonce() + ciphertext = k.Seal(ciphertext, nonce, data, nil) - ciphertext, err := k.Encrypt(ciphertext, data) - rtest.OK(b, err) + var err error b.ResetTimer() b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { - _, err = k.Decrypt(plaintext, ciphertext) + _, err = k.Open(plaintext, nonce, ciphertext, nil) rtest.OK(b, err) } }