[sca] Add initial implementation of SHA3 SCA program
Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/sca/meson.build b/sw/device/sca/meson.build
index 07cd182..84ac50b 100644
--- a/sw/device/sca/meson.build
+++ b/sw/device/sca/meson.build
@@ -23,6 +23,21 @@
},
}
+sha3_serial = declare_dependency(
+ sources: [
+ 'sha3_serial.c',
+ hw_ip_kmac_reg_h,
+ ],
+ dependencies: [
+ sw_lib_dif_kmac,
+ ],
+)
+sca_programs += {
+ 'sha3_serial': {
+ 'dependency': sha3_serial,
+ },
+}
+
foreach sca_program_name, sca_program_info: sca_programs
foreach device_name, device_lib : sw_lib_arch_core_devices
sca_program_elf = executable(
diff --git a/sw/device/sca/sha3_serial.c b/sw/device/sca/sha3_serial.c
new file mode 100644
index 0000000..a2966ca
--- /dev/null
+++ b/sw/device/sca/sha3_serial.c
@@ -0,0 +1,187 @@
+// 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/memory.h"
+#include "sw/device/lib/dif/dif_kmac.h"
+#include "sw/device/lib/runtime/log.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')*,
+ * - 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.
+ */
+
+enum {
+ /**
+ * Key length in bytes.
+ */
+ kKeyLength = 16,
+ /**
+ * Message length in bytes.
+ */
+ kMessageLength = 16,
+};
+
+/**
+ * A handle to KMAC.
+ */
+static dif_kmac_t kmac;
+
+/**
+ * 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;
+
+/**
+ * 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.params.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.params.base_addr, KMAC_CMD_REG_OFFSET,
+ bitfield_field32_write(0, KMAC_CMD_CMD_FIELD, KMAC_CMD_CMD_VALUE_DONE));
+ kmac_block_until_idle();
+}
+
+/**
+ * Issues a process command to KMAC and waits until absorb step is complete.
+ */
+static void kmac_absorb_end(void) {
+ // TODO(#7841, #7842): Remove when we finalize the way we capture traces.
+ uint32_t reg = 0;
+ reg = bitfield_field32_write(reg, KMAC_CMD_CMD_FIELD,
+ KMAC_CMD_CMD_VALUE_PROCESS);
+ mmio_region_write32(kmac.params.base_addr, KMAC_CMD_REG_OFFSET, reg);
+
+ do {
+ reg = mmio_region_read32(kmac.params.base_addr, KMAC_STATUS_REG_OFFSET);
+ } while (!bitfield_bit32_read(reg, KMAC_STATUS_SHA3_SQUEEZE_BIT));
+}
+
+/**
+ * Initializes the KMAC peripheral.
+ *
+ * This function configures KMAC to use software entropy.
+ */
+static void kmac_init(void) {
+ dif_kmac_params_t params = {
+ .base_addr = mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR),
+ };
+ CHECK(dif_kmac_init(params, &kmac) == kDifKmacOk);
+
+ dif_kmac_config_t config = (dif_kmac_config_t){
+ .entropy_mode = kDifKmacEntropyModeSoftware,
+ .entropy_seed = 0xffff,
+ .entropy_fast_process = true,
+ };
+ CHECK(dif_kmac_configure(&kmac, config) == kDifKmacOk);
+
+ 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);
+}
+
+/**
+ * Simple serial 'p' (absorb) command handler.
+ *
+ * Absorbs the given message using KMAC128 without a customization string.
+ *
+ * @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);
+
+ SS_CHECK(dif_kmac_mode_kmac_start(&kmac, kDifKmacModeKmacLen128, 0, &kmac_key,
+ NULL) == kDifKmacOk);
+
+ // TODO(#7841): Consider delaying the absorb step until triggered manually to
+ // be able to use `sca_call_and_sleep()`.
+ sca_set_trigger_high();
+ SS_CHECK(dif_kmac_absorb(&kmac, msg, msg_len, NULL) == kDifKmacOk);
+ // Note: Performing the squeeze step after this call using HW directly would
+ // produce incorrect results. See `dif_kmac_mode_kmac_start()` and
+ // `dif_kmac_squeeze()`.
+ kmac_absorb_end();
+ sca_set_trigger_low();
+
+ // Reset before the next absorb since KMAC must be idle before starting
+ // another absorb.
+ kmac_reset();
+}
+
+/**
+ * Main function.
+ *
+ * Initializes peripherals and processes simple serial packets received over
+ * UART.
+ */
+int main(void) {
+ const dif_uart_t *uart1;
+
+ sca_init(kScaTriggerSourceKmac, kScaPeripheralKmac);
+
+ LOG_INFO("Running sha3_serial");
+
+ LOG_INFO("Initializing simple serial interface to capture board.");
+ sca_get_uart(&uart1);
+ simple_serial_init(uart1);
+ simple_serial_register_handler('k', sha3_serial_set_key);
+ simple_serial_register_handler('p', sha3_serial_single_absorb);
+
+ LOG_INFO("Initializing the KMAC peripheral.");
+ kmac_init();
+
+ LOG_INFO("Starting simple serial packet handling.");
+ while (true) {
+ simple_serial_process_packet();
+ }
+}