Add secure_rand_below function

It works by masking the numbers and the using rejection sampling to
ensure a uniform generation.
This commit is contained in:
heinrich5991 2021-03-13 16:52:35 +01:00
parent 22ef342ffc
commit 782c826381
5 changed files with 79 additions and 0 deletions

View file

@ -2203,6 +2203,7 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST)
netaddr.cpp netaddr.cpp
packer.cpp packer.cpp
prng.cpp prng.cpp
secure_random.cpp
sorted_array.cpp sorted_array.cpp
str.cpp str.cpp
strip_path_and_extension.cpp strip_path_and_extension.cpp

View file

@ -3608,6 +3608,34 @@ int secure_rand(void)
return (int)(i % RAND_MAX); return (int)(i % RAND_MAX);
} }
// From https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2.
static unsigned int find_next_power_of_two_minus_one(unsigned int n)
{
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 4;
n |= n >> 16;
return n;
}
int secure_rand_below(int below)
{
unsigned int mask = find_next_power_of_two_minus_one(below);
dbg_assert(below > 0, "below must be positive");
while(1)
{
unsigned int n;
secure_random_fill(&n, sizeof(n));
n &= mask;
if((int)n < below)
{
return n;
}
}
}
#if defined(__cplusplus) #if defined(__cplusplus)
} }
#endif #endif

View file

@ -2193,6 +2193,16 @@ void secure_random_fill(void *bytes, unsigned length);
*/ */
int secure_rand(void); int secure_rand(void);
/*
Function: secure_rand_below
Returns a random nonnegative integer below the given number,
with a uniform distribution.
Parameters:
below - Upper limit (exclusive) of integers to return.
*/
int secure_rand_below(int below);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -0,0 +1,35 @@
#include "test.h"
#include <gtest/gtest.h>
#include <base/system.h>
TEST(SecureRandom, Fill)
{
unsigned int Bits = 0;
while(~Bits)
{
unsigned int Random;
secure_random_fill(&Random, sizeof(Random));
Bits |= Random;
}
}
TEST(SecureRandom, Below1)
{
EXPECT_EQ(secure_rand_below(1), 0);
}
TEST(SecureRandom, Below)
{
int BOUNDS[] = {2, 3, 4, 5, 10, 100, 127, 128, 129};
for(int i = 0; i < sizeof(BOUNDS) / sizeof(BOUNDS[0]); i++)
{
int Below = BOUNDS[i];
for(int i = 0; i < 10; i++)
{
int Random = secure_rand_below(Below);
EXPECT_GE(Random, 0);
EXPECT_LT(Random, Below);
}
}
}

View file

@ -15,5 +15,10 @@ int main(int argc, char **argv)
{ {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
net_init(); net_init();
if(secure_random_init())
{
fprintf(stderr, "random init failed\n");
return 1;
}
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }