1
1
mirror of https://github.com/henrydcase/pqc.git synced 2024-11-22 23:48:58 +00:00
pqcrypto/3rd/jitterentropy/jitterentropy-health.c
2023-01-27 22:49:29 +00:00

458 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Jitter RNG: Health Tests
*
* Copyright (C) 2021 - 2022, Joshua E. Hill <josh@keypair.us>
* Copyright (C) 2021 - 2022, Stephan Mueller <smueller@chronox.de>
*
* License: see LICENSE file in root directory
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "jitterentropy-health.h"
static jent_fips_failure_cb fips_cb = NULL;
static int jent_health_cb_switch_blocked = 0;
void jent_health_cb_block_switch(void)
{
jent_health_cb_switch_blocked = 1;
}
int jent_set_fips_failure_callback_internal(jent_fips_failure_cb cb)
{
if (jent_health_cb_switch_blocked)
return -EAGAIN;
fips_cb = cb;
return 0;
}
/***************************************************************************
* Lag Predictor Test
*
* This test is a vendor-defined conditional test that is designed to detect
* a known failure mode where the result becomes mostly deterministic
* Note that (lag_observations & JENT_LAG_MASK) is the index where the next
* value provided will be stored.
***************************************************************************/
#ifdef JENT_HEALTH_LAG_PREDICTOR
/*
* These cutoffs are configured using an entropy estimate of 1/osr under an
* alpha=2^(-22) for a window size of 131072. The other health tests use
* alpha=2^-30, but operate on much smaller window sizes. This larger selection
* of alpha makes the behavior per-lag-window similar to the APT test.
*
* The global cutoffs are calculated using the
* InverseBinomialCDF(n=(JENT_LAG_WINDOW_SIZE-JENT_LAG_HISTORY_SIZE), p=2^(-1/osr); 1-alpha)
* The local cutoffs are somewhat more complicated. For background, see Feller's
* _Introduction to Probability Theory and It's Applications_ Vol. 1,
* Chapter 13, section 7 (in particular see equation 7.11, where x is a root
* of the denominator of equation 7.6).
*
* We'll proceed using the notation of SP 800-90B Section 6.3.8 (which is
* developed in Kelsey-McKay-Turan paper "Predictive Models for Min-entropy
* Estimation".)
*
* Here, we set p=2^(-1/osr), seeking a run of successful guesses (r) with
* probability of less than (1-alpha). That is, it is very very likely
* (probability 1-alpha) that there is _no_ run of length r in a block of size
* JENT_LAG_WINDOW_SIZE-JENT_LAG_HISTORY_SIZE.
*
* We have to iteratively look for an appropriate value for the cutoff r.
*/
static const unsigned int jent_lag_global_cutoff_lookup[20] =
{ 66443, 93504, 104761, 110875, 114707, 117330, 119237, 120686, 121823,
122739, 123493, 124124, 124660, 125120, 125520, 125871, 126181, 126457,
126704, 126926 };
static const unsigned int jent_lag_local_cutoff_lookup[20] =
{ 38, 75, 111, 146, 181, 215, 250, 284, 318, 351,
385, 419, 452, 485, 518, 551, 584, 617, 650, 683 };
void jent_lag_init(struct rand_data *ec, unsigned int osr)
{
/*
* Establish the lag global and local cutoffs based on the presumed
* entropy rate of 1/osr.
*/
if (osr > ARRAY_SIZE(jent_lag_global_cutoff_lookup)) {
ec->lag_global_cutoff =
jent_lag_global_cutoff_lookup[
ARRAY_SIZE(jent_lag_global_cutoff_lookup) - 1];
} else {
ec->lag_global_cutoff = jent_lag_global_cutoff_lookup[osr - 1];
}
if (osr > ARRAY_SIZE(jent_lag_local_cutoff_lookup)) {
ec->lag_local_cutoff =
jent_lag_local_cutoff_lookup[
ARRAY_SIZE(jent_lag_local_cutoff_lookup) - 1];
} else {
ec->lag_local_cutoff = jent_lag_local_cutoff_lookup[osr - 1];
}
}
/**
* Reset the lag counters
*
* @ec [in] Reference to entropy collector
*/
static void jent_lag_reset(struct rand_data *ec)
{
unsigned int i;
/* Reset Lag counters */
ec->lag_prediction_success_count = 0;
ec->lag_prediction_success_run = 0;
ec->lag_best_predictor = 0; /* The first guess is basically arbitrary. */
ec->lag_observations = 0;
for (i = 0; i < JENT_LAG_HISTORY_SIZE; i++) {
ec->lag_scoreboard[i] = 0;
ec->lag_delta_history[i] = 0;
}
}
/*
* A macro for accessing the history. Index 0 is the last observed symbol
* index 1 is the symbol observed two inputs ago, etc.
*/
#define JENT_LAG_HISTORY(EC,LOC) \
((EC)->lag_delta_history[((EC)->lag_observations - (LOC) - 1) & \
JENT_LAG_MASK])
/**
* Insert a new entropy event into the lag predictor test
*
* @ec [in] Reference to entropy collector
* @current_delta [in] Current time delta
*/
static void jent_lag_insert(struct rand_data *ec, uint64_t current_delta)
{
uint64_t prediction;
unsigned int i;
/* Initialize the delta_history */
if (ec->lag_observations < JENT_LAG_HISTORY_SIZE) {
ec->lag_delta_history[ec->lag_observations] = current_delta;
ec->lag_observations++;
return;
}
/*
* The history is initialized. First make a guess and examine the
* results.
*/
prediction = JENT_LAG_HISTORY(ec, ec->lag_best_predictor);
if (prediction == current_delta) {
/* The prediction was correct. */
ec->lag_prediction_success_count++;
ec->lag_prediction_success_run++;
if ((ec->lag_prediction_success_run >= ec->lag_local_cutoff) ||
(ec->lag_prediction_success_count >= ec->lag_global_cutoff))
ec->health_failure |= JENT_LAG_FAILURE;
} else {
/* The prediction wasn't correct. End any run of successes.*/
ec->lag_prediction_success_run = 0;
}
/* Now update the predictors using the current data. */
for (i = 0; i < JENT_LAG_HISTORY_SIZE; i++) {
if (JENT_LAG_HISTORY(ec, i) == current_delta) {
/*
* The ith predictor (which guesses i + 1 symbols in
* the past) successfully guessed.
*/
ec->lag_scoreboard[i] ++;
/*
* Keep track of the best predictor (tie goes to the
* shortest lag)
*/
if (ec->lag_scoreboard[i] >
ec->lag_scoreboard[ec->lag_best_predictor])
ec->lag_best_predictor = i;
}
}
/*
* Finally, update the lag_delta_history array with the newly input
* value.
*/
ec->lag_delta_history[(ec->lag_observations) & JENT_LAG_MASK] =
current_delta;
ec->lag_observations++;
/*
* lag_best_predictor now is the index of the predictor with the largest
* number of correct guesses.
* This establishes our next guess.
*/
/* Do we now need a new window? */
if (ec->lag_observations >= JENT_LAG_WINDOW_SIZE)
jent_lag_reset(ec);
}
static inline uint64_t jent_delta2(struct rand_data *ec, uint64_t current_delta)
{
/* Note that delta2_n = delta_n - delta_{n-1} */
return jent_delta(JENT_LAG_HISTORY(ec, 0), current_delta);
}
static inline uint64_t jent_delta3(struct rand_data *ec, uint64_t delta2)
{
/*
* Note that delta3_n = delta2_n - delta2_{n-1}
* = delta2_n - (delta_{n-1} - delta_{n-2})
*/
return jent_delta(jent_delta(JENT_LAG_HISTORY(ec, 1),
JENT_LAG_HISTORY(ec, 0)), delta2);
}
#else /* JENT_HEALTH_LAG_PREDICTOR */
static inline void jent_lag_insert(struct rand_data *ec, uint64_t current_delta)
{
(void)ec;
(void)current_delta;
}
static inline uint64_t jent_delta2(struct rand_data *ec, uint64_t current_delta)
{
uint64_t delta2 = jent_delta(ec->last_delta, current_delta);
ec->last_delta = current_delta;
return delta2;
}
static inline uint64_t jent_delta3(struct rand_data *ec, uint64_t delta2)
{
uint64_t delta3 = jent_delta(ec->last_delta2, delta2);
ec->last_delta2 = delta2;
return delta3;
}
#endif /* JENT_HEALTH_LAG_PREDICTOR */
/***************************************************************************
* Adaptive Proportion Test
*
* This test complies with SP800-90B section 4.4.2.
***************************************************************************/
/*
* See the SP 800-90B comment #10b for the corrected cutoff for the SP 800-90B
* APT.
* http://www.untruth.org/~josh/sp80090b/UL%20SP800-90B-final%20comments%20v1.9%2020191212.pdf
* In in the syntax of R, this is C = 2 + qbinom(1 2^(30), 511, 2^(-1/osr)).
* (The original formula wasn't correct because the first symbol must
* necessarily have been observed, so there is no chance of observing 0 of these
* symbols.)
*
* For any value above 14, this yields the maximal allowable value of 512
* (by FIPS 140-2 IG 7.19 Resolution # 16, we cannot choose a cutoff value that
* renders the test unable to fail).
*/
static const unsigned int jent_apt_cutoff_lookup[15]=
{ 325, 422, 459, 477, 488, 494, 499, 502,
505, 507, 508, 509, 510, 511, 512 };
void jent_apt_init(struct rand_data *ec, unsigned int osr)
{
/*
* Establish the apt_cutoff based on the presumed entropy rate of
* 1/osr.
*/
if (osr >= ARRAY_SIZE(jent_apt_cutoff_lookup)) {
ec->apt_cutoff = jent_apt_cutoff_lookup[
ARRAY_SIZE(jent_apt_cutoff_lookup) - 1];
} else {
ec->apt_cutoff = jent_apt_cutoff_lookup[osr - 1];
}
}
/**
* Reset the APT counter
*
* @ec [in] Reference to entropy collector
*/
static void jent_apt_reset(struct rand_data *ec)
{
/* When reset, accept the _next_ value input as the new base. */
ec->apt_base_set = 0;
}
/**
* Insert a new entropy event into APT
*
* @ec [in] Reference to entropy collector
* @current_delta [in] Current time delta
*/
static void jent_apt_insert(struct rand_data *ec, uint64_t current_delta)
{
/* Initialize the base reference */
if (!ec->apt_base_set) {
ec->apt_base = current_delta; /* APT Step 1 */
ec->apt_base_set = 1; /* APT Step 2 */
/*
* Reset APT counter
* Note that we've taken in the first symbol in the window.
*/
ec->apt_count = 1; /* B = 1 */
ec->apt_observations = 1;
return;
}
if (current_delta == ec->apt_base) {
ec->apt_count++; /* B = B + 1 */
/* Note, ec->apt_count starts with one. */
if (ec->apt_count >= ec->apt_cutoff)
ec->health_failure |= JENT_APT_FAILURE;
}
ec->apt_observations++;
/* Completed one window, the next symbol input will be new apt_base. */
if (ec->apt_observations >= JENT_APT_WINDOW_SIZE)
jent_apt_reset(ec); /* APT Step 4 */
}
/***************************************************************************
* Stuck Test and its use as Repetition Count Test
*
* The Jitter RNG uses an enhanced version of the Repetition Count Test
* (RCT) specified in SP800-90B section 4.4.1. Instead of counting identical
* back-to-back values, the input to the RCT is the counting of the stuck
* values during the generation of one Jitter RNG output block.
*
* The RCT is applied with an alpha of 2^{-30} compliant to FIPS 140-2 IG 9.8.
*
* During the counting operation, the Jitter RNG always calculates the RCT
* cut-off value of C. If that value exceeds the allowed cut-off value,
* the Jitter RNG output block will be calculated completely but discarded at
* the end. The caller of the Jitter RNG is informed with an error code.
***************************************************************************/
/**
* Repetition Count Test as defined in SP800-90B section 4.4.1
*
* @ec [in] Reference to entropy collector
* @stuck [in] Indicator whether the value is stuck
*/
static void jent_rct_insert(struct rand_data *ec, int stuck)
{
/*
* If we have a count less than zero, a previous RCT round identified
* a failure. We will not overwrite it.
*/
if (ec->rct_count < 0)
return;
if (stuck) {
ec->rct_count++;
/*
* The cutoff value is based on the following consideration:
* alpha = 2^-30 as recommended in FIPS 140-2 IG 9.8.
* In addition, we require an entropy value H of 1/osr as this
* is the minimum entropy required to provide full entropy.
* Note, we collect (DATA_SIZE_BITS + ENTROPY_SAFETY_FACTOR)*osr
* deltas for inserting them into the entropy pool which should
* then have (close to) DATA_SIZE_BITS bits of entropy in the
* conditioned output.
*
* Note, ec->rct_count (which equals to value B in the pseudo
* code of SP800-90B section 4.4.1) starts with zero. Hence
* we need to subtract one from the cutoff value as calculated
* following SP800-90B. Thus C = ceil(-log_2(alpha)/H) = 30*osr.
*/
if ((unsigned int)ec->rct_count >= (30 * ec->osr)) {
ec->rct_count = -1;
ec->health_failure |= JENT_RCT_FAILURE;
}
} else {
ec->rct_count = 0;
}
}
/**
* Stuck test by checking the:
* 1st derivative of the jitter measurement (time delta)
* 2nd derivative of the jitter measurement (delta of time deltas)
* 3rd derivative of the jitter measurement (delta of delta of time deltas)
*
* All values must always be non-zero.
*
* @ec [in] Reference to entropy collector
* @current_delta [in] Jitter time delta
*
* @return
* 0 jitter measurement not stuck (good bit)
* 1 jitter measurement stuck (reject bit)
*/
unsigned int jent_stuck(struct rand_data *ec, uint64_t current_delta)
{
uint64_t delta2 = jent_delta2(ec, current_delta);
uint64_t delta3 = jent_delta3(ec, delta2);
/*
* Insert the result of the comparison of two back-to-back time
* deltas.
*/
jent_apt_insert(ec, current_delta);
jent_lag_insert(ec, current_delta);
if (!current_delta || !delta2 || !delta3) {
/* RCT with a stuck bit */
jent_rct_insert(ec, 1);
return 1;
}
/* RCT with a non-stuck bit */
jent_rct_insert(ec, 0);
return 0;
}
/**
* Report any health test failures
*
* @ec [in] Reference to entropy collector
*
* @return a bitmask indicating which tests failed
* 0 No health test failure
* 1 RCT failure
* 2 APT failure
* 4 Lag predictor test failure
*/
unsigned int jent_health_failure(struct rand_data *ec)
{
/* Test is only enabled in FIPS mode */
if (!ec->fips_enabled)
return 0;
if (fips_cb && ec->health_failure) {
fips_cb(ec, ec->health_failure);
}
return ec->health_failure;
}