| // 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/crypto/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 "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| #include "kmac_regs.h" // Generated. |
| |
| /** |
| * Security strength values. |
| * |
| * These values corresponds to the half of the capacity of Keccak permutation. |
| */ |
| typedef enum kmac_security_str { |
| kKmacSecurityStrength128 = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L128, |
| kKmacSecurityStrength224 = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L224, |
| kKmacSecurityStrength256 = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256, |
| kKmacSecurityStrength384 = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L384, |
| kKmacSecurityStrength512 = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L512, |
| } kmac_security_str_t; |
| |
| /** |
| * List of supported KMAC modes. |
| * |
| * Each `kmac_operation_t` enumeration constant is a bitfield with the |
| * following layout: |
| * - Bit 0: kmac_en (Whether to enable KMAC datapath). |
| * - Bit 1-2: Keccak hashing mode (e.g. SHA, SHAKE, or cSHAKE). |
| */ |
| typedef enum kmac_operation { |
| kKmacOperationSHA3 = KMAC_CFG_SHADOWED_MODE_VALUE_SHA3 << 1 | 0, |
| kKmacOperationSHAKE = KMAC_CFG_SHADOWED_MODE_VALUE_SHAKE << 1 | 0, |
| kKmacOperationCSHAKE = KMAC_CFG_SHADOWED_MODE_VALUE_CSHAKE << 1 | 0, |
| kKmacOperationKMAC = KMAC_CFG_SHADOWED_MODE_VALUE_CSHAKE << 1 | 1, |
| } kmac_operation_t; |
| |
| /** |
| * List of supported KMAC key sizes. |
| */ |
| typedef enum kmac_key_length { |
| kKmacKeyLength128 = KMAC_KEY_LEN_LEN_VALUE_KEY128, |
| kKmacKeyLength192 = KMAC_KEY_LEN_LEN_VALUE_KEY192, |
| kKmacKeyLength256 = KMAC_KEY_LEN_LEN_VALUE_KEY256, |
| kKmacKeyLength384 = KMAC_KEY_LEN_LEN_VALUE_KEY384, |
| kKmacKeyLength512 = KMAC_KEY_LEN_LEN_VALUE_KEY512, |
| } kmac_key_len_t; |
| |
| enum { |
| kKmacPrefixRegCount = 4 * KMAC_PREFIX_MULTIREG_COUNT, |
| kKmacBaseAddr = TOP_EARLGREY_KMAC_BASE_ADDR, |
| kKmacCfgAddr = kKmacBaseAddr + KMAC_CFG_SHADOWED_REG_OFFSET, |
| kKmacKeyShare0Addr = kKmacBaseAddr + KMAC_KEY_SHARE0_0_REG_OFFSET, |
| kKmacKeyShare1Addr = kKmacBaseAddr + KMAC_KEY_SHARE1_0_REG_OFFSET, |
| }; |
| |
| // "KMAC" string in little endian |
| static const crypto_const_uint8_buf_t kKmacFuncNameKMAC = { |
| .data = (uint8_t *)"\x4b\x4d\x41\x43", |
| .len = 4, |
| }; |
| |
| // Empty string |
| static const crypto_const_uint8_buf_t kKmacEmptyString = { |
| .data = NULL, |
| .len = 0, |
| }; |
| |
| OT_ASSERT_ENUM_VALUE(kKmacPrefixMaxSize, 4 * KMAC_PREFIX_MULTIREG_COUNT - 4); |
| |
| static const uint32_t prefix_offsets[] = { |
| KMAC_PREFIX_0_REG_OFFSET, KMAC_PREFIX_1_REG_OFFSET, |
| KMAC_PREFIX_2_REG_OFFSET, KMAC_PREFIX_3_REG_OFFSET, |
| KMAC_PREFIX_4_REG_OFFSET, KMAC_PREFIX_5_REG_OFFSET, |
| KMAC_PREFIX_6_REG_OFFSET, KMAC_PREFIX_7_REG_OFFSET, |
| KMAC_PREFIX_8_REG_OFFSET, KMAC_PREFIX_9_REG_OFFSET, |
| KMAC_PREFIX_10_REG_OFFSET, |
| }; |
| |
| // Check that KEY_SHARE registers form a continuous address space |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_1_REG_OFFSET, |
| KMAC_KEY_SHARE0_0_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_2_REG_OFFSET, |
| KMAC_KEY_SHARE0_1_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_3_REG_OFFSET, |
| KMAC_KEY_SHARE0_2_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_4_REG_OFFSET, |
| KMAC_KEY_SHARE0_3_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_5_REG_OFFSET, |
| KMAC_KEY_SHARE0_4_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_6_REG_OFFSET, |
| KMAC_KEY_SHARE0_5_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_7_REG_OFFSET, |
| KMAC_KEY_SHARE0_6_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_8_REG_OFFSET, |
| KMAC_KEY_SHARE0_7_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_9_REG_OFFSET, |
| KMAC_KEY_SHARE0_8_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_10_REG_OFFSET, |
| KMAC_KEY_SHARE0_9_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_11_REG_OFFSET, |
| KMAC_KEY_SHARE0_10_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_12_REG_OFFSET, |
| KMAC_KEY_SHARE0_11_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_13_REG_OFFSET, |
| KMAC_KEY_SHARE0_12_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_14_REG_OFFSET, |
| KMAC_KEY_SHARE0_13_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE0_15_REG_OFFSET, |
| KMAC_KEY_SHARE0_14_REG_OFFSET + 4); |
| |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_1_REG_OFFSET, |
| KMAC_KEY_SHARE1_0_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_2_REG_OFFSET, |
| KMAC_KEY_SHARE1_1_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_3_REG_OFFSET, |
| KMAC_KEY_SHARE1_2_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_4_REG_OFFSET, |
| KMAC_KEY_SHARE1_3_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_5_REG_OFFSET, |
| KMAC_KEY_SHARE1_4_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_6_REG_OFFSET, |
| KMAC_KEY_SHARE1_5_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_7_REG_OFFSET, |
| KMAC_KEY_SHARE1_6_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_8_REG_OFFSET, |
| KMAC_KEY_SHARE1_7_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_9_REG_OFFSET, |
| KMAC_KEY_SHARE1_8_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_10_REG_OFFSET, |
| KMAC_KEY_SHARE1_9_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_11_REG_OFFSET, |
| KMAC_KEY_SHARE1_10_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_12_REG_OFFSET, |
| KMAC_KEY_SHARE1_11_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_13_REG_OFFSET, |
| KMAC_KEY_SHARE1_12_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_14_REG_OFFSET, |
| KMAC_KEY_SHARE1_13_REG_OFFSET + 4); |
| OT_ASSERT_ENUM_VALUE(KMAC_KEY_SHARE1_15_REG_OFFSET, |
| KMAC_KEY_SHARE1_14_REG_OFFSET + 4); |
| |
| OT_ASSERT_ENUM_VALUE(ARRAYSIZE(prefix_offsets), KMAC_PREFIX_MULTIREG_COUNT); |
| |
| // Ensure each PREFIX register is 4 bytes |
| OT_ASSERT_ENUM_VALUE(32, KMAC_PREFIX_PREFIX_FIELD_WIDTH); |
| |
| /** |
| * Return the rate (in bytes) for given security strength. |
| * |
| * The caller must ensure that `keccak_rate` is not a NULL pointer. This is not |
| * checked within this function. |
| * |
| * @param security_str Security strength. |
| * @param keccak_rate The keccak rate in bytes. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_get_keccak_rate_bytes(kmac_security_str_t security_str, |
| size_t *keccak_rate) { |
| // Since Keccak state is 1600 bits, rate is calculated with |
| // rate = (1600 - 2*x) where x is the security strength (i.e. half the |
| // capacity). Here, `keccak_rate` is in bytes. |
| switch (security_str) { |
| case kKmacSecurityStrength128: |
| *keccak_rate = (1600 - 2 * 128) / 8; |
| break; |
| case kKmacSecurityStrength224: |
| *keccak_rate = (1600 - 2 * 224) / 8; |
| break; |
| case kKmacSecurityStrength256: |
| *keccak_rate = (1600 - 2 * 256) / 8; |
| break; |
| case kKmacSecurityStrength384: |
| *keccak_rate = (1600 - 2 * 384) / 8; |
| break; |
| case kKmacSecurityStrength512: |
| *keccak_rate = (1600 - 2 * 512) / 8; |
| break; |
| default: |
| return kKmacArgsError; |
| } |
| return kKmacOk; |
| } |
| |
| /** |
| * Return the matching enum of `kmac_key_len_t` for given key length. |
| * |
| * `key_len_enum` must not be NULL pointer. |
| * |
| * @param key_len The size of the key in bytes. |
| * @param key_len_enum The corresponding enum value to be returned. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_get_key_len_bytes(size_t key_len, |
| kmac_key_len_t *key_len_enum) { |
| switch (key_len) { |
| case 128 / 8: |
| *key_len_enum = kKmacKeyLength128; |
| break; |
| case 192 / 8: |
| *key_len_enum = kKmacKeyLength192; |
| break; |
| case 256 / 8: |
| *key_len_enum = kKmacKeyLength256; |
| break; |
| case 384 / 8: |
| *key_len_enum = kKmacKeyLength384; |
| break; |
| case 512 / 8: |
| *key_len_enum = kKmacKeyLength512; |
| break; |
| default: |
| return kKmacUnsupportedKeySizeError; |
| } |
| return kKmacOk; |
| } |
| |
| bool kmac_is_valid_key_len(size_t key_len) { |
| kmac_key_len_t key_len_enum; |
| return kmac_get_key_len_bytes(key_len, &key_len_enum) == kKmacOk; |
| } |
| |
| kmac_error_t kmac_hwip_default_configure(void) { |
| uint32_t status_reg = abs_mmio_read32(kKmacBaseAddr + KMAC_STATUS_REG_OFFSET); |
| |
| // Check that core is not in fault state |
| if (bitfield_bit32_read(status_reg, KMAC_STATUS_ALERT_FATAL_FAULT_BIT)) { |
| return kKmacFatalFaultError; |
| } |
| if (bitfield_bit32_read(status_reg, |
| KMAC_STATUS_ALERT_RECOV_CTRL_UPDATE_ERR_BIT)) { |
| return kKmacRecovFaultError; |
| } |
| // Check that core is not busy |
| if (!bitfield_bit32_read(status_reg, KMAC_STATUS_SHA3_IDLE_BIT)) { |
| return kKmacNotIdle; |
| } |
| |
| // Check that there is no err pending in intr state |
| uint32_t intr_state = |
| abs_mmio_read32(kKmacBaseAddr + KMAC_INTR_STATE_REG_OFFSET); |
| if (bitfield_bit32_read(intr_state, KMAC_INTR_STATE_KMAC_ERR_BIT)) { |
| return kKmacIntrErrPending; |
| } |
| |
| // Check CFG.regwen |
| uint32_t cfg_regwen = |
| abs_mmio_read32(kKmacBaseAddr + KMAC_CFG_REGWEN_REG_OFFSET); |
| if (!bitfield_bit32_read(cfg_regwen, KMAC_CFG_REGWEN_EN_BIT)) { |
| return kKmacCfgWriteDisabled; |
| } |
| |
| // Keep err interrupt disabled |
| uint32_t intr_reg = KMAC_INTR_ENABLE_REG_RESVAL; |
| intr_reg = bitfield_bit32_write(intr_reg, KMAC_INTR_ENABLE_KMAC_ERR_BIT, 0); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_INTR_ENABLE_REG_OFFSET, intr_reg); |
| |
| // Configure max for entropy period (use UINT32_MAX and let bitfield clamp |
| // them to their bitfield) |
| uint32_t entropy_period = KMAC_ENTROPY_PERIOD_REG_RESVAL; |
| entropy_period = bitfield_field32_write( |
| entropy_period, KMAC_ENTROPY_PERIOD_PRESCALER_FIELD, UINT32_MAX); |
| entropy_period = bitfield_field32_write( |
| entropy_period, KMAC_ENTROPY_PERIOD_WAIT_TIMER_FIELD, UINT32_MAX); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_ENTROPY_PERIOD_REG_OFFSET, |
| entropy_period); |
| |
| // Configure max for hash threshold (use UINT32_MAX and let bitfield clamp |
| // them to their bitfield) |
| uint32_t entropy_hash_threshold = |
| KMAC_ENTROPY_REFRESH_THRESHOLD_SHADOWED_REG_RESVAL; |
| entropy_hash_threshold = bitfield_field32_write( |
| entropy_hash_threshold, |
| KMAC_ENTROPY_REFRESH_THRESHOLD_SHADOWED_THRESHOLD_FIELD, UINT32_MAX); |
| abs_mmio_write32( |
| kKmacBaseAddr + KMAC_ENTROPY_REFRESH_THRESHOLD_SHADOWED_REG_OFFSET, |
| entropy_hash_threshold); |
| |
| // Configure CFG |
| uint32_t cfg_reg = KMAC_CFG_SHADOWED_REG_RESVAL; |
| // Little_endian |
| cfg_reg = |
| bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_MSG_ENDIANNESS_BIT, 0); |
| cfg_reg = |
| bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_STATE_ENDIANNESS_BIT, 0); |
| |
| // Sideload: off, default key comes from SW |
| cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_SIDELOAD_BIT, 0); |
| |
| // Entropy mode: EDN |
| cfg_reg = |
| bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_MODE_FIELD, |
| KMAC_CFG_SHADOWED_ENTROPY_MODE_VALUE_EDN_MODE); |
| |
| // Use quality randomness for message blocks too |
| cfg_reg = bitfield_bit32_write(cfg_reg, |
| KMAC_CFG_SHADOWED_ENTROPY_FAST_PROCESS_BIT, 1); |
| // Do not remask message blocks |
| cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_MSG_MASK_BIT, 0); |
| |
| // Mark entropy source as ready |
| cfg_reg = |
| bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_ENTROPY_READY_BIT, 1); |
| // Err not processed |
| cfg_reg = |
| bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_ERR_PROCESSED_BIT, 0); |
| // Unsupported modes: disabled |
| cfg_reg = bitfield_bit32_write( |
| cfg_reg, KMAC_CFG_SHADOWED_EN_UNSUPPORTED_MODESTRENGTH_BIT, 0); |
| |
| abs_mmio_write32_shadowed(kKmacBaseAddr + KMAC_CFG_SHADOWED_REG_OFFSET, |
| cfg_reg); |
| |
| return kKmacOk; |
| } |
| |
| /** |
| * Wait until given status bit is set. |
| * |
| * Loops until the `bit_position` of status register reaches the value |
| * `bit_value`. |
| * @param bit_position The bit position in the status register. |
| * @param bit_value Whether it should wait for 0 or 1. |
| * @return Error status. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t wait_status_bit(uint32_t bit_position, bool bit_value) { |
| if (bit_position > 31) { |
| return kKmacArgsError; |
| } |
| |
| while (true) { |
| uint32_t reg = abs_mmio_read32(kKmacBaseAddr + KMAC_STATUS_REG_OFFSET); |
| if (bitfield_bit32_read(reg, KMAC_STATUS_ALERT_FATAL_FAULT_BIT) || |
| bitfield_bit32_read(reg, KMAC_STATUS_ALERT_RECOV_CTRL_UPDATE_ERR_BIT)) { |
| return kKmacInternalError; |
| } |
| if (bitfield_bit32_read(reg, bit_position) == bit_value) { |
| return kKmacOk; |
| } |
| } |
| } |
| |
| /** |
| * Encode a given integer as byte array and return its size along with it. |
| * |
| * This is a common procedure that can be used to implement both `left_encode` |
| * and `right_encode` functions defined in NIST SP 800-185. Given an integer |
| * `value` it returns its encoding as a byte array in `encoding_buf`. Meanwhile, |
| * `encoding_header` keeps the size of `encoding_buf`. Later the two can be |
| * combined as below: |
| * |
| * left_encode(`value`) = `encoding_header` || `encoding_buf` |
| * right_encode(`value`) = `encoding_buf` || `encoding_header` |
| * |
| * The caller must ensure that `encoding_buf` and `encoding_header` are not |
| * NULL pointers. This is not checked within this function. |
| * |
| * @param value Integer to be encoded. |
| * @param encoding_buf The output byte array representing `value`. |
| * @param encoding_header The number of bytes written to `encoded_value`. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t little_endian_encode(size_t value, uint8_t *encoding_buf, |
| uint8_t *encoding_header) { |
| uint8_t len = 0; |
| do { |
| encoding_buf[len] = value & UINT8_MAX; |
| value >>= 8; |
| len++; |
| } while (value > 0); |
| *encoding_header = len; |
| |
| return kKmacOk; |
| } |
| |
| /** |
| * Set prefix registers. |
| * |
| * This function directly writes to PREFIX registers of KMAC HWIP. |
| * |
| * @param func_name Function name input in cSHAKE. |
| * @param cust_str Customization string input in cSHAKE. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_set_prefix_regs(crypto_const_uint8_buf_t func_name, |
| crypto_const_uint8_buf_t cust_str) { |
| // Initialize with 0 so that the last untouched bytes are set as 0x0 |
| uint32_t prefix_buffer[kKmacPrefixRegCount] = {0x0}; |
| uint8_t *prefix_buf_ptr = (uint8_t *)prefix_buffer; |
| |
| if (func_name.len + cust_str.len > kKmacPrefixMaxSize) { |
| return kKmacArgsError; |
| } |
| |
| size_t func_name_len_bits = 8 * func_name.len; |
| size_t cust_str_len_bits = 8 * cust_str.len; |
| size_t i = 0; |
| size_t write_cnt = 0; |
| uint8_t j = 0; |
| kmac_error_t err; |
| |
| // left_encode(`func_name_len_bits`) below |
| err = little_endian_encode(func_name_len_bits, prefix_buf_ptr + 1, &j); |
| if (err != kKmacOk) { |
| return err; |
| } |
| prefix_buf_ptr[0] = j; |
| write_cnt += j + 1; |
| |
| // copy `func_name` |
| memcpy(prefix_buf_ptr + write_cnt, func_name.data, func_name.len); |
| |
| write_cnt += func_name.len; |
| |
| // left_encode(`cust_str_len_bits`) below |
| err = little_endian_encode(cust_str_len_bits, prefix_buf_ptr + write_cnt + 1, |
| &j); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| // copy `cust_str` |
| prefix_buf_ptr[write_cnt] = j; |
| write_cnt += j + 1; |
| memcpy(prefix_buf_ptr + write_cnt, cust_str.data, cust_str.len); |
| |
| // Copy from `prefix_buffer` to PREFIX_REGS |
| for (i = 0; i < KMAC_PREFIX_MULTIREG_COUNT; i++) { |
| abs_mmio_write32(kKmacBaseAddr + prefix_offsets[i], prefix_buffer[i]); |
| } |
| |
| return kKmacOk; |
| } |
| |
| /** |
| * Initializes the KMAC configuration. |
| * |
| * In particular, this function sets the CFG register of KMAC for given |
| * `operation_type`. The struct type kmac_operation_t is defined in a way that |
| * each field inherently implies a fixed security strength (i.e. half of Keccak |
| * capacity). For instance, if we want to run SHA-3 with 224-bit digest size, |
| * then `operation_type` = kSHA3_224. |
| * |
| * |
| * @param operation The chosen operation, see kmac_operation_t struct. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_init(kmac_operation_t operation, |
| kmac_security_str_t security_str) { |
| kmac_error_t err = wait_status_bit(KMAC_STATUS_SHA3_IDLE_BIT, 1); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| // We need to preserve some bits of CFG register, such as: |
| // entropy_mode, entropy_ready etc. On the other hand, some bits |
| // need to be reset for each invocation. |
| uint32_t cfg_reg = |
| abs_mmio_read32(kKmacBaseAddr + KMAC_CFG_SHADOWED_REG_OFFSET); |
| |
| // Make sure kmac_en and sideload bits of CFG are reset at each invocation |
| // These bits should be set to 1 only if needed by the rest of the code |
| // in this function. |
| cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_KMAC_EN_BIT, 0); |
| cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_SIDELOAD_BIT, 0); |
| |
| // operation bit fields: Bit 0: `kmac_en`, Bit 1-2: `mode` |
| cfg_reg = bitfield_bit32_write(cfg_reg, KMAC_CFG_SHADOWED_KMAC_EN_BIT, |
| operation & 1); |
| cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_MODE_FIELD, |
| operation >> 1); |
| |
| cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD, |
| security_str); |
| abs_mmio_write32_shadowed(kKmacBaseAddr + KMAC_CFG_SHADOWED_REG_OFFSET, |
| cfg_reg); |
| |
| return kKmacOk; |
| } |
| |
| /** |
| * Configure the prefix registers with customization string. |
| * |
| * For KMAC, this function ignores `func_name` and uses "KMAC" instead. |
| * |
| * The caller must ensure that `func_name` and `cust_str` have properly |
| * allocated `data` fields whose length matches their `len` fields. |
| * |
| * In total `func_name` and `cust_str` can be at most `kKmacPrefixMaxSize` |
| * bytes. |
| * |
| * @param operation The KMAC or cSHAKE operation. |
| * @param func_name The function name, used for cSHAKE. |
| * @param cust_str The customization string (both for cSHAKE and KMAC). |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_write_prefix_block(kmac_operation_t operation, |
| crypto_const_uint8_buf_t func_name, |
| crypto_const_uint8_buf_t cust_str) { |
| if (operation == kKmacOperationCSHAKE) { |
| return kmac_set_prefix_regs(func_name, cust_str); |
| } else if (operation == kKmacOperationKMAC) { |
| return kmac_set_prefix_regs(/*func_name=*/kKmacFuncNameKMAC, cust_str); |
| } |
| return kKmacArgsError; |
| } |
| |
| /** |
| * Update the key registers with given key shares. |
| * |
| * The caller must ensure that `key` struct is properly populated (no NULL |
| * pointers and matching `len`). |
| * |
| * The accepted `key->len` values are {128 / 8, 192 / 8, 256 / 8, 384 / 8, |
| * 512 / 8}, otherwise an error will be returned. |
| * |
| * @param key The input key passed as a struct. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_write_key_block(kmac_blinded_key_t *key) { |
| kmac_key_len_t key_len_enum; |
| kmac_error_t err = kmac_get_key_len_bytes(key->len, &key_len_enum); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| uint32_t key_len_reg = bitfield_field32_write( |
| KMAC_KEY_LEN_REG_RESVAL, KMAC_KEY_LEN_LEN_FIELD, key_len_enum); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_KEY_LEN_REG_OFFSET, key_len_reg); |
| |
| for (size_t i = 0; i < key->len; i += 4) { |
| abs_mmio_write32(kKmacKeyShare0Addr + i, key->share0[i / 4]); |
| abs_mmio_write32(kKmacKeyShare1Addr + i, key->share1[i / 4]); |
| } |
| |
| return kKmacOk; |
| } |
| |
| /** |
| * Common routine for feeding message blocks during SHA/SHAKE/cSHAKE/KMAC. |
| * |
| * Before running this, the operation type must be configured with kmac_init. |
| * Then, we can use this function to feed various bytes of data to the KMAC |
| * core. Note that this is a one-shot implementation, and it does not support |
| * streaming mode. This decision is in accord with OpenTitan's Crypto Library |
| * Specification. Refer to the Hash section of this specification. |
| * |
| * Current implementation has few limitiations: |
| * |
| * 1. `message.data` pointer is assumed to be word-aligned. The case where |
| * `data` field is not divisible by 4 is not yet implemented. This is about |
| * extra protection against SCA. |
| * |
| * 2. Currently, there is no error check on consisteny of the input parameters. |
| * For instance, one can invoke SHA-3_224 with digest_len=32, which will produce |
| * 256 bits of digest. |
| * |
| * The caller must ensure that `message` and `digest` have properly |
| * allocated `data` fields whose length matches their `len` fields. |
| * |
| * @param operation The operation type. |
| * @param message Input message string. |
| * @param digest The struct to which the result will be written. |
| * @return Error code. |
| */ |
| OT_WARN_UNUSED_RESULT |
| static kmac_error_t kmac_process_msg_blocks(kmac_operation_t operation, |
| crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| uint32_t cmd_reg = KMAC_CMD_REG_RESVAL; |
| kmac_error_t err; |
| |
| // Assumption(1): `message.len` > 0 and `digest->len` > 0 |
| // Assumption(2): message.data % 4 = 0, i.e. the message.data is 32-bit |
| // aligned |
| |
| err = wait_status_bit(KMAC_STATUS_SHA3_IDLE_BIT, 1); |
| // Issue the start command, so that messages written to MSG_FIFO are forwarded |
| // to Keccak |
| cmd_reg = bitfield_field32_write(cmd_reg, KMAC_CMD_CMD_FIELD, |
| KMAC_CMD_CMD_VALUE_START); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_CMD_REG_OFFSET, cmd_reg); |
| err = wait_status_bit(KMAC_STATUS_SHA3_ABSORB_BIT, 1); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| // Begin by writing a word at a time |
| uint32_t *word_ptr = (uint32_t *)message.data; |
| size_t i = 0; |
| for (; i < message.len / 4; i++) { |
| err = wait_status_bit(KMAC_STATUS_FIFO_FULL_BIT, 0); |
| if (err != kKmacOk) { |
| return err; |
| } |
| abs_mmio_write32(kKmacBaseAddr + KMAC_MSG_FIFO_REG_OFFSET, word_ptr[i]); |
| } |
| |
| // For the last few bytes, we need to write byte at a time |
| // i = 4*(message.len/4) |
| for (i = 4 * i; i < message.len; i++) { |
| err = wait_status_bit(KMAC_STATUS_FIFO_FULL_BIT, 0); |
| if (err != kKmacOk) { |
| return err; |
| } |
| abs_mmio_write8(kKmacBaseAddr + KMAC_MSG_FIFO_REG_OFFSET + (i % 4), |
| message.data[i]); |
| } |
| |
| // If operation=KMAC, then we need to write `right_encode(digest->len)` |
| if (operation == kKmacOperationKMAC) { |
| uint32_t digest_len_bits = 8 * digest->len; |
| if (digest_len_bits / 8 != digest->len) { |
| return kKmacDigestLenTooLongError; |
| } |
| |
| // right_encode(`digest_len_bit`) below |
| // The encoded buffer in total occupies at most 256 bytes according to |
| // NIST SP 800-185 (1 byte for encoding header and 255 byte for the encoded |
| // buffer at max) |
| uint8_t buf[256] = {0}; |
| uint8_t encoding_header; |
| err = little_endian_encode(digest_len_bits, buf, &encoding_header); |
| if (err != kKmacOk) { |
| return err; |
| } |
| buf[encoding_header] = encoding_header; |
| |
| // Because the MSG_FIFO is also little endian we need to write in reverse |
| // order |
| for (size_t i = 0; i < encoding_header; i++) { |
| abs_mmio_write8( |
| kKmacBaseAddr + KMAC_MSG_FIFO_REG_OFFSET + ((message.len + i) % 4), |
| buf[encoding_header - i - 1]); |
| } |
| |
| // Finally write `encoding_header` as the last byte |
| abs_mmio_write8( |
| kKmacBaseAddr + KMAC_MSG_FIFO_REG_OFFSET + ((message.len + i) % 4), |
| buf[encoding_header]); |
| } |
| |
| // Issue the process command, so that squeezing phase can start |
| cmd_reg = KMAC_CMD_REG_RESVAL; |
| cmd_reg = bitfield_field32_write(cmd_reg, KMAC_CMD_CMD_FIELD, |
| KMAC_CMD_CMD_VALUE_PROCESS); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_CMD_REG_OFFSET, cmd_reg); |
| |
| // Wait until squeezing is done |
| err = wait_status_bit(KMAC_STATUS_SHA3_SQUEEZE_BIT, 1); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| uint32_t cfg_reg = |
| abs_mmio_read32(kKmacBaseAddr + KMAC_CFG_SHADOWED_REG_OFFSET); |
| uint32_t keccak_str = |
| bitfield_field32_read(cfg_reg, KMAC_CFG_SHADOWED_KSTRENGTH_FIELD); |
| size_t keccak_rate; |
| err = kmac_get_keccak_rate_bytes(keccak_str, &keccak_rate); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| // Finally, we can read the two shares of digest and XOR them |
| // Here the counter i denotes the number of bytes read from Keccak state |
| for (i = 0; i < digest->len; i++) { |
| // Do we require additional Keccak rounds? |
| if ((i % keccak_rate) == 0 && i > 0) { |
| // if we consumed all Keccak state and aren't done yet, run one more |
| // Keccak round |
| cmd_reg = KMAC_CMD_REG_RESVAL; |
| cmd_reg = bitfield_field32_write(cmd_reg, KMAC_CMD_CMD_FIELD, |
| KMAC_CMD_CMD_VALUE_RUN); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_CMD_REG_OFFSET, cmd_reg); |
| |
| // Let Keccak core finish the extra squeezing round |
| err = wait_status_bit(KMAC_STATUS_SHA3_SQUEEZE_BIT, 1); |
| if (err != kKmacOk) { |
| return err; |
| } |
| } |
| digest->data[i] = abs_mmio_read8(kKmacBaseAddr + KMAC_STATE_REG_OFFSET + |
| (i % keccak_rate)); |
| digest->data[i] ^= |
| abs_mmio_read8(kKmacBaseAddr + KMAC_STATE_REG_OFFSET + |
| KMAC_STATE_SIZE_BYTES / 2 + (i % keccak_rate)); |
| } |
| |
| // Release the KMAC core, so that it goes back to idle mode |
| cmd_reg = KMAC_CMD_REG_RESVAL; |
| cmd_reg = bitfield_field32_write(cmd_reg, KMAC_CMD_CMD_FIELD, |
| KMAC_CMD_CMD_VALUE_DONE); |
| abs_mmio_write32(kKmacBaseAddr + KMAC_CMD_REG_OFFSET, cmd_reg); |
| |
| return kKmacOk; |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_sha3_224(crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationSHA3, kKmacSecurityStrength224); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationSHA3, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_sha3_256(crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationSHA3, kKmacSecurityStrength256); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationSHA3, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_sha3_384(crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationSHA3, kKmacSecurityStrength384); |
| |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationSHA3, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_sha3_512(crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationSHA3, kKmacSecurityStrength512); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationSHA3, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_shake_128(crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationSHAKE, kKmacSecurityStrength128); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationSHAKE, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_shake_256(crypto_const_uint8_buf_t message, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationSHAKE, kKmacSecurityStrength256); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationSHAKE, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_cshake_128(crypto_const_uint8_buf_t message, |
| crypto_const_uint8_buf_t func_name, |
| crypto_const_uint8_buf_t cust_str, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationCSHAKE, kKmacSecurityStrength128); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| err = kmac_write_prefix_block(kKmacOperationCSHAKE, func_name, cust_str); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationCSHAKE, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_cshake_256(crypto_const_uint8_buf_t message, |
| crypto_const_uint8_buf_t func_name, |
| crypto_const_uint8_buf_t cust_str, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationCSHAKE, kKmacSecurityStrength256); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| err = kmac_write_prefix_block(kKmacOperationCSHAKE, func_name, cust_str); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationCSHAKE, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_kmac_128(kmac_blinded_key_t *key, |
| crypto_const_uint8_buf_t message, |
| crypto_const_uint8_buf_t cust_str, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationKMAC, kKmacSecurityStrength128); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| err = kmac_write_key_block(key); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| err = kmac_write_prefix_block(kKmacOperationKMAC, kKmacEmptyString, cust_str); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationKMAC, message, digest); |
| } |
| |
| OT_WARN_UNUSED_RESULT |
| kmac_error_t kmac_kmac_256(kmac_blinded_key_t *key, |
| crypto_const_uint8_buf_t message, |
| crypto_const_uint8_buf_t cust_str, |
| crypto_uint8_buf_t *digest) { |
| kmac_error_t err = kmac_init(kKmacOperationKMAC, kKmacSecurityStrength256); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| err = kmac_write_key_block(key); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| err = kmac_write_prefix_block(kKmacOperationKMAC, kKmacEmptyString, cust_str); |
| if (err != kKmacOk) { |
| return err; |
| } |
| |
| return kmac_process_msg_blocks(kKmacOperationKMAC, message, digest); |
| } |