-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Tracking Issue for secure random data generation in std
#130703
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
Comments
Disclaimer: I am one of the I think it's important for It may be worth to add the following methods to
It's also not clear whether it's allowed to overwrite the default |
Would you consider rust-lang/libs-team#159 to be a better solution? That one used the |
No, I don't think it's an appropriate solution. Firstly, it relies on For the last point I guess we could define a separate |
I don't think this needs to be a blocker. IMO a lot of |
Would it be better to make this explicit with generics? Like |
I (mostly) disagree. CSPRNGs should almost never fail. When they do, users are almost never qualified to diagnose the problem. For example: golang/go#66821 A compromise is something like this: trait RandomSource {
type Error;
fn fill_bytes(...) {
self.try_fill_bytes(...).unwrap();
}
fn try_fill_bytes(...) -> Result<..., Self::Error>
} This allows most CSPRNGs to use |
@ericlagergren As for design of fallible RNG traits, see the new |
This trait (and the topic of random value generation) should be removed from this discussion entirely in my opinion, focussing only on "secure random data generation" as in the title. Why: because (1) provision of secure random data is an important topic by itself (with many users only wanting a byte slice and with methods like Disclaimer: I am one of the |
Overriding the default source in an application that already has one from linking If overriding the
This wouldn't be a problem if the entire ecosystem could agree to always delegate this problem to on one specific crate (version) with appropriate hooks, like Edit: almost forgot that even |
There is a number of reasons to allow overriding:
Yes. How about following the Either way, overriding is probably can be left for later. I think we both agree that we need a way to expose "system" entropy source in
I agree that ideally we need a unified approach for this kind of problem. I made a similar proposal once upon a time. But I think it fits fine? Targets with
I believe that having
Well, it has happened, sort of. The problem is that
Can we add yet another sysroot crate for |
The RFC (and the competing ones I've looked at) only supports a default implementation in the crate that "declares" the externally-implementable thing. If that crate isn't
I was specifically talking about
Not to point any fingers but a counter example that's fresh on my mind because I looked at its code recently is foldhash. As another example,
Possibly, but people may object to a proliferation of sysroot crates so let's hope there's a better solution. |
Yes, it's not as if RFC is a technical specification which must be followed word-by-word. There is a number of cases where the original RFC vision has somewhat changed during implementation stages. If anything, I would say it's an oversight/deficiency of the RFC to not cover cases like this.
If a crate aims to minimize its number of dependencies as far as possible even at the cost of code quality and security, it obviously will not depend on |
As I said, I have no intention of pointing fingers at any crates. They have to navigate tricky trade-offs and complexities due to Rust's standard library (as a whole, not just |
The Random trait seems weakly motivated in terms of coupling it to RandomSource, as its design seems like it will be a much more hotly contested space, and it is (mostly) unrelated to RandomSource. |
Note that @dhardy (maintainer of rand) wrote some criticism of this at rust-lang/libs-team#393 (comment) |
Could let random_array: [u64; 100] = random(); which is a lot more convenient than using the |
I think it would be useful to have a data type like |
I disagree that it is useful in testing implementations of traits like |
As far as I know there’s no plan to make the |
It seems like one might regret adding For more complicated types (e.g., It seems better to leave this out rather than leave it in this poorly specified state. Just stabilize Although... even |
I really don't think so. The current interface works perfectly fine, it's just slightly suboptimal in some use cases (fewer than in the context of general byte-centric I/O). Providing only a So I think it's pretty clear that the method for filling a |
Perhaps something like this would work: // Users don't need to concern themselves with this type.
pub strut OutBuf([MaybeUninit<u8>]);
impl OutBuf {
// various methods to construct it and write to it, no method to read from it
}
// Users don't need to worry about implementing this, just use it
pub unsafe trait AsOutBuf {
type Init: ?Sized;
fn as_out_buf(&mut self) -> &mut OutBuf;
// Only allowed if the entire buffer was overwritten.
unsafe fn assume_init(&mut self) -> &mut Self::Init;
}
impl AsOutBuf for [u8] { type Init = [u8]; ... }
impl AsOutBuf for [MaybeUninit<u8>] { type Init = [u8]; ... }
impl<const N: usize> AsOutBuf for [u8; N] { type Init = [u8; N]; ... }
impl<const N: usize> AsOutBuf for [MaybeUninit<u8>; N] { type Init = [u8; N]; ... }
// Users can pass in any byte slice, byte array or their uninitialized counterparts
// The entire value is guaranteed to get overwritten, so if using the returned value is undesirable due to borrowing issues the users can simply use the original value safely, if it was an initialized type to begin with or they may just soundly call `assume_init` on it.
pub fn fill_random_bytes<T: AsOutBuf>(buf: &mut T) -> &mut T::Init { ... } This way it already supports all reasonable cases and people are not forced into using uninitialized API. They can write |
That helps the simple call sites remain simple, but it has several downsides:
|
I agree, but the unfortunate reality is that Rust does provide Imagine a function/method like |
Libraries that don’t require |
I think that is the best thing to do. The real issue is that we haven't deprecated the wasm32-unknown-unknown target yet; it is inevitable, but not everybody sees it yet. wasm32-unknown-unknown stuff shouldn't block progress on this. |
I don't see any reason to not deprecate it at least and provide more sensible alternative. "does not work on Being unavailable on
Well, minimizing WASM module size by getting rid of |
Seconded. All I want is something like io::read_entropy as an alternative to the getrandom crate. Does the libs team need to accept an ACP first or should someone presumptuously make an issue so we can move discussion there? |
We discussed this at length in today's @rust-lang/libs-api meeting. We didn't come to any final conclusions, but here's a summary of the discussion: We were generally amenable to the idea of exposing low-level randomness in the style of We felt that that was an independent decision, separate from the higher-level interfaces in the style of We discussed whether the low-level method being requested here is effectively the existing In discussing the possible differences, we talked about whether We did agree that, if we want to expose the IO error, we should do so by having Everyone present in the meeting was generally in favor of exposing the IO error, and doing so by having Summary:
|
@joshtriplett Implementing
Also: is |
This looks really bad for the potential future move of this trait to |
Another (smaller) issue with reusing
Any effort spent flagging this and either rewriting it with |
Chiming in to note that
I guess the question is whether the standard library should try to account for this, or just count it as a failure. |
@abgros I don't see a practical reason to expose a |
I certainly hope not. I would expect the handling of any such error to be a graceful exit, and the only reason to expose it rather than panicking is the ability to handle it gracefully.
I would not in general expect such errors to be recoverable.
|
If we're going to expose errors from the OS, the obvious type for that would be If we decide to panic, or use a different error type, then I agree that we can't use
We could absolutely do that, but the question is what we should do about error handling, if anything. If we use |
That's a valid point. |
The obvious approach is to use the Somewhat tangential rant: I believe that It covers two very distinct cases: "error code from OS" and "any error under the sun abstracted by Yes, we need some limited extendability for things like Making |
I don't think that's obvious, and it has disadvantages: code that's generic over
I agree with you completely, and that's a substantial problem. In the absence of that boxed variant, |
Judging by the Cryptographic code is a bit different in this regard since it's more common to use |
Is that really true? I had assumed, perhaps incorrectly, that But Is there anything else in Related, when people talk about having Because if it's the former then, again, I don't understand what |
Unfortunately, users do fall back to insecure sources of randomness. :/
If there is going to be a My goal here is just to make sure these new APIs are misuse resistant. |
(Note: further discussion on this would be off topic for this tracking issue; for that, I'd suggest
Teasing apart the parts of
I would not expect |
@jstarks
The idea is to start with the former and maybe move to the latter in future (in a separate sysroot crate). IIUC the main blocker for the latter is lack of a unified way to handle Having the randomness trait defined in |
Regarding error handling, one possibility would be to only produce an error when the |
I strongly disagree with this. Rust should not provide an in-process CSPRNG. Nobody (*) should be using an in-process CSPRNG for cryptographic purposes. See this comment and the responses #130703 (comment) (I really do appreciate the error handling consideration, though.) *: Except when it's the only option, or when required to (FIPS, Common Criteria, etc.). |
@Amanieu I think it may be worth to include a |
This thread is long and has gone on a few tangents so I'll attempt to go over some of the possibilities put forward. I'll try to keep it short so sorry if I miss any nuances. I'll list them in an order that roughly reflects my preference. Names below are open to bikeshedding but for the purposes of designing the API, I feel it important to settle on the shape of the API before figuring out the perfect name. mod random {
// Fill some bytes using the OS's cryptographically secure rng.
fn fill_bytes(buf: &mut [u8]);
} The argument for this is that it has the least moving parts, is easy to document and documentation is all in one place. It doesn't rely on anything outside of the function (so just read its docs) and can't go wrong which makes auditing cryptographic code easier. No error is returned; either it fills all the bytes or, in the unlikely event it fails, the program does not continue. This means there's no potential for the buffer to be used after the call unless it succeeds. It does also mean that there's no way to recover from an error (similar to A slight variation on the first option is to return an error instead of dying. mod random {
fn fill_bytes(buf: &mut [u8]) -> Result<(), SecureRngError>;
} This allows applications to recover from the failure (e.g. maybe they didn't need cryptographically secure random data after all?). Alternatively we could simply use Another option is to use the // A trait that can be implemented for all kinds of random sources.
trait RandomSource {
fn fill_bytes(&mut self, buf: &mut [u8]);
}
// The OS's cryptographically secure rng.
pub struct DefaultRandomSource;
impl RandomSource for DefaultRandomSource { ... } This would allow it to be used with the proposed The // Uses the OS's cryptographically secure rng.
struct DefaultRandomSource;
impl Read for DefaultRandomSource {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { ... }
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.read(&mut buf).map(|_| ())
}
} The upside to this is that it uses a familiar trait. The downside is that the |
I would also just add that crates like And I'd again emphasise that adding a free standing function does not necessarily prevent adding a type + trait later so it doesn't have to be an either/or situation. |
@ChrisDenton But this function should not be the end point. I strongly believe that we need RNG traits in
As a |
I very much agree with this. I think it is a good decision.
But I also very much disagree with this for the reasons stated in my (and your) previous comments. If Rust ever needs an in-process CSPRNG then it should have a separate API that informs the user about the risks, e.g., fork safety, swap safety, reseeding, etc. |
Feature gate:
#![feature(random)]
This is a tracking issue for secure random data generation support in
std
.Central to this feature are the
Random
andRandomSource
traits insidecore::random
. TheRandom
trait defines a method to create a new random value of the implementing type from random bytes generated by aRandomSource
.std
also exposes the platform's secure random number generator via theDefaultRandomSource
type which can be conveniently access via therandom::random
function.Public API
Steps / History
random
feature (alternative version) #129201Unresolved Questions
gen_bytes
andDefaultRng
, the implementation PR usesfill_bytes
andDefaultRandomSource
(see arguments progen_bytes
and profill_bytes
)Footnotes
https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html ↩
The text was updated successfully, but these errors were encountered: