summaryrefslogtreecommitdiffstats
path: root/crypto/evp/e_aes.c
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2014-12-06 20:55:10 +0000
committerMatt Caswell <matt@openssl.org>2014-12-08 10:28:34 +0000
commite6b336efa31c9916bd64504a478d56532f86d6aa (patch)
tree66b5bd690c59a025b814685b4d43dffb2b38fb65 /crypto/evp/e_aes.c
parentc857a80c9dccf82de94137410c754b18e893a55d (diff)
Add EVP support for OCB mode
Reviewed-by: Tim Hudson <tjh@openssl.org>
Diffstat (limited to 'crypto/evp/e_aes.c')
-rw-r--r--crypto/evp/e_aes.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/crypto/evp/e_aes.c b/crypto/evp/e_aes.c
index 776262343c..ae1a2b961c 100644
--- a/crypto/evp/e_aes.c
+++ b/crypto/evp/e_aes.c
@@ -107,6 +107,24 @@ typedef struct
ccm128_f str;
} EVP_AES_CCM_CTX;
+
+typedef struct
+ {
+ AES_KEY ksenc; /* AES key schedule to use for encryption */
+ AES_KEY ksdec; /* AES key schedule to use for decryption */
+ int key_set; /* Set if key initialised */
+ int iv_set; /* Set if an iv is set */
+ OCB128_CONTEXT ocb;
+ unsigned char *iv; /* Temporary IV store */
+ unsigned char tag[16];
+ unsigned char data_buf[16]; /* Store partial data blocks */
+ unsigned char aad_buf[16]; /* Store partial AAD blocks */
+ int data_buf_len;
+ int aad_buf_len;
+ int ivlen; /* IV length */
+ int taglen;
+ } EVP_AES_OCB_CTX;
+
#define MAXBITCHUNK ((size_t)1<<(sizeof(size_t)*8-4))
#ifdef VPAES_ASM
@@ -451,6 +469,58 @@ static int aesni_ccm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
static int aesni_ccm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, size_t len);
+static int aesni_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc)
+ {
+ EVP_AES_OCB_CTX *octx = ctx->cipher_data;
+ if (!iv && !key)
+ return 1;
+ if (key)
+ {
+ do
+ {
+ /* We set both the encrypt and decrypt key here because decrypt
+ * needs both. We could possibly optimise to remove setting the
+ * decrypt for an encryption operation.
+ */
+ aesni_set_encrypt_key(key, ctx->key_len * 8, &octx->ksenc);
+ aesni_set_decrypt_key(key, ctx->key_len * 8, &octx->ksdec);
+ if(!CRYPTO_ocb128_init(&octx->ocb, &octx->ksenc, &octx->ksdec,
+ (block128_f)aesni_encrypt, (block128_f)aesni_decrypt))
+ return 0;
+ }
+ while (0);
+
+ /* If we have an iv we can set it directly, otherwise use
+ * saved IV.
+ */
+ if (iv == NULL && octx->iv_set)
+ iv = octx->iv;
+ if (iv)
+ {
+ if(CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen) != 1)
+ return 0;
+ octx->iv_set = 1;
+ }
+ octx->key_set = 1;
+ }
+ else
+ {
+ /* If key set use IV, otherwise copy */
+ if (octx->key_set)
+ CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+ else
+ memcpy(octx->iv, iv, octx->ivlen);
+ octx->iv_set = 1;
+ }
+ return 1;
+ }
+
+#define aesni_ocb_cipher aes_ocb_cipher
+static int aesni_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len);
+
+
#define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
static const EVP_CIPHER aesni_##keylen##_##mode = { \
nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
@@ -837,6 +907,59 @@ static int aes_t4_ccm_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
static int aes_t4_ccm_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
const unsigned char *in, size_t len);
+
+static int aes_t4_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc)
+ {
+ EVP_AES_OCB_CTX *octx = ctx->cipher_data;
+ if (!iv && !key)
+ return 1;
+ if (key)
+ {
+ do
+ {
+ /* We set both the encrypt and decrypt key here because decrypt
+ * needs both. We could possibly optimise to remove setting the
+ * decrypt for an encryption operation.
+ */
+ aes_t4_set_encrypt_key(key, ctx->key_len * 8, &octx->ksenc);
+ aes_t4_set_decrypt_key(key, ctx->key_len * 8, &octx->ksdec);
+ if(!CRYPTO_ocb128_init(&octx->ocb, &octx->ksenc, &octx->ksdec,
+ (block128_f)aes_t4_encrypt, (block128_f)aes_t4_decrypt))
+ return 0;
+ }
+ while (0);
+
+ /* If we have an iv we can set it directly, otherwise use
+ * saved IV.
+ */
+ if (iv == NULL && octx->iv_set)
+ iv = octx->iv;
+ if (iv)
+ {
+ if(CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen) != 1)
+ return 0;
+ octx->iv_set = 1;
+ }
+ octx->key_set = 1;
+ }
+ else
+ {
+ /* If key set use IV, otherwise copy */
+ if (octx->key_set)
+ CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+ else
+ memcpy(octx->iv, iv, octx->ivlen);
+ octx->iv_set = 1;
+ }
+ return 1;
+ }
+
+#define aes_t4_ocb_cipher aes_ocb_cipher
+static int aes_t4_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len);
+
+
#define BLOCK_CIPHER_generic(nid,keylen,blocksize,ivlen,nmode,mode,MODE,flags) \
static const EVP_CIPHER aes_t4_##keylen##_##mode = { \
nid##_##keylen##_##nmode,blocksize,keylen/8,ivlen, \
@@ -2238,4 +2361,289 @@ const EVP_CIPHER *EVP_aes_256_wrap_pad(void)
return &aes_256_wrap_pad;
}
+static int aes_ocb_ctrl(EVP_CIPHER_CTX *c, int type, int arg, void *ptr)
+ {
+ EVP_AES_OCB_CTX *octx = c->cipher_data;
+ EVP_CIPHER_CTX *newc;
+ EVP_AES_OCB_CTX *new_octx;
+
+ switch (type)
+ {
+ case EVP_CTRL_INIT:
+ octx->key_set = 0;
+ octx->iv_set = 0;
+ octx->ivlen = c->cipher->iv_len;
+ octx->iv = c->iv;
+ octx->taglen = 16;
+ octx->data_buf_len = 0;
+ octx->aad_buf_len = 0;
+ return 1;
+
+ case EVP_CTRL_SET_IVLEN:
+ /* IV len must be 1 to 15 */
+ if (arg <= 0 || arg > 15)
+ return 0;
+
+ octx->ivlen = arg;
+ return 1;
+
+ case EVP_CTRL_OCB_SET_TAGLEN:
+ /* Tag len must be 0 to 16 */
+ if (arg < 0 || arg > 16)
+ return 0;
+
+ octx->taglen = arg;
+ return 1;
+
+ case EVP_CTRL_SET_TAG:
+ if (arg != octx->taglen || c->encrypt)
+ return 0;
+ memcpy(octx->tag, ptr, arg);
+ return 1;
+
+ case EVP_CTRL_GET_TAG:
+ if (arg != octx->taglen || !c->encrypt)
+ return 0;
+
+ memcpy(ptr, octx->tag, arg);
+ return 1;
+
+ case EVP_CTRL_COPY:
+ newc = (EVP_CIPHER_CTX *)ptr;
+ new_octx = newc->cipher_data;
+ return CRYPTO_ocb128_copy_ctx(&new_octx->ocb, &octx->ocb,
+ &new_octx->ksenc, &new_octx->ksdec);
+
+ default:
+ return -1;
+
+ }
+ }
+
+
+static int aes_ocb_init_key(EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc)
+ {
+ EVP_AES_OCB_CTX *octx = ctx->cipher_data;
+ if (!iv && !key)
+ return 1;
+ if (key)
+ {
+ do
+ {
+ /* We set both the encrypt and decrypt key here because decrypt
+ * needs both. We could possibly optimise to remove setting the
+ * decrypt for an encryption operation.
+ */
+#ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE)
+ {
+ vpaes_set_encrypt_key(key,ctx->key_len*8,&octx->ksenc);
+ vpaes_set_decrypt_key(key,ctx->key_len*8,&octx->ksdec);
+ if(!CRYPTO_ocb128_init(&octx->ocb,&octx->ksenc,&octx->ksdec,
+ (block128_f)vpaes_encrypt,(block128_f)vpaes_decrypt))
+ return 0;
+ break;
+ }
+#endif
+ AES_set_encrypt_key(key, ctx->key_len * 8, &octx->ksenc);
+ AES_set_decrypt_key(key, ctx->key_len * 8, &octx->ksdec);
+ if(!CRYPTO_ocb128_init(&octx->ocb, &octx->ksenc, &octx->ksdec,
+ (block128_f)AES_encrypt, (block128_f)AES_decrypt))
+ return 0;
+ }
+ while (0);
+
+ /* If we have an iv we can set it directly, otherwise use
+ * saved IV.
+ */
+ if (iv == NULL && octx->iv_set)
+ iv = octx->iv;
+ if (iv)
+ {
+ if(CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen) != 1)
+ return 0;
+ octx->iv_set = 1;
+ }
+ octx->key_set = 1;
+ }
+ else
+ {
+ /* If key set use IV, otherwise copy */
+ if (octx->key_set)
+ CRYPTO_ocb128_setiv(&octx->ocb, iv, octx->ivlen, octx->taglen);
+ else
+ memcpy(octx->iv, iv, octx->ivlen);
+ octx->iv_set = 1;
+ }
+ return 1;
+ }
+
+static int aes_ocb_cipher(EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+ {
+ unsigned char *buf;
+ int *buf_len;
+ int written_len = 0;
+ size_t trailing_len;
+ EVP_AES_OCB_CTX *octx = ctx->cipher_data;
+
+ /* If IV or Key not set then return error */
+ if (!octx->iv_set)
+ return -1;
+
+ if (!octx->key_set)
+ return -1;
+
+ if (in)
+ {
+ /* Need to ensure we are only passing full blocks to low level OCB
+ * routines. We do it here rather than in EVP_EncryptUpdate/
+ * EVP_DecryptUpdate because we need to pass full blocks of AAD too
+ * and those routines don't support that
+ */
+
+ /* Are we dealing with AAD or normal data here? */
+ if (out == NULL)
+ {
+ buf = octx->aad_buf;
+ buf_len = &(octx->aad_buf_len);
+ }
+ else
+ {
+ buf = octx->data_buf;
+ buf_len = &(octx->data_buf_len);
+ }
+
+ /* If we've got a partially filled buffer from a previous call then use
+ * that data first
+ */
+ if(*buf_len)
+ {
+ unsigned int remaining;
+
+ remaining = 16 - (*buf_len);
+ if(remaining > len)
+ {
+ memcpy(buf+(*buf_len), in, len);
+ *(buf_len)+=len;
+ return 0;
+ }
+ memcpy(buf+(*buf_len), in, remaining);
+
+ /* If we get here we've filled the buffer, so process it */
+ len -= remaining;
+ in += remaining;
+ if (out == NULL)
+ {
+ if(!CRYPTO_ocb128_aad(&octx->ocb, buf, 16))
+ return -1;
+ }
+ else if (ctx->encrypt)
+ {
+ if(!CRYPTO_ocb128_encrypt(&octx->ocb, buf, out, 16))
+ return -1;
+ }
+ else
+ {
+ if(!CRYPTO_ocb128_decrypt(&octx->ocb, buf, out, 16))
+ return -1;
+ }
+ written_len = 16;
+ *buf_len = 0;
+ }
+
+ /* Do we have a partial block to handle at the end? */
+ trailing_len = len % 16;
+
+ /* If we've got some full blocks to handle, then process these first */
+ if(len != trailing_len)
+ {
+ if (out == NULL)
+ {
+ if(!CRYPTO_ocb128_aad(&octx->ocb, in, len-trailing_len))
+ return -1;
+ }
+ else if (ctx->encrypt)
+ {
+ if(!CRYPTO_ocb128_encrypt(&octx->ocb, in, out, len-trailing_len))
+ return -1;
+ }
+ else
+ {
+ if(!CRYPTO_ocb128_decrypt(&octx->ocb, in, out, len-trailing_len))
+ return -1;
+ }
+ written_len += len-trailing_len;
+ in += len-trailing_len;
+ }
+
+ /* Handle any trailing partial block */
+ if(trailing_len)
+ {
+ memcpy(buf, in, trailing_len);
+ *buf_len = trailing_len;
+ }
+
+ return written_len;
+ }
+ else
+ {
+ /* First of all empty the buffer of any partial block that we might
+ * have been provided - both for data and AAD
+ */
+ if(octx->data_buf_len)
+ {
+ if (ctx->encrypt)
+ {
+ if(!CRYPTO_ocb128_encrypt(&octx->ocb, octx->data_buf, out,
+ octx->data_buf_len))
+ return -1;
+ }
+ else
+ {
+ if(!CRYPTO_ocb128_decrypt(&octx->ocb, octx->data_buf, out,
+ octx->data_buf_len))
+ return -1;
+ }
+ written_len = octx->data_buf_len;
+ octx->data_buf_len = 0;
+ }
+ if(octx->aad_buf_len)
+ {
+ if(!CRYPTO_ocb128_aad(&octx->ocb, octx->aad_buf, octx->aad_buf_len))
+ return -1;
+ octx->aad_buf_len = 0;
+ }
+ /* If decrypting then verify */
+ if (!ctx->encrypt)
+ {
+ if (octx->taglen < 0)
+ return -1;
+ if (CRYPTO_ocb128_finish(&octx->ocb,
+ octx->tag, octx->taglen) != 0)
+ return -1;
+ octx->iv_set = 0;
+ return written_len;
+ }
+ /* If encrypting then just get the tag */
+ if(CRYPTO_ocb128_tag(&octx->ocb, octx->tag, 16) != 1)
+ return -1;
+ /* Don't reuse the IV */
+ octx->iv_set = 0;
+ return written_len;
+ }
+ }
+
+static int aes_ocb_cleanup(EVP_CIPHER_CTX *c)
+ {
+ EVP_AES_OCB_CTX *octx = c->cipher_data;
+ CRYPTO_ocb128_cleanup(&octx->ocb);
+ return 1;
+ }
+
+BLOCK_CIPHER_custom(NID_aes,128,16,12,ocb,OCB,CUSTOM_FLAGS)
+BLOCK_CIPHER_custom(NID_aes,192,16,12,ocb,OCB,CUSTOM_FLAGS)
+BLOCK_CIPHER_custom(NID_aes,256,16,12,ocb,OCB,CUSTOM_FLAGS)
+
#endif