Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 17 additions & 48 deletions src/testrand_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
#include "util.h"

static uint64_t secp256k1_test_state[4];
static uint64_t secp256k1_test_rng_integer;
static int secp256k1_test_rng_integer_bits_left = 0;

SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) {
static const unsigned char PREFIX[19] = "secp256k1 test init";
Expand All @@ -36,7 +34,6 @@ SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16
for (j = 0; j < 8; ++j) s = (s << 8) | out32[8*i + j];
secp256k1_test_state[i] = s;
}
secp256k1_test_rng_integer_bits_left = 0;
}

SECP256K1_INLINE static uint64_t rotl(const uint64_t x, int k) {
Expand All @@ -57,58 +54,30 @@ SECP256K1_INLINE static uint64_t secp256k1_testrand64(void) {
}

SECP256K1_INLINE static uint64_t secp256k1_testrand_bits(int bits) {
uint64_t ret;
if (secp256k1_test_rng_integer_bits_left < bits) {
secp256k1_test_rng_integer = secp256k1_testrand64();
secp256k1_test_rng_integer_bits_left = 64;
}
ret = secp256k1_test_rng_integer;
secp256k1_test_rng_integer >>= bits;
secp256k1_test_rng_integer_bits_left -= bits;
ret &= ((~((uint64_t)0)) >> (64 - bits));
return ret;
if (bits == 0) return 0;
return secp256k1_testrand64() >> (64 - bits);
}

SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) {
return secp256k1_testrand_bits(32);
return secp256k1_testrand64() >> 32;
}

static uint32_t secp256k1_testrand_int(uint32_t range) {
/* We want a uniform integer between 0 and range-1, inclusive.
* B is the smallest number such that range <= 2**B.
* two mechanisms implemented here:
* - generate B bits numbers until one below range is found, and return it
* - find the largest multiple M of range that is <= 2**(B+A), generate B+A
* bits numbers until one below M is found, and return it modulo range
* The second mechanism consumes A more bits of entropy in every iteration,
* but may need fewer iterations due to M being closer to 2**(B+A) then
* range is to 2**B. The array below (indexed by B) contains a 0 when the
* first mechanism is to be used, and the number A otherwise.
*/
static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0};
uint32_t trange, mult;
int bits = 0;
if (range <= 1) {
return 0;
}
trange = range - 1;
while (trange > 0) {
trange >>= 1;
bits++;
uint32_t mask = 0;
uint32_t range_copy;
/* Reduce range by 1, changing its meaning to "maximum value". */
VERIFY_CHECK(range != 0);
range -= 1;
/* Count the number of bits in range. */
range_copy = range;
while (range_copy) {
mask = (mask << 1) | 1U;
range_copy >>= 1;
}
if (addbits[bits]) {
bits = bits + addbits[bits];
mult = ((~((uint32_t)0)) >> (32 - bits)) / range;
trange = range * mult;
} else {
trange = range;
mult = 1;
}
while(1) {
uint32_t x = secp256k1_testrand_bits(bits);
if (x < trange) {
return (mult == 1) ? x : (x % range);
}
/* Generation loop. */
while (1) {
uint32_t val = secp256k1_testrand64() & mask;
if (val <= range) return val;
}
}

Expand Down
108 changes: 32 additions & 76 deletions src/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,35 @@ static void random_scalar_order_b32(unsigned char *b32) {
secp256k1_scalar_get_b32(b32, &num);
}

static void run_xoshiro256pp_tests(void) {
{
size_t i;
/* Sanity check that we run before the actual seeding. */
for (i = 0; i < sizeof(secp256k1_test_state)/sizeof(secp256k1_test_state[0]); i++) {
CHECK(secp256k1_test_state[i] == 0);
}
}
{
int i;
unsigned char buf32[32];
unsigned char seed16[16] = {
'C', 'H', 'I', 'C', 'K', 'E', 'N', '!',
'C', 'H', 'I', 'C', 'K', 'E', 'N', '!',
};
unsigned char buf32_expected[32] = {
0xAF, 0xCC, 0xA9, 0x16, 0xB5, 0x6C, 0xE3, 0xF0,
0x44, 0x3F, 0x45, 0xE0, 0x47, 0xA5, 0x08, 0x36,
0x4C, 0xCC, 0xC1, 0x18, 0xB2, 0xD8, 0x8F, 0xEF,
0x43, 0x26, 0x15, 0x57, 0x37, 0x00, 0xEF, 0x30,
};
secp256k1_testrand_seed(seed16);
for (i = 0; i < 17; i++) {
secp256k1_testrand256(buf32);
}
CHECK(secp256k1_memcmp_var(buf32, buf32_expected, sizeof(buf32)) == 0);
}
}

static void run_selftest_tests(void) {
/* Test public API */
secp256k1_selftest();
Expand Down Expand Up @@ -804,78 +833,6 @@ static void run_tagged_sha256_tests(void) {
CHECK(secp256k1_memcmp_var(hash32, hash_expected, sizeof(hash32)) == 0);
}

/***** RANDOM TESTS *****/

static void test_rand_bits(int rand32, int bits) {
/* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to
* get a false negative chance below once in a billion */
static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316};
/* We try multiplying the results with various odd numbers, which shouldn't
* influence the uniform distribution modulo a power of 2. */
static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011};
/* We only select up to 6 bits from the output to analyse */
unsigned int usebits = bits > 6 ? 6 : bits;
unsigned int maxshift = bits - usebits;
/* For each of the maxshift+1 usebits-bit sequences inside a bits-bit
number, track all observed outcomes, one per bit in a uint64_t. */
uint64_t x[6][27] = {{0}};
unsigned int i, shift, m;
/* Multiply the output of all rand calls with the odd number m, which
should not change the uniformity of its distribution. */
for (i = 0; i < rounds[usebits]; i++) {
uint32_t r = (rand32 ? secp256k1_testrand32() : secp256k1_testrand_bits(bits));
CHECK((((uint64_t)r) >> bits) == 0);
for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) {
uint32_t rm = r * mults[m];
for (shift = 0; shift <= maxshift; shift++) {
x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1)));
}
}
}
for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) {
for (shift = 0; shift <= maxshift; shift++) {
/* Test that the lower usebits bits of x[shift] are 1 */
CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0);
}
}
}

