[sw/dif] Add tests for HMAC DIF This change adds some simple hardware smoke tests for the HMAC DIF. These tests perform the following checks/actions: 1. Configure the HMAC engine. 2. Push a message into the HMAC engine. 3. Check that the HMAC engine is reporting the correct message length. 4. Kick off a run of HMAC/SHA256. 5. Read the digest and compare the result to the expected result. This test is run in both HMAC and SHA256-only modes. The purpose of these tests is to provide some basic sanity checks on hardware and to serve as a working example of how to interface with the DIF. Unit tests of the HMAC DIF are to follow. Signed-off-by: Jon Flatley <jflat@google.com>
diff --git a/sw/device/tests/dif/dif_hmac_sanitytest.c b/sw/device/tests/dif/dif_hmac_sanitytest.c new file mode 100644 index 0000000..d038ab5 --- /dev/null +++ b/sw/device/tests/dif/dif_hmac_sanitytest.c
@@ -0,0 +1,207 @@ +// 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/dif/dif_hmac.h" +#include "sw/device/lib/arch/device.h" +#include "sw/device/lib/base/log.h" +#include "sw/device/lib/base/mmio.h" +#include "sw/device/lib/common.h" +#include "sw/device/lib/flash_ctrl.h" +#include "sw/device/lib/runtime/check.h" +#include "sw/device/lib/testing/test_main.h" +#include "sw/device/lib/uart.h" + +#define HMAC0_BASE_ADDR 0x40120000 +#define MAX_FIFO_FILL 10 + +const test_config_t kTestConfig = { + .can_clobber_uart = false, +}; + +static const char kData[142] = + "Every one suspects himself of at least one of " + "the cardinal virtues, and this is mine: I am " + "one of the few honest people that I have ever " + "known"; + +static uint32_t kHmacKey[8] = {0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89}; + +static const uint32_t kExpectedShaDigest[8] = { + 0xdc96c23d, 0xaf36e268, 0xcb68ff71, 0xe92f76e2, + 0xb8a8379d, 0x426dc745, 0x19f5cff7, 0x4ec9c6d6}; + +static const uint32_t kExpectedHmacDigest[8] = { + 0xe4987b39, 0x3f83d390, 0xc2f3bbaf, 0x3195dbfa, + 0x23fb480c, 0xb012ae5e, 0xf1394d28, 0x1940ceeb}; + +/** + * Initialize the HMAC engine. Return `true` if the configuration is valid. + */ +static void test_setup(const dif_hmac_config_t *config, dif_hmac_t *hmac) { + dif_hmac_result_t res = dif_hmac_init(config, hmac); + + CHECK(res != kDifHmacBadArg, "Invalid arguments encountered in HMAC init."); + CHECK(res == kDifHmacOk, "Unknown error encountered in HMAC init."); +} + +/** + * Start HMAC in the correct mode. If `key` == NULL use SHA256 mode, otherwise + * use the provided key in HMAC mode. + */ +static void test_start(const dif_hmac_t *hmac, const uint8_t *key) { + dif_hmac_result_t res; + // Let a null key indicate we are operating in SHA256-only mode. + if (key == NULL) { + res = dif_hmac_mode_sha256_start(hmac); + } else { + res = dif_hmac_mode_hmac_start(hmac, key); + } + + CHECK(res != kDifHmacBadArg, "Invalid arguments encountered in HMAC start."); + CHECK(res == kDifHmacOk, "Unknown error encountered in HMAC start."); +} + +/** + * 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_hmac_fifo_result_t res = + dif_hmac_fifo_push(hmac, dp, len - (dp - data), &sent_bytes); + + CHECK(res != kDifHmacFifoBadArg, + "Invalid arguments encountered while pushing to FIFO."); + + if (res == kDifHmacFifoFull) { + ++fifo_fill_count; + } else { + CHECK(res != kDifHmacFifoBadArg, + "Invalid arguments encountered while pushing to FIFO."); + CHECK(res == kDifHmacFifoOk, + "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 { + dif_hmac_result_t res = dif_hmac_fifo_count_entries(hmac, &fifo_depth); + + CHECK(res != kDifHmacBadArg, + "Invalid arguments encountered checking FIFO depth."); + CHECK(res == kDifHmacOk, "Unknown error encountered checking FIFO depth."); + } while (fifo_depth > 0); +} + +/** + * Read and compare the length of the message in the HMAC engine to the length + * of the message sent in bits. + */ +static void check_message_length(const dif_hmac_t *hmac, + uint64_t expected_sent_bits) { + uint64_t sent_bits; + dif_hmac_result_t res = dif_hmac_get_message_length(hmac, &sent_bits); + + CHECK(res != kDifHmacBadArg, + "Invalid arguments encountered checking message length."); + CHECK(res == kDifHmacOk, + "Unknown error encountered checking message length."); + + // TODO: Support 64-bit integers in logging. + CHECK(expected_sent_bits == sent_bits, + "Message length mismatch. Expected %u bits but got %u bits.", + (uint32_t)expected_sent_bits, (uint32_t)sent_bits); +} + +/** + * Kick off the HMAC (or SHA256) run. + */ +static void run_hmac(const dif_hmac_t *hmac) { + dif_hmac_result_t res = dif_hmac_process(hmac); + + CHECK(res != kDifHmacBadArg, "Invalid arguments encountered running HMAC."); + CHECK(res == kDifHmacOk, "Unknown error encountered running 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_hmac_digest_result_t res = dif_hmac_digest_read(hmac, &digest_result); + + CHECK(res != kDifHmacDigestBadArg, + "Invalid arguments encountered reading HMAC digest."); + + hmac_done = (res != kDifHmacDigestProcessing); + + if (hmac_done) { + CHECK(res == kDifHmacDigestOk, + "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) { + test_start(hmac, key); + push_message(hmac, data, len); + wait_for_fifo_empty(hmac); + check_message_length(hmac, len * 8); + run_hmac(hmac); + check_digest(hmac, expected_digest); +} + +bool test_main() { + LOG_INFO("Running HMAC DIF test..."); + + dif_hmac_config_t hmac_config = { + .base_addr = mmio_region_from_addr(HMAC0_BASE_ADDR), + .digest_endianness = kDifHmacEndiannessBig, + .message_endianness = kDifHmacEndiannessBig, + }; + + dif_hmac_t hmac; + test_setup(&hmac_config, &hmac); + + LOG_INFO("Running test SHA256 pass 1..."); + run_test(&hmac, kData, sizeof(kData), NULL, kExpectedShaDigest); + + LOG_INFO("Running test SHA256 pass 2..."); + 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); + + LOG_INFO("Running test HMAC pass 2..."); + run_test(&hmac, kData, sizeof(kData), (uint8_t *)(&kHmacKey[0]), + kExpectedHmacDigest); + + return true; +}
diff --git a/sw/device/tests/dif/meson.build b/sw/device/tests/dif/meson.build index 6388ff6..e14ca80 100644 --- a/sw/device/tests/dif/meson.build +++ b/sw/device/tests/dif/meson.build
@@ -143,3 +143,17 @@ ) sw_tests += { 'dif_rv_timer_sanitytest': dif_rv_timer_sanitytest_lib } +dif_hmac_sanitytest_lib = declare_dependency( + link_with: static_library( + 'dif_hmac_sanitytest_lib', + sources: ['dif_hmac_sanitytest.c'], + dependencies: [ + sw_dif_hmac, + sw_lib_base_log, + sw_lib_mmio, + sw_lib_runtime_hart, + ], + ), +) + +sw_tests += { 'dif_hmac_sanitytest': dif_hmac_sanitytest_lib }