diff --git a/internal/archiver/archiver_test.go b/internal/archiver/archiver_test.go index 01e9d9e74..293de9152 100644 --- a/internal/archiver/archiver_test.go +++ b/internal/archiver/archiver_test.go @@ -32,6 +32,7 @@ type Rdr interface { func benchmarkChunkEncrypt(b testing.TB, buf, buf2 []byte, rd Rdr, key *crypto.Key) { rd.Seek(0, 0) ch := chunker.New(rd, testPol) + nonce := crypto.NewRandomNonce() for { chunk, err := ch.Next(buf) @@ -42,12 +43,10 @@ func benchmarkChunkEncrypt(b testing.TB, buf, buf2 []byte, rd Rdr, key *crypto.K rtest.OK(b, err) - // reduce length of buf rtest.Assert(b, uint(len(chunk.Data)) == chunk.Length, "invalid length: got %d, expected %d", len(chunk.Data), chunk.Length) - _, err = key.Encrypt(buf2, chunk.Data) - rtest.OK(b, err) + _ = key.Seal(buf2[:0], nonce, chunk.Data, nil) } } @@ -71,6 +70,7 @@ func BenchmarkChunkEncrypt(b *testing.B) { func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *crypto.Key) { ch := chunker.New(rd, testPol) + nonce := crypto.NewRandomNonce() for { chunk, err := ch.Next(buf) @@ -78,8 +78,7 @@ func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *crypto.Key) break } - // reduce length of chunkBuf - key.Encrypt(chunk.Data, chunk.Data) + _ = key.Seal(chunk.Data[:0], nonce, chunk.Data, nil) } } diff --git a/internal/checker/checker.go b/internal/checker/checker.go index c557ab475..efef7ae94 100644 --- a/internal/checker/checker.go +++ b/internal/checker/checker.go @@ -724,15 +724,15 @@ func checkPack(ctx context.Context, r restic.Repository, id restic.ID) error { continue } - n, err := r.Key().Decrypt(buf, buf) + nonce, ciphertext := buf[:r.Key().NonceSize()], buf[r.Key().NonceSize():] + plaintext, err := r.Key().Open(ciphertext[:0], nonce, ciphertext, nil) if err != nil { debug.Log(" error decrypting blob %v: %v", blob.ID.Str(), err) errs = append(errs, errors.Errorf("blob %v: %v", i, err)) continue } - buf = buf[:n] - hash := restic.Hash(buf) + hash := restic.Hash(plaintext) if !hash.Equal(blob.ID) { debug.Log(" Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str()) errs = append(errs, errors.Errorf("Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str())) diff --git a/internal/pack/pack.go b/internal/pack/pack.go index 292f9dcb0..577b4c763 100644 --- a/internal/pack/pack.go +++ b/internal/pack/pack.go @@ -75,10 +75,10 @@ func (p *Packer) Finalize() (uint, error) { return 0, err } - encryptedHeader, err := p.k.Encrypt(nil, hdrBuf.Bytes()) - if err != nil { - return 0, err - } + encryptedHeader := make([]byte, 0, hdrBuf.Len()+p.k.Overhead()+p.k.NonceSize()) + nonce := crypto.NewRandomNonce() + encryptedHeader = append(encryptedHeader, nonce...) + encryptedHeader = p.k.Seal(encryptedHeader, nonce, hdrBuf.Bytes(), nil) // append the header n, err := p.wr.Write(encryptedHeader) @@ -268,15 +268,19 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, err return nil, err } - n, err := k.Decrypt(buf, buf) + if len(buf) < k.NonceSize()+k.Overhead() { + return nil, errors.New("invalid header, too small") + } + + nonce, buf := buf[:k.NonceSize()], buf[k.NonceSize():] + buf, err = k.Open(buf[:0], nonce, buf, nil) if err != nil { return nil, err } - buf = buf[:n] hdrRd := bytes.NewReader(buf) - entries = make([]restic.Blob, 0, uint(n)/entrySize) + entries = make([]restic.Blob, 0, uint(len(buf))/entrySize) pos := uint(0) for { diff --git a/internal/repository/key.go b/internal/repository/key.go index e378991cb..e29e09257 100644 --- a/internal/repository/key.go +++ b/internal/repository/key.go @@ -87,12 +87,12 @@ func OpenKey(ctx context.Context, s *Repository, name string, password string) ( } // decrypt master keys - buf := make([]byte, len(k.Data)) - n, err := k.user.Decrypt(buf, k.Data) + nonce, ciphertext := k.Data[:k.user.NonceSize()], k.Data[k.user.NonceSize():] + buf := make([]byte, 0, len(ciphertext)) + buf, err = k.user.Open(buf, nonce, ciphertext, nil) if err != nil { return nil, err } - buf = buf[:n] // restore json k.master = &crypto.Key{} @@ -221,7 +221,11 @@ func AddKey(ctx context.Context, s *Repository, password string, template *crypt return nil, errors.Wrap(err, "Marshal") } - newkey.Data, err = newkey.user.Encrypt(nil, buf) + nonce := crypto.NewRandomNonce() + ciphertext := make([]byte, 0, len(buf)+newkey.user.Overhead()+newkey.user.NonceSize()) + ciphertext = append(ciphertext, nonce...) + ciphertext = newkey.user.Seal(ciphertext, nonce, buf, nil) + newkey.Data = ciphertext // dump as json buf, err = json.Marshal(newkey) diff --git a/internal/repository/repack.go b/internal/repository/repack.go index 3bba26803..3eacea182 100644 --- a/internal/repository/repack.go +++ b/internal/repository/repack.go @@ -90,14 +90,13 @@ func Repack(ctx context.Context, repo restic.Repository, packs restic.IDSet, kee h, tempfile.Name(), len(buf), n) } - n, err = repo.Key().Decrypt(buf, buf) + nonce, ciphertext := buf[:repo.Key().NonceSize()], buf[repo.Key().NonceSize():] + plaintext, err := repo.Key().Open(ciphertext[:0], nonce, ciphertext, nil) if err != nil { return nil, err } - buf = buf[:n] - - id := restic.Hash(buf) + id := restic.Hash(plaintext) if !id.Equal(entry.ID) { debug.Log("read blob %v/%v from %v: wrong data returned, hash is %v", h.Type, h.ID, tempfile.Name(), id) @@ -105,7 +104,7 @@ func Repack(ctx context.Context, repo restic.Repository, packs restic.IDSet, kee h, tempfile.Name(), id) } - _, err = repo.SaveBlob(ctx, entry.Type, buf, entry.ID) + _, err = repo.SaveBlob(ctx, entry.Type, plaintext, entry.ID) if err != nil { return nil, err } diff --git a/internal/repository/repository.go b/internal/repository/repository.go index 161eb8734..9bf686548 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -79,13 +79,13 @@ func (r *Repository) LoadAndDecrypt(ctx context.Context, t restic.FileType, id r return nil, errors.Errorf("load %v: invalid data returned", h) } - // decrypt - n, err := r.decryptTo(buf, buf) + nonce, ciphertext := buf[:r.key.NonceSize()], buf[r.key.NonceSize():] + plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) if err != nil { return nil, err } - return buf[:n], nil + return plaintext, nil } // sortCachedPacks moves all cached pack files to the front of blobs. @@ -156,20 +156,22 @@ func (r *Repository) loadBlob(ctx context.Context, id restic.ID, t restic.BlobTy } // decrypt - n, err = r.decryptTo(plaintextBuf, plaintextBuf) + nonce, ciphertext := plaintextBuf[:r.key.NonceSize()], plaintextBuf[r.key.NonceSize():] + plaintext, err := r.key.Open(ciphertext[:0], nonce, ciphertext, nil) if err != nil { lastError = errors.Errorf("decrypting blob %v failed: %v", id, err) continue } - plaintextBuf = plaintextBuf[:n] // check hash - if !restic.Hash(plaintextBuf).Equal(id) { + if !restic.Hash(plaintext).Equal(id) { lastError = errors.Errorf("blob %v returned invalid hash", id) continue } - return len(plaintextBuf), nil + // move decrypted data to the start of the provided buffer + copy(plaintextBuf[0:], plaintext) + return len(plaintext), nil } if lastError != nil { @@ -210,11 +212,12 @@ func (r *Repository) SaveAndEncrypt(ctx context.Context, t restic.BlobType, data ciphertext := getBuf() defer freeBuf(ciphertext) + ciphertext = ciphertext[:0] + nonce := crypto.NewRandomNonce() + ciphertext = append(ciphertext, nonce...) + // encrypt blob - ciphertext, err := r.Encrypt(ciphertext, data) - if err != nil { - return restic.ID{}, err - } + ciphertext = r.key.Seal(ciphertext, nonce, data, nil) // find suitable packer and add blob var pm *packerManager @@ -266,10 +269,11 @@ func (r *Repository) SaveJSONUnpacked(ctx context.Context, t restic.FileType, it // storage hash. func (r *Repository) SaveUnpacked(ctx context.Context, t restic.FileType, p []byte) (id restic.ID, err error) { ciphertext := restic.NewBlobBuffer(len(p)) - ciphertext, err = r.Encrypt(ciphertext, p) - if err != nil { - return restic.ID{}, err - } + ciphertext = ciphertext[:0] + nonce := crypto.NewRandomNonce() + ciphertext = append(ciphertext, nonce...) + + ciphertext = r.key.Seal(ciphertext, nonce, p, nil) id = restic.Hash(ciphertext) h := restic.Handle{Type: t, Name: id.String()} @@ -522,26 +526,6 @@ func (r *Repository) init(ctx context.Context, password string, cfg restic.Confi return err } -// decrypt authenticates and decrypts ciphertext and stores the result in -// plaintext. -func (r *Repository) decryptTo(plaintext, ciphertext []byte) (int, error) { - if r.key == nil { - return 0, errors.New("key for repository not set") - } - - return r.key.Decrypt(plaintext, ciphertext) -} - -// Encrypt encrypts and authenticates the plaintext and saves the result in -// ciphertext. -func (r *Repository) Encrypt(ciphertext, plaintext []byte) ([]byte, error) { - if r.key == nil { - return nil, errors.New("key for repository not set") - } - - return r.key.Encrypt(ciphertext, plaintext) -} - // Key returns the current master key. func (r *Repository) Key() *crypto.Key { return r.key