| // 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/crypto/drivers/entropy_kat.h" |
| |
| #include "sw/device/lib/base/abs_mmio.h" |
| #include "sw/device/lib/base/bitfield.h" |
| #include "sw/device/lib/base/memory.h" |
| #include "sw/device/lib/crypto/drivers/entropy.h" |
| #include "sw/device/lib/runtime/log.h" |
| |
| #include "csrng_regs.h" // Generated |
| #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| |
| enum { |
| kBaseCsrng = TOP_EARLGREY_CSRNG_BASE_ADDR, |
| }; |
| |
| /** |
| * CSRNG internal state selector ID. |
| */ |
| typedef enum entropy_csrng_internal_state_id { |
| /** |
| * CSRNG instance assigned to Entropy Distribution Network (EDN) 0. |
| */ |
| kCsrngInternalStateIdEdn0 = 0, |
| /** |
| * CSRNG instance assigned to Entropy Distribution Network (EDN) 1. |
| */ |
| kCsrngInternalStateIdEdn1 = 1, |
| /** |
| * CSRNG instance assigned to software interface. |
| */ |
| kCsrngInternalStateIdSw = 2, |
| } entropy_csrng_internal_state_id_t; |
| |
| /** |
| * CSRNG internal state. |
| */ |
| typedef struct entropy_csrng_internal_state { |
| /** |
| * Indicates the number of requests for pseudorandom bits since instantiation |
| * or reseeding. |
| */ |
| uint32_t reseed_counter; |
| /** |
| * Internal V working state with a 128bit block size. |
| */ |
| uint32_t v[4]; |
| /** |
| * Internal key used to configure the internal CSRNG cipher. |
| */ |
| uint32_t key[8]; |
| /** |
| * Set to true when the CSRNG instance has been instantiated. |
| */ |
| bool instantiated; |
| /** |
| * Set to true when FIPS compliant entropy was provided directly by the |
| * entropy source to instantiate or reseed the CSRNG instance. |
| */ |
| bool fips_compliance; |
| } entropy_csrng_internal_state_t; |
| |
| static void entropy_csrng_internal_state_get( |
| entropy_csrng_internal_state_id_t instance_id, |
| entropy_csrng_internal_state_t *state) { |
| // Select the instance id to read the internal state from, request a state |
| // machine halt, and wait for the internal registers to be ready to be read. |
| uint32_t reg = bitfield_field32_write( |
| 0, CSRNG_INT_STATE_NUM_INT_STATE_NUM_FIELD, instance_id); |
| abs_mmio_write32(kBaseCsrng + CSRNG_INT_STATE_NUM_REG_OFFSET, reg); |
| |
| // Read the internal state. |
| state->reseed_counter = |
| abs_mmio_read32(kBaseCsrng + CSRNG_INT_STATE_VAL_REG_OFFSET); |
| |
| for (size_t i = 0; i < ARRAYSIZE(state->v); ++i) { |
| state->v[i] = abs_mmio_read32(kBaseCsrng + CSRNG_INT_STATE_VAL_REG_OFFSET); |
| } |
| |
| for (size_t i = 0; i < ARRAYSIZE(state->key); ++i) { |
| state->key[i] = |
| abs_mmio_read32(kBaseCsrng + CSRNG_INT_STATE_VAL_REG_OFFSET); |
| } |
| |
| uint32_t flags = abs_mmio_read32(kBaseCsrng + CSRNG_INT_STATE_VAL_REG_OFFSET); |
| |
| // The following bit indexes are defined in |
| // https://docs.opentitan.org/hw/ip/csrng/doc/#working-state-values |
| state->instantiated = bitfield_bit32_read(flags, /*bit_index=*/0u); |
| state->fips_compliance = bitfield_bit32_read(flags, /*bit_index=*/1u); |
| } |
| |
| /** |
| * Checks the CSRNG internal state against `expected` values. |
| * |
| * @param csrng A CSRNG handle. |
| * @param expected Expected CSRNG internal state. |
| */ |
| static status_t check_internal_state( |
| const entropy_csrng_internal_state_t *expected) { |
| entropy_csrng_internal_state_t got = {0}; |
| entropy_csrng_internal_state_get(kCsrngInternalStateIdSw, &got); |
| if (memcmp(&got, expected, sizeof(entropy_csrng_internal_state_t)) == 0) { |
| return OK_STATUS(); |
| } |
| return INTERNAL(); |
| } |
| |
| status_t entropy_csrng_kat(void) { |
| TRY(entropy_csrng_uninstantiate()); |
| |
| const entropy_seed_material_t kEntropyInput = { |
| .data = {0x73bec010, 0x9262474c, 0x16a30f76, 0x531b51de, 0x2ee494e5, |
| 0xdfec9db3, 0xcb7a879d, 0x5600419c, 0xca79b0b0, 0xdda33b5c, |
| 0xa468649e, 0xdf5d73fa}, |
| .len = 12, |
| }; |
| TRY(entropy_csrng_instantiate( |
| /*disable_trng_input=*/kHardenedBoolTrue, &kEntropyInput)); |
| |
| const entropy_csrng_internal_state_t kExpectedStateInstantiate = { |
| .reseed_counter = 1, |
| .v = {0x06b8f59e, 0x43c0b2c2, 0x21052502, 0x217b5214}, |
| .key = {0x941709fd, 0xd8a25860, 0x861aecf3, 0x98a701a1, 0x0eb2c33b, |
| 0x74c08fad, 0x632d5227, 0x8c52f901}, |
| .instantiated = true, |
| .fips_compliance = false, |
| }; |
| TRY(check_internal_state(&kExpectedStateInstantiate)); |
| |
| enum { |
| kExpectedOutputLen = 16, |
| }; |
| |
| uint32_t got[kExpectedOutputLen]; |
| TRY(entropy_csrng_generate(/*seed_material=*/NULL, got, kExpectedOutputLen)); |
| TRY(entropy_csrng_generate(/*seed_material=*/NULL, got, kExpectedOutputLen)); |
| |
| const entropy_csrng_internal_state_t kExpectedStateGenerate = { |
| .reseed_counter = 3, |
| .v = {0xe73e3392, 0x7d2e92b1, 0x1a0bac9d, 0x53c78ac6}, |
| |
| .key = {0x66d1b85a, 0xc19d4dfd, 0x053b73e3, 0xe9dc0f90, 0x3f015bc8, |
| 0x4436e5fd, 0x1cccc697, 0x1a1c6e5f}, |
| .instantiated = true, |
| .fips_compliance = false, |
| }; |
| TRY(check_internal_state(&kExpectedStateGenerate)); |
| |
| // TODO(#13342): csrng does not provide a linear output order. For example, |
| // note the test vector output word order: 12,13,14,15 8,9,10,11 4,5,6,7 |
| // 0,1,2,3. |
| const uint32_t kExpectedOutput[kExpectedOutputLen] = { |
| 0xe48bb8cb, 0x1012c84c, 0x5af8a7f1, 0xd1c07cd9, 0xdf82ab22, 0x771c619b, |
| 0xd40fccb1, 0x87189e99, 0x510494b3, 0x64f7ac0c, 0x2581f391, 0x80b1dc2f, |
| 0x793e01c5, 0x87b107ae, 0xdb17514c, 0xa43c41b7, |
| }; |
| if (!memcmp(got, kExpectedOutput, sizeof(kExpectedOutput))) { |
| return OK_STATUS(); |
| } |
| |
| return INTERNAL(); |
| } |