-
Notifications
You must be signed in to change notification settings - Fork 432
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
Several OsRng improvements #484
Conversation
Did you test this for all those operating systems? |
I wish 😄. But most of the changes are around I finished digging though the Haiku source code (with help from a virtual machine). There is just about no documentation, but And I am trying to test Solaris with a syscall interface. Trying to cross-compile from a virtual machine with Ubuntu, to test it on a virtual machine with Solaris. 😢 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some very nice clean-ups here!
Removed the WASM without stdweb stub (never understood why we should have it).
Initially this was added to allow compiling to WASM, possibly with the goal of making remaining functionality available (not quite sure). By removing this I think we'll now get a compile-failure? Another option would be to feature-gate like with no_std
. Or maybe WASM without stdweb
should only work in no_std
mode, I don't know.
Regardless, I'm a little concerned about this change, especially for the 0.5 release, though it may be the best option in the end. (Also that snippet in JitterRng
which just fails — ideally we would not include features which just fail at run-time.)
src/rngs/os.rs
Outdated
/// `wasm32-unknown-emscripten` and `wasm32-experimental-emscripten` use | ||
/// Emscripten's emulation of `/dev/random` on web browsers and Node.js. | ||
/// Unfortunately it falls back to the insecure `Math.random()` if a browser | ||
/// doesn't support [`Crypto.getRandomValues`][12].a browser doesn't support Crypto.getRandomValues. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy+paste error?
src/rngs/os.rs
Outdated
// Read a single byte from `/dev/random` in non-blocking mode, to determine | ||
// if the OS RNG is already seeded. | ||
fn try_dev_random() -> Result<(), io::Error> { | ||
info!("OsRng: opening random device /dev/random"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"testing random device"?
src/rngs/os.rs
Outdated
#[cfg(any(target_arch = "x86_64", target_arch = "x86", | ||
target_arch = "arm", target_arch = "aarch64", | ||
target_arch = "s390x", target_arch = "powerpc", | ||
target_arch = "mips", target_arch = "mips64"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nicer still if this repetition could be avoided. Perhaps const NR_GETRANDOM: Option<libc::c_long>
and a single definition of getrandom
?
src/rngs/os.rs
Outdated
#[cfg(any(target_arch = "x86_64", target_arch = "x86", | ||
target_arch = "arm", target_arch = "aarch64", | ||
target_arch = "s390x", target_arch = "powerpc", | ||
target_arch = "mips", target_arch = "mips64"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same, seems like this function doesn't need two definitions.
src/rngs/os.rs
Outdated
Ok(()) | ||
impl OsRng { | ||
pub fn new() -> Result<OsRng, Error> { | ||
open_random_device("/dev/urandom", false)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment above says read from /dev/random
. Which is correct?
I think that explaines it. We didn't have a
I am not completely used to the idea of having some sort of stability 😄. But I don't think this PR really makes anything worse on the systems that Rust supports.
I think we need to test someday that is using a good timer on all platforms Rust supports. And then we may even re-implement that little part that |
I am not sure adding the commit to add support for Solaris is the right thing to add. I think it is correct or pretty close, as it is mostly the same as the one for Linux. It compiles, but I just can't get it to link against my copy of the libraries from Solaris... Is having something better than nothing? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! How much of this is tested? Still, we do the best we can...
src/rngs/os.rs
Outdated
} else if kind == io::ErrorKind::WouldBlock { | ||
// Potentially this would waste bytes, but since we use | ||
// /dev/urandom blocking only happens if not initialised. | ||
// Also, wasting the bytes in dest doesn't matter very much. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you understand the man page? Apparently the function returns -1 when it would block, and may set 0 or the number of bytes requested, and one should check the return value — but this is -1!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is an old comment, I noticed it after making the commit. I was going over the documentation a bit more right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, the manual is not very clear. Both -1 and 0 can indicate an error. We should ask for bytes in chunks of 1024, while it are chunks of 1040 for /dev/random
. And apparently it is guaranteed the chunk it either not or completely filled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1040 is an unusual chunk size. Yeah, I love confusing manuals (not).
One thing that I think still needs a better look is what happens when the OS RNG is not yet seeded. Does it block, return an error, return lower-quality bytes, unknown, or n/a? And does that happen in And I wonder if it makes sense to add a little more abstraction in this file, not sure yet. |
Sorry, I keep playing with this file. I have tried to refactor the trait OsRngImpl where Self: Sized {
fn new() -> Result<Self, Error>;
fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error>;
fn max_chunk_size(&self) -> Option<usize> { None }
fn method_str(&self) -> &'static str;
} In my opinion it helped making things easier to understand. Logging and dividing into chunks is now done in one place (and thus uniformly), in I have tried cross-compiling almost all targets. It turned out Fuchsia and CloudABI didn't work before... Redox doesn't work yet. The Edit: one disadvantage of the changes here is that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sensible idea to use a trait. Still not entirely convinced about replacing try_fill_bytes
with three other methods but seems okay.
src/rngs/os.rs
Outdated
} | ||
for slice in dest.chunks_mut(max) { | ||
let result = getrandom(&mut slice); | ||
if result == -1 || result == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might as well just do result <= 0
in this case since on success this is always > 0, though according to the manual (can we trust it?) it only returns 0 on error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this case I like to point out both, so it is clearly not a typo.
src/rngs/os.rs
Outdated
trait OsRngImpl where Self: Sized { | ||
fn new() -> Result<Self, Error>; | ||
fn fill_chunk(&mut self, dest: &mut [u8]) -> Result<(), Error>; | ||
fn max_chunk_size(&self) -> Option<usize> { None } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May as well just use core::usize::MAX
and drop the Option
since this is the default impl.
src/rngs/os.rs
Outdated
OsRngMethod::RandomDevice => read_random_device(slice, Some(1040)), | ||
} | ||
OsRngMethod::GetRandom => Some(1024), | ||
OsRngMethod::RandomDevice => Some(1040), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seriously? Save the logic and just use 1024.
src/rngs/os.rs
Outdated
} | ||
Ok(()) | ||
} | ||
|
||
fn max_chunk_size(&self) -> Option<usize> { | ||
Some(<ULONG>::max_value() as usize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this is u32::MAX
. Should probably be the default; it's not like anyone will use more anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It costs us nothing to do the right thing.
I am mostly happy with it now. And thank you for also looking carefully 😄. I am not sure yet what the right thing to do is around blocking. On Linux, NetBSD and Solaris we have the choice between an error and blocking. Other systems always block, or don't document what happens when the OS RNG hasn't yet collected enough entropy. And Linux, FreeBSD (not NetBSD) and Solaris have the ability to poll on We currently choose to get an error, and have some approximation of blocking in the Would it make sense to store whether we know the OS RNG is not yet initialized, and to use a blocking read in |
Added one more commit. This is not getting easier... I added a
Edit: Still doesn't work quite right on Solaris... |
026489a
to
8ae5b43
Compare
Update to use the correct open flags on Solaris. I'll stop touching this now 😄. |
I might wait a couple of days before reviewing just in case 😆 |
I had a look at the recent changes — generally looks good, but goes a bit beyond what I can easily review 😄 . I'll leave it to you @pitdicker; if you think it's ready the merge this. |
/dev/urandom
. Besides Linux (which it is pretty specialized for), it was only 'correct' for NetBSD. I think it is better to not compile, and add support for a platform, than to have a default implementation. Then we can make sure it is tested, and matches the recommendations for that platform.OsRng
./dev/random
./dev/urandom
is faster, but using IBAA, the predecessor of ISAAC, of which the security is unknown./dev/urandom
, without testing/dev/random
, and splits the read up in chunks.stdweb
split the read up in chunks of 65536 bytes.unix
module.stdweb
stub (never understood why we should have it).Fixes #332.