675 lines
17 KiB
C
675 lines
17 KiB
C
/* Copyright (c) (2018-2020) Apple Inc. All rights reserved.
|
||
*
|
||
* corecrypto is licensed under Apple Inc.’s Internal Use License Agreement (which
|
||
* is contained in the License.txt file distributed with corecrypto) and only to
|
||
* people who accept that license. IMPORTANT: Any license rights granted to you by
|
||
* Apple Inc. (if any) are limited to internal use within your organization only on
|
||
* devices and computers you own or control, for the sole purpose of verifying the
|
||
* security characteristics and correct functioning of the Apple Software. You may
|
||
* not, directly or indirectly, redistribute the Apple Software or any portions thereof.
|
||
*/
|
||
|
||
#include <setjmp.h>
|
||
#include <unistd.h>
|
||
|
||
#include "testmore.h"
|
||
|
||
#include <corecrypto/cc.h>
|
||
#include <corecrypto/cckprng.h>
|
||
#include "cckprng_internal.h"
|
||
#include <corecrypto/ccaes.h>
|
||
#include "testbyteBuffer.h"
|
||
|
||
#if CC_LINUX && !CC_ANDROID
|
||
#define CC_PRNG_SEEDTESTS 0
|
||
#else
|
||
#define CC_PRNG_SEEDTESTS 1
|
||
#endif
|
||
|
||
#include "ccshadow.h"
|
||
|
||
#if CC_PRNG_SEEDTESTS
|
||
|
||
struct file {
|
||
const char *path;
|
||
int fd;
|
||
uint8_t buf[256];
|
||
size_t len;
|
||
size_t pos;
|
||
};
|
||
|
||
static const int seedfd = 3;
|
||
static const int randomfd = 4;
|
||
|
||
static struct file filetab[] = {
|
||
{ .path = CCKPRNG_SEEDFILE, .fd = seedfd },
|
||
{ .path = CCKPRNG_RANDOMDEV, .fd = randomfd },
|
||
};
|
||
|
||
static struct file *seedfile = &filetab[0];
|
||
static struct file *randomdev = &filetab[1];
|
||
|
||
static struct file *file_lookup_path(const char *path)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < 2; i += 1) {
|
||
if (strcmp(path, filetab[i].path) == 0) {
|
||
return &filetab[i];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static struct file *file_lookup_fd(int fd)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < 2; i += 1) {
|
||
if (fd == filetab[i].fd) {
|
||
return &filetab[i];
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static void file_erase(struct file *f)
|
||
{
|
||
cc_clear(sizeof(f->buf), f->buf);
|
||
f->pos = 0;
|
||
f->len = 0;
|
||
}
|
||
|
||
static void file_setup(struct file *f, const void *buf, size_t buflen)
|
||
{
|
||
memcpy(f->buf, buf, buflen);
|
||
f->pos = 0;
|
||
f->len = buflen;
|
||
}
|
||
|
||
static int open_lookup(const char *path, int oflag, mode_t mode)
|
||
{
|
||
(void)oflag;
|
||
(void)mode;
|
||
|
||
return file_lookup_path(path)->fd;
|
||
}
|
||
|
||
static int open_seedfile_fail(const char *path, int oflag, mode_t mode)
|
||
{
|
||
if (strcmp(path, CCKPRNG_SEEDFILE) == 0) {
|
||
return -1;
|
||
}
|
||
|
||
return open_lookup(path, oflag, mode);
|
||
}
|
||
|
||
static int open_randomdev_fail(const char *path, int oflag, mode_t mode)
|
||
{
|
||
if (strcmp(path, CCKPRNG_RANDOMDEV) == 0) {
|
||
return -1;
|
||
}
|
||
|
||
return open_lookup(path, oflag, mode);
|
||
}
|
||
|
||
static int close_nop(int fd)
|
||
{
|
||
(void)fd;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t read_fail(int fd, void *buf, size_t nbytes)
|
||
{
|
||
(void)fd;
|
||
(void)buf;
|
||
(void)nbytes;
|
||
|
||
return -1;
|
||
}
|
||
|
||
static ssize_t write_fail(int fd, const void *buf, size_t nbytes)
|
||
{
|
||
(void)fd;
|
||
(void)buf;
|
||
(void)nbytes;
|
||
|
||
return -1;
|
||
}
|
||
|
||
// The size of this buffer is arbitrary, but it is meant to be larger
|
||
// than and indivisible by CCKPRNG_SEEDSIZE
|
||
static const uint8_t seed[173] = {
|
||
0x14, 0xfb, 0xb6, 0xc1, 0xd3, 0x6d, 0x96, 0x93, 0x99, 0x04, 0x01, 0x41, 0xc9, 0xbe, 0x18, 0xe1, 0x65, 0x9f, 0x4c, 0xc5,
|
||
0x93, 0x00, 0x83, 0xb6, 0x7e, 0x02, 0xf0, 0x50, 0xcb, 0xf1, 0xdc, 0x76, 0xce, 0xa8, 0x44, 0x1c, 0x36, 0x1f, 0xa6, 0x8a,
|
||
0x17, 0x13, 0x3d, 0x5a, 0x94, 0x7d, 0xd1, 0x04, 0x8e, 0xd9, 0xef, 0xc3, 0x95, 0xa0, 0x40, 0x8a, 0xe2, 0xb2, 0xe5, 0x7c,
|
||
0x17, 0x20, 0xa8, 0xbf, 0xd0, 0xc9, 0x8d, 0xb1, 0x18, 0x10, 0x5f, 0x04, 0xac, 0xda, 0xb9, 0x85, 0xc7, 0x9a, 0xfc, 0x40,
|
||
0x9b, 0xf3, 0x64, 0x33, 0xa0, 0x13, 0x3c, 0x1c, 0x29, 0x6e, 0x87, 0xb9, 0x4c, 0xa1, 0xd7, 0xb5, 0x77, 0x83, 0xcb, 0x54,
|
||
0x39, 0x0e, 0x5f, 0x91, 0x5e, 0x1b, 0xd3, 0x74, 0xc7, 0x5a, 0x92, 0x73, 0x84, 0x7f, 0xe2, 0x2c, 0xf3, 0xfe, 0x26, 0xf5,
|
||
0x8d, 0xac, 0x89, 0xff, 0x01, 0x0f, 0x8e, 0x48, 0x20, 0xef, 0x05, 0xe4, 0x45, 0x27, 0x60, 0x77, 0xa7, 0x19, 0xcc, 0x0f,
|
||
0xc1, 0x35, 0xe9, 0xbd, 0x9f, 0x75, 0xb1, 0x2f, 0x4e, 0xba, 0xc2, 0x24, 0xc8, 0x7b, 0x09, 0x3f, 0xd3, 0x3b, 0x2c, 0x67,
|
||
0x52, 0x80, 0xc8, 0x07, 0x62, 0x01, 0x75, 0xdd, 0x70, 0x61, 0xd6, 0xae, 0x1a,
|
||
};
|
||
|
||
static ssize_t read_manybytes(int fd, void *buf, size_t nbytes)
|
||
{
|
||
struct file *f = file_lookup_fd(fd);
|
||
|
||
nbytes = CC_MIN(f->len - f->pos, nbytes);
|
||
memcpy(buf, &f->buf[f->pos], nbytes);
|
||
f->pos += nbytes;
|
||
|
||
return (ssize_t)nbytes;
|
||
}
|
||
|
||
static ssize_t read_onebyte(int fd, void *buf, size_t nbytes)
|
||
{
|
||
(void)nbytes;
|
||
|
||
return read_manybytes(fd, buf, 1);
|
||
}
|
||
|
||
static ssize_t write_manybytes(int fd, const void *buf, size_t nbytes)
|
||
{
|
||
struct file *f = file_lookup_fd(fd);
|
||
|
||
// This should never happen
|
||
if (nbytes > sizeof(f->buf) - f->pos) {
|
||
return -1;
|
||
}
|
||
|
||
memcpy(&f->buf[f->pos], buf, nbytes);
|
||
f->pos += nbytes;
|
||
f->len += nbytes;
|
||
|
||
return (ssize_t)nbytes;
|
||
}
|
||
|
||
static ssize_t write_onebyte(int fd, const void *buf, size_t nbytes)
|
||
{
|
||
(void)nbytes;
|
||
|
||
return write_manybytes(fd, buf, 1);
|
||
}
|
||
|
||
static int cckprng_test_loadseed(void)
|
||
{
|
||
file_setup(seedfile, seed, sizeof(seed));
|
||
file_erase(randomdev);
|
||
|
||
close_mock = close_nop;
|
||
|
||
open_mock = open_seedfile_fail;
|
||
is(cckprng_loadseed(), CCKPRNG_SEEDFILE_OPEN, "cckprng_loadseed open seedfile");
|
||
seedfile->pos = 0;
|
||
|
||
open_mock = open_randomdev_fail;
|
||
is(cckprng_loadseed(), CCKPRNG_RANDOMDEV_OPEN, "cckprng_loadseed open random device");
|
||
seedfile->pos = 0;
|
||
|
||
open_mock = open_lookup;
|
||
read_mock = read_fail;
|
||
is(cckprng_loadseed(), CCKPRNG_SEEDFILE_READ, "cckprng_loadseed read seedfile");
|
||
seedfile->pos = 0;
|
||
|
||
read_mock = read_manybytes;
|
||
write_mock = write_fail;
|
||
is(cckprng_loadseed(), CCKPRNG_RANDOMDEV_WRITE, "cckprng_loadseed write random device");
|
||
seedfile->pos = 0;
|
||
|
||
open_mock = open_lookup;
|
||
read_mock = read_manybytes;
|
||
write_mock = write_manybytes;
|
||
file_erase(randomdev);
|
||
seedfile->pos = 0;
|
||
is(cckprng_loadseed(), CCERR_OK, "cckprng_loadseed ok (many-byte read, many-byte write)");
|
||
is(randomdev->len, seedfile->len, "cckprng_loadseed size (many-byte read, many-byte write)");
|
||
ok_memcmp(seedfile->buf, randomdev->buf, seedfile->len, "cckprng_loadseed match (many-byte read, many-byte write)");
|
||
|
||
read_mock = read_onebyte;
|
||
write_mock = write_manybytes;
|
||
file_erase(randomdev);
|
||
seedfile->pos = 0;
|
||
is(cckprng_loadseed(), CCERR_OK, "cckprng_loadseed ok (one-byte read, many-byte write)");
|
||
is(randomdev->len, seedfile->len, "cckprng_loadseed size (one-byte read, many-byte write)");
|
||
ok_memcmp(seedfile->buf, randomdev->buf, seedfile->len, "cckprng_loadseed match (one-byte read, many-byte write)");
|
||
|
||
read_mock = read_manybytes;
|
||
write_mock = write_onebyte;
|
||
file_erase(randomdev);
|
||
seedfile->pos = 0;
|
||
is(cckprng_loadseed(), CCERR_OK, "cckprng_loadseed ok (many-byte read, one-byte write)");
|
||
is(randomdev->len, seedfile->len, "cckprng_loadseed size (many-byte read, one-byte write)");
|
||
ok_memcmp(seedfile->buf, randomdev->buf, seedfile->len, "cckprng_loadseed match (many-byte read, one-byte write)");
|
||
|
||
read_mock = read_onebyte;
|
||
write_mock = write_onebyte;
|
||
file_erase(randomdev);
|
||
seedfile->pos = 0;
|
||
is(cckprng_loadseed(), CCERR_OK, "cckprng_loadseed ok (one-byte read, one-byte write)");
|
||
is(randomdev->len, seedfile->len, "cckprng_loadseed size (one-byte read, one-byte write)");
|
||
ok_memcmp(seedfile->buf, randomdev->buf, seedfile->len, "cckprng_loadseed match (one-byte read, one-byte write)");
|
||
|
||
open_mock = NULL;
|
||
read_mock = NULL;
|
||
write_mock = NULL;
|
||
close_mock = NULL;
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int getentropy_fail(void *buf, size_t buflen)
|
||
{
|
||
(void)buf;
|
||
(void)buflen;
|
||
|
||
return -1;
|
||
}
|
||
|
||
static int getentropy_seed(void *buf, size_t buflen)
|
||
{
|
||
memcpy(buf, seed, buflen);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int fchmod_fail(int fd, mode_t mode)
|
||
{
|
||
(void)fd;
|
||
(void)mode;
|
||
|
||
return -1;
|
||
}
|
||
|
||
static int fchmod_nop(int fd, mode_t mode)
|
||
{
|
||
(void)fd;
|
||
(void)mode;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int fchown_fail(int fd, uid_t owner, gid_t group)
|
||
{
|
||
(void)fd;
|
||
(void)owner;
|
||
(void)group;
|
||
|
||
return -1;
|
||
}
|
||
|
||
static int fchown_nop(int fd, uid_t owner, gid_t group)
|
||
{
|
||
(void)fd;
|
||
(void)owner;
|
||
(void)group;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int cckprng_test_storeseed(void)
|
||
{
|
||
close_mock = close_nop;
|
||
|
||
getentropy_mock = getentropy_fail;
|
||
is(cckprng_storeseed(), CCKPRNG_GETENTROPY, "cckprng_storeseed get entropy");
|
||
getentropy_mock = getentropy_seed;
|
||
|
||
open_mock = open_seedfile_fail;
|
||
is(cckprng_storeseed(), CCKPRNG_SEEDFILE_OPEN, "cckprng_storeseed open seedfile");
|
||
|
||
open_mock = open_lookup;
|
||
fchmod_mock = fchmod_fail;
|
||
is(cckprng_storeseed(), CCKPRNG_SEEDFILE_CHMOD, "cckprng_storeseed chmod seedfile");
|
||
|
||
fchmod_mock = fchmod_nop;
|
||
fchown_mock = fchown_fail;
|
||
is(cckprng_storeseed(), CCKPRNG_SEEDFILE_CHOWN, "cckprng_storeseed chown seedfile");
|
||
|
||
fchown_mock = fchown_nop;
|
||
write_mock = write_manybytes;
|
||
file_erase(seedfile);
|
||
is(cckprng_storeseed(), CCERR_OK, "cckprng_storeseed ok (many-byte write)");
|
||
is(seedfile->len, CCKPRNG_SEEDSIZE, "cckprng_storeseed size (many-byte write)");
|
||
ok_memcmp(seedfile->buf, seed, seedfile->len, "cckprng_storeseed match (many-byte write)");
|
||
|
||
write_mock = write_onebyte;
|
||
file_erase(seedfile);
|
||
is(cckprng_storeseed(), CCERR_OK, "cckprng_storeseed ok (one-byte write)");
|
||
is(seedfile->len, CCKPRNG_SEEDSIZE, "cckprng_storeseed size (one-byte write)");
|
||
ok_memcmp(seedfile->buf, seed, seedfile->len, "cckprng_storeseed match (one-byte write)");
|
||
|
||
getentropy_mock = NULL;
|
||
open_mock = NULL;
|
||
fchmod_mock = NULL;
|
||
fchown_mock = NULL;
|
||
write_mock = NULL;
|
||
close_mock = NULL;
|
||
|
||
return 1;
|
||
}
|
||
|
||
#endif /* CC_PRNG_SEEDTESTS */
|
||
|
||
/*
|
||
|
||
Test Structures
|
||
|
||
*/
|
||
|
||
enum {
|
||
OP_INIT,
|
||
OP_RESEED,
|
||
OP_REFRESH,
|
||
OP_GENERATE
|
||
};
|
||
|
||
struct cckprng_op {
|
||
unsigned id;
|
||
unsigned kind;
|
||
bool abort;
|
||
};
|
||
|
||
struct cckprng_vector {
|
||
unsigned id;
|
||
const char *note;
|
||
unsigned nops;
|
||
const struct cckprng_op **ops;
|
||
};
|
||
|
||
struct cckprng_op_init {
|
||
struct cckprng_op hd;
|
||
uint8_t seed[32];
|
||
uint8_t nonce[8];
|
||
ccrng_fortuna_getentropy getentropy;
|
||
};
|
||
|
||
struct cckprng_op_reseed {
|
||
struct cckprng_op hd;
|
||
uint64_t nonce;
|
||
size_t seed_nbytes;
|
||
uint8_t seed[512];
|
||
};
|
||
|
||
struct cckprng_op_refresh {
|
||
struct cckprng_op hd;
|
||
uint64_t rand;
|
||
int32_t nsamples;
|
||
bool needreseed;
|
||
};
|
||
|
||
struct cckprng_op_generate {
|
||
struct cckprng_op hd;
|
||
size_t rand_nbytes;
|
||
struct {
|
||
const uint8_t rand[512];
|
||
const uint8_t key[32];
|
||
const uint8_t ctr[16];
|
||
} out;
|
||
};
|
||
|
||
struct kat_ctx {
|
||
struct cckprng_ctx ctx;
|
||
bool reseed;
|
||
uint8_t rand[512];
|
||
int gen_err;
|
||
};
|
||
|
||
typedef void (*process_fn_t)(struct kat_ctx *, const struct cckprng_op *);
|
||
typedef void (*verify_fn_t)(struct kat_ctx *, const struct cckprng_op *);
|
||
|
||
static uint64_t cckprng_reseed_nonce_static;
|
||
static uint64_t cckprng_reseed_nonce_func()
|
||
{
|
||
return cckprng_reseed_nonce_static;
|
||
}
|
||
|
||
#if CC_USE_L4
|
||
#define cckprng_reseed_nonce_mock_func ert_time_now_mock
|
||
#elif CC_KERNEL
|
||
#define cckprng_reseed_nonce_mock_func mach_absolute_time_mock
|
||
#elif CC_DARWIN
|
||
#define cckprng_reseed_nonce_mock_func mach_absolute_time_mock
|
||
#elif defined(_WIN32)
|
||
#define cckprng_reseed_nonce_mock_func cc_absolute_time_mock
|
||
#elif CC_LINUX
|
||
#if CORECRYPTO_SIMULATE_POSIX_ENVIRONMENT
|
||
#define cckprng_reseed_nonce_mock_func mach_absolute_time_mock
|
||
#else
|
||
#define cckprng_reseed_nonce_mock_func cc_absolute_time_mock
|
||
#endif
|
||
#endif
|
||
|
||
|
||
static uint64_t rand_static;
|
||
static bool cc_rdrand_static(uint64_t *rand)
|
||
{
|
||
*rand = rand_static;
|
||
return true;
|
||
}
|
||
|
||
static int32_t fortuna_get_entropy_nsamples;
|
||
static int32_t fortuna_get_entropy_ones(size_t *entropy_nbytes, void *entropy, void *arg)
|
||
{
|
||
(void) arg;
|
||
uint8_t *out = (uint8_t *) entropy;
|
||
for (size_t i = 0; i < *entropy_nbytes; i++) {
|
||
out[i] = 0x01;
|
||
}
|
||
return fortuna_get_entropy_nsamples;
|
||
}
|
||
|
||
/*
|
||
|
||
Process & Verify Functions
|
||
|
||
*/
|
||
|
||
static void process_init(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
const struct cckprng_op_init *o = (const struct cckprng_op_init *)op;
|
||
struct cckprng_ctx *ctx = &kat_ctx->ctx;
|
||
|
||
cckprng_init(ctx, sizeof(o->seed), o->seed, sizeof(o->nonce), o->nonce, o->getentropy, NULL);
|
||
}
|
||
|
||
static void verify_init(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
(void) kat_ctx;
|
||
(void) op;
|
||
}
|
||
|
||
static void process_reseed(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
const struct cckprng_op_reseed *o = (const struct cckprng_op_reseed *)op;
|
||
struct cckprng_ctx *ctx = &kat_ctx->ctx;
|
||
|
||
cckprng_reseed_nonce_static = o->nonce;
|
||
cckprng_reseed(ctx, o->seed_nbytes, o->seed);
|
||
}
|
||
|
||
static void verify_reseed(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
(void) kat_ctx;
|
||
(void) op;
|
||
}
|
||
|
||
static void process_refresh(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
const struct cckprng_op_refresh *o = (const struct cckprng_op_refresh *)op;
|
||
struct cckprng_ctx *ctx = &kat_ctx->ctx;
|
||
|
||
fortuna_get_entropy_nsamples = o->nsamples;
|
||
rand_static = o->rand;
|
||
|
||
cckprng_refresh(ctx);
|
||
}
|
||
|
||
static void verify_refresh(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
const struct cckprng_op_refresh *o = (const struct cckprng_op_refresh *)op;
|
||
struct cckprng_ctx *ctx = &kat_ctx->ctx;
|
||
is(ctx->needreseed, o->needreseed, "refresh needreseed");
|
||
}
|
||
|
||
|
||
static void process_generate(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
const struct cckprng_op_generate *o = (const struct cckprng_op_generate *)op;
|
||
struct cckprng_ctx *ctx = &kat_ctx->ctx;
|
||
|
||
cckprng_generate(ctx, 0, o->rand_nbytes, kat_ctx->rand);
|
||
}
|
||
|
||
static void verify_generate(struct kat_ctx *kat_ctx, const struct cckprng_op *op)
|
||
{
|
||
const struct cckprng_op_generate *o = (const struct cckprng_op_generate *)op;
|
||
|
||
ok_memcmp(kat_ctx->rand, o->out.rand, o->rand_nbytes, "generate rand");
|
||
}
|
||
|
||
/*
|
||
|
||
KAT Functions
|
||
|
||
*/
|
||
|
||
static void cckprng_test_kat(const struct cckprng_vector *vec)
|
||
{
|
||
struct kat_ctx kat_ctx;
|
||
process_fn_t process_fns[] = {
|
||
process_init,
|
||
process_reseed,
|
||
process_refresh,
|
||
process_generate
|
||
};
|
||
verify_fn_t verify_fns[] = {
|
||
verify_init,
|
||
verify_reseed,
|
||
verify_refresh,
|
||
verify_generate
|
||
};
|
||
|
||
cc_clear(sizeof(kat_ctx), &kat_ctx);
|
||
//cc_abort_mock = cc_abort_longjmp; TKTK
|
||
cckprng_reseed_nonce_mock_func = cckprng_reseed_nonce_func;
|
||
cc_rdrand_mock = cc_rdrand_static;
|
||
|
||
for (unsigned i = 0; i < vec->nops; i += 1) {
|
||
const struct cckprng_op *op = vec->ops[i];
|
||
|
||
/* TKTK
|
||
if (setjmp(env)) {
|
||
ok(op->abort, "erroneous abort");
|
||
goto cleanup;
|
||
}
|
||
*/
|
||
|
||
// Reset ephemeral bits of state
|
||
|
||
//kat_ctx.reseed = false;
|
||
cc_clear(sizeof(kat_ctx.rand), kat_ctx.rand);
|
||
|
||
// Process vector
|
||
process_fns[op->kind](&kat_ctx, op);
|
||
|
||
// Verify results
|
||
|
||
/* TKTK
|
||
ok(!op->abort, "no abort");
|
||
if (op->abort) {
|
||
goto cleanup;
|
||
}
|
||
*/
|
||
verify_fns[op->kind](&kat_ctx, op);
|
||
}
|
||
|
||
//ok(diag_eq(&kat_ctx.ctx, vec), "incorrect diagnostics");
|
||
|
||
//cleanup:
|
||
//cc_abort_mock = NULL;
|
||
cckprng_reseed_nonce_mock_func = NULL;
|
||
cc_rdrand_mock = NULL;
|
||
}
|
||
|
||
#include "cckprng_kat.inc"
|
||
|
||
static int cckprng_test_runner() {
|
||
diag("Start Fortuna KAT Tests");
|
||
|
||
for (unsigned i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i += 1) {
|
||
cckprng_test_kat(test_vectors[i]);
|
||
}
|
||
|
||
diag("End Fortuna KAT Tests");
|
||
return 0;
|
||
}
|
||
|
||
#if CC_TSAN
|
||
#include <pthread.h>
|
||
static struct cckprng_ctx tsan_kprng;
|
||
static int32_t cckprng_tsan_get_entropy(size_t *nbytes, void *entropy, void *arg) {
|
||
(void) arg;
|
||
uint8_t *eb = (uint8_t *) entropy;
|
||
for (size_t i = 0; i < *nbytes; i++) {
|
||
eb[i] = i & 0xff;
|
||
}
|
||
return 2048;
|
||
}
|
||
|
||
static void *cckprng_tsan_thread_generate(void *arg) {
|
||
(void) arg;
|
||
uint8_t generate[32] = {0};
|
||
for (int i = 0; i < 10000; i++) {
|
||
cckprng_generate(&tsan_kprng, 0, sizeof(generate), generate);
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
static void *cckprng_tsan_thread_reseed(void *arg) {
|
||
(void) arg;
|
||
uint8_t reseed[32] = {1,2,3,4,5};
|
||
for (int i = 0; i < 10000; i++) {
|
||
cckprng_reseed(&tsan_kprng, sizeof(reseed), reseed);
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
static void cckprng_tsan_test() {
|
||
uint8_t seed[64] = {0};
|
||
uint8_t nonce[32] = {0};
|
||
cckprng_init(&tsan_kprng, sizeof(seed), seed, sizeof(nonce), nonce, cckprng_tsan_get_entropy, NULL);
|
||
|
||
pthread_t t_generate, t_reseed;
|
||
|
||
pthread_create(&t_generate, NULL, cckprng_tsan_thread_generate, NULL);
|
||
pthread_create(&t_reseed, NULL, cckprng_tsan_thread_reseed, NULL);
|
||
|
||
pthread_join(t_generate, NULL);
|
||
pthread_join(t_reseed, NULL);
|
||
}
|
||
#endif
|
||
|
||
int cckprng_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv)
|
||
{
|
||
plan_tests(4030);
|
||
|
||
#if CC_PRNG_SEEDTESTS
|
||
ok(cckprng_test_loadseed(), "cckprng_test_loadseed");
|
||
ok(cckprng_test_storeseed(), "cckprng_test_storeseed");
|
||
#endif /* CC_PRNG_SEEDTESTS */
|
||
|
||
cckprng_test_runner();
|
||
|
||
#if CC_TSAN
|
||
diag("Start KPRNG TSAN Tests");
|
||
cckprng_tsan_test();
|
||
diag("End KPRNG TSAN Tests");
|
||
#endif
|
||
|
||
return 0;
|
||
}
|