aes: add support for GCM Galois Counter Mode (#106)

* aes: add support for GCM Galois Counter Mode (AEAD)
* add tag functions
* variable length IV
* set crypt direction
* remove usage of old AES api
* aes: set IV using EVP_CipherInit_ex
This commit is contained in:
Alfred E. Heggestad 2018-02-01 12:00:57 +01:00 committed by Richard Aas
parent 624187196d
commit b42724a64b
4 changed files with 190 additions and 98 deletions

View file

@ -11,14 +11,17 @@
/** AES mode */
enum aes_mode {
AES_MODE_CTR /**< AES Counter mode (CTR) */
AES_MODE_CTR, /**< AES Counter mode (CTR) */
AES_MODE_GCM, /**< AES Galois Counter Mode (GCM) */
};
struct aes;
int aes_alloc(struct aes **stp, enum aes_mode mode,
const uint8_t *key, size_t key_bits,
const uint8_t iv[AES_BLOCK_SIZE]);
void aes_set_iv(struct aes *aes, const uint8_t iv[AES_BLOCK_SIZE]);
const uint8_t *iv);
void aes_set_iv(struct aes *aes, const uint8_t *iv);
int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len);
int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len);
int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen);
int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen);

View file

@ -30,7 +30,7 @@ static void destructor(void *arg)
int aes_alloc(struct aes **stp, enum aes_mode mode,
const uint8_t *key, size_t key_bits,
const uint8_t iv[AES_BLOCK_SIZE])
const uint8_t *iv)
{
struct aes *st;
size_t key_bytes = key_bits / 8;
@ -75,7 +75,7 @@ int aes_alloc(struct aes **stp, enum aes_mode mode,
}
void aes_set_iv(struct aes *st, const uint8_t iv[AES_BLOCK_SIZE])
void aes_set_iv(struct aes *st, const uint8_t *iv)
{
CCCryptorStatus status;
@ -123,3 +123,23 @@ int aes_decr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len)
{
return aes_encr(st, out, in, len);
}
int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)
{
(void)aes;
(void)tag;
(void)taglen;
return ENOSYS;
}
int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)
{
(void)aes;
(void)tag;
(void)taglen;
return ENOSYS;
}

View file

