Skip to content
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

Add TryRngCore and TryCryptoRng traits #1424

Merged
merged 5 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Rework fallability
  • Loading branch information
newpavlov committed May 7, 2024
commit 515ad2cbb897e502752470e3207c1a587c042b55
2 changes: 1 addition & 1 deletion .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
rm target/doc/.lock

- name: Setup Pages
uses: actions/configure-pages@v4
uses: actions/configure-pages@v5

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md).
You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful.

## [Unreleased]
- Add `rand::distributions::WeightedIndex::{weight, weights, total_weight}` (#1420)
- Bump the MSRV to 1.61.0

## [0.9.0-alpha.1] - 2024-03-18
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ compiler versions will be compatible. This is especially true of Rand's
experimental `simd_support` feature.

Rand supports limited functionality in `no_std` mode (enabled via
`default-features = false`). In this case, `OsRng` and `from_entropy` are
`default-features = false`). In this case, `OsRng` and `from_os_rng` are
unavailable (unless `getrandom` is enabled), large parts of `seq` are
unavailable (unless `alloc` is enabled), and `thread_rng` and `random` are
unavailable.
Expand Down
2 changes: 1 addition & 1 deletion rand_chacha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Links:
`rand_chacha` is `no_std` compatible when disabling default features; the `std`
feature can be explicitly required to re-enable `std` support. Using `std`
allows detection of CPU features and thus better optimisation. Using `std`
also enables `getrandom` functionality, such as `ChaCha20Rng::from_entropy()`.
also enables `getrandom` functionality, such as `ChaCha20Rng::from_os_rng()`.


# License
Expand Down
91 changes: 51 additions & 40 deletions rand_chacha/src/chacha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
use self::core::fmt;
use crate::guts::ChaCha;
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, Error, RngCore, SeedableRng};
use rand_core::{CryptoRng, RngCore, SeedableRng};

#[cfg(feature = "serde1")] use serde::{Serialize, Deserialize, Serializer, Deserializer};
#[cfg(feature = "serde1")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

// NB. this must remain consistent with some currently hard-coded numbers in this module
const BUF_BLOCKS: u8 = 4;
Expand Down Expand Up @@ -68,7 +69,7 @@ impl<T> fmt::Debug for Array64<T> {
}

