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
320 changes: 290 additions & 30 deletions drivers/builtin/src/psa_crypto_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include <mbedtls/error_common.h>
#include "mbedtls/constant_time.h"
#include "constant_time_internal.h"
#include <string.h>

#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
Expand Down Expand Up @@ -154,11 +155,139 @@ static psa_status_t psa_hmac_finish_internal(
#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */

#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
static psa_status_t cmac_setup(mbedtls_psa_mac_operation_t *operation,
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer)
/*
* Multiplication by u in the Galois field of GF(2^n)
*
* As explained in NIST SP 800-38B, this can be computed:
*
* If MSB(p) = 0, then p = (p << 1)
* If MSB(p) = 1, then p = (p << 1) ^ R_n
* with R_64 = 0x1B and R_128 = 0x87
*
* Input and output MUST NOT point to the same buffer
* Block size must be 8 bytes or 16 bytes - the block sizes for DES and AES.
*/
static int cmac_multiply_by_u(unsigned char *output,
const unsigned char *input,
size_t blocksize)
{
const unsigned char R_128 = 0x87;
unsigned char R_n;
uint32_t overflow = 0x00;
int i;

if (blocksize == PSA_AES_BLOCK_SIZE) {
R_n = R_128;
}
#if defined(PSA_WANT_KEY_TYPE_DES)
else if (blocksize == PSA_DES_BLOCK_SIZE) {
const unsigned char R_64 = 0x1B;
R_n = R_64;
}
#endif
else {
return PSA_ERROR_INVALID_ARGUMENT;
}

for (i = (int) blocksize - 4; i >= 0; i -= 4) {
uint32_t i32 = MBEDTLS_GET_UINT32_BE(&input[i], 0);
uint32_t new_overflow = i32 >> 31;
i32 = (i32 << 1) | overflow;
MBEDTLS_PUT_UINT32_BE(i32, &output[i], 0);
overflow = new_overflow;
}

R_n = (unsigned char) mbedtls_ct_uint_if_else_0(mbedtls_ct_bool(input[0] >> 7), R_n);
output[blocksize - 1] ^= R_n;

return 0;
}

/*
* Generate subkeys
*
* - as specified by RFC 4493, section 2.3 Subkey Generation Algorithm
*/
static int cmac_generate_subkeys(psa_cipher_operation_t *ctx, size_t block_size,
unsigned char *K1, unsigned char *K2)
{
int ret = PSA_ERROR_CORRUPTION_DETECTED;
unsigned char L[PSA_CMAC_MAX_BLOCK_SIZE];
size_t olen;

mbedtls_platform_zeroize(L, sizeof(L));

/* Calculate Ek(0) */
if ((ret = psa_cipher_update(ctx, L, block_size, L, PSA_CMAC_MAX_BLOCK_SIZE, &olen)) != 0) {
goto exit;
}

/*
* Generate K1 and K2
*/
if ((ret = cmac_multiply_by_u(K1, L, block_size)) != 0) {
goto exit;
}

if ((ret = cmac_multiply_by_u(K2, K1, block_size)) != 0) {
goto exit;
}

exit:
mbedtls_platform_zeroize(L, sizeof(L));

return ret;
}

/*
* Create padded last block from (partial) last block.
*
* We can't use the padding option from the cipher layer, as it only works for
* CBC and we use ECB mode, and anyway we need to XOR K1 or K2 in addition.
*/
static void cmac_pad(unsigned char padded_block[PSA_CMAC_MAX_BLOCK_SIZE],
size_t padded_block_len,
const unsigned char *last_block,
size_t last_block_len)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t j;

for (j = 0; j < padded_block_len; j++) {
if (j < last_block_len) {
padded_block[j] = last_block[j];
} else if (j == last_block_len) {
padded_block[j] = 0x80;
} else {
padded_block[j] = 0x00;
}
}
}

static psa_status_t psa_cmac_abort_internal(
mbedtls_psa_cmac_operation_t *cmac)
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;

status = psa_destroy_key(cmac->key_id);
if (status != PSA_SUCCESS) {
return status;
}

status = psa_cipher_abort(&cmac->cipher_ctx);
if (status != PSA_SUCCESS) {
return status;
}

mbedtls_platform_zeroize(cmac, sizeof(mbedtls_psa_cmac_operation_t));
return status;
}

