summaryrefslogtreecommitdiffstats
path: root/store
diff options
context:
space:
mode:
authorFrank Denis <github@pureftpd.org>2015-06-24 16:29:45 +0200
committerFrank Denis <github@pureftpd.org>2015-06-24 16:56:57 +0200
commit34d688ca72d8ef13b83987cc3b3d9b93ab989d9f (patch)
tree0bb44d5e4ef275665060357b61afed2db4c93852 /store
parent5bc0a19953a4c66058da08e7d560a01b0d428607 (diff)
downloadchat-34d688ca72d8ef13b83987cc3b3d9b93ab989d9f.tar.gz
chat-34d688ca72d8ef13b83987cc3b3d9b93ab989d9f.tar.bz2
chat-34d688ca72d8ef13b83987cc3b3d9b93ab989d9f.zip
SQL store: use authenticated encryption
Data were encrypted using AES-CFB, with a properly randomized IV, but without any authenticators. This allows the data to be tampered with, without being noticed by the application. This diff slightly changes the encryption/decryption functions in sql_store.go to add a HMAC-SHA256 authenticator to encrypted messages. Two keys are derived from AtRestEncryptKey: the first half of SHA512(AtRestEncryptKey) for the block cipher and the second half for the MAC. This can be changed to a KDF if needed. The decryption function also checks that base64 decoding actually worked, and that the ciphertext is long enough to include the IV and the MAC. Unfortunately, it breaks backward compatibility. But if such a change has to be made, it has to be made early.
Diffstat (limited to 'store')
-rw-r--r--store/sql_store.go38
1 files changed, 32 insertions, 6 deletions
diff --git a/store/sql_store.go b/store/sql_store.go
index a2deea6ba..bef8b4867 100644
--- a/store/sql_store.go
+++ b/store/sql_store.go
@@ -7,6 +7,9 @@ import (
l4g "code.google.com/p/log4go"
"crypto/aes"
"crypto/cipher"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/sha512"
crand "crypto/rand"
dbsql "database/sql"
"encoding/base64"
@@ -327,20 +330,26 @@ func encrypt(key []byte, text string) (string, error) {
}
plaintext := []byte(text)
+ skey := sha512.Sum512(key)
+ ekey, akey := skey[:32], skey[32:]
- block, err := aes.NewCipher(key)
+ block, err := aes.NewCipher(ekey)
if err != nil {
return "", err
}
- ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+ macfn := hmac.New(sha256.New, akey)
+ ciphertext := make([]byte, aes.BlockSize+macfn.Size()+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(crand.Reader, iv); err != nil {
return "", err
}
stream := cipher.NewCFBEncrypter(block, iv)
- stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+ stream.XORKeyStream(ciphertext[aes.BlockSize+macfn.Size():], plaintext)
+ macfn.Write(ciphertext[aes.BlockSize+macfn.Size():])
+ mac := macfn.Sum(nil)
+ copy(ciphertext[aes.BlockSize:aes.BlockSize+macfn.Size()], mac)
return base64.URLEncoding.EncodeToString(ciphertext), nil
}
@@ -351,9 +360,26 @@ func decrypt(key []byte, cryptoText string) (string, error) {
return "{}", nil
}
- ciphertext, _ := base64.URLEncoding.DecodeString(cryptoText)
+ ciphertext, err := base64.URLEncoding.DecodeString(cryptoText)
+ if err != nil {
+ return "", err
+ }
+
+ skey := sha512.Sum512(key)
+ ekey, akey := skey[:32], skey[32:]
+ macfn := hmac.New(sha256.New, akey)
+ if len(ciphertext) < aes.BlockSize+macfn.Size() {
+ return "", errors.New("short ciphertext")
+ }
+
+ macfn.Write(ciphertext[aes.BlockSize+macfn.Size():])
+ expectedMac := macfn.Sum(nil)
+ mac := ciphertext[aes.BlockSize:aes.BlockSize+macfn.Size()]
+ if hmac.Equal(expectedMac, mac) != true {
+ return "", errors.New("Incorrect MAC for the given ciphertext")
+ }
- block, err := aes.NewCipher(key)
+ block, err := aes.NewCipher(ekey)
if err != nil {
return "", err
}
@@ -362,7 +388,7 @@ func decrypt(key []byte, cryptoText string) (string, error) {
return "", errors.New("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
- ciphertext = ciphertext[aes.BlockSize:]
+ ciphertext = ciphertext[aes.BlockSize+macfn.Size():]
stream := cipher.NewCFBDecrypter(block, iv)