Skip to content

Commit 1dbef99

Browse files
committed
tink-core: allow override of global Rng generator
1 parent 0e22ea0 commit 1dbef99

File tree

2 files changed

+79
-1
lines changed

2 files changed

+79
-1
lines changed

core/src/subtle/random.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,21 @@
1919
/// Re-export the particular version of the `rand` crate whose types appear in the API.
2020
pub use rand;
2121

22+
use lazy_static::lazy_static;
2223
use rand::Rng;
24+
use std::sync::RwLock;
25+
26+
/// Possible factory functions for producing [`Generator`] instances.
27+
pub enum Factory {
28+
ThreadRng,
29+
OsRng,
30+
User(fn() -> Box<dyn Generator>),
31+
}
32+
33+
lazy_static! {
34+
/// Global factory that produces [`Generator`] instances.
35+
static ref RNG_FACTORY: RwLock<fn() -> Box<dyn Generator>> = RwLock::new(thread_rng_factory);
36+
}
2337

2438
/// Trait that encapsulates the required traits that a generator instance
2539
/// must implement.
@@ -29,9 +43,32 @@ pub trait Generator: rand::RngCore + rand::CryptoRng {}
2943
/// suitable as a Tink [`Generator`].
3044
impl<T> Generator for T where T: rand::RngCore + rand::CryptoRng {}
3145

46+
/// Factory function that produces [`rand::rngs::ThreadRng`] instances.
47+
fn thread_rng_factory() -> Box<dyn Generator> {
48+
// Available if `rand` has feature `std` enabled.
49+
Box::new(rand::thread_rng())
50+
}
51+
52+
/// Factory function that produces [`rand::rngs::OsRng`] instances.
53+
fn os_rng_factory() -> Box<dyn Generator> {
54+
// Available if `rand` has feature `getrandom` enabled.
55+
Box::new(rand::rngs::OsRng)
56+
}
57+
58+
/// Set the global factory that produces [`Generator`] instances.
59+
pub fn set_factory(factory: Factory) {
60+
let mut global = RNG_FACTORY.write().unwrap(); // safe: lock
61+
*global = match factory {
62+
Factory::ThreadRng => thread_rng_factory,
63+
Factory::OsRng => os_rng_factory,
64+
Factory::User(f) => f,
65+
};
66+
}
67+
3268
/// Return a random number generator suitable for cryptographic operation.
3369
pub fn rng() -> Box<dyn Generator> {
34-
Box::new(rand::thread_rng())
70+
let factory = RNG_FACTORY.read().unwrap(); // safe: lock
71+
factory()
3572
}
3673

3774
/// Return a vector of the given `size` filled with random bytes.

tests/tests/core/subtle/random_test.rs

+41
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,44 @@ fn test_random_uint() {
3030
let v2 = random::get_random_uint32();
3131
assert_ne!(v1, v2, "Just unlucky?");
3232
}
33+
34+
#[test]
35+
fn test_generator_factory() {
36+
random::set_factory(random::Factory::User(test_factory));
37+
let mut rng = random::rng();
38+
assert_eq!(rng.next_u32(), 4);
39+
assert_eq!(rng.next_u32(), 4);
40+
assert_eq!(rng.next_u32(), 4);
41+
random::set_factory(random::Factory::ThreadRng);
42+
let mut rng = random::rng();
43+
assert_ne!(rng.next_u64(), rng.next_u64());
44+
}
45+
46+
fn test_factory() -> Box<dyn random::Generator> {
47+
Box::new(BogusGenerator {})
48+
}
49+
50+
struct BogusGenerator;
51+
52+
impl random::rand::RngCore for BogusGenerator {
53+
fn next_u32(&mut self) -> u32 {
54+
4 // chosen by fair dice roll. guaranteed to be random.
55+
}
56+
57+
fn next_u64(&mut self) -> u64 {
58+
self.next_u32() as u64
59+
}
60+
61+
fn fill_bytes(&mut self, dest: &mut [u8]) {
62+
for b in dest {
63+
*b = 4;
64+
}
65+
}
66+
67+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), random::rand::Error> {
68+
self.fill_bytes(dest);
69+
Ok(())
70+
}
71+
}
72+
73+
impl random::rand::CryptoRng for BogusGenerator {}

0 commit comments

Comments
 (0)