mirror of
https://github.com/henrydcase/pqc.git
synced 2024-11-26 09:21:28 +00:00
186 lines
4.4 KiB
C
186 lines
4.4 KiB
C
/* Jitter RNG: GCD health test
|
|
*
|
|
* 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.h"
|
|
#include "jitterentropy-gcd.h"
|
|
|
|
/* The common divisor for all timestamp deltas */
|
|
static uint64_t jent_common_timer_gcd = 0;
|
|
|
|
static inline int jent_gcd_tested(void)
|
|
{
|
|
return (jent_common_timer_gcd != 0);
|
|
}
|
|
|
|
/* A straight forward implementation of the Euclidean algorithm for GCD. */
|
|
static inline uint64_t jent_gcd64(uint64_t a, uint64_t b)
|
|
{
|
|
/* Make a greater a than or equal b. */
|
|
if (a < b) {
|
|
uint64_t c = a;
|
|
|
|
a = b;
|
|
b = c;
|
|
}
|
|
|
|
/* Now perform the standard inner-loop for this algorithm.*/
|
|
while (b != 0) {
|
|
uint64_t r;
|
|
|
|
r = a % b;
|
|
|
|
a = b;
|
|
b = r;
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
static int jent_gcd_analyze_internal(uint64_t *delta_history, size_t nelem,
|
|
uint64_t *running_gcd_out,
|
|
uint64_t *delta_sum_out)
|
|
{
|
|
uint64_t running_gcd, delta_sum = 0;
|
|
size_t i;
|
|
|
|
if (!delta_history)
|
|
return -EAGAIN;
|
|
|
|
running_gcd = delta_history[0];
|
|
|
|
/* Now perform the analysis on the accumulated delta data. */
|
|
for (i = 1; i < nelem; i++) {
|
|
/*
|
|
* ensure that we have a varying delta timer which is necessary
|
|
* for the calculation of entropy -- perform this check
|
|
* only after the first loop is executed as we need to prime
|
|
* the old_data value
|
|
*/
|
|
if (delta_history[i] >= delta_history[i - 1])
|
|
delta_sum += delta_history[i] - delta_history[i - 1];
|
|
else
|
|
delta_sum += delta_history[i - 1] - delta_history[i];
|
|
|
|
/*
|
|
* This calculates the gcd of all the delta values. that is
|
|
* gcd(delta_1, delta_2, ..., delta_nelem)
|
|
|
|
* Some timers increment by a fixed (non-1) amount each step.
|
|
* This code checks for such increments, and allows the library
|
|
* to output the number of such changes have occurred.
|
|
*/
|
|
running_gcd = jent_gcd64(delta_history[i], running_gcd);
|
|
}
|
|
|
|
*running_gcd_out = running_gcd;
|
|
*delta_sum_out = delta_sum;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int jent_gcd_analyze(uint64_t *delta_history, size_t nelem)
|
|
{
|
|
uint64_t running_gcd, delta_sum;
|
|
int ret = jent_gcd_analyze_internal(delta_history, nelem, &running_gcd,
|
|
&delta_sum);
|
|
|
|
if (ret == -EAGAIN)
|
|
return 0;
|
|
|
|
/*
|
|
* Variations of deltas of time must on average be larger than 1 to
|
|
* ensure the entropy estimation implied with 1 is preserved.
|
|
*/
|
|
if (delta_sum <= nelem - 1) {
|
|
ret = EMINVARVAR;
|
|
goto out;
|
|
}
|
|
|
|
/* Set a sensible maximum value. */
|
|
if (running_gcd >= UINT32_MAX / 2) {
|
|
ret = ECOARSETIME;
|
|
goto out;
|
|
}
|
|
|
|
/* Adjust all deltas by the observed (small) common factor. */
|
|
if (!jent_gcd_tested())
|
|
jent_common_timer_gcd = running_gcd;
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
uint64_t *jent_gcd_init(size_t nelem)
|
|
{
|
|
uint64_t *delta_history;
|
|
|
|
delta_history = jent_zalloc(nelem * sizeof(uint64_t));
|
|
if (!delta_history)
|
|
return NULL;
|
|
|
|
return delta_history;
|
|
}
|
|
|
|
void jent_gcd_fini(uint64_t *delta_history, size_t nelem)
|
|
{
|
|
if (delta_history)
|
|
jent_zfree(delta_history,
|
|
(unsigned int)(nelem * sizeof(uint64_t)));
|
|
}
|
|
|
|
int jent_gcd_get(uint64_t *value)
|
|
{
|
|
if (!jent_gcd_tested())
|
|
return 1;
|
|
|
|
*value = jent_common_timer_gcd;
|
|
return 0;
|
|
}
|
|
|
|
int jent_gcd_selftest(void)
|
|
{
|
|
#define JENT_GCD_SELFTEST_ELEM 10
|
|
#define JENT_GCD_SELFTEST_EXP 3ULL
|
|
uint64_t *gcd = jent_gcd_init(JENT_GCD_SELFTEST_ELEM);
|
|
uint64_t running_gcd, delta_sum;
|
|
unsigned int i;
|
|
int ret = EGCD;
|
|
|
|
if (!gcd)
|
|
return EMEM;
|
|
|
|
for (i = 0; i < JENT_GCD_SELFTEST_ELEM; i++)
|
|
jent_gcd_add_value(gcd, i * JENT_GCD_SELFTEST_EXP, i);
|
|
|
|
if (jent_gcd_analyze_internal(gcd, JENT_GCD_SELFTEST_ELEM,
|
|
&running_gcd, &delta_sum))
|
|
goto out;
|
|
|
|
if (running_gcd != JENT_GCD_SELFTEST_EXP)
|
|
goto out;
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
jent_gcd_fini(gcd, JENT_GCD_SELFTEST_ELEM);
|
|
return ret;
|
|
}
|