blob: 50d02a74c3924b69625ba9b2e622b4997172b173 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/dif/dif_kmac.h"
#include <assert.h>
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/memory.h"
#include "kmac_regs.h" // Generated.
enum {
/**
* The maximum amount of usable bits in the output state.
*
* This constant may be assumed to be a multiple of 32.
*
* The actual number of usable bits may be lower than the value defined
* depending on the mode in use. The intent is that this constant is useful
* for sizing fixed length buffers.
*
* Formula for the rate in bits is:
*
* r = 1600 - c
*
* Where c is the capacity (the security level in bits multiplied by two).
*
* The lowest security level is 128 (e.g. SHAKE128).
*/
kDifKmacMaximumBitRate = 1600 - (2 * 128),
};
dif_result_t dif_kmac_customization_string_init(
const char *data, size_t len, dif_kmac_customization_string_t *out) {
if ((data == NULL && len != 0) || out == NULL) {
return kDifBadArg;
}
if (len > kDifKmacMaxCustomizationStringLen) {
return kDifBadArg;
}
static_assert(kDifKmacMaxCustomizationStringLen <= UINT16_MAX / 8,
"length requires more than 3 bytes to left encode");
static_assert(ARRAYSIZE(out->buffer) >= kDifKmacMaxCustomizationStringLen + 3,
"buffer is not large enough");
// Left encode length in bits.
uint16_t bits = ((uint16_t)len) * 8;
char *buffer = out->buffer;
if (bits <= UINT8_MAX) {
out->length = len + 2;
*buffer++ = 1;
*buffer++ = (char)bits;
} else {
out->length = len + 3;
*buffer++ = 2;
// Most significant byte is first (i.e. big-endian).
*buffer++ = (char)(bits >> 8);
*buffer++ = (char)bits;
}
memcpy(buffer, data, len);
return kDifOk;
}
dif_result_t dif_kmac_function_name_init(const char *data, size_t len,
dif_kmac_function_name_t *out) {
if ((data == NULL && len != 0) || out == NULL) {
return kDifBadArg;
}
if (len > kDifKmacMaxFunctionNameLen) {
return kDifBadArg;
}
static_assert(kDifKmacMaxFunctionNameLen <= UINT8_MAX / 8,
"length requires more than 2 bytes to left encode");
static_assert(ARRAYSIZE(out->buffer) >= kDifKmacMaxFunctionNameLen + 2,
"buffer is not large enough");
// Length of the data to be stored into buffer.
out->length = len + 2;
// Left encode length in bits.
out->buffer[0] = 1;
out->buffer[1] = (char)(len * 8);
memcpy(&out->buffer[2], data, len);
return kDifOk;
}
/**
* Report whether the hardware is currently idle.
*
* If the hardware is not idle then the `CFG` register is locked.
*
* @param kmac Handle.
* @returns Whether the hardware is currently idle or not.
*/
static bool is_state_idle(const dif_kmac_t *kmac) {
uint32_t reg = mmio_region_read32(kmac->base_addr, KMAC_STATUS_REG_OFFSET);
return bitfield_bit32_read(reg, KMAC_STATUS_SHA3_IDLE_BIT);
}
/**
* Report whether the hardware is currently in the absorb state and accepting
* writes to the message FIFO.
*
* Note that writes to the message FIFO may still block if it is full.
*
* @param kmac Handle.
* @returns Whether the hardware is currently absorbing or not.
*/
static bool is_state_absorb(const dif_kmac_t *kmac) {
uint32_t reg = mmio_region_read32(kmac->base_addr, KMAC_STATUS_REG_OFFSET);
return bitfield_bit32_read(reg, KMAC_STATUS_SHA3_ABSORB_BIT);
}
/**
* Report whether the hardware is currently in the squeeze state which means
* that the output state is valid and may be read by software.
*
* @param kmac Handle.
* @returns Whether the hardware is currently in the squeeze state or not.
*/
static bool is_state_squeeze(const dif_kmac_t *kmac) {
uint32_t reg = mmio_region_read32(kmac->base_addr, KMAC_STATUS_REG_OFFSET);
return bitfield_bit32_read(reg, KMAC_STATUS_SHA3_SQUEEZE_BIT);
}
/**
* Report whether the hardware has indicated a error.
*
* @param kmac Handle.
* @returns True if an error occurred, False otherwise.
*/
static bool has_error_occurred(const dif_kmac_t *kmac) {
uint32_t reg =
mmio_region_read32(kmac->base_addr, KMAC_INTR_STATE_REG_OFFSET);
return bitfield_bit32_read(reg, KMAC_INTR_STATE_KMAC_ERR_BIT);
}
dif_result_t dif_kmac_poll_status(const dif_kmac_t *kmac, uint32_t flag) {
while (true) {
uint32_t reg = mmio_region_read32(kmac->base_addr, KMAC_STATUS_REG_OFFSET);
if (bitfield_bit32_read(reg, flag)) {
break;
}
if (has_error_occurred(kmac)) {
return kDifError;
}
}
return kDifOk;
}
dif_result_t dif_kmac_configure(dif_kmac_t *kmac, dif_kmac_config_t config) {
if (kmac == NULL) {
return kDifBadArg;
}
// Entropy mode.
uint32_t entropy_mode_value;
bool entropy_ready = false;
switch (config.entropy_mode) {
case kDifKmacEntropyModeIdle:
entropy_mode_value = KMAC_CFG_SHADOWED_ENTROPY_MODE_VALUE_IDLE_MODE;
break;
case kDifKmacEntropyModeEdn:
entropy_mode_value = KMAC_CFG_SHADOWED_ENTROPY_MODE_VALUE_EDN_MODE;
entropy_ready = true;
break;
case kDifKmacEntropyModeSoftware:
entropy_mode_value = KMAC_CFG_SHADOWED_ENTROPY_MODE_VALUE_SW_MODE;
entropy_ready = true;
break;
default:
return kDifBadArg;
}
// Check that the hardware is in an idle state.
if (!is_state_idle(kmac)) {
return kDifLocked;
}
// Write entropy period register.
uint32_t entropy_period_reg = 0;
entropy_period_reg = bitfield_field32_write(
entropy_period_reg, KMAC_ENTROPY_PERIOD_WAIT_TIMER_FIELD,
config.entropy_wait_timer);
entropy_period_reg = bitfield_field32_write(
entropy_period_reg, KMAC_ENTROPY_PERIOD_PRESCALER_FIELD,
config.entropy_prescaler);
mmio_region_write32(kmac->base_addr, KMAC_ENTROPY_PERIOD_REG_OFFSET,
entropy_period_reg);
// Write threshold register.
uint32_t entropy_threshold_reg =
KMAC_ENTROPY_REFRESH_THRESHOLD_SHADOWED_REG_RESVAL;
entropy_threshold_reg = bitfield_field32_write(
entropy_threshold_reg,
KMAC_ENTROPY_REFRESH_THRESHOLD_SHADOWED_THRESHOLD_FIELD,
config.entropy_hash_threshold);
mmio_region_write32_shadowed(
kmac->base_addr, KMAC_ENTROPY_REFRESH_THRESHOLD_SHADOWED_REG_OFFSET,
entropy_threshold_reg);
// Write configuration register.
uint32_t cfg_reg = 0;
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_MSG_ENDIANNESS_BIT,
config.message_big_endian);
cfg_reg =
bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_STATE_ENDIANNESS_BIT,
config.output_big_endian);
cfg_reg = bitfield_field32_write(
cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_MODE_FIELD, entropy_mode_value);
cfg_reg =
bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_FAST_PROCESS_BIT,
config.entropy_fast_process);
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_SIDELOAD_BIT,
config.sideload);
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_READY_BIT,
entropy_ready);
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_MSG_MASK_BIT,
config.msg_mask);
mmio_region_write32_shadowed(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET,
cfg_reg);
// Write entropy seed registers.
for (int i = 0; i < kDifKmacEntropySeedWords; ++i) {
mmio_region_write32(kmac->base_addr,
KMAC_ENTROPY_SEED_0_REG_OFFSET + i * sizeof(uint32_t),
config.entropy_seed[i]);
}
return kDifOk;
}
/**
* Calculate the rate (r) in bits from the given security level.
*
* @param security_level Security level in bits.
* @returns Rate in bits.
*/
static uint32_t calculate_rate_bits(uint32_t security_level) {
// Formula for the rate in bits is:
//
// r = 1600 - c
//
// Where c is the capacity (the security level in bits multiplied by two).
return 1600 - 2 * security_level;
}
dif_result_t dif_kmac_mode_sha3_start(
const dif_kmac_t *kmac, dif_kmac_operation_state_t *operation_state,
dif_kmac_mode_sha3_t mode) {
if (kmac == NULL || operation_state == NULL) {
return kDifBadArg;
}
// Set key strength and calculate rate (r) and digest length (d) in 32-bit
// words.
uint32_t kstrength;
switch (mode) {
case kDifKmacModeSha3Len224:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L224;
operation_state->offset = 0;
operation_state->r = calculate_rate_bits(224) / 32;
operation_state->d = 224 / 32;
break;
case kDifKmacModeSha3Len256:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256;
operation_state->offset = 0;
operation_state->r = calculate_rate_bits(256) / 32;
operation_state->d = 256 / 32;
break;
case kDifKmacModeSha3Len384:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L384;
operation_state->offset = 0;
operation_state->r = calculate_rate_bits(384) / 32;
operation_state->d = 384 / 32;
break;
case kDifKmacModeSha3Len512:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L512;
operation_state->offset = 0;
operation_state->r = calculate_rate_bits(512) / 32;
operation_state->d = 512 / 32;
break;
default:
return kDifBadArg;
}
// Hardware must be idle to start an operation.
if (!is_state_idle(kmac)) {
return kDifError;
}
operation_state->squeezing = false;
operation_state->append_d = false;
// Configure SHA-3 mode with the given strength.
uint32_t cfg_reg =
mmio_region_read32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD,
kstrength);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD,
KMAC_CFG_SHADOWED_MODE_VALUE_SHA3);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
// Issue start command.
uint32_t cmd_reg =
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
// Poll until the status register is in the 'absorb' state.
return dif_kmac_poll_status(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
dif_result_t dif_kmac_mode_shake_start(
const dif_kmac_t *kmac, dif_kmac_operation_state_t *operation_state,
dif_kmac_mode_shake_t mode) {
if (kmac == NULL || operation_state == NULL) {
return kDifBadArg;
}
// Set key strength and calculate rate (r).
uint32_t kstrength;
switch (mode) {
case kDifKmacModeShakeLen128:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L128;
operation_state->r = calculate_rate_bits(128) / 32;
break;
case kDifKmacModeShakeLen256:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256;
operation_state->r = calculate_rate_bits(256) / 32;
break;
default:
return kDifBadArg;
}
// Hardware must be idle to start an operation.
if (!is_state_idle(kmac)) {
return kDifError;
}
operation_state->squeezing = false;
operation_state->append_d = false;
operation_state->d = 0; // Zero indicates variable digest length.
operation_state->offset = 0;
// Configure SHAKE mode with the given strength.
uint32_t cfg_reg =
mmio_region_read32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD,
kstrength);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD,
KMAC_CFG_SHADOWED_MODE_VALUE_SHAKE);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
// Issue start command.
uint32_t cmd_reg =
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
return dif_kmac_poll_status(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
dif_result_t dif_kmac_mode_cshake_start(
const dif_kmac_t *kmac, dif_kmac_operation_state_t *operation_state,
dif_kmac_mode_cshake_t mode, const dif_kmac_function_name_t *n,
const dif_kmac_customization_string_t *s) {
if (kmac == NULL || operation_state == NULL) {
return kDifBadArg;
}
// Use SHAKE if both N and S are empty strings.
bool n_is_empty = n == NULL || (n->buffer[0] == 1 && n->buffer[1] == 0);
bool s_is_empty = s == NULL || (s->buffer[0] == 1 && s->buffer[1] == 0);
if (n_is_empty && s_is_empty) {
switch (mode) {
case kDifKmacModeCshakeLen128:
return dif_kmac_mode_shake_start(kmac, operation_state,
kDifKmacModeShakeLen128);
case kDifKmacModeCshakeLen256:
return dif_kmac_mode_shake_start(kmac, operation_state,
kDifKmacModeShakeLen256);
default:
return kDifBadArg;
}
}
// Set key strength and calculate rate (r).
uint32_t kstrength;
switch (mode) {
case kDifKmacModeCshakeLen128:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L128;
operation_state->r = calculate_rate_bits(128) / 32;
break;
case kDifKmacModeCshakeLen256:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256;
operation_state->r = calculate_rate_bits(256) / 32;
break;
default:
return kDifBadArg;
}
// Hardware must be idle to start an operation.
if (!is_state_idle(kmac)) {
return kDifError;
}
operation_state->squeezing = false;
operation_state->append_d = false;
operation_state->d = 0; // Zero indicates variable digest length.
operation_state->offset = 0;
// Configure cSHAKE mode with the given strength.
uint32_t cfg_reg =
mmio_region_read32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD,
kstrength);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD,
KMAC_CFG_SHADOWED_MODE_VALUE_CSHAKE);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
// Calculate PREFIX register values.
uint32_t prefix_regs[11] = {0};
uint8_t *prefix_data = (uint8_t *)prefix_regs;
if (n == NULL || n->length < 3) {
// Append left encoded empty string.
prefix_data[0] = 1;
prefix_data[1] = 0;
prefix_data += 2;
} else {
memcpy(prefix_data, n->buffer, n->length);
prefix_data += n->length;
}
if (s == NULL || s->length == 0) {
// Append left encoded empty string.
prefix_data[0] = 1;
prefix_data[1] = 0;
} else {
memcpy(prefix_data, s->buffer, s->length);
}
// Write PREFIX register values.
const mmio_region_t base = kmac->base_addr;
mmio_region_write32(base, KMAC_PREFIX_0_REG_OFFSET, prefix_regs[0]);
mmio_region_write32(base, KMAC_PREFIX_1_REG_OFFSET, prefix_regs[1]);
mmio_region_write32(base, KMAC_PREFIX_2_REG_OFFSET, prefix_regs[2]);
mmio_region_write32(base, KMAC_PREFIX_3_REG_OFFSET, prefix_regs[3]);
mmio_region_write32(base, KMAC_PREFIX_4_REG_OFFSET, prefix_regs[4]);
mmio_region_write32(base, KMAC_PREFIX_5_REG_OFFSET, prefix_regs[5]);
mmio_region_write32(base, KMAC_PREFIX_6_REG_OFFSET, prefix_regs[6]);
mmio_region_write32(base, KMAC_PREFIX_7_REG_OFFSET, prefix_regs[7]);
mmio_region_write32(base, KMAC_PREFIX_8_REG_OFFSET, prefix_regs[8]);
mmio_region_write32(base, KMAC_PREFIX_9_REG_OFFSET, prefix_regs[9]);
mmio_region_write32(base, KMAC_PREFIX_10_REG_OFFSET, prefix_regs[10]);
// Issue start command.
uint32_t cmd_reg =
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
return dif_kmac_poll_status(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
dif_result_t dif_kmac_mode_kmac_start(
const dif_kmac_t *kmac, dif_kmac_operation_state_t *operation_state,
dif_kmac_mode_kmac_t mode, size_t l, const dif_kmac_key_t *k,
const dif_kmac_customization_string_t *s) {
if (kmac == NULL || operation_state == NULL || k == NULL ||
l > kDifKmacMaxOutputLenWords) {
return kDifBadArg;
}
// Set key strength and calculate rate (r).
uint32_t kstrength;
switch (mode) {
case kDifKmacModeKmacLen128:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L128;
operation_state->r = calculate_rate_bits(128) / 32;
break;
case kDifKmacModeKmacLen256:
kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256;
operation_state->r = calculate_rate_bits(256) / 32;
break;
default:
return kDifBadArg;
}
operation_state->squeezing = false;
operation_state->offset = 0;
operation_state->d = l;
operation_state->append_d = true;
uint32_t key_len;
switch (k->length) {
case kDifKmacKeyLen128:
key_len = KMAC_KEY_LEN_LEN_VALUE_KEY128;
break;
case kDifKmacKeyLen192:
key_len = KMAC_KEY_LEN_LEN_VALUE_KEY192;
break;
case kDifKmacKeyLen256:
key_len = KMAC_KEY_LEN_LEN_VALUE_KEY256;
break;
case kDifKmacKeyLen384:
key_len = KMAC_KEY_LEN_LEN_VALUE_KEY384;
break;
case kDifKmacKeyLen512:
key_len = KMAC_KEY_LEN_LEN_VALUE_KEY512;
break;
default:
return kDifBadArg;
}
// Hardware must be idle to start an operation.
if (!is_state_idle(kmac)) {
return kDifError;
}
// Set key length and shares.
mmio_region_write32(kmac->base_addr, KMAC_KEY_LEN_REG_OFFSET, key_len);
for (int i = 0; i < ARRAYSIZE(k->share0); ++i) {
mmio_region_write32(kmac->base_addr,
KMAC_KEY_SHARE0_0_REG_OFFSET + i * sizeof(uint32_t),
k->share0[i]);
mmio_region_write32(kmac->base_addr,
KMAC_KEY_SHARE1_0_REG_OFFSET + i * sizeof(uint32_t),
k->share1[i]);
}
// Configure cSHAKE mode with the given strength and enable KMAC mode.
uint32_t cfg_reg =
mmio_region_read32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET);
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_KMAC_EN_BIT, true);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD,
kstrength);
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD,
KMAC_CFG_SHADOWED_MODE_VALUE_CSHAKE);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
mmio_region_write32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
// Initialize prefix registers with function name ("KMAC") and empty
// customization string. The empty customization string will be overwritten if
// a non-empty string is provided.
uint32_t prefix_regs[11] = {
0x4D4B2001, // 1 32 'K' 'M'
0x00014341, // 'A' 'C' 1 0
};
// Encoded customization string (s) must be at least 3 bytes long if it is not
// the empty string.
if (s != NULL && s->length >= 3) {
// First two bytes overwrite the pre-encoded empty customization string.
prefix_regs[1] &= 0xFFFF;
prefix_regs[1] |= (uint32_t)((uint8_t)s->buffer[0]) << 16;
prefix_regs[1] |= (uint32_t)((uint8_t)s->buffer[1]) << 24;
memcpy(&prefix_regs[2], &s->buffer[2], s->length - 2);
}
// Write PREFIX register values.
const mmio_region_t base = kmac->base_addr;
mmio_region_write32(base, KMAC_PREFIX_0_REG_OFFSET, prefix_regs[0]);
mmio_region_write32(base, KMAC_PREFIX_1_REG_OFFSET, prefix_regs[1]);
mmio_region_write32(base, KMAC_PREFIX_2_REG_OFFSET, prefix_regs[2]);
mmio_region_write32(base, KMAC_PREFIX_3_REG_OFFSET, prefix_regs[3]);
mmio_region_write32(base, KMAC_PREFIX_4_REG_OFFSET, prefix_regs[4]);
mmio_region_write32(base, KMAC_PREFIX_5_REG_OFFSET, prefix_regs[5]);
mmio_region_write32(base, KMAC_PREFIX_6_REG_OFFSET, prefix_regs[6]);
mmio_region_write32(base, KMAC_PREFIX_7_REG_OFFSET, prefix_regs[7]);
mmio_region_write32(base, KMAC_PREFIX_8_REG_OFFSET, prefix_regs[8]);
mmio_region_write32(base, KMAC_PREFIX_9_REG_OFFSET, prefix_regs[9]);
mmio_region_write32(base, KMAC_PREFIX_10_REG_OFFSET, prefix_regs[10]);
// Issue start command.
uint32_t cmd_reg =
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_START);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
return dif_kmac_poll_status(kmac, KMAC_STATUS_SHA3_ABSORB_BIT);
}
static void msg_fifo_write(const dif_kmac_t *kmac, const unsigned char *data,
size_t len) {
// Copy message using aligned word sized loads and stores where possible to
// improve performance. Note: the parts of the message copied a byte at a time
// will not be byte swapped in big-endian mode.
for (; len != 0 && ((uintptr_t)data) % sizeof(uint32_t); --len) {
mmio_region_write8(kmac->base_addr, KMAC_MSG_FIFO_REG_OFFSET, *data++);
}
for (; len >= sizeof(uint32_t); len -= sizeof(uint32_t)) {
mmio_region_write32(kmac->base_addr, KMAC_MSG_FIFO_REG_OFFSET,
read_32(data));
data += sizeof(uint32_t);
}
for (; len != 0; --len) {
mmio_region_write8(kmac->base_addr, KMAC_MSG_FIFO_REG_OFFSET, *data++);
}
}
dif_result_t dif_kmac_absorb(const dif_kmac_t *kmac,
dif_kmac_operation_state_t *operation_state,
const void *msg, size_t len, size_t *processed) {
// Set the number of bytes processed to 0.
if (processed != NULL) {
*processed = 0;
}
if (kmac == NULL || operation_state == NULL || (msg == NULL && len != 0)) {
return kDifBadArg;
}
// Check that an operation has been started.
if (operation_state->r == 0) {
return kDifError;
}
// Poll until the the status register is in the 'absorb' state.
if (!is_state_absorb(kmac)) {
return kDifError;
}
// Copy message using aligned word sized loads and stores where possible to
// improve performance. Note: the parts of the message copied a byte at a time
// will not be byte swapped in big-endian mode.
const unsigned char *data = (const unsigned char *)msg;
dif_kmac_status_t status;
while (len > 0) {
// Read the status register.
DIF_RETURN_IF_ERROR(dif_kmac_get_status(kmac, &status));
// Calculate the remaining space in the message FIFO based on the
// `FIFO_DEPTH` status field.
size_t free_entries = (KMAC_PARAM_NUM_ENTRIES_MSG_FIFO - status.fifo_depth);
size_t max_len = free_entries * KMAC_PARAM_NUM_BYTES_MSG_FIFO_ENTRY;
size_t write_len = (len < max_len) ? len : max_len;
msg_fifo_write(kmac, data, write_len);
data += write_len;
len -= write_len;
// If `processed` is non-null, do not continue after the first iteration;
// return the number of bytes written and `kDifKmacIncomplete`.
if (processed != NULL) {
*processed = write_len;
break;
}
}
return kDifOk;
}
dif_result_t dif_kmac_squeeze(const dif_kmac_t *kmac,
dif_kmac_operation_state_t *operation_state,
uint32_t *out, size_t len, size_t *processed) {
if (kmac == NULL || operation_state == NULL || (out == NULL && len != 0)) {
return kDifBadArg;
}
// Set `processed` to 0 so we can return early without setting it again.
if (processed != NULL) {
*processed = 0;
}
const mmio_region_t base = kmac->base_addr;
// Move into squeezing state (if not already in it).
// Do this even if the length requested is 0 or too big.
if (!operation_state->squeezing) {
if (operation_state->append_d) {
// The KMAC operation requires that the output length (d) in bits be right
// encoded and appended to the end of the message.
// Note: kDifKmacMaxOutputLenWords could be reduced to make this code
// simpler. For example, a maximum of `(UINT16_MAX - 32) / 32` (just under
// 8 KiB) would mean that d is guaranteed to be less than 0xFFFF.
uint32_t d = operation_state->d * 32;
int len = 1 + (d > 0xFF) + (d > 0xFFFF) + (d > 0xFFFFFF);
int shift = (len - 1) * 8;
while (shift >= 8) {
mmio_region_write8(base, KMAC_MSG_FIFO_REG_OFFSET,
(uint8_t)(d >> shift));
shift -= 8;
}
mmio_region_write8(base, KMAC_MSG_FIFO_REG_OFFSET, (uint8_t)d);
mmio_region_write8(base, KMAC_MSG_FIFO_REG_OFFSET, (uint8_t)len);
}
operation_state->squeezing = true;
// Issue squeeze command.
uint32_t cmd_reg = bitfield_field32_write(0, KMAC_CMD_CMD_FIELD,
KMAC_CMD_CMD_VALUE_PROCESS);
mmio_region_write32(base, KMAC_CMD_REG_OFFSET, cmd_reg);
}
// If the operation has a fixed length output then the total number of bytes
// requested must not exceed that length.
if (operation_state->d != 0 &&
len > (operation_state->d - operation_state->offset)) {
return kDifError;
}
if (len == 0) {
return kDifOk;
}
while (len > 0) {
size_t n = len;
size_t remaining = operation_state->r - operation_state->offset;
if (operation_state->d != 0 && operation_state->d < operation_state->r) {
remaining = operation_state->d - operation_state->offset;
}
if (n > remaining) {
n = remaining;
}
if (n == 0) {
// Reduce the digest length to reflect consumed output state.
if (operation_state->d != 0) {
if (operation_state->d <= operation_state->r) {
return kDifError;
}
operation_state->d -= operation_state->r;
}
// Issue run command to generate more state.
uint32_t cmd_reg =
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_RUN);
mmio_region_write32(base, KMAC_CMD_REG_OFFSET, cmd_reg);
operation_state->offset = 0;
continue;
}
// Poll the status register until in the 'squeeze' state.
DIF_RETURN_IF_ERROR(
dif_kmac_poll_status(kmac, KMAC_STATUS_SHA3_SQUEEZE_BIT));
uint32_t offset =
KMAC_STATE_REG_OFFSET + operation_state->offset * sizeof(uint32_t);
for (size_t i = 0; i < n; ++i) {
// Read both shares from state register and combine using XOR.
uint32_t share0 = mmio_region_read32(base, offset);
uint32_t share1 =
mmio_region_read32(base, offset + kDifKmacStateShareOffset);
*out++ = share0 ^ share1;
offset += sizeof(uint32_t);
}
operation_state->offset += n;
len -= n;
if (processed != NULL) {
*processed += n;
}
}
return kDifOk;
}
dif_result_t dif_kmac_end(const dif_kmac_t *kmac,
dif_kmac_operation_state_t *operation_state) {
if (kmac == NULL || operation_state == NULL) {
return kDifBadArg;
}
// The hardware should (must?) complete squeeze operation before the DONE
// command is issued.
if (!operation_state->squeezing) {
return kDifError;
}
while (true) {
if (is_state_squeeze(kmac)) {
break;
}
// TODO(#6248): check for error.
}
// Issue done command.
uint32_t cmd_reg =
bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_DONE);
mmio_region_write32(kmac->base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
// Reset operation state.
operation_state->squeezing = false;
operation_state->append_d = false;
operation_state->offset = 0;
operation_state->r = 0;
operation_state->d = 0;
return kDifOk;
}
dif_result_t dif_kmac_config_is_locked(const dif_kmac_t *kmac,
bool *is_locked) {
if (kmac == NULL || is_locked == NULL) {
return kDifBadArg;
}
uint32_t reg =
mmio_region_read32(kmac->base_addr, KMAC_CFG_REGWEN_REG_OFFSET);
*is_locked = !bitfield_bit32_read(reg, KMAC_CFG_REGWEN_EN_BIT);
return kDifOk;
}
dif_result_t dif_kmac_get_status(const dif_kmac_t *kmac,
dif_kmac_status_t *kmac_status) {
if (kmac == NULL || kmac_status == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(kmac->base_addr, KMAC_STATUS_REG_OFFSET);
kmac_status->sha3_state = bitfield_field32_read(
reg,
(bitfield_field32_t){.mask = 0x07, .index = KMAC_STATUS_SHA3_IDLE_BIT});
kmac_status->fifo_depth =
bitfield_field32_read(reg, KMAC_STATUS_FIFO_DEPTH_FIELD);
kmac_status->fifo_state = bitfield_field32_read(
reg,
(bitfield_field32_t){.mask = 0x03, .index = KMAC_STATUS_FIFO_EMPTY_BIT});
kmac_status->faults = bitfield_field32_read(
reg, (bitfield_field32_t){.mask = 0x03,
.index = KMAC_STATUS_ALERT_FATAL_FAULT_BIT});
return kDifOk;
}
dif_result_t dif_kmac_get_hash_counter(const dif_kmac_t *kmac,
uint32_t *hash_ctr) {
if (kmac == NULL || hash_ctr == NULL) {
return kDifBadArg;
}
uint32_t reg = mmio_region_read32(kmac->base_addr,
KMAC_ENTROPY_REFRESH_HASH_CNT_REG_OFFSET);
*hash_ctr =
bitfield_field32_read(reg, KMAC_ENTROPY_REFRESH_HASH_CNT_HASH_CNT_FIELD);
return kDifOk;
}
dif_result_t dif_kmac_get_error(const dif_kmac_t *kmac,
dif_kmac_error_t *error) {
if (kmac == NULL || error == NULL) {
return kDifBadArg;
}
*error = mmio_region_read32(kmac->base_addr, KMAC_ERR_CODE_REG_OFFSET);
return kDifOk;
}
dif_result_t dif_kmac_reset(const dif_kmac_t *kmac,
dif_kmac_operation_state_t *operation_state) {
if (kmac == NULL || operation_state == NULL) {
return kDifBadArg;
}
operation_state->d = 0;
operation_state->r = 0;
operation_state->offset = 0;
operation_state->squeezing = false;
uint32_t reg =
mmio_region_read32(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET);
reg = bitfield_bit32_write(reg, KMAC_CFG_SHADOWED_ERR_PROCESSED_BIT, 1);
mmio_region_write32_shadowed(kmac->base_addr, KMAC_CFG_SHADOWED_REG_OFFSET,
reg);
return kDifOk;
}