From 4b258b5ed54f975a4a3b3d661e6f33b7cecb4889 Mon Sep 17 00:00:00 2001 From: D3v Date: Sun, 7 Jan 2024 00:30:13 +0100 Subject: [PATCH] Insecure symmetric bundle coverage bump --- aged/age_bind_test.go | 1 - asymmetric/ecdsa_test.go | 1 - compression/compression.go | 174 --------------------------- compression/compression_test.go | 9 +- insecure/symmetric/symmetric.go | 87 ++++++++------ insecure/symmetric/symmetric_test.go | 151 ++++++++++++++++------- 6 files changed, 159 insertions(+), 264 deletions(-) diff --git a/aged/age_bind_test.go b/aged/age_bind_test.go index 46b7dc8..62d3e9d 100644 --- a/aged/age_bind_test.go +++ b/aged/age_bind_test.go @@ -213,7 +213,6 @@ func TestRoundTrips(t *testing.T) { Compress: true, }, }, - } for _, tt := range tests { diff --git a/asymmetric/ecdsa_test.go b/asymmetric/ecdsa_test.go index 3867bf7..9c0dc07 100644 --- a/asymmetric/ecdsa_test.go +++ b/asymmetric/ecdsa_test.go @@ -259,4 +259,3 @@ func BenchmarkEcdsa(b *testing.B) { } }) } - diff --git a/compression/compression.go b/compression/compression.go index 395b016..6397a1f 100644 --- a/compression/compression.go +++ b/compression/compression.go @@ -388,177 +388,3 @@ func (b *Brotli) SetLevel(level int) { func (b *Brotli) GetName() string { return "br" } - -// In progress -/* -type FSE struct { - DecompressLimit int - compressScratch fse.Scratch - decompressScratch fse.Scratch -} - -func (f *FSE) Compress(in []byte) ([]byte, error) { - - f.compressScratch.Out = nil - _, err := fse.Compress(in, &f.compressScratch) - if err != nil { - return nil, err - } - - return f.compressScratch.Out, nil -} - -func (f *FSE) CompressStream(in io.Reader, out io.Writer) error { - data, err := io.ReadAll(in) - if err != nil { - return err - } - - f.compressScratch.Out = nil - _, err = fse.Compress(data, &f.compressScratch) - if err != nil { - return err - } - - _, err = out.Write(f.compressScratch.Out) - return err -} - -func (f *FSE) Decompress(in []byte) ([]byte, error) { - f.decompressScratch.Out = nil - f.decompressScratch.DecompressLimit = f.DecompressLimit - - _, err := fse.Decompress(in, &f.decompressScratch) - if err != nil { - return nil, err - } - return f.decompressScratch.Out, nil -} - -func (f *FSE) DecompressStream(in io.Reader, out io.Writer) error { - data, err := io.ReadAll(in) - if err != nil { - return err - } - - f.decompressScratch.Out = nil - f.decompressScratch.DecompressLimit = f.DecompressLimit - _, err = fse.Decompress(data, &f.decompressScratch) - if err != nil { - return err - } - - _, err = out.Write(f.decompressScratch.Out) - return err -} - -func (f *FSE) GetLevel() int { - return f.DecompressLimit -} - -func (f *FSE) SetLevel(level int) { - f.DecompressLimit = level -} - -func (f *FSE) GetName() string { - return "fse" -} - -type Huff0X1 struct { - compressScratch huff0.Scratch - decompressScratch huff0.Scratch -} - -func (h *Huff0X1) Compress(in []byte) ([]byte, error) { - h.decompressScratch.Out = nil - h.decompressScratch.OutData = nil - h.decompressScratch.OutTable = nil - h.decompressScratch.Reuse = huff0.ReusePolicyNone - - _, _, err := huff0.Compress1X(in, &h.compressScratch) - if err != nil { - return nil, err - } - - return h.compressScratch.Out, nil -} - -func (h *Huff0X1) CompressStream(in io.Reader, out io.Writer) error { - h.decompressScratch.Out = nil - h.decompressScratch.OutData = nil - h.decompressScratch.OutTable = nil - h.decompressScratch.Reuse = huff0.ReusePolicyNone - - data, err := io.ReadAll(in) - if err != nil { - return err - } - - _, _, err = huff0.Compress1X(data, &h.compressScratch) - if err != nil { - return err - } - - _, err = out.Write(h.compressScratch.Out) - return err -} - -func (h *Huff0X1) Decompress(in []byte) ([]byte, error) { - h.decompressScratch.Out = nil - h.decompressScratch.OutData = nil - h.decompressScratch.OutTable = nil - h.decompressScratch.Reuse = huff0.ReusePolicyNone - - var err error - var remain []byte - _, remain, err = huff0.ReadTable(in, &h.decompressScratch) - if err != nil { - return nil, nil - } - - out, err := h.decompressScratch.Decompress1X(remain) - if err != nil { - return nil, nil - } - - return out, nil -} - -func (h *Huff0X1) DecompressStream(in io.Reader, out io.Writer) error { - h.decompressScratch.Out = nil - h.decompressScratch.OutData = nil - h.decompressScratch.OutTable = nil - h.decompressScratch.Reuse = huff0.ReusePolicyNone - - data, err := io.ReadAll(in) - if err != nil { - return err - } - - var remain []byte - _, remain, err = huff0.ReadTable(data, &h.decompressScratch) - if err != nil { - return err - } - - raw, err := h.decompressScratch.Decompress1X(remain) - if err != nil { - return err - } - - _, err = io.Copy(out, bytes.NewReader(raw)) - return err -} - -func (h *Huff0X1) GetLevel() int { - return 0 -} - -func (h *Huff0X1) SetLevel(level int) { - -} - -func (h *Huff0X1) GetName() string { - return "huff0" -} -*/ diff --git a/compression/compression_test.go b/compression/compression_test.go index 11a0af9..f4575b6 100644 --- a/compression/compression_test.go +++ b/compression/compression_test.go @@ -18,7 +18,7 @@ type compressor struct { modes []int } -func compressionArlgorithms() ([]compressor) { +func compressionArlgorithms() []compressor { genericModes := []int{compression.BestCompression, compression.BestSpeed, compression.NoCompression, compression.DefaultCompression, compression.HuffmanOnly} zstdModes := []int{compression.ZstdSpeedBestCompression, compression.ZstdSpeedBetterCompression, compression.ZstdSpeedDefault, compression.ZstdSpeedFastest} brotliModes := []int{compression.BrotliBestCompression, compression.BrotliDefaultCompression, compression.BrotliBestSpeed} @@ -58,11 +58,10 @@ type compressionSample struct { data []byte } -func compressionSamples() ([]compressionSample) { +func compressionSamples() []compressionSample { ed25519 := asymmetric.Ed25519{} - err := ed25519.Generate() - if err != nil { + if err := ed25519.Generate(); err != nil { panic(err) } @@ -95,8 +94,6 @@ func compressionSamples() ([]compressionSample) { return testData } - - func BenchmarkRoundTrip(b *testing.B) { compressors := compressionArlgorithms() compressionSamples := compressionSamples() diff --git a/insecure/symmetric/symmetric.go b/insecure/symmetric/symmetric.go index fa39958..907b06b 100644 --- a/insecure/symmetric/symmetric.go +++ b/insecure/symmetric/symmetric.go @@ -19,13 +19,13 @@ type Symmetric interface { type SecretBox struct{} -func (s *SecretBox) Encrypt(secret, plaintext []byte) ([]byte, error) { - if len(secret) != 32 { - return nil, errors.New("wrong secret size") +func (s *SecretBox) Encrypt(key, payload []byte) ([]byte, error) { + if len(key) != 32 { + return nil, errors.New("invalid key size") } var secretKey [32]byte - subtle.ConstantTimeCopy(1, secretKey[:], secret) + subtle.ConstantTimeCopy(1, secretKey[:], key) nonce_raw, err := generic.CSPRNG(24) if err != nil { @@ -35,21 +35,21 @@ func (s *SecretBox) Encrypt(secret, plaintext []byte) ([]byte, error) { var nonce [24]byte subtle.ConstantTimeCopy(1, nonce[:], nonce_raw) - return secretbox.Seal(nonce[:], plaintext, &nonce, &secretKey), nil + return secretbox.Seal(nonce[:], payload, &nonce, &secretKey), nil } -func (s *SecretBox) Decrypt(secret, ciphertext []byte) ([]byte, error) { - if len(secret) != 32 { - return nil, errors.New("wrong secret size") +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[:], secret) + subtle.ConstantTimeCopy(1, secretKey[:], key) var nonce [24]byte - subtle.ConstantTimeCopy(1, nonce[:], ciphertext[:24]) + subtle.ConstantTimeCopy(1, nonce[:], payload[:24]) - decrypted, ok := secretbox.Open(nil, ciphertext[24:], &nonce, &secretKey) + decrypted, ok := secretbox.Open(nil, payload[24:], &nonce, &secretKey) if !ok { return nil, errors.New("decryption error") } @@ -59,7 +59,10 @@ func (s *SecretBox) Decrypt(secret, ciphertext []byte) ([]byte, error) { type AesCTR struct{} -func (a *AesCTR) Encrypt(key, data []byte) ([]byte, error) { +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") @@ -70,7 +73,7 @@ func (a *AesCTR) Encrypt(key, data []byte) ([]byte, error) { return nil, err } - ciphertext := make([]byte, aes.BlockSize+len(data)) + ciphertext := make([]byte, aes.BlockSize+len(payload)) iv := ciphertext[:aes.BlockSize] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err @@ -81,18 +84,22 @@ func (a *AesCTR) Encrypt(key, data []byte) ([]byte, error) { } stream := cipher.NewCTR(block, iv) - stream.XORKeyStream(ciphertext[aes.BlockSize:], data) + stream.XORKeyStream(ciphertext[aes.BlockSize:], payload) return ciphertext, nil } -func (a *AesCTR) Decrypt(key, data []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") } - if len(data) < aes.BlockSize { - return nil, errors.New("ciphertext too short") + if len(payload) < aes.BlockSize { + return nil, errors.New("payload is too short") } block, err := aes.NewCipher(key) @@ -100,22 +107,26 @@ func (a *AesCTR) Decrypt(key, data []byte) ([]byte, error) { return nil, err } - payload := make([]byte, len(data)-aes.BlockSize) - stream := cipher.NewCTR(block, data[:aes.BlockSize]) - stream.XORKeyStream(payload, data[aes.BlockSize:]) + plaintext := make([]byte, len(payload)-aes.BlockSize) + stream := cipher.NewCTR(block, payload[:aes.BlockSize]) + stream.XORKeyStream(plaintext, payload[aes.BlockSize:]) - return payload, nil + return plaintext, nil } type AesCBC struct{} -func (a *AesCBC) Encrypt(key, data []byte) ([]byte, error) { +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") } - if len(data)%aes.BlockSize != 0 { - return nil, errors.New("plaintext is not a multiple of the block size") + if len(payload)%aes.BlockSize != 0 { + return nil, errors.New("payload is not a multiple of the block size") } block, err := aes.NewCipher(key) @@ -123,7 +134,7 @@ func (a *AesCBC) Encrypt(key, data []byte) ([]byte, error) { return nil, err } - ciphertext := make([]byte, aes.BlockSize+len(data)) + ciphertext := make([]byte, aes.BlockSize+len(payload)) iv := ciphertext[:aes.BlockSize] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err @@ -134,21 +145,21 @@ func (a *AesCBC) Encrypt(key, data []byte) ([]byte, error) { } mode := cipher.NewCBCEncrypter(block, iv) - mode.CryptBlocks(ciphertext[aes.BlockSize:], data) + mode.CryptBlocks(ciphertext[aes.BlockSize:], payload) return ciphertext, nil } -func (a *AesCBC) Decrypt(key, data []byte) ([]byte, error) { - if generic.AllZero(key) { - return nil, errors.New("key is all zero") +func (a *AesCBC) Decrypt(key, payload []byte) ([]byte, error) { + if len(key) != 32 { + return nil, errors.New("invalid key size") } - if len(data) < aes.BlockSize { - return nil, errors.New("ciphertext too short") + if generic.AllZero(key) { + return nil, errors.New("key is all zero") } - if len(data)%aes.BlockSize != 0 { - return nil, errors.New("ciphertext is not a multiple of the block size") + if len(payload)%aes.BlockSize != 0 || len(payload) < aes.BlockSize { + return nil, errors.New("payload is not a multiple of the block size") } block, err := aes.NewCipher(key) @@ -156,12 +167,12 @@ func (a *AesCBC) Decrypt(key, data []byte) ([]byte, error) { return nil, err } - ciphertext := data[aes.BlockSize:] - iv := data[:aes.BlockSize] + ciphertext := payload[aes.BlockSize:] + iv := payload[:aes.BlockSize] - payload := make([]byte, len(data)-aes.BlockSize) + plaintext := make([]byte, len(payload)-aes.BlockSize) mode := cipher.NewCBCDecrypter(block, iv) - mode.CryptBlocks(payload, ciphertext) + mode.CryptBlocks(plaintext, ciphertext) - return payload, nil + return plaintext, nil } diff --git a/insecure/symmetric/symmetric_test.go b/insecure/symmetric/symmetric_test.go index b3f279c..536bca8 100644 --- a/insecure/symmetric/symmetric_test.go +++ b/insecure/symmetric/symmetric_test.go @@ -1,8 +1,6 @@ package symmetric_test import ( - "encoding/hex" - "testing" "github.com/D3vl0per/crypt/generic" @@ -10,51 +8,116 @@ import ( r "github.com/stretchr/testify/require" ) -func TestNaClSecretBox(t *testing.T) { - secret, err := hex.DecodeString("aec26928fb2a0177806eb76b22a116b2d3d2471ac44ffe9f27aee1da3839eff1") - r.NoError(t, err) - - payload := []byte("https://xkcd.com/936/") - - sym := symmetric.SecretBox{} - ciphertext, err := sym.Encrypt(secret, payload) - r.NoError(t, err) - - plaintext, err := sym.Decrypt(secret, ciphertext) - r.NoError(t, err) - - r.Equal(t, payload, plaintext) +func TestSymmetricEncryption(t *testing.T) { + testCases := []struct { + name string + sym symmetric.Symmetric + key []byte + payload []byte + }{ + { + name: "NaClSecretBox", + sym: &symmetric.SecretBox{}, + key: generateKey(t, 32), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AesCTR", + sym: &symmetric.AesCTR{}, + key: generateKey(t, 32), + payload: []byte("https://xkcd.com/936/"), + }, + { + name: "AesCBC", + sym: &symmetric.AesCBC{}, + key: generateKey(t, 32), + payload: []byte("exampleplaintext"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ciphertext, err := tc.sym.Encrypt(tc.key, tc.payload) + r.NoError(t, err) + + plaintext, err := tc.sym.Decrypt(tc.key, ciphertext) + r.NoError(t, err) + + r.Equal(t, tc.payload, plaintext) + }) + } } - -func TestAesCTR(t *testing.T) { - key, err := generic.CSPRNG(32) - r.NoError(t, err) - - payload := []byte("https://xkcd.com/936/") - - sym := symmetric.AesCTR{} - ciphertext, err := sym.Encrypt(key, payload) - r.NoError(t, err) - - plaintext, err := sym.Decrypt(key, ciphertext) - r.NoError(t, err) - - r.Equal(t, payload, plaintext) +func TestAesCBCKeyFault(t *testing.T) { + testCases := []struct { + key []byte + payload []byte + expectedErr string + sym symmetric.Symmetric + }{ + { + key: generateKey(t, 31), + payload: make([]byte, 16), + expectedErr: "invalid key size", + sym: &symmetric.SecretBox{}, + }, + { + 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", + sym: &symmetric.AesCTR{}, + }, + { + 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", + sym: &symmetric.AesCBC{}, + }, + } + + 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) + } } -func TestAesCBC(t *testing.T) { - key, err := generic.CSPRNG(32) +func generateKey(t *testing.T, size int) []byte { + key, err := generic.CSPRNG(int64(size)) r.NoError(t, err) - - payload := []byte("exampleplaintext") - - sym := symmetric.AesCBC{} - ciphertext, err := sym.Encrypt(key, payload) - r.NoError(t, err) - - plaintext, err := sym.Decrypt(key, ciphertext) - r.NoError(t, err) - - r.Equal(t, payload, plaintext) + return key }