|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | #include "sw/device/tests/entropy_src_kat_impl.h" | 
|  |  | 
|  | #include "sw/device/lib/base/memory.h" | 
|  | #include "sw/device/lib/base/mmio.h" | 
|  | #include "sw/device/lib/dif/dif_base.h" | 
|  | #include "sw/device/lib/dif/dif_entropy_src.h" | 
|  | #include "sw/device/lib/runtime/log.h" | 
|  | #include "sw/device/lib/testing/entropy_testutils.h" | 
|  | #include "sw/device/lib/testing/test_framework/check.h" | 
|  |  | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"  // Generated. | 
|  |  | 
|  | enum { | 
|  | /** | 
|  | * The size of the buffer used in firmware to process the entropy bits in | 
|  | * firmware override mode. | 
|  | */ | 
|  | kEntropyFifoBufferSize = 12, | 
|  | /** | 
|  | * The number of attemps for performing operations before timing out. | 
|  | */ | 
|  | kKatTestTimeoutAttempts = 256, | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Cleanly disables the SHA3 conditioner while in SHA3 mode, prompting | 
|  | * the release of a conditioned seed. | 
|  | * | 
|  | * If stopping the conditioner fails, due to pending data keep trying for | 
|  | * at most kKatTestTimeoutAttempts. | 
|  | * | 
|  | * @param entropy An entropy source instance. | 
|  | */ | 
|  | static void stop_sha3_conditioner(dif_entropy_src_t *entropy_src) { | 
|  | uint32_t fail_count = 0; | 
|  | dif_result_t op_result; | 
|  |  | 
|  | do { | 
|  | op_result = dif_entropy_src_conditioner_stop(entropy_src); | 
|  | if (op_result == kDifIpFifoFull) { | 
|  | fail_count++; | 
|  | CHECK(fail_count < kKatTestTimeoutAttempts); | 
|  | } else { | 
|  | CHECK_DIF_OK(op_result); | 
|  | } | 
|  | } while (op_result == kDifIpFifoFull); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Flushes any previously absorbed entropy from the SHA3 conditioning block. | 
|  | * | 
|  | * @param entropy An entropy source instance. | 
|  | */ | 
|  | static void flush_sha3_conditioner(dif_entropy_src_t *entropy_src) { | 
|  | // Start and stop the conditioner, without adding any new entropy. | 
|  | CHECK_DIF_OK(dif_entropy_src_conditioner_start(entropy_src)); | 
|  | stop_sha3_conditioner(entropy_src); | 
|  |  | 
|  | int fail_count = 0; | 
|  |  | 
|  | // Read (and discard) the resulting seed. | 
|  | uint32_t got[kEntropyFifoBufferSize]; | 
|  | for (size_t i = 0; i < ARRAYSIZE(got); ++i) { | 
|  | dif_result_t op_result = | 
|  | dif_entropy_src_non_blocking_read(entropy_src, &got[i]); | 
|  | if (op_result == kDifUnavailable) { | 
|  | fail_count++; | 
|  | CHECK(fail_count < kKatTestTimeoutAttempts); | 
|  | } else { | 
|  | CHECK_DIF_OK(op_result); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void entropy_src_kat_test(dif_entropy_src_t *entropy_src) { | 
|  | entropy_testutils_stop_all(); | 
|  | entropy_testutils_fw_override_enable(entropy_src, kEntropyFifoBufferSize, | 
|  | /*route_to_firmware=*/true, | 
|  | /*bypass_conditioner=*/false); | 
|  |  | 
|  | // Though most of the entropy_src state is cleared on disable, the | 
|  | // SHA3 conditioner accumulates entropy even from aborted seeds. For | 
|  | // the KAT though, we must clear any previously absorbed entropy. | 
|  | flush_sha3_conditioner(entropy_src); | 
|  |  | 
|  | const uint32_t kInputMsg[kEntropyFifoBufferSize] = { | 
|  | 0xa52a0da9, 0xcae141b2, 0x6d5bab9d, 0x2c3e5cc0, 0x225afc93, 0x5d31a610, | 
|  | 0x91b7f960, 0x0d566bb3, 0xef35e170, 0x94ba7d8e, 0x534eb741, 0x6b60b0da, | 
|  | }; | 
|  |  | 
|  | CHECK_DIF_OK(dif_entropy_src_conditioner_start(entropy_src)); | 
|  |  | 
|  | dif_result_t op_result; | 
|  | uint32_t fail_count = 0; | 
|  | uint32_t count; | 
|  | uint32_t total = 0; | 
|  |  | 
|  | // Load the input data. | 
|  | do { | 
|  | op_result = dif_entropy_src_observe_fifo_write( | 
|  | entropy_src, kInputMsg + total, ARRAYSIZE(kInputMsg) - total, &count); | 
|  | total += count; | 
|  | if (op_result == kDifIpFifoFull) { | 
|  | fail_count++; | 
|  | CHECK(fail_count < kKatTestTimeoutAttempts); | 
|  | } else { | 
|  | fail_count = 0; | 
|  | CHECK_DIF_OK(op_result); | 
|  | } | 
|  | } while (total < ARRAYSIZE(kInputMsg)); | 
|  |  | 
|  | // Cleanly disable the conditioner. | 
|  | stop_sha3_conditioner(entropy_src); | 
|  |  | 
|  | fail_count = 0; | 
|  | uint32_t got[kEntropyFifoBufferSize]; | 
|  | for (size_t i = 0; i < ARRAYSIZE(got); ++i) { | 
|  | op_result = dif_entropy_src_non_blocking_read(entropy_src, &got[i]); | 
|  | if (op_result == kDifUnavailable) { | 
|  | fail_count++; | 
|  | CHECK(fail_count < kKatTestTimeoutAttempts); | 
|  | } else { | 
|  | CHECK_DIF_OK(op_result); | 
|  | } | 
|  | } | 
|  |  | 
|  | const uint32_t kExpectedDigest[kEntropyFifoBufferSize] = { | 
|  | 0x1c88164a, 0x5ff456e1, 0x0845dbdf, 0xbe233f8e, 0x7a5a4c1b, 0x5d31356a, | 
|  | 0x751cc536, 0x337375f2, 0x85a1ac19, 0x1333abd4, 0xf745fe0f, 0xc5bbf151, | 
|  | }; | 
|  |  | 
|  | CHECK_ARRAYS_EQ(got, kExpectedDigest, kEntropyFifoBufferSize, | 
|  | "Unexpected digest value."); | 
|  | } |