Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/random/fortuna.c
Show First 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | |||||
#ifdef _KERNEL | #ifdef _KERNEL | ||||
static struct sysctl_ctx_list random_clist; | static struct sysctl_ctx_list random_clist; | ||||
RANDOM_CHECK_UINT(fs_minpoolsize, RANDOM_FORTUNA_MINPOOLSIZE, RANDOM_FORTUNA_MAXPOOLSIZE); | RANDOM_CHECK_UINT(fs_minpoolsize, RANDOM_FORTUNA_MINPOOLSIZE, RANDOM_FORTUNA_MAXPOOLSIZE); | ||||
#else | #else | ||||
static uint8_t zero_region[RANDOM_ZERO_BLOCKSIZE]; | static uint8_t zero_region[RANDOM_ZERO_BLOCKSIZE]; | ||||
#endif | #endif | ||||
static void random_fortuna_pre_read(void); | static void random_fortuna_pre_read(void); | ||||
static void random_fortuna_read(uint8_t *, u_int); | static void random_fortuna_read(uint8_t *, size_t); | ||||
static bool random_fortuna_seeded(void); | static bool random_fortuna_seeded(void); | ||||
static bool random_fortuna_seeded_internal(void); | static bool random_fortuna_seeded_internal(void); | ||||
static void random_fortuna_process_event(struct harvest_event *); | static void random_fortuna_process_event(struct harvest_event *); | ||||
static void random_fortuna_init_alg(void *); | static void random_fortuna_init_alg(void *); | ||||
static void random_fortuna_deinit_alg(void *); | static void random_fortuna_deinit_alg(void *); | ||||
static void random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount); | static void random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount); | ||||
▲ Show 20 Lines • Show All 161 Lines • ▼ Show 20 Lines | random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount) | ||||
explicit_bzero(hash, sizeof(hash)); | explicit_bzero(hash, sizeof(hash)); | ||||
/* Unblock the device if this is the first time we are reseeding. */ | /* Unblock the device if this is the first time we are reseeding. */ | ||||
if (uint128_is_zero(fortuna_state.fs_counter)) | if (uint128_is_zero(fortuna_state.fs_counter)) | ||||
randomdev_unblock(); | randomdev_unblock(); | ||||
uint128_increment(&fortuna_state.fs_counter); | uint128_increment(&fortuna_state.fs_counter); | ||||
} | } | ||||
/*- | /*- | ||||
* FS&K - GenerateBlocks() | * FS&K - PseudoRandomData() | ||||
* Generate a number of complete blocks of random output. | * | ||||
* If Chacha20 is used, output size is unrestricted. If AES-CTR is used, | |||||
* output size MUST be <= 1MB and a multiple of RANDOM_BLOCKSIZE. The | |||||
* reasoning for this is discussed in FS&K 9.4; the significant distinction | |||||
* between the two ciphers is that AES has a *block* size of 128 bits while | |||||
* Chacha has a *block* size of 512 bits. | |||||
*/ | */ | ||||
static __inline void | static __inline void | ||||
random_fortuna_genblocks(uint8_t *buf, u_int blockcount) | random_fortuna_genrandom(uint8_t *buf, size_t bytecount) | ||||
{ | { | ||||
uint8_t newkey[RANDOM_KEYSIZE]; | |||||
RANDOM_RESEED_ASSERT_LOCK_OWNED(); | RANDOM_RESEED_ASSERT_LOCK_OWNED(); | ||||
KASSERT(!uint128_is_zero(fortuna_state.fs_counter), ("FS&K: C != 0")); | |||||
/* | |||||
* Fills buf with RANDOM_BLOCKSIZE * blockcount bytes of keystream. | |||||
* Increments fs_counter as it goes. | |||||
*/ | |||||
randomdev_keystream(&fortuna_state.fs_key, &fortuna_state.fs_counter, | |||||
buf, blockcount); | |||||
} | |||||
/*- | /*- | ||||
* FS&K - PseudoRandomData() | * FS&K - assert(n < 2^20 (== 1 MB)) when 128-bit block cipher is used | ||||
* This generates no more than 2^20 bytes of data, and cleans up its | |||||
* internal state when finished. It is assumed that a whole number of | |||||
* blocks are available for writing; any excess generated will be | |||||
* ignored. | |||||
*/ | |||||
static __inline void | |||||
random_fortuna_genrandom(uint8_t *buf, u_int bytecount) | |||||
{ | |||||
uint8_t temp[RANDOM_BLOCKSIZE * RANDOM_KEYS_PER_BLOCK]; | |||||
u_int blockcount; | |||||
RANDOM_RESEED_ASSERT_LOCK_OWNED(); | |||||
/*- | |||||
* FS&K - assert(n < 2^20 (== 1 MB) | |||||
* - r = first-n-bytes(GenerateBlocks(ceil(n/16))) | * - r = first-n-bytes(GenerateBlocks(ceil(n/16))) | ||||
* - K = GenerateBlocks(2) | * - K = GenerateBlocks(2) | ||||
*/ | */ | ||||
KASSERT((bytecount <= RANDOM_FORTUNA_MAX_READ), ("invalid single read request to Fortuna of %d bytes", bytecount)); | KASSERT(random_chachamode || bytecount <= RANDOM_FORTUNA_MAX_READ, | ||||
blockcount = howmany(bytecount, RANDOM_BLOCKSIZE); | ("%s: invalid large read request: %zu bytes", __func__, | ||||
random_fortuna_genblocks(buf, blockcount); | bytecount)); | ||||
random_fortuna_genblocks(temp, RANDOM_KEYS_PER_BLOCK); | |||||
randomdev_encrypt_init(&fortuna_state.fs_key, temp); | /* | ||||
explicit_bzero(temp, sizeof(temp)); | * This is where FS&K would invoke GenerateBlocks(). GenerateBlocks() | ||||
* doesn't make a lot of sense or have much value if we use bytecount | |||||
* for the API (which is useful for ciphers that do not require | |||||
* block-sized output, like Chacha20). | |||||
* | |||||
* Just invoke our PRF abstraction directly, which is responsible for | |||||
* updating fs_counter ('C'). | |||||
*/ | |||||
randomdev_keystream(&fortuna_state.fs_key, &fortuna_state.fs_counter, | |||||
buf, bytecount); | |||||
randomdev_keystream(&fortuna_state.fs_key, &fortuna_state.fs_counter, | |||||
newkey, sizeof(newkey)); | |||||
randomdev_encrypt_init(&fortuna_state.fs_key, newkey); | |||||
explicit_bzero(newkey, sizeof(newkey)); | |||||
} | } | ||||
/*- | /*- | ||||
* FS&K - RandomData() (Part 1) | * FS&K - RandomData() (Part 1) | ||||
* Used to return processed entropy from the PRNG. There is a pre_read | * Used to return processed entropy from the PRNG. There is a pre_read | ||||
* required to be present (but it can be a stub) in order to allow | * required to be present (but it can be a stub) in order to allow | ||||
* specific actions at the begin of the read. | * specific actions at the begin of the read. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | #endif | ||||
explicit_bzero(s, sizeof(s)); | explicit_bzero(s, sizeof(s)); | ||||
explicit_bzero(temp, sizeof(temp)); | explicit_bzero(temp, sizeof(temp)); | ||||
} | } | ||||
/*- | /*- | ||||
* FS&K - RandomData() (Part 2) | * FS&K - RandomData() (Part 2) | ||||
* Main read from Fortuna, continued. May be called multiple times after | * Main read from Fortuna, continued. May be called multiple times after | ||||
* the random_fortuna_pre_read() above. | * the random_fortuna_pre_read() above. | ||||
* The supplied buf MUST be a multiple of RANDOM_BLOCKSIZE in size. | * | ||||
* Lots of code presumes this for efficiency, both here and in other | * The supplied buf MAY not be a multiple of RANDOM_BLOCKSIZE in size; it is | ||||
* routines. You are NOT allowed to break this! | * the responsibility of the algorithm to accommodate partial block reads, if a | ||||
* block output mode is used. | |||||
*/ | */ | ||||
void | void | ||||
random_fortuna_read(uint8_t *buf, u_int bytecount) | random_fortuna_read(uint8_t *buf, size_t bytecount) | ||||
{ | { | ||||
uint8_t remainder_buf[RANDOM_BLOCKSIZE]; | |||||
size_t read_directly_len, read_chunk; | |||||
KASSERT((bytecount % RANDOM_BLOCKSIZE) == 0, ("%s(): bytecount (= %d) must be a multiple of %d", __func__, bytecount, RANDOM_BLOCKSIZE )); | /* | ||||
* The underlying AES generator expects multiples of RANDOM_BLOCKSIZE. | |||||
*/ | |||||
if (random_chachamode) | |||||
read_directly_len = bytecount; | |||||
else | |||||
read_directly_len = rounddown(bytecount, RANDOM_BLOCKSIZE); | |||||
RANDOM_RESEED_LOCK(); | RANDOM_RESEED_LOCK(); | ||||
random_fortuna_genrandom(buf, bytecount); | KASSERT(!uint128_is_zero(fortuna_state.fs_counter), ("FS&K: C != 0")); | ||||
while (read_directly_len > 0) { | |||||
/* | |||||
* 128-bit block ciphers like AES must be re-keyed at 1MB | |||||
* intervals to avoid unacceptable statistical differentiation | |||||
* from true random data. | |||||
* | |||||
* 512-bit block ciphers like Chacha20 do not have this | |||||
* problem. (FS&K 9.4) | |||||
*/ | |||||
if (random_chachamode) | |||||
read_chunk = read_directly_len; | |||||
else | |||||
read_chunk = MIN(read_directly_len, | |||||
RANDOM_FORTUNA_MAX_READ); | |||||
/* | |||||
* For now, we hold the global Fortuna mutex, so yield | |||||
* periodically to provide vague availability to other lock | |||||
* users. PAGE_SIZE is chosen to match existing behavior. | |||||
*/ | |||||
read_chunk = MIN(read_chunk, PAGE_SIZE); | |||||
random_fortuna_genrandom(buf, read_chunk); | |||||
buf += read_chunk; | |||||
read_directly_len -= read_chunk; | |||||
bytecount -= read_chunk; | |||||
/* Perform the actual yield. */ | |||||
if (read_directly_len != 0) { | |||||
RANDOM_RESEED_UNLOCK(); | RANDOM_RESEED_UNLOCK(); | ||||
RANDOM_RESEED_LOCK(); | |||||
} | |||||
} | |||||
if (bytecount > 0) | |||||
random_fortuna_genrandom(remainder_buf, sizeof(remainder_buf)); | |||||
RANDOM_RESEED_UNLOCK(); | |||||
if (bytecount > 0) { | |||||
memcpy(buf, remainder_buf, bytecount); | |||||
explicit_bzero(remainder_buf, sizeof(remainder_buf)); | |||||
} | |||||
} | } | ||||
#ifdef _KERNEL | #ifdef _KERNEL | ||||
static bool block_seeded_status = false; | static bool block_seeded_status = false; | ||||
SYSCTL_BOOL(_kern_random, OID_AUTO, block_seeded_status, CTLFLAG_RWTUN, | SYSCTL_BOOL(_kern_random, OID_AUTO, block_seeded_status, CTLFLAG_RWTUN, | ||||
&block_seeded_status, 0, | &block_seeded_status, 0, | ||||
"If non-zero, pretend Fortuna is in an unseeded state. By setting " | "If non-zero, pretend Fortuna is in an unseeded state. By setting " | ||||
"this as a tunable, boot can be tested as if the random device is " | "this as a tunable, boot can be tested as if the random device is " | ||||
Show All 29 Lines |