diff --git a/agent/bitwarden/crypto/crypto.go b/agent/bitwarden/crypto/crypto.go index fb850da..dbae73a 100644 --- a/agent/bitwarden/crypto/crypto.go +++ b/agent/bitwarden/crypto/crypto.go @@ -9,6 +9,7 @@ import ( "crypto/sha256" "crypto/x509" "encoding/base64" + "errors" "fmt" "io" "math" @@ -170,7 +171,7 @@ func isMacValid(message, messageMAC, key []byte) bool { return hmac.Equal(messageMAC, expectedMAC) } -func encryptAESCBC256(data, key []byte) (iv, ciphertext []byte, _ error) { +func encryptAESCBC256(data, key []byte) (iv, ciphertext []byte, err error) { data = padPKCS7(data, aes.BlockSize) block, err := aes.NewCipher(key) if err != nil { @@ -182,12 +183,28 @@ func encryptAESCBC256(data, key []byte) (iv, ciphertext []byte, _ error) { if _, err := io.ReadFull(cryptorand.Reader, iv); err != nil { return nil, nil, err } + + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + iv = nil + ciphertext = nil + err = errors.New("error encrypting AES CBC 256 data") + } + } + }() + mode := cipher.NewCBCEncrypter(block, iv) mode.CryptBlocks(ciphertext, data) return iv, ciphertext, nil } -func decryptAESCBC256(iv, ciphertext, key []byte) ([]byte, error) { +func decryptAESCBC256(iv, ciphertext, key []byte) (decryptedData []byte, err error) { + ciphertextCopy := make([]byte, len(ciphertext)) + copy(ciphertextCopy, ciphertext) + block, err := aes.NewCipher(key) if err != nil { return nil, err @@ -195,28 +212,50 @@ func decryptAESCBC256(iv, ciphertext, key []byte) ([]byte, error) { if len(iv) != aes.BlockSize { return nil, fmt.Errorf("iv length does not match AES block size") } - if len(ciphertext)%aes.BlockSize != 0 { + if len(ciphertextCopy)%aes.BlockSize != 0 { return nil, fmt.Errorf("ciphertext is not a multiple of AES block size") } + + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + decryptedData = nil + err = errors.New("error decrypting AES CBC 256 data") + } + } + }() + mode := cipher.NewCBCDecrypter(block, iv) - mode.CryptBlocks(ciphertext, ciphertext) // decrypt in-place - data, err := unpadPKCS7(ciphertext, aes.BlockSize) + mode.CryptBlocks(ciphertextCopy, ciphertextCopy) // decrypt in-place + data, err := unpadPKCS7(ciphertextCopy, aes.BlockSize) if err != nil { return nil, err } - return data, nil + + resultBuffer := make([]byte, len(data)) + copy(resultBuffer, data) + return resultBuffer, nil } func unpadPKCS7(src []byte, size int) ([]byte, error) { - n := src[len(src)-1] - if len(src)%size != 0 { - return nil, fmt.Errorf("expected PKCS7 padding for block size %d, but have %d bytes", size, len(src)) + srcCopy := make([]byte, len(src)) + copy(srcCopy, src) + + n := srcCopy[len(srcCopy)-1] + if len(srcCopy)%size != 0 { + return nil, fmt.Errorf("expected PKCS7 padding for block size %d, but have %d bytes", size, len(srcCopy)) } - if len(src) <= int(n) { - return nil, fmt.Errorf("cannot unpad %d bytes out of a total of %d", n, len(src)) + if len(srcCopy) <= int(n) { + return nil, fmt.Errorf("cannot unpad %d bytes out of a total of %d", n, len(srcCopy)) } - src = src[:len(src)-int(n)] - return src, nil + srcCopy = srcCopy[:len(srcCopy)-int(n)] + + resultCopy := make([]byte, len(srcCopy)) + copy(resultCopy, srcCopy) + + return resultCopy, nil } func padPKCS7(src []byte, size int) []byte { diff --git a/agent/bitwarden/crypto/encstring.go b/agent/bitwarden/crypto/encstring.go index 18d8da3..0536d7e 100644 --- a/agent/bitwarden/crypto/encstring.go +++ b/agent/bitwarden/crypto/encstring.go @@ -2,8 +2,6 @@ package crypto import ( "bytes" - "crypto/aes" - "crypto/cipher" "crypto/hmac" "crypto/rand" "crypto/rsa" @@ -12,12 +10,14 @@ import ( "crypto/x509" "errors" "fmt" - "io" "strconv" "github.com/awnumar/memguard" + "github.com/quexten/goldwarden/logging" ) +var cryptoLog = logging.GetLogger("Goldwarden", "Crypto") + type EncString struct { Type EncStringType IV, CT, MAC []byte @@ -131,11 +131,6 @@ func DecryptWith(s EncString, key SymmetricEncryptionKey) ([]byte, error) { return nil, err } - block, err := aes.NewCipher(encKeyData) - if err != nil { - return nil, err - } - switch s.Type { case AesCbc256_B64, AesCbc256_HmacSha256_B64: break @@ -161,13 +156,11 @@ func DecryptWith(s EncString, key SymmetricEncryptionKey) ([]byte, error) { return nil, fmt.Errorf("decrypt: invalid IV length, expected %d, got %d", block.BlockSize(), len(s.IV)) } - mode := cipher.NewCBCDecrypter(block, s.IV) - dst := make([]byte, len(s.CT)) - mode.CryptBlocks(dst, s.CT) - dst, err = unpadPKCS7(dst, aes.BlockSize) + dst, err := decryptAESCBC256(s.IV, s.CT, encKeyData) if err != nil { return nil, err } + return dst, nil } @@ -188,19 +181,13 @@ func EncryptWith(data []byte, encType EncStringType, key SymmetricEncryptionKey) return s, fmt.Errorf("encrypt: unsupported cipher type %q", s.Type) } s.Type = encType - data = padPKCS7(data, aes.BlockSize) - block, err := aes.NewCipher(encKeyData) + iv, ciphertext, err := encryptAESCBC256(data, encKeyData) if err != nil { return s, err } - s.IV = make([]byte, aes.BlockSize) - if _, err := io.ReadFull(rand.Reader, s.IV); err != nil { - return s, err - } - s.CT = make([]byte, len(data)) - mode := cipher.NewCBCEncrypter(block, s.IV) - mode.CryptBlocks(s.CT, data) + s.CT = ciphertext + s.IV = iv if encType == AesCbc256_HmacSha256_B64 { if len(macKeyData) == 0 {