rust-random / getrandom

A small cross-platform library for retrieving random data from (operating) system source

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is the ESPIDF implementation actually correct?

briansmith opened this issue · comments

When reviewing the corresponding change to ring, briansmith/ring#1944, I noticed that the implementation in getrandom for ESPIDF seems really questionable. The ESPIDF documentation is quite vague on what promises its PRNG is making, especially when the OS isn't fully configured. It also encourages people to use a userspace CSPRNG; it isn't clear if this suggestion is intended to just address performance or security or both.

In particular, should the ESPIDF random API considered more of an entropy source, but not a full CSPRNG?

In ring we've taken the approach, temporarily, of using getrandom on this OS only if the user opts in with a feature flag that draws attention to this concern.

I also think that we should consider adding support for the custom feature for this "OS".

This issue has more information about esp_fill_random. This line certainly raises red flags:

When using the random number generator, make sure at least either the SAR ADC, high-speed ADC, or RTC20M_CLK is enabled. Otherwise, pseudo-random numbers will be returned.

I would expect to at least get an error code in this case. Also, I couldn't find whether they use proper whitening of entropy received from hardware. We probably should work with ESP IDF people to clarify exact guarantees and if necessary maybe ask them to add a separate function for cryptographically secure randomness.

Looking at espressif/esp-idf#8725 and the ESP-IDF RNG docs, it seems like the only thing we are missing API-wise is a function to check if we are in the "secure configuration" or not, which would basically check that either:

  • The RF subsystem is enabled
  • bootloader_random_enable() is active

My reading of the docs is that if one of those conditions is true, ESP-IDF guarantees esp_random will emit secure random numbers, so would be fine for use with this crate. Users will still want to use a wrapping CSPRNG if they are querying a bunch of randomness, as esp_random is rate limited. However, that's not an ESP-IDF specific issue. A bunch of our supported targets can't handle high throughput. This is why we already recommend using a user-space CSPRNG if you need a bunch of randomness.

For now we should probably try to convince Espressif to add an bool esp_random_is_secure() function.

Until such a function exists, we should probably just add a warning in the docs that you have to be running ESP-IDF in the secure configuration mentioned above to get cryptographically secure random numbers. Disabling support entirely seems counterproductive as it's not like there's anything better ESP-IDF users can do right now.

I am open to the possibility of having built-in ESP-IDF support if they provide APIs that allow us to be confident that the implementation is correct. However, I think that we shouldn't provide an implementation that we don't think is correct in the meantime. So I suggest we move this target back to being a "custom" target for now. Then somebody can provide the current implementation in a separate crate, and people can choose whether that crate is good enough for their need.

Personally, I would want our policy to roughly reflect what's currently in the README and our documentation. Namely that this crate:

retrieves random data from the target's Operating System or platform. We generally assume the platform always provides high-quality cryptographically secure random data, and that the platform environment is correctly configured to ensure this is the case. For more information, see the specific APIs we use below:

The main reason for having such a policy is that:

  • We, in general, cannot verify the implementation of all CSPRNG APIs. Many operating systems we target are closed source (macOS, Windows, VXWorks) or sparsely documented (Redox).
  • There are so many different environment configurations which can decrease the entropy provided by various platform APIs, and it's not possible in general to document or detect them all. For example:
    • LD_PRELOAD interposing on most unix platforms
    • Insecure Linux kernel configurations
    • Trusting that userspace provided "good" cryptographic randomness via RNDADDENTROPY

This also reduces the scope of what this crate is responsible for. If our policy is "we use the best OS API available" then:

  • If there's a better API that we should be using, users can file a bug with us.
  • If there's some deficiency with the OS API or environment, users can file a bug with their OS vendor.

For the ESP-IDF issue specifically, if it were possible for users to write a better implementation than ours using getrandom::register_custom_getrandom!, we could direct users to do this. As far as I can tell though, there is not. So the best approach is to just use the best API we have, and document the environment requirements.

For the ESP-IDF issue specifically, if it were possible for users to write a better implementation than ours using getrandom::register_custom_getrandom!, we could direct users to do this.

OTOH, people are using getrandom expecting that it implements a secure CSPRNG. Disregarding RDRAND, every other implementation that we delegate to at least claims to be a good CSPRNG without any additional work required by the end-user. The ESP-IDF implementation doesn't claim to be a good CSPRNG at all, does not claim to use any DRBG; in fact, it is specifically documented to use something that we can see needs improvement; see https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#rng. Also its documentation specifically recommends using a custom DRBG at https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html.

So, I do think that on espidf we should allow a custom implementation to override our default, and get entropy from the espidf entropy source through getrandom. Perhaps we could expose a separate getentropy API for this, where getentropy is exactly like getrandom_uninit today but where we don't claim full CSPRNG properties, and which wouldn't be deprecated on espdif.

One scheme that would work well for ESP-IDF would be for the user to register a custom implementation of getrandom that uses their favorite userspace CSPRNG implementation, seeded (and occasionally reseeded) by such a getentropy API. Since this would be a backward-incompatible change, it could be done using the "custom2" scheme I suggested in #230 (comment), which would allow for such improvements in aSemVer-compatible release.

One scheme that would work well for ESP-IDF would be for the user to register a custom implementation of getrandom that uses their favorite userspace CSPRNG implementation, seeded (and occasionally reseeded) by such a getentropy API.

This also could be useful for the seed CSR defined in the RISC-V scalar crypto spec. It (somewhat annoyingly for software developers) states the following:

The output entropy (seed[15:0] in ES16 state) is not necessarily fully conditioned randomness due to
hardware and energy limitations of smaller, low-powered implementations. However, minimum requirements are
defined. The main requirement is that 2-to-1 cryptographic post-processing in 256-bit input blocks will yield 128-
bit "full entropy" output blocks.

So we can not use its output directly as we do with RDRAND, we have to use it as a CSPRNG seed. But it's unclear how to make plugging of a custom userspace CSPRNG implementation convenient enough. By relying on rand_core we risk introducing a weird circular dependency, but without it we will not be able to use existing CSPRNG implementation crates.

For now, we probably can just hide the backend behind a configuration flag introduced as part of #498, i.e. users would have to explicitly opt-in into it, fully understanding potential consequences.