[sw, dif_kmac] Add implementation of cSHAKE (blocking only)
In addition to implementing cSHAKE his change modifies two parts of
the API:
1. A length field is added to the function name and customization
string structs. This avoids having to decode the buffered value
to get its length in order to copy it.
2. The order of the parameters of the cSHAKE start function is
modified slightly to make it more consistent with the
specification.
Signed-off-by: Michael Munday <mike.munday@lowrisc.org>
diff --git a/hw/top_earlgrey/dv/chip_dif_tests.hjson b/hw/top_earlgrey/dv/chip_dif_tests.hjson
index b967298..8f827ec 100644
--- a/hw/top_earlgrey/dv/chip_dif_tests.hjson
+++ b/hw/top_earlgrey/dv/chip_dif_tests.hjson
@@ -56,6 +56,12 @@
en_run_modes: ["sw_test_mode"]
}
{
+ name: chip_dif_kmac_cshake_smoketest
+ uvm_test_seq: chip_sw_base_vseq
+ sw_images: ["sw/device/tests/dif_kmac_cshake_smoketest:1"]
+ en_run_modes: ["sw_test_mode"]
+ }
+ {
name: chip_dif_otbn_smoketest
uvm_test_seq: chip_sw_base_vseq
sw_images: ["sw/device/tests/dif_otbn_smoketest:1"]
diff --git a/hw/top_earlgrey/dv/verilator_sim_cfg.hjson b/hw/top_earlgrey/dv/verilator_sim_cfg.hjson
index dba761b..787300c 100644
--- a/hw/top_earlgrey/dv/verilator_sim_cfg.hjson
+++ b/hw/top_earlgrey/dv/verilator_sim_cfg.hjson
@@ -141,6 +141,10 @@
sw_images: ["sw/device/tests/dif_kmac_smoketest:1"]
}
{
+ name: dif_kmac_cshake_smoketest
+ sw_images: ["sw/device/tests/dif_kmac_cshake_smoketest:1"]
+ }
+ {
name: flash_ctrl_test
sw_images: ["sw/device/tests/flash_ctrl_test:1"]
}
diff --git a/sw/device/lib/dif/dif_kmac.c b/sw/device/lib/dif/dif_kmac.c
index 534a122..4638a51 100644
--- a/sw/device/lib/dif/dif_kmac.c
+++ b/sw/device/lib/dif/dif_kmac.c
@@ -56,9 +56,11 @@
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);
@@ -85,6 +87,9 @@
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);
@@ -343,6 +348,109 @@
return kDifKmacOk;
}
+dif_kmac_result_t dif_kmac_mode_cshake_start(
+ dif_kmac_t *kmac, dif_kmac_mode_cshake_t mode,
+ const dif_kmac_function_name_t *n,
+ const dif_kmac_customization_string_t *s) {
+ if (kmac == NULL) {
+ return kDifKmacBadArg;
+ }
+
+ // 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, kDifKmacModeShakeLen128);
+ case kDifKmacModeCshakeLen256:
+ return dif_kmac_mode_shake_start(kmac, kDifKmacModeShakeLen256);
+ default:
+ return kDifKmacBadArg;
+ }
+ }
+
+ // Set key strength and calculate rate (r).
+ uint32_t kstrength;
+ switch (mode) {
+ case kDifKmacModeCshakeLen128:
+ kstrength = KMAC_CFG_KSTRENGTH_VALUE_L128;
+ kmac->r = calculate_rate_bits(128) / 32;
+ break;
+ case kDifKmacModeCshakeLen256:
+ kstrength = KMAC_CFG_KSTRENGTH_VALUE_L256;
+ kmac->r = calculate_rate_bits(256) / 32;
+ break;
+ default:
+ return kDifKmacBadArg;
+ }
+ kmac->d = 0; // Zero indicates variable digest length.
+ kmac->offset = 0;
+
+ // Hardware must be idle to start an operation.
+ if (!is_state_idle(kmac->params)) {
+ return kDifKmacError;
+ }
+
+ // Configure cSHAKE mode with the given strength.
+ uint32_t cfg_reg =
+ mmio_region_read32(kmac->params.base_addr, KMAC_CFG_REG_OFFSET);
+ cfg_reg =
+ bitfield_field32_write(cfg_reg, KMAC_CFG_KSTRENGTH_FIELD, kstrength);
+ cfg_reg = bitfield_field32_write(cfg_reg, KMAC_CFG_MODE_FIELD,
+ KMAC_CFG_MODE_VALUE_CSHAKE);
+ mmio_region_write32(kmac->params.base_addr, KMAC_CFG_REG_OFFSET, cfg_reg);
+
+ // Calculate PREFIX register values.
+ uint32_t prefix_regs[11] = {0};
+ uint8_t *prefix_data = (void *)prefix_regs;
+ if (n == NULL) {
+ // 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) {
+ // 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->params.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->params.base_addr, KMAC_CMD_REG_OFFSET, cmd_reg);
+
+ // Poll until the status register is in the 'absorb' state.
+ while (true) {
+ if (is_state_absorb(kmac->params)) {
+ break;
+ }
+ // TODO(#6248): check for error.
+ }
+
+ return kDifKmacOk;
+}
+
dif_kmac_result_t dif_kmac_absorb(dif_kmac_t *kmac, const void *msg, size_t len,
size_t *processed) {
// Set the number of bytes processed to 0.
diff --git a/sw/device/lib/dif/dif_kmac.h b/sw/device/lib/dif/dif_kmac.h
index 764b13b..2d82a67 100644
--- a/sw/device/lib/dif/dif_kmac.h
+++ b/sw/device/lib/dif/dif_kmac.h
@@ -373,6 +373,8 @@
/** Encoded S: left_encode(len(S)) || S */
char buffer[kDifKmacMaxCustomizationStringLen +
kDifKmacMaxCustomizationStringOverhead];
+ /** Length of data in buffer in bytes. */
+ uint32_t length;
} dif_kmac_customization_string_t;
/**
@@ -383,6 +385,8 @@
typedef struct dif_kmac_function_name {
/** Encoded N: left_encode(len(N)) || N */
char buffer[kDifKmacMaxFunctionNameLen + kDifKmacMaxFunctionNameOverhead];
+ /** Length of data in buffer in bytes. */
+ uint32_t length;
} dif_kmac_function_name_t;
/**
@@ -550,15 +554,15 @@
*
* @param kmac A KMAC handle.
* @param mode The mode of operation.
- * @param s Customization string (optional).
* @param n Function name (optional).
+ * @param s Customization string (optional).
* @return The result of the operation.
*/
DIF_WARN_UNUSED_RESULT
dif_kmac_result_t dif_kmac_mode_cshake_start(
dif_kmac_t *kmac, dif_kmac_mode_cshake_t mode,
- const dif_kmac_customization_string_t *s,
- const dif_kmac_function_name_t *n);
+ const dif_kmac_function_name_t *n,
+ const dif_kmac_customization_string_t *s);
/**
* Start a KMAC operation.
diff --git a/sw/device/tests/dif/dif_kmac_cshake_smoketest.c b/sw/device/tests/dif/dif_kmac_cshake_smoketest.c
new file mode 100644
index 0000000..2bc9100
--- /dev/null
+++ b/sw/device/tests/dif/dif_kmac_cshake_smoketest.c
@@ -0,0 +1,142 @@
+// 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/arch/device.h"
+#include "sw/device/lib/base/memory.h"
+#include "sw/device/lib/base/mmio.h"
+#include "sw/device/lib/dif/dif_kmac.h"
+#include "sw/device/lib/flash_ctrl.h"
+#include "sw/device/lib/runtime/log.h"
+#include "sw/device/lib/testing/check.h"
+#include "sw/device/lib/testing/test_main.h"
+
+#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
+
+const test_config_t kTestConfig;
+
+#define DIGEST_LEN_CSHAKE_MAX 4
+
+/**
+ * cSHAKE test description.
+ */
+typedef struct cshake_test {
+ dif_kmac_mode_cshake_t mode;
+
+ const char *message;
+ size_t message_len;
+
+ const char *function_name;
+ size_t function_name_len;
+
+ const char *customization_string;
+ size_t customization_string_len;
+
+ const uint32_t digest[DIGEST_LEN_CSHAKE_MAX];
+ size_t digest_len;
+} cshake_test_t;
+
+/**
+ * cSHAKE tests.
+ */
+const cshake_test_t cshake_tests[] = {
+ {
+ .mode = kDifKmacModeCshakeLen128,
+ .message = "OpenTitan",
+ .message_len = 9,
+ .function_name = "",
+ .function_name_len = 0,
+ .customization_string = "",
+ .customization_string_len = 0,
+ .digest = {0x235a6522, 0x3bd735ac, 0x77832247, 0xc6b12919},
+ .digest_len = 4, // Rate (r) is 42 words.
+ },
+ {
+ .mode = kDifKmacModeCshakeLen128,
+ .message = "OpenTitan",
+ .message_len = 9,
+ .function_name = "A",
+ .function_name_len = 1,
+ .customization_string = "",
+ .customization_string_len = 0,
+ .digest = {0xf2f20928, 0xa2a59a0, 0xfc1e5d5d, 0x1cee38d0},
+ .digest_len = 4, // Rate (r) is 42 words.
+ },
+ {
+ .mode = kDifKmacModeCshakeLen256,
+ .message = "OpenTitan",
+ .message_len = 9,
+ .function_name = "",
+ .function_name_len = 0,
+ .customization_string = "Ibex",
+ .customization_string_len = 4,
+ .digest = {0xcd582d56, 0x59e88860, 0xa4344c29, 0x5576778f},
+ .digest_len = 4, // Rate (r) is 34 words.
+ },
+ {
+ .mode = kDifKmacModeCshakeLen256,
+ .message = "OpenTitan",
+ .message_len = 9,
+ .function_name = "Ibex",
+ .function_name_len = 4,
+ .customization_string =
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+ .customization_string_len = 32,
+ .digest = {0xda353307, 0xdf18e570, 0x6211cee0, 0x716e816c},
+ .digest_len = 4, // Rate (r) is 34 words.
+ },
+};
+
+bool test_main() {
+ LOG_INFO("Running KMAC DIF cSHAKE test...");
+
+ // Intialize KMAC hardware.
+ dif_kmac_t kmac;
+ CHECK(dif_kmac_init((dif_kmac_params_t){.base_addr = mmio_region_from_addr(
+ TOP_EARLGREY_KMAC_BASE_ADDR)},
+ &kmac) == kDifKmacOk);
+
+ // Configure KMAC hardware using software entropy.
+ dif_kmac_config_t config = (dif_kmac_config_t){
+ .entropy_mode = kDifKmacEntropyModeSoftware,
+ .entropy_seed = 0xffff,
+ .entropy_fast_process = kDifKmacToggleEnabled,
+ };
+ CHECK(dif_kmac_configure(&kmac, config) == kDifKmacOk);
+
+ // Run cSHAKE test cases using single blocking absorb/squeeze operations.
+ for (int i = 0; i < ARRAYSIZE(cshake_tests); ++i) {
+ cshake_test_t test = cshake_tests[i];
+
+ dif_kmac_function_name_t n;
+ CHECK(dif_kmac_function_name_init(
+ test.function_name, test.function_name_len, &n) == kDifKmacOk);
+
+ dif_kmac_customization_string_t s;
+ CHECK(dif_kmac_customization_string_init(test.customization_string,
+ test.customization_string_len,
+ &s) == kDifKmacOk);
+
+ // Use NULL for empty strings to exercise that code path.
+ dif_kmac_function_name_t *np = test.function_name_len == 0 ? NULL : &n;
+ dif_kmac_customization_string_t *sp =
+ test.customization_string_len == 0 ? NULL : &s;
+
+ CHECK(dif_kmac_mode_cshake_start(&kmac, test.mode, np, sp) == kDifKmacOk);
+ CHECK(dif_kmac_absorb(&kmac, test.message, test.message_len, NULL) ==
+ kDifKmacOk);
+ uint32_t out[DIGEST_LEN_CSHAKE_MAX];
+ CHECK(DIGEST_LEN_CSHAKE_MAX >= test.digest_len);
+ CHECK(dif_kmac_squeeze(&kmac, out, test.digest_len, NULL) == kDifKmacOk);
+ CHECK(dif_kmac_end(&kmac) == kDifKmacOk);
+
+ for (int j = 0; j < test.digest_len; ++j) {
+ CHECK(out[j] == test.digest[j],
+ "test %d: mismatch at %d got=0x%x want=0x%x", i, j, out[j],
+ test.digest[j]);
+ }
+ }
+
+ return true;
+}
diff --git a/sw/device/tests/dif/meson.build b/sw/device/tests/dif/meson.build
index 289ca22..b1ab9a6 100644
--- a/sw/device/tests/dif/meson.build
+++ b/sw/device/tests/dif/meson.build
@@ -100,6 +100,24 @@
}
}
+dif_kmac_cshake_smoketest_lib = declare_dependency(
+ link_with: static_library(
+ 'dif_kmac_cshake_smoketest_lib',
+ sources: ['dif_kmac_cshake_smoketest.c'],
+ dependencies: [
+ sw_lib_dif_kmac,
+ sw_lib_runtime_log,
+ sw_lib_mmio,
+ sw_lib_runtime_hart,
+ ],
+ ),
+)
+sw_tests += {
+ 'dif_kmac_cshake_smoketest': {
+ 'library': dif_kmac_cshake_smoketest_lib,
+ }
+}
+
dif_rstmgr_smoketest_lib = declare_dependency(
link_with: static_library(
'dif_rstmgr_smoketest_lib',
diff --git a/test/systemtest/config.py b/test/systemtest/config.py
index 3ffb813..7ba4c72 100644
--- a/test/systemtest/config.py
+++ b/test/systemtest/config.py
@@ -78,6 +78,9 @@
"name": "dif_kmac_smoketest",
},
{
+ "name": "dif_kmac_cshake_smoketest",
+ },
+ {
"name": "flash_ctrl_test",
},
{