Open rajveermalviya opened 1 year ago
cc @lrhn
(I will send a patch soon!)
It would be breaking to add it to Random
. We could add it as an extension method.
extension RandomExtensions on Random {
int get nextByte => nextInt(256);
Uint8List nextBytes(int length) {
final result = Uint8List(length);
for (var i=0; i<length; i++) {
result[i] = nextByte;
}
return result;
}
}
@natebosch, I see that the example you provided uses Uint8List
- does that mean it's okay for dart:math
to expose a type from dart:typed_data
?
If we did the extension, we would want to consider just defining it inside of dart:typed_data
.
That is also potentially breaking though, I wouldn't be that surprised if somebody already has an implementation such as the one suggested here, defined in their own project or even a pub package.
The problem with generating random bytes, plural, is that the default random generator doesn't have that much entropy, so creating 256 "random bytes" will not generate more than about 8-16 bytes of entropy anyway.
If you use a cryptographic random number generator, it's still problematic to use a lot of entropy at once, you may still exhaust it's entropy pool, which can take time to refill.
(disclaimer: I am not a cryptography expert)
It's true, exhaustion of the entropy pool is bad - but If i am not wrong, doesn't current API also have the same problem?
final rand = Random.secure();
List.generate(1024, (_) => rand.nextInt(256), growable: false); // This could still lead to exhaustion
In case of Random.secure it's worse because exhaustion can stall the process & also stall other processes that want to use the crypto random generator, until the entropy pool is filled, essentially Denial-Of-Service.
So here It's developers responsibility to be nice and ask for small amount of bytes & less amount of times. Don't know what really can be done here.
On the web, you can use getRandomBytes
of using the secure Random
generator, and we can do something similar natively.
Using plain Random
, I guess we can document it as just doing a sequence of nextInt
operations with an undefined size. This should ensure that providing the same random object, with the same seeded state, gives the same result.
Or we can use the same generator as getRandomValues
, and use the original Random
for the seed.
And as you say, it's up to the user to not assume more entropy than can be provided. We just need to document that.
If you use a cryptographic random number generator, it's still problematic to use a lot of entropy at once, you may still exhaust it's entropy pool, which can take time to refill.
On what platforms is this a problem? I can see on both Android, Linux and Mac OS, Dart uses /dev/urandom
as device for getting random numbers where urandom are documented to never block:
When read, the /dev/urandom device returns random bytes using a pseudorandom number generator seeded from the entropy pool. Reads from this device do not block (i.e., the CPU is not yielded), but can incur an appreciable delay when requesting large amounts of data.
If we talks about the "appreciable delay" then sure. But it is not really a question of using "a lot of entropy" since we are not using it up. Especially modern "urandom" are really fast today.
But could not find anything about how the Windows API is working so could be the Windows platform there is the problem child?
(Another note. I wounder if it would make sense to change the Linux implementation to instead of opening the character device /dev/urandom
to instead use the getrandom()
system call. As far I understand, it is the more "modern" way to get random numbers on Linux platforms today).
Sorry to be a bit off topic, but the examples above incorrectly use rand.nextInt(255)
instead of rand.nextInt(256)
. This github issue is one of the first when searching for "generate random Uint8List
" so I though it might be worth correcting it. The documentation for Random
's nextInt
is a bit confusing in that is uses the max
as the name of the argument which usually means inclusive, but is actually exclusive.
Here is an example from the dart source code:
final kTokenByteSize = 8;
Uint8List bytes = Uint8List(kTokenByteSize);
Random random = Random.secure();
for (int i = 0; i < kTokenByteSize; i++) {
bytes[i] = random.nextInt(256);
}
Currently the
Random
class doesn't provide a way to get a randomly generated raw bytes. It is especially useful when generating a random string or in other cases where multiple calls to the secure random generator for each byte is not desirable.Many runtimes provides the same like:
crypto/rand.Read
, deprecated but availablemath/rand.Read
)SecureRandom.nextBytes
,Random.nextBytes
)crypto.getRandomValues
)