mirror of https://github.com/restic/restic.git
crypto: Clarify slice usage for Encrypt/Decrypt
This commit is contained in:
parent
63e79539e9
commit
8e867817a5
|
@ -206,14 +206,15 @@ func (k *EncryptionKey) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
// Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext ||
|
// Encrypt encrypts and signs data. Stored in ciphertext is IV || Ciphertext ||
|
||||||
// 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 point to the same slice.
|
// necessary. ciphertext and plaintext may not point to (exactly) the same
|
||||||
|
// slice or non-intersecting slices.
|
||||||
func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) {
|
func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) {
|
||||||
// extend ciphertext slice if necessary
|
// extend ciphertext slice if necessary
|
||||||
if cap(ciphertext) < len(plaintext)+Extension {
|
ciphertext = ciphertext[:cap(ciphertext)]
|
||||||
|
if len(ciphertext) < len(plaintext)+Extension {
|
||||||
ext := len(plaintext) + Extension - cap(ciphertext)
|
ext := len(plaintext) + Extension - cap(ciphertext)
|
||||||
n := len(ciphertext)
|
|
||||||
ciphertext = append(ciphertext, make([]byte, ext)...)
|
ciphertext = append(ciphertext, make([]byte, ext)...)
|
||||||
ciphertext = ciphertext[:n]
|
ciphertext = ciphertext[:cap(ciphertext)]
|
||||||
}
|
}
|
||||||
|
|
||||||
iv := newIV()
|
iv := newIV()
|
||||||
|
@ -224,18 +225,21 @@ func Encrypt(ks *Key, ciphertext, plaintext []byte) ([]byte, error) {
|
||||||
|
|
||||||
e := cipher.NewCTR(c, iv[:])
|
e := cipher.NewCTR(c, iv[:])
|
||||||
|
|
||||||
e.XORKeyStream(ciphertext[ivSize:cap(ciphertext)], plaintext)
|
e.XORKeyStream(ciphertext[ivSize:], plaintext)
|
||||||
copy(ciphertext, iv[:])
|
copy(ciphertext, iv[:])
|
||||||
|
// truncate to only conver iv and actual ciphertext
|
||||||
ciphertext = ciphertext[:ivSize+len(plaintext)]
|
ciphertext = ciphertext[:ivSize+len(plaintext)]
|
||||||
|
|
||||||
mac := poly1305Sign(ciphertext[ivSize:], ciphertext[:ivSize], &ks.Sign)
|
mac := poly1305Sign(ciphertext[ivSize:], ciphertext[:ivSize], &ks.Sign)
|
||||||
|
// append the mac tag
|
||||||
ciphertext = append(ciphertext, mac...)
|
ciphertext = append(ciphertext, mac...)
|
||||||
|
|
||||||
return ciphertext, nil
|
return ciphertext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// IV || Ciphertext || MAC. plaintext and ciphertext may point to (exactly) the
|
||||||
|
// same slice.
|
||||||
func Decrypt(ks *Key, plaintext, ciphertext []byte) ([]byte, error) {
|
func Decrypt(ks *Key, plaintext, ciphertext []byte) ([]byte, error) {
|
||||||
// check for plausible length
|
// check for plausible length
|
||||||
if len(ciphertext) < ivSize+macSize {
|
if len(ciphertext) < ivSize+macSize {
|
||||||
|
|
|
@ -31,9 +31,15 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||||
|
|
||||||
ciphertext, err := crypto.Encrypt(k, restic.GetChunkBuf("TestEncryptDecrypt"), data)
|
ciphertext, err := crypto.Encrypt(k, restic.GetChunkBuf("TestEncryptDecrypt"), data)
|
||||||
OK(t, err)
|
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, err := crypto.Decrypt(k, nil, ciphertext)
|
plaintext, err := crypto.Decrypt(k, nil, ciphertext)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
Assert(t, len(plaintext) == len(data),
|
||||||
|
"plaintext length does not match: want %d, got %d",
|
||||||
|
len(data), len(plaintext))
|
||||||
|
|
||||||
restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext)
|
restic.FreeChunkBuf("TestEncryptDecrypt", ciphertext)
|
||||||
|
|
||||||
|
@ -54,7 +60,7 @@ func TestSmallBuffer(t *testing.T) {
|
||||||
|
|
||||||
ciphertext := make([]byte, size/2)
|
ciphertext := make([]byte, size/2)
|
||||||
ciphertext, 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
|
// 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",
|
||||||
cap(ciphertext))
|
cap(ciphertext))
|
||||||
|
@ -77,12 +83,12 @@ func TestSameBuffer(t *testing.T) {
|
||||||
_, err = io.ReadFull(f, data)
|
_, err = io.ReadFull(f, data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
ciphertext := make([]byte, size)
|
ciphertext := make([]byte, 0, size+crypto.Extension)
|
||||||
copy(ciphertext, data)
|
|
||||||
|
|
||||||
ciphertext, err = crypto.Encrypt(k, ciphertext, ciphertext)
|
ciphertext, err = crypto.Encrypt(k, ciphertext, data)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
|
|
||||||
|
// use the same buffer for decryption
|
||||||
ciphertext, err = crypto.Decrypt(k, ciphertext, ciphertext)
|
ciphertext, err = crypto.Decrypt(k, ciphertext, ciphertext)
|
||||||
OK(t, err)
|
OK(t, err)
|
||||||
Assert(t, bytes.Equal(ciphertext, data),
|
Assert(t, bytes.Equal(ciphertext, data),
|
||||||
|
|
Loading…
Reference in New Issue