forked from TrueCloudLab/restic
Reorganize crypto code
Move all crypto functions to package "crypto", move random generators for tests into helper package.
This commit is contained in:
parent
8e8f31d3fe
commit
3a2525809c
10 changed files with 166 additions and 187 deletions
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/restic/restic"
|
"github.com/restic/restic"
|
||||||
|
@ -16,21 +15,6 @@ import (
|
||||||
var benchArchiveDirectory = flag.String("test.benchdir", ".", "benchmark archiving a real directory (default: .)")
|
var benchArchiveDirectory = flag.String("test.benchdir", ".", "benchmark archiving a real directory (default: .)")
|
||||||
var testPol = chunker.Pol(0x3DA3358B4DC173)
|
var testPol = chunker.Pol(0x3DA3358B4DC173)
|
||||||
|
|
||||||
func get_random(seed, count int) []byte {
|
|
||||||
buf := make([]byte, count)
|
|
||||||
|
|
||||||
rnd := rand.New(rand.NewSource(int64(seed)))
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
buf[i] = byte(rnd.Uint32())
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomReader(seed, size int) *bytes.Reader {
|
|
||||||
return bytes.NewReader(get_random(seed, size))
|
|
||||||
}
|
|
||||||
|
|
||||||
const bufSize = chunker.MiB
|
const bufSize = chunker.MiB
|
||||||
|
|
||||||
type Rdr interface {
|
type Rdr interface {
|
||||||
|
@ -66,7 +50,7 @@ func benchmarkChunkEncrypt(b testing.TB, buf []byte, rd Rdr, key *restic.Key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkChunkEncrypt(b *testing.B) {
|
func BenchmarkChunkEncrypt(b *testing.B) {
|
||||||
data := get_random(23, 10<<20) // 10MiB
|
data := Random(23, 10<<20) // 10MiB
|
||||||
rd := bytes.NewReader(data)
|
rd := bytes.NewReader(data)
|
||||||
|
|
||||||
be := setupBackend(b)
|
be := setupBackend(b)
|
||||||
|
@ -110,7 +94,7 @@ func BenchmarkChunkEncryptParallel(b *testing.B) {
|
||||||
defer teardownBackend(b, be)
|
defer teardownBackend(b, be)
|
||||||
key := setupKey(b, be, "geheim")
|
key := setupKey(b, be, "geheim")
|
||||||
|
|
||||||
data := get_random(23, 10<<20) // 10MiB
|
data := Random(23, 10<<20) // 10MiB
|
||||||
|
|
||||||
buf := restic.GetChunkBuf("BenchmarkChunkEncryptParallel")
|
buf := restic.GetChunkBuf("BenchmarkChunkEncryptParallel")
|
||||||
|
|
||||||
|
|
19
crypto/buffer_pool.go
Normal file
19
crypto/buffer_pool.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
const defaultBufSize = 2048
|
||||||
|
|
||||||
|
var bufPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, defaultBufSize)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBuffer() []byte {
|
||||||
|
return bufPool.Get().([]byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
func freeBuffer(buf []byte) {
|
||||||
|
bufPool.Put(buf)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package restic
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -6,11 +6,13 @@ import (
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/restic/restic/chunker"
|
||||||
"golang.org/x/crypto/poly1305"
|
"golang.org/x/crypto/poly1305"
|
||||||
"golang.org/x/crypto/scrypt"
|
"golang.org/x/crypto/scrypt"
|
||||||
)
|
)
|
||||||
|
@ -21,8 +23,30 @@ const (
|
||||||
MACKeySizeR = 16 // for Poly1305
|
MACKeySizeR = 16 // for Poly1305
|
||||||
MACKeySize = MACKeySizeK + MACKeySizeR // for Poly1305-AES128
|
MACKeySize = MACKeySizeK + MACKeySizeR // for Poly1305-AES128
|
||||||
ivSize = aes.BlockSize
|
ivSize = aes.BlockSize
|
||||||
|
|
||||||
|
macSize = poly1305.TagSize // Poly1305 size is 16 byte
|
||||||
|
CiphertextExtension = ivSize + macSize
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrUnauthenticated is returned when ciphertext verification has failed.
|
||||||
|
ErrUnauthenticated = errors.New("ciphertext verification failed")
|
||||||
|
|
||||||
|
// ErrBufferTooSmall is returned when the destination slice is too small
|
||||||
|
// for the ciphertext.
|
||||||
|
ErrBufferTooSmall = errors.New("destination buffer too small")
|
||||||
|
)
|
||||||
|
|
||||||
|
// MasterKeys holds signing and encryption keys for a repository. It is stored
|
||||||
|
// encrypted and signed as a JSON data structure in the Data field of the Key
|
||||||
|
// structure. For the master key, the secret random polynomial used for content
|
||||||
|
// defined chunking is included.
|
||||||
|
type MasterKeys struct {
|
||||||
|
Sign MACKey `json:"sign"`
|
||||||
|
Encrypt AESKey `json:"encrypt"`
|
||||||
|
ChunkerPolynomial chunker.Pol `json:"chunker_polynomial,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type AESKey [32]byte
|
type AESKey [32]byte
|
||||||
type MACKey struct {
|
type MACKey struct {
|
||||||
K [16]byte // for AES128
|
K [16]byte // for AES128
|
||||||
|
@ -118,7 +142,7 @@ func poly1305_verify(msg []byte, nonce []byte, key *MACKey, mac []byte) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns new encryption and mac keys. k.MACKey.R is already masked.
|
// returns new encryption and mac keys. k.MACKey.R is already masked.
|
||||||
func generateRandomKeys() (k *MasterKeys) {
|
func GenerateRandomKeys() (k *MasterKeys) {
|
||||||
k = &MasterKeys{}
|
k = &MasterKeys{}
|
||||||
n, err := rand.Read(k.Encrypt[:])
|
n, err := rand.Read(k.Encrypt[:])
|
||||||
if n != AESKeySize || err != nil {
|
if n != AESKeySize || err != nil {
|
||||||
|
@ -249,16 +273,17 @@ func Decrypt(ks *MasterKeys, plaintext, ciphertext []byte) ([]byte, error) {
|
||||||
return plaintext, nil
|
return plaintext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runs scrypt(password)
|
// KDF derives encryption and signing keys from the password using the supplied
|
||||||
func kdf(k *Key, password string) (*MasterKeys, error) {
|
// parameters N, R and P and the Salt.
|
||||||
if len(k.Salt) == 0 {
|
func KDF(N, R, P int, salt []byte, password string) (*MasterKeys, error) {
|
||||||
|
if len(salt) == 0 {
|
||||||
return nil, fmt.Errorf("scrypt() called with empty salt")
|
return nil, fmt.Errorf("scrypt() called with empty salt")
|
||||||
}
|
}
|
||||||
|
|
||||||
derKeys := &MasterKeys{}
|
derKeys := &MasterKeys{}
|
||||||
|
|
||||||
keybytes := MACKeySize + AESKeySize
|
keybytes := MACKeySize + AESKeySize
|
||||||
scryptKeys, err := scrypt.Key([]byte(password), k.Salt, k.N, k.R, k.P, keybytes)
|
scryptKeys, err := scrypt.Key([]byte(password), salt, N, R, P, keybytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deriving keys from password: %v", err)
|
return nil, fmt.Errorf("error deriving keys from password: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -296,7 +321,7 @@ func (e *encryptWriter) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return buffer
|
// return buffer
|
||||||
FreeChunkBuf("EncryptWriter", e.data.Bytes())
|
bufPool.Put(e.data.Bytes())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -354,7 +379,7 @@ func (e *encryptWriter) Write(p []byte) (int, error) {
|
||||||
func EncryptTo(ks *MasterKeys, wr io.Writer) io.WriteCloser {
|
func EncryptTo(ks *MasterKeys, wr io.Writer) io.WriteCloser {
|
||||||
ew := &encryptWriter{
|
ew := &encryptWriter{
|
||||||
iv: generateRandomIV(),
|
iv: generateRandomIV(),
|
||||||
data: bytes.NewBuffer(GetChunkBuf("EncryptWriter")[:0]),
|
data: bytes.NewBuffer(getBuffer()[:0]),
|
||||||
key: ks,
|
key: ks,
|
||||||
origWr: wr,
|
origWr: wr,
|
||||||
}
|
}
|
||||||
|
@ -426,7 +451,7 @@ func (d *decryptReader) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeChunkBuf("decryptReader", d.buf)
|
freeBuffer(d.buf)
|
||||||
d.buf = nil
|
d.buf = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -438,7 +463,7 @@ func (d *decryptReader) Close() error {
|
||||||
// afterwards. If a MAC verification failure is observed, it is returned
|
// afterwards. If a MAC verification failure is observed, it is returned
|
||||||
// immediately.
|
// immediately.
|
||||||
func DecryptFrom(ks *MasterKeys, rd io.Reader) (io.ReadCloser, error) {
|
func DecryptFrom(ks *MasterKeys, rd io.Reader) (io.ReadCloser, error) {
|
||||||
ciphertext := GetChunkBuf("decryptReader")
|
ciphertext := getBuffer()
|
||||||
|
|
||||||
ciphertext = ciphertext[0:cap(ciphertext)]
|
ciphertext = ciphertext[0:cap(ciphertext)]
|
||||||
n, err := io.ReadFull(rd, ciphertext)
|
n, err := io.ReadFull(rd, ciphertext)
|
|
@ -1,4 +1,4 @@
|
||||||
package restic
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -98,24 +98,22 @@ func should_panic(f func()) (did_panic bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCrypto(t *testing.T) {
|
func TestCrypto(t *testing.T) {
|
||||||
r := &Key{}
|
|
||||||
|
|
||||||
for _, tv := range test_values {
|
for _, tv := range test_values {
|
||||||
// test encryption
|
// test encryption
|
||||||
r.master = &MasterKeys{
|
k := &MasterKeys{
|
||||||
Encrypt: tv.ekey,
|
Encrypt: tv.ekey,
|
||||||
Sign: tv.skey,
|
Sign: tv.skey,
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := make([]byte, maxCiphertextSize)
|
msg := make([]byte, 0, 8*1024*1024) // use 8MiB for now
|
||||||
n, err := Encrypt(r.master, msg, tv.plaintext)
|
n, err := Encrypt(k, msg, tv.plaintext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
msg = msg[:n]
|
msg = msg[:n]
|
||||||
|
|
||||||
// decrypt message
|
// decrypt message
|
||||||
_, err = Decrypt(r.master, []byte{}, msg)
|
_, err = Decrypt(k, []byte{}, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -123,12 +121,22 @@ func TestCrypto(t *testing.T) {
|
||||||
// change mac, this must fail
|
// change mac, this must fail
|
||||||
msg[len(msg)-8] ^= 0x23
|
msg[len(msg)-8] ^= 0x23
|
||||||
|
|
||||||
if _, err = Decrypt(r.master, []byte{}, msg); err != ErrUnauthenticated {
|
if _, err = Decrypt(k, []byte{}, msg); err != ErrUnauthenticated {
|
||||||
t.Fatal("wrong HMAC value not detected")
|
t.Fatal("wrong MAC value not detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset mac
|
||||||
|
msg[len(msg)-8] ^= 0x23
|
||||||
|
|
||||||
|
// tamper with message, this must fail
|
||||||
|
msg[16+5] ^= 0x85
|
||||||
|
|
||||||
|
if _, err = Decrypt(k, []byte{}, msg); err != ErrUnauthenticated {
|
||||||
|
t.Fatal("tampered message not detected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// test decryption
|
// test decryption
|
||||||
p, err := Decrypt(r.master, []byte{}, tv.ciphertext)
|
p, err := Decrypt(k, []byte{}, tv.ciphertext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
package restic_test
|
package crypto_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"flag"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -9,13 +10,14 @@ import (
|
||||||
|
|
||||||
"github.com/restic/restic"
|
"github.com/restic/restic"
|
||||||
"github.com/restic/restic/chunker"
|
"github.com/restic/restic/chunker"
|
||||||
|
"github.com/restic/restic/crypto"
|
||||||
. "github.com/restic/restic/test"
|
. "github.com/restic/restic/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var testLargeCrypto = flag.Bool("test.largecrypto", false, "also test crypto functions with large payloads")
|
||||||
|
|
||||||
func TestEncryptDecrypt(t *testing.T) {
|
func TestEncryptDecrypt(t *testing.T) {
|
||||||
s := setupBackend(t)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(t, s)
|
|
||||||
k := setupKey(t, s, testPassword)
|
|
||||||
|
|
||||||
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
||||||
if *testLargeCrypto {
|
if *testLargeCrypto {
|
||||||
|
@ -24,14 +26,14 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||||
|
|
||||||
for _, size := range tests {
|
for _, size := range tests {
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
_, err := io.ReadFull(randomReader(42, size), data)
|
_, err := io.ReadFull(RandomReader(42, size), data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
ciphertext := restic.GetChunkBuf("TestEncryptDecrypt")
|
ciphertext := restic.GetChunkBuf("TestEncryptDecrypt")
|
||||||
n, err := k.Encrypt(ciphertext, data)
|
n, err := crypto.Encrypt(k, ciphertext, data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
plaintext, err := k.Decrypt(nil, ciphertext[:n])
|
plaintext, err := crypto.Decrypt(k, nil, ciphertext[:n])
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext)
|
restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext)
|
||||||
|
@ -41,9 +43,7 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmallBuffer(t *testing.T) {
|
func TestSmallBuffer(t *testing.T) {
|
||||||
s := setupBackend(t)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(t, s)
|
|
||||||
k := setupKey(t, s, testPassword)
|
|
||||||
|
|
||||||
size := 600
|
size := 600
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
|
@ -54,9 +54,9 @@ func TestSmallBuffer(t *testing.T) {
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
ciphertext := make([]byte, size/2)
|
ciphertext := make([]byte, size/2)
|
||||||
_, err = k.Encrypt(ciphertext, data)
|
_, err = crypto.Encrypt(k, ciphertext, data)
|
||||||
// this must throw an error, since the target slice is too small
|
// this must throw an error, since the target slice is too small
|
||||||
Assert(t, err != nil && err == restic.ErrBufferTooSmall,
|
Assert(t, err != nil && err == crypto.ErrBufferTooSmall,
|
||||||
"expected restic.ErrBufferTooSmall, got %#v", err)
|
"expected restic.ErrBufferTooSmall, got %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +65,7 @@ func TestLargeEncrypt(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
s := setupBackend(t)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(t, s)
|
|
||||||
k := setupKey(t, s, testPassword)
|
|
||||||
|
|
||||||
for _, size := range []int{chunker.MaxSize, chunker.MaxSize + 1, chunker.MaxSize + 1<<20} {
|
for _, size := range []int{chunker.MaxSize, chunker.MaxSize + 1, chunker.MaxSize + 1<<20} {
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
|
@ -77,11 +75,11 @@ func TestLargeEncrypt(t *testing.T) {
|
||||||
_, err = io.ReadFull(f, data)
|
_, err = io.ReadFull(f, data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
ciphertext := make([]byte, size+restic.CiphertextExtension)
|
ciphertext := make([]byte, size+crypto.CiphertextExtension)
|
||||||
n, err := k.Encrypt(ciphertext, data)
|
n, err := crypto.Encrypt(k, ciphertext, data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
plaintext, err := k.Decrypt([]byte{}, ciphertext[:n])
|
plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext[:n])
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
Equals(t, plaintext, data)
|
Equals(t, plaintext, data)
|
||||||
|
@ -90,18 +88,16 @@ func TestLargeEncrypt(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkEncryptWriter(b *testing.B) {
|
func BenchmarkEncryptWriter(b *testing.B) {
|
||||||
size := 8 << 20 // 8MiB
|
size := 8 << 20 // 8MiB
|
||||||
rd := randomReader(23, size)
|
rd := RandomReader(23, size)
|
||||||
|
|
||||||
be := setupBackend(b)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(b, be)
|
|
||||||
k := setupKey(b, be, testPassword)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
rd.Seek(0, 0)
|
rd.Seek(0, 0)
|
||||||
wr := k.EncryptTo(ioutil.Discard)
|
wr := crypto.EncryptTo(k, ioutil.Discard)
|
||||||
_, err := io.Copy(wr, rd)
|
_, err := io.Copy(wr, rd)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
OK(b, wr.Close())
|
OK(b, wr.Close())
|
||||||
|
@ -112,31 +108,25 @@ func BenchmarkEncrypt(b *testing.B) {
|
||||||
size := 8 << 20 // 8MiB
|
size := 8 << 20 // 8MiB
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
|
|
||||||
be := setupBackend(b)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(b, be)
|
buf := make([]byte, len(data)+crypto.CiphertextExtension)
|
||||||
k := setupKey(b, be, testPassword)
|
|
||||||
|
|
||||||
buf := make([]byte, len(data)+restic.CiphertextExtension)
|
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := k.Encrypt(buf, data)
|
_, err := crypto.Encrypt(k, buf, data)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkDecryptReader(b *testing.B) {
|
func BenchmarkDecryptReader(b *testing.B) {
|
||||||
be := setupBackend(b)
|
|
||||||
defer teardownBackend(b, be)
|
|
||||||
k := setupKey(b, be, testPassword)
|
|
||||||
|
|
||||||
size := 8 << 20 // 8MiB
|
size := 8 << 20 // 8MiB
|
||||||
buf := get_random(23, size)
|
buf := Random(23, size)
|
||||||
|
k := crypto.GenerateRandomKeys()
|
||||||
|
|
||||||
ciphertext := make([]byte, len(buf)+restic.CiphertextExtension)
|
ciphertext := make([]byte, len(buf)+crypto.CiphertextExtension)
|
||||||
_, err := k.Encrypt(ciphertext, buf)
|
_, err := crypto.Encrypt(k, ciphertext, buf)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
|
|
||||||
rd := bytes.NewReader(ciphertext)
|
rd := bytes.NewReader(ciphertext)
|
||||||
|
@ -146,7 +136,7 @@ func BenchmarkDecryptReader(b *testing.B) {
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
rd.Seek(0, 0)
|
rd.Seek(0, 0)
|
||||||
decRd, err := k.DecryptFrom(rd)
|
decRd, err := crypto.DecryptFrom(k, rd)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
|
|
||||||
_, err = io.Copy(ioutil.Discard, decRd)
|
_, err = io.Copy(ioutil.Discard, decRd)
|
||||||
|
@ -155,12 +145,10 @@ func BenchmarkDecryptReader(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncryptDecryptReader(b *testing.B) {
|
func BenchmarkEncryptDecryptReader(b *testing.B) {
|
||||||
be := setupBackend(b)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(b, be)
|
|
||||||
k := setupKey(b, be, testPassword)
|
|
||||||
|
|
||||||
size := 8 << 20 // 8MiB
|
size := 8 << 20 // 8MiB
|
||||||
rd := randomReader(23, size)
|
rd := RandomReader(23, size)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
|
@ -169,12 +157,12 @@ func BenchmarkEncryptDecryptReader(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
rd.Seek(0, 0)
|
rd.Seek(0, 0)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
wr := k.EncryptTo(buf)
|
wr := crypto.EncryptTo(k, buf)
|
||||||
_, err := io.Copy(wr, rd)
|
_, err := io.Copy(wr, rd)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
OK(b, wr.Close())
|
OK(b, wr.Close())
|
||||||
|
|
||||||
r, err := k.DecryptFrom(buf)
|
r, err := crypto.DecryptFrom(k, buf)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
|
|
||||||
_, err = io.Copy(ioutil.Discard, r)
|
_, err = io.Copy(ioutil.Discard, r)
|
||||||
|
@ -188,31 +176,27 @@ func BenchmarkDecrypt(b *testing.B) {
|
||||||
size := 8 << 20 // 8MiB
|
size := 8 << 20 // 8MiB
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
|
|
||||||
s := setupBackend(b)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(b, s)
|
|
||||||
k := setupKey(b, s, testPassword)
|
|
||||||
|
|
||||||
ciphertext := restic.GetChunkBuf("BenchmarkDecrypt")
|
ciphertext := restic.GetChunkBuf("BenchmarkDecrypt")
|
||||||
defer restic.FreeChunkBuf("BenchmarkDecrypt", ciphertext)
|
defer restic.FreeChunkBuf("BenchmarkDecrypt", ciphertext)
|
||||||
plaintext := restic.GetChunkBuf("BenchmarkDecrypt")
|
plaintext := restic.GetChunkBuf("BenchmarkDecrypt")
|
||||||
defer restic.FreeChunkBuf("BenchmarkDecrypt", plaintext)
|
defer restic.FreeChunkBuf("BenchmarkDecrypt", plaintext)
|
||||||
|
|
||||||
n, err := k.Encrypt(ciphertext, data)
|
n, err := crypto.Encrypt(k, ciphertext, data)
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
b.SetBytes(int64(size))
|
b.SetBytes(int64(size))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
plaintext, err = k.Decrypt(plaintext, ciphertext[:n])
|
plaintext, err = crypto.Decrypt(k, plaintext, ciphertext[:n])
|
||||||
OK(b, err)
|
OK(b, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncryptStreamWriter(t *testing.T) {
|
func TestEncryptStreamWriter(t *testing.T) {
|
||||||
s := setupBackend(t)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(t, s)
|
|
||||||
k := setupKey(t, s, testPassword)
|
|
||||||
|
|
||||||
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
||||||
if *testLargeCrypto {
|
if *testLargeCrypto {
|
||||||
|
@ -221,23 +205,23 @@ func TestEncryptStreamWriter(t *testing.T) {
|
||||||
|
|
||||||
for _, size := range tests {
|
for _, size := range tests {
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
_, err := io.ReadFull(randomReader(42, size), data)
|
_, err := io.ReadFull(RandomReader(42, size), data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
ciphertext := bytes.NewBuffer(nil)
|
ciphertext := bytes.NewBuffer(nil)
|
||||||
wr := k.EncryptTo(ciphertext)
|
wr := crypto.EncryptTo(k, ciphertext)
|
||||||
|
|
||||||
_, err = io.Copy(wr, bytes.NewReader(data))
|
_, err = io.Copy(wr, bytes.NewReader(data))
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
OK(t, wr.Close())
|
OK(t, wr.Close())
|
||||||
|
|
||||||
l := len(data) + restic.CiphertextExtension
|
l := len(data) + crypto.CiphertextExtension
|
||||||
Assert(t, len(ciphertext.Bytes()) == l,
|
Assert(t, len(ciphertext.Bytes()) == l,
|
||||||
"wrong ciphertext length: expected %d, got %d",
|
"wrong ciphertext length: expected %d, got %d",
|
||||||
l, len(ciphertext.Bytes()))
|
l, len(ciphertext.Bytes()))
|
||||||
|
|
||||||
// decrypt with default function
|
// decrypt with default function
|
||||||
plaintext, err := k.Decrypt([]byte{}, ciphertext.Bytes())
|
plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext.Bytes())
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
Assert(t, bytes.Equal(data, plaintext),
|
Assert(t, bytes.Equal(data, plaintext),
|
||||||
"wrong plaintext after decryption: expected %02x, got %02x",
|
"wrong plaintext after decryption: expected %02x, got %02x",
|
||||||
|
@ -246,9 +230,7 @@ func TestEncryptStreamWriter(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecryptStreamReader(t *testing.T) {
|
func TestDecryptStreamReader(t *testing.T) {
|
||||||
s := setupBackend(t)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(t, s)
|
|
||||||
k := setupKey(t, s, testPassword)
|
|
||||||
|
|
||||||
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
||||||
if *testLargeCrypto {
|
if *testLargeCrypto {
|
||||||
|
@ -257,19 +239,19 @@ func TestDecryptStreamReader(t *testing.T) {
|
||||||
|
|
||||||
for _, size := range tests {
|
for _, size := range tests {
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
_, err := io.ReadFull(randomReader(42, size), data)
|
_, err := io.ReadFull(RandomReader(42, size), data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
ciphertext := make([]byte, size+restic.CiphertextExtension)
|
ciphertext := make([]byte, size+crypto.CiphertextExtension)
|
||||||
|
|
||||||
// encrypt with default function
|
// encrypt with default function
|
||||||
n, err := k.Encrypt(ciphertext, data)
|
n, err := crypto.Encrypt(k, ciphertext, data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
Assert(t, n == len(data)+restic.CiphertextExtension,
|
Assert(t, n == len(data)+crypto.CiphertextExtension,
|
||||||
"wrong number of bytes returned after encryption: expected %d, got %d",
|
"wrong number of bytes returned after encryption: expected %d, got %d",
|
||||||
len(data)+restic.CiphertextExtension, n)
|
len(data)+crypto.CiphertextExtension, n)
|
||||||
|
|
||||||
rd, err := k.DecryptFrom(bytes.NewReader(ciphertext))
|
rd, err := crypto.DecryptFrom(k, bytes.NewReader(ciphertext))
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
plaintext, err := ioutil.ReadAll(rd)
|
plaintext, err := ioutil.ReadAll(rd)
|
||||||
|
@ -282,9 +264,7 @@ func TestDecryptStreamReader(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncryptWriter(t *testing.T) {
|
func TestEncryptWriter(t *testing.T) {
|
||||||
s := setupBackend(t)
|
k := crypto.GenerateRandomKeys()
|
||||||
defer teardownBackend(t, s)
|
|
||||||
k := setupKey(t, s, testPassword)
|
|
||||||
|
|
||||||
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
tests := []int{5, 23, 2<<18 + 23, 1 << 20}
|
||||||
if *testLargeCrypto {
|
if *testLargeCrypto {
|
||||||
|
@ -293,11 +273,11 @@ func TestEncryptWriter(t *testing.T) {
|
||||||
|
|
||||||
for _, size := range tests {
|
for _, size := range tests {
|
||||||
data := make([]byte, size)
|
data := make([]byte, size)
|
||||||
_, err := io.ReadFull(randomReader(42, size), data)
|
_, err := io.ReadFull(RandomReader(42, size), data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
wr := k.EncryptTo(buf)
|
wr := crypto.EncryptTo(k, buf)
|
||||||
|
|
||||||
_, err = io.Copy(wr, bytes.NewReader(data))
|
_, err = io.Copy(wr, bytes.NewReader(data))
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
@ -305,13 +285,13 @@ func TestEncryptWriter(t *testing.T) {
|
||||||
|
|
||||||
ciphertext := buf.Bytes()
|
ciphertext := buf.Bytes()
|
||||||
|
|
||||||
l := len(data) + restic.CiphertextExtension
|
l := len(data) + crypto.CiphertextExtension
|
||||||
Assert(t, len(ciphertext) == l,
|
Assert(t, len(ciphertext) == l,
|
||||||
"wrong ciphertext length: expected %d, got %d",
|
"wrong ciphertext length: expected %d, got %d",
|
||||||
l, len(ciphertext))
|
l, len(ciphertext))
|
||||||
|
|
||||||
// decrypt with default function
|
// decrypt with default function
|
||||||
plaintext, err := k.Decrypt([]byte{}, ciphertext)
|
plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
Assert(t, bytes.Equal(data, plaintext),
|
Assert(t, bytes.Equal(data, plaintext),
|
||||||
"wrong plaintext after decryption: expected %02x, got %02x",
|
"wrong plaintext after decryption: expected %02x, got %02x",
|
90
key.go
90
key.go
|
@ -13,24 +13,13 @@ import (
|
||||||
|
|
||||||
"github.com/restic/restic/backend"
|
"github.com/restic/restic/backend"
|
||||||
"github.com/restic/restic/chunker"
|
"github.com/restic/restic/chunker"
|
||||||
|
"github.com/restic/restic/crypto"
|
||||||
"github.com/restic/restic/debug"
|
"github.com/restic/restic/debug"
|
||||||
|
|
||||||
"golang.org/x/crypto/poly1305"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// max size is 8MiB, defined in chunker
|
|
||||||
const macSize = poly1305.TagSize // Poly1305 size is 16 byte
|
|
||||||
const maxCiphertextSize = ivSize + chunker.MaxSize + macSize
|
|
||||||
const CiphertextExtension = ivSize + macSize
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrUnauthenticated is returned when ciphertext verification has failed.
|
|
||||||
ErrUnauthenticated = errors.New("ciphertext verification failed")
|
|
||||||
// ErrNoKeyFound is returned when no key for the repository could be decrypted.
|
// ErrNoKeyFound is returned when no key for the repository could be decrypted.
|
||||||
ErrNoKeyFound = errors.New("no key could be found")
|
ErrNoKeyFound = errors.New("no key could be found")
|
||||||
// ErrBufferTooSmall is returned when the destination slice is too small
|
|
||||||
// for the ciphertext.
|
|
||||||
ErrBufferTooSmall = errors.New("destination buffer too small")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: figure out scrypt values on the fly depending on the current
|
// TODO: figure out scrypt values on the fly depending on the current
|
||||||
|
@ -55,22 +44,12 @@ type Key struct {
|
||||||
Salt []byte `json:"salt"`
|
Salt []byte `json:"salt"`
|
||||||
Data []byte `json:"data"`
|
Data []byte `json:"data"`
|
||||||
|
|
||||||
user *MasterKeys
|
user *crypto.MasterKeys
|
||||||
master *MasterKeys
|
master *crypto.MasterKeys
|
||||||
|
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MasterKeys holds signing and encryption keys for a repository. It is stored
|
|
||||||
// encrypted and signed as a JSON data structure in the Data field of the Key
|
|
||||||
// structure. For the master key, the secret random polynomial used for content
|
|
||||||
// defined chunking is included.
|
|
||||||
type MasterKeys struct {
|
|
||||||
Sign MACKey `json:"sign"`
|
|
||||||
Encrypt AESKey `json:"encrypt"`
|
|
||||||
ChunkerPolynomial chunker.Pol `json:"chunker_polynomial,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey initializes a master key in the given backend and encrypts it with
|
// CreateKey initializes a master key in the given backend and encrypts it with
|
||||||
// the password.
|
// the password.
|
||||||
func CreateKey(s Server, password string) (*Key, error) {
|
func CreateKey(s Server, password string) (*Key, error) {
|
||||||
|
@ -90,19 +69,19 @@ func OpenKey(s Server, name string, password string) (*Key, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// derive user key
|
// derive user key
|
||||||
k.user, err = kdf(k, password)
|
k.user, err = crypto.KDF(k.N, k.R, k.P, k.Salt, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt master keys
|
// decrypt master keys
|
||||||
buf, err := k.DecryptUser([]byte{}, k.Data)
|
buf, err := crypto.Decrypt(k.user, []byte{}, k.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore json
|
// restore json
|
||||||
k.master = &MasterKeys{}
|
k.master = &crypto.MasterKeys{}
|
||||||
err = json.Unmarshal(buf, k.master)
|
err = json.Unmarshal(buf, k.master)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -190,14 +169,14 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// call KDF to derive user key
|
// call KDF to derive user key
|
||||||
newkey.user, err = kdf(newkey, password)
|
newkey.user, err = crypto.KDF(newkey.N, newkey.R, newkey.P, newkey.Salt, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if template == nil {
|
if template == nil {
|
||||||
// generate new random master keys
|
// generate new random master keys
|
||||||
newkey.master = generateRandomKeys()
|
newkey.master = crypto.GenerateRandomKeys()
|
||||||
// generate random polynomial for cdc
|
// generate random polynomial for cdc
|
||||||
p, err := chunker.RandomPolynomial()
|
p, err := chunker.RandomPolynomial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -218,7 +197,7 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
newkey.Data = GetChunkBuf("key")
|
newkey.Data = GetChunkBuf("key")
|
||||||
n, err = newkey.EncryptUser(newkey.Data, buf)
|
n, err = crypto.Encrypt(newkey.user, newkey.Data, buf)
|
||||||
newkey.Data = newkey.Data[:n]
|
newkey.Data = newkey.Data[:n]
|
||||||
|
|
||||||
// dump as json
|
// dump as json
|
||||||
|
@ -254,52 +233,23 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
|
||||||
return newkey, nil
|
return newkey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Key) newIV(buf []byte) error {
|
|
||||||
_, err := io.ReadFull(rand.Reader, buf[:ivSize])
|
|
||||||
buf = buf[:ivSize]
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptUser encrypts and signs data with the user key. Stored in ciphertext
|
|
||||||
// is IV || Ciphertext || MAC.
|
|
||||||
func (k *Key) EncryptUser(ciphertext, plaintext []byte) (int, error) {
|
|
||||||
return Encrypt(k.user, ciphertext, plaintext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt encrypts and signs data with the master key. Stored in ciphertext is
|
// Encrypt encrypts and signs data with the master key. Stored in ciphertext is
|
||||||
// IV || Ciphertext || MAC. Returns the ciphertext length.
|
// IV || Ciphertext || MAC. Returns the ciphertext length.
|
||||||
func (k *Key) Encrypt(ciphertext, plaintext []byte) (int, error) {
|
func (k *Key) Encrypt(ciphertext, plaintext []byte) (int, error) {
|
||||||
return Encrypt(k.master, ciphertext, plaintext)
|
return crypto.Encrypt(k.master, ciphertext, plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncryptTo encrypts and signs data with the master key. The returned
|
// EncryptTo encrypts and signs data with the master key. The returned
|
||||||
// io.Writer writes IV || Ciphertext || HMAC. For the hash function, SHA256 is
|
// io.Writer writes IV || Ciphertext || HMAC. For the hash function, SHA256 is
|
||||||
// used.
|
// used.
|
||||||
func (k *Key) EncryptTo(wr io.Writer) io.WriteCloser {
|
func (k *Key) EncryptTo(wr io.Writer) io.WriteCloser {
|
||||||
return EncryptTo(k.master, wr)
|
return crypto.EncryptTo(k.master, wr)
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptUserTo encrypts and signs data with the user key. The returned
|
|
||||||
// io.Writer writes IV || Ciphertext || HMAC. For the hash function, SHA256 is
|
|
||||||
// used.
|
|
||||||
func (k *Key) EncryptUserTo(wr io.Writer) io.WriteCloser {
|
|
||||||
return EncryptTo(k.user, wr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt verifes and decrypts the ciphertext with the master key. Ciphertext
|
// Decrypt verifes and decrypts the ciphertext with the master key. Ciphertext
|
||||||
// must be in the form IV || Ciphertext || MAC.
|
// must be in the form IV || Ciphertext || MAC.
|
||||||
func (k *Key) Decrypt(plaintext, ciphertext []byte) ([]byte, error) {
|
func (k *Key) Decrypt(plaintext, ciphertext []byte) ([]byte, error) {
|
||||||
return Decrypt(k.master, plaintext, ciphertext)
|
return crypto.Decrypt(k.master, plaintext, ciphertext)
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptUser verifes and decrypts the ciphertext with the user key. Ciphertext
|
|
||||||
// must be in the form IV || Ciphertext || MAC.
|
|
||||||
func (k *Key) DecryptUser(plaintext, ciphertext []byte) ([]byte, error) {
|
|
||||||
return Decrypt(k.user, plaintext, ciphertext)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptFrom verifies and decrypts the ciphertext read from rd and makes it
|
// DecryptFrom verifies and decrypts the ciphertext read from rd and makes it
|
||||||
|
@ -309,27 +259,17 @@ func (k *Key) DecryptUser(plaintext, ciphertext []byte) ([]byte, error) {
|
||||||
// afterwards. If an MAC verification failure is observed, it is returned
|
// afterwards. If an MAC verification failure is observed, it is returned
|
||||||
// immediately.
|
// immediately.
|
||||||
func (k *Key) DecryptFrom(rd io.Reader) (io.ReadCloser, error) {
|
func (k *Key) DecryptFrom(rd io.Reader) (io.ReadCloser, error) {
|
||||||
return DecryptFrom(k.master, rd)
|
return crypto.DecryptFrom(k.master, rd)
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptFrom verifies and decrypts the ciphertext read from rd with the user
|
|
||||||
// key and makes it available on the returned Reader. Ciphertext must be in the
|
|
||||||
// form IV || Ciphertext || MAC. In order to correctly verify the ciphertext,
|
|
||||||
// rd is drained, locally buffered and made available on the returned Reader
|
|
||||||
// afterwards. If an MAC verification failure is observed, it is returned
|
|
||||||
// immediately.
|
|
||||||
func (k *Key) DecryptUserFrom(rd io.Reader) (io.ReadCloser, error) {
|
|
||||||
return DecryptFrom(k.user, rd)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Master() returns the master keys for this repository. Only included for
|
// Master() returns the master keys for this repository. Only included for
|
||||||
// debug purposes.
|
// debug purposes.
|
||||||
func (k *Key) Master() *MasterKeys {
|
func (k *Key) Master() *crypto.MasterKeys {
|
||||||
return k.master
|
return k.master
|
||||||
}
|
}
|
||||||
|
|
||||||
// User() returns the user keys for this key. Only included for debug purposes.
|
// User() returns the user keys for this key. Only included for debug purposes.
|
||||||
func (k *Key) User() *MasterKeys {
|
func (k *Key) User() *crypto.MasterKeys {
|
||||||
return k.user
|
return k.user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ import (
|
||||||
|
|
||||||
var testPassword = "foobar"
|
var testPassword = "foobar"
|
||||||
var testCleanup = flag.Bool("test.cleanup", true, "clean up after running tests (remove local backend directory with all content)")
|
var testCleanup = flag.Bool("test.cleanup", true, "clean up after running tests (remove local backend directory with all content)")
|
||||||
var testLargeCrypto = flag.Bool("test.largecrypto", false, "also test crypto functions with large payloads")
|
|
||||||
var testTempDir = flag.String("test.tempdir", "", "use this directory for temporary storage (default: system temp dir)")
|
var testTempDir = flag.String("test.tempdir", "", "use this directory for temporary storage (default: system temp dir)")
|
||||||
|
|
||||||
func setupBackend(t testing.TB) restic.Server {
|
func setupBackend(t testing.TB) restic.Server {
|
||||||
|
|
3
pools.go
3
pools.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/restic/restic/chunker"
|
"github.com/restic/restic/chunker"
|
||||||
|
"github.com/restic/restic/crypto"
|
||||||
"github.com/restic/restic/debug"
|
"github.com/restic/restic/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +21,8 @@ type poolStats struct {
|
||||||
max int
|
max int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxCiphertextSize = crypto.CiphertextExtension + chunker.MaxSize
|
||||||
|
|
||||||
func (s *poolStats) Get(k string) {
|
func (s *poolStats) Get(k string) {
|
||||||
s.m.Lock()
|
s.m.Lock()
|
||||||
defer s.m.Unlock()
|
defer s.m.Unlock()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/restic/restic/backend"
|
"github.com/restic/restic/backend"
|
||||||
"github.com/restic/restic/chunker"
|
"github.com/restic/restic/chunker"
|
||||||
|
"github.com/restic/restic/crypto"
|
||||||
"github.com/restic/restic/debug"
|
"github.com/restic/restic/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -158,11 +159,11 @@ func (s Server) Save(t backend.Type, data []byte, id backend.ID) (Blob, error) {
|
||||||
var ciphertext []byte
|
var ciphertext []byte
|
||||||
|
|
||||||
// if the data is small enough, use a slice from the pool
|
// if the data is small enough, use a slice from the pool
|
||||||
if len(data) <= maxCiphertextSize-ivSize-macSize {
|
if len(data) <= maxCiphertextSize-crypto.CiphertextExtension {
|
||||||
ciphertext = GetChunkBuf("ch.Save()")
|
ciphertext = GetChunkBuf("ch.Save()")
|
||||||
defer FreeChunkBuf("ch.Save()", ciphertext)
|
defer FreeChunkBuf("ch.Save()", ciphertext)
|
||||||
} else {
|
} else {
|
||||||
l := len(data) + ivSize + macSize
|
l := len(data) + crypto.CiphertextExtension
|
||||||
|
|
||||||
debug.Log("Server.Save", "create large slice of %d bytes for ciphertext", l)
|
debug.Log("Server.Save", "create large slice of %d bytes for ciphertext", l)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package test_helper
|
package test_helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -45,3 +47,21 @@ func Str2ID(s string) backend.ID {
|
||||||
|
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Random returns size bytes of pseudo-random data derived from the seed.
|
||||||
|
func Random(seed, count int) []byte {
|
||||||
|
buf := make([]byte, count)
|
||||||
|
|
||||||
|
rnd := rand.New(rand.NewSource(int64(seed)))
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
buf[i] = byte(rnd.Uint32())
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomReader returns a reader that returns size bytes of pseudo-random data
|
||||||
|
// derived from the seed.
|
||||||
|
func RandomReader(seed, size int) *bytes.Reader {
|
||||||
|
return bytes.NewReader(Random(seed, size))
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue