Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 40 additions & 24 deletions src/wp_cmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ static void wp_cmac_free(wp_CmacCtx* macCtx)
* Allocates space for the key in the wolfSSL CMAC object.
*
* @param [in, out] macCtx CMAC context object.
* @param [in] key Key data to set.
* @param [in] key Key data to set. NULL restarts with the cached key.
* @param [in] keyLen Length of key data in bytes.
* @param [in] restart Restart CMAC calculation.
* @return 1 on success.
Expand All @@ -117,34 +117,43 @@ static int wp_cmac_set_key(wp_CmacCtx* macCtx, const unsigned char* key,

WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_cmac_set_key");

if (keyLen > AES_256_KEY_SIZE) {
/* A NULL key restarts with the key already cached. */
if (key == NULL) {
keyLen = macCtx->keyLen;
}

if (keyLen == 0) {
ok = 0;
}
if (ok && (macCtx->expKeySize != 0) && (keyLen != macCtx->expKeySize)) {
if (ok && (key != NULL) && (keyLen > AES_256_KEY_SIZE)) {
ok = 0;
}
if (ok) {
if (macCtx->keyLen > 0) {
OPENSSL_cleanse(macCtx->key, macCtx->keyLen);
}
macCtx->keyLen = keyLen;
if (ok && (key != NULL) && (macCtx->expKeySize != 0) &&
Comment thread
yosuke-wolfssl marked this conversation as resolved.
Comment thread
yosuke-wolfssl marked this conversation as resolved.
(keyLen != macCtx->expKeySize)) {
ok = 0;
}
if (ok && (key != NULL)) {
/* The cached length stays zero until the key is installed. */
OPENSSL_cleanse(macCtx->key, macCtx->keyLen);
macCtx->keyLen = 0;
XMEMCPY(macCtx->key, key, keyLen);

if (restart) {
#if LIBWOLFSSL_VERSION_HEX >= 0x05000000
int rc = wc_InitCmac_ex(&macCtx->cmac, macCtx->key,
(word32)macCtx->keyLen, macCtx->type, NULL, NULL,
INVALID_DEVID);
#else
int rc = wc_InitCmac(&macCtx->cmac, macCtx->key,
(word32)macCtx->keyLen, macCtx->type, NULL);
#endif
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_InitCmac/wc_InitCmac_ex", rc);
ok = 0;
}
}
if (ok && restart) {
#if LIBWOLFSSL_VERSION_HEX >= 0x05000000
int rc = wc_InitCmac_ex(&macCtx->cmac, macCtx->key, (word32)keyLen,
macCtx->type, NULL, NULL, INVALID_DEVID);
#else
int rc = wc_InitCmac(&macCtx->cmac, macCtx->key, (word32)keyLen,
macCtx->type, NULL);
#endif
if (rc != 0) {
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_InitCmac/wc_InitCmac_ex", rc);
ok = 0;
}
}
if (ok) {
macCtx->keyLen = keyLen;
}

WOLFPROV_LEAVE(WP_LOG_COMP_MAC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__), ok);
return ok;
Expand Down Expand Up @@ -190,7 +199,7 @@ static wp_CmacCtx* wp_cmac_dup(wp_CmacCtx* src)
* Initializes an CMAC context object with a key and parameters.
*
* @param [in, out] macCtx CMAC context object to initialize.
* @param [in] key Key data to set.
* @param [in] key Key data to set. NULL restarts with the cached key.
* @param [in] keyLen Length of key data in bytes.
* @param [in] params Extra parameters to set.
* @return 1 on success.
Expand All @@ -211,7 +220,7 @@ static int wp_cmac_init(wp_CmacCtx* macCtx, const unsigned char* key,
}
if (ok) {
macCtx->size = AES_BLOCK_SIZE;
if ((key != NULL) && (!wp_cmac_set_key(macCtx, key, keyLen, 1))) {
if (!wp_cmac_set_key(macCtx, key, keyLen, 1)) {
ok = 0;
}
}
Expand All @@ -236,6 +245,10 @@ static int wp_cmac_update(wp_CmacCtx* macCtx, const unsigned char* data,

WOLFPROV_ENTER(WP_LOG_COMP_MAC, "wp_cmac_update");

if (macCtx->keyLen == 0) {
ok = 0;
}

while (ok && (dataLen > 0)) {
word32 chunk = (!WP_FITS_WORD32(dataLen)) ?
0xFFFFFFFFU : (word32)dataLen;
Expand Down Expand Up @@ -278,6 +291,9 @@ static int wp_cmac_final(wp_CmacCtx* macCtx, unsigned char* out, size_t* outl,
if (ok && (outSize < macCtx->size)) {
ok = 0;
}
if (ok && (macCtx->keyLen == 0)) {
ok = 0;
}

if (ok) {
outSz = (word32)macCtx->size;
Expand Down
219 changes: 219 additions & 0 deletions test/test_cmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,225 @@ int test_cmac_multi_update(void *data)
return err;
}

/**
* Test that one CMAC context can be reset by calling EVP_MAC_init() with no
* key, both after EVP_MAC_final() and mid-stream, reusing the cached key.
*/
static int test_cmac_reinit_helper(OSSL_LIB_CTX *libCtx, unsigned char *macA,
unsigned char *macB)
{
int err;
EVP_MAC *emac = NULL;
EVP_MAC_CTX *ctx = NULL;
OSSL_PARAM params[3];
char cipher[] = "AES-256-CBC";
unsigned char key[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
};
unsigned char msgA[32];
unsigned char msgB[21];
unsigned char macC[AES_BLOCK_SIZE];
unsigned char macD[AES_BLOCK_SIZE];
size_t macASz = AES_BLOCK_SIZE;
size_t macBSz = AES_BLOCK_SIZE;
size_t macCSz = sizeof(macC);
size_t macDSz = sizeof(macD);

memset(msgA, 0x41, sizeof(msgA));
memset(msgB, 0x5a, sizeof(msgB));

params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER,
cipher, 0);
params[1] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
(void *)key, sizeof(key));
params[2] = OSSL_PARAM_construct_end();

err = (emac = EVP_MAC_fetch(libCtx, "CMAC", NULL)) == NULL;
if (err == 0) {
err = (ctx = EVP_MAC_CTX_new(emac)) == NULL;
}
if (err == 0) {
err = EVP_MAC_CTX_set_params(ctx, params) != 1;
}

/* First round with installed key by EVP_MAC_CTX_set_params() above. */
if (err == 0) {
err = EVP_MAC_init(ctx, NULL, 0, NULL) != 1;
}
if (err == 0) {
err = EVP_MAC_update(ctx, msgA, sizeof(msgA)) != 1;
}
if (err == 0) {
err = EVP_MAC_final(ctx, macA, &macASz, AES_BLOCK_SIZE) != 1;
}

/* Reset after final and MAC a different message with the cached key. */
if (err == 0) {
err = EVP_MAC_init(ctx, NULL, 0, NULL) != 1;
}
if (err == 0) {
err = EVP_MAC_update(ctx, msgB, sizeof(msgB)) != 1;
}
if (err == 0) {
err = EVP_MAC_final(ctx, macB, &macBSz, AES_BLOCK_SIZE) != 1;
}

/* The first message must produce the first MAC again. */
if (err == 0) {
err = EVP_MAC_init(ctx, NULL, 0, NULL) != 1;
}
if (err == 0) {
err = EVP_MAC_update(ctx, msgA, sizeof(msgA)) != 1;
}
if (err == 0) {
err = EVP_MAC_final(ctx, macC, &macCSz, sizeof(macC)) != 1;
}
if ((err == 0) && ((macCSz != macASz) ||
(memcmp(macC, macA, macASz) != 0))) {
PRINT_ERR_MSG("CMAC after reset doesn't match the first MAC");
err = 1;
}

/* A reset mid-stream must discard the data buffered so far. */
if (err == 0) {
err = EVP_MAC_init(ctx, NULL, 0, NULL) != 1;
}
if (err == 0) {
err = EVP_MAC_update(ctx, msgA, sizeof(msgA)) != 1;
}
if (err == 0) {
err = EVP_MAC_init(ctx, NULL, 0, NULL) != 1;
}
if (err == 0) {
err = EVP_MAC_update(ctx, msgB, sizeof(msgB)) != 1;
}
if (err == 0) {
err = EVP_MAC_final(ctx, macD, &macDSz, sizeof(macD)) != 1;
}
if ((err == 0) && ((macDSz != macBSz) ||
(memcmp(macD, macB, macBSz) != 0))) {
PRINT_ERR_MSG("CMAC after mid-stream reset covers stale data");
err = 1;
}

EVP_MAC_CTX_free(ctx);
EVP_MAC_free(emac);
return err;
}

/**
* A key that never made it into the CMAC object must not be usable on a
* later keyless restart.
*/
static int test_cmac_reinit_stale_key(OSSL_LIB_CTX *libCtx)
{
int err;
EVP_MAC *emac = NULL;
EVP_MAC_CTX *ctx = NULL;
OSSL_PARAM keyOnly[2];
OSSL_PARAM cipherOnly[2];
char cipher[] = "AES-128-CBC";
unsigned char key[32];
unsigned char msg[32];
unsigned char mac[AES_BLOCK_SIZE];
size_t macSz = sizeof(mac);

memset(key, 0x0b, sizeof(key));
memset(msg, 0x41, sizeof(msg));

keyOnly[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
(void *)key, sizeof(key));
keyOnly[1] = OSSL_PARAM_construct_end();
cipherOnly[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER,
cipher, 0);
cipherOnly[1] = OSSL_PARAM_construct_end();

err = (emac = EVP_MAC_fetch(libCtx, "CMAC", NULL)) == NULL;
if (err == 0) {
err = (ctx = EVP_MAC_CTX_new(emac)) == NULL;
}
/* No cipher is set yet, so installing the key cannot succeed. */
if (err == 0) {
err = EVP_MAC_CTX_set_params(ctx, keyOnly) == 1;
if (err != 0) {
PRINT_ERR_MSG("Setting a CMAC key with no cipher succeeded");
}
}
if (err == 0) {
err = EVP_MAC_CTX_set_params(ctx, cipherOnly) != 1;
}
/* The rejected key must not be picked up by a keyless restart. */
if (err == 0) {
err = EVP_MAC_init(ctx, NULL, 0, NULL) == 1;
if (err != 0) {
PRINT_ERR_MSG("CMAC restarted with a key that was never set");
}
}
if ((err == 0) && (EVP_MAC_update(ctx, msg, sizeof(msg)) == 1) &&
(EVP_MAC_final(ctx, mac, &macSz, sizeof(mac)) == 1)) {
PRINT_ERR_MSG("CMAC produced a MAC from a key that was never set");
err = 1;
}

/* EVP_MAC_final on its own must fail the same way. */
if (err == 0) {
EVP_MAC_CTX_free(ctx);
err = (ctx = EVP_MAC_CTX_new(emac)) == NULL;
}
if (err == 0) {
err = EVP_MAC_CTX_set_params(ctx, cipherOnly) != 1;
}
if (err == 0) {
macSz = sizeof(mac);
err = EVP_MAC_final(ctx, mac, &macSz, sizeof(mac)) == 1;
if (err != 0) {
PRINT_ERR_MSG("CMAC finalized with a key that was never set");
}
}

EVP_MAC_CTX_free(ctx);
EVP_MAC_free(emac);
return err;
}

int test_cmac_reinit(void *data)
Comment thread
yosuke-wolfssl marked this conversation as resolved.
{
int err;
unsigned char osslMacA[AES_BLOCK_SIZE];
unsigned char osslMacB[AES_BLOCK_SIZE];
unsigned char wpMacA[AES_BLOCK_SIZE];
unsigned char wpMacB[AES_BLOCK_SIZE];

(void)data;

PRINT_MSG("CMAC context reset with OpenSSL");
err = test_cmac_reinit_helper(osslLibCtx, osslMacA, osslMacB);
if (err == 0) {
PRINT_MSG("CMAC context reset with wolfProvider");
err = test_cmac_reinit_helper(wpLibCtx, wpMacA, wpMacB);
}
if ((err == 0) && (memcmp(osslMacA, wpMacA, AES_BLOCK_SIZE) != 0)) {
PRINT_ERR_MSG("First CMAC doesn't match OpenSSL");
err = 1;
}
if ((err == 0) && (memcmp(osslMacB, wpMacB, AES_BLOCK_SIZE) != 0)) {
PRINT_ERR_MSG("CMAC after reset doesn't match OpenSSL");
err = 1;
}
if (err == 0) {
PRINT_MSG("CMAC keyless restart with a rejected key, OpenSSL");
err = test_cmac_reinit_stale_key(osslLibCtx);
}
if (err == 0) {
PRINT_MSG("CMAC keyless restart with a rejected key, wolfProvider");
err = test_cmac_reinit_stale_key(wpLibCtx);
}
return err;
}

int test_cmac_dup(void *data)
{
int ret = 0;
Expand Down
1 change: 1 addition & 0 deletions test/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ TEST_CASE test_case[] = {
#ifdef WP_HAVE_CMAC
TEST_DECL(test_cmac_create, &flags),
TEST_DECL(test_cmac_multi_update, &flags),
TEST_DECL(test_cmac_reinit, &flags),
TEST_DECL(test_cmac_dup, &flags),
TEST_DECL(test_cmac_size_query, &flags),
TEST_DECL(test_cmac_large_buffer, &flags),
Expand Down
1 change: 1 addition & 0 deletions test/unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ int test_mac_sig_dup(void *data);
#ifdef WP_HAVE_CMAC
int test_cmac_create(void *data);
int test_cmac_multi_update(void *data);
int test_cmac_reinit(void *data);
int test_cmac_dup(void *data);
int test_cmac_size_query(void *data);
int test_cmac_large_buffer(void *data);
Expand Down
Loading