From bace9d8be3bac2ace0322d1660d5ece1aaeaa8d7 Mon Sep 17 00:00:00 2001 From: D3v Date: Sun, 7 Jan 2024 03:10:58 +0100 Subject: [PATCH] Remove AES limitations, symmetric coverage bump --- insecure/asymmetric/asymmetric_test.go | 4 + insecure/symmetric/symmetric.go | 29 +- insecure/symmetric/symmetric_test.go | 219 ++++++++--- symmetric/symmetric.go | 30 +- symmetric/symmetric_test.go | 511 ++++++++++++++++++------- 5 files changed, 568 insertions(+), 225 deletions(-) diff --git a/insecure/asymmetric/asymmetric_test.go b/insecure/asymmetric/asymmetric_test.go index 0003b42..c1b7a9e 100644 --- a/insecure/asymmetric/asymmetric_test.go +++ b/insecure/asymmetric/asymmetric_test.go @@ -40,4 +40,8 @@ func TestE2EEEncryptDecryptBox(t *testing.T) { r.Equal(t, expectedPlaintext, plaintext) t.Log("Ciphertext:", ciphertext) + + expectedPlaintextError, err := asymmetric.DecryptBox(sender.PK, recipient.SK, make([]byte, 32)) + r.Nil(t, expectedPlaintextError) + r.ErrorContains(t, err, "decryption error") } diff --git a/insecure/symmetric/symmetric.go b/insecure/symmetric/symmetric.go index 907b06b..8c4d59a 100644 --- a/insecure/symmetric/symmetric.go +++ b/insecure/symmetric/symmetric.go @@ -42,10 +42,13 @@ func (s *SecretBox) Decrypt(key, payload []byte) ([]byte, error) { if len(key) != 32 { return nil, errors.New("invalid key size") } - var secretKey [32]byte subtle.ConstantTimeCopy(1, secretKey[:], key) + if len(payload) < 24 { + return nil, errors.New("payload is too short") + } + var nonce [24]byte subtle.ConstantTimeCopy(1, nonce[:], payload[:24]) @@ -60,10 +63,6 @@ func (s *SecretBox) Decrypt(key, payload []byte) ([]byte, error) { type AesCTR struct{} func (a *AesCTR) Encrypt(key, payload []byte) ([]byte, error) { - if len(key) != 32 { - return nil, errors.New("invalid key size") - } - if generic.AllZero(key) { return nil, errors.New("key is all zero") } @@ -79,10 +78,6 @@ func (a *AesCTR) Encrypt(key, payload []byte) ([]byte, error) { return nil, err } - if generic.AllZero(iv) { - return nil, errors.New("iv is all zero") - } - stream := cipher.NewCTR(block, iv) stream.XORKeyStream(ciphertext[aes.BlockSize:], payload) @@ -90,10 +85,6 @@ func (a *AesCTR) Encrypt(key, payload []byte) ([]byte, error) { } func (a *AesCTR) Decrypt(key, payload []byte) ([]byte, error) { - if len(key) != 32 { - return nil, errors.New("invalid key size") - } - if generic.AllZero(key) { return nil, errors.New("key is all zero") } @@ -117,10 +108,6 @@ func (a *AesCTR) Decrypt(key, payload []byte) ([]byte, error) { type AesCBC struct{} func (a *AesCBC) Encrypt(key, payload []byte) ([]byte, error) { - if len(key) != 32 { - return nil, errors.New("invalid key size") - } - if generic.AllZero(key) { return nil, errors.New("key is all zero") } @@ -140,20 +127,12 @@ func (a *AesCBC) Encrypt(key, payload []byte) ([]byte, error) { return nil, err } - if generic.AllZero(iv) { - return nil, errors.New("iv is all zero") - } - mode := cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(ciphertext[aes.BlockSize:], payload) return ciphertext, nil } func (a *AesCBC) Decrypt(key, payload []byte) ([]byte, error) { - if len(key) != 32 { - return nil, errors.New("invalid key size") - } - if generic.AllZero(key) { return nil, errors.New("key is all zero") } diff --git a/insecure/symmetric/symmetric_test.go b/insecure/symmetric/symmetric_test.go index 536bca8..43ac79d 100644 --- a/insecure/symmetric/symmetric_test.go +++ b/insecure/symmetric/symmetric_test.go @@ -8,7 +8,7 @@ import ( r "github.com/stretchr/testify/require" ) -func TestSymmetricEncryption(t *testing.T) { +func TestE2E(t *testing.T) { testCases := []struct { name string sym symmetric.Symmetric @@ -48,71 +48,190 @@ func TestSymmetricEncryption(t *testing.T) { } } -func TestAesCBCKeyFault(t *testing.T) { - testCases := []struct { +func TestE2EFault(t *testing.T) { + + type cases struct { + name string key []byte payload []byte expectedErr string + } + + type testStructue struct { + name string sym symmetric.Symmetric - }{ + encrypt []cases + decrypt []cases + } + + testCases := []testStructue{ { - key: generateKey(t, 31), - payload: make([]byte, 16), - expectedErr: "invalid key size", + name: "NaClSecretBox", sym: &symmetric.SecretBox{}, + encrypt: []cases{ + { + name: "key size 31, invalid key size", + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + }, + decrypt: []cases{ + { + name: "key size 31, invalid key size", + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "non-decryptable payload, decryption error", + key: make([]byte, 32), + payload: make([]byte, 32), + expectedErr: "decryption error", + }, + { + name: "invalid payload,payload is too short", + key: generateKey(t, 32), + payload: make([]byte, 16), + expectedErr: "payload is too short", + }, + }, }, { - key: generateKey(t, 31), - payload: make([]byte, 16), - expectedErr: "invalid key size", - sym: &symmetric.AesCTR{}, - }, - { - key: generateKey(t, 33), - payload: make([]byte, 16), - expectedErr: "invalid key size", - sym: &symmetric.AesCTR{}, - }, - { - key: make([]byte, 32), - payload: make([]byte, 16), - expectedErr: "key is all zero", + name: "AES-CTR", sym: &symmetric.AesCTR{}, + encrypt: []cases{ + { + name: "key size 31, invalid key size", + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key size 33, invalid key size", + key: generateKey(t, 33), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key is all zero", + key: make([]byte, 32), + payload: make([]byte, 16), + expectedErr: "key is all zero", + }, + }, + decrypt: []cases{ + { + name: "key size 31, invalid key size", + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key size 33, invalid key size", + key: generateKey(t, 33), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key is all zero", + key: make([]byte, 32), + payload: make([]byte, 16), + expectedErr: "key is all zero", + }, + { + name: "invalid payload, payload is too short", + key: generateKey(t, 32), + payload: make([]byte, 15), + expectedErr: "payload is too short", + }, + }, }, { - key: generateKey(t, 31), - payload: make([]byte, 16), - expectedErr: "invalid key size", - sym: &symmetric.AesCBC{}, - }, - { - key: generateKey(t, 33), - payload: make([]byte, 16), - expectedErr: "invalid key size", - sym: &symmetric.AesCBC{}, - }, - { - key: make([]byte, 32), - payload: make([]byte, 16), - expectedErr: "key is all zero", - sym: &symmetric.AesCBC{}, - }, - { - key: generateKey(t, 32), - payload: make([]byte, 14), - expectedErr: "payload is not a multiple of the block size", + name: "AES-CBC", sym: &symmetric.AesCBC{}, + encrypt: []cases{ + { + name: "key size 31, invalid key size", + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key size 33, invalid key size", + key: generateKey(t, 33), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key size 15, invalid key size", + key: generateKey(t, 15), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key is all zero", + key: make([]byte, 32), + payload: make([]byte, 16), + expectedErr: "key is all zero", + }, + { + name: "payload is not a multiple of the block size", + key: generateKey(t, 32), + payload: make([]byte, 14), + expectedErr: "payload is not a multiple of the block size", + }, + }, + decrypt: []cases{ + { + name: "key size 31, invalid key size", + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key size 33, invalid key size", + key: generateKey(t, 33), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key size 15, invalid key size", + key: generateKey(t, 15), + payload: make([]byte, 16), + expectedErr: "invalid key size", + }, + { + name: "key is all zero", + key: make([]byte, 32), + payload: make([]byte, 16), + expectedErr: "key is all zero", + }, + { + name: "payload is not a multiple of the block size", + key: generateKey(t, 32), + payload: make([]byte, 14), + expectedErr: "payload is not a multiple of the block size", + }, + }, }, } for _, tc := range testCases { - ciphertext, err := tc.sym.Encrypt(tc.key, tc.payload) - r.Nil(t, ciphertext) - r.ErrorContains(t, err, tc.expectedErr) - - plaintext, err := tc.sym.Decrypt(tc.key, ciphertext) - r.Nil(t, plaintext) - r.ErrorContains(t, err, tc.expectedErr) + for _, c := range tc.encrypt { + t.Run(tc.name+ "/encryption/" + c.name, func(t *testing.T) { + ciphertext, err := tc.sym.Encrypt(c.key, c.payload) + r.Nil(t, ciphertext) + r.ErrorContains(t, err, c.expectedErr) + }) + } + for _, c := range tc.decrypt { + t.Run(tc.name+ "/decryption/" + c.name, func(t *testing.T) { + plaintext, err := tc.sym.Decrypt(c.key, c.payload) + r.Nil(t, plaintext) + r.ErrorContains(t, err, c.expectedErr) + }) + } } } diff --git a/symmetric/symmetric.go b/symmetric/symmetric.go index 2f75ebd..b3eb479 100644 --- a/symmetric/symmetric.go +++ b/symmetric/symmetric.go @@ -41,8 +41,8 @@ type XChaCha20Stream struct { // XChaCha20-Poly1305 func (x *XChaCha20) Encrypt(key, plaintext []byte) ([]byte, error) { - if len(key) != chacha20poly1305.KeySize { - return nil, errors.New("wrong key size") + if generic.AllZero(key) { + return nil, errors.New("key is all zero") } aead, err := chacha20poly1305.NewX(key) @@ -64,8 +64,8 @@ func (x *XChaCha20) Encrypt(key, plaintext []byte) ([]byte, error) { } func (x *XChaCha20) Decrypt(key, ciphertext []byte) ([]byte, error) { - if len(key) != chacha20poly1305.KeySize { - return nil, errors.New("wrong secret size") + if generic.AllZero(key) { + return nil, errors.New("key is all zero") } aead, err := chacha20poly1305.NewX(key) @@ -103,10 +103,6 @@ func (x *Xor) Encrypt(key, payload []byte) ([]byte, error) { xored := make([]byte, len(payload)) subtle.XORBytes(xored, payload, key) - if len(payload) != len(xored) || len(key) != len(xored) { - return nil, errors.New("xored array length mismatch") - } - if generic.AllZero(xored) { return nil, errors.New("xored array has just zeroes") } @@ -120,6 +116,10 @@ func (x *Xor) Decrypt(key, payload []byte) ([]byte, error) { // XChaCha20-Poly1305 Age Stream func (x *XChaCha20Stream) Encrypt(in io.Reader, out io.Writer) error { + if generic.AllZero(x.Key) { + return errors.New("key is all zero") + } + if len(x.Key) != chacha20poly1305.KeySize { return errors.New("wrong key size") } @@ -147,6 +147,10 @@ func (x *XChaCha20Stream) Encrypt(in io.Reader, out io.Writer) error { } func (x *XChaCha20Stream) Decrypt(in io.Reader, out io.Writer) error { + if generic.AllZero(x.Key) { + return errors.New("key is all zero") + } + if len(x.Key) != chacha20poly1305.KeySize { return errors.New("wrong key size") } @@ -178,7 +182,7 @@ type stream struct { func (s *stream) reader(src io.Reader, key []byte) (io.Reader, error) { nonce := make([]byte, chacha20poly1305.NonceSizeX) if _, err := io.ReadFull(src, nonce); err != nil { - return nil, errors.New("failed to read nonce") + return nil, err } streamerKey, err := s.key(key, nonce) @@ -228,10 +232,6 @@ func (a *AesGCM) Encrypt(key, payload []byte) ([]byte, error) { return nil, errors.New("key is all zero") } - if len(key) != 32 { - return nil, errors.New("wrong key size, must be 32 bytes") - } - aes, err := aes.NewCipher(key) if err != nil { return nil, err @@ -259,10 +259,6 @@ func (a *AesGCM) Decrypt(key, ciphertext []byte) ([]byte, error) { return nil, errors.New("key is all zero") } - if len(key) != 32 { - return nil, errors.New("wrong key size, must be 32 bytes") - } - aes, err := aes.NewCipher(key) if err != nil { return nil, err diff --git a/symmetric/symmetric_test.go b/symmetric/symmetric_test.go index fa5812f..13aaa5f 100644 --- a/symmetric/symmetric_test.go +++ b/symmetric/symmetric_test.go @@ -2,174 +2,419 @@ package symmetric_test import ( "bytes" - "encoding/hex" + "crypto/sha256" "testing" "github.com/D3vl0per/crypt/generic" "github.com/D3vl0per/crypt/symmetric" r "github.com/stretchr/testify/require" - "golang.org/x/crypto/chacha20poly1305" ) -func TestStreamXChaCha20(t *testing.T) { - plainText := []byte("https://xkcd.com/936/") - out := &bytes.Buffer{} - in := bytes.NewReader(plainText) - - key, err := generic.CSPRNG(chacha20poly1305.KeySize) - r.NoError(t, err) - - sym := symmetric.XChaCha20Stream{ - Key: key, +func TestE2EE(t *testing.T) { + testCases := []struct { + name string + sym symmetric.Symmetric + key []byte + payload []byte + }{ + { + name: "XOR", + sym: &symmetric.Xor{}, + key: generateKey(t, 21), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "XChaCha20", + sym: &symmetric.XChaCha20{}, + key: generateKey(t, 32), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "XChaCha20-AAD", + sym: &symmetric.XChaCha20{ + AdditionalData: []byte("AAD"), + }, + key: generateKey(t, 32), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AES-GCM-128", + sym: &symmetric.AesGCM{}, + key: generateKey(t, 16), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AES-GCM-128-AAD", + sym: &symmetric.AesGCM{ + AdditionalData: []byte("AAD"), + }, + key: generateKey(t, 16), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AES-GCM-192", + sym: &symmetric.AesGCM{}, + key: generateKey(t, 24), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AES-GCM-192-AAD", + sym: &symmetric.AesGCM{ + AdditionalData: []byte("AAD"), + }, + key: generateKey(t, 24), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AES-GCM-256", + sym: &symmetric.AesGCM{}, + key: generateKey(t, 32), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AES-GCM-256", + sym: &symmetric.AesGCM{ + AdditionalData: []byte("AAD"), + }, + key: generateKey(t, 32), + payload: []byte("https://xkcd.com/936/"), + }, } + hex := generic.Hex{} + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Logf("Key: %s", hex.Encode(tc.key)) + ciphertext, err := tc.sym.Encrypt(tc.key, tc.payload) + r.NoError(t, err) - err = sym.Encrypt(in, out) - r.NoError(t, err) + t.Logf("Encrypted file size: %d\n", len(ciphertext)) + t.Logf("Encrypted value: %s", hex.Encode(ciphertext)) - t.Logf("Key: %s", hex.EncodeToString(key)) - t.Logf("Encrypted file size: %d\n", out.Len()) - t.Logf("Encrypted value: %s", hex.EncodeToString(out.Bytes())) + plaintext, err := tc.sym.Decrypt(tc.key, ciphertext) + r.NoError(t, err) - rr := bytes.NewReader(out.Bytes()) - out2 := &bytes.Buffer{} + t.Logf("Decrypted file size: %d\n", len(plaintext)) + t.Logf("Decrypted value: %s", string(plaintext)) - sym2 := symmetric.XChaCha20Stream{ - Key: key, + r.Equal(t, tc.payload, plaintext) + }) } - r.NoError(t, sym2.Decrypt(rr, out2)) - - t.Logf("Decrypted file size: %d\n", out2.Len()) - t.Logf("Decrypted value: %s", out2.String()) - r.Equal(t, out2.Bytes(), plainText) -} - -func TestXChaCha20(t *testing.T) { - secret, err := hex.DecodeString("aec26928fb2a0177806eb76b22a116b2d3d2471ac44ffe9f27aee1da3839eff1") - r.NoError(t, err) - payload := []byte("https://xkcd.com/936/") - - sym := symmetric.XChaCha20{} - - ciphertext, err := sym.Encrypt(secret, payload) - r.NoError(t, err) - - sym2 := symmetric.XChaCha20{} - - plaintext, err := sym2.Decrypt(secret, ciphertext) - r.NoError(t, err) - - r.Equal(t, payload, plaintext) - - // AAD test - aad := []byte("proposal") - - sym3 := symmetric.XChaCha20{ - AdditionalData: aad, + testCasesStream := []struct { + name string + sym symmetric.SymmetricStream + payload []byte + }{ + { + name: "XChaCha20-Stream", + sym: &symmetric.XChaCha20Stream{ + Key: generateKey(t, 32), + }, + payload: generateKey(t, 32), + }, + { + name: "XChaCha20-Stream", + sym: &symmetric.XChaCha20Stream{ + Key: generateKey(t, 32), + Hash: sha256.New, + }, + payload: generateKey(t, 32), + }, } - ciphertext2, err := sym3.Encrypt(secret, payload) - r.NoError(t, err) - t.Log("Ciphertext AAD (hex): ", hex.EncodeToString(ciphertext2)) - t.Log("AAD (hex): ", hex.EncodeToString(aad)) + for _, tc := range testCasesStream { + t.Run(tc.name, func(t *testing.T) { + out := &bytes.Buffer{} + in := bytes.NewReader(tc.payload) - sym4 := symmetric.XChaCha20{ - AdditionalData: aad, - } + err := tc.sym.Encrypt(in, out) + r.NoError(t, err) - plaintext2, err := sym4.Decrypt(secret, ciphertext2) - r.NoError(t, err) + rr := bytes.NewReader(out.Bytes()) + out2 := &bytes.Buffer{} - r.Equal(t, payload, plaintext2) -} - -func TestXOR(t *testing.T) { - a := []byte{0x0f, 0x1a, 0x2b, 0x3c} - b := []byte{0x2a, 0x1b, 0x0c, 0x3d} + r.NoError(t, tc.sym.Decrypt(rr, out2)) - sym := symmetric.Xor{} - expected := []byte{0x25, 0x01, 0x27, 0x01} - result, err := sym.Encrypt(a, b) - r.NoError(t, err) + r.Equal(t, out2.Bytes(), tc.payload) + }) + } - r.Equal(t, expected, result) } -func TestAESGCM(t *testing.T) { - secret, err := generic.CSPRNG(32) - r.NoError(t, err) - payload := []byte("https://xkcd.com/936/") - - sym := symmetric.AesGCM{} - - ciphertext, err := sym.Encrypt(secret, payload) - r.NoError(t, err) - t.Log("Ciphertext (hex): ", hex.EncodeToString(ciphertext)) - - sym2 := symmetric.AesGCM{} +func TestE2EEFault(t *testing.T) { + type cases struct { + name string + key []byte + payload []byte + expectedErr string + } - plaintext, err := sym2.Decrypt(secret, ciphertext) - r.NoError(t, err) + type testStructue struct { + name string + sym symmetric.Symmetric + encrypt []cases + decrypt []cases + } - r.Equal(t, payload, plaintext) + type casesStream struct { + sym symmetric.SymmetricStream + name string + payload []byte + expectedErr string + } - // AAD test - aad := []byte("proposal") + type testStructueStream struct { + name string + encrypt []casesStream + decrypt []casesStream + } - sym3 := symmetric.AesGCM{ - AdditionalData: aad, + testCases := []testStructue{ + { + name: "XOR", + sym: &symmetric.Xor{}, + encrypt: []cases{ + { + name: "key and payload size mismatch", + key: make([]byte, 32), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "insecure xor operation, key and payload length need to be equal", + }, + { + name: "zero ciphertext", + key: make([]byte, 32), + payload: make([]byte, 32), + expectedErr: "xored array has just zeroes", + }, + }, + }, + { + name: "AES-GCM", + sym: &symmetric.AesGCM{}, + encrypt: []cases{ + { + name: "zero key", + key: make([]byte, 32), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "key is all zero", + }, + { + name: "key size 15, invalid key size", + key: generateKey(t, 15), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "invalid key size", + }, + { + name: "key size 23, invalid key size", + key: generateKey(t, 23), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "invalid key size", + }, + { + name: "key size 33, invalid key size", + key: generateKey(t, 33), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "invalid key size", + }, + }, + decrypt: []cases{ + { + name: "zero key", + key: make([]byte, 32), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "key is all zero", + }, + { + name: "key size 15, invalid key size", + key: generateKey(t, 15), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "invalid key size", + }, + { + name: "key size 23, invalid key size", + key: generateKey(t, 23), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "invalid key size", + }, + { + name: "key size 33, invalid key size", + key: generateKey(t, 33), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "invalid key size", + }, + { + name: "wrong ciphertext size", + key: generateKey(t, 32), + payload: generateKey(t, 11), + expectedErr: "ciphertext too short", + }, + { + name: "non-decryptable payload", + key: generateKey(t, 32), + payload: generateKey(t, 32), + expectedErr: "cipher: message authentication failed", + }, + }, + }, + { + name: "AES-GCM-AAD", + sym: &symmetric.AesGCM{ + AdditionalData: []byte("AAD"), + }, + decrypt: []cases{ + { + name: "non-decryptable payload", + key: generateKey(t, 32), + payload: generateKey(t, 32), + expectedErr: "cipher: message authentication failed", + }, + }, + }, + { + name: "XChaCha20", + sym: &symmetric.XChaCha20{}, + encrypt: []cases{ + { + name: "zero key", + key: make([]byte, 32), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "key is all zero", + }, + { + name: "wrong key size", + key: generateKey(t, 33), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "chacha20poly1305: bad key length", + }, + }, + decrypt: []cases{ + { + name: "zero key", + key: make([]byte, 32), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "key is all zero", + }, + { + name: "wrong key size", + key: generateKey(t, 33), + payload: []byte("https://xkcd.com/936/"), + expectedErr: "chacha20poly1305: bad key length", + }, + { + name: "wrong ciphertext size", + key: generateKey(t, 32), + payload: generateKey(t, 23), + expectedErr: "ciphertext too short", + }, + { + name: "non-decryptable payload", + key: generateKey(t, 32), + payload: generateKey(t, 32), + expectedErr: "chacha20poly1305: message authentication failed", + }, + }, + }, + { + name: "XChaCha20-AAD", + sym: &symmetric.XChaCha20{ + AdditionalData: []byte("AAD"), + }, + decrypt: []cases{ + { + name: "non-decryptable payload", + key: generateKey(t, 32), + payload: generateKey(t, 32), + expectedErr: "chacha20poly1305: message authentication failed", + }, + }, + }, } - ciphertext2, err := sym3.Encrypt(secret, payload) - r.NoError(t, err) - t.Log("Ciphertext AAD (hex): ", hex.EncodeToString(ciphertext2)) - t.Log("AAD (hex): ", hex.EncodeToString(aad)) + testCasesStream := []testStructueStream{ + { + name: "XChaCha20-Stream", + encrypt: []casesStream{ + { + name: "zero key", + sym: &symmetric.XChaCha20Stream{ + Key: make([]byte, 32), + }, + payload: []byte("https://xkcd.com/936/"), + expectedErr: "key is all zero", + }, + { + name: "wrong key size", + sym: &symmetric.XChaCha20Stream{ + Key: generateKey(t, 31), + }, + payload: []byte("https://xkcd.com/936/"), + expectedErr: "wrong key size", + }, + }, + decrypt: []casesStream{ + { + name: "zero key", + sym: &symmetric.XChaCha20Stream{ + Key: make([]byte, 32), + }, + payload: []byte("https://xkcd.com/936/"), + expectedErr: "key is all zero", + }, + { + name: "wrong key size", + sym: &symmetric.XChaCha20Stream{ + Key: generateKey(t, 31), + }, + payload: []byte("https://xkcd.com/936/"), + expectedErr: "wrong key size", + }, + }, + }, + } - sym4 := symmetric.AesGCM{ - AdditionalData: aad, + for _, tc := range testCases { + for _, c := range tc.encrypt { + t.Run(tc.name+"/encryption/"+c.name, func(t *testing.T) { + ciphertext, err := tc.sym.Encrypt(c.key, c.payload) + r.Nil(t, ciphertext) + r.ErrorContains(t, err, c.expectedErr) + }) + } + for _, c := range tc.decrypt { + t.Run(tc.name+"/decryption/"+c.name, func(t *testing.T) { + plaintext, err := tc.sym.Decrypt(c.key, c.payload) + r.Nil(t, plaintext) + r.ErrorContains(t, err, c.expectedErr) + }) + } } - plaintext2, err := sym4.Decrypt(secret, ciphertext2) - r.NoError(t, err) + for _, tc := range testCasesStream { + for _, c := range tc.encrypt { + t.Run(tc.name+"/encryption/"+c.name, func(t *testing.T) { + out := &bytes.Buffer{} + in := bytes.NewReader(c.payload) + + err := c.sym.Encrypt(in, out) + r.ErrorContains(t, err, c.expectedErr) + }) + } + for _, c := range tc.decrypt { + t.Run(tc.name+"/decryption/"+c.name, func(t *testing.T) { + out := &bytes.Buffer{} + in := bytes.NewReader(c.payload) + + err := c.sym.Decrypt(in, out) + r.ErrorContains(t, err, c.expectedErr) + }) + } + } - r.Equal(t, payload, plaintext2) } -func TestAESGCMFails(t *testing.T) { - secret, err := generic.CSPRNG(32) +func generateKey(t *testing.T, size int) []byte { + key, err := generic.CSPRNG(int64(size)) r.NoError(t, err) - - payload := []byte("https://xkcd.com/936/") - - sym := symmetric.AesGCM{} - - zeroKey := make([]byte, 32) - zeroCiphertext, err := sym.Encrypt(zeroKey, payload) - r.Error(t, err) - r.Empty(t, zeroCiphertext) - r.EqualError(t, err, "key is all zero") - - invalidKey := []byte("0123456789abcdef") - invalidCiphertext, err := sym.Encrypt(invalidKey, payload) - r.Error(t, err) - r.Empty(t, invalidCiphertext) - r.EqualError(t, err, "wrong key size, must be 32 bytes") - - zeroPlaintext, err := sym.Decrypt(zeroKey, zeroCiphertext) - r.Error(t, err) - r.Empty(t, zeroPlaintext) - r.EqualError(t, err, "key is all zero") - - invalidCiphertext2 := []byte("ciphertext") - invalidPlaintext, err := sym.Decrypt(invalidKey, invalidCiphertext2) - r.Error(t, err) - r.Empty(t, invalidPlaintext) - r.EqualError(t, err, "wrong key size, must be 32 bytes") - - shortCiphertext := []byte("short") - shortPlaintext, err := sym.Decrypt(secret, shortCiphertext) - r.Error(t, err) - r.Empty(t, shortPlaintext) - r.EqualError(t, err, "ciphertext too short") + return key }