From bdaa54155cceb34846a202d0027054fd51493695 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Mon, 7 Feb 2011 18:16:33 +0000 Subject: [PATCH] Initial *very* experimental EVP support for AES-GCM. Note: probably very broken and subject to change. --- CHANGES | 9 ++ crypto/evp/e_aes.c | 210 ++++++++++++++++++++++++++++++++++++++++++++- crypto/evp/evp.h | 7 ++ 3 files changed, 225 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 2375076ae6..72b5ace4bc 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,15 @@ Changes between 1.0.1 and 1.1.0 [xx XXX xxxx] + *) Initial, experimental EVP support for AES-GCM. AAD can be input by + setting output buffer to NULL. The *Final function must be + called although it will not retrieve any additional data. The tag + can be set or retrieved with a ctrl. The IV length is by default 12 + bytes (96 bits) but can be set to an alternative value. If the IV + length exceeds the maximum IV length (currently 16 bytes) it cannot be + set before the key. + [Steve Henson] + *) New flag in ciphers: EVP_CIPH_FLAG_CUSTOM_CIPHER. This means the underlying do_cipher function handles all cipher semantics itself including padding and finalisation. This is useful if (for example) diff --git a/crypto/evp/e_aes.c b/crypto/evp/e_aes.c index b4da40d0e2..34a350a880 100644 --- a/crypto/evp/e_aes.c +++ b/crypto/evp/e_aes.c @@ -57,8 +57,8 @@ #include #include #include -#include #include "evp_locl.h" +#include static int aes_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc); @@ -187,4 +187,212 @@ static int aes_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, return 1; } +typedef struct + { + /* AES key schedule to use */ + AES_KEY ks; + /* Set if key initialised */ + int key_set; + /* Set if an iv is set */ + int iv_set; + /* Pointer to GCM128_CTX: FIXME actual structure later */ + GCM128_CONTEXT *gcm; + /* IV length */ + int ivlen; + /* Tag to verify */ + unsigned char tag[16]; + int taglen; + } EVP_AES_GCM_CTX; + +static int aes_gcm_cleanup(EVP_CIPHER_CTX *c) + { + EVP_AES_GCM_CTX *gctx = c->cipher_data; + if (gctx->gcm) + CRYPTO_gcm128_release(gctx->gcm); + return 1; + } + +static int aes_gcm_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr) + { + EVP_AES_GCM_CTX *gctx = c->cipher_data; + switch (type) + { + case EVP_CTRL_INIT: + gctx->gcm = NULL; + gctx->key_set = 0; + gctx->iv_set = 0; + gctx->ivlen = c->cipher->iv_len; + gctx->taglen = -1; + return 1; + + case EVP_CTRL_GCM_SET_IVLEN: + if (arg <= 0) + return 0; + gctx->ivlen = arg; + return 1; + + case EVP_CTRL_GCM_SET_TAG: + if (arg <= 0 || arg > 16 || c->encrypt) + return 0; + memcpy(gctx->tag, ptr, arg); + gctx->taglen = arg; + return 1; + + case EVP_CTRL_GCM_GET_TAG: + if (arg <= 0 || arg > 16 || !c->encrypt || gctx->taglen < 0) + return 0; + memcpy(ptr, gctx->tag, arg); + return 1; + + default: + return -1; + + } + } + +static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int enc) + { + EVP_AES_GCM_CTX *gctx = ctx->cipher_data; + if (!iv && !key) + return 1; + if (key) + { + AES_set_encrypt_key(key, ctx->key_len * 8, &gctx->ks); + if (!gctx->gcm) + { + gctx->gcm = + CRYPTO_gcm128_new(&gctx->ks, (block128_f)AES_encrypt); + if (!gctx->gcm) + return 0; + } + else + CRYPTO_gcm128_init(gctx->gcm, &gctx->ks, (block128_f)AES_encrypt); + /* If we have an iv can set it directly, otherwise use + * saved IV. + */ + if (iv == NULL && gctx->iv_set) + iv = ctx->iv; + if (iv) + { + CRYPTO_gcm128_setiv(gctx->gcm, iv, gctx->ivlen); + gctx->iv_set = 1; + } + gctx->key_set = 1; + } + else + { + /* If key set use IV, otherwise copy */ + if (gctx->key_set) + CRYPTO_gcm128_setiv(gctx->gcm, iv, gctx->ivlen); + else + { + /* If IV is too large for EVP_CIPHER_CTX buffer + * return an error. This can be avoided by either + * setting the key first or key and iv simultaneously. + */ + if (gctx->ivlen > EVP_MAX_IV_LENGTH) + return 0; + memcpy(ctx->iv, iv, gctx->ivlen); + } + gctx->iv_set = 1; + } + return 1; + } + +static int aes_gcm(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) + { + EVP_AES_GCM_CTX *gctx = ctx->cipher_data; + /* If not set up, return error */ + if (!gctx->iv_set && !gctx->key_set) + return -1; + if (!ctx->encrypt && gctx->taglen < 0) + return -1; + if (in) + { + if (out == NULL) + CRYPTO_gcm128_aad(gctx->gcm, in, len); + else if (ctx->encrypt) + CRYPTO_gcm128_encrypt(gctx->gcm, in, out, len); + else + CRYPTO_gcm128_decrypt(gctx->gcm, in, out, len); + return len; + } + else + { + if (!ctx->encrypt) + { + if (CRYPTO_gcm128_finish(gctx->gcm, + gctx->tag, gctx->taglen) != 0) + return -1; + gctx->iv_set = 0; + return 0; + } + CRYPTO_gcm128_tag(gctx->gcm, gctx->tag, 16); + gctx->taglen = 16; + /* Don't reuse the IV */ + gctx->iv_set = 0; + return 0; + } + + } + +static const EVP_CIPHER aes_128_gcm_cipher= + { + NID_aes_128_gcm,1,16,12, + EVP_CIPH_GCM_MODE|EVP_CIPH_FLAG_FIPS|EVP_CIPH_FLAG_DEFAULT_ASN1 + | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER + | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT, + aes_gcm_init_key, + aes_gcm, + aes_gcm_cleanup, + sizeof(EVP_AES_GCM_CTX), + NULL, + NULL, + aes_gcm_ctrl, + NULL + }; + +const EVP_CIPHER *EVP_aes_128_gcm (void) +{ return &aes_128_gcm_cipher; } + +static const EVP_CIPHER aes_192_gcm_cipher= + { + NID_aes_128_gcm,1,24,12, + EVP_CIPH_GCM_MODE|EVP_CIPH_FLAG_FIPS|EVP_CIPH_FLAG_DEFAULT_ASN1 + | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER + | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT, + aes_gcm_init_key, + aes_gcm, + aes_gcm_cleanup, + sizeof(EVP_AES_GCM_CTX), + NULL, + NULL, + aes_gcm_ctrl, + NULL + }; + +const EVP_CIPHER *EVP_aes_192_gcm (void) +{ return &aes_192_gcm_cipher; } + +static const EVP_CIPHER aes_256_gcm_cipher= + { + NID_aes_128_gcm,1,32,12, + EVP_CIPH_GCM_MODE|EVP_CIPH_FLAG_FIPS|EVP_CIPH_FLAG_DEFAULT_ASN1 + | EVP_CIPH_CUSTOM_IV | EVP_CIPH_FLAG_CUSTOM_CIPHER + | EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CTRL_INIT, + aes_gcm_init_key, + aes_gcm, + aes_gcm_cleanup, + sizeof(EVP_AES_GCM_CTX), + NULL, + NULL, + aes_gcm_ctrl, + NULL + }; + +const EVP_CIPHER *EVP_aes_256_gcm (void) +{ return &aes_256_gcm_cipher; } + #endif diff --git a/crypto/evp/evp.h b/crypto/evp/evp.h index 6604f3484b..f42bc2687a 100644 --- a/crypto/evp/evp.h +++ b/crypto/evp/evp.h @@ -329,6 +329,7 @@ struct evp_cipher_st #define EVP_CIPH_CFB_MODE 0x3 #define EVP_CIPH_OFB_MODE 0x4 #define EVP_CIPH_CTR_MODE 0x5 +#define EVP_CIPH_GCM_MODE 0x6 #define EVP_CIPH_MODE 0xF0007 /* Set if variable length cipher */ #define EVP_CIPH_VARIABLE_LENGTH 0x8 @@ -370,6 +371,9 @@ struct evp_cipher_st #define EVP_CTRL_RAND_KEY 0x6 #define EVP_CTRL_PBE_PRF_NID 0x7 #define EVP_CTRL_COPY 0x8 +#define EVP_CTRL_GCM_SET_IVLEN 0x9 +#define EVP_CTRL_GCM_GET_TAG 0x10 +#define EVP_CTRL_GCM_SET_TAG 0x11 typedef struct evp_cipher_info_st { @@ -770,6 +774,7 @@ const EVP_CIPHER *EVP_aes_128_cfb128(void); # define EVP_aes_128_cfb EVP_aes_128_cfb128 const EVP_CIPHER *EVP_aes_128_ofb(void); const EVP_CIPHER *EVP_aes_128_ctr(void); +const EVP_CIPHER *EVP_aes_128_gcm(void); const EVP_CIPHER *EVP_aes_192_ecb(void); const EVP_CIPHER *EVP_aes_192_cbc(void); const EVP_CIPHER *EVP_aes_192_cfb1(void); @@ -778,6 +783,7 @@ const EVP_CIPHER *EVP_aes_192_cfb128(void); # define EVP_aes_192_cfb EVP_aes_192_cfb128 const EVP_CIPHER *EVP_aes_192_ofb(void); const EVP_CIPHER *EVP_aes_192_ctr(void); +const EVP_CIPHER *EVP_aes_192_gcm(void); const EVP_CIPHER *EVP_aes_256_ecb(void); const EVP_CIPHER *EVP_aes_256_cbc(void); const EVP_CIPHER *EVP_aes_256_cfb1(void); @@ -786,6 +792,7 @@ const EVP_CIPHER *EVP_aes_256_cfb128(void); # define EVP_aes_256_cfb EVP_aes_256_cfb128 const EVP_CIPHER *EVP_aes_256_ofb(void); const EVP_CIPHER *EVP_aes_256_ctr(void); +const EVP_CIPHER *EVP_aes_256_gcm(void); #endif #ifndef OPENSSL_NO_CAMELLIA const EVP_CIPHER *EVP_camellia_128_ecb(void); -- 2.25.1