Skip to content

Commit

Permalink
Merge pull request #29 from Bit-Nation/feature/aes_ctr
Browse files Browse the repository at this point in the history
AES CTR
  • Loading branch information
florianlenz committed Jun 15, 2018
2 parents f6105ba + 9aab602 commit 8c50a61
Show file tree
Hide file tree
Showing 20 changed files with 1,094 additions and 274 deletions.
7 changes: 6 additions & 1 deletion cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,15 @@ func main() {
return
}

rawStore, err := exportedAccount.Marshal()
if err != nil {
panic(err)
}

err = db.Write("account", id.String(), &Account{
ID: id.String(),
Name: accountName,
AccountStore: exportedAccount,
AccountStore: string(rawStore),
Profile: string(rawProfile),
})

Expand Down
1 change: 1 addition & 0 deletions client/client_key_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func (s *DoubleRatchetKeyStore) Get(k dr.Key, msgNum uint) (mk dr.Key, ok bool)
}

resp := <-respCha

if resp.Error != nil {
resp.Close(nil)
return dr.Key{}, false
Expand Down
2 changes: 1 addition & 1 deletion client/client_key_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestDoubleRatchetKeyStore_GetSuccess(t *testing.T) {

mustBeEqual(`{"key":"0000000000000000000100010000000000010001000000000000000100000001","msg_num":7}`, rpcCall.Data)

requireNil(api.Receive(rpcCall.Id, `{"error":"","payload":"{\"key\":\"{\\\"iv\\\":\\\"f+ZgKGwhkz82bokcs7HI8A==\\\",\\\"cipher_text\\\":\\\"Qu0SeXs/ahOjqcwPRIrK0sR9ngirapvt33x3SNLayFs=\\\",\\\"mac\\\":\\\"vJhCluH/bWdkcaA3vtTzDDsYFVO0A7UcL7wbPbvYdG0=\\\",\\\"v\\\":1}\"}"}`))
requireNil(api.Receive(rpcCall.Id, `{"error":"","payload":"{\"key\":\"{\\\"iv\\\":\\\"3Zd2O1KxUz2OZnWQPrTgCg==\\\",\\\"cipher_text\\\":\\\"q4FO26h5TICATqwwp9RXXXes1jX8asn+0TkL5Khx8Oc=\\\",\\\"mac\\\":\\\"XwX884HeXuodY3vgoKvmZcGkW0oPu2fBvRafxAsMu/I=\\\",\\\"v\\\":2}\"}"}`))

}
}
Expand Down
77 changes: 77 additions & 0 deletions crypto/aes/cfb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package aes

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
)

var cfbRandReader io.Reader = rand.Reader

// encrypt a plain text with given secret
// Deprecated: use CTREncrypt instead
func CFBEncrypt(plainText PlainText, key Secret) (CipherText, error) {

// create block
block, err := aes.NewCipher(key[:])
if err != nil {
return CipherText{}, err
}

// create IV
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(cfbRandReader, iv); err != nil {
return CipherText{}, err
}

// create cfb encrypter
stream := cipher.NewCFBEncrypter(block, iv)
cipherText := make([]byte, len(plainText))
stream.XORKeyStream(cipherText, plainText)

// cipher text
ct := CipherText{
IV: iv,
CipherText: cipherText,
Version: 1,
}

// create mac
mac, err := vOneMac(ct, key)
if err != nil {
return CipherText{}, err
}
ct.Mac = mac

return ct, nil

}

// decrypt a cipher text with given secret
// Deprecated: use CFBDecrypt instead
func CFBDecrypt(cipherText CipherText, key Secret) (PlainText, error) {

// create block
block, err := aes.NewCipher(key[:])
if err != nil {
return PlainText{}, err
}

valid, err := cipherText.ValidMAC(key)
if err != nil {
return PlainText{}, err
}
if !valid {
return PlainText{}, MacError
}

stream := cipher.NewCFBDecrypter(block, cipherText.IV)

cc := cipherText.CipherText

// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(cc, cc)

return cc, nil
}
183 changes: 183 additions & 0 deletions crypto/aes/cfb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package aes

import (
"bytes"
"encoding/hex"
"testing"

require "github.com/stretchr/testify/require"
)

