Repository navigation
Open
Labels
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.
Description
Feature gate: #![feature(random)]
This is a tracking issue for secure random data generation support in std.
Central to this feature is the RandomSource trait inside core::random, which generates random bytes. std also exposes the platform's secure random number generator via the DefaultRandomSource type. There is a Distribution<T> trait for distributions that can sample random values of a specific type, and a random function for convenience to allow things like random(1..=6).
Public API
// core::random
pub trait Rng {
fn fill_bytes(&mut self, bytes: &mut [u8]);
}
impl<'a, R: Rng + ?Sized> Rng for &'a mut R { ... }
pub trait Distribution<T> {
fn sample(&self, source: &mut (impl Rng + ?Sized)) -> T;
}
impl Distribution<bool> for RangeFull { ... }
impl Distribution</* all integer types */> for RangeFull { ... }
impl Distribution</* all integer types */> for core::range::RangeInclusive<_> { ... }
// std::random (additionally)
pub struct SystemRng;
impl Rng for SystemRng { ... }
pub fn random<T>(dist: impl Distribution<T>) -> T;Steps / History
- ACP: Simple secure random number generation libs-team#393
- Implementation
- std: implement the
randomfeature (alternative version) #129201 - Updates to random number generation APIs #143710 (API rework adding
Distribution<T>) - Rename
RandomSource->Rng,DefaultRandomSource->SystemRng#157539 - core: implement
Rngfor references #159435 - core: implement bounded random sampling #159592
- core: make the
Rnga by-value parameter ofDistribution::sample#160205
- std: implement the
- FCPs1 and stabilization PRs
Unresolved Questions
- Naming: the ACP used
gen_bytesandDefaultRng, the implementation PR usesfill_bytesandDefaultRandomSource(see arguments progen_bytesand profill_bytes) - Concerns listed at Simple secure random number generation libs-team#393 (comment) should be addressed
Footnotes
Reacted by Elichai Turkel, Waleed Dahshan, Gabriel Bjørnager Jensen, Barak Ugav, Jacob Pratt, İrem, Joshua Maleszewski, GalaxySnail, Wareya, Arne Keller and 35 more
Reacted by Kolsky, Ziru Niu, Tau and PrecureKamiyu
Reacted by Bugen Zhao
Reacted by Yosh, Joe Birr-Pixton, Clément Nerma, may, Adam McKellar, worstprgr, Petr Portnov | PROgrm_JARvis, Hantong Chen, Jakub Beránek, İrem and 7 more
Reacted by Gabriel Bjørnager Jensen, Martin Habovštiak, phil, Keziah Biermann, Hugo and Kristijan Sedlak
Reacted by Clément Nerma, Petr Portnov | PROgrm_JARvis, Wiktor Kwapisiewicz and Gabgobie
Reacted by Ruby Lazuli, clubby789, valkyrie_pilot, Gnome!, Fnige, Andréy Lesnikóv, 0x241F31, may, José Luis Cruz, Pascal S. de Kloe and 6 more
Metadata
Metadata
Assignees
Labels
C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.
Activity
newpavlov commentedon Nov 29, 2024
Disclaimer: I am one of the
getrandomdevelopers.I think it's important for
RandomSourcemethods to properly return potential errors. Getting randomness is an IO operation and it may fail. In some context it's important to process such errors instead of panicking. The error may be eitherio::Erroror something likegetrandom::Error(i.e. a thin wrapper aroundNonZeroU32).It may be worth to add the following methods to
RandomSource:fill_byteswhich works with uninitialized buffers, e.g. based onBorrowedBuf. Yes, zeroization of buffers is usually a very small cost compared to a syscall, but it still goes against the zero-cost spirit.u32andu64. Some platforms support direct generation of such values (e.g. RDRAND, WASI, etc.). Going throughfill_byteswill be a bit less efficient in such cases.HashMapseeding is the most obvious use-case for this.It's also not clear whether it's allowed to overwrite the default
RandomSourcesupplied withstdsimilarly toGlobalAlloc.joboet commentedon Nov 29, 2024
Would you consider rust-lang/libs-team#159 to be a better solution? That one used the
Readtrait to fulfil everything you mention.newpavlov commentedon Nov 29, 2024
No, I don't think it's an appropriate solution. Firstly, it relies on
io::Error, while IIUC you intend for this API to be available incore. Secondly, it does not provide methods for generation ofu32andu64. As I wrote, going through the byte interface is not always efficient. Finally, most ofio::Readmethods are not relevant here.For the last point I guess we could define a separate
DefaultInsecureRandomSourcetype.bstrie commentedon Nov 29, 2024
I don't think this needs to be a blocker. IMO a lot of
std::ioshould be moved intocore--not the OS-specific implementations obviously, but all the cross-platform things like type definitions, same as what happened withcore::net.wwylele commentedon Nov 29, 2024
Would it be better to make this explicit with generics? Like
fn random<R: RandomSource + ?Sized>(source: &mut R) -> Self;. This gives user the ability to specify the type name when needed.ericlagergren commentedon Nov 29, 2024
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:
This allows most CSPRNGs to use
Error = Infallible, but still has support for weird HSMs, etc.newpavlov commentedon Nov 29, 2024
@ericlagergren
I've assumed this trait is for a "system" RNG, which will work together with a
#[global_allocator]-like way to register implementation. I don't think that we need a general RNG trait instd/coreas I wrote in this comment.As for design of fallible RNG traits, see the new
rand_corecrate.dhardy commentedon Nov 30, 2024
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
from_ne_bytesalready providing safe conversion) and (2) because random value generation is a whole other topic (including uniform-ranged samples and much more).Disclaimer: I am one of the
randdevelopers.randoriginally had a similar trait which got removed; the closest surviving equivalent isStandardUniform.hanna-kruppe commentedon Nov 30, 2024
@newpavlov
Overriding the default source in an application that already has one from linking
stdseems questionable. It's not that I can't imagine any use case for it, but the established pattern for such overrides allows any crate in the dependency graph to do it (it's only an error if you link two such crates), instead of putting the leaf binary/cdylib/staticlib artifact in charge. As you articulated in the context ofgetrandom, that's a security risk for applications. So aRandomSourceequivalent should probably be more restrictive in who can override it, but that's not the existing pattern. It also doesn't seem to fit with the proposed generalization of that pattern via "externally implementable functions" (rust-lang/rfcs#3632) -- if that RFC is accepted, any new API surface should use it instead of adding new one-off mechanisms.If overriding the
stdsource isn't supported, then it could work the same way as#[panic_handler]: you must supply an implementation if you don't linkstd, but if you do linkstdthen supplying your own is an error. This would still be extremely useful. Currently, every crate that's (optionally)no_stdand needs some randomness, most commonly for seedingHashers, has to cobble together some sub-par ad-hoc solution to try and get some entropy from somewhere. There's a bunch of partial solutions that are better than nothing (const-random, taking addresses of global/local variables and praying that there's some ASLR, a global counter when atomics are available,cfg-gated access to target-specific sources like CPU cycle counters or x86RDRAND) but:wasm32-unknown-unknownandwasm32v1-nonetargets .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
getrandom, but evidently that's not happening. Putting this capability intocore(or a newno_stdsysroot crate, comparable toalloc) has a better chance of solving this coordination problem. Well, at least eventually, once everyone's MSRV has caught up.Edit: almost forgot that even
std::collections::Hash{Map,Set}depend on having a source of random seeds. A way to supply such a source without linkingstdcould help with moving those types toalloc, although as #27242 (comment) points out, it's not backwards compatible to make such a source mandatory forno_std+allocapplications.newpavlov commentedon Nov 30, 2024
@hanna-kruppe
There is a number of reasons to allow overriding:
Yes. How about following the
getrandompath and allow override only when a special configuration flag is passed to the compiler?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
stdand a way to define this source forstd-less targets.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
stdcould implicitly usestd_random_implcrate for "external implementation" of thegetrandom-like functions and users will be able to override it in application crates if necessary.I believe that having
stdforwasm32-unknown-unknownwas a big mistake in the first place and thewasm32v1-nonetarget is a good step in the direction of amending it. So I hope we will not give too much attention to its special circumstances.Well, it has happened, sort of.
getrandomis reasonably popular in the ecosystem even after excludingrandusers.The problem is that
stdalready effectively includes its variant ofgetrandomforHashMapseeding and people reasonably want to get access to that. And I think problem of getting "system" entropy is fundamental enough for having it instd(well, not in thestdper se, let's say in the sysroot crate set).Can we add yet another sysroot crate for
HashMapwhich will depend on bothallocand the hypothetical "system entropy" crate?hanna-kruppe commentedon Nov 30, 2024
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
std, then an implementation fromstdwould not count as "default" but conflict with any other definition. So we'd need another special carve-out forstd(the very thing we'd want to avoid by adding a general language feature), or the language feature needs to become much more general to support overrideable default implementations from another source.I was specifically talking about
no_stdlibraries, for which the two targets are basically equivalent. Both don't have any source of entropy implied by the target tuple (instruction set, OS, env, etc.), and if you want to add one it'll have to involve whatever application-specific interface the wasm module has with its host.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,
ahashonly usesgetrandomoptionally (though it's on by default). If you're only usingahashindirectly through another library that disables the feature, then it's not gonna usegetrandomunless you happen to notice this and add a direct dependency to enable the feature. In that case there is a solution, at least, but it's still not discoverable.Possibly, but people may object to a proliferation of sysroot crates so let's hope there's a better solution.
newpavlov commentedon Nov 30, 2024
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
getrandom, despite it being the de facto standard for getting system entropy. I think most people will agree that hacks like this have a strong smell. The same applies tofastrand(amusingly, it still usesgetrandomfor Web WASM) and other "partial solutions" listed by you. As you can notice, both hack their way into usingstdto get system entropy and either use a fixed seed or pile even more hacks whenstdis not available.hanna-kruppe commentedon Nov 30, 2024
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
std) not yet providing any entropy access. Let's keep this issue focused on changing that.196 remaining items
Auto merge of #159592 - joboet:range_distribution, r=JohnTitor
Auto merge of #159592 - joboet:range_distribution, r=JohnTitor
Auto merge of #159592 - joboet:range_distribution, r=JohnTitor
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Rollup merge of rust-lang#162212 - a1phyr:box_random, r=joboet
Unrolled build for #162212 in rollup 162310
Rollup merge of #162212 - a1phyr:box_random, r=joboet
Rollup merge of #162212 - a1phyr:box_random, r=joboet