1
1
espelhamento de https://github.com/henrydcase/pqc.git sincronizado 2024-11-22 15:39:07 +00:00
pqcrypto/3rd/jitterentropy/jitterentropy-health.c
2023-01-27 22:49:29 +00:00

458 linhas
14 KiB
C
Original Anotar Histórico

This file contains ambiguous Unicode characters

Este arquivo contém caracteres Unicode que podem ser confundidos com outros caracteres. Se você acha que isso é intencional, pode ignorar esse aviso com segurança. Use o botão Escapar para revelá-los invisible_runes_line=`Esta linha tem caracteres unicode invisíveis

/* 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;
}