summaryrefslogtreecommitdiff
path: root/chip/g/trng.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/trng.c')
-rw-r--r--chip/g/trng.c236
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 */