Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce a thread-local Cryptographically Secure Pseudo Random Number Generator for general use #6704

Closed
andrewrk opened this issue Oct 16, 2020 · 3 comments · Fixed by #7482
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Milestone

Comments

@andrewrk
Copy link
Member

@jedisct1 writes in #6622 (comment):

In C, at least on BSD, macOS and WASI (libc), the problem was solved with arc4random(), that automatically seeds a local CSPRNG from the system once and then uses a stream cipher to quickly derive everything else from it, in a thread-safe way. On OpenBSD, absolutely all the base applications requiring secure random numbers use this and nothing else. This is fast, simple and safe.

In Rust, rand::thread_rng() does the same and is what virtually everyone mechanically uses when secure random numbers are required (or OsRng but arbitrarily). This is simple and safe to use from anywhere, and doesn't require passing states around.

Would it be worth having something similar? We currently have crypto.getRandom() that has the above security guarantees, but requires a syscall every time some output is needed. It also doesn't implement the Rand interface.

The idea behind it being thread-local is that it would not cause any contention or syscalls when multiple threads simultaneously want to use a CSPRNG. However, I do think we should use comptime logic to detect if arc4random will be available, and use that as the backing function call instead of duplicating RNG code in the runtime binary. This should be handled transparently so that a higher level interface does not have to care about these details.

There are several calllsites right now that use std.crypto.randomBytes which would be improved to use this API because it would prevent unnecessary syscalls. Even the initial seed syscall can be elided in some cases; for example it would be appropriate for zig start code to use AT_RANDOM from the linux "auxval" to perform the initial seed in the main thread.

One thing to consider is, optimally, we would not want to pay the cost of initializing the RNG before main() and with the spawn of each thread, and the cost of the Thread Local Storage, and the cost of the binary bloat, if the RNG does not end up being used. It is unclear how this could be accomplished, however, I am OK with proceeding on this now and trying to solve this optimization problem later.

@andrewrk andrewrk added standard library This issue involves writing Zig code for the standard library. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. accepted This proposal is planned. labels Oct 16, 2020
@andrewrk andrewrk added this to the 0.8.0 milestone Oct 16, 2020
@LemonBoy
Copy link
Contributor

we would not want to pay the cost of initializing the RNG before main() and with the spawn of each thread, and the cost of the Thread Local Storage, and the cost of the binary bloat, if the RNG does not end up being used.

The space requirement for the Gimli rng are negligible as it only uses 48 bytes of memory.

Fetching entropy at startup is also needed to initialize the stack canaries (not yet implemented, we need a way to tell LLVM not to emit the stack protector sequence in the early startup functions).
All things considered the overhead of a getentropy call is a droplet of piss in the ocean if compared to the process spawning/thread creation sequence.

On Linux you get 16 bytes of good ™️ entropy for free via the AT_RANDOM that may not be enough to properly seed the rng (Gimli needs 32 bytes) and deriving a seed from a non csprng may not be ideal here.

@daurnimator
Copy link
Contributor

On Linux you get 16 bytes of good tm entropy for free via the AT_RANDOM that may not be enough to properly seed the rng (Gimli needs 32 bytes)

16 bytes is sufficient (cryptographically); it would be safe to extend. See #6622 (comment) for further discussion.

deriving a seed from a non csprng may not be ideal here.

It comes straight out of the kernel CSPRNG via get_random_bytes (however in early boot it might not be seeded well; same as calling getrandom with GRND_NONBLOCK set).

@LemonBoy
Copy link
Contributor

It comes straight out of the kernel CSPRNG via get_random_bytes (however in early boot it might not be seeded well; same as calling getrandom with GRND_NONBLOCK set).

I was talking of the idea of stretching the 16 (or 8, if we don't want to reuse the first 8) bytes of AT_RANDOM with another RNG, the same way you use SplitMix to seed a Xoroshiro RNG.

16 bytes is sufficient (cryptographically); it would be safe to extend. See #6622 (comment) for further discussion.

I'll take your word on this.

andrewrk added a commit that referenced this issue Dec 18, 2020
std.crypto.random

* cross platform, even freestanding
* can't fail. on initialization for some systems requires calling
  os.getrandom(), in which case there are rare but theoretically
  possible errors. The code panics in these cases, however the
  application may choose to override the default seed function and then
  handle the failure another way.
* thread-safe
* supports the full Random interface
* cryptographically secure
* no syscall required to initialize on Linux (AT_RANDOM)
* calls arc4random on systems that support it

`std.crypto.randomBytes` is removed in favor of `std.crypto.random.bytes`.

I moved some of the Random implementations into their own files in the
interest of organization.

stage2 no longer requires passing a RNG; instead it uses this API.

Closes #6704
andrewrk added a commit that referenced this issue Dec 18, 2020
std.crypto.random

* cross platform, even freestanding
* can't fail. on initialization for some systems requires calling
  os.getrandom(), in which case there are rare but theoretically
  possible errors. The code panics in these cases, however the
  application may choose to override the default seed function and then
  handle the failure another way.
* thread-safe
* supports the full Random interface
* cryptographically secure
* no syscall required to initialize on Linux (AT_RANDOM)
* calls arc4random on systems that support it

`std.crypto.randomBytes` is removed in favor of `std.crypto.random.bytes`.

I moved some of the Random implementations into their own files in the
interest of organization.

stage2 no longer requires passing a RNG; instead it uses this API.

Closes #6704
andrewrk added a commit that referenced this issue Dec 18, 2020
std.crypto.random

* cross platform, even freestanding
* can't fail. on initialization for some systems requires calling
  os.getrandom(), in which case there are rare but theoretically
  possible errors. The code panics in these cases, however the
  application may choose to override the default seed function and then
  handle the failure another way.
* thread-safe
* supports the full Random interface
* cryptographically secure
* no syscall required to initialize on Linux (AT_RANDOM)
* calls arc4random on systems that support it

`std.crypto.randomBytes` is removed in favor of `std.crypto.random.bytes`.

I moved some of the Random implementations into their own files in the
interest of organization.

stage2 no longer requires passing a RNG; instead it uses this API.

Closes #6704
aarvay pushed a commit to aarvay/zig that referenced this issue Jan 4, 2021
std.crypto.random

* cross platform, even freestanding
* can't fail. on initialization for some systems requires calling
  os.getrandom(), in which case there are rare but theoretically
  possible errors. The code panics in these cases, however the
  application may choose to override the default seed function and then
  handle the failure another way.
* thread-safe
* supports the full Random interface
* cryptographically secure
* no syscall required to initialize on Linux (AT_RANDOM)
* calls arc4random on systems that support it

`std.crypto.randomBytes` is removed in favor of `std.crypto.random.bytes`.

I moved some of the Random implementations into their own files in the
interest of organization.

stage2 no longer requires passing a RNG; instead it uses this API.

Closes ziglang#6704
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted This proposal is planned. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. standard library This issue involves writing Zig code for the standard library.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants