Skip to content

Commit bc6b330

Browse files
giarc3coltfred
andauthored
Upgrade rand and dalek (#225)
* Upgrade deps * cargo sort * Add back a simple ReseedingRng * protect against overflows that couldn't ever happen, but it makes it more obviously correct * correct comment * Bump minor and add changelog * small changes * Remove the constant from the comment. Also fix other comment --------- Co-authored-by: Colt Frederickson <coltfred@gmail.com>
1 parent 3223f19 commit bc6b330

9 files changed

Lines changed: 99 additions & 49 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
# Changelog
22

3+
## 0.16.0
4+
5+
- [[#225](https://github.com/IronCoreLabs/recrypt-rs/pull/225)]
6+
- Update rand to 0.10.x and rand_chacha to 0.10.x
7+
- Add custom `ReseedingRng` wrapper since rand no longer has one. Automatically reseed from system entropy every 64 KiB of output, matching `ThreadRng`'s reseeding interval.
8+
39
## 0.15.0
410

5-
- [[#193](https://github.com/IronCoreLabs/recrypt-rs/pull/194)]
11+
- [[#193](https://github.com/IronCoreLabs/recrypt-rs/pull/193)]
612
- Update rand to 0.9.x
713
- Update gridiron to 0.12.2 to support 64 bit optimization. Note that the 64 bit backend is the default. See feature flags section of the [readme](./README.md).
814
- Change MSRV to Rust 1.88.0

Cargo.toml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "recrypt"
3-
version = "0.15.0"
3+
version = "0.16.0"
44
authors = ["IronCore Labs <info@ironcorelabs.com>"]
55
readme = "README.md"
66
license = "AGPL-3.0-only"
@@ -38,21 +38,17 @@ disable_memlock = []
3838
cfg-if = "1"
3939
clear_on_drop = "0.2"
4040
derivative = "2.1"
41-
# Can't upgrade to the latest pre until rand 0.10 is released.
42-
ed25519-dalek = { version = "=3.0.0-pre.1", default-features = false, features = [
43-
"rand_core",
44-
"fast",
45-
] }
41+
ed25519-dalek = { version = "=3.0.0-pre.6", default-features = false, features = ["rand_core", "fast"] }
4642
# Explicit dependency so we can pass the wasm-bindgen flag to it
47-
getrandom = { version = "0.3", optional = true }
43+
getrandom = { version = "0.4", optional = true }
4844
gridiron = { version = "0.12.2", default-features = false }
4945
hex = "0.4"
5046
lazy_static = "1.4"
5147
log = "0.4"
5248
num-traits = "0.2"
5349
quick-error = "2"
54-
rand = "0.9"
55-
rand_chacha = "0.9"
50+
rand = "0.10"
51+
rand_chacha = "0.10"
5652
sha2 = "0.10"
5753

5854
[target.'cfg(all(unix, not(target_arch = "wasm32")))'.dependencies]

src/api.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ use derivative::Derivative;
2424
use gridiron::fp_256::Fp256;
2525
use gridiron::fp_256::Monty as Monty256;
2626
use rand;
27-
use rand::rngs::ReseedingRng;
28-
use rand_chacha::ChaCha20Core;
2927
use std;
3028
use std::fmt;
3129

@@ -45,12 +43,7 @@ impl Recrypt<Sha256, Ed25519, RandomBytes<DefaultRng>> {
4543
///
4644
/// The RNG will periodically reseed itself from the system's best entropy source.
4745
pub fn new() -> Recrypt<Sha256, Ed25519, RandomBytes<DefaultRng>> {
48-
// 1 MB
49-
const BYTES_BEFORE_RESEEDING: u64 = 1024 * 1024;
50-
Recrypt::new_with_rand(
51-
ReseedingRng::<ChaCha20Core, _>::new(BYTES_BEFORE_RESEEDING, rand::rngs::OsRng)
52-
.expect("Calling OsRng failed to seed Rng."),
53-
)
46+
Recrypt::new_with_rand(Default::default())
5447
}
5548
}
5649

@@ -60,7 +53,7 @@ impl Default for Recrypt<Sha256, Ed25519, RandomBytes<DefaultRng>> {
6053
}
6154
}
6255

63-
impl<CR: rand::CryptoRng + rand::RngCore> Recrypt<Sha256, Ed25519, RandomBytes<CR>> {
56+
impl<CR: rand::CryptoRng> Recrypt<Sha256, Ed25519, RandomBytes<CR>> {
6457
/// Construct a Recrypt with the given RNG. Unless you have specific needs using `new()` is recommended.
6558
pub fn new_with_rand(r: CR) -> Recrypt<Sha256, Ed25519, RandomBytes<CR>> {
6659
let pairing = internal::pairing::Pairing::new();
@@ -634,9 +627,7 @@ pub trait SchnorrOps {
634627
) -> bool;
635628
}
636629

637-
impl<H: Sha256Hashing, S, CR: rand::RngCore + rand::CryptoRng> SchnorrOps
638-
for Recrypt<H, S, RandomBytes<CR>>
639-
{
630+
impl<H: Sha256Hashing, S, CR: rand::CryptoRng> SchnorrOps for Recrypt<H, S, RandomBytes<CR>> {
640631
fn schnorr_sign<A: Hashable>(
641632
&self,
642633
priv_key: &PrivateKey,
@@ -671,7 +662,7 @@ pub trait Ed25519Ops {
671662
fn generate_ed25519_key_pair(&self) -> SigningKeypair;
672663
}
673664

674-
impl<H, S, CR: rand::RngCore + rand::CryptoRng> Ed25519Ops for Recrypt<H, S, RandomBytes<CR>> {
665+
impl<H, S, CR: rand::CryptoRng> Ed25519Ops for Recrypt<H, S, RandomBytes<CR>> {
675666
///Generate a signing key pair for use with the `Ed25519Signing` trait using the random number generator
676667
///used to back the `RandomBytes` struct.
677668
fn generate_ed25519_key_pair(&self) -> SigningKeypair {

src/api_480.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ use derivative::Derivative;
2424
use gridiron::fp_480::Fp480;
2525
use gridiron::fp_480::Monty as Monty480;
2626
use rand;
27-
use rand::rngs::ReseedingRng;
28-
use rand_chacha::ChaCha20Core;
2927
use std;
3028
use std::fmt;
3129
/// Recrypt public API - 480-bit
@@ -45,12 +43,7 @@ impl Recrypt480<Sha256, Ed25519, RandomBytes<DefaultRng>> {
4543
///
4644
/// The RNG will periodically reseed itself from the system's best entropy source.
4745
pub fn new() -> Recrypt480<Sha256, Ed25519, RandomBytes<DefaultRng>> {
48-
// 2 MB
49-
const BYTES_BEFORE_RESEEDING: u64 = 2 * 1024 * 1024;
50-
Recrypt480::new_with_rand(
51-
ReseedingRng::<ChaCha20Core, _>::new(BYTES_BEFORE_RESEEDING, rand::rngs::OsRng)
52-
.expect("Calling OsRng failed to seed Rng."),
53-
)
46+
Recrypt480::new_with_rand(Default::default())
5447
}
5548
}
5649

@@ -60,7 +53,7 @@ impl Default for Recrypt480<Sha256, Ed25519, RandomBytes<DefaultRng>> {
6053
}
6154
}
6255

63-
impl<CR: rand::CryptoRng + rand::RngCore> Recrypt480<Sha256, Ed25519, RandomBytes<CR>> {
56+
impl<CR: rand::CryptoRng> Recrypt480<Sha256, Ed25519, RandomBytes<CR>> {
6457
/// Construct a Recrypt480 with the given RNG. Unless you have specific needs using `new()` is recommended.
6558
pub fn new_with_rand(r: CR) -> Recrypt480<Sha256, Ed25519, RandomBytes<CR>> {
6659
let pairing = pairing::Pairing::new();
@@ -635,9 +628,7 @@ pub trait SchnorrOps {
635628
) -> bool;
636629
}
637630

638-
impl<H: Sha256Hashing, S, CR: rand::RngCore + rand::CryptoRng> SchnorrOps
639-
for Recrypt480<H, S, RandomBytes<CR>>
640-
{
631+
impl<H: Sha256Hashing, S, CR: rand::CryptoRng> SchnorrOps for Recrypt480<H, S, RandomBytes<CR>> {
641632
fn schnorr_sign<A: Hashable>(
642633
&self,
643634
priv_key: &PrivateKey,
@@ -672,7 +663,7 @@ pub trait Ed25519Ops {
672663
fn generate_ed25519_key_pair(&self) -> SigningKeypair;
673664
}
674665

675-
impl<H, S, CR: rand::RngCore + rand::CryptoRng> Ed25519Ops for Recrypt480<H, S, RandomBytes<CR>> {
666+
impl<H, S, CR: rand::CryptoRng> Ed25519Ops for Recrypt480<H, S, RandomBytes<CR>> {
676667
///Generate a signing key pair for use with the `Ed25519Signing` trait using the random number generator
677668
///used to back the `RandomBytes` struct.
678669
fn generate_ed25519_key_pair(&self) -> SigningKeypair {

src/api_common.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::internal;
22
use quick_error::quick_error;
3-
use rand::rngs::{OsRng, ReseedingRng};
43

54
quick_error! {
65
/// Errors generated by the API
@@ -34,7 +33,7 @@ quick_error! {
3433
}
3534
}
3635

37-
pub type DefaultRng = ReseedingRng<rand_chacha::ChaChaCore, OsRng>;
36+
pub type DefaultRng = crate::api::ReseedingRng<rand_chacha::ChaCha20Rng>;
3837
pub type Result<T> = std::result::Result<T, RecryptErr>;
3938

4039
impl From<internal::InternalError> for RecryptErr {

src/internal/ed25519.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl From<SigningKeypair> for [u8; 64] {
6262

6363
impl SigningKeypair {
6464
const ENCODED_SIZE_BYTES: usize = 64;
65-
pub fn new<CR: rand::RngCore + rand::CryptoRng>(rng: &Mutex<CR>) -> SigningKeypair {
65+
pub fn new<CR: rand::CryptoRng>(rng: &Mutex<CR>) -> SigningKeypair {
6666
let signing_key = ed25519_dalek::SigningKey::generate::<CR>(&mut *take_lock(rng));
6767

6868
//Unchecked is safe because the public is on the curve and the size is statically guaranteed.

src/internal/rand_bytes.rs

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,106 @@
11
use rand;
2-
use rand::{CryptoRng, RngCore};
2+
use rand::CryptoRng;
33

44
use crate::internal::take_lock;
55
use rand::SeedableRng;
6+
use rand::rngs::SysRng;
7+
use rand::{TryCryptoRng, TryRng};
8+
use std::convert::Infallible;
69
use std::default::Default;
710
use std::ops::DerefMut;
811
use std::sync::Mutex;
912

13+
/// Reseed threshold in bytes — matches ThreadRng's 64 KiB interval.
14+
const RESEED_THRESHOLD: usize = 64 * 1024;
15+
1016
/// Generation of random bytes for cryptographic operations
1117
pub trait RandomBytesGen {
1218
fn random_bytes_32(&self) -> [u8; 32];
1319
fn random_bytes_60(&self) -> [u8; 60];
1420
}
1521

16-
pub struct RandomBytes<T: CryptoRng + RngCore> {
22+
/// A CSPRNG wrapper that automatically reseeds from system entropy periodically.
23+
/// Implements [`CryptoRng`] so it can be used anywhere a `CryptoRng` is expected.
24+
pub struct ReseedingRng<T: CryptoRng + SeedableRng> {
25+
inner: T,
26+
bytes_generated: usize,
27+
}
28+
29+
impl<T: CryptoRng + SeedableRng> Default for ReseedingRng<T> {
30+
fn default() -> Self {
31+
ReseedingRng::new(
32+
T::try_from_rng(&mut SysRng).expect("Failed to seed RNG from system entropy"),
33+
)
34+
}
35+
}
36+
37+
impl<T: CryptoRng + SeedableRng> ReseedingRng<T> {
38+
pub fn new(rng: T) -> Self {
39+
ReseedingRng {
40+
inner: rng,
41+
bytes_generated: 0,
42+
}
43+
}
44+
45+
fn reseed_if_needed(&mut self) {
46+
if self.bytes_generated >= RESEED_THRESHOLD {
47+
if let Ok(reseeded) = T::try_from_rng(&mut SysRng) {
48+
self.inner = reseeded;
49+
}
50+
// On reseed failure, continue with existing state rather than panicking.
51+
// The current state is still cryptographically valid and the likelihood of
52+
// SysRng erroring is very low.
53+
self.bytes_generated = 0;
54+
}
55+
}
56+
}
57+
58+
impl<T: CryptoRng + SeedableRng> TryRng for ReseedingRng<T> {
59+
type Error = Infallible;
60+
61+
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
62+
self.reseed_if_needed();
63+
let val = self.inner.next_u32();
64+
self.bytes_generated = self.bytes_generated.saturating_add(4);
65+
Ok(val)
66+
}
67+
68+
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
69+
self.reseed_if_needed();
70+
let val = self.inner.next_u64();
71+
self.bytes_generated = self.bytes_generated.saturating_add(8);
72+
Ok(val)
73+
}
74+
75+
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
76+
self.reseed_if_needed();
77+
self.inner.fill_bytes(dst);
78+
self.bytes_generated = self.bytes_generated.saturating_add(dst.len());
79+
Ok(())
80+
}
81+
}
82+
83+
impl<T: CryptoRng + SeedableRng> TryCryptoRng for ReseedingRng<T> {}
84+
85+
pub struct RandomBytes<T: CryptoRng> {
1786
pub(crate) rng: Mutex<T>,
1887
}
1988

20-
impl Default for RandomBytes<rand_chacha::ChaChaRng> {
89+
impl Default for RandomBytes<ReseedingRng<rand_chacha::ChaChaRng>> {
2190
fn default() -> Self {
22-
RandomBytes::new(rand_chacha::ChaChaRng::from_os_rng())
91+
RandomBytes::new(ReseedingRng::default())
2392
}
2493
}
2594

26-
impl<CR: CryptoRng + RngCore> RandomBytes<CR> {
95+
impl<CR: CryptoRng> RandomBytes<CR> {
2796
pub fn new(rng: CR) -> Self {
2897
RandomBytes {
2998
rng: Mutex::new(rng),
3099
}
31100
}
32101
}
33102

34-
impl<CR: CryptoRng + RngCore> RandomBytesGen for RandomBytes<CR> {
103+
impl<CR: CryptoRng> RandomBytesGen for RandomBytes<CR> {
35104
fn random_bytes_32(&self) -> [u8; 32] {
36105
let mut bytes: [u8; 32] = [0u8; 32];
37106
take_lock(&self.rng).deref_mut().fill_bytes(&mut bytes);

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
//!
8282
//! ## Using serde_json to serialize/deserialize the bytes of keys
8383
//!
84-
//! The bytes of the PrivateKey and PublicKey data structures can be serialized and deserialized to and from JSON String and/or a Vec<u8> using the following methods.
84+
//! The bytes of the PrivateKey and PublicKey data structures can be serialized and deserialized to and from JSON String and/or a `Vec<u8>` using the following methods.
8585
//! Simply add [serde_json](https://crates.io/crates/serde_json) as a dependency to your application.
8686
//! ```rust
8787
//! use recrypt::prelude::*;

tests/concurrency.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
use rand::SeedableRng;
2-
use rand_chacha;
31
use recrypt::prelude::*;
42
use std::sync::Arc;
53
use std::thread;
64
#[test]
75
fn generate_plaintexts() {
8-
let recrypt = Arc::new(Recrypt::new_with_rand(rand_chacha::ChaChaRng::from_os_rng()));
6+
let recrypt = Arc::new(Recrypt::new());
97

108
let mut threads = vec![];
119
for _i in 0..10 {

0 commit comments

Comments
 (0)