의 미러
https://github.com/henrydcase/pqc.git
synced 2024-11-26 01:11:25 +00:00
458 lines
14 KiB
C
458 lines
14 KiB
C
|
/* 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;
|
|||
|
}
|