|
18 | 18 |
|
19 | 19 | // Note: the C implementation of `Jitterentropy` relies on being compiled
|
20 | 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. |
| 21 | +// compiler not optimize out code which does influence timing jitter, but is |
| 22 | +// technically dead code. |
23 | 23 |
|
24 | 24 | use rand_core::{RngCore, CryptoRng, Error, ErrorKind, impls};
|
25 | 25 |
|
@@ -47,10 +47,90 @@ const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE;
|
47 | 47 | /// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when
|
48 | 48 | /// [`OsRng`] is not available.
|
49 | 49 | ///
|
| 50 | +/// `JitterRng` can be used without the standard library, but not conveniently, |
| 51 | +/// you must provide a high-precision timer and carefully have to follow the |
| 52 | +/// instructions of [`new_with_timer`]. |
| 53 | +/// |
50 | 54 | /// This implementation is based on
|
51 | 55 | /// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0.
|
52 | 56 | ///
|
| 57 | +/// # Quality testing |
| 58 | +/// |
| 59 | +/// [`JitterRng::new()`] has build-in, but limited, quality testing, however |
| 60 | +/// before using `JitterRng` on untested hardware, or after changes that could |
| 61 | +/// effect how the code is optimized (such as a new LLVM version), it is |
| 62 | +/// recommend to run the much more stringent |
| 63 | +/// [NIST SP 800-90B Entropy Estimation Suite]( |
| 64 | +/// https://github.com/usnistgov/SP800-90B_EntropyAssessment). |
| 65 | +/// |
| 66 | +/// Use the following code using [`timer_stats`] to collect the data: |
| 67 | +/// |
| 68 | +/// ```no_run |
| 69 | +/// use rand::jitter::JitterRng; |
| 70 | +/// # |
| 71 | +/// # use std::error::Error; |
| 72 | +/// # use std::fs::File; |
| 73 | +/// # use std::io::Write; |
| 74 | +/// # |
| 75 | +/// # fn try_main() -> Result<(), Box<Error>> { |
| 76 | +/// let mut rng = JitterRng::new()?; |
| 77 | +/// |
| 78 | +/// // 1_000_000 results are required for the |
| 79 | +/// // NIST SP 800-90B Entropy Estimation Suite |
| 80 | +/// const ROUNDS: usize = 1_000_000; |
| 81 | +/// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS); |
| 82 | +/// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS); |
| 83 | +/// |
| 84 | +/// for _ in 0..ROUNDS { |
| 85 | +/// deltas_variable.push(rng.timer_stats(true) as u8); |
| 86 | +/// deltas_minimal.push(rng.timer_stats(false) as u8); |
| 87 | +/// } |
| 88 | +/// |
| 89 | +/// // Write out after the statistics collection loop, to not disturb the |
| 90 | +/// // test results. |
| 91 | +/// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; |
| 92 | +/// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; |
| 93 | +/// # |
| 94 | +/// # Ok(()) |
| 95 | +/// # } |
| 96 | +/// # |
| 97 | +/// # fn main() { |
| 98 | +/// # try_main().unwrap(); |
| 99 | +/// # } |
| 100 | +/// ``` |
| 101 | +/// |
| 102 | +/// This will produce two files: `jitter_rng_var.bin` and `jitter_rng_min.bin`. |
| 103 | +/// Run the Entropy Estimation Suite in three configurations, as outlined below. |
| 104 | +/// Every run has two steps. One step to produce an estimation, another to |
| 105 | +/// validate the estimation. |
| 106 | +/// |
| 107 | +/// 1. Estimate the expected amount of entropy that is at least available with |
| 108 | +/// each round of the entropy collector. This number should be greater than |
| 109 | +/// the amount estimated with `64 / test_timer()`. |
| 110 | +/// ```sh |
| 111 | +/// python noniid_main.py -v jitter_rng_var.bin 8 |
| 112 | +/// restart.py -v jitter_rng_var.bin 8 <min-entropy> |
| 113 | +/// ``` |
| 114 | +/// 2. Estimate the expected amount of entropy that is available in the last 4 |
| 115 | +/// bits of the timer delta after running noice sources. Note that a value of |
| 116 | +/// `3.70` is the minimum estimated entropy for true randomness. |
| 117 | +/// ```sh |
| 118 | +/// python noniid_main.py -v -u 4 jitter_rng_var.bin 4 |
| 119 | +/// restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy> |
| 120 | +/// ``` |
| 121 | +/// 3. Estimate the expected amount of entropy that is available to the entropy |
| 122 | +/// collector if both noice sources only run their minimal number of times. |
| 123 | +/// This measures the absolute worst-case, and gives a lower bound for the |
| 124 | +/// available entropy. |
| 125 | +/// ```sh |
| 126 | +/// python noniid_main.py -v -u 4 jitter_rng_min.bin 4 |
| 127 | +/// restart.py -v -u 4 jitter_rng_min.bin 4 <min-entropy> |
| 128 | +/// ``` |
| 129 | +/// |
53 | 130 | /// [`OsRng`]: struct.OsRng.html
|
| 131 | +/// [`JitterRng::new()`]: struct.JitterRng.html#method.new |
| 132 | +/// [`new_with_timer`]: struct.JitterRng.html#method.new_with_timer |
| 133 | +/// [`timer_stats`]: struct.JitterRng.html#method.timer_stats |
54 | 134 | pub struct JitterRng {
|
55 | 135 | data: u64, // Actual random number
|
56 | 136 | // Number of rounds to run the entropy collector per 64 bits
|
@@ -670,81 +750,19 @@ impl JitterRng {
|
670 | 750 | }
|
671 | 751 |
|
672 | 752 | /// Statistical test: return the timer delta of one normal run of the
|
673 |
| - /// `JitterEntropy` entropy collector. |
| 753 | + /// `JitterRng` entropy collector. |
674 | 754 | ///
|
675 | 755 | /// Setting `var_rounds` to `true` will execute the memory access and the
|
676 | 756 | /// CPU jitter noice sources a variable amount of times (just like a real
|
677 |
| - /// `JitterEntropy` round). |
| 757 | + /// `JitterRng` round). |
678 | 758 | ///
|
679 | 759 | /// Setting `var_rounds` to `false` will execute the noice sources the
|
680 | 760 | /// minimal number of times. This can be used to measure the minimum amount
|
681 |
| - /// of entropy one round of entropy collector can collect in the worst case. |
682 |
| - /// |
683 |
| - /// # Example |
684 |
| - /// |
685 |
| - /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite]( |
686 |
| - /// https://github.com/usnistgov/SP800-90B_EntropyAssessment). |
687 |
| - /// |
688 |
| - /// This is the recommended way to test the quality of `JitterRng`. It |
689 |
| - /// should be run before using the RNG on untested hardware, after changes |
690 |
| - /// that could effect how the code is optimised, and after major compiler |
691 |
| - /// compiler changes, like a new LLVM version. |
692 |
| - /// |
693 |
| - /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. |
694 |
| - /// |
695 |
| - /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it |
696 |
| - /// with `restart.py -v jitter_rng_var.bin 8 <min-entropy>`. |
697 |
| - /// This number is the expected amount of entropy that is at least available |
698 |
| - /// for each round of the entropy collector. This number should be greater |
699 |
| - /// than the amount estimated with `64 / test_timer()`. |
700 |
| - /// |
701 |
| - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and |
702 |
| - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>`. |
703 |
| - /// This number is the expected amount of entropy that is available in the |
704 |
| - /// last 4 bits of the timer delta after running noice sources. Note that |
705 |
| - /// a value of 3.70 is the minimum estimated entropy for true randomness. |
706 |
| - /// |
707 |
| - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and |
708 |
| - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 <min-entropy>`. |
709 |
| - /// This number is the expected amount of entropy that is available to the |
710 |
| - /// entropy collecter if both noice sources only run their minimal number of |
711 |
| - /// times. This measures the absolute worst-case, and gives a lower bound |
712 |
| - /// for the available entropy. |
713 |
| - /// |
714 |
| - /// ```rust,no_run |
715 |
| - /// use rand::jitter::JitterRng; |
716 |
| - /// # |
717 |
| - /// # use std::error::Error; |
718 |
| - /// # use std::fs::File; |
719 |
| - /// # use std::io::Write; |
720 |
| - /// # |
721 |
| - /// # fn try_main() -> Result<(), Box<Error>> { |
722 |
| - /// let mut rng = JitterRng::new()?; |
723 |
| - /// |
724 |
| - /// // 1_000_000 results are required for the NIST SP 800-90B Entropy |
725 |
| - /// // Estimation Suite |
726 |
| - /// const ROUNDS: usize = 1_000_000; |
727 |
| - /// let mut deltas_variable: Vec<u8> = Vec::with_capacity(ROUNDS); |
728 |
| - /// let mut deltas_minimal: Vec<u8> = Vec::with_capacity(ROUNDS); |
729 |
| - /// |
730 |
| - /// for _ in 0..ROUNDS { |
731 |
| - /// deltas_variable.push(rng.timer_stats(true) as u8); |
732 |
| - /// deltas_minimal.push(rng.timer_stats(false) as u8); |
733 |
| - /// } |
734 |
| - /// |
735 |
| - /// // Write out after the statistics collection loop, to not disturb the |
736 |
| - /// // test results. |
737 |
| - /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; |
738 |
| - /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; |
739 |
| - /// # |
740 |
| - /// # Ok(()) |
741 |
| - /// # } |
742 |
| - /// # |
743 |
| - /// # fn main() { |
744 |
| - /// # try_main().unwrap(); |
745 |
| - /// # } |
746 |
| - /// ``` |
| 761 | + /// of entropy one round of the entropy collector can collect in the worst |
| 762 | + /// case. |
747 | 763 | ///
|
| 764 | + /// See [Quality testing](struct.JitterRng.html#quality-testing) on how to |
| 765 | + /// use `timer_stats` to test the quality of `JitterRng`. |
748 | 766 | #[cfg(feature="std")]
|
749 | 767 | pub fn timer_stats(&mut self, var_rounds: bool) -> i64 {
|
750 | 768 | let mut mem = [0; MEMORY_SIZE];
|
|
0 commit comments