diff options
author | Matt Caswell <matt@openssl.org> | 2014-12-06 20:55:10 +0000 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2014-12-08 10:28:34 +0000 |
commit | e6b336efa31c9916bd64504a478d56532f86d6aa (patch) | |
tree | 66b5bd690c59a025b814685b4d43dffb2b38fb65 /crypto/evp/e_aes.c | |
parent | c857a80c9dccf82de94137410c754b18e893a55d (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.c | 408 |
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 |