diff options
-rw-r--r-- | board/cr50/board.c | 4 | ||||
-rw-r--r-- | board/cr50/dcrypto/trng.c | 46 | ||||
-rw-r--r-- | board/cr50/fips_rand.c | 103 | ||||
-rw-r--r-- | board/cr50/fips_rand.h | 25 |
4 files changed, 90 insertions, 88 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c index dfa05b8ef9..96f90f6cee 100644 --- a/board/cr50/board.c +++ b/board/cr50/board.c @@ -13,6 +13,7 @@ #include "ec_version.h" #include "endian.h" #include "extension.h" +#include "fips_rand.h" #include "flash.h" #include "flash_config.h" #include "gpio.h" @@ -34,7 +35,6 @@ #include "system_chip.h" #include "task.h" #include "tpm_registers.h" -#include "trng.h" #include "uart_bitbang.h" #include "uartn.h" #include "usart.h" @@ -850,7 +850,7 @@ static void board_init(void) configure_board_specific_gpios(); init_pmu(); reset_wake_logic(); - init_trng(); + fips_init_trng(); maybe_trigger_ite_sync(); init_jittery_clock(1); diff --git a/board/cr50/dcrypto/trng.c b/board/cr50/dcrypto/trng.c index 94363b29c4..6045243615 100644 --- a/board/cr50/dcrypto/trng.c +++ b/board/cr50/dcrypto/trng.c @@ -4,10 +4,11 @@ */ #include "common.h" +#include "fips.h" +#include "fips_rand.h" #include "flash_log.h" #include "init_chip.h" #include "registers.h" -#include "trng.h" #include "watchdog.h" #include "console.h" @@ -39,7 +40,12 @@ */ #define TRNG_EMPTY_COUNT 0x7ff -void init_trng(void) +/** + * Number of attempts to reset TRNG after stall is detected. + */ +#define TRNG_RESET_COUNT 8 + +void fips_init_trng(void) { #if (!(defined(CONFIG_CUSTOMIZED_RO) && defined(SECTION_IS_RO))) /* @@ -101,25 +107,49 @@ void init_trng(void) GWRITE(TRNG, GO_EVENT, 1); } -uint32_t rand(void) -{ uint32_t empty_count = 0; +uint64_t read_rand(void) +{ + uint32_t empty_count = 0; + uint32_t reset_count = 0; + +#ifdef CRYPTO_TEST_SETUP + /* Do we need to simulate error? */ + if (fips_break_cmd == FIPS_BREAK_TRNG) + return (uint64_t)1UL << 32; /* Valid result, but value = 0. */ +#endif - while (GREAD(TRNG, EMPTY)) { + /** + * Make sure we never hang in the loop - try at max TRNG_RESET_COUNT + * reset attempts, then return error + */ + while (GREAD(TRNG, EMPTY) && (reset_count < TRNG_RESET_COUNT)) { if (GREAD_FIELD(TRNG, FSM_STATE, FSM_IDLE) || empty_count > TRNG_EMPTY_COUNT) { /* TRNG timed out, restart */ GWRITE(TRNG, STOP_WORK, 1); -#if !defined(SECTION_IS_RO) && defined(CONFIG_FLASH_LOG) flash_log_add_event(FE_LOG_TRNG_STALL, 0, NULL); -#endif GWRITE(TRNG, GO_EVENT, 1); empty_count = 0; + reset_count++; } empty_count++; } - return GREAD(TRNG, READ_DATA); + /** + * High 32-bits set to zero in case of error; + * otherwise value >> 32 == 1 + */ + return (uint64_t)GREAD(TRNG, READ_DATA) | + ((uint64_t)(reset_count < TRNG_RESET_COUNT) << 32); +} + +/* TODO(sukhomlinov): replace uses with fips_trng32(). */ +uint32_t rand(void) +{ + /* Just ignore validity status. */ + return (uint32_t)read_rand(); } +/* TODO(sukhomlinov): replace uses with fips_rand_bytes(). */ void rand_bytes(void *buffer, size_t len) { int random_togo = 0; diff --git a/board/cr50/fips_rand.c b/board/cr50/fips_rand.c index 85ffa9ef6c..e5488bcf29 100644 --- a/board/cr50/fips_rand.c +++ b/board/cr50/fips_rand.c @@ -11,7 +11,6 @@ #include "registers.h" #include "task.h" #include "timer.h" -#include "trng.h" #include "util.h" /** @@ -38,16 +37,6 @@ static uint32_t entropy_fifo[ENTROPY_SIZE_WORDS]; * at least 2^-50 */ -/** - * rand() should be able to return error code if reading from TRNG failed - * return as struct with 2 params is more efficient as data is passed in - * registers - */ -struct rand_result { - uint32_t random_value; - bool valid; -}; - /* state data for TRNG health test */ static struct { @@ -163,49 +152,6 @@ static bool fips_powerup_passed(void) rand_state.apt_initialized; } -/** - * Attempts to read TRNG_EMPTY before reporting a stall. - * Practically data should be available in less than 777 - * cycles under normal conditions. Give 4 attempts to - * reset before making decision TRNG is broken - */ -#define TRNG_EMPTY_COUNT 777 -#define TRNG_RESET_COUNT 4 - -/** - * replica of rand() with interface which returns errors properly - */ -static struct rand_result read_rand(void) -{ - uint32_t empty_count = 0; - uint32_t reset_count = 0; - -#ifdef CRYPTO_TEST_SETUP - /* Do we need to simulate error? */ - if (fips_break_cmd == FIPS_BREAK_TRNG) - return (struct rand_result){ .random_value = 0, .valid = true }; -#endif - - /** - * make sure we never hang in the loop - try at max 1 - * reset attempt, then return error - */ - while (GREAD(TRNG, EMPTY) && (reset_count < TRNG_RESET_COUNT)) { - if (GREAD_FIELD(TRNG, FSM_STATE, FSM_IDLE) || - empty_count > TRNG_EMPTY_COUNT) { - /* TRNG timed out, restart */ - GWRITE(TRNG, STOP_WORK, 1); - flash_log_add_event(FE_LOG_TRNG_STALL, 0, NULL); - GWRITE(TRNG, GO_EVENT, 1); - empty_count = 0; - reset_count++; - } - empty_count++; - } - return (struct rand_result){ .random_value = GREAD(TRNG, READ_DATA), - .valid = - (reset_count < TRNG_RESET_COUNT) }; -} /** * get random from TRNG and run continuous health tests. @@ -213,26 +159,25 @@ static struct rand_result read_rand(void) * @param power_up if non-zero indicates warm-up mode * @return random value from TRNG */ -static struct rand_result fips_trng32(int power_up) +static uint64_t fips_trng32(int power_up) { - struct rand_result r; + uint64_t r; /* Continuous health tests should have been initialized by now */ if (!(power_up || fips_crypto_allowed())) - return (struct rand_result){ .random_value = 0, - .valid = false }; + return 0; /* get noise */ r = read_rand(); - if (r.valid) { - if (!repetition_count_test(r.random_value)) { + if (rand_valid(r)) { + if (!repetition_count_test((uint32_t)r)) { fips_set_status(FIPS_FATAL_TRNG_RCT); - r.valid = false; + r = (uint32_t)r; } - if (!adaptive_proportion_test(r.random_value)) { + if (!adaptive_proportion_test((uint32_t)r)) { fips_set_status(FIPS_FATAL_TRNG_APT); - r.valid = false; + r = (uint32_t)r; } } else fips_set_status(FIPS_FATAL_TRNG_OTHER); @@ -243,8 +188,9 @@ static struct rand_result fips_trng32(int power_up) bool fips_trng_bytes(void *buffer, size_t len) { uint8_t *buf = (uint8_t *)buffer; - uint32_t random_togo = 0; - struct rand_result r; + size_t random_togo = 0; + uint64_t rand; + uint32_t r; /** * Retrieve random numbers in 4 byte quantities and pack as many bytes * as needed into 'buffer'. If len is not divisible by 4, the @@ -252,14 +198,15 @@ bool fips_trng_bytes(void *buffer, size_t len) */ while (len--) { if (!random_togo) { - r = fips_trng32(0); - if (!r.valid) + rand = fips_trng32(0); + if (!rand_valid(rand)) return false; - random_togo = sizeof(r.random_value); + r = (uint32_t)rand; + random_togo = sizeof(r); } - *buf++ = (uint8_t)r.random_value; + *buf++ = (uint8_t)r; random_togo--; - r.random_value >>= 8; + r >>= 8; } return true; } @@ -278,19 +225,20 @@ bool fips_trng_startup(int stage) /* Startup tests per NIST SP800-90B, Section 4 */ /* 4096 1-bit samples, in 2 steps, 2048 bit in each */ for (uint32_t i = 0; i < (TRNG_INIT_WORDS) / 2; i++) { - struct rand_result r = fips_trng32(1); + uint64_t r = fips_trng32(1); - if (!r.valid) + if (!rand_valid(r)) return false; /* store entropy for further use */ - entropy_fifo[i % ARRAY_SIZE(entropy_fifo)] = r.random_value; + entropy_fifo[i % ARRAY_SIZE(entropy_fifo)] = (uint32_t)r; } return fips_powerup_passed(); } bool fips_drbg_init(void) { - struct rand_result nonce; + uint64_t nonce; + uint32_t random; if (!fips_crypto_allowed()) return EC_ERROR_INVALID_CONFIG; @@ -304,18 +252,19 @@ bool fips_drbg_init(void) * Add 32 * 0.85 = 27 bits from nonce. */ nonce = fips_trng32(0); - if (!nonce.valid) + if (!rand_valid(nonce)) return false; + random = (uint32_t)nonce; /* read another 512 bits of noise */ if (!fips_trng_bytes(&entropy_fifo, sizeof(entropy_fifo))) return false; hmac_drbg_init(&fips_drbg, &entropy_fifo, sizeof(entropy_fifo), - &nonce.random_value, sizeof(nonce.random_value), NULL, + &random, sizeof(random), NULL, 0); - set_fast_random_seed(fips_trng32(0).random_value); + set_fast_random_seed((uint32_t)fips_trng32(0)); rand_state.drbg_initialized = 1; return true; } diff --git a/board/cr50/fips_rand.h b/board/cr50/fips_rand.h index 14eba3fe32..dca1f473bf 100644 --- a/board/cr50/fips_rand.h +++ b/board/cr50/fips_rand.h @@ -19,6 +19,29 @@ extern "C" { #define TRNG_SAMPLE_BITS 1 /** + * Initialize the true random number generator (TRNG) in FIPS-compliant + * way: + * 1. Set 1-bit alphabet + * 2. Set maximum possible range for internal ring-oscillator + * 3. Disable any other post-processing beyond #2 + **/ +void fips_init_trng(void); + +/** + * Returns random number with indication wherever reading is valid. This is + * different from rand() which doesn't provide any indication. + * High 32-bits set to zero in case of error; otherwise value >> 32 == 1 + * Use of uint64_t vs. struct results in more efficient code. + */ +uint64_t read_rand(void); + +/* Return true if read_rand() result contains valid random from TRNG. */ +static inline bool rand_valid(uint64_t rand) +{ + return (rand >> 32) != 0; +} + +/** * TRNG Health Tests * * If any of the approved continuous health tests are used by the entropy @@ -75,7 +98,7 @@ extern "C" { * over at least 4096 consecutive samples. * Note: This function can throw FIPS_FATAL_TRNG error * - * To hide latenccy of reading TRNG data, this test is executed in 2 stages + * To hide latency of reading TRNG data, this test is executed in 2 stages * @param stage is 0 or 1, choosing the stage. On each stage 2048 * samples are processed. Assuming that some other tasks can be executed * between stages, when TRNG FIFO if filled with samples. |