wazero
wazero copied to clipboard
Evaluate the correct built-in RNG implementation
Is your feature request related to a problem? Please describe. A while back, we switched from math to crypto RNG. This seemed correct at the time, but go's docs hint at a sandbox problem
// Reader is a global, shared instance of a cryptographically
// secure random number generator.
//
// On Linux and FreeBSD, Reader uses getrandom(2) if available, /dev/urandom otherwise.
// On OpenBSD, Reader uses getentropy(2).
// On other Unix-like systems, Reader reads from /dev/urandom.
// On Windows systems, Reader uses the RtlGenRandom API.
What I'm concerned with is a WebAssembly module exhausting the host entropy source. Elsewhere, such as clocks (#616) we don't expose something that can be used as a covert channel, or break the sandbox.
Moreover, there's a tacit "capabilities" approach in how people describe WASI, though implementation is very inconsistent across projects. A capabilities approach would default to a fake random, or at least something that can't exhaust the host.
Describe the solution you'd like
I'd like advice on if we should change the default and what it would change to. If we are to stay the same, that's also fine, but we should raise a PR to update RATIONALE.md on it.
So, I think we need to clarify for the API
- what the value users should supply, which satisfies what imports need?
- ex an adaquately seeded CSPRNG vs something else
- what is the default when one was never supplied?
Related context
Imports that will consume this:
- "wasi_snapshot_01" "random_get
- https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
- "wasi-random" "getrandom"
- https://github.com/WebAssembly/wasi-random/blob/main/wasi-random.wit.md#getrandom
- "go" "runtime.getRandomData"
- https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js#L286
Related imports we can address now or another way, possibly arch-specific, later:
- "env" "seed"
- https://www.assemblyscript.org/concepts.html#special-imports
- "wasi-random" "insecure-random"
- https://github.com/WebAssembly/wasi-random/blob/main/wasi-random.wit.md#insecure-random
@knqyf263 @sam-at-luther if both of you have time to contribute a suggestion, much obliged
Maybe there's a better way, but an easy one is to use hashes. Hashes are PRF by default; In that particular cases seems that XOF (Extendable-Output Functions) can be used as "DRBG", and will do the job just fine.
Personally, I use that quite alot with Blake2, something like (error checks removed for simplicity):
import (
"crypto/rand"
"fmt"
"golang.org/x/crypto/blake2b"
)
type rngGenerator struct {
blake2b.XOF
}
func newRNGGenereator() (rng rngGenerator) {
key := make([]byte, 64)
rand.Read(key)
rng.XOF, _ = blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
return rng
}
func main() {
rng := newRNGGenereator()
output := make([]byte, 123)
for i := 0; i < 10; i++ {
rng.Read(output) // can read many times, up to 256GiB, I think. The size can vary, it's not fixed.
fmt.Printf("%X \n", output)
}
}
You need rand
for the initialization, but after that each Read
is independent from the rand
and the XOF is used to generate random data. In that case, the guest can request bunch for random data and that will be random and independent from the host entropy and others guests. You may need to re-initialize the XOF once and while (due to 256Gib limits), but that still saving from reading rand
everytime, it's also possible to extend itself, using the output as seed to the next XOF.
EDIT: To make it easier to undestand: random_get
function will read from the XOF, instead of getting it from rand. On each new module, it will create one new XOF "session" and use rand
just once.
It's possible to use SHA-3 too. In that case it's ShakeHash (https://pkg.go.dev/golang.org/x/crypto/sha3#ShakeHash), same concept. I'm not sure if SHA-3 have such limitation on the stream size.
However, both imports packages from golang.org/x/crypto, and maybe there's a simple and better way to do the thing.
Thanks @inkeliz. I'll defer until other end users chime in. Commenting only because you mentioned something I left out of the desc. the consumers of this api (and we may need more than one, similar to how we have wall time and nanotime)
So, I think we need to clarify for the API
- what the value users should supply, which satisfies what imports need?
- ex an adaquately seeded CSPRNG vs something else
- what is the default when one was never supplied?
Here are the use cases I've added to the desc
- "wasi_snapshot_01" "random_get
- https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
- "wasi-random" "getrandom"
- https://github.com/WebAssembly/wasi-random/blob/main/wasi-random.wit.md#getrandom
- "go" "runtime.getRandomData"
- https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js#L286
We also have some related imports, too
- "env" "seed"
- https://www.assemblyscript.org/concepts.html#special-imports
- "wasi-random" "insecure-random"
- https://github.com/WebAssembly/wasi-random/blob/main/wasi-random.wit.md#insecure-random
what's the next action on this issue, given that no one has chimed in since Jun 2022?