Merge pull request #1051 from restic/refactor-crypto

crypto: Make Encrypt/Decrypt a method of *Key
This commit is contained in:
Alexander Neumann 2017-06-21 19:26:11 +02:00
commit 9583dc820f
10 changed files with 58 additions and 51 deletions

View file

@ -43,7 +43,7 @@ func benchmarkChunkEncrypt(b testing.TB, buf, buf2 []byte, rd Rdr, key *crypto.K
Assert(b, uint(len(chunk.Data)) == chunk.Length,
"invalid length: got %d, expected %d", len(chunk.Data), chunk.Length)
_, err = crypto.Encrypt(key, buf2, chunk.Data)
_, err = key.Encrypt(buf2, chunk.Data)
OK(b, err)
}
}
@ -76,7 +76,7 @@ func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *crypto.Key)
}
// reduce length of chunkBuf
crypto.Encrypt(key, chunk.Data, chunk.Data)
key.Encrypt(chunk.Data, chunk.Data)
}
}

View file

@ -13,7 +13,6 @@ import (
"restic/hashing"
"restic"
"restic/crypto"
"restic/debug"
"restic/pack"
"restic/repository"
@ -725,7 +724,7 @@ func checkPack(ctx context.Context, r restic.Repository, id restic.ID) error {
continue
}
n, err := crypto.Decrypt(r.Key(), buf, buf)
n, err := r.Key().Decrypt(buf, buf)
if err != nil {
debug.Log(" error decrypting blob %v: %v", blob.ID.Str(), err)
errs = append(errs, errors.Errorf("blob %v: %v", i, err))

View file

@ -19,7 +19,9 @@ const (
macKeySize = macKeySizeK + macKeySizeR // for Poly1305-AES128
ivSize = aes.BlockSize
macSize = poly1305.TagSize
macSize = poly1305.TagSize
// Extension is the number of bytes a plaintext is enlarged by encrypting it.
Extension = ivSize + macSize
)
@ -32,11 +34,14 @@ var (
// encrypted and authenticated as a JSON data structure in the Data field of the Key
// structure.
type Key struct {
MAC MACKey `json:"mac"`
Encrypt EncryptionKey `json:"encrypt"`
MACKey `json:"mac"`
EncryptionKey `json:"encrypt"`
}
// EncryptionKey is key used for encryption
type EncryptionKey [32]byte
// MACKey is used to sign (authenticate) data.
type MACKey struct {
K [16]byte // for AES-128
R [16]byte // for Poly1305
@ -123,22 +128,22 @@ func poly1305Verify(msg []byte, nonce []byte, key *MACKey, mac []byte) bool {
func NewRandomKey() *Key {
k := &Key{}
n, err := rand.Read(k.Encrypt[:])
n, err := rand.Read(k.EncryptionKey[:])
if n != aesKeySize || err != nil {
panic("unable to read enough random bytes for encryption key")
}
n, err = rand.Read(k.MAC.K[:])
n, err = rand.Read(k.MACKey.K[:])
if n != macKeySizeK || err != nil {
panic("unable to read enough random bytes for MAC encryption key")
}
n, err = rand.Read(k.MAC.R[:])
n, err = rand.Read(k.MACKey.R[:])
if n != macKeySizeR || err != nil {
panic("unable to read enough random bytes for MAC key")
}
maskKey(&k.MAC)
maskKey(&k.MACKey)
return k
}
@ -156,10 +161,12 @@ type jsonMACKey struct {
R []byte `json:"r"`
}
// MarshalJSON converts the MACKey to JSON.
func (m *MACKey) MarshalJSON() ([]byte, error) {
return json.Marshal(jsonMACKey{K: m.K[:], R: m.R[:]})
}
// UnmarshalJSON fills the key m with data from the JSON representation.
func (m *MACKey) UnmarshalJSON(data []byte) error {
j := jsonMACKey{}
err := json.Unmarshal(data, &j)
@ -194,10 +201,12 @@ func (m *MACKey) Valid() bool {
return false
}
// MarshalJSON converts the EncryptionKey to JSON.
func (k *EncryptionKey) MarshalJSON() ([]byte, error) {
return json.Marshal(k[:])
}
// UnmarshalJSON fills the key k with data from the JSON representation.
func (k *EncryptionKey) UnmarshalJSON(data []byte) error {
d := make([]byte, aesKeySize)
err := json.Unmarshal(data, &d)
@ -228,8 +237,8 @@ var ErrInvalidCiphertext = errors.New("invalid ciphertext, same slice used for p
// MAC. Encrypt returns the new ciphertext slice, which is extended when
// necessary. ciphertext and plaintext may not point to (exactly) the same
// slice or non-intersecting slices.
func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) {
if !ks.Valid() {
func (k *Key) Encrypt(ciphertext []byte, plaintext []byte) ([]byte, error) {
if !k.Valid() {
return nil, errors.New("invalid key")
}
@ -248,7 +257,7 @@ func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) {
}
iv := newIV()
c, err := aes.NewCipher(ks.Encrypt[:])
c, err := aes.NewCipher(k.EncryptionKey[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
@ -261,7 +270,7 @@ func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) {
// truncate to only cover iv and actual ciphertext
ciphertext = ciphertext[:ivSize+len(plaintext)]
mac := poly1305MAC(ciphertext[ivSize:], ciphertext[:ivSize], &ks.MAC)
mac := poly1305MAC(ciphertext[ivSize:], ciphertext[:ivSize], &k.MACKey)
ciphertext = append(ciphertext, mac...)
return ciphertext, nil
@ -270,8 +279,8 @@ func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) {
// Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form
// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the
// same slice.
func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) {
if !ks.Valid() {
func (k *Key) Decrypt(plaintext []byte, ciphertextWithMac []byte) (int, error) {
if !k.Valid() {
return 0, errors.New("invalid key")
}
@ -291,7 +300,7 @@ func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) {
ciphertextWithIV, mac := ciphertextWithMac[:l], ciphertextWithMac[l:]
// verify mac
if !poly1305Verify(ciphertextWithIV[ivSize:], ciphertextWithIV[:ivSize], &ks.MAC, mac) {
if !poly1305Verify(ciphertextWithIV[ivSize:], ciphertextWithIV[:ivSize], &k.MACKey, mac) {
return 0, ErrUnauthenticated
}
@ -303,7 +312,7 @@ func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) {
}
// decrypt data
c, err := aes.NewCipher(ks.Encrypt[:])
c, err := aes.NewCipher(k.EncryptionKey[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
@ -318,5 +327,5 @@ func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) {
// Valid tests if the key is valid.
func (k *Key) Valid() bool {
return k.Encrypt.Valid() && k.MAC.Valid()
return k.EncryptionKey.Valid() && k.MACKey.Valid()
}

View file

@ -90,18 +90,18 @@ func TestCrypto(t *testing.T) {
for _, tv := range testValues {
// test encryption
k := &Key{
Encrypt: tv.ekey,
MAC: tv.skey,
EncryptionKey: tv.ekey,
MACKey: tv.skey,
}
msg, err := Encrypt(k, msg, tv.plaintext)
msg, err := k.Encrypt(msg, tv.plaintext)
if err != nil {
t.Fatal(err)
}
// decrypt message
buf := make([]byte, len(tv.plaintext))
n, err := Decrypt(k, buf, msg)
n, err := k.Decrypt(buf, msg)
if err != nil {
t.Fatal(err)
}
@ -110,7 +110,7 @@ func TestCrypto(t *testing.T) {
// change mac, this must fail
msg[len(msg)-8] ^= 0x23
if _, err = Decrypt(k, buf, msg); err != ErrUnauthenticated {
if _, err = k.Decrypt(buf, msg); err != ErrUnauthenticated {
t.Fatal("wrong MAC value not detected")
}
@ -120,13 +120,13 @@ func TestCrypto(t *testing.T) {
// tamper with message, this must fail
msg[16+5] ^= 0x85
if _, err = Decrypt(k, buf, msg); err != ErrUnauthenticated {
if _, err = k.Decrypt(buf, msg); err != ErrUnauthenticated {
t.Fatal("tampered message not detected")
}
// test decryption
p := make([]byte, len(tv.ciphertext))
n, err = Decrypt(k, p, tv.ciphertext)
n, err = k.Decrypt(p, tv.ciphertext)
if err != nil {
t.Fatal(err)
}

View file

@ -26,14 +26,14 @@ func TestEncryptDecrypt(t *testing.T) {
data := Random(42, size)
buf := make([]byte, size+crypto.Extension)
ciphertext, err := crypto.Encrypt(k, buf, data)
ciphertext, err := k.Encrypt(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 := make([]byte, len(ciphertext))
n, err := crypto.Decrypt(k, plaintext, ciphertext)
n, err := k.Decrypt(plaintext, ciphertext)
OK(t, err)
plaintext = plaintext[:n]
Assert(t, len(plaintext) == len(data),
@ -53,7 +53,7 @@ func TestSmallBuffer(t *testing.T) {
OK(t, err)
ciphertext := make([]byte, size/2)
ciphertext, err = crypto.Encrypt(k, ciphertext, data)
ciphertext, err = k.Encrypt(ciphertext, data)
// this must extend the slice
Assert(t, cap(ciphertext) > size/2,
"expected extended slice, but capacity is only %d bytes",
@ -61,7 +61,7 @@ func TestSmallBuffer(t *testing.T) {
// check for the correct plaintext
plaintext := make([]byte, len(ciphertext))
n, err := crypto.Decrypt(k, plaintext, ciphertext)
n, err := k.Decrypt(plaintext, ciphertext)
OK(t, err)
plaintext = plaintext[:n]
Assert(t, bytes.Equal(plaintext, data),
@ -78,11 +78,11 @@ func TestSameBuffer(t *testing.T) {
ciphertext := make([]byte, 0, size+crypto.Extension)
ciphertext, err = crypto.Encrypt(k, ciphertext, data)
ciphertext, err = k.Encrypt(ciphertext, data)
OK(t, err)
// use the same buffer for decryption
n, err := crypto.Decrypt(k, ciphertext, ciphertext)
n, err := k.Decrypt(ciphertext, ciphertext)
OK(t, err)
ciphertext = ciphertext[:n]
Assert(t, bytes.Equal(ciphertext, data),
@ -94,7 +94,7 @@ func TestCornerCases(t *testing.T) {
// 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)
c, err := k.Encrypt(nil, nil)
OK(t, err)
Assert(t, len(c) == crypto.Extension,
@ -102,12 +102,12 @@ func TestCornerCases(t *testing.T) {
len(c))
// this should decrypt to nil
n, err := crypto.Decrypt(k, nil, c)
n, err := k.Decrypt(nil, c)
OK(t, err)
Equals(t, 0, n)
// test encryption for same slice, this should return an error
_, err = crypto.Encrypt(k, c, c)
_, err = k.Encrypt(c, c)
Equals(t, crypto.ErrInvalidCiphertext, err)
}
@ -123,10 +123,10 @@ func TestLargeEncrypt(t *testing.T) {
_, err := io.ReadFull(rand.Reader, data)
OK(t, err)
ciphertext, err := crypto.Encrypt(k, make([]byte, size+crypto.Extension), data)
ciphertext, err := k.Encrypt(make([]byte, size+crypto.Extension), data)
OK(t, err)
plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext)
plaintext, err := k.Decrypt([]byte{}, ciphertext)
OK(t, err)
Equals(t, plaintext, data)
@ -144,7 +144,7 @@ func BenchmarkEncrypt(b *testing.B) {
b.SetBytes(int64(size))
for i := 0; i < b.N; i++ {
_, err := crypto.Encrypt(k, buf, data)
_, err := k.Encrypt(buf, data)
OK(b, err)
}
}
@ -158,14 +158,14 @@ func BenchmarkDecrypt(b *testing.B) {
plaintext := make([]byte, size)
ciphertext := make([]byte, size+crypto.Extension)
ciphertext, err := crypto.Encrypt(k, ciphertext, data)
ciphertext, err := k.Encrypt(ciphertext, data)
OK(b, err)
b.ResetTimer()
b.SetBytes(int64(size))
for i := 0; i < b.N; i++ {
_, err = crypto.Decrypt(k, plaintext, ciphertext)
_, err = k.Decrypt(plaintext, ciphertext)
OK(b, err)
}
}

View file

@ -81,10 +81,10 @@ func KDF(p KDFParams, salt []byte, password string) (*Key, error) {
}
// first 32 byte of scrypt output is the encryption key
copy(derKeys.Encrypt[:], scryptKeys[:aesKeySize])
copy(derKeys.EncryptionKey[:], scryptKeys[:aesKeySize])
// next 32 byte of scrypt output is the mac key, in the form k||r
macKeyFromSlice(&derKeys.MAC, scryptKeys[aesKeySize:])
macKeyFromSlice(&derKeys.MACKey, scryptKeys[aesKeySize:])
return derKeys, nil
}

View file

@ -75,7 +75,7 @@ func (p *Packer) Finalize() (uint, error) {
return 0, err
}
encryptedHeader, err := crypto.Encrypt(p.k, nil, hdrBuf.Bytes())
encryptedHeader, err := p.k.Encrypt(nil, hdrBuf.Bytes())
if err != nil {
return 0, err
}
@ -268,7 +268,7 @@ func List(k *crypto.Key, rd io.ReaderAt, size int64) (entries []restic.Blob, err
return nil, err
}
n, err := crypto.Decrypt(k, buf, buf)
n, err := k.Decrypt(buf, buf)
if err != nil {
return nil, err
}

View file

@ -88,7 +88,7 @@ func OpenKey(ctx context.Context, s *Repository, name string, password string) (
// decrypt master keys
buf := make([]byte, len(k.Data))
n, err := crypto.Decrypt(k.user, buf, k.Data)
n, err := k.user.Decrypt(buf, k.Data)
if err != nil {
return nil, err
}
@ -218,7 +218,7 @@ func AddKey(ctx context.Context, s *Repository, password string, template *crypt
return nil, errors.Wrap(err, "Marshal")
}
newkey.Data, err = crypto.Encrypt(newkey.user, nil, buf)
newkey.Data, err = newkey.user.Encrypt(nil, buf)
// dump as json
buf, err = json.Marshal(newkey)

View file

@ -5,7 +5,6 @@ import (
"crypto/sha256"
"io"
"restic"
"restic/crypto"
"restic/debug"
"restic/fs"
"restic/hashing"
@ -88,7 +87,7 @@ func Repack(ctx context.Context, repo restic.Repository, packs restic.IDSet, kee
h, tempfile.Name(), len(buf), n)
}
n, err = crypto.Decrypt(repo.Key(), buf, buf)
n, err = repo.Key().Decrypt(buf, buf)
if err != nil {
return nil, err
}

View file

@ -420,7 +420,7 @@ func (r *Repository) decryptTo(plaintext, ciphertext []byte) (int, error) {
return 0, errors.New("key for repository not set")
}
return crypto.Decrypt(r.key, plaintext, ciphertext)
return r.key.Decrypt(plaintext, ciphertext)
}
// Encrypt encrypts and authenticates the plaintext and saves the result in
@ -430,7 +430,7 @@ func (r *Repository) Encrypt(ciphertext, plaintext []byte) ([]byte, error) {
return nil, errors.New("key for repository not set")
}
return crypto.Encrypt(r.key, ciphertext, plaintext)
return r.key.Encrypt(ciphertext, plaintext)
}
// Key returns the current master key.