16
16
17
17
//! Non-physical true random number generator based on timing jitter.
18
18
19
+ // Note: the C implementation of `Jitterentropy` relies on being compiled
20
+ // without optimizations. This implementation goes through lengths to make the
21
+ // compiler not optimise out what is technically dead code, but that does
22
+ // influence timing jitter.
23
+
19
24
use rand_core:: { RngCore , CryptoRng , Error , ErrorKind , impls} ;
20
25
21
26
use core:: { fmt, mem, ptr} ;
@@ -31,7 +36,7 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE;
31
36
///
32
37
/// This is a true random number generator, as opposed to pseudo-random
33
38
/// generators. Random numbers generated by `JitterRng` can be seen as fresh
34
- /// entropy. A consequence is that is orders of magnitude slower than `OsRng`
39
+ /// entropy. A consequence is that is orders of magnitude slower than [ `OsRng`]
35
40
/// and PRNGs (about 10<sup>3</sup>..10<sup>6</sup> slower).
36
41
///
37
42
/// There are very few situations where using this RNG is appropriate. Only very
@@ -40,21 +45,19 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE;
40
45
/// predict.
41
46
///
42
47
/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when
43
- /// `OsRng` is not available.
48
+ /// [ `OsRng`] is not available.
44
49
///
45
50
/// This implementation is based on
46
51
/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0.
47
- //
48
- // Note: the C implementation relies on being compiled without optimizations.
49
- // This implementation goes through lengths to make the compiler not optimise
50
- // out what is technically dead code, but that does influence timing jitter.
52
+ ///
53
+ /// [`OsRng`]: os/struct.OsRng.html
51
54
pub struct JitterRng {
52
55
data : u64 , // Actual random number
53
56
// Number of rounds to run the entropy collector per 64 bits
54
57
rounds : u8 ,
55
58
// Timer used by `measure_jitter`
56
59
timer : fn ( ) -> u64 ,
57
- // Memory for the Memory Access noise source FIXME
60
+ // Memory for the Memory Access noise source
58
61
mem_prev_index : u16 ,
59
62
// Make `next_u32` not waste 32 bits
60
63
data_half_used : bool ,
@@ -100,7 +103,9 @@ impl fmt::Debug for JitterRng {
100
103
}
101
104
}
102
105
103
- /// An error that can occur when `test_timer` fails.
106
+ /// An error that can occur when [`JitterRng::test_timer`] fails.
107
+ ///
108
+ /// [`JitterRng::test_timer`]: struct.JitterRng.html#method.test_timer
104
109
#[ derive( Debug , Clone , PartialEq , Eq ) ]
105
110
pub enum TimerError {
106
111
/// No timer available.
@@ -157,8 +162,9 @@ impl From<TimerError> for Error {
157
162
static JITTER_ROUNDS : AtomicUsize = ATOMIC_USIZE_INIT ;
158
163
159
164
impl JitterRng {
160
- /// Create a new `JitterRng`.
161
- /// Makes use of `std::time` for a timer.
165
+ /// Create a new `JitterRng`. Makes use of `std::time` for a timer, or a
166
+ /// platform-specific function with higher accuracy if necessary and
167
+ /// available.
162
168
///
163
169
/// During initialization CPU execution timing jitter is measured a few
164
170
/// hundred times. If this does not pass basic quality tests, an error is
@@ -188,10 +194,44 @@ impl JitterRng {
188
194
/// The timer must have nanosecond precision.
189
195
///
190
196
/// This method is more low-level than `new()`. It is the responsibility of
191
- /// the caller to run `test_timer` before using any numbers generated with
192
- /// `JitterRng`, and optionally call `set_rounds()`. Also it is important to
193
- /// call `gen_entropy` once before using the first result to initialize the
194
- /// entropy collection pool.
197
+ /// the caller to run [`test_timer`] before using any numbers generated with
198
+ /// `JitterRng`, and optionally call [`set_rounds`]. Also it is important to
199
+ /// consume at least one `u64` before using the first result to initialize
200
+ /// the entropy collection pool.
201
+ ///
202
+ /// # Example
203
+ ///
204
+ /// ```rust
205
+ /// # use rand::{Rng, Error};
206
+ /// use rand::JitterRng;
207
+ ///
208
+ /// # fn try_inner() -> Result<(), Error> {
209
+ /// fn get_nstime() -> u64 {
210
+ /// use std::time::{SystemTime, UNIX_EPOCH};
211
+ ///
212
+ /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
213
+ /// // The correct way to calculate the current time is
214
+ /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
215
+ /// // But this is faster, and the difference in terms of entropy is
216
+ /// // negligible (log2(10^9) == 29.9).
217
+ /// dur.as_secs() << 30 | dur.subsec_nanos() as u64
218
+ /// }
219
+ ///
220
+ /// let mut rng = JitterRng::new_with_timer(get_nstime);
221
+ /// let rounds = rng.test_timer()?;
222
+ /// rng.set_rounds(rounds); // optional
223
+ /// let _ = rng.gen::<u64>();
224
+ ///
225
+ /// // Ready for use
226
+ /// let v: u64 = rng.gen();
227
+ /// # Ok(())
228
+ /// # }
229
+ ///
230
+ /// # try_inner().unwrap();
231
+ /// ```
232
+ ///
233
+ /// [`test_timer`]: struct.JitterRng.html#method.test_timer
234
+ /// [`set_rounds`]: struct.JitterRng.html#method.set_rounds
195
235
pub fn new_with_timer ( timer : fn ( ) -> u64 ) -> JitterRng {
196
236
JitterRng {
197
237
data : 0 ,
@@ -206,10 +246,12 @@ impl JitterRng {
206
246
/// This must be greater than zero, and has a big impact on performance
207
247
/// and output quality.
208
248
///
209
- /// `new_with_timer` conservatively uses 64 rounds, but often less rounds
249
+ /// [ `new_with_timer`] conservatively uses 64 rounds, but often less rounds
210
250
/// can be used. The `test_timer()` function returns the minimum number of
211
251
/// rounds required for full strength (platform dependent), so one may use
212
252
/// `rng.set_rounds(rng.test_timer()?);` or cache the value.
253
+ ///
254
+ /// [`new_with_timer`]: struct.JitterRng.html#method.new_with_timer
213
255
pub fn set_rounds ( & mut self , rounds : u8 ) {
214
256
assert ! ( rounds > 0 ) ;
215
257
self . rounds = rounds;
@@ -447,17 +489,14 @@ impl JitterRng {
447
489
self . data
448
490
}
449
491
450
- #[ cfg( all( target_arch = "wasm32" , not( target_os = "emscripten" ) ) ) ]
451
- pub fn test_timer ( & mut self ) -> Result < u8 , TimerError > {
452
- return Err ( TimerError :: NoTimer ) ;
453
- }
454
-
455
492
/// Basic quality tests on the timer, by measuring CPU timing jitter a few
456
493
/// hundred times.
457
494
///
458
495
/// If succesful, this will return the estimated number of rounds necessary
459
- /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause
496
+ /// to collect 64 bits of entropy. Otherwise a [ `TimerError`] with the cause
460
497
/// of the failure will be returned.
498
+ ///
499
+ /// [`TimerError`]: jitter/enum.TimerError.html
461
500
#[ cfg( not( all( target_arch = "wasm32" , not( target_os = "emscripten" ) ) ) ) ]
462
501
pub fn test_timer ( & mut self ) -> Result < u8 , TimerError > {
463
502
debug ! ( "JitterRng: testing timer ..." ) ;
@@ -595,6 +634,10 @@ impl JitterRng {
595
634
Ok ( log2_lookup[ delta_average as usize ] )
596
635
}
597
636
}
637
+ #[ cfg( all( target_arch = "wasm32" , not( target_os = "emscripten" ) ) ) ]
638
+ pub fn test_timer ( & mut self ) -> Result < u8 , TimerError > {
639
+ return Err ( TimerError :: NoTimer ) ;
640
+ }
598
641
599
642
/// Statistical test: return the timer delta of one normal run of the
600
643
/// `JitterEntropy` entropy collector.
@@ -640,33 +683,17 @@ impl JitterRng {
640
683
///
641
684
/// ```rust,no_run
642
685
/// use rand::JitterRng;
643
- ///
686
+ /// #
644
687
/// # use std::error::Error;
645
688
/// # use std::fs::File;
646
689
/// # use std::io::Write;
647
690
/// #
648
691
/// # fn try_main() -> Result<(), Box<Error>> {
649
- /// fn get_nstime() -> u64 {
650
- /// use std::time::{SystemTime, UNIX_EPOCH};
651
- ///
652
- /// let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
653
- /// // The correct way to calculate the current time is
654
- /// // `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
655
- /// // But this is faster, and the difference in terms of entropy is
656
- /// // negligible (log2(10^9) == 29.9).
657
- /// dur.as_secs() << 30 | dur.subsec_nanos() as u64
658
- /// }
659
- ///
660
- /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`.
661
- /// // 'new' always runst `test_timer`, and can therefore fail to
662
- /// // initialize. We want to be able to get the statistics even when the
663
- /// // timer test fails.
664
- /// let mut rng = JitterRng::new_with_timer(get_nstime);
692
+ /// let mut rng = JitterRng::new()?;
665
693
///
666
694
/// // 1_000_000 results are required for the NIST SP 800-90B Entropy
667
695
/// // Estimation Suite
668
- /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow
669
- /// const ROUNDS: usize = 10_000;
696
+ /// const ROUNDS: usize = 1_000_000;
670
697
/// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS);
671
698
/// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS);
672
699
///
@@ -687,6 +714,7 @@ impl JitterRng {
687
714
/// # try_main().unwrap();
688
715
/// # }
689
716
/// ```
717
+ ///
690
718
#[ cfg( feature="std" ) ]
691
719
pub fn timer_stats ( & mut self , var_rounds : bool ) -> i64 {
692
720
let mut mem = [ 0 ; MEMORY_SIZE ] ;
@@ -701,27 +729,28 @@ impl JitterRng {
701
729
702
730
#[ cfg( feature="std" ) ]
703
731
mod platform {
704
- #[ cfg( not( any( target_os = "macos" , target_os = "ios" , target_os = "windows" , all( target_arch = "wasm32" , not( target_os = "emscripten" ) ) ) ) ) ]
732
+ #[ cfg( not( any( target_os = "macos" , target_os = "ios" , target_os = "windows" ,
733
+ all( target_arch = "wasm32" , not( target_os = "emscripten" ) ) ) ) ) ]
705
734
pub fn get_nstime ( ) -> u64 {
706
735
use std:: time:: { SystemTime , UNIX_EPOCH } ;
707
736
708
737
let dur = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) ;
709
738
// The correct way to calculate the current time is
710
739
// `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
711
- // But this is faster, and the difference in terms of entropy is negligible
712
- // (log2(10^9) == 29.9).
740
+ // But this is faster, and the difference in terms of entropy is
741
+ // negligible (log2(10^9) == 29.9).
713
742
dur. as_secs ( ) << 30 | dur. subsec_nanos ( ) as u64
714
743
}
715
744
716
745
#[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
717
746
pub fn get_nstime ( ) -> u64 {
718
747
extern crate libc;
719
748
// On Mac OS and iOS std::time::SystemTime only has 1000ns resolution.
720
- // We use `mach_absolute_time` instead. This provides a CPU dependent unit,
721
- // to get real nanoseconds the result should by multiplied by numer/denom
722
- // from `mach_timebase_info`.
723
- // But we are not interested in the exact nanoseconds, just entropy. So we
724
- // use the raw result.
749
+ // We use `mach_absolute_time` instead. This provides a CPU dependent
750
+ // unit, to get real nanoseconds the result should by multiplied by
751
+ // numer/denom from `mach_timebase_info`.
752
+ // But we are not interested in the exact nanoseconds, just entropy. So
753
+ // we use the raw result.
725
754
unsafe { libc:: mach_absolute_time ( ) }
726
755
}
727
756
0 commit comments