// Test the encrypt and decrypt function in one batch
func CFBTestSuccessEncryptDecrypt(t *testing.T) {

secret := Secret{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
value := []byte("I am the value")

//Encrypt
cipherText, e := CFBEncrypt(value, secret)
require.Nil(t, e)

//Decrypt
res, err := CFBDecrypt(cipherText, secret)
require.Nil(t, err)

//Decrypted value must match the given value
require.Equal(t, string(value), string(res))

}

func CFBTestFailedDecryption(t *testing.T) {

secret := Secret{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
value := []byte("I am the plain text")

// encrypt
cipherText, e := CFBEncrypt(value, secret)
require.Nil(t, e)

// change last byte to fail on decryption
secret[31] = 0x01

// decrypt
plainText, err := CFBDecrypt(cipherText, secret)
require.Equal(t, PlainText{}, plainText)
require.EqualError(t, err, "invalid key - message authentication failed")

}

func TestCFBEncrypt(t *testing.T) {

// encryption key
encryptionKey, err := hex.DecodeString("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
require.Nil(t, err)

// aes cfb test vectors
testVectors := []testVector{
testVector{
iv: "000102030405060708090a0b0c0d0e0f",
plainText: "6bc1bee22e409f96e93d7e117393172a",
cipherText: "dc7e84bfda79164b7ecd8486985d3860",
},
testVector{
iv: "dc7e84bfda79164b7ecd8486985d3860",
plainText: "ae2d8a571e03ac9c9eb76fac45af8e51",
cipherText: "39ffed143b28b1c832113c6331e5407b",
},
testVector{
iv: "39ffed143b28b1c832113c6331e5407b",
plainText: "30c81c46a35ce411e5fbc1191a0a52ef",
cipherText: "df10132415e54b92a13ed0a8267ae2f9",
},
testVector{
iv: "df10132415e54b92a13ed0a8267ae2f9",
plainText: "f69f2445df4f9b17ad2b417be66c3710",
cipherText: "75a385741ab9cef82031623d55b1e471",
},
}

for _, v := range testVectors {

// initialisation vector
iv, err := hex.DecodeString(v.iv)
require.Nil(t, err)
cfbRandReader = bytes.NewReader(iv)

// plain text
plainText, err := hex.DecodeString(v.plainText)
require.Nil(t, err)

// cipher text
cipherText, err := hex.DecodeString(v.cipherText)
require.Nil(t, err)
// create secret key
secKey := Secret{}
copy(secKey[:], encryptionKey)

// encrypt
ct, err := CFBEncrypt(plainText, secKey)
require.Nil(t, err)

require.Equal(t, hex.EncodeToString(cipherText), hex.EncodeToString(ct.CipherText))

}

}

func TestCFBDecrypt(t *testing.T) {

// encryption key
encryptionKey, err := hex.DecodeString("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4")
require.Nil(t, err)

// aes cfb test vectors
testVectors := []testVector{
testVector{
iv: "000102030405060708090a0b0c0d0e0f",
plainText: "6bc1bee22e409f96e93d7e117393172a",
cipherText: "dc7e84bfda79164b7ecd8486985d3860",
mac: "43517b9531fdb8565ce2a6af1cbb24930cb7ec2b3685701cfac6731bb861cb60",
},
testVector{
iv: "dc7e84bfda79164b7ecd8486985d3860",
plainText: "ae2d8a571e03ac9c9eb76fac45af8e51",
cipherText: "39ffed143b28b1c832113c6331e5407b",
mac: "de571950b74a72055810fad70421676aeadd651d6bd3ba6ad0348a7c7c9ebc8e",
},
testVector{
iv: "39ffed143b28b1c832113c6331e5407b",
plainText: "30c81c46a35ce411e5fbc1191a0a52ef",
cipherText: "df10132415e54b92a13ed0a8267ae2f9",
mac: "6ce26e57380b71dec45177dda9d1d322e1dceba40b462b6c15990a43bb1b2ec2",
},
testVector{
iv: "df10132415e54b92a13ed0a8267ae2f9",
plainText: "f69f2445df4f9b17ad2b417be66c3710",
cipherText: "75a385741ab9cef82031623d55b1e471",
mac: "73fb351bba3a8299d2271ff489dc4ed2412e69653b9877bc19a6b408c51e83da",
},
}

for _, v := range testVectors {

// initialisation vector
iv, err := hex.DecodeString(v.iv)
require.Nil(t, err)
cfbRandReader = bytes.NewReader(iv)

// mac
mac, err := hex.DecodeString(v.mac)
require.Nil(t, err)

// cipher text
cipherText, err := hex.DecodeString(v.cipherText)
require.Nil(t, err)

// create secret key
secKey := Secret{}
copy(secKey[:], encryptionKey)

// encrypt
plain, err := CFBDecrypt(CipherText{
IV: iv,
Mac: mac,
CipherText: cipherText,
Version: uint8(1),
}, secKey)
require.Nil(t, err)

require.Equal(t, v.plainText, hex.EncodeToString(plain))

}

}
74 changes: 74 additions & 0 deletions crypto/aes/ctr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package aes

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
)

var ctrRandReader io.Reader = rand.Reader

// encrypt plain text by given key using AES CTR 256
func CTREncrypt(plainText PlainText, secret Secret) (CipherText, error) {

// block
block, err := aes.NewCipher(secret[:])
if err != nil {
return CipherText{}, err
}

// initialisation vector
iv := make([]byte, 16)
_, err = io.ReadFull(ctrRandReader, iv)
if err != nil {
return CipherText{}, err
}

// create cipher text
cipherText := make([]byte, len(plainText))

// encrypt
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(cipherText, plainText)

// create cipher text
ct := CipherText{
IV: iv,
CipherText: cipherText,
Version: 2,
}

// create mac
mac, err := vTwoMac(ct, secret)
ct.Mac = mac

return ct, err
}

// decrypt cipher text by given key
func CTRDecrypt(cipherText CipherText, key Secret) (PlainText, error) {

// create block
block, err := aes.NewCipher(key[:])
if err != nil {
return PlainText{}, err
}

// validate key
valid, err := cipherText.ValidMAC(key)
if err != nil {
return PlainText{}, err
}
if !valid {
return PlainText{}, MacError
}

// decrypt
plainText := make(PlainText, len(cipherText.CipherText))
stream := cipher.NewCTR(block, cipherText.IV)
stream.XORKeyStream(plainText, cipherText.CipherText)

return plainText, nil

}
Loading

0 comments on commit 8c50a61

Please sign in to comment.