summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2019-11-20 09:50:40 -0800
committerCommit Bot <commit-bot@chromium.org>2020-06-17 22:30:57 +0000
commit32730b21cfd504438d6a711834b445c68ec19ae5 (patch)
tree592c1eb4de6cf12f85921bd2e8e4cead92869c4a /chip
parentd61ca497127ee518d65b26975cf3fadd62bc0a9a (diff)
downloadchrome-ec-32730b21cfd504438d6a711834b445c68ec19ae5.tar.gz
cr50: use NIST-compliant configuration of TRNG
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. More details on entropy estimate tests are in associated bug. Entropy measurements using NIST assessment tool didn't report noticeable change in entropy estimate. However, more changes are needed to use DRBG instead of raw TRNG for all purposes. TRNG changes reviewed also at https://crrev.com/c/1926384 BUG=b:138577834 TEST=test/tpm_test/nist_entropy.sh Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> Change-Id: I5a578b90b8b7a77fae6a218eec48e87e7644ab44 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2240519 Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Reviewed-by: Andrey Pronin <apronin@chromium.org> Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Commit-Queue: Vadim Sukhomlinov <sukhomlinov@chromium.org> Auto-Submit: Vadim Sukhomlinov <sukhomlinov@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r--chip/g/trng.c203
1 files changed, 134 insertions, 69 deletions
diff --git a/chip/g/trng.c b/chip/g/trng.c
index afd9fa86e3..b64c3c7dbe 100644
--- a/chip/g/trng.c
+++ b/chip/g/trng.c
@@ -8,6 +8,35 @@
#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 400
+ * cycles under normal conditions.
+ */
+#define TRNG_EMPTY_COUNT 400
void init_trng(void)
{
@@ -21,12 +50,50 @@ void init_trng(void)
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);
- GWRITE(TRNG, POST_PROCESSING_CTRL,
- GC_TRNG_POST_PROCESSING_CTRL_SHUFFLE_BITS_MASK |
- GC_TRNG_POST_PROCESSING_CTRL_CHURN_MODE_MASK);
- GWRITE(TRNG, SLICE_MAX_UPPER_LIMIT, 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);
@@ -34,16 +101,20 @@ void init_trng(void)
}
uint32_t rand(void)
-{
+{ uint32_t empty_count = 0;
+
while (GREAD(TRNG, EMPTY)) {
- if (GREAD_FIELD(TRNG, FSM_STATE, FSM_IDLE)) {
+ 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);
}
@@ -70,57 +141,40 @@ void rand_bytes(void *buffer, size_t len)
}
}
-#if !defined(SECTION_IS_RO) && defined(TEST_TRNG)
+#if !defined(SECTION_IS_RO) && defined(CRYPTO_TEST_SETUP)
#include "console.h"
#include "watchdog.h"
-static uint32_t histogram[256];
-static int command_rand(int argc, char **argv)
+static void print_rand_stat(uint32_t *histogram, size_t size)
{
- int count = 1000; /* Default number of cycles. */
struct pair {
uint32_t value;
uint32_t count;
};
struct pair min;
struct pair max;
-
- if (argc == 2)
- count = strtoi(argv[1], NULL, 10);
-
- memset(histogram, 0, sizeof(histogram));
- ccprintf("Retrieving %d random words.\n", count);
- while (count-- > 0) {
- uint32_t rvalue;
- int size;
-
- rvalue = rand();
- for (size = 0; size < sizeof(rvalue); size++)
- histogram[((uint8_t *)&rvalue)[size]]++;
-
- if (!(count % 10000))
- watchdog_reload();
- }
+ size_t count;
min.count = ~0;
max.count = 0;
- for (count = 0; count < ARRAY_SIZE(histogram); count++) {
+ max.value = ~0;
+ min.value = ~0;
+
+ for (count = 0; count < size; count++) {
if (histogram[count] > max.count) {
max.count = histogram[count];
max.value = count;
- continue;
}
- if (histogram[count] >= min.count)
- continue;
-
- min.count = histogram[count];
- min.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 < ARRAY_SIZE(histogram); count++) {
+ for (count = 0; count < size; count++) {
if (!(count % 8)) {
ccprintf("\n");
cflush();
@@ -128,43 +182,54 @@ static int command_rand(int argc, char **argv)
ccprintf(" %6d", histogram[count]);
}
ccprintf("\n");
- return EC_SUCCESS;
}
-DECLARE_CONSOLE_COMMAND(rand, command_rand, NULL, NULL);
-#endif /* !defined(SECTION_IS_RO) && defined(TEST_TRNG) */
-
-#ifdef CRYPTO_TEST_SETUP
-#include "extension.h"
-/*
- * This extension command is similar to TPM2_GetRandom, but made
- * available for CRYPTO_TEST = 1 which disables TPM
- * Command structure, shared out of band with the test driver running
- * on the host:
- *
- * field | size | note
- * ===================================================================
- * text_len | 2 | size of the text to process, big endian
- */
-static enum vendor_cmd_rc trng_test(enum vendor_cmd_cc code, void *buf,
- size_t input_size, size_t *response_size)
+
+/* 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)
{
- uint16_t text_len;
- uint8_t *cmd;
- size_t response_room = *response_size;
+ 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]]++;
- if (input_size != sizeof(text_len)) {
- *response_size = 0;
- return VENDOR_RC_BOGUS_ARGS;
+ /* 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();
}
- cmd = buf;
- text_len = *cmd++;
- text_len = text_len * 256 + *cmd++;
- text_len = MIN(text_len, response_room);
- rand_bytes(buf, text_len);
- *response_size = text_len;
- return VENDOR_RC_SUCCESS;
-}
+ 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));
-DECLARE_VENDOR_COMMAND(VENDOR_CC_TRNG_TEST, trng_test);
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(rand, command_rand, NULL, NULL);
-#endif /* CRYPTO_TEST_SETUP */
+#endif /* CRYPTO_TEST_SETUP */