blob: 412d80453023911efb1fcd5dca5a8f2335b69454 [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/silicon_creator/lib/drivers/kmac.h"
#include "sw/device/lib/base/abs_mmio.h"
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/silicon_creator/lib/error.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "kmac_regs.h" // Generated.
enum {
/**
* Base address of the KMAC hardware MMIO interface.
*/
kBase = TOP_EARLGREY_KMAC_BASE_ADDR,
/**
* Keccak capacity for SHAKE256.
*
* See FIPS 202, section 6.2.
*/
kShake256KeccakCapacity = 2 * 256,
/**
* Keccak rate for SHAKE256 (bits).
*
* Rate is 1600 - capacity (FIPS 202, section 6.2).
*/
kShake256KeccakRateBits = 1600 - kShake256KeccakCapacity,
/**
* Keccak rate for SHAKE256 (bytes).
*/
kShake256KeccakRateBytes = kShake256KeccakRateBits / 8,
/**
* Keccak rate for SHAKE256 (words).
*/
kShake256KeccakRateWords = kShake256KeccakRateBytes / sizeof(uint32_t),
/**
* Size of one share of the Keccak state.
*/
kStateShareSize = KMAC_STATE_SIZE_BYTES / 2,
/**
* Address of first share of Keccak state.
*/
kAddrStateShare0 = kBase + KMAC_STATE_REG_OFFSET,
/**
* Address of second share of Keccak state.
*/
kAddrStateShare1 = kBase + KMAC_STATE_REG_OFFSET + kStateShareSize,
};
// Double-check that calculated rate is smaller than one share of the state.
static_assert(kShake256KeccakRateWords <= kStateShareSize,
"assert SHAKE256 rate is <= share size");
/**
* Polls the KMAC block state until the desired status bit is set.
*
* If the KMAC block registers an error, this routine exits early and returns
* `kErrorKmacInvalidStatus`.
*
* @param bit_index Bit within the status register to poll.
* @return Result of the operation.
*/
static rom_error_t poll_state(bitfield_bit32_index_t bit_index) {
// The success condition of this function is:
// - The specified bit in the status register is 1, and
// - The error bit in KMAC's INTR_STATE register is 0.
//
// In order to make fault injection more difficult, we compute these values
// in a slightly convoluted way so that skipping any few instructions will
// not reach the success condition.
uint32_t status = 0;
rom_error_t res = launder32(kErrorOk ^ UINT32_MAX);
uint32_t is_error = (uint32_t)kHardenedBoolFalse;
do {
// Read the error bit.
uint32_t intr_state = abs_mmio_read32(kBase + KMAC_INTR_STATE_REG_OFFSET);
uint32_t err_bit = launder32((uint32_t)bitfield_bit32_read(
intr_state, KMAC_INTR_STATE_KMAC_ERR_BIT));
// If there is no error, (~err_bit) + 1 will be zero and `is_error` will
// remain `kHardenedBoolFalse`. Otherwise, ~err_bit + 1 will be
// UINT32_MAX and all bits will flip to produce a garbage value.
is_error ^= (~err_bit) + 1;
// Read the status register.
status = abs_mmio_read32(kBase + KMAC_STATUS_REG_OFFSET);
uint32_t flag = launder32((uint32_t)bitfield_bit32_read(status, bit_index));
// If `flag` is 0, then `res` will be unchanged (and remain `kErrorOk ^
// UINT32_MAX`). If it is 1, then all bits except the LSB will flip,
// meaning `res = kErrorOk ^ 1`.
res ^= ((~flag) + 1) << 1;
} while (!bitfield_bit32_read(launder32(status), bit_index) &&
launder32(is_error) == kHardenedBoolFalse);
// If the bit is set, this xor will set `res = kErrorOk`.
res ^= bitfield_bit32_read(status, bit_index);
if (launder32(is_error) == kHardenedBoolFalse) {
HARDENED_CHECK_EQ(is_error, kHardenedBoolFalse);
// The only way to get here is if the desired flag is set, meaning `res =
// kErrorOk`.
return res;
}
return kErrorKmacInvalidStatus;
}
/**
* Issue a command to the KMAC block.
*
* @param cmd_value Value to write to the CMD register.
*/
static void issue_command(uint32_t cmd_value) {
uint32_t cmd_reg = bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, cmd_value);
abs_mmio_write32(kBase + KMAC_CMD_REG_OFFSET, cmd_reg);
}
rom_error_t kmac_shake256_configure(void) {
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_IDLE_BIT));
uint32_t entropy_period_reg = KMAC_ENTROPY_PERIOD_REG_RESVAL;
// Set the wait timer to the maximum count.
entropy_period_reg = bitfield_field32_write(
entropy_period_reg, KMAC_ENTROPY_PERIOD_WAIT_TIMER_FIELD,
KMAC_ENTROPY_PERIOD_WAIT_TIMER_MASK);
// Set the prescaler to the maximum number of cycles.
entropy_period_reg = bitfield_field32_write(
entropy_period_reg, KMAC_ENTROPY_PERIOD_PRESCALER_FIELD,
KMAC_ENTROPY_PERIOD_PRESCALER_MASK);
abs_mmio_write32(kBase + KMAC_ENTROPY_PERIOD_REG_OFFSET, entropy_period_reg);
uint32_t cfg_reg = KMAC_CFG_SHADOWED_REG_RESVAL;
// Set `CFG.KMAC_EN` bit to 0.
// NOTE: If this driver is ever modified to perform an operation with
// KMAC_EN=true and use entropy from EDN, then the absorb() function must
// poll `STATUS.fifo_depth` to avoid a specific EDN-KMAC-Ibex deadlock
// scenario. See `absorb()` and the KMAC documentation for details.
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_KMAC_EN_BIT, 0);
// Set `CFG.KSTRENGTH` field to 256-bit strength.
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD,
KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256);
// Set `CFG.MODE` field to SHAKE.
cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD,
KMAC_CFG_SHADOWED_MODE_VALUE_SHAKE);
// Set `CFG.MSG_ENDIANNESS` bit to 0 (little-endian).
cfg_reg =
bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_MSG_ENDIANNESS_BIT, 0);
// Set `CFG.STATE_ENDIANNESS` bit to 0 (little-endian).
cfg_reg =
bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_STATE_ENDIANNESS_BIT, 0);
// Set `CFG.SIDELOAD` bit to 0 (no sideloading).
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_SIDELOAD_BIT, 0);
// Set `CFG.ENTROPY_MODE` field to use software entropy. SHAKE does not
// require any entropy, so there is no reason we should wait for entropy
// availability before we start hashing.
cfg_reg =
bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_MODE_FIELD,
KMAC_CFG_SHADOWED_ENTROPY_MODE_VALUE_SW_MODE);
// Set `CFG.ENTROPY_FAST_PROCESS` bit to 0.
cfg_reg = bitfield_bit32_write(cfg_reg,
KMAC_CFG_SHADOWED_ENTROPY_FAST_PROCESS_BIT, 0);
// Set `CFG.MSG_MASK` bit to 0.
cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_MSG_MASK_BIT, 0);
// Set `CFG.ENTROPY_READY` bit to 1.
cfg_reg =
bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_READY_BIT, 1);
// Set `CFG.ERR_PROCESSED` bit to 0.
cfg_reg =
bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_ERR_PROCESSED_BIT, 0);
// Set `CFG.EN_UNSUPPORTED_MODESTRENGTH` bit to 0.
cfg_reg = bitfield_bit32_write(
cfg_reg, KMAC_CFG_SHADOWED_EN_UNSUPPORTED_MODESTRENGTH_BIT, 0);
abs_mmio_write32_shadowed(kBase + KMAC_CFG_SHADOWED_REG_OFFSET, cfg_reg);
// Write entropy seed registers (all-zero). Even though the values are
// irrelevant, these registers must be written for the KMAC block to consider
// its entropy "ready" and to begin operation.
abs_mmio_write32(kBase + KMAC_ENTROPY_SEED_0_REG_OFFSET, 0);
abs_mmio_write32(kBase + KMAC_ENTROPY_SEED_1_REG_OFFSET, 0);
abs_mmio_write32(kBase + KMAC_ENTROPY_SEED_2_REG_OFFSET, 0);
abs_mmio_write32(kBase + KMAC_ENTROPY_SEED_3_REG_OFFSET, 0);
abs_mmio_write32(kBase + KMAC_ENTROPY_SEED_4_REG_OFFSET, 0);
return kErrorOk;
}
rom_error_t kmac_shake256_start(void) {
// Block until KMAC hardware is idle.
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_IDLE_BIT));
// Issue `CMD.START` to start the operation.
issue_command(KMAC_CMD_CMD_VALUE_START);
// Block until KMAC hardware is in the `absorb` state. After `CMD.START`,
// KMAC should never move out of the `absorb` state until `CMD.PROCESS` is
// issued, so we get significant performance gains by polling only once here
// instead of before every `absorb`.
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_ABSORB_BIT));
return kErrorOk;
}
void kmac_shake256_absorb(const uint8_t *in, size_t inlen) {
// This implementation does not poll `STATUS.fifo_depth` as recommended in
// the KMAC documentation. Normally, polling is required to prevent a
// deadlock scenario between Ibex, KMAC, and EDN. However, in this case it is
// safe to skip because `kmac_shake256_configure()` sets KMAC to use
// software-only entropy, and sets `kmac_en` to false (so KMAC will not
// produce entropy requests anyway). Since KMAC will therefore not block on
// EDN, it is guaranteed to keep processing message blocks. For more details,
// see the KMAC documentation:
// https://docs.opentitan.org/hw/ip/kmac/doc/#fifo-depth-and-empty-status
// Use byte-wide writes until the input pointer is aligned.
// Note: writes to the KMAC message FIFO are not required to be aligned.
for (; inlen > 0 && misalignment32_of((uintptr_t)in); --inlen, ++in) {
abs_mmio_write8(kBase + KMAC_MSG_FIFO_REG_OFFSET, *in);
}
// Use word writes for all full words.
for (; inlen >= sizeof(uint32_t);
inlen -= sizeof(uint32_t), in += sizeof(uint32_t)) {
abs_mmio_write32(kBase + KMAC_MSG_FIFO_REG_OFFSET, read_32(in));
}
// Use byte-wide writes for anything left over.
for (; inlen > 0; --inlen, ++in) {
abs_mmio_write8(kBase + KMAC_MSG_FIFO_REG_OFFSET, *in);
}
HARDENED_CHECK_EQ(inlen, 0);
}
void kmac_shake256_absorb_words(const uint32_t *in, size_t inlen) {
// This implementation does not poll `STATUS.fifo_depth` as recommended in
// the KMAC documentation. Normally, polling is required to prevent a
// deadlock scenario between Ibex, KMAC, and EDN. However, in this case it is
// safe to skip because `kmac_shake256_configure()` sets KMAC to use
// software-only entropy, and sets `kmac_en` to false (so KMAC will not
// produce entropy requests anyway). Since KMAC will therefore not block on
// EDN, it is guaranteed to keep processing message blocks. For more details,
// see the KMAC documentation:
// https://docs.opentitan.org/hw/ip/kmac/doc/#fifo-depth-and-empty-status
for (; inlen > 0; --inlen, ++in) {
abs_mmio_write32(kBase + KMAC_MSG_FIFO_REG_OFFSET, *in);
}
HARDENED_CHECK_EQ(inlen, 0);
}
void kmac_shake256_squeeze_start(void) {
// Issue `CMD.PROCESS` to move to the squeezing state.
issue_command(KMAC_CMD_CMD_VALUE_PROCESS);
}
rom_error_t kmac_shake256_squeeze_end(uint32_t *out, size_t outlen) {
size_t idx = 0;
while (launder32(idx) < outlen) {
// Since we always read in increments of the SHAKE-256 rate, the index at
// start should always be a multiple of the rate.
HARDENED_CHECK_EQ(idx % kShake256KeccakRateWords, 0);
// Poll the status register until in the 'squeeze' state.
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_SQUEEZE_BIT));
// Read words from the state registers (either `outlen` or the maximum
// number of words available).
size_t offset = 0;
for (; launder32(idx) < outlen && offset < kShake256KeccakRateWords;
++offset) {
uint32_t share0 =
abs_mmio_read32(kAddrStateShare0 + offset * sizeof(uint32_t));
uint32_t share1 =
abs_mmio_read32(kAddrStateShare1 + offset * sizeof(uint32_t));
out[idx] = share0 ^ share1;
++idx;
}
if (offset == kShake256KeccakRateWords) {
// If we read all the remaining words, issue `CMD.RUN` to generate more
// state.
HARDENED_CHECK_EQ(offset, kShake256KeccakRateWords);
issue_command(KMAC_CMD_CMD_VALUE_RUN);
}
}
HARDENED_CHECK_EQ(idx, outlen);
// Poll the status register until in the 'squeeze' state.
HARDENED_RETURN_IF_ERROR(poll_state(KMAC_STATUS_SHA3_SQUEEZE_BIT));
// Issue `CMD.DONE` to finish the operation.
issue_command(KMAC_CMD_CMD_VALUE_DONE);
return kErrorOk;
}