2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-26 14:56:29 +00:00

crypto: Match signature of Encrypt() to Decrypt()

This commit is contained in:
Alexander Neumann 2015-04-12 20:58:41 +02:00
parent 7e6acfe44d
commit f8e1043ad3
5 changed files with 63 additions and 35 deletions

View File

@ -205,29 +205,33 @@ func (k *EncryptionKey) UnmarshalJSON(data []byte) error {
}
// Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext ||
// MAC. Encrypt returns the ciphertext's length.
func Encrypt(ks *Key, ciphertext, plaintext []byte) (int, error) {
if cap(ciphertext) < len(plaintext)+ivSize+macSize {
return 0, ErrBufferTooSmall
// MAC. Encrypt returns the new ciphertext slice, which is extended when
// necessary. ciphertext and plaintext may point to the same slice.
func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) {
// extend ciphertext slice if necessary
if cap(ciphertext) < len(plaintext)+Extension {
ext := len(plaintext) + Extension - cap(ciphertext)
n := len(ciphertext)
ciphertext = append(ciphertext, make([]byte, ext)...)
ciphertext = ciphertext[:n]
}
iv := newIV()
copy(ciphertext, iv[:])
c, err := aes.NewCipher(ks.Encrypt[:])
if err != nil {
panic(fmt.Sprintf("unable to create cipher: %v", err))
}
e := cipher.NewCTR(c, ciphertext[:ivSize])
e := cipher.NewCTR(c, iv[:])
e.XORKeyStream(ciphertext[ivSize:cap(ciphertext)], plaintext)
copy(ciphertext, iv[:])
ciphertext = ciphertext[:ivSize+len(plaintext)]
mac := poly1305_sign(ciphertext[ivSize:], ciphertext[:ivSize], &ks.Sign)
ciphertext = append(ciphertext, mac...)
return len(ciphertext), nil
return ciphertext, nil
}
// Decrypt verifies and decrypts the ciphertext. Ciphertext must be in the form

View File

@ -98,6 +98,7 @@ func should_panic(f func()) (did_panic bool) {
}
func TestCrypto(t *testing.T) {
msg := make([]byte, 0, 8*1024*1024) // use 8MiB for now
for _, tv := range test_values {
// test encryption
k := &Key{
@ -105,12 +106,10 @@ func TestCrypto(t *testing.T) {
Sign: tv.skey,
}
msg := make([]byte, 0, 8*1024*1024) // use 8MiB for now
n, err := Encrypt(k, msg, tv.plaintext)
msg, err := Encrypt(k, msg, tv.plaintext)
if err != nil {
t.Fatal(err)
}
msg = msg[:n]
// decrypt message
_, err = Decrypt(k, []byte{}, msg)

View File

@ -29,11 +29,10 @@ func TestEncryptDecrypt(t *testing.T) {
_, err := io.ReadFull(RandomReader(42, size), data)
OK(t, err)
ciphertext := restic.GetChunkBuf("TestEncryptDecrypt")
n, err := crypto.Encrypt(k, ciphertext, data)
ciphertext, err := crypto.Encrypt(k, restic.GetChunkBuf("TestEncryptDecrypt"), data)
OK(t, err)
plaintext, err := crypto.Decrypt(k, nil, ciphertext[:n])
plaintext, err := crypto.Decrypt(k, nil, ciphertext)
OK(t, err)
restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext)
@ -54,10 +53,40 @@ func TestSmallBuffer(t *testing.T) {
OK(t, err)
ciphertext := make([]byte, size/2)
_, err = crypto.Encrypt(k, ciphertext, data)
ciphertext, err = crypto.Encrypt(k, ciphertext, data)
// this must throw an error, since the target slice is too small
Assert(t, err != nil && err == crypto.ErrBufferTooSmall,
"expected restic.ErrBufferTooSmall, got %#v", err)
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 TestSameBuffer(t *testing.T) {
k := crypto.NewKey()
size := 600
data := make([]byte, size)
f, err := os.Open("/dev/urandom")
OK(t, err)
_, err = io.ReadFull(f, data)
OK(t, err)
ciphertext := make([]byte, size)
copy(ciphertext, data)
ciphertext, err = crypto.Encrypt(k, ciphertext, ciphertext)
OK(t, err)
ciphertext, err = crypto.Decrypt(k, ciphertext, ciphertext)
OK(t, err)
Assert(t, bytes.Equal(ciphertext, data),
"wrong plaintext returned")
}
func TestLargeEncrypt(t *testing.T) {
@ -75,11 +104,10 @@ func TestLargeEncrypt(t *testing.T) {
_, err = io.ReadFull(f, data)
OK(t, err)
ciphertext := make([]byte, size+crypto.Extension)
n, err := crypto.Encrypt(k, ciphertext, data)
ciphertext, err := crypto.Encrypt(k, make([]byte, size+crypto.Extension), data)
OK(t, err)
plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext[:n])
plaintext, err := crypto.Decrypt(k, []byte{}, ciphertext)
OK(t, err)
Equals(t, plaintext, data)
@ -183,14 +211,14 @@ func BenchmarkDecrypt(b *testing.B) {
plaintext := restic.GetChunkBuf("BenchmarkDecrypt")
defer restic.FreeChunkBuf("BenchmarkDecrypt", plaintext)
n, err := crypto.Encrypt(k, ciphertext, data)
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[:n])
plaintext, err = crypto.Decrypt(k, plaintext, ciphertext)
OK(b, err)
}
}
@ -245,11 +273,11 @@ func TestDecryptStreamReader(t *testing.T) {
ciphertext := make([]byte, size+crypto.Extension)
// encrypt with default function
n, err := crypto.Encrypt(k, ciphertext, data)
ciphertext, err = crypto.Encrypt(k, ciphertext, data)
OK(t, err)
Assert(t, n == len(data)+crypto.Extension,
Assert(t, len(ciphertext) == len(data)+crypto.Extension,
"wrong number of bytes returned after encryption: expected %d, got %d",
len(data)+crypto.Extension, n)
len(data)+crypto.Extension, len(ciphertext))
rd, err := crypto.DecryptFrom(k, bytes.NewReader(ciphertext))
OK(t, err)

9
key.go
View File

@ -196,9 +196,7 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
return nil, err
}
newkey.Data = GetChunkBuf("key")
n, err = crypto.Encrypt(newkey.user, newkey.Data, buf)
newkey.Data = newkey.Data[:n]
newkey.Data, err = crypto.Encrypt(newkey.user, GetChunkBuf("key"), buf)
// dump as json
buf, err = json.Marshal(newkey)
@ -234,8 +232,9 @@ func AddKey(s Server, password string, template *Key) (*Key, error) {
}
// Encrypt encrypts and signs data with the master key. Stored in ciphertext is
// IV || Ciphertext || MAC. Returns the ciphertext length.
func (k *Key) Encrypt(ciphertext, plaintext []byte) (int, error) {
// IV || Ciphertext || MAC. Returns the ciphertext, which is extended if
// necessary.
func (k *Key) Encrypt(ciphertext, plaintext []byte) ([]byte, error) {
return crypto.Encrypt(k.master, ciphertext, plaintext)
}

View File

@ -172,13 +172,11 @@ func (s Server) Save(t backend.Type, data []byte, id backend.ID) (Blob, error) {
}
// encrypt blob
n, err := s.Encrypt(ciphertext, data)
ciphertext, err := s.Encrypt(ciphertext, data)
if err != nil {
return Blob{}, err
}
ciphertext = ciphertext[:n]
// compute ciphertext hash
sid := backend.Hash(ciphertext)
@ -309,9 +307,9 @@ func (s Server) Decrypt(ciphertext []byte) ([]byte, error) {
return s.key.Decrypt([]byte{}, ciphertext)
}
func (s Server) Encrypt(ciphertext, plaintext []byte) (int, error) {
func (s Server) Encrypt(ciphertext, plaintext []byte) ([]byte, error) {
if s.key == nil {
return 0, errors.New("key for server not set")
return nil, errors.New("key for server not set")
}
return s.key.Encrypt(ciphertext, plaintext)