diff --git a/key.go b/key.go index a7f4d7c4e..436206171 100644 --- a/key.go +++ b/key.go @@ -109,7 +109,7 @@ func OpenKey(s Server, id backend.ID, password string) (*Key, error) { } // decrypt master keys - buf, err := k.DecryptUser(k.Data) + buf, err := k.DecryptUser([]byte{}, k.Data) if err != nil { return nil, err } @@ -447,10 +447,15 @@ func (k *Key) EncryptUserTo(wr io.Writer) io.WriteCloser { // Decrypt verifes and decrypts the ciphertext. Ciphertext must be in the form // IV || Ciphertext || HMAC. -func (k *Key) decrypt(ks *keys, ciphertext []byte) ([]byte, error) { +func (k *Key) decrypt(ks *keys, plaintext, ciphertext []byte) ([]byte, error) { // check for plausible length if len(ciphertext) < ivSize+hmacSize { - panic("trying to decryipt invalid data: ciphertext too small") + panic("trying to decrypt invalid data: ciphertext too small") + } + + if cap(plaintext) < len(ciphertext) { + // extend plaintext + plaintext = append(plaintext, make([]byte, len(ciphertext)-cap(plaintext))...) } hm := hmac.New(sha256.New, ks.Sign) @@ -483,22 +488,22 @@ func (k *Key) decrypt(ks *keys, ciphertext []byte) ([]byte, error) { // decrypt e := cipher.NewCTR(c, iv) - plaintext := make([]byte, len(ciphertext)) e.XORKeyStream(plaintext, ciphertext) + plaintext = plaintext[:len(ciphertext)] return plaintext, nil } // Decrypt verifes and decrypts the ciphertext with the master key. Ciphertext // must be in the form IV || Ciphertext || HMAC. -func (k *Key) Decrypt(ciphertext []byte) ([]byte, error) { - return k.decrypt(k.master, ciphertext) +func (k *Key) Decrypt(plaintext, ciphertext []byte) ([]byte, error) { + return k.decrypt(k.master, plaintext, ciphertext) } // DecryptUser verifes and decrypts the ciphertext with the user key. Ciphertext // must be in the form IV || Ciphertext || HMAC. -func (k *Key) DecryptUser(ciphertext []byte) ([]byte, error) { - return k.decrypt(k.user, ciphertext) +func (k *Key) DecryptUser(plaintext, ciphertext []byte) ([]byte, error) { + return k.decrypt(k.user, plaintext, ciphertext) } type decryptReader struct { @@ -518,8 +523,7 @@ func (d *decryptReader) Read(dst []byte) (int, error) { remaining := len(d.buf) - d.pos if len(dst) >= remaining { n := copy(dst, d.buf[d.pos:]) - FreeChunkBuf("decryptReader", d.buf) - d.buf = nil + d.Close() return n, io.EOF } diff --git a/key_int_test.go b/key_int_test.go index db62a86a3..a1fb8fd5e 100644 --- a/key_int_test.go +++ b/key_int_test.go @@ -56,7 +56,7 @@ func TestCrypto(t *testing.T) { msg = msg[:n] // decrypt message - _, err = r.decrypt(r.master, msg) + _, err = r.decrypt(r.master, []byte{}, msg) if err != nil { t.Fatal(err) } @@ -64,12 +64,12 @@ func TestCrypto(t *testing.T) { // change hmac, this must fail msg[len(msg)-8] ^= 0x23 - if _, err = r.decrypt(r.master, msg); err != ErrUnauthenticated { + if _, err = r.decrypt(r.master, []byte{}, msg); err != ErrUnauthenticated { t.Fatal("wrong HMAC value not detected") } // test decryption - p, err := r.decrypt(r.master, tv.ciphertext) + p, err := r.decrypt(r.master, []byte{}, tv.ciphertext) if err != nil { t.Fatal(err) } diff --git a/key_test.go b/key_test.go index 182aa2fe4..920cb2dd0 100644 --- a/key_test.go +++ b/key_test.go @@ -69,7 +69,7 @@ func TestEncryptDecrypt(t *testing.T) { n, err := k.Encrypt(ciphertext, data) ok(t, err) - plaintext, err := k.Decrypt(ciphertext[:n]) + plaintext, err := k.Decrypt(nil, ciphertext[:n]) ok(t, err) restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext) @@ -119,7 +119,7 @@ func TestLargeEncrypt(t *testing.T) { n, err := k.Encrypt(ciphertext, data) ok(t, err) - plaintext, err := k.Decrypt(ciphertext[:n]) + plaintext, err := k.Decrypt([]byte{}, ciphertext[:n]) ok(t, err) equals(t, plaintext, data) @@ -153,10 +153,11 @@ func BenchmarkEncrypt(b *testing.B) { defer teardownBackend(b, be) k := setupKey(b, be, testPassword) + buf := make([]byte, len(data)+restic.CiphertextExtension) + b.ResetTimer() b.SetBytes(int64(size)) - buf := make([]byte, len(data)+restic.CiphertextExtension) for i := 0; i < b.N; i++ { _, err := k.Encrypt(buf, data) ok(b, err) @@ -216,6 +217,8 @@ func BenchmarkEncryptDecryptReader(b *testing.B) { _, err = io.Copy(ioutil.Discard, r) ok(b, err) } + + restic.PoolAlloc() } func BenchmarkDecrypt(b *testing.B) { @@ -227,6 +230,10 @@ func BenchmarkDecrypt(b *testing.B) { k := setupKey(b, s, testPassword) ciphertext := restic.GetChunkBuf("BenchmarkDecrypt") + defer restic.FreeChunkBuf("BenchmarkDecrypt", ciphertext) + plaintext := restic.GetChunkBuf("BenchmarkDecrypt") + defer restic.FreeChunkBuf("BenchmarkDecrypt", plaintext) + n, err := k.Encrypt(ciphertext, data) ok(b, err) @@ -234,10 +241,9 @@ func BenchmarkDecrypt(b *testing.B) { b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { - _, err := k.Decrypt(ciphertext[:n]) + plaintext, err = k.Decrypt(plaintext, ciphertext[:n]) ok(b, err) } - restic.FreeChunkBuf("BenchmarkDecrypt", ciphertext) } func TestEncryptStreamWriter(t *testing.T) { @@ -268,7 +274,7 @@ func TestEncryptStreamWriter(t *testing.T) { l, len(ciphertext.Bytes())) // decrypt with default function - plaintext, err := k.Decrypt(ciphertext.Bytes()) + plaintext, err := k.Decrypt([]byte{}, ciphertext.Bytes()) ok(t, err) assert(t, bytes.Equal(data, plaintext), "wrong plaintext after decryption: expected %02x, got %02x", @@ -342,7 +348,7 @@ func TestEncryptWriter(t *testing.T) { l, len(ciphertext)) // decrypt with default function - plaintext, err := k.Decrypt(ciphertext) + plaintext, err := k.Decrypt([]byte{}, ciphertext) ok(t, err) assert(t, bytes.Equal(data, plaintext), "wrong plaintext after decryption: expected %02x, got %02x", diff --git a/server.go b/server.go index 2dc6b1435..ce6b3c287 100644 --- a/server.go +++ b/server.go @@ -333,7 +333,7 @@ func (s Server) Decrypt(ciphertext []byte) ([]byte, error) { return nil, errors.New("key for server not set") } - return s.key.Decrypt(ciphertext) + return s.key.Decrypt([]byte{}, ciphertext) } func (s Server) Encrypt(ciphertext, plaintext []byte) (int, error) { @@ -361,7 +361,7 @@ func (s Server) EachDecrypted(t backend.Type, f func(backend.ID, []byte, error)) return } - buf, err := s.key.Decrypt(data) + buf, err := s.key.Decrypt([]byte{}, data) if err != nil { f(id, nil, err) return