diff options
Diffstat (limited to 'chip/g/trng.c')
-rw-r--r-- | chip/g/trng.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/chip/g/trng.c b/chip/g/trng.c new file mode 100644 index 0000000000..94363b29c4 --- /dev/null +++ b/chip/g/trng.c @@ -0,0 +1,236 @@ +/* Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "common.h" +#include "flash_log.h" +#include "init_chip.h" +#include "registers.h" +#include "trng.h" +#include "watchdog.h" +#include "console.h" + +/** + * The H1 TRNG uses the collapse time of a ring oscillator (RO) that is + * initialized in a 3x mode (three enable pulses) and eventually collapses + * to a stable 1x mode as a result of accumulated jitter (thermal noise). + * A Phase-Frequency Detector (PFD) compares the 3x RO to a reference + * RO (1.5x) and captures the state of a counter that is incremented + * from the reference RO. The resulting reference-cycles-to-collapse + * distribution is log-normal, and truncation of the counter bits results in + * a distribution that approaches uniform. + * + * TRNG_SAMPLE_BITS defines how many bits to use from the 16 bit counter + * output coming from the analog unit. Entropy is highest in least significant + * bits of counter. For FIPS-certified code use just Bit 0 - it provides + * highest entropy, allows better security settings for TRNG and simplifies + * implementation of continuous health tests. + */ +#ifndef TRNG_SAMPLE_BITS +#define TRNG_SAMPLE_BITS 1 +#endif + +/** + * Attempts to read TRNG_EMPTY before reporting a stall. Practically data should + * be available in less than 0x7ff cycles under normal conditions. 0x7ff was + * chosen to match the hardware TRNG TIMEOUT_COUNTER. Test on boards with slow + * TRNG before reducing this number. + */ +#define TRNG_EMPTY_COUNT 0x7ff + +void init_trng(void) +{ +#if (!(defined(CONFIG_CUSTOMIZED_RO) && defined(SECTION_IS_RO))) + /* + * Most of the trng initialization requires high permissions. If RO has + * dropped the permission level, dont try to read or write these high + * permission registers because it will cause rolling reboots. RO + * should do the TRNG initialization before dropping the level. + */ + if (!runlevel_is_high()) + return; +#endif + /** + * According to NIST SP 800-90B only vetted conditioning mechanism + * should be used for post-processing raw entropy. + * See SP 800-90B, 3.1.5.1 Using Vetted Conditioning Components. + * Use of non-vetted algorithms is governed in 3.1.5.2, but + * assumes conservative coefficient 0.85 for entropy estimate, + * which increase number of requests to TRNG to get desirable + * entropy and prevents from getting full entropy. + */ + GWRITE(TRNG, POST_PROCESSING_CTRL, 0); + + /** + * TRNG can return up to 16 bits at a time, but highest bits + * have lower entropy. Practically on Cr50 only 13 bits can be + * used - setting to higher value makes TRNG_EMPTY always set. + * Entropy assessed to be reasonable (one bit H > 0.85) + * for up to 8 bits [7..0]. + * Time to get 32bit random is roughly 160/TRNG_SAMPLE_BITS us. + */ + GWRITE(TRNG, SLICE_MAX_UPPER_LIMIT, TRNG_SAMPLE_BITS - 1); + + /* lowest bit have highest entropy, so always start from it */ + GWRITE(TRNG, SLICE_MIN_LOWER_LIMIT, 0); + + /** + * Analog logic cannot create a value < 8 under normal operating + * conditions, but there's a chance that an attacker could coax + * them out. + * Bit 0 - Enable rejection for values outside of range specified + * by TRNG_ALLOWED_VALUES register + */ + GWRITE(TRNG, SECURE_POST_PROCESSING_CTRL, 0x1); + + /** + * Since for FIPS settings we use TRNG_SAMPLE_BITS = 1, + * and take only bit 0 from internal 16 bit reading, no bias is + * created for bit 0 if allowed_min is set to 6, which + * actually means min accepted value is 8 (RTL adds +2). + * TRNG_ALLOWED_VALUES_MAX=0x04 (accept all values up to 2^16-1). + * So, range will be [8..65535], with probability for bit 0 and 1 + * remaining 50/50. + */ + GWRITE(TRNG, ALLOWED_VALUES_MIN, 0x26); + + GWRITE(TRNG, TIMEOUT_COUNTER, 0x7ff); + GWRITE(TRNG, TIMEOUT_MAX_TRY_NUM, 4); + GWRITE(TRNG, POWER_DOWN_B, 1); + GWRITE(TRNG, GO_EVENT, 1); +} + +uint32_t rand(void) +{ uint32_t empty_count = 0; + + while (GREAD(TRNG, EMPTY)) { + 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; + } + empty_count++; + } + return GREAD(TRNG, READ_DATA); +} + +void rand_bytes(void *buffer, size_t len) +{ + int random_togo = 0; + int buffer_index = 0; + uint32_t random_value; + uint8_t *buf = (uint8_t *) buffer; + + /* + * Retrieve random numbers in 4 byte quantities and pack as many bytes + * as needed into 'buffer'. If len is not divisible by 4, the + * remaining random bytes get dropped. + */ + while (buffer_index < len) { + if (!random_togo) { + random_value = rand(); + random_togo = sizeof(random_value); + } + buf[buffer_index++] = random_value >> + ((random_togo-- - 1) * 8); + } +} + +#if !defined(SECTION_IS_RO) && defined(CRYPTO_TEST_SETUP) +#include "console.h" +#include "watchdog.h" + +static void print_rand_stat(uint32_t *histogram, size_t size) +{ + struct pair { + uint32_t value; + uint32_t count; + }; + struct pair min; + struct pair max; + size_t count; + + min.count = ~0; + max.count = 0; + max.value = ~0; + min.value = ~0; + + for (count = 0; count < size; count++) { + if (histogram[count] > max.count) { + max.count = histogram[count]; + max.value = count; + } + if (histogram[count] < min.count) { + min.count = histogram[count]; + min.value = count; + } + } + + ccprintf("min %d(%d), max %d(%d)", min.count, min.value, + max.count, max.value); + + for (count = 0; count < size; count++) { + if (!(count % 8)) { + ccprintf("\n"); + cflush(); + } + ccprintf(" %6d", histogram[count]); + } + ccprintf("\n"); +} + +/* histogram at byte level */ +static uint32_t histogram[256]; +/* histogram at level of TRNG samples */ +static uint32_t histogram_trng[1 << TRNG_SAMPLE_BITS]; + +static int command_rand(int argc, char **argv) +{ + int count = 1000; /* Default number of cycles. */ + uint32_t val = 0, bits = 0; + + if (argc == 2) + count = strtoi(argv[1], NULL, 10); + + memset(histogram, 0, sizeof(histogram)); + memset(histogram_trng, 0, sizeof(histogram_trng)); + ccprintf("Retrieving %d 32-bit random words.\n", count); + while (count-- > 0) { + uint32_t rvalue; + int size; + + rvalue = rand(); + /* update byte-level histogram */ + for (size = 0; size < sizeof(rvalue); size++) + histogram[((uint8_t *)&rvalue)[size]]++; + + /* update histogram on TRNG sample size level */ + val = (val | (rvalue << bits)) & ((1 << TRNG_SAMPLE_BITS) - 1); + rvalue >>= TRNG_SAMPLE_BITS - bits; + bits += 32; + while (bits >= TRNG_SAMPLE_BITS) { + histogram_trng[val]++; + val = rvalue & ((1 << TRNG_SAMPLE_BITS) - 1); + rvalue >>= TRNG_SAMPLE_BITS; + bits -= TRNG_SAMPLE_BITS; + }; + + if (!(count % 10000)) + watchdog_reload(); + } + ccprintf("Byte-level histogram:\n"); + print_rand_stat(histogram, ARRAY_SIZE(histogram)); + ccprintf("\nSample-level (%d bits) histogram:\n", TRNG_SAMPLE_BITS); + print_rand_stat(histogram_trng, ARRAY_SIZE(histogram_trng)); + + return EC_SUCCESS; +} +DECLARE_SAFE_CONSOLE_COMMAND(rand, command_rand, NULL, NULL); + +#endif /* CRYPTO_TEST_SETUP */ |