Skip to content

nrf_security: cracen: Add support for multipart cmac to lm20 #22912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions subsys/nrf_security/src/drivers/cracen/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ config CRACEN_HW_VERSION_BASE
config CRACEN_HW_VERSION_LITE
def_bool SOC_NRF54LM20A || SOC_NRF54LV10A

config CRACEN_USE_MULTIPART_WORKAROUNDS
def_bool SOC_NRF54LM20A

# Configure CRACEN_LOG_LEVEL
module = CRACEN
module-str = CRACEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ if(CONFIG_PSA_NEED_CRACEN_MAC_DRIVER)
endif()
endif()

if(CONFIG_CRACEN_USE_MULTIPART_WORKAROUNDS)
list(APPEND cracen_driver_sources
${CMAKE_CURRENT_LIST_DIR}/src/cracen_sw_mac_cmac.c
)
endif()

if(CONFIG_PSA_NEED_CRACEN_KEY_MANAGEMENT_DRIVER OR CONFIG_PSA_NEED_CRACEN_KMU_DRIVER OR CONFIG_MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
list(APPEND cracen_driver_sources
${CMAKE_CURRENT_LIST_DIR}/src/key_management.c
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ struct cracen_aead_operation {
};
typedef struct cracen_aead_operation cracen_aead_operation_t;

struct cracen_cmac_context_s {
uint8_t mac_state[SX_BLKCIPHER_AES_BLK_SZ]; /* The current MAC state */
uint8_t partial_block[SX_BLKCIPHER_AES_BLK_SZ];
size_t partial_len;
size_t processed_len; /* Total length of data processed so far (in bytes) */
};
typedef struct cracen_cmac_context_s cracen_cmac_context_t;

struct cracen_mac_operation_s {
psa_algorithm_t alg;
size_t mac_size;
Expand Down Expand Up @@ -206,6 +214,9 @@ struct cracen_mac_operation_s {
struct sxmac ctx;
struct sxkeyref keyref;
uint8_t key_buffer[CRACEN_MAX_AES_KEY_SIZE];
#if defined(CONFIG_CRACEN_USE_MULTIPART_WORKAROUNDS)
cracen_cmac_context_t sw_ctx;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs to be conditionally included as well since it will increase stack usage for all the targets otherwise.

#endif /* CONFIG_CRACEN_USE_MULTIPART_WORKAROUNDS */
} cmac;
#endif /* PSA_NEED_CRACEN_CMAC */
uint8_t _unused;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,26 +181,3 @@ psa_status_t cracen_cmac_finish(cracen_mac_operation_t *operation)
sx_status = sx_mac_wait(&operation->cmac.ctx);
return silex_statuscodes_to_psa(sx_status);
}

psa_status_t cracen_cmac_compute(cracen_mac_operation_t *operation, const uint8_t *input,
size_t input_length, uint8_t *mac)
{
int sx_status;

sx_status = sx_mac_feed(&operation->cmac.ctx, input, input_length);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_mac_generate(&operation->cmac.ctx, mac);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_mac_wait(&operation->cmac.ctx);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

return PSA_SUCCESS;
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,3 @@ psa_status_t cracen_cmac_update(cracen_mac_operation_t *operation, const uint8_t
* @return PSA_SUCCESS on success or a valid PSA status code.
*/
psa_status_t cracen_cmac_finish(cracen_mac_operation_t *operation);

/**
* @brief Compute a CMAC in a single pass, without context switching.
*
* @note This function assumes the setup function is called first.
*
* @return PSA_SUCCESS on success or a valid PSA status code.
*/
psa_status_t cracen_cmac_compute(cracen_mac_operation_t *operation, const uint8_t *input,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this removed? I see that the function is implemented in cracen_mac_cmac.c.

size_t input_length, uint8_t *mac);
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/
#include <stdint.h>
#include <string.h>
#include <psa/crypto.h>
#include <sxsymcrypt/blkcipher.h>
#include <sxsymcrypt/keyref.h>
#include "cracen_sw_mac_cmac.h"
#include <sxsymcrypt/aes.h>

#define CMAC_PADDING_BYTE (0x80)
#define AES_CMAC_MSB (0x80)
#define CMAC_CONSTANT_RB (0x87)

static psa_status_t cracen_aes_ecb_encrypt(const struct sxkeyref *key, const uint8_t *input,
uint8_t *output)
{
struct sxblkcipher blkciph;
int sx_status;

sx_status = sx_blkcipher_create_aesecb_enc(&blkciph, key);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_blkcipher_crypt(&blkciph, input, SX_BLKCIPHER_AES_BLK_SZ, output);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_blkcipher_run(&blkciph);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_blkcipher_wait(&blkciph);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

return PSA_SUCCESS;
}

psa_status_t cracen_sw_cmac_setup(cracen_mac_operation_t *operation,
const psa_key_attributes_t *attributes, const uint8_t *key_buffer,
size_t key_buffer_size)
{
psa_status_t status = PSA_SUCCESS;
int sx_status;

/* Only AES-CMAC is supported */
if (psa_get_key_type(attributes) != PSA_KEY_TYPE_AES) {
return PSA_ERROR_NOT_SUPPORTED;
}

if (key_buffer_size != 16 && key_buffer_size != 24 && key_buffer_size != 32) {
return PSA_ERROR_NOT_SUPPORTED;
}

/* Load the key reference */
if (key_buffer_size > sizeof(operation->cmac.key_buffer)) {
return PSA_ERROR_INVALID_ARGUMENT;
}

memcpy(operation->cmac.key_buffer, key_buffer, key_buffer_size);
status = cracen_load_keyref(attributes, operation->cmac.key_buffer, key_buffer_size,
&operation->cmac.keyref);
if (status != PSA_SUCCESS) {
return status;
}

sx_status = sx_mac_create_aescmac(&operation->cmac.ctx, &operation->cmac.keyref);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

operation->bytes_left_for_next_block = SX_BLKCIPHER_AES_BLK_SZ;
operation->is_first_block = true;

return PSA_SUCCESS;
}

static void left_shift_block(const uint8_t *in, uint8_t *out)
{
uint8_t carry = 0;
uint8_t byte;

for (int i = SX_BLKCIPHER_AES_BLK_SZ - 1; i >= 0; i--) {
byte = in[i];
out[i] = (byte << 1) | carry;
carry = (byte & AES_CMAC_MSB) ? 1 : 0;
}
}

psa_status_t cracen_cmac_derive_subkeys(cracen_mac_operation_t *operation, uint8_t *k1, uint8_t *k2)
{
uint8_t empty_block[SX_BLKCIPHER_AES_BLK_SZ] = {0};
uint8_t L[SX_BLKCIPHER_AES_BLK_SZ]; /* L is defined in RFC 4493 */
psa_status_t status = cracen_aes_ecb_encrypt(&operation->cmac.keyref, empty_block, L);

if (status != PSA_SUCCESS) {
return status;
}

left_shift_block(L, k1);
if (L[0] & AES_CMAC_MSB) {
k1[SX_BLKCIPHER_AES_BLK_SZ - 1] ^= CMAC_CONSTANT_RB;
}

left_shift_block(k1, k2);
if (k1[0] & AES_CMAC_MSB) {
k2[SX_BLKCIPHER_AES_BLK_SZ - 1] ^= CMAC_CONSTANT_RB;
}
return PSA_SUCCESS;
}

psa_status_t cracen_sw_cmac_update(cracen_mac_operation_t *operation, const uint8_t *data,
size_t data_len)
{
psa_status_t psa_status;
size_t bytes_to_copy;
size_t remaining = data_len;
size_t offset = 0;

while (remaining > 0) {
bytes_to_copy = SX_BLKCIPHER_AES_BLK_SZ - operation->cmac.sw_ctx.partial_len;
if (bytes_to_copy > remaining) {
bytes_to_copy = remaining;
}

memcpy(operation->cmac.sw_ctx.partial_block + operation->cmac.sw_ctx.partial_len,
data + offset, bytes_to_copy);
operation->cmac.sw_ctx.partial_len += bytes_to_copy;
offset += bytes_to_copy;
remaining -= bytes_to_copy;

/* Only encrypt a full block if we know more data is coming */
if (operation->cmac.sw_ctx.partial_len == SX_BLKCIPHER_AES_BLK_SZ &&
remaining > 0) {
cracen_xorbytes(operation->cmac.sw_ctx.mac_state,
operation->cmac.sw_ctx.partial_block,
SX_BLKCIPHER_AES_BLK_SZ);
psa_status = cracen_aes_ecb_encrypt(&operation->cmac.keyref,
operation->cmac.sw_ctx.mac_state,
operation->cmac.sw_ctx.mac_state);
if (psa_status != PSA_SUCCESS) {
return psa_status;
}
operation->cmac.sw_ctx.partial_len = 0;
memset(operation->cmac.sw_ctx.partial_block, 0, SX_BLKCIPHER_AES_BLK_SZ);
}
}

return PSA_SUCCESS;
}

psa_status_t cracen_sw_cmac_finish(cracen_mac_operation_t *operation)
{
psa_status_t psa_status;
uint8_t last_block[SX_BLKCIPHER_AES_BLK_SZ] = {0};
uint8_t k1[SX_BLKCIPHER_AES_BLK_SZ]; /* k1 is defined in RFC 4493 */
uint8_t k2[SX_BLKCIPHER_AES_BLK_SZ]; /* k2 is defined in RFC 4493 */

psa_status = cracen_cmac_derive_subkeys(operation, k1, k2);
if (psa_status != PSA_SUCCESS) {
return psa_status;
}

/* If full block, XOR with K1; otherwise pad and XOR with K2 */
if (operation->cmac.sw_ctx.partial_len == SX_BLKCIPHER_AES_BLK_SZ) {
cracen_xorbytes(operation->cmac.sw_ctx.partial_block, k1, SX_BLKCIPHER_AES_BLK_SZ);
memcpy(last_block, operation->cmac.sw_ctx.partial_block, SX_BLKCIPHER_AES_BLK_SZ);
} else {
memcpy(last_block, operation->cmac.sw_ctx.partial_block,
operation->cmac.sw_ctx.partial_len);

last_block[operation->cmac.sw_ctx.partial_len] = CMAC_PADDING_BYTE;
cracen_xorbytes(last_block, k2, SX_BLKCIPHER_AES_BLK_SZ);
}

/* Final MAC: encrypt (mac_state XOR last_block) */
cracen_xorbytes(operation->cmac.sw_ctx.mac_state, last_block, SX_BLKCIPHER_AES_BLK_SZ);

psa_status =
cracen_aes_ecb_encrypt(&operation->cmac.keyref, operation->cmac.sw_ctx.mac_state,
operation->cmac.sw_ctx.mac_state);
if (psa_status != PSA_SUCCESS) {
return psa_status;
}

memcpy(operation->input_buffer, operation->cmac.sw_ctx.mac_state, SX_BLKCIPHER_AES_BLK_SZ);
operation->mac_size = SX_BLKCIPHER_AES_BLK_SZ;

return PSA_SUCCESS;
}

/* Single-shot operations still use hw cmac */
psa_status_t cracen_sw_cmac_compute(cracen_mac_operation_t *operation, const uint8_t *input,
size_t input_length, uint8_t *mac)
{
int sx_status;

sx_status = sx_mac_feed(&operation->cmac.ctx, input, input_length);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_mac_generate(&operation->cmac.ctx, mac);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

sx_status = sx_mac_wait(&operation->cmac.ctx);
if (sx_status != SX_OK) {
return silex_statuscodes_to_psa(sx_status);
}

return PSA_SUCCESS;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include <psa/crypto.h>
#include "common.h"
#include "cracen_psa_primitives.h"

/**
* @brief Setup for software based CMAC operation
*
* This function initializes a CMAC operation with the provided key attributes
* and key buffer. It prepares the operation structure for subsequent CMAC
* processing. This operation runs cmac in software using hardware AES-ECB primitives
*
* @param[in,out] operation Pointer to the CMAC operation structure to be initialized.
* @param[in] attributes Pointer to the key attributes structure.
* @param[in] key_buffer Pointer to the buffer containing the key material.
* @param[in] key_buffer_size Size of the key buffer in bytes.
*
* @return PSA_SUCCESS on success or a valid PSA status code.
*/
psa_status_t cracen_sw_cmac_setup(cracen_mac_operation_t *op,
const psa_key_attributes_t *attributes, const uint8_t *key_buffer,
size_t key_buffer_size);

/**
* @brief Update function software based CMAC operation
*
* This function processes a chunk of input data as part of a CMAC operation.
* It can be called multiple times to process data in chunks.
* This operation runs cmac in software using hardware AES-ECB primitives
*
* @param[in,out] operation Pointer to the CMAC operation structure.
* @param[in] input Pointer to the input data buffer.
* @param[in] input_length Length of the input data in bytes.
*
* @return PSA_SUCCESS on success or a valid PSA status code.
*/
psa_status_t cracen_sw_cmac_update(cracen_mac_operation_t *op, const uint8_t *data,
size_t data_len);

/**
* @brief Function to finalize a C software based CMAC operation.
*
* This function completes the CMAC operation and releases any resources
* associated with it. It should be called after all input data has been
* processed. This operation runs cmac in software using hardware AES-ECB primitives
*
* @param[in,out] operation Pointer to the CMAC operation structure.
*
* @return PSA_SUCCESS on success or a valid PSA status code.
*/
psa_status_t cracen_sw_cmac_finish(cracen_mac_operation_t *op);

/**
* @brief Compute a CMAC in a single pass, without context switching.
*
* @note This function assumes the setup function is called first.
*
* @return PSA_SUCCESS on success or a valid PSA status code.
*/
psa_status_t cracen_cmac_compute(cracen_mac_operation_t *op, const uint8_t *input,
size_t input_length, uint8_t *mac);
Loading