/* Subrange must be a whole divisor of range, and at most 64 */
static void test_rand_int(uint32_t range, uint32_t subrange) {
/* (1-1/subrange)^rounds < 1/10^9 */
int rounds = (subrange * 2073) / 100;
int i;
uint64_t x = 0;
CHECK((range % subrange) == 0);
for (i = 0; i < rounds; i++) {
uint32_t r = secp256k1_testrand_int(range);
CHECK(r < range);
r = r % subrange;
x |= (((uint64_t)1) << r);
}
/* Test that the lower subrange bits of x are 1. */
CHECK(((~x) << (64 - subrange)) == 0);
}

static void run_rand_bits(void) {
size_t b;
test_rand_bits(1, 32);
for (b = 1; b <= 32; b++) {
test_rand_bits(0, b);
}
}

static void run_rand_int(void) {
static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432};
static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64};
unsigned int m, s;
for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) {
for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) {
test_rand_int(ms[m] * ss[s], ss[s]);
}
}
}

/***** MODINV TESTS *****/

/* Compute the modular inverse of (odd) x mod 2^64. */
Expand Down Expand Up @@ -7693,6 +7650,9 @@ int main(int argc, char **argv) {
}
printf("test count = %i\n", COUNT);

/* run test RNG tests (must run before we really initialize the test RNG) */
run_xoshiro256pp_tests();

/* find random seed */
secp256k1_testrand_init(argc > 2 ? argv[2] : NULL);

Expand Down Expand Up @@ -7730,10 +7690,6 @@ int main(int argc, char **argv) {
/* scratch tests */
run_scratch_tests();

/* randomness tests */
run_rand_bits();
run_rand_int();

/* integer arithmetic tests */
#ifdef SECP256K1_WIDEMUL_INT128
run_int128_tests();
Expand Down