diff --git a/crypto/crypto.go b/crypto/crypto.go index 258348e96..77dadb7a5 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -206,14 +206,15 @@ func (k *EncryptionKey) UnmarshalJSON(data []byte) error { // Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext || // MAC. Encrypt returns the new ciphertext slice, which is extended when -// necessary. ciphertext and plaintext may point to the same slice. +// necessary. ciphertext and plaintext may not point to (exactly) the same +// slice or non-intersecting slices. func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) { // extend ciphertext slice if necessary - if cap(ciphertext) < len(plaintext)+Extension { + ciphertext = ciphertext[:cap(ciphertext)] + if len(ciphertext) < len(plaintext)+Extension { ext := len(plaintext) + Extension - cap(ciphertext) - n := len(ciphertext) ciphertext = append(ciphertext, make([]byte, ext)...) - ciphertext = ciphertext[:n] + ciphertext = ciphertext[:cap(ciphertext)] } iv := newIV() @@ -224,18 +225,21 @@ func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) { e := cipher.NewCTR(c, iv[:]) - e.XORKeyStream(ciphertext[ivSize:cap(ciphertext)], plaintext) + e.XORKeyStream(ciphertext[ivSize:], plaintext) copy(ciphertext, iv[:]) + // truncate to only conver iv and actual ciphertext ciphertext = ciphertext[:ivSize+len(plaintext)] mac := poly1305Sign(ciphertext[ivSize:], ciphertext[:ivSize], &ks.Sign) + // append the mac tag ciphertext = append(ciphertext, mac...) return ciphertext, nil } // Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form -// IV || Ciphertext || MAC. +// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the +// same slice. func Decrypt(ks *Key, plaintext, ciphertext []byte) ([]byte, error) { // check for plausible length if len(ciphertext) < ivSize+macSize { diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 6b0506fc7..bf1863abf 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -31,9 +31,15 @@ func TestEncryptDecrypt(t *testing.T) { ciphertext, err := crypto.Encrypt(k, restic.GetChunkBuf("TestEncryptDecrypt"), data) OK(t, err) + Assert(t, len(ciphertext) == len(data)+crypto.Extension, + "ciphertext length does not match: want %d, got %d", + len(data)+crypto.Extension, len(ciphertext)) plaintext, err := crypto.Decrypt(k, nil, ciphertext) OK(t, err) + Assert(t, len(plaintext) == len(data), + "plaintext length does not match: want %d, got %d", + len(data), len(plaintext)) restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext) @@ -54,7 +60,7 @@ func TestSmallBuffer(t *testing.T) { ciphertext := make([]byte, size/2) ciphertext, err = crypto.Encrypt(k, ciphertext, data) - // this must throw an error, since the target slice is too small + // this must extend the slice Assert(t, cap(ciphertext) > size/2, "expected extended slice, but capacity is only %d bytes", cap(ciphertext)) @@ -77,12 +83,12 @@ func TestSameBuffer(t *testing.T) { _, err = io.ReadFull(f, data) OK(t, err) - ciphertext := make([]byte, size) - copy(ciphertext, data) + ciphertext := make([]byte, 0, size+crypto.Extension) - ciphertext, err = crypto.Encrypt(k, ciphertext, ciphertext) + ciphertext, err = crypto.Encrypt(k, ciphertext, data) OK(t, err) + // use the same buffer for decryption ciphertext, err = crypto.Decrypt(k, ciphertext, ciphertext) OK(t, err) Assert(t, bytes.Equal(ciphertext, data),