/*
- * Copyright 2010-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2010-2023 The OpenSSL Project Authors. All Rights Reserved.
*
- * Licensed under the OpenSSL license (the "License"). You may not use
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
+/*
+ * CMAC low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "internal/cryptlib.h"
#include <openssl/cmac.h>
+#include <openssl/err.h>
+#define LOCAL_BUF_SIZE 2048
struct CMAC_CTX_st {
/* Cipher context to use */
EVP_CIPHER_CTX *cctx;
{
CMAC_CTX *ctx;
- ctx = OPENSSL_malloc(sizeof(*ctx));
- if (ctx == NULL)
+ if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
return NULL;
ctx->cctx = EVP_CIPHER_CTX_new();
if (ctx->cctx == NULL) {
void CMAC_CTX_cleanup(CMAC_CTX *ctx)
{
- EVP_CIPHER_CTX_cleanup(ctx->cctx);
+ EVP_CIPHER_CTX_reset(ctx->cctx);
OPENSSL_cleanse(ctx->tbl, EVP_MAX_BLOCK_LENGTH);
OPENSSL_cleanse(ctx->k1, EVP_MAX_BLOCK_LENGTH);
OPENSSL_cleanse(ctx->k2, EVP_MAX_BLOCK_LENGTH);
int CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in)
{
int bl;
+
if (in->nlast_block == -1)
return 0;
+ if ((bl = EVP_CIPHER_CTX_get_block_size(in->cctx)) == 0)
+ return 0;
if (!EVP_CIPHER_CTX_copy(out->cctx, in->cctx))
return 0;
- bl = EVP_CIPHER_CTX_block_size(in->cctx);
memcpy(out->k1, in->k1, bl);
memcpy(out->k2, in->k2, bl);
memcpy(out->tbl, in->tbl, bl);
const EVP_CIPHER *cipher, ENGINE *impl)
{
static const unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH] = { 0 };
+ int block_len;
+
/* All zeros means restart */
if (!key && !cipher && !impl && keylen == 0) {
/* Not initialised */
return 0;
if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, zero_iv))
return 0;
- memset(ctx->tbl, 0, EVP_CIPHER_CTX_block_size(ctx->cctx));
+ block_len = EVP_CIPHER_CTX_get_block_size(ctx->cctx);
+ if (block_len == 0)
+ return 0;
+ memset(ctx->tbl, 0, block_len);
ctx->nlast_block = 0;
return 1;
}
/* Initialise context */
- if (cipher && !EVP_EncryptInit_ex(ctx->cctx, cipher, impl, NULL, NULL))
- return 0;
+ if (cipher != NULL) {
+ /* Ensure we can't use this ctx until we also have a key */
+ ctx->nlast_block = -1;
+ if (!EVP_EncryptInit_ex(ctx->cctx, cipher, impl, NULL, NULL))
+ return 0;
+ }
/* Non-NULL key means initialisation complete */
- if (key) {
+ if (key != NULL) {
int bl;
- if (!EVP_CIPHER_CTX_cipher(ctx->cctx))
+
+ /* If anything fails then ensure we can't use this ctx */
+ ctx->nlast_block = -1;
+ if (EVP_CIPHER_CTX_get0_cipher(ctx->cctx) == NULL)
return 0;
- if (!EVP_CIPHER_CTX_set_key_length(ctx->cctx, keylen))
+ if (EVP_CIPHER_CTX_set_key_length(ctx->cctx, keylen) <= 0)
return 0;
if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, key, zero_iv))
return 0;
- bl = EVP_CIPHER_CTX_block_size(ctx->cctx);
- if (!EVP_Cipher(ctx->cctx, ctx->tbl, zero_iv, bl))
+ if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) < 0)
+ return 0;
+ if (EVP_Cipher(ctx->cctx, ctx->tbl, zero_iv, bl) <= 0)
return 0;
make_kn(ctx->k1, ctx->tbl, bl);
make_kn(ctx->k2, ctx->k1, bl);
int CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen)
{
const unsigned char *data = in;
- size_t bl;
+ int bl;
+ size_t max_burst_blocks, cipher_blocks;
+ unsigned char buf[LOCAL_BUF_SIZE];
+
if (ctx->nlast_block == -1)
return 0;
if (dlen == 0)
return 1;
- bl = EVP_CIPHER_CTX_block_size(ctx->cctx);
+ if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) == 0)
+ return 0;
/* Copy into partial block if we need to */
if (ctx->nlast_block > 0) {
size_t nleft;
+
nleft = bl - ctx->nlast_block;
if (dlen < nleft)
nleft = dlen;
return 1;
data += nleft;
/* Else not final block so encrypt it */
- if (!EVP_Cipher(ctx->cctx, ctx->tbl, ctx->last_block, bl))
+ if (EVP_Cipher(ctx->cctx, ctx->tbl, ctx->last_block, bl) <= 0)
return 0;
}
/* Encrypt all but one of the complete blocks left */
- while (dlen > bl) {
- if (!EVP_Cipher(ctx->cctx, ctx->tbl, data, bl))
- return 0;
- dlen -= bl;
- data += bl;
+
+ max_burst_blocks = LOCAL_BUF_SIZE / bl;
+ cipher_blocks = (dlen - 1) / bl;
+ if (max_burst_blocks == 0) {
+ /*
+ * When block length is greater than local buffer size,
+ * use ctx->tbl as cipher output.
+ */
+ while (dlen > (size_t)bl) {
+ if (EVP_Cipher(ctx->cctx, ctx->tbl, data, bl) <= 0)
+ return 0;
+ dlen -= bl;
+ data += bl;
+ }
+ } else {
+ while (cipher_blocks > max_burst_blocks) {
+ if (EVP_Cipher(ctx->cctx, buf, data, max_burst_blocks * bl) <= 0)
+ return 0;
+ dlen -= max_burst_blocks * bl;
+ data += max_burst_blocks * bl;
+ cipher_blocks -= max_burst_blocks;
+ }
+ if (cipher_blocks > 0) {
+ if (EVP_Cipher(ctx->cctx, buf, data, cipher_blocks * bl) <= 0)
+ return 0;
+ dlen -= cipher_blocks * bl;
+ data += cipher_blocks * bl;
+ memcpy(ctx->tbl, &buf[(cipher_blocks - 1) * bl], bl);
+ }
}
/* Copy any data left to last block buffer */
memcpy(ctx->last_block, data, dlen);
int CMAC_Final(CMAC_CTX *ctx, unsigned char *out, size_t *poutlen)
{
int i, bl, lb;
+
if (ctx->nlast_block == -1)
return 0;
- bl = EVP_CIPHER_CTX_block_size(ctx->cctx);
- *poutlen = (size_t)bl;
+ if ((bl = EVP_CIPHER_CTX_get_block_size(ctx->cctx)) == 0)
+ return 0;
+ if (poutlen != NULL)
+ *poutlen = (size_t)bl;
if (!out)
return 1;
lb = ctx->nlast_block;
for (i = 0; i < bl; i++)
out[i] = ctx->last_block[i] ^ ctx->k2[i];
}
- if (!EVP_Cipher(ctx->cctx, out, out, bl)) {
+ if (EVP_Cipher(ctx->cctx, out, out, bl) <= 0) {
OPENSSL_cleanse(out, bl);
return 0;
}