macro_rules! chacha_impl {
($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr, $abst:ident) => {
($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr, $abst:ident,) => {
#[doc=$doc]
#[derive(Clone, PartialEq, Eq)]
pub struct $ChaChaXCore {
Expand All @@ -85,6 +86,7 @@ macro_rules! chacha_impl {
impl BlockRngCore for $ChaChaXCore {
type Item = u32;
type Results = Array64<u32>;

#[inline]
fn generate(&mut self, r: &mut Self::Results) {
self.state.refill4($rounds, &mut r.0);
Expand All @@ -93,9 +95,12 @@ macro_rules! chacha_impl {

impl SeedableRng for $ChaChaXCore {
type Seed = [u8; 32];

#[inline]
fn from_seed(seed: Self::Seed) -> Self {
$ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]) }
$ChaChaXCore {
state: ChaCha::new(&seed, &[0u8; 8]),
}
}
}

Expand Down Expand Up @@ -146,6 +151,7 @@ macro_rules! chacha_impl {

impl SeedableRng for $ChaChaXRng {
type Seed = [u8; 32];

#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let core = $ChaChaXCore::from_seed(seed);
Expand All @@ -160,18 +166,16 @@ macro_rules! chacha_impl {
fn next_u32(&mut self) -> u32 {
self.rng.next_u32()
}

#[inline]
fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
}

#[inline]
fn fill_bytes(&mut self, bytes: &mut [u8]) {
self.rng.fill_bytes(bytes)
}
#[inline]
fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> {
self.rng.try_fill_bytes(bytes)
}
}

impl $ChaChaXRng {
Expand Down Expand Up @@ -209,11 +213,9 @@ macro_rules! chacha_impl {
#[inline]
pub fn set_word_pos(&mut self, word_offset: u128) {
let block = (word_offset / u128::from(BLOCK_WORDS)) as u64;
self.rng.core.state.set_block_pos(block);
self.rng
.core
.state
.set_block_pos(block);
self.rng.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize);
}

/// Set the stream number.
Expand All @@ -229,10 +231,7 @@ macro_rules! chacha_impl {
/// indirectly via `set_word_pos`), but this is not directly supported.
#[inline]
pub fn set_stream(&mut self, stream: u64) {
self.rng
.core
.state
.set_nonce(stream);
self.rng.core.state.set_nonce(stream);
if self.rng.index() != 64 {
let wp = self.get_word_pos();
self.set_word_pos(wp);
Expand All @@ -242,24 +241,20 @@ macro_rules! chacha_impl {
/// Get the stream number.
#[inline]
pub fn get_stream(&self) -> u64 {
self.rng
.core
.state
.get_nonce()
self.rng.core.state.get_nonce()
}

/// Get the seed.
#[inline]
pub fn get_seed(&self) -> [u8; 32] {
self.rng
.core
.state
.get_seed()
self.rng.core.state.get_seed()
}
}

impl CryptoRng for $ChaChaXRng {}

rand_core::impl_try_crypto_rng_from_crypto_rng!($ChaChaXRng);

impl From<$ChaChaXCore> for $ChaChaXRng {
fn from(core: $ChaChaXCore) -> Self {
$ChaChaXRng {
Expand All @@ -286,22 +281,20 @@ macro_rules! chacha_impl {
}
#[cfg(feature = "serde1")]
impl<'de> Deserialize<'de> for $ChaChaXRng {
fn deserialize<D>(d: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where D: Deserializer<'de> {
$abst::$ChaChaXRng::deserialize(d).map(|x| Self::from(&x))
}
}

mod $abst {
#[cfg(feature = "serde1")] use serde::{Serialize, Deserialize};
#[cfg(feature = "serde1")] use serde::{Deserialize, Serialize};

// The abstract state of a ChaCha stream, independent of implementation choices. The
// comparison and serialization of this object is considered a semver-covered part of
// the API.
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(
feature = "serde1",
derive(Serialize, Deserialize),
)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub(crate) struct $ChaChaXRng {
seed: [u8; 32],
stream: u64,
Expand Down Expand Up @@ -331,27 +324,45 @@ macro_rules! chacha_impl {
}
}
}
}
};
}

chacha_impl!(ChaCha20Core, ChaCha20Rng, 10, "ChaCha with 20 rounds", abstract20);
chacha_impl!(ChaCha12Core, ChaCha12Rng, 6, "ChaCha with 12 rounds", abstract12);
chacha_impl!(ChaCha8Core, ChaCha8Rng, 4, "ChaCha with 8 rounds", abstract8);
chacha_impl!(
ChaCha20Core,
ChaCha20Rng,
10,
"ChaCha with 20 rounds",
abstract20,
);
chacha_impl!(
ChaCha12Core,
ChaCha12Rng,
6,
"ChaCha with 12 rounds",
abstract12,
);
chacha_impl!(
ChaCha8Core,
ChaCha8Rng,
4,
"ChaCha with 8 rounds",
abstract8,
);

#[cfg(test)]
mod test {
use rand_core::{RngCore, SeedableRng};

#[cfg(feature = "serde1")] use super::{ChaCha20Rng, ChaCha12Rng, ChaCha8Rng};
#[cfg(feature = "serde1")] use super::{ChaCha12Rng, ChaCha20Rng, ChaCha8Rng};

type ChaChaRng = super::ChaCha20Rng;

#[cfg(feature = "serde1")]
#[test]
fn test_chacha_serde_roundtrip() {
let seed = [
1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, 0, 0, 0, 0,
0, 2, 92,
1, 0, 52, 0, 0, 0, 0, 0, 1, 0, 10, 0, 22, 32, 0, 0, 2, 0, 55, 49, 0, 11, 0, 0, 3, 0, 0,
0, 0, 0, 2, 92,
];
let mut rng1 = ChaCha20Rng::from_seed(seed);
let mut rng2 = ChaCha12Rng::from_seed(seed);
Expand Down Expand Up @@ -388,7 +399,7 @@ mod test {
#[test]
fn test_chacha_serde_format_stability() {
let j = r#"{"seed":[4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8,15,16,23,42,4,8],"stream":27182818284,"word_pos":314159265359}"#;
let r: ChaChaRng = serde_json::from_str(&j).unwrap();
let r: ChaChaRng = serde_json::from_str(j).unwrap();
let j1 = serde_json::to_string(&r).unwrap();
assert_eq!(j, j1);
}
Expand All @@ -402,7 +413,7 @@ mod test {
let mut rng1 = ChaChaRng::from_seed(seed);
assert_eq!(rng1.next_u32(), 137206642);

let mut rng2 = ChaChaRng::from_rng(rng1).unwrap();
let mut rng2 = ChaChaRng::from_rng(&mut rng1);
assert_eq!(rng2.next_u32(), 1325750369);
}

Expand Down Expand Up @@ -598,7 +609,7 @@ mod test {

#[test]
fn test_chacha_word_pos_wrap_exact() {
use super::{BUF_BLOCKS, BLOCK_WORDS};
use super::{BLOCK_WORDS, BUF_BLOCKS};
let mut rng = ChaChaRng::from_seed(Default::default());
// refilling the buffer in set_word_pos will wrap the block counter to 0
let last_block = (1 << 68) - u128::from(BUF_BLOCKS * BLOCK_WORDS);
Expand Down
91 changes: 91 additions & 0 deletions rand_core/src/blanket_impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#[cfg(feature = "alloc")] use alloc::boxed::Box;

use crate::{CryptoRng, RngCore, TryCryptoRng, TryRngCore};

impl<'a, R: RngCore + ?Sized> RngCore for &'a mut R {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
R::next_u32(self)
}

#[inline(always)]
fn next_u64(&mut self) -> u64 {
R::next_u64(self)
}

#[inline(always)]
fn fill_bytes(&mut self, dst: &mut [u8]) {
R::fill_bytes(self, dst)
}
}

impl<'a, R: CryptoRng + ?Sized> CryptoRng for &'a mut R {}

impl<'a, R: TryRngCore + ?Sized> TryRngCore for &'a mut R {
type Error = R::Error;

#[inline(always)]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
R::try_next_u32(self)
}

#[inline(always)]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
R::try_next_u64(self)
}

#[inline(always)]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
R::try_fill_bytes(self, dst)
}
}

impl<'a, R: TryCryptoRng + ?Sized> TryCryptoRng for &'a mut R {}

#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl<R: RngCore + ?Sized> RngCore for Box<R> {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
R::next_u32(self)
}

#[inline(always)]
fn next_u64(&mut self) -> u64 {
R::next_u64(self)
}

#[inline(always)]
fn fill_bytes(&mut self, dest: &mut [u8]) {
R::fill_bytes(self, dest)
}
}

#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl<R: CryptoRng + ?Sized> CryptoRng for Box<R> {}

#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl<R: TryRngCore + ?Sized> TryRngCore for Box<R> {
type Error = R::Error;

#[inline(always)]
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
R::try_next_u32(self)
}

#[inline(always)]
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
R::try_next_u64(self)
}

#[inline(always)]
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
R::try_fill_bytes(self, dst)
}
}

#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl<R: TryCryptoRng + ?Sized> TryCryptoRng for Box<R> {}
Loading