@ -13,14 +13,60 @@
#include <re_aes.h>
#ifdef EVP_CIPH_CTR_MODE
struct aes {
EVP_CIPHER_CTX *ctx;
enum aes_mode mode;
bool encr;
};
static const EVP_CIPHER *aes_cipher(enum aes_mode mode, size_t key_bits)
{
if (mode == AES_MODE_CTR) {
switch (key_bits) {
case 128: return EVP_aes_128_ctr();
case 192: return EVP_aes_192_ctr();
case 256: return EVP_aes_256_ctr();
default:
return NULL;
}
}
else if (mode == AES_MODE_GCM) {
switch (key_bits) {
case 128: return EVP_aes_128_gcm();
case 256: return EVP_aes_256_gcm();
default:
return NULL;
}
}
else {
return NULL;
}
}
static inline bool set_crypt_dir(struct aes *aes, bool encr)
{
if (aes->encr != encr) {
/* update the encrypt/decrypt direction */
if (!EVP_CipherInit_ex(aes->ctx, NULL, NULL,
NULL, NULL, encr)) {
ERR_clear_error();
return false;
}
aes->encr = encr;
}
return true;
}
static void destructor(void *arg)
{
struct aes *st = arg;
@ -38,7 +84,7 @@ static void destructor(void *arg)
int aes_alloc(struct aes **aesp, enum aes_mode mode,
const uint8_t *key, size_t key_bits,
const uint8_t iv[AES_BLOCK_SIZE])
const uint8_t *iv)
{
const EVP_CIPHER *cipher;
struct aes *st;
@ -47,13 +93,17 @@ int aes_alloc(struct aes **aesp, enum aes_mode mode,
if (!aesp || !key)
return EINVAL;
if (mode != AES_MODE_CTR)
cipher = aes_cipher(mode, key_bits);
if (!cipher)
return ENOTSUP;
st = mem_zalloc(sizeof(*st), destructor);
if (!st)
return ENOMEM;
st->mode = mode;
st->encr = true;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
st->ctx = EVP_CIPHER_CTX_new();
if (!st->ctx) {
@ -72,17 +122,6 @@ int aes_alloc(struct aes **aesp, enum aes_mode mode,
EVP_CIPHER_CTX_init(st->ctx);
#endif
switch (key_bits) {
case 128: cipher = EVP_aes_128_ctr(); break;
case 192: cipher = EVP_aes_192_ctr(); break;
case 256: cipher = EVP_aes_256_ctr(); break;
default:
re_fprintf(stderr, "aes: unknown key: %zu bits\n", key_bits);
err = EINVAL;
goto out;
}
r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv);
if (!r) {
ERR_clear_error();
@ -99,14 +138,14 @@ int aes_alloc(struct aes **aesp, enum aes_mode mode,
}
void aes_set_iv(struct aes *aes, const uint8_t iv[AES_BLOCK_SIZE])
void aes_set_iv(struct aes *aes, const uint8_t *iv)
{
int r;
if (!aes || !iv)
return;
r = EVP_EncryptInit_ex(aes->ctx, NULL, NULL, NULL, iv);
r = EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, iv, -1);
if (!r)
ERR_clear_error();
}
@ -116,9 +155,12 @@ int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
{
int c_len = (int)len;
if (!aes || !out || !in)
if (!aes || !in)
return EINVAL;
if (!set_crypt_dir(aes, true))
return EPROTO;
if (!EVP_EncryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
ERR_clear_error();
return EPROTO;
@ -128,91 +170,98 @@ int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
}
#else /* EVP_CIPH_CTR_MODE */
struct aes {
AES_KEY key;
uint8_t iv[AES_BLOCK_SIZE];
};
static void destructor(void *arg)
int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
{
struct aes *st = arg;
int c_len = (int)len;
memset(&st->key, 0, sizeof(st->key));
}
int aes_alloc(struct aes **aesp, enum aes_mode mode,
const uint8_t *key, size_t key_bits,
const uint8_t iv[AES_BLOCK_SIZE])
{
struct aes *st;
int err = 0, r;
if (!aesp || !key)
if (!aes || !in)
return EINVAL;
if (mode != AES_MODE_CTR)
return ENOTSUP;
if (!set_crypt_dir(aes, false))
return EPROTO;
st = mem_zalloc(sizeof(*st), destructor);
if (!st)
return ENOMEM;
r = AES_set_encrypt_key(key, (int)key_bits, &st->key);
if (r != 0) {
err = EPROTO;
goto out;
if (!EVP_DecryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
ERR_clear_error();
return EPROTO;
}
if (iv)
memcpy(st->iv, iv, sizeof(st->iv));
out:
if (err)
mem_deref(st);
else
*aesp = st;
return err;
}
void aes_set_iv(struct aes *aes, const uint8_t iv[AES_BLOCK_SIZE])
{
if (!aes)
return;
if (iv)
memcpy(aes->iv, iv, sizeof(aes->iv));
}
int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
{
unsigned char ec[AES_BLOCK_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned int num = 0;
if (!aes || !out || !in)
return EINVAL;
AES_ctr128_encrypt(in, out, len, &aes->key, aes->iv, ec, &num);
return 0;
}
#endif /* EVP_CIPH_CTR_MODE */
/*
* Common code:
/**
* Get the authentication tag for an AEAD cipher (e.g. GCM)
*
* @param aes AES Context
* @param tag Authentication tag
* @param taglen Length of Authentication tag
*
* @return 0 if success, otherwise errorcode
*/
int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)
{
return aes_encr(aes, out, in, len);
int tmplen;
if (!aes || !tag || !taglen)
return EINVAL;
switch (aes->mode) {
case AES_MODE_GCM:
if (!EVP_EncryptFinal_ex(aes->ctx, NULL, &tmplen)) {
ERR_clear_error();
return EPROTO;
}
if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG,
(int)taglen, tag)) {
ERR_clear_error();
return EPROTO;
}
return 0;
default:
return ENOTSUP;
}
}
/**
* Authenticate a decryption tag for an AEAD cipher (e.g. GCM)
*
* @param aes AES Context
* @param tag Authentication tag
* @param taglen Length of Authentication tag
*
* @return 0 if success, otherwise errorcode
*
* @retval EAUTH if authentication failed
*/
int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)
{
int tmplen;
if (!aes || !tag || !taglen)
return EINVAL;
switch (aes->mode) {
case AES_MODE_GCM:
if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG,
(int)taglen, (void *)tag)) {
ERR_clear_error();
return EPROTO;
}
if (EVP_DecryptFinal_ex(aes->ctx, NULL, &tmplen) <= 0) {
ERR_clear_error();
return EAUTH;
}
return 0;
default:
return ENOTSUP;
}
}

View file

@ -45,3 +45,23 @@ int aes_decr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len)
(void)len;
return ENOSYS;
}
int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)
{
(void)aes;
(void)tag;
(void)taglen;
return ENOSYS;
}
int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen)
{
(void)aes;
(void)tag;
(void)taglen;
return ENOSYS;
}