Skip to content

Commit

Permalink
Merge bitcoin-core/secp256k1#1298: Remove randomness tests
Browse files Browse the repository at this point in the history
6ec3731 Simplify test PRNG implementation (Pieter Wuille)
fb5bfa4 Add static test vector for Xoshiro256++ (Tim Ruffing)
723e8ca Remove randomness tests (Pieter Wuille)

Pull request description:

ACKs for top commit:
  real-or-random:
    utACK 6ec3731
  jonasnick:
    ACK 6ec3731

Tree-SHA512: 4cbbb9c42e31f067b17dd9169ae5d5e68bce77d1253452db9df523d3be2b5d61002d5a4203e5a153f257ec63c5ff2113555743eeb402d4b6c573069ea494d407
  • Loading branch information
jonasnick committed Jul 18, 2023
2 parents b40e2d3 + 6ec3731 commit c545fdc
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 124 deletions.
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 @@ -181,6 +181,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 @@ -824,78 +853,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 @@ -7735,6 +7692,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 @@ -7772,10 +7732,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

0 comments on commit c545fdc

Please sign in to comment.