summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVadim Sukhomlinov <sukhomlinov@google.com>2021-09-24 09:50:21 -0700
committerCommit Bot <commit-bot@chromium.org>2021-09-24 20:22:47 +0000
commit5235525f86bf5b28009afbfc37b459add0723762 (patch)
tree371c257013115b3a3bf5dc08a827a09d6ee9e3ad
parent24c5d1beb44ad229e962a9178e98468b8fe9705f (diff)
downloadchrome-ec-5235525f86bf5b28009afbfc37b459add0723762.tar.gz
cr50: update TRNG continuous test logic to handle intermittent errors
TRNG health tests have defined false positive. NIST recommends values in the range of 2^(-20) to 20^(-40) - parameter alpha. We choose 2^(-40), and computed thresholds for 2^(-30) if needed. In case of false positive we will try to read several times and update statistics to see if error is intermittent, skip those values until we either get recovered statistics or will be out of attempts. When out of attempts we declare a persistent error and report it. With this implementation we reduce probability of false positive to 2^(-160). This is in compliance with NIST SP 800-90B, 4.3 point 2: When the health tests fail, the entropy source shall notify the consuming application (e.g., the RBG) of the error condition. The developer may have defined different types of failures (e.g., intermittent and persistent), and the application is allowed to react differently to different types of failures (e.g., by inhibiting output for a short time). The developer is allowed to define different cutoff values to detect intermittent and persistent failures. If so, these values (with corresponding false alarm probabilities) shall be specified in the submission documentation. If the entropy source detects intermittent failures and allows the noise source to return to normal functioning, the designer shall provide evidence that: a) The intermittent failures handled in this way are indeed extremely likely to be intermittent failures; and b) the tests will detect a permanent failure when one occurs, and will ultimately signal an error condition to the consuming application and cease operation. In the case where a persistent failure is detected, the entropy source shall not produce any outputs. BUG=b:134594373 TEST=make BOARD=cr50 CRYPTO_TEST=1; In ccd: rand_perf rand perf (repeat several times, each time 8000 readings from TRNG) fips trng rand perf (should report errors) Signed-off-by: Vadim Sukhomlinov <sukhomlinov@google.com> Change-Id: I9db545c1a1e82e7e091724fab6fe46edebeb0650 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3182622 Reviewed-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Reviewed-by: Andrey Pronin <apronin@chromium.org> Tested-by: Vadim Sukhomlinov <sukhomlinov@chromium.org> Commit-Queue: Vadim Sukhomlinov <sukhomlinov@chromium.org>
-rw-r--r--board/cr50/dcrypto/fips_rand.c96
-rw-r--r--board/cr50/dcrypto/fips_rand.h24
2 files changed, 71 insertions, 49 deletions
diff --git a/board/cr50/dcrypto/fips_rand.c b/board/cr50/dcrypto/fips_rand.c
index e9a7fe7ac7..f50d6300fc 100644
--- a/board/cr50/dcrypto/fips_rand.c
+++ b/board/cr50/dcrypto/fips_rand.c
@@ -50,6 +50,12 @@ static struct {
} rand_state;
/**
+ * We use FIPS_UNINITIALIZED as default (zero) value to accumulate
+ * errors, so check it is really zero.
+ */
+BUILD_ASSERT(FIPS_UNINITIALIZED == 0);
+
+/**
* NIST SP 800-90B 4.4.1
* The repetition count test detects abnormal runs of 0s or 1s.
* RCT_CUTOFF_BITS must be >= 32.
@@ -60,7 +66,7 @@ static struct {
* readings, packed into 32-bit words.
* @return false if test failed
*/
-static bool repetition_count_test(uint32_t rnd)
+static enum fips_status repetition_count_test(uint32_t rnd)
{
uint32_t clz, ctz, clo, cto;
@@ -77,7 +83,7 @@ static bool repetition_count_test(uint32_t rnd)
*/
if ((ctz + rand_state.last_clz >= RCT_CUTOFF_SAMPLES) ||
(cto + rand_state.last_clo >= RCT_CUTOFF_SAMPLES))
- return false;
+ return FIPS_FATAL_TRNG_RCT;
/**
* merge series of repetitive values - update running counters in
@@ -96,7 +102,7 @@ static bool repetition_count_test(uint32_t rnd)
/* check we collected enough bits for statistics */
if (rand_state.rct_count < RCT_CUTOFF_WORDS)
++rand_state.rct_count;
- return true;
+ return FIPS_UNINITIALIZED;
}
static int misbalanced(uint32_t count)
@@ -122,7 +128,7 @@ static int popcount(uint32_t x)
* Instead of storing actual samples we can store pop counts
* of each 32bit reading, which would fit in 8-bit.
*/
-bool adaptive_proportion_test(uint32_t rnd)
+static enum fips_status adaptive_proportion_test(uint32_t rnd)
{
/* update rolling count */
rand_state.count -= rand_state.pops[rand_state.oldest];
@@ -142,8 +148,8 @@ bool adaptive_proportion_test(uint32_t rnd)
}
/* check when initialized */
if (rand_state.apt_initialized && misbalanced(rand_state.count))
- return false;
- return true;
+ return FIPS_FATAL_TRNG_APT;
+ return FIPS_UNINITIALIZED;
}
static bool fips_powerup_passed(void)
@@ -152,42 +158,45 @@ static bool fips_powerup_passed(void)
rand_state.apt_initialized;
}
-
/**
- * get random from TRNG and run continuous health tests.
+ * Get random from TRNG and run continuous health tests.
* it is also can simulate stuck-bit error
- * @param power_up if non-zero indicates warm-up mode
+ * @param power_up if true indicates warm-up mode
* @return random value from TRNG
*/
-static uint64_t fips_trng32(int power_up)
+static uint64_t fips_trng32(void)
{
uint64_t r;
+ uint32_t remaining_tries = 4;
+ enum fips_status error = FIPS_UNINITIALIZED;
- /* Continuous health tests should have been initialized by now */
- if (!(power_up || fips_crypto_allowed()))
- return 0;
-
- /* get noise */
- r = read_rand();
+ do {
+ r = read_rand();
- if (rand_valid(r)) {
- if (!repetition_count_test((uint32_t)r)) {
- fips_set_status(FIPS_FATAL_TRNG_RCT);
- r = (uint32_t)r;
- }
- if (!adaptive_proportion_test((uint32_t)r)) {
- fips_set_status(FIPS_FATAL_TRNG_APT);
- r = (uint32_t)r;
+ /* We can't read from TRNG. read_rand() made several tries. */
+ if (!rand_valid(r)) {
+ fips_set_status(FIPS_FATAL_TRNG_OTHER);
+ break;
}
- } else
- fips_set_status(FIPS_FATAL_TRNG_OTHER);
-
+ error = repetition_count_test((uint32_t)r);
+ error |= adaptive_proportion_test((uint32_t)r);
+ remaining_tries--;
+ /* Repeat several times if statistical tests doesn't pass. */
+ } while (remaining_tries && error != FIPS_UNINITIALIZED);
+
+ if (error != FIPS_UNINITIALIZED) {
+ fips_set_status(error);
+ r = (uint32_t)r; /* Set result as invalid. */
+ }
return r;
}
uint64_t fips_trng_rand32(void)
{
- return fips_trng32(0);
+ if (!fips_crypto_allowed())
+ return 0;
+
+ return fips_trng32();
}
bool fips_trng_bytes(void *buffer, size_t len)
@@ -196,6 +205,9 @@ bool fips_trng_bytes(void *buffer, size_t len)
size_t random_togo = 0;
uint64_t rand;
uint32_t r;
+
+ if (!fips_crypto_allowed())
+ return false;
/**
* Retrieve random numbers in 4 byte quantities and pack as many bytes
* as needed into 'buffer'. If len is not divisible by 4, the
@@ -203,7 +215,7 @@ bool fips_trng_bytes(void *buffer, size_t len)
*/
while (len--) {
if (!random_togo) {
- rand = fips_trng32(0);
+ rand = fips_trng32();
if (!rand_valid(rand))
return false;
r = (uint32_t)rand;
@@ -230,7 +242,7 @@ 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++) {
- uint64_t r = fips_trng32(1);
+ uint64_t r = fips_trng32();
if (!rand_valid(r))
return false;
@@ -258,7 +270,7 @@ bool fips_drbg_init(void)
* this is roughly equal to 435 bits of full entropy.
* Add 32 * 0.85 = 27 bits from nonce.
*/
- nonce = fips_trng32(0);
+ nonce = fips_trng32();
if (!rand_valid(nonce))
return false;
random = (uint32_t)nonce;
@@ -271,7 +283,7 @@ bool fips_drbg_init(void)
&random, sizeof(random), NULL,
0);
- set_fast_random_seed((uint32_t)fips_trng32(0));
+ set_fast_random_seed((uint32_t)fips_trng32());
rand_state.drbg_initialized = true;
return true;
}
@@ -387,7 +399,11 @@ static int cmd_rand_perf(int argc, char **argv)
starttime = get_time().val;
for (k = 0; k < 10; k++) {
for (j = 0; j < 100; j++)
- fips_rand_bytes(buf, sizeof(buf));
+ if (!fips_rand_bytes(buf, sizeof(buf))) {
+ ccprintf("DRBG test failed\n");
+ return EC_ERROR_HW_INTERNAL;
+ }
+ ;
watchdog_reload();
cflush();
}
@@ -398,18 +414,10 @@ static int cmd_rand_perf(int argc, char **argv)
starttime = get_time().val;
for (k = 0; k < 10; k++) {
for (j = 0; j < 100; j++)
- rand_bytes(&buf, sizeof(buf));
- watchdog_reload();
- }
- starttime = get_time().val - starttime;
- ccprintf("time for 1000 rand_byte() = %llu\n", starttime);
- cflush();
-
- starttime = get_time().val;
- for (k = 0; k < 10; k++) {
- for (j = 0; j < 100; j++)
- if (!fips_trng_bytes(&buf, sizeof(buf)))
+ if (!fips_trng_bytes(&buf, sizeof(buf))) {
ccprintf("FIPS TRNG error\n");
+ return EC_ERROR_HW_INTERNAL;
+ }
watchdog_reload();
}
starttime = get_time().val - starttime;
diff --git a/board/cr50/dcrypto/fips_rand.h b/board/cr50/dcrypto/fips_rand.h
index 10e44c7414..1010ad5e4d 100644
--- a/board/cr50/dcrypto/fips_rand.h
+++ b/board/cr50/dcrypto/fips_rand.h
@@ -18,6 +18,11 @@ extern "C" {
#define TRNG_SAMPLE_BITS 1
+/**
+ * Probability of false positive in single APT/RCT test
+ * defined as 2^(-TRNG_TEST_ALPHA).
+ */
+#define TRNG_TEST_ALPHA 40
/**
* TRNG Health Tests
@@ -43,9 +48,14 @@ extern "C" {
* (1) Repetition Count Test (RCT) NIST SP 800-90B 4.4.1
* Cut off value is computed as:
* c = ceil(1 + (-log2 alpha)/H);
- * alpha = 2^-50, H = 0.85; RCT_CUTOFF = CEIL(1+(50/0.85))
+ * alpha = 2^-50, H = 0.8; RCT_CUTOFF = CEIL(1+(ALPHA/0.8))
*/
-#define RCT_CUTOFF_SAMPLES 60
+#if TRNG_TEST_ALPHA == 40
+#define RCT_CUTOFF_SAMPLES 51
+#else
+/* RCT cut off for TRNG_TEST_ALPHA == 30 */
+#define RCT_CUTOFF_SAMPLES 39
+#endif
/**
* Number of 32-bit words containing RCT_CUTOFF_SAMPLES samples
@@ -66,10 +76,14 @@ extern "C" {
#define APT_WINDOW_SIZE_NWORDS (BITS_TO_WORDS(APT_WINDOW_SIZE_BITS))
/**
* Cut off value = CRITBINOM(W, power(2,(-H)),1-α).
- * 692 = CRITBINOM(1024, power(2,(-0.85)), 1 - 2^(-50))
+ * 698 = CRITBINOM(1024, power(2,(-0.8)), 1 - 2^(-40))
*/
-#define APT_CUTOFF_SAMPLES 692
-
+#if TRNG_TEST_ALPHA == 40
+#define APT_CUTOFF_SAMPLES 698
+#else
+/* APT cut off for TRNG_TEST_ALPHA == 30 */
+#define APT_CUTOFF_SAMPLES 682
+#endif
#ifdef __cplusplus
}