Encrypt backups
rafaeljusto opened this issue · comments
Encrypt backup when sending to the cloud and decrypt it when retrieving. Do this only if there's a symmetric key defined.
We should check the performance when encrypting large backup files. Also, we need to improve the algorithm:
Note that this example is simplistic in that it omits any
authentication of the encrypted data. If you were actually to use
StreamReader in this manner, an attacker could flip arbitrary bits in
the output.
Some links related to authentication of the encrypted data:
http://stackoverflow.com/questions/30138477/golang-aes-streamreader-encryption-example-omits-any-authentication-of-the-enc
http://security.stackexchange.com/questions/33569/why-do-you-need-message-authentication-in-addition-to-encryption/33576#33576
Reminder: We could be trying to decrypt a file that was not encrypted in the first place. So maybe we should ignore the decryption errors and return the file as it is.
Not in time for version v2.0.0
An idea would be encrypting/decryoting in chunks using GCM:
func encrypt(filename string, secret string) (encryptedFilename string, err error) {
archive, err := os.Open(filename)
if err != nil {
return "", err
}
defer archive.Close()
encryptedArchive, err := ioutil.TempFile("", "toglacier-")
if err != nil {
return "", err
}
defer encryptedArchive.Close()
block, err := aes.NewCipher([]byte(secret))
if err != nil {
return "", err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
for {
chunk := make([]byte, 1048576) // 1MB
n, err := archive.Read(chunk)
if err == io.EOF {
break
} else if err != nil {
return "", err
}
chunk = chunk[:n]
nonce := make([]byte, aesgcm.NonceSize())
if _, err := io.ReadFull(RandomSource, nonce); err != nil {
return "", err
}
encryptedChunk := aesgcm.Seal(nil, nonce, chunk, nil)
if _, err := encryptedArchive.Write(nonce); err != nil {
return "", err
}
if _, err := encryptedArchive.Write(encryptedChunk); err != nil {
return "", err
}
}
return encryptedArchive.Name(), nil
}
func decrypt(encryptedFilename string, secret string) (filename string, err error) {
encryptedArchive, err := os.Open(encryptedFilename)
if err != nil {
return "", err
}
defer encryptedArchive.Close()
archive, err := ioutil.TempFile("", "toglacier-")
if err != nil {
return "", err
}
defer archive.Close()
block, err := aes.NewCipher([]byte(secret))
if err != nil {
return "", err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return "", err
}
for {
nonce := make([]byte, aesgcm.NonceSize())
n, err := archive.Read(nonce)
if err == io.EOF {
break
} else if err != nil {
return "", err
}
nonce = nonce[:n]
chunk := make([]byte, 1048576) // 1MB
n, err = archive.Read(chunk)
if err == io.EOF {
break
} else if err != nil {
return "", err
}
chunk = chunk[:n]
decryptedChunk, err := aesgcm.Open(nil, nonce, chunk, nil)
if err != nil {
return "", err
}
if _, err := archive.Write(decryptedChunk); err != nil {
return "", err
}
}
return archive.Name(), nil
}
Reminder: We could be trying to decrypt a file that was not encrypted in the first place. So maybe we should ignore the decryption errors and return the file as it is.
Done. We added an encrypted:
label at the beginning of the archive to identify when the content can be decrypted.
We should check the performance when encrypting large backup files. Also, we need to improve the algorithm:
Note that this example is simplistic in that it omits any authentication of the encrypted data. If you were actually to use StreamReader in this manner, an attacker could flip arbitrary bits in the output.
To add authentication HMAC-SHA256 we need to read the file content before encrypting, than we could append the hash result to the encrypted data so it's possible to validate later when we decrypt.