static psa_status_t psa_cmac_setup_internal(mbedtls_psa_cmac_operation_t *cmac,
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size)
{
int status = PSA_ERROR_CORRUPTION_DETECTED;

#if defined(PSA_WANT_KEY_TYPE_DES)
/* Mbed TLS CMAC does not accept 3DES with only two keys, nor does it accept
Expand All @@ -170,27 +299,167 @@ static psa_status_t cmac_setup(mbedtls_psa_mac_operation_t *operation,
}
#endif

const mbedtls_cipher_info_t *cipher_info =
mbedtls_cipher_info_from_psa(
PSA_ALG_CMAC,
psa_get_key_type(attributes),
psa_get_key_bits(attributes),
NULL);
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_type_t key_type = psa_get_key_type(attributes);
size_t key_bits = psa_get_key_bits(attributes);
psa_algorithm_t alg = PSA_ALG_ECB_NO_PADDING;

/* Set up key attributes for PSA import */
psa_set_key_type(&key_attributes, key_type);
psa_set_key_bits(&key_attributes, key_bits);
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_ENCRYPT);
psa_set_key_algorithm(&key_attributes, alg);

/* Import key for cipher operations */
status = psa_import_key(&key_attributes, key_buffer, key_buffer_size, &cmac->key_id);
if (status != PSA_SUCCESS) {
return status;
}

status = psa_cipher_encrypt_setup(&cmac->cipher_ctx, cmac->key_id, alg);
if (status != 0) {
return status;
}

const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_psa(alg, key_type, key_bits, NULL);
if (cipher_info == NULL) {
return PSA_ERROR_NOT_SUPPORTED;
}

ret = mbedtls_cipher_setup(&operation->ctx.cmac, cipher_info);
if (ret != 0) {
/* Ensure we're using a supported block cipher */
switch (cipher_info->type) {
case MBEDTLS_CIPHER_AES_128_ECB:
case MBEDTLS_CIPHER_AES_192_ECB:
case MBEDTLS_CIPHER_AES_256_ECB:
case MBEDTLS_CIPHER_DES_EDE3_ECB:
break;
default:
return PSA_ERROR_INVALID_ARGUMENT;
}

cmac->unprocessed_len = 0;
cmac->cipher_block_length = mbedtls_cipher_info_get_block_size(cipher_info);
mbedtls_platform_zeroize(cmac->state, sizeof(cmac->state));
mbedtls_platform_zeroize(cmac->unprocessed_block, sizeof(cmac->unprocessed_block));
return PSA_SUCCESS;
}

static psa_status_t psa_cmac_update_internal(
mbedtls_psa_cmac_operation_t *cmac,
const uint8_t *data,
size_t data_length)
{
unsigned char *state = cmac->state;
int ret = 0;
size_t n, j, olen, block_size;

if (cmac == NULL || data == NULL) {
return PSA_ERROR_INVALID_ARGUMENT;
}

block_size = cmac->cipher_block_length;

/* Without the MBEDTLS_ASSUME below, gcc -O3 will generate a warning of the form
* error: writing 16 bytes into a region of size 0 [-Werror=stringop-overflow=] */
MBEDTLS_ASSUME(block_size <= PSA_CMAC_MAX_BLOCK_SIZE);

/* Is there data still to process from the last call, that's greater in
* size than a block? */
if (cmac->unprocessed_len > 0 &&
data_length > block_size - cmac->unprocessed_len) {
memcpy(&cmac->unprocessed_block[cmac->unprocessed_len],
data,
block_size - cmac->unprocessed_len);

mbedtls_xor_no_simd(state, cmac->unprocessed_block, state, block_size);

if ((ret = psa_cipher_update(&cmac->cipher_ctx, state, block_size, state, PSA_CMAC_MAX_BLOCK_SIZE, &olen)) != 0) {
goto exit;
}

data += block_size - cmac->unprocessed_len;
data_length -= block_size - cmac->unprocessed_len;
cmac->unprocessed_len = 0;
}

/* n is the number of blocks including any final partial block */
n = (data_length + block_size - 1) / block_size;

/* Iterate across the input data in block sized chunks, excluding any
* final partial or complete block */
for (j = 1; j < n; j++) {
mbedtls_xor_no_simd(state, data, state, block_size);

if ((ret = psa_cipher_update(&cmac->cipher_ctx, state, block_size, state, PSA_CMAC_MAX_BLOCK_SIZE, &olen)) != 0) {
goto exit;
}

data_length -= block_size;
data += block_size;
}

/* If there is data left over that wasn't aligned to a block */
if (data_length > 0) {
memcpy(&cmac->unprocessed_block[cmac->unprocessed_len],
data,
data_length);
cmac->unprocessed_len += data_length;
}

exit:
return ret;
}

