[sw/dif] CSRNG Updates
Add implementations for the following functions
* `dif_csrng_instantiate()`
* `dif_csrng_reseed()`
* `dif_csrng_update()`
* `dif_csrng_generate()`
* `dif_csrng_read_output()`
Update `dif_csrng_smoketest.cc` to run the CSRNG in software mode
with a determistic seed.
Signed-off-by: Miguel Osorio <miguelosorio@google.com>
diff --git a/sw/device/lib/dif/dif_csrng.c b/sw/device/lib/dif/dif_csrng.c
index 5743986..00067e8 100644
--- a/sw/device/lib/dif/dif_csrng.c
+++ b/sw/device/lib/dif/dif_csrng.c
@@ -9,6 +9,83 @@
#include "csrng_regs.h" // Generated
+/**
+ * Supported CSRNG application commands.
+ * See https://docs.opentitan.org/hw/ip/csrng/doc/#command-header for
+ * details.
+ */
+typedef enum csrng_app_cmd_id {
+ kCsrngAppCmdInstantiate = 1,
+ kCsrngAppCmdReseed = 2,
+ kCsrngAppCmdGenerate = 3,
+ kCsrngAppCmdUpdate = 4,
+ kCsrngAppCmdUnisntantiate = 5,
+} csrng_app_cmd_id_t;
+
+/**
+ * CSRNG application interface command header parameters.
+ */
+typedef struct csrng_app_cmd {
+ /**
+ * Application command.
+ */
+ csrng_app_cmd_id_t id;
+ /**
+ * Entropy source enable.
+ *
+ * Mapped to flag0 in the hardware command interface.
+ */
+ dif_csrng_entropy_src_toggle_t entropy_src_enable;
+ /**
+ * Seed material. Only used in `kCsrngAppCmdInstantiate`, `kCsrngAppCmdReseed`
+ * and `kCsrngAppCmdUpdate` commands.
+ */
+ const dif_csrng_seed_material_t *seed_material;
+ /**
+ * Generate length. Specified as number of 128bit blocks.
+ */
+ uint32_t generate_len;
+} csrng_app_cmd_t;
+
+/**
+ * Writes application command `cmd` to the CSRNG_CMD_REQ_REG register.
+ * Returns the result of the operation.
+ */
+static dif_csrng_result_t write_application_command(
+ const dif_csrng_t *csrng, const csrng_app_cmd_t *cmd) {
+ if (csrng == NULL || cmd == NULL) {
+ return kDifCsrngBadArg;
+ }
+
+ // The application command header is not specified as a register in the
+ // hardware specification, so the fields are mapped here by hand. The
+ // command register also accepts arbitrary 32bit data.
+ const uint32_t kAppCmdBitFlag0 = 8;
+ const bitfield_field32_t kAppCmdFieldCmdId = {.mask = 0xf, .index = 0};
+ const bitfield_field32_t kAppCmdFieldCmdLen = {.mask = 0xf, .index = 4};
+ const bitfield_field32_t kAppCmdFieldGlen = {.mask = 0x7ffff, .index = 12};
+
+ uint32_t cmd_len =
+ cmd->seed_material == NULL ? 0 : cmd->seed_material->seed_material_len;
+
+ if (cmd_len & ~kAppCmdFieldCmdLen.mask) {
+ return kDifCsrngBadArg;
+ }
+
+ // Build and write application command header.
+ uint32_t reg = bitfield_field32_write(0, kAppCmdFieldCmdId, cmd->id);
+ reg = bitfield_field32_write(reg, kAppCmdFieldCmdLen, cmd_len);
+ reg = bitfield_bit32_write(reg, kAppCmdBitFlag0, cmd->entropy_src_enable);
+ reg = bitfield_field32_write(reg, kAppCmdFieldGlen, cmd->generate_len);
+ mmio_region_write32(csrng->params.base_addr, CSRNG_CMD_REQ_REG_OFFSET, reg);
+
+ for (uint32_t i = 0; i < cmd_len; ++i) {
+ mmio_region_write32(csrng->params.base_addr, CSRNG_CMD_REQ_REG_OFFSET,
+ cmd->seed_material->seed_material[i]);
+ }
+ return kDifCsrngOk;
+}
+
dif_csrng_result_t dif_csrng_init(dif_csrng_params_t params,
dif_csrng_t *csrng) {
if (csrng == NULL) {
@@ -36,6 +113,68 @@
return kDifCsrngOk;
}
+dif_csrng_result_t dif_csrng_instantiate(
+ const dif_csrng_t *csrng, dif_csrng_entropy_src_toggle_t entropy_src_enable,
+ const dif_csrng_seed_material_t *seed_material) {
+ const csrng_app_cmd_t app_cmd = {
+ .id = kCsrngAppCmdInstantiate,
+ .entropy_src_enable = entropy_src_enable,
+ .seed_material = seed_material,
+ .generate_len = 0,
+ };
+ return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_reseed(
+ const dif_csrng_t *csrng, const dif_csrng_seed_material_t *seed_material) {
+ const csrng_app_cmd_t app_cmd = {
+ .id = kCsrngAppCmdReseed,
+ .entropy_src_enable = false,
+ .seed_material = seed_material,
+ .generate_len = 0,
+ };
+ return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_update(
+ const dif_csrng_t *csrng, const dif_csrng_seed_material_t *seed_material) {
+ const csrng_app_cmd_t app_cmd = {
+ .id = kCsrngAppCmdUpdate,
+ .entropy_src_enable = false,
+ .seed_material = seed_material,
+ .generate_len = 0,
+ };
+ return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_generate(const dif_csrng_t *csrng, size_t len) {
+ if (len == 0) {
+ return kDifCsrngBadArg;
+ }
+
+ // Round up the number of 128bit blocks. Aligning with respect to uint32_t.
+ // TODO(#6112): Consider using a canonical reference for alignment operations.
+ const uint32_t num_128bit_blocks = (len + 3) / 4;
+
+ const csrng_app_cmd_t app_cmd = {
+ .id = kCsrngAppCmdGenerate,
+ .entropy_src_enable = false,
+ .seed_material = NULL,
+ .generate_len = num_128bit_blocks,
+ };
+ return write_application_command(csrng, &app_cmd);
+}
+
+dif_csrng_result_t dif_csrng_uninstantiate(const dif_csrng_t *csrng) {
+ const csrng_app_cmd_t app_cmd = {
+ .id = kCsrngAppCmdUnisntantiate,
+ .entropy_src_enable = false,
+ .seed_material = NULL,
+ .generate_len = 0,
+ };
+ return write_application_command(csrng, &app_cmd);
+}
+
dif_csrng_result_t dif_csrng_get_cmd_interface_status(
const dif_csrng_t *csrng, dif_csrng_cmd_status_t *status) {
if (csrng == NULL || status == NULL) {
@@ -76,3 +215,16 @@
bitfield_bit32_read(reg, CSRNG_GENBITS_VLD_GENBITS_FIPS_BIT);
return kDifCsrngOk;
}
+
+dif_csrng_result_t dif_csrng_read_output(const dif_csrng_t *csrng,
+ uint32_t *buf, size_t len) {
+ if (csrng == NULL || buf == NULL) {
+ return kDifCsrngBadArg;
+ }
+
+ for (uint32_t i = 0; i < len; ++i) {
+ buf[i] =
+ mmio_region_read32(csrng->params.base_addr, CSRNG_GENBITS_REG_OFFSET);
+ }
+ return kDifCsrngOk;
+}
diff --git a/sw/device/lib/dif/dif_csrng.h b/sw/device/lib/dif/dif_csrng.h
index 24128c2..0b9eda2 100644
--- a/sw/device/lib/dif/dif_csrng.h
+++ b/sw/device/lib/dif/dif_csrng.h
@@ -199,11 +199,11 @@
* Note: Software may opt to XOR the seed material with a seed to implement
* a software assisted FIPS mode of operation.
*/
- kDifCsrngEntropySrcToggleDisable,
+ kDifCsrngEntropySrcToggleDisable = 1,
/**
* Entropy source XOR'ed with the provided seed material.
*/
- kDifCsrngEntropySrcToggleEnable,
+ kDifCsrngEntropySrcToggleEnable = 0,
} dif_csrng_entropy_src_toggle_t;
/**
diff --git a/sw/device/tests/dif/dif_csrng_smoketest.c b/sw/device/tests/dif/dif_csrng_smoketest.c
index 1b6c7ae..4922660 100644
--- a/sw/device/tests/dif/dif_csrng_smoketest.c
+++ b/sw/device/tests/dif/dif_csrng_smoketest.c
@@ -13,6 +13,88 @@
const test_config_t kTestConfig;
+/**
+ * FIPS CAVP test vector for CTR DRBG.
+ *
+ * CAVP test parameters.
+ * Cipher: AES-256
+ * Derivation Function: No
+ * Prediction Resistance: False
+ */
+const dif_csrng_seed_material_t kEntropyInput = {
+ .seed_material =
+ {
+ 0xe4bc23c5,
+ 0x089a19d8,
+ 0x6f4119cb,
+ 0x3fa08c0a,
+ 0x4991e0a1,
+ 0xdef17e10,
+ 0x1e4c14d9,
+ 0xc323460a,
+ 0x7c2fb58e,
+ 0x0b086c6c,
+ 0x57b55f56,
+ 0xcae25bad,
+ },
+ .seed_material_len = 12,
+};
+
+/**
+ * Expected CSRNG output.
+ */
+const uint32_t kExpectedOutput[] = {
+ 0xb2cb8905, 0xc05e5950, 0xca318950, 0x96be29ea, 0x3d5a3b82, 0xb2694955,
+ 0x54eb80fe, 0x07de43e1, 0x93b9e7c3, 0xece73b80, 0xe062b1c1, 0xf68202fb,
+ 0xb1c52a04, 0x0ea24788, 0x64295282, 0x234aaada,
+};
+const uint32_t kExpectedOutputLen = 16;
+
+/**
+ * Wait for the `csrng` instance command interface to be ready to
+ * accept commands. Aborts test execution if an error is found.
+ */
+static void wait_for_csrng_cmd_ready(const dif_csrng_t *csrng) {
+ dif_csrng_cmd_status_t cmd_status = {0};
+ while (cmd_status != kDifCsrngCmdStatusReady) {
+ CHECK(dif_csrng_get_cmd_interface_status(csrng, &cmd_status) ==
+ kDifCsrngOk);
+ CHECK(cmd_status != kDifCsrngCmdStatusError);
+ }
+}
+
+/**
+ * Run CAVP CTR DRBG Counter=0 with `kEntropyInput` deterministic
+ * seed material.
+ */
+void test_ctr_drbg_ctr0(const dif_csrng_t *csrng) {
+ wait_for_csrng_cmd_ready(csrng);
+ CHECK(dif_csrng_instantiate(csrng, kDifCsrngEntropySrcToggleDisable,
+ &kEntropyInput) == kDifCsrngOk);
+
+ wait_for_csrng_cmd_ready(csrng);
+ const dif_csrng_seed_material_t seed_material = {0};
+ CHECK(dif_csrng_reseed(csrng, &seed_material) == kDifCsrngOk);
+
+ wait_for_csrng_cmd_ready(csrng);
+ CHECK(dif_csrng_generate(csrng, kExpectedOutputLen) == kDifCsrngOk);
+
+ dif_csrng_output_status_t output_status = {0};
+ while (!output_status.valid_data) {
+ CHECK(dif_csrng_get_output_status(csrng, &output_status) == kDifCsrngOk);
+ }
+
+ uint32_t output[16];
+ CHECK(dif_csrng_read_output(csrng, output, kExpectedOutputLen) ==
+ kDifCsrngOk);
+
+ // TODO(#5982): Enable CSRNG SW path without entropy source.
+ for (uint32_t i = 0; i < 16; ++i) {
+ LOG_INFO("[%d] got = 0x%x; expected = 0x%x", i, output[i],
+ kExpectedOutput[i]);
+ }
+}
+
bool test_main() {
const dif_csrng_params_t params = {
.base_addr = mmio_region_from_addr(TOP_EARLGREY_CSRNG_BASE_ADDR),
@@ -25,10 +107,7 @@
};
CHECK(dif_csrng_configure(&csrng, config) == kDifCsrngOk);
- // TODO: Instantiate
- // TODO: Generate
- // TODO: Check for output ready
- // TODO: Compare output
+ test_ctr_drbg_ctr0(&csrng);
return true;
}
diff --git a/sw/device/tests/dif/dif_csrng_unittest.cc b/sw/device/tests/dif/dif_csrng_unittest.cc
index 9e916a8..64a08dd 100644
--- a/sw/device/tests/dif/dif_csrng_unittest.cc
+++ b/sw/device/tests/dif/dif_csrng_unittest.cc
@@ -4,6 +4,9 @@
#include "sw/device/lib/dif/dif_csrng.h"
+#include <array>
+#include <vector>
+
#include "gtest/gtest.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/testing/mock_mmio.h"
@@ -132,5 +135,131 @@
EXPECT_EQ(status.fips_mode, false);
}
+/**
+ * DRBG commands are tested using this test group as the underlying
+ * command interface is shared across API functions.
+ */
+class CommandTest : public DifCsrngTest {
+ protected:
+ dif_csrng_seed_material_t seed_material_ = {
+ .seed_material_len = 0,
+ .seed_material = {0},
+ };
+};
+
+TEST_F(CommandTest, InstantiateOk) {
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000101);
+ EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleDisable,
+ &seed_material_),
+ kDifCsrngOk);
+
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000001);
+ EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleEnable,
+ &seed_material_),
+ kDifCsrngOk);
+
+ seed_material_.seed_material[0] = 0x5a5a5a5a;
+ seed_material_.seed_material_len = 1;
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000011);
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+ EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleEnable,
+ &seed_material_),
+ kDifCsrngOk);
+}
+
+TEST_F(CommandTest, InstantiateBadArgs) {
+ EXPECT_EQ(dif_csrng_instantiate(nullptr, kDifCsrngEntropySrcToggleDisable,
+ &seed_material_),
+ kDifCsrngBadArg);
+
+ // Failed overflow check.
+ seed_material_.seed_material_len = 16;
+ EXPECT_EQ(dif_csrng_instantiate(&csrng_, kDifCsrngEntropySrcToggleDisable,
+ &seed_material_),
+ kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, ReseedOk) {
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000002);
+ EXPECT_EQ(dif_csrng_reseed(&csrng_, &seed_material_), kDifCsrngOk);
+
+ seed_material_.seed_material[0] = 0x5a5a5a5a;
+ seed_material_.seed_material_len = 1;
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000012);
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+ EXPECT_EQ(dif_csrng_reseed(&csrng_, &seed_material_), kDifCsrngOk);
+}
+
+TEST_F(CommandTest, ReseedBadArgs) {
+ EXPECT_EQ(dif_csrng_reseed(nullptr, &seed_material_), kDifCsrngBadArg);
+
+ // Failed overflow check.
+ seed_material_.seed_material_len = 16;
+ EXPECT_EQ(dif_csrng_reseed(&csrng_, &seed_material_), kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, UpdateOk) {
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000004);
+ EXPECT_EQ(dif_csrng_update(&csrng_, &seed_material_), kDifCsrngOk);
+
+ seed_material_.seed_material[0] = 0x5a5a5a5a;
+ seed_material_.seed_material_len = 1;
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000014);
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x5a5a5a5a);
+ EXPECT_EQ(dif_csrng_update(&csrng_, &seed_material_), kDifCsrngOk);
+}
+
+TEST_F(CommandTest, UpdateBadArgs) {
+ EXPECT_EQ(dif_csrng_update(nullptr, &seed_material_), kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, GenerateOk) {
+ // 512bits = 16 x 32bit = 4 x 128bit blocks
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00004003);
+ EXPECT_EQ(dif_csrng_generate(&csrng_, /*len=*/16), kDifCsrngOk);
+
+ // 576bits = 18 x 32bit = 5 x 128bit blocks (rounded up)
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00005003);
+ EXPECT_EQ(dif_csrng_generate(&csrng_, /*len=*/18), kDifCsrngOk);
+}
+
+TEST_F(CommandTest, GenerateBadArgs) {
+ EXPECT_EQ(dif_csrng_generate(nullptr, /*len=*/1), kDifCsrngBadArg);
+ EXPECT_EQ(dif_csrng_generate(&csrng_, /*len=*/0), kDifCsrngBadArg);
+}
+
+TEST_F(CommandTest, UninstantiateOk) {
+ EXPECT_WRITE32(CSRNG_CMD_REQ_REG_OFFSET, 0x00000005);
+ EXPECT_EQ(dif_csrng_uninstantiate(&csrng_), kDifCsrngOk);
+}
+
+class ReadOutputTest : public DifCsrngTest {};
+
+TEST_F(ReadOutputTest, ReadOk) {
+ constexpr std::array<uint32_t, 4> kExpected = {
+ 0x00000000,
+ 0x11111111,
+ 0x22222222,
+ 0x33333333,
+ };
+
+ for (const uint32_t val : kExpected) {
+ EXPECT_READ32(CSRNG_GENBITS_REG_OFFSET, val);
+ }
+
+ std::vector<uint32_t> got(kExpected.size());
+ EXPECT_EQ(dif_csrng_read_output(&csrng_, got.data(), got.size()),
+ kDifCsrngOk);
+ EXPECT_THAT(got, testing::ElementsAreArray(kExpected));
+}
+
+TEST_F(ReadOutputTest, ReadBadArgs) {
+ EXPECT_EQ(dif_csrng_read_output(&csrng_, nullptr, /*len=*/0),
+ kDifCsrngBadArg);
+
+ uint32_t data;
+ EXPECT_EQ(dif_csrng_read_output(nullptr, &data, /*len=*/1), kDifCsrngBadArg);
+}
+
} // namespace
} // namespace dif_entropy_unittest