Skip to content

Commit 81530c9

Browse files
committed
crypto: Add support for a seedable RNG
1 parent 023576c commit 81530c9

File tree

3 files changed

+26
-11
lines changed

3 files changed

+26
-11
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ base64 = "0.22"
2323
thiserror = "1.0.20"
2424
chrono = "0.4.13"
2525
lazy_static = "1.4.0"
26+
rand_chacha = "0.3"
2627

2728
# Optional
2829
serde = { version = "1", features=["derive"], optional = true }

src/stdlib/crypto.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::convert::TryFrom;
22

3-
use bitcoin::hashes::{self, Hash};
3+
use bitcoin::hashes::{self, sha256, Hash};
44
use bitcoin::secp256k1::{self, rand};
5-
use rand::{random, thread_rng, RngCore};
5+
use rand::{thread_rng, Rng, RngCore, SeedableRng};
6+
use rand_chacha::ChaCha12Rng;
67

78
use crate::runtime::scope::{Mutable, ScopeRef};
89
use crate::runtime::{Array, Error, Result, Value};
@@ -101,26 +102,38 @@ pub mod fns {
101102
}
102103

103104
// Generate a random Bytes sequence
104-
/// rand::bytes(Int size) -> Bytes
105+
/// rand::bytes(Int size[, Bytes seed]) -> Bytes
105106
pub fn rand_bytes(args: Array, _: &ScopeRef) -> Result<Value> {
106-
let size = args.arg_into()?;
107+
let (size, seed) = args.args_into()?;
107108
let mut bytes = vec![0u8; size];
108-
thread_rng().fill_bytes(&mut bytes);
109+
make_rng(seed).fill_bytes(&mut bytes);
109110
Ok(bytes.into())
110111
}
111112

112113
/// Generate a random signed 64-bit integer
113-
/// rand::i64() -> Int
114+
/// rand::i64([Bytes seed]) -> Int
114115
pub fn rand_i64(args: Array, _: &ScopeRef) -> Result<Value> {
115-
args.no_args()?;
116-
Ok(random::<i64>().into())
116+
let seed = args.arg_into()?;
117+
Ok(make_rng(seed).gen::<i64>().into())
117118
}
118119

119120
/// Generate a random 64-bit float in the [0, 1) range
120-
/// rand::f64() -> Float
121+
/// rand::f64([Bytes seed]) -> Float
121122
pub fn rand_f64(args: Array, _: &ScopeRef) -> Result<Value> {
122-
args.no_args()?;
123-
Ok(random::<f64>().into())
123+
let seed = args.arg_into()?;
124+
Ok(make_rng(seed).gen::<f64>().into())
125+
}
126+
127+
fn make_rng(seed: Option<Vec<u8>>) -> ChaCha12Rng {
128+
// Uses ChaCha12 (rand's current default StdRng) directly for reproducibility
129+
// From https://docs.rs/rand/latest/rand/rngs/struct.StdRng.html: "The algorithm is deterministic but should not be
130+
// considered reproducible due to dependence on configuration and possible replacement in future library versions.
131+
// For a secure reproducible generator, we recommend use of the rand_chacha crate directly."
132+
if let Some(seed) = seed {
133+
ChaCha12Rng::from_seed(sha256::Hash::hash(&seed).to_byte_array())
134+
} else {
135+
ChaCha12Rng::from_rng(thread_rng()).unwrap()
136+
}
124137
}
125138
}
126139

0 commit comments

Comments
 (0)