Page MenuHomeFreeBSD

random: Ingest extra fast entropy when !seeded
ClosedPublic

Authored by cperciva on Jul 13 2022, 12:48 AM.
Tags
None
Referenced Files
Unknown Object (File)
Tue, Jan 17, 2:54 AM
Unknown Object (File)
Dec 24 2022, 10:40 AM
Unknown Object (File)
Dec 14 2022, 4:05 AM
Unknown Object (File)
Dec 13 2022, 12:05 PM
Unknown Object (File)
Dec 13 2022, 11:56 AM
Unknown Object (File)
Dec 13 2022, 11:56 AM
Unknown Object (File)
Dec 13 2022, 11:52 AM

Details

Summary

We periodically ingest entropy from pollable entropy sources, but only
8 bytes at a time and only occasionally enough to feed all of Fortuna's
pools once per second. This can result in Fortuna remaining unseeded
for a nontrivial amount of time when there is no entropy passed in from
the boot loader, even if RDRAND is available to quickly provide a large
amount of entropy.

Detect in random_sources_feed if we are not yet seeded, and increase the
amount of immediate entropy harvesting we perform, in order to "fill"
Fortuna's entropy pools and avoid having

random: randomdev_wait_until_seeded unblock wait

stall the boot process when entropy is available.

This speeds up the FreeBSD boot in the Firecracker VM by 2.3 seconds.

Discussed with: cem
Sponsored by: https://www.patreon.com/cperciva

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

Discussed with: cem

Is maybe overselling it. Like I said in email, I don't think this is the right place to do it. Functionally, I don't think it will break anything.

I'd be happy to green-light this, but I also wonder if there is not a better way. For now, I'm at "no objection" for a merge, but with a request for further exploration of e.g. cached boot-time entropy stashing. (Could a VM do that?)

delphij added a subscriber: delphij.

This looks good to me in principle.

Before committing, could you please add comments explaining why we are doing this, and ideally, perhaps also how these values were chosen (see my comment inline)?

sys/dev/random/random_harvestq.c
257

I'd like to check if my understanding was correct for the !ra_seeded() case:

  1. The 64 was chosen because it's the RANDOM_FORTUNA_DEFPOOLSIZE
  2. The intention was to attempt to fully populate all pools with fast source data in one random_kthread interation

We then iterate over all live sources:

  1. Realistically, most system has only one live source, that's the CPU provided RNG.
  2. For systems where no live source is available, the code below wold be no-op (we then fall back to waiting for other sources, which we have to do anyways)
  3. For systems where multiple live sources are available (virtio-rng, or crypto hardware), we could potentially harvest more than we need (but I think this is Okay because we only do the full harvest once as long as they are not all too slow).

So the net effect for a typical !ra_seeded() case is that when the random_kthread() calls random_sources_feed(), we attempt to fully fill Fortuna's entropy pools (@10Hz, but hopefully only once) with live sources.

For systems with boot entropy loaded, because we already primed RNG well before the random_kthread, this would be a no-op.

258

If the understanding above was correct, should this be changed to sizeof(entropy) instead?

This revision is now accepted and ready to land.Jul 13 2022, 8:12 AM

I'd be happy to green-light this, but I also wonder if there is not a better way. For now, I'm at "no objection" for a merge, but with a request for further exploration of e.g. cached boot-time entropy stashing. (Could a VM do that?)

I think there is no standard way of doing it (for supplying (potentially continuous stream of) entropy to the guest, VirtIO RNG is a relatively standardized way that works for many hypervisors). I think other operating systems might just happily take VirtIO RNG, or RDSEED along with their regular entropy sources to perform the initialization.

Doing the harvesting in random_sources_feed() seems to be reasonable: it would only do the actual work after the live entropy source is registered, and that's well after random_harvestq_prime so if there is saved boot entropy that was loaded by loader, we wouldn't be performing the "fast" fill at all.

Something to keep in mind re: exactly 64 is that it's possible for entropy sources to return less entropy than was requested, if for example some internal state means they don't have anything available at the moment. That's one reason I prefer polling repeatedly until seeded (or some spin threshold is crossed).

I'd be happy to green-light this, but I also wonder if there is not a better way. For now, I'm at "no objection" for a merge, but with a request for further exploration of e.g. cached boot-time entropy stashing. (Could a VM do that?)

In most cases we have entropy provided to us by the boot loader, either from UEFI (if the platform implements UEFI RNG functionality) or from /boot/entropy (if it's not the first time the system boots). The case where I'm tripping over this is the first (and typically *only*) time the VM in question boots, and it's booting without the boot loader so we can't get the entropy that way (even if /boot/entropy existed).

Update to add comments and fix sizeof(entropy).

This revision now requires review to proceed.Jul 20 2022, 5:05 AM

@delphij Thanks for the catch re HARVESTSIZE vs sizeof(entropy)... I keep on forgetting that HARVESTSIZE is weirdly measured in words rather than bytes.

I added a lengthy comment which I think helps explain what's going on. Please let me know if anything is still unclear.

This revision is now accepted and ready to land.Jul 20 2022, 6:02 AM