2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-13 14:22:23 +00:00

crypto: Make Encrypt/Decrypt a method of *Key

This commit is contained in:
Alexander Neumann 2017-06-19 21:12:53 +02:00
parent b0fb95dfc9
commit f960831f10
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, Assert(b, uint(len(chunk.Data)) == chunk.Length,
"invalid length: got %d, expected %d", 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) OK(b, err)
} }
} }
@ -76,7 +76,7 @@ func benchmarkChunkEncryptP(b *testing.PB, buf []byte, rd Rdr, key *crypto.Key)
} }
// reduce length of chunkBuf // 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/hashing"
"restic" "restic"
"restic/crypto"
"restic/debug" "restic/debug"
"restic/pack" "restic/pack"
"restic/repository" "restic/repository"
@ -725,7 +724,7 @@ func checkPack(ctx context.Context, r restic.Repository, id restic.ID) error {
continue continue
} }
n, err := crypto.Decrypt(r.Key(), buf, buf) n, err := r.Key().Decrypt(buf, buf)
if err != nil { if err != nil {
debug.Log(" error decrypting blob %v: %v", blob.ID.Str(), err) debug.Log(" error decrypting blob %v: %v", blob.ID.Str(), err)
errs = append(errs, errors.Errorf("blob %v: %v", i, err)) errs = append(errs, errors.Errorf("blob %v: %v", i, err))

View File

@ -19,7 +19,9 @@ const (
macKeySize = macKeySizeK + macKeySizeR // for Poly1305-AES128 macKeySize = macKeySizeK + macKeySizeR // for Poly1305-AES128
ivSize = aes.BlockSize 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 Extension = ivSize + macSize
) )
@ -32,11 +34,14 @@ var (
// encrypted and authenticated as a JSON data structure in the Data field of the Key // encrypted and authenticated as a JSON data structure in the Data field of the Key
// structure. // structure.
type Key struct { type Key struct {
MAC MACKey `json:"mac"` MACKey `json:"mac"`
Encrypt EncryptionKey `json:"encrypt"` EncryptionKey `json:"encrypt"`
} }
// EncryptionKey is key used for encryption
type EncryptionKey [32]byte type EncryptionKey [32]byte
// MACKey is used to sign (authenticate) data.
type MACKey struct { type MACKey struct {
K [16]byte // for AES-128 K [16]byte // for AES-128
R [16]byte // for Poly1305 R [16]byte // for Poly1305
@ -123,22 +128,22 @@ func poly1305Verify(msg []byte, nonce []byte, key *MACKey, mac []byte) bool {
func NewRandomKey() *Key { func NewRandomKey() *Key {
k := &Key{} k := &Key{}
n, err := rand.Read(k.Encrypt[:]) n, err := rand.Read(k.EncryptionKey[:])
if n != aesKeySize || err != nil { if n != aesKeySize || err != nil {
panic("unable to read enough random bytes for encryption key") 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 { if n != macKeySizeK || err != nil {
panic("unable to read enough random bytes for MAC encryption key") 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 { if n != macKeySizeR || err != nil {
panic("unable to read enough random bytes for MAC key") panic("unable to read enough random bytes for MAC key")
} }
maskKey(&k.MAC) maskKey(&k.MACKey)
return k return k
} }
@ -156,10 +161,12 @@ type jsonMACKey struct {
R []byte `json:"r"` R []byte `json:"r"`
} }
// MarshalJSON converts the MACKey to JSON.
func (m *MACKey) MarshalJSON() ([]byte, error) { func (m *MACKey) MarshalJSON() ([]byte, error) {
return json.Marshal(jsonMACKey{K: m.K[:], R: m.R[:]}) 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 { func (m *MACKey) UnmarshalJSON(data []byte) error {
j := jsonMACKey{} j := jsonMACKey{}
err := json.Unmarshal(data, &j) err := json.Unmarshal(data, &j)
@ -194,10 +201,12 @@ func (m *MACKey) Valid() bool {
return false return false
} }
// MarshalJSON converts the EncryptionKey to JSON.
func (k *EncryptionKey) MarshalJSON() ([]byte, error) { func (k *EncryptionKey) MarshalJSON() ([]byte, error) {
return json.Marshal(k[:]) return json.Marshal(k[:])
} }
// UnmarshalJSON fills the key k with data from the JSON representation.
func (k *EncryptionKey) UnmarshalJSON(data []byte) error { func (k *EncryptionKey) UnmarshalJSON(data []byte) error {
d := make([]byte, aesKeySize) d := make([]byte, aesKeySize)
err := json.Unmarshal(data, &d) 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 // MAC. Encrypt returns the new ciphertext slice, which is extended when
// necessary. ciphertext and plaintext may not point to (exactly) the same // necessary. ciphertext and plaintext may not point to (exactly) the same
// slice or non-intersecting slices. // slice or non-intersecting slices.
func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) { func (k *Key) Encrypt(ciphertext []byte, plaintext []byte) ([]byte, error) {
if !ks.Valid() { if !k.Valid() {
return nil, errors.New("invalid key") return nil, errors.New("invalid key")
} }
@ -248,7 +257,7 @@ func Encrypt(ks *Key, ciphertext []byte, plaintext []byte) ([]byte, error) {
} }
iv := newIV() iv := newIV()
c, err := aes.NewCipher(ks.Encrypt[:]) c, err := aes.NewCipher(k.EncryptionKey[:])
if err != nil { if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err)) 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 // truncate to only cover iv and actual ciphertext
ciphertext = ciphertext[:ivSize+len(plaintext)] 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...) ciphertext = append(ciphertext, mac...)
return ciphertext, nil 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 // Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form
// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the // IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the
// same slice. // same slice.
func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) { func (k *Key) Decrypt(plaintext []byte, ciphertextWithMac []byte) (int, error) {
if !ks.Valid() { if !k.Valid() {
return 0, errors.New("invalid key") 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:] ciphertextWithIV, mac := ciphertextWithMac[:l], ciphertextWithMac[l:]
// verify mac // verify mac
if !poly1305Verify(ciphertextWithIV[ivSize:], ciphertextWithIV[:ivSize], &ks.MAC, mac) { if !poly1305Verify(ciphertextWithIV[ivSize:], ciphertextWithIV[:ivSize], &k.MACKey, mac) {
return 0, ErrUnauthenticated return 0, ErrUnauthenticated
} }
@ -303,7 +312,7 @@ func Decrypt(ks *Key, plaintext []byte, ciphertextWithMac []byte) (int, error) {
} }
// decrypt data // decrypt data
c, err := aes.NewCipher(ks.Encrypt[:]) c, err := aes.NewCipher(k.EncryptionKey[:])
if err != nil { if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err)) 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. // Valid tests if the key is valid.
func (k *Key) Valid() bool { 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 { for _, tv := range testValues {
// test encryption // test encryption
k := &Key{ k := &Key{
Encrypt: tv.ekey, EncryptionKey: tv.ekey,
MAC: tv.skey, MACKey: tv.skey,
} }
msg, err := Encrypt(k, msg, tv.plaintext) msg, err := k.Encrypt(msg, tv.plaintext)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// decrypt message // decrypt message
buf := make([]byte, len(tv.plaintext)) buf := make([]byte, len(tv.plaintext))
n, err := Decrypt(k, buf, msg) n, err := k.Decrypt(buf, msg)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -110,7 +110,7 @@ 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(k, buf, msg); err != ErrUnauthenticated { if _, err = k.Decrypt(buf, msg); err != ErrUnauthenticated {
t.Fatal("wrong MAC value not detected") t.Fatal("wrong MAC value not detected")
} }
@ -120,13 +120,13 @@ func TestCrypto(t *testing.T) {
// tamper with message, this must fail // tamper with message, this must fail
msg[16+5] ^= 0x85 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") t.Fatal("tampered message not detected")
} }
// test decryption // test decryption
p := make([]byte, len(tv.ciphertext)) p := make([]byte, len(tv.ciphertext))
n, err = Decrypt(k, p, tv.ciphertext) n, err = k.Decrypt(p, tv.ciphertext)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

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

View File

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

View File

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

View File

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