Skip to main content
grammar; misc changes
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291

Now, is it possible to change this initial threshold for the non-blocking pool (if you don't want to learn all the nitty-gritty, skip to the end of this answer)? I suspected it was not, but I wasn't sure, so I went to consult to the most authoritative documentation available,: the source. The syscall getrandom(2) is defined in the kernel randomness driver. Note that this is specific to Linux kernel 4.14 (major changes to the randomness driver were made in 4.8).

static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; char *p; // Enter the critical section (acquiringacquire athe spinlock). if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; // If crng_ready() is already true, leave the critical section and return. if (crng_ready()) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } // Mix in the values at cp with the CRNG state. Increment crng_init_cnt // for each byte from cp that gets mixed in (up to len times). p = (unsigned char *) &primary_crng.state[4]; while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) { p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } // Leave the critical section (release the spinlock). spin_unlock_irqrestore(&primary_crng.lock, flags); // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1. if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } return 1; } 

From this, we see that crng_init_cnt is incremented for each byte which crng_fast_load() takes in. The function is called early at boot in various entropy-gathering functions to add as much possible data to the pool early on. We're almost there! Last thing to do is finefind out what the value of CRNG_INIT_CNT_THRESH, defined here:

  • CHACHA20_KEY_SIZE is hardcoded atas 32.
  • CRNG_INIT_CNT_THRESH is double CHACHA20_KEY_SIZE, making it is 64.
  • crng_init_cnt is incremented for every byte of early randomness gathered.
  • When at least 64 bytes of randomness are gathered, crng_init is set to 1.
  • When crng_init is 1, crng_ready() evaluates true.
  • When crng_ready() evaluates true, getrandom() resumes and returns.

The amount of early entropy required before getrandom() resumes and returns is not in fact 128 bits. It is already hardcoded atas 64 bytes (512 bits), twice the amount you wanted.

Now, is it possible to change this initial threshold for the non-blocking pool (if you don't want to learn all the nitty-gritty, skip to the end of this answer)? I suspected it was not, but I wasn't sure, so I went to consult to the most authoritative documentation available, the source. The syscall getrandom(2) is defined in the kernel randomness driver. Note that this is specific to Linux kernel 4.14 (major changes to the randomness driver were made in 4.8).

static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; char *p; // Enter the critical section (acquiring a spinlock). if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; // If crng_ready() is already true, leave the critical section and return. if (crng_ready()) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } // Mix in the values at cp with the CRNG state. Increment crng_init_cnt // for each byte from cp that gets mixed in (up to len times). p = (unsigned char *) &primary_crng.state[4]; while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) { p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } // Leave the critical section (release the spinlock). spin_unlock_irqrestore(&primary_crng.lock, flags); // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1. if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } return 1; } 

From this, we see that crng_init_cnt is incremented for each byte which crng_fast_load() takes in. The function is called early at boot in various entropy-gathering functions to add as much possible data to the pool early on. We're almost there! Last thing to do is fine out what the value of CRNG_INIT_CNT_THRESH, defined here:

  • CHACHA20_KEY_SIZE is hardcoded at 32.
  • CRNG_INIT_CNT_THRESH is double CHACHA20_KEY_SIZE, making it is 64.
  • crng_init_cnt is incremented for every byte of early randomness gathered.
  • When at least 64 bytes of randomness are gathered, crng_init is set to 1.
  • When crng_init is 1, crng_ready() evaluates true.
  • When crng_ready() evaluates true, getrandom() resumes and returns.

The amount of early entropy required before getrandom() resumes and returns is not in fact 128 bits. It is already hardcoded at 64 bytes (512 bits), twice the amount you wanted.

Now, is it possible to change this initial threshold for the non-blocking pool (if you don't want to learn all the nitty-gritty, skip to the end of this answer)? I suspected it was not, but I wasn't sure, so I went to consult to the most authoritative documentation available: the source. The syscall getrandom(2) is defined in the kernel randomness driver. Note that this is specific to Linux kernel 4.14 (major changes to the randomness driver were made in 4.8).

static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; char *p; // Enter the critical section (acquire the spinlock). if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; // If crng_ready() is already true, leave the critical section and return. if (crng_ready()) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } // Mix in the values at cp with the CRNG state. Increment crng_init_cnt // for each byte from cp that gets mixed in (up to len times). p = (unsigned char *) &primary_crng.state[4]; while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) { p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } // Leave the critical section (release the spinlock). spin_unlock_irqrestore(&primary_crng.lock, flags); // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1. if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } return 1; } 

From this, we see that crng_init_cnt is incremented for each byte which crng_fast_load() takes in. The function is called early at boot in various entropy-gathering functions to add as much possible data to the pool early on. We're almost there! Last thing to do is find out the value of CRNG_INIT_CNT_THRESH, defined here:

  • CHACHA20_KEY_SIZE is hardcoded as 32.
  • CRNG_INIT_CNT_THRESH is double CHACHA20_KEY_SIZE, making it 64.
  • crng_init_cnt is incremented for every byte of early randomness gathered.
  • When at least 64 bytes of randomness are gathered, crng_init is set to 1.
  • When crng_init is 1, crng_ready() evaluates true.
  • When crng_ready() evaluates true, getrandom() resumes and returns.

The amount of early entropy required before getrandom() resumes and returns is not in fact 128 bits. It is already hardcoded as 64 bytes (512 bits), twice the amount you wanted.

improve comments
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291

Comments added by me for clarification.

getrandom() to the getrandom() syscall code:

SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, unsigned int, flags) { int ret; // If the flags bitmask is invalid, return an error. if (flags & ~(GRND_NONBLOCK|GRND_RANDOM)) return -EINVAL; // Cap the requested size at the maximum value. if (count > INT_MAX) count = INT_MAX; // If the blocking pool is selected, read from it and return. // The _random_read() function will deal with blocking. if (flags & GRND_RANDOM) return _random_read(flags & GRND_NONBLOCK, buf, count); // AtIf thiswe pointget to here, we know the non-blocking pool was selected. // Is the CRNG ready? Evaluates true if it is not. if (!crng_ready()) { // If we want to bail out and not block, return -EAGAIN. if (flags & GRND_NONBLOCK) return -EAGAIN; // Otherwise, block until random bytes become available. ret = wait_for_random_bytes(); if (unlikely(ret)) return ret; } // Finally, read from the non-blocking pool and return. return urandom_read(NULL, buf, count, NULL); } 

OK, so most of this is pretty self-evident, but what are the functions crng_ready() and wait_for_random_bytes() for? The latter is defined in the same file.

wait_for_random_bytes():

So now we know in the definition of getrandom() that, if the non-blocking pool is selected, it will check if crng_ready() returns true. If it does not return true, then we will wait, sleeping until it does. What does crng_ready() do? It turns out it's defined as a simple macro.

crng_ready():

// If the crng_init variable is > 0 (which is likely), evaluate true. #define crng_ready() (likely(crng_init > 0)) 

The variable starts out as zero, but what matters is where exactly it is set to 1. It appears that this is done in the crng_fast_load() function, defined here.

crng_fast_load():

static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; char *p; // Enter the atomiccritical section (acquiring a spinlock). if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; // If crng_ready() is already true, leave the atomiccritical section and return. if (crng_ready()) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } // Mix in the values at cp with the CRNG state. Increment crng_init_cnt // for each byte from cp that gets mixed in (up to len times). p = (unsigned char *) &primary_crng.state[4]; while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) { p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } // Leave the atomiccritical section (release the spinlock). spin_unlock_irqrestore(&primary_crng.lock, flags); // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1. if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } return 1; } 

From this, we see that crng_init_cnt is incremented for each byte which crng_fast_load() takes in. The function is called early at boot in various entropy-gathering functions to add as much possible data to the pool early on. We're almost there! Last thing to do is fine out what the value of CRNG_INIT_CNT_THRESH, defined here.

CRNG_INIT_CNT_THRESH and CHACHA20_KEY_SIZE:

So it's double CHACHA20_KEY_SIZE. This one is defined in a header file, crypto/chacha20.h.:

Let's see where this all leads to:

Comments added by me for clarification.

getrandom()

SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, unsigned int, flags) { int ret; // If the flags bitmask is invalid, return an error. if (flags & ~(GRND_NONBLOCK|GRND_RANDOM)) return -EINVAL; // Cap the requested size at the maximum value. if (count > INT_MAX) count = INT_MAX; // If the blocking pool is selected, read from it and return. // The _random_read() function will deal with blocking. if (flags & GRND_RANDOM) return _random_read(flags & GRND_NONBLOCK, buf, count); // At this point, we know the non-blocking pool was selected. // Is the CRNG ready? Evaluates true if it is not. if (!crng_ready()) { // If we want to bail out and not block, return -EAGAIN. if (flags & GRND_NONBLOCK) return -EAGAIN; // Otherwise, block until random bytes become available. ret = wait_for_random_bytes(); if (unlikely(ret)) return ret; } // Finally, read from the non-blocking pool and return. return urandom_read(NULL, buf, count, NULL); } 

OK, so most of this is pretty self-evident, but what are the functions crng_ready() and wait_for_random_bytes() for? The latter is defined in the same file.

wait_for_random_bytes()

So now we know in the definition of getrandom() that, if the non-blocking pool is selected, it will check if crng_ready() returns true. If it does not return true, then we will wait, sleeping until it does. What does crng_ready() do? It turns out it's defined as a simple macro.

crng_ready()

// If the crng_init variable is > 0 (which is likely), evaluate true. #define crng_ready() (likely(crng_init > 0)) 

The variable starts out as zero, but what matters is where exactly it is set to 1. It appears that this is done in the crng_fast_load() function, defined here.

crng_fast_load()

static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; char *p; // Enter the atomic section. if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; // If crng_ready() is already true, leave the atomic section and return. if (crng_ready()) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } // Mix in the values at cp with the CRNG state. Increment crng_init_cnt // for each byte from cp that gets mixed in (up to len times). p = (unsigned char *) &primary_crng.state[4]; while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) { p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } // Leave the atomic section. spin_unlock_irqrestore(&primary_crng.lock, flags); // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1. if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } return 1; } 

From this, we see that crng_init_cnt is incremented for each byte which crng_fast_load() takes in. The function is called early at boot in various entropy-gathering functions to add as much possible data to the pool early on. We're almost there! Last thing to do is fine out what the value of CRNG_INIT_CNT_THRESH, defined here.

CRNG_INIT_CNT_THRESH and CHACHA20_KEY_SIZE

So it's double CHACHA20_KEY_SIZE. This one is defined in a header file, crypto/chacha20.h.

Let's see where this all leads to:

Comments added by me for clarification to the getrandom() syscall code:

SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, unsigned int, flags) { int ret; // If the flags bitmask is invalid, return an error. if (flags & ~(GRND_NONBLOCK|GRND_RANDOM)) return -EINVAL; // Cap the requested size at the maximum value. if (count > INT_MAX) count = INT_MAX; // If the blocking pool is selected, read from it and return. // The _random_read() function will deal with blocking. if (flags & GRND_RANDOM) return _random_read(flags & GRND_NONBLOCK, buf, count); // If we get to here, we know the non-blocking pool was selected. // Is the CRNG ready? Evaluates true if it is not. if (!crng_ready()) { // If we want to bail out and not block, return -EAGAIN. if (flags & GRND_NONBLOCK) return -EAGAIN; // Otherwise, block until random bytes become available. ret = wait_for_random_bytes(); if (unlikely(ret)) return ret; } // Finally, read from the non-blocking pool and return. return urandom_read(NULL, buf, count, NULL); } 

OK, so most of this is pretty self-evident, but what are the functions crng_ready() and wait_for_random_bytes() for? The latter is defined in the same file:

So now we know in the definition of getrandom() that, if the non-blocking pool is selected, it will check if crng_ready() returns true. If it does not return true, then we will wait, sleeping until it does. What does crng_ready() do? It turns out it's defined as a simple macro:

#define crng_ready() (likely(crng_init > 0)) 

The variable starts out as zero, but what matters is where exactly it is set to 1. It appears that this is done in the crng_fast_load() function, defined here:

static int crng_fast_load(const char *cp, size_t len) { unsigned long flags; char *p; // Enter the critical section (acquiring a spinlock). if (!spin_trylock_irqsave(&primary_crng.lock, flags)) return 0; // If crng_ready() is already true, leave the critical section and return. if (crng_ready()) { spin_unlock_irqrestore(&primary_crng.lock, flags); return 0; } // Mix in the values at cp with the CRNG state. Increment crng_init_cnt // for each byte from cp that gets mixed in (up to len times). p = (unsigned char *) &primary_crng.state[4]; while (len > 0 && crng_init_cnt < CRNG_INIT_CNT_THRESH) { p[crng_init_cnt % CHACHA20_KEY_SIZE] ^= *cp; cp++; crng_init_cnt++; len--; } // Leave the critical section (release the spinlock). spin_unlock_irqrestore(&primary_crng.lock, flags); // If crng_init_cnt is >= CRNG_INIT_CNT_THRESH, set crng_init to 1. if (crng_init_cnt >= CRNG_INIT_CNT_THRESH) { invalidate_batched_entropy(); crng_init = 1; wake_up_interruptible(&crng_init_wait); pr_notice("random: fast init done\n"); } return 1; } 

From this, we see that crng_init_cnt is incremented for each byte which crng_fast_load() takes in. The function is called early at boot in various entropy-gathering functions to add as much possible data to the pool early on. We're almost there! Last thing to do is fine out what the value of CRNG_INIT_CNT_THRESH, defined here:

So it's double CHACHA20_KEY_SIZE. This one is defined in a header file, crypto/chacha20.h:

switch to https
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291

You can increase the value of the kernel.random.read_wakeup_threshold sysctl. This sysctl changes the behavior of the blocking poolblocking pool, forcing users of the blocking pool to wait until the entropy estimate exceeds this value. From the manpage at random(4)random(4):

Now, is it possible to change this initial threshold for the non-blocking pool (if you don't want to learn all the nitty-gritty, skip to the end of this answer)? I suspected it was not, but I wasn't sure, so I went to consult to the most authoritative documentation available, the source. The syscall getrandom(2)getrandom(2) is defined in the kernel randomness driver. Note that this is specific to Linux kernel 4.14 (major changes to the randomness driver were made in 4.8).

You can increase the value of the kernel.random.read_wakeup_threshold sysctl. This sysctl changes the behavior of the blocking pool, forcing users of the blocking pool to wait until the entropy estimate exceeds this value. From the manpage at random(4):

Now, is it possible to change this initial threshold for the non-blocking pool (if you don't want to learn all the nitty-gritty, skip to the end of this answer)? I suspected it was not, but I wasn't sure, so I went to consult to the most authoritative documentation available, the source. The syscall getrandom(2) is defined in the kernel randomness driver. Note that this is specific to Linux kernel 4.14 (major changes to the randomness driver were made in 4.8).

You can increase the value of the kernel.random.read_wakeup_threshold sysctl. This sysctl changes the behavior of the blocking pool, forcing users of the blocking pool to wait until the entropy estimate exceeds this value. From the manpage at random(4):

Now, is it possible to change this initial threshold for the non-blocking pool (if you don't want to learn all the nitty-gritty, skip to the end of this answer)? I suspected it was not, but I wasn't sure, so I went to consult to the most authoritative documentation available, the source. The syscall getrandom(2) is defined in the kernel randomness driver. Note that this is specific to Linux kernel 4.14 (major changes to the randomness driver were made in 4.8).

Commonmark migration
Source Link
Loading
free-electrons is now bootlin
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291
Loading
corrected stupid mistake (64*8=512, not 256)
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291
Loading
misc improvements, added links
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291
Loading
small typo when copy/pasting.
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291
Loading
Source Link
forest
  • 67.8k
  • 20
  • 221
  • 291
Loading