| // 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; |
| } |