|  | // 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/base/macros.h" | 
|  | #include "sw/device/lib/base/memory.h" | 
|  | #include "sw/device/lib/dif/dif_kmac.h" | 
|  | #include "sw/device/lib/runtime/log.h" | 
|  | #include "sw/device/lib/testing/test_framework/ottf_main.h" | 
|  | #include "sw/device/lib/testing/test_framework/ottf_test_config.h" | 
|  | #include "sw/device/sca/lib/prng.h" | 
|  | #include "sw/device/sca/lib/sca.h" | 
|  | #include "sw/device/sca/lib/simple_serial.h" | 
|  |  | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | 
|  | #include "kmac_regs.h" | 
|  |  | 
|  | /** | 
|  | * OpenTitan program for side-channel analysis of the absorb step of a KMAC128 | 
|  | * operation using a 128-bit key. | 
|  | * | 
|  | * This program implements the following simple serial commands: | 
|  | *   - Set key ('k')*, | 
|  | *   - Absorb ('p')*, | 
|  | *   - FvsR batch absorb ('b')*, | 
|  | *   - FvsR batch fixed key set ('t')*, | 
|  | *   - Version ('v')+, | 
|  | *   - Seed PRNG ('s')+, | 
|  | * Commands marked with * are implemented in this file. Those marked with + are | 
|  | * implemented in the simple serial library. See | 
|  | * https://wiki.newae.com/SimpleSerial for details on the protocol. | 
|  | */ | 
|  |  | 
|  | OTTF_DEFINE_TEST_CONFIG(); | 
|  |  | 
|  | enum { | 
|  | /** | 
|  | * Key length in bytes. | 
|  | */ | 
|  | kKeyLength = 16, | 
|  | /** | 
|  | * Message length in bytes. | 
|  | */ | 
|  | kMessageLength = 16, | 
|  | /** | 
|  | * Digest length in 32-bit words. | 
|  | */ | 
|  | kDigestLength = 8, | 
|  | /** | 
|  | * Number of cycles (at `kClockFreqCpuHz`) that Ibex should sleep to minimize | 
|  | * noise during SHA3 operations. Caution: This number should be chosen to | 
|  | * provide enough time. Otherwise, Ibex might wake up while SHA3 is still busy | 
|  | * and disturb the capture. Currently, we use a start trigger delay of 40 | 
|  | * clock cycles and the scope captures 200 clock cycles at kClockFreqCpuHz | 
|  | * (2000 samples). | 
|  | */ | 
|  | kIbexSha3SleepCycles = 800, | 
|  | /** | 
|  | * Max number of traces per batch. | 
|  | */ | 
|  | kNumBatchOpsMax = 128, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * A handle to KMAC. | 
|  | */ | 
|  | static dif_kmac_t kmac; | 
|  |  | 
|  | /** | 
|  | * KMAC operation state. | 
|  | */ | 
|  | static dif_kmac_operation_state_t kmac_operation_state; | 
|  |  | 
|  | /** | 
|  | * KMAC key. | 
|  | * | 
|  | * Used for caching the key in the 'k' (set key) command packet until it is used | 
|  | * when handling a 'p' (absorb) command. | 
|  | */ | 
|  | static dif_kmac_key_t kmac_key; | 
|  |  | 
|  | /** | 
|  | * KMAC fixed key. | 
|  | * | 
|  | * Used for caching the fixed key in the 't' (set fixed key) command packet | 
|  | * until it is used when handling a 'b' (batch capture) command. | 
|  | */ | 
|  | uint8_t key_fixed[kKeyLength]; | 
|  |  | 
|  | /** | 
|  | * Fixed-key indicator. | 
|  | * | 
|  | * Used in the 'b' (batch capture) command for indicating whether to use fixed | 
|  | * or random key. | 
|  | */ | 
|  | static bool run_fixed = false; | 
|  |  | 
|  | /** | 
|  | * An array of keys to be used in a batch | 
|  | */ | 
|  | uint8_t batch_keys[kNumBatchOpsMax][kKeyLength]; | 
|  |  | 
|  | /** | 
|  | * An array of messages to be used in a batch | 
|  | */ | 
|  | uint8_t batch_messages[kNumBatchOpsMax][kMessageLength]; | 
|  |  | 
|  | /** | 
|  | * Blocks until KMAC is idle. | 
|  | */ | 
|  | static void kmac_block_until_idle(void) { | 
|  | // TODO(#7842): Remove when `dif_kmac_get_status()` is implemented. | 
|  | uint32_t reg; | 
|  | do { | 
|  | reg = mmio_region_read32(kmac.base_addr, KMAC_STATUS_REG_OFFSET); | 
|  | } while (!bitfield_bit32_read(reg, KMAC_STATUS_SHA3_IDLE_BIT)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Resets KMAC to idle state. | 
|  | */ | 
|  | static void kmac_reset(void) { | 
|  | // TODO(#7842): Remove when `dif_kmac_reset()` is implemented. | 
|  | mmio_region_write32( | 
|  | kmac.base_addr, KMAC_CMD_REG_OFFSET, | 
|  | bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_DONE)); | 
|  | kmac_block_until_idle(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Report whether the hardware is currently idle. | 
|  | * | 
|  | * If the hardware is not idle then the `CFG` register is locked. | 
|  | * | 
|  | * @param params Hardware parameters. | 
|  | * @returns Whether the hardware is currently idle or not. | 
|  | */ | 
|  | static bool is_state_idle(void) { | 
|  | uint32_t reg = mmio_region_read32(kmac.base_addr, KMAC_STATUS_REG_OFFSET); | 
|  | return bitfield_bit32_read(reg, KMAC_STATUS_SHA3_IDLE_BIT); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Starts KMAC message without sending START command. | 
|  | * | 
|  | * Based on dif_kmac_mode_kmac_start(). | 
|  | * | 
|  | * Unlike dif_kmac_mode_kmac_start(), this function doesn't provide the START | 
|  | * command to the hardware, i.e., just the key is provided and the initial setup | 
|  | * for starting a new message is performed. | 
|  | */ | 
|  | static dif_result_t kmac_msg_start(dif_kmac_mode_kmac_t mode, size_t l, | 
|  | const dif_kmac_key_t *k, | 
|  | const dif_kmac_customization_string_t *s) { | 
|  | if (k == NULL || l > kDifKmacMaxOutputLenWords) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // Set key strength and calculate rate (r). | 
|  | uint32_t kstrength; | 
|  | switch (mode) { | 
|  | case kDifKmacModeCshakeLen128: | 
|  | kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L128; | 
|  | kmac_operation_state.r = calculate_rate_bits(128) / 32; | 
|  | break; | 
|  | case kDifKmacModeCshakeLen256: | 
|  | kstrength = KMAC_CFG_SHADOWED_KSTRENGTH_VALUE_L256; | 
|  | kmac_operation_state.r = calculate_rate_bits(256) / 32; | 
|  | break; | 
|  | default: | 
|  | return kDifBadArg; | 
|  | } | 
|  | kmac_operation_state.offset = 0; | 
|  | kmac_operation_state.d = l; | 
|  | kmac_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()) { | 
|  | return kDifError; | 
|  | } | 
|  |  | 
|  | // Set key length and shares. | 
|  | // Uniform sharing is achieved by XORing a random number into both shares. | 
|  | mmio_region_write32(kmac.base_addr, KMAC_KEY_LEN_REG_OFFSET, key_len); | 
|  | for (int i = 0; i < ARRAYSIZE(k->share0); ++i) { | 
|  | const uint32_t a = next_lfsr(); | 
|  | mmio_region_write32(kmac.base_addr, | 
|  | KMAC_KEY_SHARE0_0_REG_OFFSET + i * sizeof(uint32_t), | 
|  | k->share0[i] ^ a); | 
|  | mmio_region_write32(kmac.base_addr, | 
|  | KMAC_KEY_SHARE1_0_REG_OFFSET + i * sizeof(uint32_t), | 
|  | k->share1[i] ^ a); | 
|  | } | 
|  |  | 
|  | // 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]); | 
|  |  | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Writes the message including its length to the message FIFO. | 
|  | * | 
|  | * Based on dif_kmac_absorb(). | 
|  | * | 
|  | * Unlike dif_kmac_absorb(), this function 1) doesn't require the hardware | 
|  | * to enter the 'absorb' state before writing the message into the message | 
|  | * FIFO, and 2) appends the output length afterwards (normally done as | 
|  | * part of dif_kmac_squeeze()). | 
|  | */ | 
|  | static dif_result_t kmac_msg_write(const void *msg, size_t msg_len, | 
|  | size_t *processed) { | 
|  | // Set the number of bytes processed to 0. | 
|  | if (processed != NULL) { | 
|  | *processed = 0; | 
|  | } | 
|  |  | 
|  | if (msg == NULL && msg_len != 0) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | // Check that an operation has been started. | 
|  | if (kmac_operation_state.r == 0) { | 
|  | return kDifError; | 
|  | } | 
|  |  | 
|  | // Copy the message one byte at a time. | 
|  | // This could be sped up copying a word at a time but be careful | 
|  | // about message endianness (e.g. only copy a word at a time when in | 
|  | // little-endian mode). | 
|  | for (size_t i = 0; i < msg_len; ++i) { | 
|  | mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, | 
|  | ((const uint8_t *)msg)[i]); | 
|  | } | 
|  |  | 
|  | if (processed != NULL) { | 
|  | *processed = msg_len; | 
|  | } | 
|  |  | 
|  | // 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 = kmac_operation_state.d * 32; | 
|  | int out_len = 1 + (d > 0xFF) + (d > 0xFFFF) + (d > 0xFFFFFF); | 
|  | int shift = (out_len - 1) * 8; | 
|  | while (shift >= 8) { | 
|  | mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, | 
|  | (uint8_t)(d >> shift)); | 
|  | shift -= 8; | 
|  | } | 
|  | mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, (uint8_t)d); | 
|  | mmio_region_write8(kmac.base_addr, KMAC_MSG_FIFO_REG_OFFSET, | 
|  | (uint8_t)out_len); | 
|  | kmac_operation_state.squeezing = true; | 
|  |  | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Starts actual processing of a previously provided message. | 
|  | * | 
|  | * This function issues a START command directly followed by a PROCESS command. | 
|  | */ | 
|  | static void kmac_msg_proc(void) { | 
|  | // 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); | 
|  |  | 
|  | // Issue PROCESS command. | 
|  | cmd_reg = | 
|  | bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_PROCESS); | 
|  | mmio_region_write32(kmac.base_addr, KMAC_CMD_REG_OFFSET, cmd_reg); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Waits until the hardware enters the 'squeeze' state. | 
|  | * | 
|  | * If the hardware enters the `squeeze` state, this means the output state is | 
|  | * valid and can be read by software. | 
|  | */ | 
|  | static void kmac_msg_done(void) { | 
|  | // TODO(#7841, #7842): Remove when we finalize the way we capture traces. | 
|  | uint32_t reg; | 
|  | do { | 
|  | reg = mmio_region_read32(kmac.base_addr, KMAC_STATUS_REG_OFFSET); | 
|  | } while (!bitfield_bit32_read(reg, KMAC_STATUS_SHA3_SQUEEZE_BIT)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Reads the digest from the hardware. | 
|  | * | 
|  | * Based on dif_kmac_squeeze(). | 
|  | * | 
|  | * Unlike dif_kmac_squeeze(), this function 1) doesn't wait until the hardware | 
|  | * enters the 'squeeze' state, 2) doesn't append the output length, 3) doesn't | 
|  | * support the generation of more state. | 
|  | */ | 
|  | static dif_result_t kmac_get_digest(uint32_t *out, size_t len) { | 
|  | if (out == NULL && len != 0) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | while (len > 0) { | 
|  | size_t n = len; | 
|  | size_t remaining = kmac_operation_state.r - kmac_operation_state.offset; | 
|  | if (kmac_operation_state.d != 0 && | 
|  | kmac_operation_state.d < kmac_operation_state.r) { | 
|  | remaining = kmac_operation_state.d - kmac_operation_state.offset; | 
|  | } | 
|  | if (n > remaining) { | 
|  | n = remaining; | 
|  | } | 
|  | if (n == 0) { | 
|  | // Normally, the hardware would now have to generate more state. But | 
|  | // since at this point, the power measurement is already stopped, we don't | 
|  | // support that here. | 
|  | return kDifError; | 
|  | } | 
|  |  | 
|  | uint32_t offset = | 
|  | KMAC_STATE_REG_OFFSET + kmac_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(kmac.base_addr, offset); | 
|  | uint32_t share1 = | 
|  | mmio_region_read32(kmac.base_addr, offset + kDifKmacStateShareOffset); | 
|  | *out++ = share0 ^ share1; | 
|  | offset += sizeof(uint32_t); | 
|  | } | 
|  | kmac_operation_state.offset += n; | 
|  | len -= n; | 
|  | } | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Initializes the KMAC peripheral. | 
|  | * | 
|  | * This function configures KMAC to use software entropy. | 
|  | */ | 
|  | static void kmac_init(void) { | 
|  | SS_CHECK_DIF_OK( | 
|  | dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac)); | 
|  |  | 
|  | dif_kmac_config_t config = (dif_kmac_config_t){ | 
|  | .entropy_mode = kDifKmacEntropyModeSoftware, | 
|  | .entropy_seed = {0xaa25b4bf, 0x48ce8fff, 0x5a78282a, 0x48465647, | 
|  | 0x70410fef}, | 
|  | .entropy_fast_process = false, | 
|  | .msg_mask = true, | 
|  | }; | 
|  | SS_CHECK_DIF_OK(dif_kmac_configure(&kmac, config)); | 
|  |  | 
|  | kmac_block_until_idle(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Simple serial 'k' (set key) command handler. | 
|  | * | 
|  | * This function simply caches the provided key in the static `kmac_key` | 
|  | * variable so that it can be used in subsequent operations. This function does | 
|  | * not use key shares to simplify side-channel analysis. The key must be | 
|  | * `kKeyLength` bytes long. | 
|  | * | 
|  | * @param key Key. Must be `kKeyLength` bytes long. | 
|  | * @param key_len Key length. Must be equal to `kKeyLength`. | 
|  | * @return Result of the operation. | 
|  | */ | 
|  | static void sha3_serial_set_key(const uint8_t *key, size_t key_len) { | 
|  | SS_CHECK(key_len == kKeyLength); | 
|  |  | 
|  | kmac_key = (dif_kmac_key_t){ | 
|  | .length = kDifKmacKeyLen128, | 
|  | }; | 
|  | memcpy(kmac_key.share0, key, kKeyLength); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Absorbs a message using KMAC128 without a customization string. | 
|  | * | 
|  | * @param msg Message. | 
|  | * @param msg_len Message length. | 
|  | */ | 
|  | static void sha3_serial_absorb(const uint8_t *msg, size_t msg_len) { | 
|  | // Start a new message and write data to message FIFO. | 
|  | SS_CHECK_DIF_OK( | 
|  | kmac_msg_start(kDifKmacModeKmacLen128, kDigestLength, &kmac_key, NULL)); | 
|  | SS_CHECK_DIF_OK(kmac_msg_write(msg, msg_len, NULL)); | 
|  |  | 
|  | // Start the SHA3 processing (this triggers the capture) and go to sleep. | 
|  | // Using the SecCmdDelay hardware parameter, the KMAC unit is | 
|  | // configured to start operation 40 cycles after receiving the START and PROC | 
|  | // commands. This allows Ibex to go to sleep in order to not disturb the | 
|  | // capture. | 
|  | sca_call_and_sleep(kmac_msg_proc, kIbexSha3SleepCycles); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Simple serial 'p' (absorb) command handler. | 
|  | * | 
|  | * Absorbs the given message using KMAC128 without a customization string, | 
|  | * and sends the digest over UART. This function also handles the trigger | 
|  | * signal. | 
|  | * | 
|  | * @param msg Message. | 
|  | * @param msg_len Message length. | 
|  | */ | 
|  | static void sha3_serial_single_absorb(const uint8_t *msg, size_t msg_len) { | 
|  | SS_CHECK(msg_len == kMessageLength); | 
|  |  | 
|  | // Ungate the capture trigger signal and then start the operation. | 
|  | sca_set_trigger_high(); | 
|  | sha3_serial_absorb(msg, msg_len); | 
|  | sca_set_trigger_low(); | 
|  |  | 
|  | // Check KMAC has finsihed processing the message. | 
|  | kmac_msg_done(); | 
|  |  | 
|  | // Read the digest and send it to the host for verification. | 
|  | uint32_t out[kDigestLength]; | 
|  | SS_CHECK_DIF_OK(kmac_get_digest(out, kDigestLength)); | 
|  | simple_serial_send_packet('r', (uint8_t *)out, kDigestLength * 4); | 
|  |  | 
|  | // Reset before the next absorb since KMAC must be idle before starting | 
|  | // another absorb. | 
|  | kmac_reset(); | 
|  | } | 
|  |  | 
|  | static void sha3_serial_fixed_key_set(const uint8_t *key, size_t key_len) { | 
|  | SS_CHECK(key_len == kKeyLength); | 
|  | memcpy(key_fixed, key, key_len); | 
|  | } | 
|  |  | 
|  | static void sha3_serial_batch(const uint8_t *data, size_t data_len) { | 
|  | uint32_t num_encryptions = 0; | 
|  | uint32_t out[kDigestLength]; | 
|  | uint32_t batch_digest[kDigestLength]; | 
|  | SS_CHECK(data_len == sizeof(num_encryptions)); | 
|  | num_encryptions = read_32(data); | 
|  |  | 
|  | for (uint32_t j = 0; j < kDigestLength; ++j) { | 
|  | batch_digest[j] = 0; | 
|  | } | 
|  |  | 
|  | for (uint32_t i = 0; i < num_encryptions; ++i) { | 
|  | if (run_fixed) { | 
|  | memcpy(batch_keys[i], key_fixed, kKeyLength); | 
|  | } else { | 
|  | prng_rand_bytes(batch_keys[i], kKeyLength); | 
|  | } | 
|  | prng_rand_bytes(batch_messages[i], kMessageLength); | 
|  | run_fixed = batch_messages[i][0] & 0x1; | 
|  | } | 
|  |  | 
|  | for (uint32_t i = 0; i < num_encryptions; ++i) { | 
|  | kmac_reset(); | 
|  | memcpy(kmac_key.share0, batch_keys[i], kKeyLength); | 
|  |  | 
|  | sca_set_trigger_high(); | 
|  | sha3_serial_absorb(batch_messages[i], kMessageLength); | 
|  | sca_set_trigger_low(); | 
|  |  | 
|  | kmac_msg_done(); | 
|  | SS_CHECK_DIF_OK(kmac_get_digest(out, kDigestLength)); | 
|  |  | 
|  | // The correctness of each batch is verified by computing and sending | 
|  | // the batch digest. This digest is computed by XORing all outputs of | 
|  | // the batch. | 
|  | for (uint32_t j = 0; j < kDigestLength; ++j) { | 
|  | batch_digest[j] ^= out[j]; | 
|  | } | 
|  | } | 
|  | // Send the batch digest to the host for verification. | 
|  | simple_serial_send_packet('r', (uint8_t *)batch_digest, kDigestLength * 4); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Simple serial 'l' (seed lfsr) command handler. | 
|  | * | 
|  | * This function only supports 4-byte seeds. | 
|  | * | 
|  | * @param seed A buffer holding the seed. | 
|  | */ | 
|  | static void sha3_serial_seed_lfsr(const uint8_t *seed, size_t seed_len) { | 
|  | SS_CHECK(seed_len == sizeof(uint32_t)); | 
|  | seed_lfsr(read_32(seed)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Main function. | 
|  | * | 
|  | * Initializes peripherals and processes simple serial packets received over | 
|  | * UART. | 
|  | */ | 
|  | bool test_main(void) { | 
|  | sca_init(kScaTriggerSourceKmac, kScaPeripheralIoDiv4 | kScaPeripheralKmac); | 
|  |  | 
|  | LOG_INFO("Running kmac_serial"); | 
|  |  | 
|  | LOG_INFO("Initializing simple serial interface to capture board."); | 
|  | simple_serial_init(sca_get_uart()); | 
|  | simple_serial_register_handler('k', sha3_serial_set_key); | 
|  | simple_serial_register_handler('p', sha3_serial_single_absorb); | 
|  | simple_serial_register_handler('b', sha3_serial_batch); | 
|  | simple_serial_register_handler('t', sha3_serial_fixed_key_set); | 
|  | simple_serial_register_handler('l', sha3_serial_seed_lfsr); | 
|  |  | 
|  | LOG_INFO("Initializing the KMAC peripheral."); | 
|  | kmac_init(); | 
|  |  | 
|  | LOG_INFO("Starting simple serial packet handling."); | 
|  | while (true) { | 
|  | simple_serial_process_packet(); | 
|  | } | 
|  | } |