blob: 7f176d69aa1abd0e384191ae7eb137bd01b0cfd0 [file] [log] [blame]
// 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.");
}