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) } }
func TestDecryptStreamReader(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) var err error ciphertext := make([]byte, size+crypto.Extension) // encrypt with default function ciphertext, err = crypto.Encrypt(k, ciphertext, data) OK(t, err) Assert(t, len(ciphertext) == len(data)+crypto.Extension, "wrong number of bytes returned after encryption: expected %d, got %d", len(data)+crypto.Extension, len(ciphertext)) rd, err := crypto.DecryptFrom(k, bytes.NewReader(ciphertext)) OK(t, err) plaintext, err := ioutil.ReadAll(rd) OK(t, err) Assert(t, bytes.Equal(data, plaintext), "wrong plaintext after decryption: expected %02x, got %02x", data, plaintext) } }
func TestCreatePack(t *testing.T) { // create random keys k := crypto.NewRandomKey() bufs, packData, packSize := newPack(t, k, testLens) Equals(t, uint(len(packData)), packSize) verifyBlobs(t, bufs, k, bytes.NewReader(packData), packSize) }
func TestShortPack(t *testing.T) { k := crypto.NewRandomKey() bufs, packData, packSize := newPack(t, k, []int{23}) b := mem.New() id := restic.Hash(packData) handle := restic.Handle{Type: restic.DataFile, Name: id.String()} OK(t, b.Save(handle, packData)) verifyBlobs(t, bufs, k, restic.ReaderAt(b, handle), packSize) }
func TestPackerManager(t *testing.T) { rnd := newRandReader(rand.NewSource(23)) be := mem.New() pm := newPackerManager(be, crypto.NewRandomKey()) blobBuf := make([]byte, maxBlobSize) bytes := fillPacks(t, rnd, be, pm, blobBuf) bytes += flushRemainingPacks(t, rnd, be, pm) t.Logf("saved %d bytes", bytes) }
func TestUnpackReadSeeker(t *testing.T) { // create random keys k := crypto.NewRandomKey() bufs, packData, packSize := newPack(t, k, testLens) b := mem.New() id := restic.Hash(packData) handle := restic.Handle{Type: restic.DataFile, Name: id.String()} OK(t, b.Save(handle, packData)) verifyBlobs(t, bufs, k, restic.ReaderAt(b, handle), packSize) }
func TestUnpackReadSeeker(t *testing.T) { // create random keys k := crypto.NewRandomKey() bufs, packData, packSize := newPack(t, k) b := mem.New() id := backend.Hash(packData) handle := backend.Handle{Type: backend.Data, Name: id.String()} OK(t, b.Save(handle, packData)) rd := backend.NewReadSeeker(b, handle) verifyBlobs(t, bufs, k, rd, packSize) }
func BenchmarkEncrypt(b *testing.B) { size := 8 << 20 // 8MiB data := make([]byte, size) k := crypto.NewRandomKey() buf := make([]byte, len(data)+crypto.Extension) b.ResetTimer() b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { _, err := crypto.Encrypt(k, buf, data) OK(b, err) } }
func BenchmarkPackerManager(t *testing.B) { rnd := newRandReader(rand.NewSource(23)) be := &fakeBackend{} pm := newPackerManager(be, crypto.NewRandomKey()) blobBuf := make([]byte, maxBlobSize) t.ResetTimer() bytes := 0 for i := 0; i < t.N; i++ { bytes += fillPacks(t, rnd, be, pm, blobBuf) } bytes += flushRemainingPacks(t, rnd, be, pm) t.Logf("saved %d bytes", bytes) }
func BenchmarkEncryptWriter(b *testing.B) { size := 8 << 20 // 8MiB k := crypto.NewRandomKey() b.ResetTimer() b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { rd := RandomLimitReader(23, size) wr := crypto.EncryptTo(k, ioutil.Discard) n, err := io.Copy(wr, rd) OK(b, err) OK(b, wr.Close()) Assert(b, n == int64(size), "not enough bytes writter: want %d, got %d", size, n) } }
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 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) } }
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 BenchmarkDecryptReader(b *testing.B) { size := 8 << 20 // 8MiB buf := Random(23, size) k := crypto.NewRandomKey() ciphertext := make([]byte, len(buf)+crypto.Extension) _, err := crypto.Encrypt(k, ciphertext, buf) OK(b, err) rd := bytes.NewReader(ciphertext) b.ResetTimer() b.SetBytes(int64(size)) for i := 0; i < b.N; i++ { rd.Seek(0, 0) decRd, err := crypto.DecryptFrom(k, rd) OK(b, err) _, err = io.Copy(ioutil.Discard, decRd) OK(b, err) } }
func BenchmarkEncryptDecryptReader(b *testing.B) { k := crypto.NewRandomKey() size := 8 << 20 // 8MiB b.ResetTimer() b.SetBytes(int64(size)) buf := bytes.NewBuffer(nil) for i := 0; i < b.N; i++ { rd := RandomLimitReader(23, size) buf.Reset() wr := crypto.EncryptTo(k, buf) _, err := io.Copy(wr, rd) OK(b, err) OK(b, wr.Close()) r, err := crypto.DecryptFrom(k, buf) OK(b, err) _, err = io.Copy(ioutil.Discard, r) OK(b, err) } }
// AddKey adds a new key to an already existing repository. func AddKey(s *Repository, password string, template *crypto.Key) (*Key, error) { // make sure we have valid KDF parameters if KDFParams == nil { p, err := crypto.Calibrate(KDFTimeout, KDFMemory) if err != nil { return nil, errors.Wrap(err, "Calibrate") } KDFParams = &p debug.Log("calibrated KDF parameters are %v", p) } // fill meta data about key newkey := &Key{ Created: time.Now(), KDF: "scrypt", N: KDFParams.N, R: KDFParams.R, P: KDFParams.P, } hn, err := os.Hostname() if err == nil { newkey.Hostname = hn } usr, err := user.Current() if err == nil { newkey.Username = usr.Username } // generate random salt newkey.Salt, err = crypto.NewSalt() if err != nil { panic("unable to read enough random bytes for salt: " + err.Error()) } // call KDF to derive user key newkey.user, err = crypto.KDF(*KDFParams, newkey.Salt, password) if err != nil { return nil, err } if template == nil { // generate new random master keys newkey.master = crypto.NewRandomKey() } else { // copy master keys from old key newkey.master = template } // encrypt master keys (as json) with user key buf, err := json.Marshal(newkey.master) if err != nil { return nil, errors.Wrap(err, "Marshal") } newkey.Data, err = crypto.Encrypt(newkey.user, nil, buf) // dump as json buf, err = json.Marshal(newkey) if err != nil { return nil, errors.Wrap(err, "Marshal") } // store in repository and return h := restic.Handle{ Type: restic.KeyFile, Name: restic.Hash(buf).String(), } err = s.be.Save(h, buf) if err != nil { return nil, err } newkey.name = h.Name return newkey, nil }
func TestCreatePack(t *testing.T) { type Buf struct { data []byte id backend.ID } bufs := []Buf{} for _, l := range lengths { b := make([]byte, l) _, err := io.ReadFull(rand.Reader, b) OK(t, err) h := sha256.Sum256(b) bufs = append(bufs, Buf{data: b, id: h}) } // create random keys k := crypto.NewRandomKey() // pack blobs p := pack.NewPacker(k, nil) for _, b := range bufs { p.Add(pack.Tree, b.id, bytes.NewReader(b.data)) } packData, err := p.Finalize() OK(t, err) written := 0 for _, l := range lengths { written += l } // header length written += binary.Size(uint32(0)) // header written += len(lengths) * (binary.Size(pack.BlobType(0)) + binary.Size(uint32(0)) + backend.IDSize) // header crypto written += crypto.Extension // check length Equals(t, written, len(packData)) Equals(t, uint(written), p.Size()) // read and parse it again rd := bytes.NewReader(packData) np, err := pack.NewUnpacker(k, rd) OK(t, err) Equals(t, len(np.Entries), len(bufs)) for i, b := range bufs { e := np.Entries[i] Equals(t, b.id, e.ID) brd, err := e.GetReader(rd) OK(t, err) data, err := ioutil.ReadAll(brd) OK(t, err) Assert(t, bytes.Equal(b.data, data), "data for blob %v doesn't match", i) } }
// AddKey adds a new key to an already existing repository. func AddKey(s *Repository, password string, template *crypto.Key) (*Key, error) { // fill meta data about key newkey := &Key{ Created: time.Now(), KDF: "scrypt", N: scryptN, R: scryptR, P: scryptP, } hn, err := os.Hostname() if err == nil { newkey.Hostname = hn } usr, err := user.Current() if err == nil { newkey.Username = usr.Username } // generate random salt newkey.Salt = make([]byte, scryptSaltsize) n, err := rand.Read(newkey.Salt) if n != scryptSaltsize || err != nil { panic("unable to read enough random bytes for salt") } // call KDF to derive user key newkey.user, err = crypto.KDF(newkey.N, newkey.R, newkey.P, newkey.Salt, password) if err != nil { return nil, err } if template == nil { // generate new random master keys newkey.master = crypto.NewRandomKey() } else { // copy master keys from old key newkey.master = template } // encrypt master keys (as json) with user key buf, err := json.Marshal(newkey.master) if err != nil { return nil, err } newkey.Data, err = crypto.Encrypt(newkey.user, nil, buf) // dump as json buf, err = json.Marshal(newkey) if err != nil { return nil, err } // store in repository and return h := backend.Handle{ Type: backend.Key, Name: backend.Hash(buf).String(), } err = s.be.Save(h, buf) if err != nil { return nil, err } newkey.name = h.Name return newkey, nil }