mirror of
https://github.com/octoleo/restic.git
synced 2024-11-02 11:46:36 +00:00
Merge pull request #1051 from restic/refactor-crypto
crypto: Make Encrypt/Decrypt a method of *Key
This commit is contained in:
commit
9583dc820f
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -20,6 +20,8 @@ const (
|
||||
ivSize = aes.BlockSize
|
||||
|
||||
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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user