static psa_status_t psa_cmac_finish_internal(
mbedtls_psa_cmac_operation_t *cmac,
uint8_t *mac, size_t mac_size)
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t olen, block_size;

unsigned char *state, *last_block;
unsigned char K1[PSA_CMAC_MAX_BLOCK_SIZE];
unsigned char K2[PSA_CMAC_MAX_BLOCK_SIZE];
unsigned char M_last[PSA_CMAC_MAX_BLOCK_SIZE];

if (cmac == NULL || mac == NULL) {
return PSA_ERROR_INVALID_ARGUMENT;
}

state = cmac->state;
block_size = cmac->cipher_block_length;

mbedtls_platform_zeroize(K1, sizeof(K1));
mbedtls_platform_zeroize(K2, sizeof(K2));
cmac_generate_subkeys(&cmac->cipher_ctx, block_size, K1, K2);

last_block = cmac->unprocessed_block;

/* Calculate last block */
if (cmac->unprocessed_len < block_size) {
cmac_pad(M_last, block_size, last_block, cmac->unprocessed_len);
mbedtls_xor(M_last, M_last, K2, block_size);
} else {
/* Last block is complete block */
mbedtls_xor(M_last, last_block, K1, block_size);
}

mbedtls_xor(state, M_last, state, block_size);
if ((status = psa_cipher_update(&cmac->cipher_ctx, state, block_size, state, PSA_CMAC_MAX_BLOCK_SIZE, &olen)) != 0) {
goto exit;
}

ret = mbedtls_cipher_cmac_starts(&operation->ctx.cmac,
key_buffer,
psa_get_key_bits(attributes));
memcpy(mac, state, mac_size);

exit:
return mbedtls_to_psa_error(ret);
mbedtls_platform_zeroize(K1, sizeof(K1));
mbedtls_platform_zeroize(K2, sizeof(K2));

cmac->unprocessed_len = 0;
mbedtls_platform_zeroize(cmac->unprocessed_block, sizeof(cmac->unprocessed_block));
mbedtls_platform_zeroize(state, PSA_CMAC_MAX_BLOCK_SIZE);

return status;
}
#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */

Expand All @@ -209,7 +478,7 @@ static psa_status_t mac_init(

#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
mbedtls_cipher_init(&operation->ctx.cmac);
memset(&operation->ctx.cmac, 0, sizeof(operation->ctx.cmac));
status = PSA_SUCCESS;
} else
#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
Expand Down Expand Up @@ -241,7 +510,7 @@ psa_status_t mbedtls_psa_mac_abort(mbedtls_psa_mac_operation_t *operation)
} else
#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
mbedtls_cipher_free(&operation->ctx.cmac);
return psa_cmac_abort_internal(&operation->ctx.cmac);
} else
#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
Expand Down Expand Up @@ -290,8 +559,7 @@ static psa_status_t psa_mac_setup(mbedtls_psa_mac_operation_t *operation,
if (PSA_ALG_FULL_LENGTH_MAC(alg) == PSA_ALG_CMAC) {
/* Key buffer size for CMAC is dictated by the key bits set on the
* attributes, and previously validated by the core on key import. */
(void) key_buffer_size;
status = cmac_setup(operation, attributes, key_buffer);
status = psa_cmac_setup_internal(&operation->ctx.cmac, attributes, key_buffer, key_buffer_size);
} else
#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
Expand Down Expand Up @@ -349,9 +617,7 @@ psa_status_t mbedtls_psa_mac_update(

#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
return mbedtls_to_psa_error(
mbedtls_cipher_cmac_update(&operation->ctx.cmac,
input, input_length));
return psa_cmac_update_internal(&operation->ctx.cmac, input, input_length);
} else
#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
Expand All @@ -375,13 +641,7 @@ static psa_status_t psa_mac_finish_internal(
{
#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
uint8_t tmp[PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE];
int ret = mbedtls_cipher_cmac_finish(&operation->ctx.cmac, tmp);
if (ret == 0) {
memcpy(mac, tmp, mac_size);
}
mbedtls_platform_zeroize(tmp, sizeof(tmp));
return mbedtls_to_psa_error(ret);
return psa_cmac_finish_internal(&operation->ctx.cmac, mac, mac_size);
} else
#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
Expand Down
Loading