blob: 33ad0980b96625a13ffd1308e1b33f710e9f52be [file] [log] [blame] [edit]
// 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/macros.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_kmac.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"
#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
OTTF_DEFINE_TEST_CONFIG(.enable_concurrency = false,
.can_clobber_uart = false, );
/**
* Struct to pack timeout values.
*/
typedef struct kmac_edn_timeout {
uint16_t prescaler;
uint16_t wait_timer;
// Whether we expect timeout for hard-coded (`prescaler`, `wait_timer`) pairs
bool timeout_expected;
} kmac_edn_timeout_t;
/**
* Define various timer values to be used in the test.
*/
static const kmac_edn_timeout_t kTestTimeoutVals[] = {
{
.prescaler = 0x000,
.wait_timer = 0x0001,
.timeout_expected = true,
},
{
.prescaler = 0x000,
.wait_timer = 0x0001,
.timeout_expected = true,
},
{
.prescaler = 0x3ff,
.wait_timer = 0xffff,
.timeout_expected = false,
},
{
.prescaler = 0x3ff,
.wait_timer = 0xffff,
.timeout_expected = false,
},
};
enum {
kKmacTimeoutTestCount = ARRAYSIZE(kTestTimeoutVals),
// Maxiumum digest size in bytes
kKmacDigestLenMax = 100,
};
/**
* KMAC test description.
*/
typedef struct kmac_test {
dif_kmac_mode_kmac_t mode;
dif_kmac_key_t key;
const char *message;
size_t message_len;
const char *customization_string;
size_t customization_string_len;
const uint32_t digest[kKmacDigestLenMax];
size_t digest_len;
bool digest_len_is_fixed;
} kmac_test_t;
/**
* A single KMAC example:
* https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf
*/
const kmac_test_t kKmacTestVector = {
.mode = kDifKmacModeKmacLen256,
.key =
(dif_kmac_key_t){
.share0 = {0x43424140, 0x47464544, 0x4b4a4948, 0x4f4e4f4c,
0x53525150, 0x57565554, 0x5b5a5958, 0x5f5e5d5c},
.share1 = {0},
.length = kDifKmacKeyLen256,
},
.message =
"\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"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
"\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
.message_len = 200,
.customization_string = "My Tagged Application",
.customization_string_len = 21,
.digest = {0x1c73bed5, 0x73d74e95, 0x59bb4628, 0xe3a8e3db, 0x7ae7830f,
0x5944ff4b, 0xb4c2f1f2, 0xceb8ebec, 0xc601ba67, 0x57b88a2e,
0x9b492d8d, 0x6727bbd1, 0x90117868, 0x6a300a02, 0x1d28de97,
0x5d3030cc},
.digest_len = 16,
.digest_len_is_fixed = false,
};
bool test_main(void) {
LOG_INFO("Running KMAC ENTROPY test...");
// Initialize KMAC HWIP
dif_kmac_t kmac;
CHECK_DIF_OK(
dif_kmac_init(mmio_region_from_addr(TOP_EARLGREY_KMAC_BASE_ADDR), &kmac));
// Configure KMAC to use EDN entropy
dif_kmac_config_t config = {
.entropy_mode = kDifKmacEntropyModeEdn,
.entropy_fast_process = false,
// In the last iteration, we expect to see that hash_ctr rewinds to 0
.entropy_hash_threshold = kKmacTimeoutTestCount,
.message_big_endian = false,
.output_big_endian = false,
.sideload = false,
.msg_mask = false,
};
// Handle string encoding
dif_kmac_customization_string_t str_buffer;
CHECK_DIF_OK(dif_kmac_customization_string_init(
kKmacTestVector.customization_string,
kKmacTestVector.customization_string_len, &str_buffer));
// When customization_string is empty, use NULL to activate empty str path
dif_kmac_customization_string_t *str_buffer_ptr =
kKmacTestVector.customization_string_len == 0 ? NULL : &str_buffer;
size_t test_digest_len =
kKmacTestVector.digest_len_is_fixed ? kKmacTestVector.digest_len : 0;
for (size_t i = 0; i < kKmacTimeoutTestCount; ++i) {
LOG_INFO("loop_ctr = %d", i);
kmac_edn_timeout_t timeout_test = kTestTimeoutVals[i];
config.entropy_prescaler = timeout_test.prescaler;
config.entropy_wait_timer = timeout_test.wait_timer;
CHECK_DIF_OK(dif_kmac_configure(&kmac, config));
dif_kmac_operation_state_t kmac_operation_state;
CHECK_DIF_OK(dif_kmac_mode_kmac_start(
&kmac, &kmac_operation_state, kKmacTestVector.mode, test_digest_len,
&kKmacTestVector.key, str_buffer_ptr));
CHECK_DIF_OK(dif_kmac_absorb(&kmac, &kmac_operation_state,
kKmacTestVector.message,
kKmacTestVector.message_len, NULL));
CHECK(kKmacDigestLenMax >= kKmacTestVector.digest_len);
// This is where timeout might happen, so we handle dif return manually
uint32_t out[kKmacDigestLenMax];
dif_result_t res =
dif_kmac_squeeze(&kmac, &kmac_operation_state, out,
kKmacTestVector.digest_len, /*processed=*/NULL);
// It is OK to get kDifError at this point because of possible timeout
CHECK(res == kDifOk || res == kDifError);
// Check if there is a new error
bool irq_err_pending;
CHECK_DIF_OK(
dif_kmac_irq_is_pending(&kmac, kDifKmacIrqKmacErr, &irq_err_pending));
if (irq_err_pending) {
dif_kmac_error_t err_status;
CHECK_DIF_OK(dif_kmac_get_error(&kmac, &err_status));
CHECK(err_status == kDifErrorEntropyWaitTimerExpired,
"Error other than EDN timeout occurred.");
LOG_INFO("EDN timed out.");
} else {
LOG_INFO("EDN seed received before timeout.");
}
// At this point, irq_err_pending says if timeout is observed
CHECK(irq_err_pending == timeout_test.timeout_expected,
"EDN timeout expectation doesn't match observation.");
// Flush out the result and check correctness
CHECK_DIF_OK(dif_kmac_end(&kmac, &kmac_operation_state));
// If err interrupt is generated, we need clean-up
if (irq_err_pending) {
// Clean INTR_STATE
CHECK_DIF_OK(dif_kmac_irq_acknowledge_all(&kmac));
// Reset FSM by setting `err_processed` bit
CHECK_DIF_OK(dif_kmac_reset(&kmac, &kmac_operation_state));
// At this point, we expect that there are no remaining interrupts.
dif_kmac_irq_state_snapshot_t intr_snapshot;
CHECK_DIF_OK(dif_kmac_irq_get_state(&kmac, &intr_snapshot));
CHECK(intr_snapshot == 0,
"INTR_STATE is non-zero after timeout clean-up.");
bool is_kmac_locked;
CHECK_DIF_OK(dif_kmac_config_is_locked(&kmac, &is_kmac_locked));
CHECK(!is_kmac_locked, "KMAC still locked after timeout clean-up.");
}
// We check whether hash counter is increasing/rewinding as expected
uint32_t hash_ctr;
CHECK_DIF_OK(dif_kmac_get_hash_counter(&kmac, &hash_ctr));
CHECK(hash_ctr == (i + 1) % (kKmacTimeoutTestCount),
"Hash counter increment is wrong.");
LOG_INFO("hash_ctr = %d", hash_ctr);
}
return true;
}