Принудительно заставить RNG openssl возвращать повторяемую последовательность байтов
Для модульных тестов криптографической утилиты я хотел бы иметь возможность использовать криптографический генератор случайных чисел OpenSSL (оба RAND_bytes
а также RAND_pseudo_bytes
) возвращать предсказуемые, повторяемые последовательности байтов, так что различные шифротексты в свою очередь предсказуемы и могут быть встроены в тестовые векторы. (Все остальные ключевые материалы находятся под моим контролем.)
Я знаю, что это полностью побеждает безопасность. Это будет использоваться только для юнит-тестов.
Я не могу просто позвонить RAND_seed
с фиксированным семенем перед каждым испытанием, потому что (кажется) ГСЧ автоматически высевает себя из /dev/urandom
хочу ли я этого или нет, и в любом случае RAND_seed
не сбрасывает ГСЧ, он только добавляет семя в пул энтропии.
Есть какой-либо способ сделать это? (В крайнем случае, похоже, я мог бы написать свой собственный движок PRNG, но я хотел бы думать, что есть более простой вариант.)
2 ответа
Вы можете принудительно включить RNG FIPS ANSI X9.31 в тестовый режим во время выполнения, но не использовать SSLeay RNG (по умолчанию). Если вы перекомпилируете OpenSSL с -DPREDICT
ГСЧ по умолчанию выведет предсказуемую последовательность чисел, но это не очень удобно.
RAND_pseudo_bytes
Функция генерирует предсказуемый ряд чисел, то есть она не добавляет энтропию к себе автоматически, как RAND_bytes
, Но, как вы заметили, можно только добавить энтропию в начальное число, но не указывать начальное в явном виде, поэтому между запусками программы вы получите разные числа. Также не полезно.
Но написание собственного предсказуемого двигателя ГСЧ не сложно. На самом деле, я проведу вас через это, сделав рэнд-движок с помощью stdlib rand()
в его ядре:
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <openssl/rand.h>
// These don't need to do anything if you don't have anything for them to do.
static void stdlib_rand_cleanup() {}
static void stdlib_rand_add(const void *buf, int num, double add_entropy) {}
static int stdlib_rand_status() { return 1; }
// Seed the RNG. srand() takes an unsigned int, so we just use the first
// sizeof(unsigned int) bytes in the buffer to seed the RNG.
static void stdlib_rand_seed(const void *buf, int num)
{
assert(num >= sizeof(unsigned int));
srand( *((unsigned int *) buf) );
}
// Fill the buffer with random bytes. For each byte in the buffer, we generate
// a random number and clamp it to the range of a byte, 0-255.
static int stdlib_rand_bytes(unsigned char *buf, int num)
{
for( int index = 0; index < num; ++index )
{
buf[index] = rand() % 256;
}
return 1;
}
// Create the table that will link OpenSSL's rand API to our functions.
RAND_METHOD stdlib_rand_meth = {
stdlib_rand_seed,
stdlib_rand_bytes,
stdlib_rand_cleanup,
stdlib_rand_add,
stdlib_rand_bytes,
stdlib_rand_status
};
// This is a public-scope accessor method for our table.
RAND_METHOD *RAND_stdlib() { return &stdlib_rand_meth; }
int main()
{
// If we're in test mode, tell OpenSSL to use our special RNG. If we
// don't call this function, OpenSSL uses the SSLeay RNG.
int test_mode = 1;
if( test_mode )
{
RAND_set_rand_method(RAND_stdlib());
}
unsigned int seed = 0x00beef00;
unsigned int rnum[5];
RAND_seed(&seed, sizeof(seed));
RAND_bytes((unsigned char *)&rnum[0], sizeof(rnum));
printf("%u %u %u %u %u\n", rnum[0], rnum[1], rnum[2], rnum[3], rnum[4]);
return 0;
}
Каждый раз, когда вы запускаете эту программу, она сеет srand()
с тем же номером и, следовательно, каждый раз дает вам одну и ту же последовательность случайных чисел.
corruptor:scratch indiv$ g++ rand.cpp -o r -lcrypto -g
corruptor:scratch indiv$ ./r
1547399009 981369121 2368920148 925292993 788088604
corruptor:scratch indiv$ ./r
1547399009 981369121 2368920148 925292993 788088604
corruptor:scratch indiv$
Напишите обертку вокруг библиотеки. Затем замените его во время теста на ваш собственный макет, который возвращает ваши магические значения.
Помните, что в модульном тесте вы не пытаетесь протестировать OpenSSL. Вы пытаетесь проверить свой код.