blob: 4d710f257d96670712bbc73be38f808fc1f82e4e [file] [log] [blame]
/**
* @file openssl/aes.c AES (Advanced Encryption Standard) using OpenSSL
*
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <re_types.h>
#include <re_fmt.h>
#include <re_mem.h>
#include <re_aes.h>
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;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (st->ctx)
EVP_CIPHER_CTX_free(st->ctx);
#else
if (st->ctx)
EVP_CIPHER_CTX_cleanup(st->ctx);
mem_deref(st->ctx);
#endif
}
int aes_alloc(struct aes **aesp, enum aes_mode mode,
const uint8_t *key, size_t key_bits,
const uint8_t *iv)
{
const EVP_CIPHER *cipher;
struct aes *st;
int err = 0, r;
if (!aesp || !key)
return EINVAL;
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) {
ERR_clear_error();
err = ENOMEM;
goto out;
}
#else
st->ctx = mem_zalloc(sizeof(*st->ctx), NULL);
if (!st->ctx) {
err = ENOMEM;
goto out;
}
EVP_CIPHER_CTX_init(st->ctx);
#endif
r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv);
if (!r) {
ERR_clear_error();
err = EPROTO;
}
out:
if (err)
mem_deref(st);
else
*aesp = st;
return err;
}
void aes_set_iv(struct aes *aes, const uint8_t *iv)
{
int r;
if (!aes || !iv)
return;
r = EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, iv, -1);
if (!r)
ERR_clear_error();
}
int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
{
int c_len = (int)len;
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;
}
return 0;
}
int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len)
{
int c_len = (int)len;
if (!aes || !in)
return EINVAL;
if (!set_crypt_dir(aes, false))
return EPROTO;
if (!EVP_DecryptUpdate(aes->ctx, out, &c_len, in, (int)len)) {
ERR_clear_error();
return EPROTO;
}
return 0;
}
/**
* 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_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen)
{
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;
}
}