How we use random?
#include <stdlib.h>
int main{
srand(0); //result of seed 0 and seed 1 might be the same
printf("%d",rand());
}
#include <stdlib.h>
#include <time.h>
int main{
srand((unsigned)time(NULL));
printf("%d",rand());
}
Let's see how rand() works
int __random_r (struct random_data *buf, int32_t *result){
.
.
int32_t val = ((state[0] * 1103515245U) + 12345U) & 0x7fffffff;
state[0] = val;
*result = val;
.
result
here would addtionaly divided by specific value as output.
Linear Congruential Generator
\[ S_{n+1} = (aS_n + c) \ mod \ m,\ S_n \in \{0,1,... m -1 \} \]
int __random_r (struct random_data *buf, int32_t *result){
.
.
int32_t val = ((state[0] * 1103515245U) + 12345U) & 0x7fffffff;
state[0] = val;
*result = val;
.
If we want to reach a counter with a period of \(m\). \(a\), \(c\), \(m\) should satisfy the Hull-Dobell theorem. (Theorem 1)
Pseudo Random Number Generator
LFSR
Mersenne twister
LSFR is used widely in Linux Kernel RNG
Before…
LFSR might be unsafed and you could cracked it up with SMT(z3) solver.
Z3 & LFSR
RNG in Linux Kernel
/dev/random
(blocked)/dev/urandom
(non-blocked)
/dev/hwrng
(for hardware)cat /dev/{u}random
cat /dev/hwrng
Entropy Extraction
RDSEED
RDRAND
as saltstatic void extract_entropy(void *buf, size_t len){
spin_lock_irqsave(&input_pool.lock, flags);
/* seed = HASHPRF(last_key, entropy_input) */
blake2s_final(&input_pool.hash, seed);
/* next_key = HASHPRF(seed, RDSEED || 0) */
blake2s(next_key, (u8 *)&block, seed, sizeof(next_key), sizeof(block), sizeof(seed));
blake2s_init_key(&input_pool.hash, BLAKE2S_HASH_SIZE, next_key, sizeof(next_key));
spin_unlock_irqrestore(&input_pool.lock, flags);
memzero_explicit(next_key, sizeof(next_key));
while (len) {
i = min_t(size_t, len, BLAKE2S_HASH_SIZE);
/* output = HASHPRF(seed, RDSEED || ++counter) */
++block.counter;
blake2s(buf, (u8 *)&block, seed, i, sizeof(block), sizeof(seed));
...
}
Entropy Expansion
static void _get_random_bytes(void *buf, size_t len)
{
...
first_block_len = min_t(size_t, 32, len);
crng_make_state(chacha_state, buf, first_block_len);
len -= first_block_len;
buf += first_block_len;
...
...
while (len) {
if (len < CHACHA_BLOCK_SIZE) {
chacha20_block(chacha_state, tmp);
memcpy(buf, tmp, len);
memzero_explicit(tmp, sizeof(tmp));
break;
}
chacha20_block(chacha_state, buf);
if (unlikely(chacha_state[12] == 0))
++chacha_state[13];
len -= CHACHA_BLOCK_SIZE;
buf += CHACHA_BLOCK_SIZE;
}
...
}
Entropy Source
add_device_randomness()
: entropy from data that likely differ between devices (MAC address, serial numbers), doesn't increase the estimated entropy.add_input_randomness()
: key or mouse eventadd_interrupt_randomness()
random_get_entropy()
if (!timer_pending(&fast_pool->mix)) {
fast_pool->mix.expires = jiffies;
add_timer_on(&fast_pool->mix, raw_smp_processor_id());
}
Linux Jitter Dance
https://github.com/torvalds/linux/commit/50ee7529ec45
static ssize_t urandom_read_iter(struct kiocb *kiocb, struct iov_iter *iter)
{
...
if (!crng_ready())
try_to_generate_entropy();
...
return get_random_bytes_user(iter);
}
Linux Jitter Dance
static int jent_timer(char *data, size_t len)
{
__u64 time, time2;
time = time2 = 0;
...
time = random_get_entropy();
time2 = random_get_entropy();
snprintf(data, len, "%lld\n", (time2 - time));
...
}
/dev/hwrng
$cat /sys/class/misc/hw_random/rng_available
$cat /sys/class/misc/hw_random/rng_current
hw_register
/drivers/char/hw_random/core.c
int hwrng_register(struct hwrng *rng)
{
...//check duplicate
list_for_each_entry(tmp, &rng_list, list) {
if (strcmp(tmp->name, rng->name) == 0)
goto out_unlock;
}
...
if (!current_rng ||
(!cur_rng_set_by_user && rng->quality > current_rng->quality)) {
/*
* Set new rng as current as the new rng source
* provides better entropy quality and was not
* chosen by userspace.
*/
err = set_current_rng(rng);
if (err)
goto out_unlock;
/* to use current_rng in add_early_randomness() we need
* to take a ref
*/
is_new_current = true;
kref_get(&rng->ref);
}
...
if (is_new_current || !rng->init) {
add_early_randomness(rng);
}
...
}
Implement a simple zero hwrng
static struct hwrng zero_rng = {
.name= "zero_rng",
.read = zero_rng_read
};
static int zero_rng_init(void){
return hwrng_register(&zero_rng);
}
static int zero_rng_read(struct hwrng*rng, void *data, size_t max, bool wait){
u8 *buf;
buf = data;
for(int i = 0;i<max;i++){
buf[0];
}
return max;
}
use rdrand
as hwrng output
void get_rdrand(uint8_t *mem){
asm("rdrand %%rax\n\t"
"mov %%rax, %0\n\t"
:"=m"(*mem)::"0");
}
static int rdrand_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
u8 *buf;
u8 rnd8[8];
buf = data;
int i;
for (i=0;i<max;i++){
if ((i%8)==0)
get_hw_rand2(rnd8);
buf[i] = rnd8[i%8];
}
return max;
}
test rng tool
rngtest -c 1000 </dev/random
ent file
dd if=/dev/hwrng/ of=hw bs=4096 count=1