[hmac_enc, tests] Factor out common functionality into testutils
The smoketest is written in such way, that large portion of the
static API can be factored out as testutils helpers.
This is useful in terms of the next commit, which adds the
2nd part of the hmac_enc_test.
Signed-off-by: Silvestrs Timofejevs <silvestrst@lowrisc.org>
diff --git a/sw/device/lib/testing/hmac_testutils.c b/sw/device/lib/testing/hmac_testutils.c
index 72f118f..fc50434 100644
--- a/sw/device/lib/testing/hmac_testutils.c
+++ b/sw/device/lib/testing/hmac_testutils.c
@@ -5,6 +5,7 @@
#include "sw/device/lib/testing/hmac_testutils.h"
#include "sw/device/lib/dif/dif_hmac.h"
+#include "sw/device/lib/runtime/ibex.h"
#include "sw/device/lib/testing/check.h"
void hmac_testutils_check_message_length(const dif_hmac_t *hmac,
@@ -20,3 +21,55 @@
(uint32_t)(expected_sent_bits >> 32), (uint32_t)expected_sent_bits,
(uint32_t)(sent_bits >> 32), (uint32_t)sent_bits);
}
+
+/**
+ * Checks whether the HMAC FIFO is empty.
+ */
+static bool check_fifo_empty(const dif_hmac_t *hmac) {
+ uint32_t fifo_depth;
+ CHECK_DIF_OK(dif_hmac_fifo_count_entries(hmac, &fifo_depth));
+ return fifo_depth == 0;
+}
+void hmac_testutils_fifo_empty_polled(const dif_hmac_t *hmac) {
+ IBEX_SPIN_FOR(check_fifo_empty(hmac), HMAC_TESTUTILS_FIFO_EMPTY_USEC);
+}
+
+static bool check_finished(const dif_hmac_t *hmac,
+ dif_hmac_digest_t *digest_out) {
+ dif_result_t res = dif_hmac_finish(hmac, digest_out);
+ CHECK(res == kDifOk || res == kDifUnavailable, "HMAC error = %d", res);
+
+ return res == kDifOk;
+}
+void hmac_testutils_finish_polled(const dif_hmac_t *hmac,
+ dif_hmac_digest_t *digest_out) {
+ IBEX_SPIN_FOR(check_finished(hmac, digest_out),
+ HMAC_TESTUTILS_FINISH_TIMEOUT_USEC);
+}
+
+void hmac_testutils_finish_and_check_polled(const dif_hmac_t *hmac,
+ const dif_hmac_digest_t *expected) {
+ dif_hmac_digest_t digest;
+ hmac_testutils_finish_polled(hmac, &digest);
+ CHECK_BUFFER(digest.digest, expected, ARRAYSIZE(digest.digest));
+}
+
+void hmac_testutils_push_message(const dif_hmac_t *hmac, const char *data,
+ size_t len) {
+ const char *dp = data;
+ size_t sent_bytes;
+
+ while (dp - data < len) {
+ dif_result_t res =
+ dif_hmac_fifo_push(hmac, dp, len - (dp - data), &sent_bytes);
+ CHECK(res == kDifOk || res == kDifIpFifoFull, "HMAC error = %d", res);
+
+ // Wait until the FIFO is drained before pushing more data. This helps
+ // to prevent the undesirable back pressure condition.
+ if (res == kDifIpFifoFull) {
+ hmac_testutils_fifo_empty_polled(hmac);
+ }
+
+ dp += sent_bytes;
+ }
+}
diff --git a/sw/device/lib/testing/hmac_testutils.h b/sw/device/lib/testing/hmac_testutils.h
index 626014f..37566a8 100644
--- a/sw/device/lib/testing/hmac_testutils.h
+++ b/sw/device/lib/testing/hmac_testutils.h
@@ -8,13 +8,94 @@
#include <assert.h>
#include <stdint.h>
+#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/dif/dif_hmac.h"
/**
- * Read and compare the length of the message in the HMAC engine to the length
- * of the message sent in bits.
+ * Timeouts to be used for different HMAC operations.
+ *
+ * All timeouts are calculated against the `kClockFreqCpuHz`, in order
+ * to cover a range of targets. Please see:
+ * https://docs.opentitan.org/hw/ip/hmac/doc/
+ *
+ * 10 cycles are added to the length of the corresponding operation as
+ * described in the documentation. This is to cover any potential
+ * inconsistencies or minor IP changes.
+ *
+ * To avoid cases with sub uS timeout due to very high clock frequency, we
+ * guarantee 1uS minimal timeout by adding it in the end.
+ */
+
+/**
+ * FIFO empty timeout.
+ *
+ * single HMAC block compression takes 80 cycles.
+ */
+#define HMAC_TESTUTILS_FIFO_EMPTY_USEC (80 + 10) * 1000000 / kClockFreqCpuHz + 1
+
+/**
+ * HMAC done timeout.
+ *
+ * Final hash calculation takes 360 cycles, which consists of one block
+ * compression and extra HMAC computation.
+ */
+#define HMAC_TESTUTILS_FINISH_TIMEOUT_USEC \
+ (360 + 10) * 1000000 / kClockFreqCpuHz + 1
+
+/**
+ * Reads and compares the actual sent message length against expected.
+ *
+ * The message length is provided in bits.
+ *
+ * @param hmac An HMAC handle.
+ * @param expected_sent_bits Expected size of hashed data in bits.
*/
void hmac_testutils_check_message_length(const dif_hmac_t *hmac,
uint64_t expected_sent_bits);
+/**
+ * Spins until the HMAC FIFO is empty, or has timed out.
+ *
+ * Internally uses `kHmacTestutilsFifoEmptyTimeoutUsec`.
+ *
+ * @param hmac An HMAC handle.
+ */
+void hmac_testutils_fifo_empty_polled(const dif_hmac_t *hmac);
+
+/**
+ * Spins until the HMAC has finished processing final hash, or timed out.
+ *
+ * Internally uses `kHmacTestutilsFinishTimeoutUsec`.
+ *
+ * @param hmac An HMAC handle.
+ * @param digest_out HMAC final digest.
+ */
+void hmac_testutils_finish_polled(const dif_hmac_t *hmac,
+ dif_hmac_digest_t *digest_out);
+
+/**
+ * Spins until HMAC has processed the final hash, and compares the digests.
+ *
+ * Convenience function that combines `hmac_testutils_finish_polled` and
+ * and `CHECK_BUFFER`.
+ *
+ * @param hmac An HMAC handle.
+ * @param expected Expected HMAC final digest.
+ */
+void hmac_testutils_finish_and_check_polled(const dif_hmac_t *hmac,
+ const dif_hmac_digest_t *expected);
+
+/**
+ * Loads entire message into the HMAC engine.
+ *
+ * Internally uses `hmac_testutils_fifo_empty_polled` after every push to
+ * avoid the back pressure.
+ *
+ * @param hmac An HMAC handle.
+ * @param data Data to be hashed.
+ * @param len Size of the data to be hashed.
+ */
+void hmac_testutils_push_message(const dif_hmac_t *hmac, const char *data,
+ size_t len);
+
#endif // OPENTITAN_SW_DEVICE_LIB_TESTING_HMAC_TESTUTILS_H_
diff --git a/sw/device/tests/hmac_smoketest.c b/sw/device/tests/hmac_smoketest.c
index 3666eb9..89b72ef 100644
--- a/sw/device/tests/hmac_smoketest.c
+++ b/sw/device/tests/hmac_smoketest.c
@@ -13,8 +13,6 @@
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
-#define MAX_FIFO_FILL 10
-
const test_config_t kTestConfig;
static const dif_hmac_transaction_t kHmacTransactionConfig = {
@@ -31,13 +29,33 @@
static uint32_t kHmacKey[8] = {0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89};
-static const uint32_t kExpectedShaDigest[8] = {
- 0x3dc296dc, 0x68e236af, 0x71ff68cb, 0xe2762fe9,
- 0x9d37a8b8, 0x45c76d42, 0xf7cff519, 0xd6c6c94e};
+static const dif_hmac_digest_t kExpectedShaDigest = {
+ .digest =
+ {
+ 0x3dc296dc,
+ 0x68e236af,
+ 0x71ff68cb,
+ 0xe2762fe9,
+ 0x9d37a8b8,
+ 0x45c76d42,
+ 0xf7cff519,
+ 0xd6c6c94e,
+ },
+};
-static const uint32_t kExpectedHmacDigest[8] = {
- 0x397b98e4, 0x90d3833f, 0xafbbf3c2, 0xfadb9531,
- 0x0c48fb23, 0x5eae12b0, 0x284d39f1, 0xebce4019};
+static const dif_hmac_digest_t kExpectedHmacDigest = {
+ .digest =
+ {
+ 0x397b98e4,
+ 0x90d3833f,
+ 0xafbbf3c2,
+ 0xfadb9531,
+ 0x0c48fb23,
+ 0x5eae12b0,
+ 0x284d39f1,
+ 0xebce4019,
+ },
+};
/**
* Initialize the HMAC engine. Return `true` if the configuration is valid.
@@ -60,88 +78,21 @@
}
/**
- * Load a message into the HMAC engine. This function may block if the FIFO
- * fills up.
- */
-static void push_message(const dif_hmac_t *hmac, const char *data, size_t len) {
- int fifo_fill_count = 0;
- const char *dp = data;
- size_t sent_bytes;
-
- while (dp - data < len) {
- dif_result_t res =
- dif_hmac_fifo_push(hmac, dp, len - (dp - data), &sent_bytes);
-
- CHECK(res != kDifBadArg,
- "Invalid arguments encountered while pushing to FIFO.");
-
- if (res == kDifIpFifoFull) {
- ++fifo_fill_count;
- } else {
- CHECK(res != kDifBadArg,
- "Invalid arguments encountered while pushing to FIFO.");
- CHECK(res == kDifOk, "Unknown error encountered while pushing to FIFO.");
- }
-
- CHECK(fifo_fill_count <= MAX_FIFO_FILL,
- "FIFO filled up too may times, giving up.");
-
- dp += sent_bytes;
- }
-}
-
-/** Spin while the HMAC FIFO still has entries in it. Once the FIFO is empty we
- * can check the message length. Return `true` if there are no errors.
- */
-static void wait_for_fifo_empty(const dif_hmac_t *hmac) {
- uint32_t fifo_depth;
- do {
- CHECK_DIF_OK(dif_hmac_fifo_count_entries(hmac, &fifo_depth));
- } while (fifo_depth > 0);
-}
-
-/**
* Kick off the HMAC (or SHA256) run.
*/
static void run_hmac(const dif_hmac_t *hmac) {
CHECK_DIF_OK(dif_hmac_process(hmac));
}
-/**
- * Read the HMAC digest and compare it to the expected result.
- */
-static void check_digest(const dif_hmac_t *hmac,
- const uint32_t *expected_digest) {
- dif_hmac_digest_t digest_result;
- bool hmac_done = false;
- do {
- dif_result_t res = dif_hmac_finish(hmac, &digest_result);
-
- CHECK(res != kDifBadArg,
- "Invalid arguments encountered reading HMAC digest.");
-
- hmac_done = (res != kDifUnavailable);
-
- if (hmac_done) {
- CHECK(res == kDifOk, "Unknown error encountered reading HMAC digest.");
- }
- } while (!hmac_done);
-
- for (int i = 0; i < 8; ++i) {
- CHECK(expected_digest[i] == digest_result.digest[i],
- "Digest mismatch, expected: 0x%X at index [%i] but got: 0x%X.",
- expected_digest[i], i, digest_result.digest[i]);
- }
-}
-
static void run_test(const dif_hmac_t *hmac, const char *data, size_t len,
- const uint8_t *key, const uint32_t *expected_digest) {
+ const uint8_t *key,
+ const dif_hmac_digest_t *expected_digest) {
test_start(hmac, key);
- push_message(hmac, data, len);
- wait_for_fifo_empty(hmac);
+ hmac_testutils_push_message(hmac, data, len);
+ hmac_testutils_fifo_empty_polled(hmac);
hmac_testutils_check_message_length(hmac, len * 8);
run_hmac(hmac);
- check_digest(hmac, expected_digest);
+ hmac_testutils_finish_and_check_polled(hmac, expected_digest);
}
bool test_main() {
@@ -151,18 +102,18 @@
test_setup(mmio_region_from_addr(TOP_EARLGREY_HMAC_BASE_ADDR), &hmac);
LOG_INFO("Running test SHA256 pass 1...");
- run_test(&hmac, kData, sizeof(kData), NULL, kExpectedShaDigest);
+ run_test(&hmac, kData, sizeof(kData), NULL, &kExpectedShaDigest);
LOG_INFO("Running test SHA256 pass 2...");
- run_test(&hmac, kData, sizeof(kData), NULL, kExpectedShaDigest);
+ run_test(&hmac, kData, sizeof(kData), NULL, &kExpectedShaDigest);
LOG_INFO("Running test HMAC pass 1...");
run_test(&hmac, kData, sizeof(kData), (uint8_t *)(&kHmacKey[0]),
- kExpectedHmacDigest);
+ &kExpectedHmacDigest);
LOG_INFO("Running test HMAC pass 2...");
run_test(&hmac, kData, sizeof(kData), (uint8_t *)(&kHmacKey[0]),
- kExpectedHmacDigest);
+ &kExpectedHmacDigest);
return true;
}