// decrypt authenticates and decrypts ciphertext and stores the result in // plaintext. func (r *Repository) decryptTo(plaintext, ciphertext []byte) ([]byte, error) { if r.key == nil { return nil, errors.New("key for repository not set") } return crypto.Decrypt(r.key, nil, ciphertext) }
func TestEncryptWriter(t *testing.T) { k := crypto.NewRandomKey() tests := []int{5, 23, 2<<18 + 23, 1 << 20} if testLargeCrypto { tests = append(tests, 7<<20+123) } for _, size := range tests { data := Random(42, size) buf := bytes.NewBuffer(nil) wr := crypto.EncryptTo(k, buf) _, err := io.Copy(wr, bytes.NewReader(data)) OK(t, err) OK(t, wr.Close()) ciphertext := buf.Bytes() l := len(data) + crypto.Extension Assert(t, len(ciphertext) == l, "wrong ciphertext length: expected %d, got %d", l, len(ciphertext)) // decrypt with default function plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext) OK(t, err) Assert(t, bytes.Equal(data, plaintext), "wrong plaintext after decryption: expected %02x, got %02x", data, plaintext) } }
func TestEncryptDecrypt(t *testing.T) { k := crypto.NewRandomKey() tests := []int{5, 23, 2<<18 + 23, 1 << 20} if testLargeCrypto { tests = append(tests, 7<<20+123) } for _, size := range tests { data := Random(42, size) buf := make([]byte, size+crypto.Extension) ciphertext, err := crypto.Encrypt(k, buf, 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)) Equals(t, plaintext, data) } }
// List returns the list of entries found in a pack file. func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, err error) { buf, err := readHeader(rd, size) if err != nil { return nil, err } n, err := crypto.Decrypt(k, buf, buf) if err != nil { return nil, err } buf = buf[:n] hdrRd := bytes.NewReader(buf) pos := uint(0) for { e := headerEntry{} err = binary.Read(hdrRd, binary.LittleEndian, &e) if errors.Cause(err) == io.EOF { break } if err != nil { return nil, errors.Wrap(err, "binary.Read") } entry := restic.Blob{ Length: uint(e.Length), ID: e.ID, Offset: pos, } switch e.Type { case 0: entry.Type = restic.DataBlob case 1: entry.Type = restic.TreeBlob default: return nil, errors.Errorf("invalid type %d", e.Type) } entries = append(entries, entry) pos += uint(e.Length) } return entries, nil }
// OpenKey tries do decrypt the key specified by name with the given password. func OpenKey(s *Repository, name string, password string) (*Key, error) { k, err := LoadKey(s, name) if err != nil { debug.Log("LoadKey(%v) returned error %v", name[:12], err) return nil, err } // check KDF if k.KDF != "scrypt" { return nil, errors.New("only supported KDF is scrypt()") } // derive user key params := crypto.KDFParams{ N: k.N, R: k.R, P: k.P, } k.user, err = crypto.KDF(params, k.Salt, password) if err != nil { return nil, errors.Wrap(err, "crypto.KDF") } // decrypt master keys buf := make([]byte, len(k.Data)) n, err := crypto.Decrypt(k.user, buf, k.Data) if err != nil { return nil, err } buf = buf[:n] // restore json k.master = &crypto.Key{} err = json.Unmarshal(buf, k.master) if err != nil { debug.Log("Unmarshal() returned error %v", err) return nil, errors.Wrap(err, "Unmarshal") } k.name = name if !k.Valid() { return nil, errors.New("Invalid key for repository") } return k, nil }
// checkPack reads a pack and checks the integrity of all blobs. func checkPack(r restic.Repository, id restic.ID) error { debug.Log("checking pack %v", id.Str()) h := restic.Handle{Type: restic.DataFile, Name: id.String()} buf, err := backend.LoadAll(r.Backend(), h, nil) if err != nil { return err } hash := restic.Hash(buf) if !hash.Equal(id) { debug.Log("Pack ID does not match, want %v, got %v", id.Str(), hash.Str()) return errors.Errorf("Pack ID does not match, want %v, got %v", id.Str(), hash.Str()) } blobs, err := pack.List(r.Key(), bytes.NewReader(buf), int64(len(buf))) if err != nil { return err } var errs []error for i, blob := range blobs { debug.Log(" check blob %d: %v", i, blob.ID.Str()) plainBuf := make([]byte, blob.Length) n, err := crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length]) 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 } plainBuf = plainBuf[:n] hash := restic.Hash(plainBuf) 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())) continue } } if len(errs) > 0 { return errors.Errorf("pack %v contains %v errors: %v", id.Str(), len(errs), errs) } return nil }
// checkPack reads a pack and checks the integrity of all blobs. func checkPack(r *repository.Repository, id backend.ID) error { debug.Log("Checker.checkPack", "checking pack %v", id.Str()) h := backend.Handle{Type: backend.Data, Name: id.String()} buf, err := backend.LoadAll(r.Backend(), h, nil) if err != nil { return err } hash := backend.Hash(buf) if !hash.Equal(id) { debug.Log("Checker.checkPack", "Pack ID does not match, want %v, got %v", id.Str(), hash.Str()) return fmt.Errorf("Pack ID does not match, want %v, got %v", id.Str(), hash.Str()) } unpacker, err := pack.NewUnpacker(r.Key(), bytes.NewReader(buf)) if err != nil { return err } var errs []error for i, blob := range unpacker.Entries { debug.Log("Checker.checkPack", " check blob %d: %v", i, blob.ID.Str()) plainBuf := make([]byte, blob.Length) plainBuf, err = crypto.Decrypt(r.Key(), plainBuf, buf[blob.Offset:blob.Offset+blob.Length]) if err != nil { debug.Log("Checker.checkPack", " error decrypting blob %v: %v", blob.ID.Str(), err) errs = append(errs, fmt.Errorf("blob %v: %v", i, err)) continue } hash := backend.Hash(plainBuf) if !hash.Equal(blob.ID) { debug.Log("Checker.checkPack", " Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str()) errs = append(errs, fmt.Errorf("Blob ID does not match, want %v, got %v", blob.ID.Str(), hash.Str())) continue } } if len(errs) > 0 { return fmt.Errorf("pack %v contains %v errors: %v", id.Str(), len(errs), errs) } return nil }
func TestSameBuffer(t *testing.T) { k := crypto.NewRandomKey() size := 600 data := make([]byte, size) _, err := io.ReadFull(rand.Reader, data) OK(t, err) ciphertext := make([]byte, 0, size+crypto.Extension) 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), "wrong plaintext returned") }
func BenchmarkDecrypt(b *testing.B) { size := 8 << 20 // 8MiB data := make([]byte, size) k := crypto.NewRandomKey() plaintext := make([]byte, size) ciphertext := make([]byte, size+crypto.Extension) ciphertext, err := crypto.Encrypt(k, ciphertext, data) OK(b, err) b.ResetTimer() b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { plaintext, err = crypto.Decrypt(k, plaintext, ciphertext) OK(b, err) } }
func TestCornerCases(t *testing.T) { k := crypto.NewRandomKey() // nil plaintext should encrypt to the empty string // nil ciphertext should allocate a new slice for the ciphertext c, err := crypto.Encrypt(k, nil, nil) OK(t, err) Assert(t, len(c) == crypto.Extension, "wrong length returned for ciphertext, expected 0, got %d", len(c)) // this should decrypt to nil p, err := crypto.Decrypt(k, nil, c) OK(t, err) Equals(t, []byte(nil), p) // test encryption for same slice, this should return an error _, err = crypto.Encrypt(k, c, c) Equals(t, crypto.ErrInvalidCiphertext, err) }
func TestSmallBuffer(t *testing.T) { k := crypto.NewRandomKey() size := 600 data := make([]byte, size) _, err := io.ReadFull(rand.Reader, data) OK(t, err) ciphertext := make([]byte, size/2) ciphertext, err = crypto.Encrypt(k, ciphertext, data) // this must extend the slice Assert(t, cap(ciphertext) > size/2, "expected extended slice, but capacity is only %d bytes", cap(ciphertext)) // check for the correct plaintext plaintext, err := crypto.Decrypt(k, nil, ciphertext) OK(t, err) Assert(t, bytes.Equal(plaintext, data), "wrong plaintext returned") }
func TestLargeEncrypt(t *testing.T) { if !testLargeCrypto { t.SkipNow() } k := crypto.NewRandomKey() for _, size := range []int{chunker.MaxSize, chunker.MaxSize + 1, chunker.MaxSize + 1<<20} { data := make([]byte, size) _, err := io.ReadFull(rand.Reader, data) OK(t, err) ciphertext, err := crypto.Encrypt(k, make([]byte, size+crypto.Extension), data) OK(t, err) plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext) OK(t, err) Equals(t, plaintext, data) } }
// OpenKey tries do decrypt the key specified by name with the given password. func OpenKey(s *Repository, name string, password string) (*Key, error) { k, err := LoadKey(s, name) if err != nil { debug.Log("OpenKey", "LoadKey(%v) returned error %v", name[:12], err) return nil, err } // check KDF if k.KDF != "scrypt" { return nil, errors.New("only supported KDF is scrypt()") } // derive user key k.user, err = crypto.KDF(k.N, k.R, k.P, k.Salt, password) if err != nil { return nil, err } // decrypt master keys buf, err := crypto.Decrypt(k.user, []byte{}, k.Data) if err != nil { return nil, err } // restore json k.master = &crypto.Key{} err = json.Unmarshal(buf, k.master) if err != nil { debug.Log("OpenKey", "Unmarshal() returned error %v", err) return nil, err } k.name = name if !k.Valid() { return nil, errors.New("Invalid key for repository") } return k, nil }
// Repack takes a list of packs together with a list of blobs contained in // these packs. Each pack is loaded and the blobs listed in keepBlobs is saved // into a new pack. Afterwards, the packs are removed. This operation requires // an exclusive lock on the repo. func Repack(repo restic.Repository, packs restic.IDSet, keepBlobs restic.BlobSet) (err error) { debug.Log("repacking %d packs while keeping %d blobs", len(packs), len(keepBlobs)) buf := make([]byte, 0, maxPackSize) for packID := range packs { // load the complete pack h := restic.Handle{Type: restic.DataFile, Name: packID.String()} l, err := repo.Backend().Load(h, buf[:cap(buf)], 0) if errors.Cause(err) == io.ErrUnexpectedEOF { err = nil buf = buf[:l] } if err != nil { return err } debug.Log("pack %v loaded (%d bytes)", packID.Str(), len(buf)) blobs, err := pack.List(repo.Key(), bytes.NewReader(buf), int64(len(buf))) if err != nil { return err } debug.Log("processing pack %v, blobs: %v", packID.Str(), len(blobs)) var plaintext []byte for _, entry := range blobs { h := restic.BlobHandle{ID: entry.ID, Type: entry.Type} if !keepBlobs.Has(h) { continue } debug.Log(" process blob %v", h) ciphertext := buf[entry.Offset : entry.Offset+entry.Length] plaintext = plaintext[:len(plaintext)] if len(plaintext) < len(ciphertext) { plaintext = make([]byte, len(ciphertext)) } debug.Log(" ciphertext %d, plaintext %d", len(plaintext), len(ciphertext)) n, err := crypto.Decrypt(repo.Key(), plaintext, ciphertext) if err != nil { return err } plaintext = plaintext[:n] _, err = repo.SaveBlob(entry.Type, plaintext, entry.ID) if err != nil { return err } debug.Log(" saved blob %v", entry.ID.Str()) keepBlobs.Delete(h) } } if err := repo.Flush(); err != nil { return err } for packID := range packs { err := repo.Backend().Remove(restic.DataFile, packID.String()) if err != nil { debug.Log("error removing pack %v: %v", packID.Str(), err) return err } debug.Log("removed pack %v", packID.Str()) } return nil }