prng.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Copyright (c) 2018, The Tor Project, Inc.
  2. // Copyright (c) 2018, isis agora lovecruft
  3. // See LICENSE for licensing information
  4. //! Wrappers for Tor's pseudo-random number generator to provide implementations
  5. //! of `rand_core` traits.
  6. use rand_core::impls;
  7. #[cfg(test)] use rand_core::CryptoRng;
  8. use rand_core::Error;
  9. use rand_core::RngCore;
  10. use rand_core::SeedableRng;
  11. /// A cryptographically-/insecure/ psuedo-random number generator based
  12. /// on a mixed congruential generator.
  13. ///
  14. /// Specifically the PRNG state, `X`, is mutated by the following
  15. /// discontinuous linear equation:
  16. ///
  17. /// ```text
  18. /// X_{i} = (a X_{i-1} + b) mod n
  19. /// ```
  20. ///
  21. /// where, in our case, we reuse the same parameters as OpenBSD and glibc,
  22. /// `a=1103515245`, `b=12345`, and `n=2147483647`, which should produce a
  23. /// maximal period over the range `0..u32::MAX`.
  24. ///
  25. /// # Note
  26. ///
  27. /// We reimplement the C here, rather than wrapping it, as it's one line of
  28. /// pure-Rust code (meaning it can also trivially be used in Rust tests without
  29. /// running into potential linker issues), as opposed to a few lines of `unsafe`
  30. /// calls to C.
  31. ///
  32. /// # Warning
  33. ///
  34. /// This should hopefully go without saying, but this PRNG is completely
  35. /// insecure and should never be used for anything an adversary should be unable
  36. /// to predict.
  37. //
  38. // C_RUST_COUPLED: `tor_weak_rng_t` /src/common/util.c
  39. pub struct TorInsecurePrng {
  40. state: u32,
  41. }
  42. impl SeedableRng for TorInsecurePrng {
  43. type Seed = [u8; 4];
  44. /// Create a new PRNG from a random 32-bit seed.
  45. //
  46. // C_RUST_COUPLED: `tor_init_weak_random()` /src/common/util.c
  47. fn from_seed(seed: Self::Seed) -> Self {
  48. let mut combined: u32 = seed[0].to_le() as u32;
  49. // Rather than using std::mem::transmute, we'll just bitwise-OR them
  50. // into each other.
  51. combined = (seed[1].to_le() as u32) << 8 | combined;
  52. combined = (seed[2].to_le() as u32) << 16 | combined;
  53. combined = (seed[2].to_le() as u32) << 24 | combined;
  54. TorInsecurePrng{ state: (combined & 0x7fffffff).to_le() }
  55. }
  56. }
  57. impl TorInsecurePrng {
  58. /// This is the equivalent function to `tor_weak_random()`.
  59. //
  60. // C_RUST_COUPLED: `tor_weak_random()` /src/common/util.c
  61. pub fn next_i32(&mut self) -> i32 {
  62. // The C code appears to purposefully overflow the 32-bit state integer.
  63. self.state = (self.state.wrapping_mul(1103515245).wrapping_add(12345) & 0x7fffffff).to_le();
  64. self.state as i32
  65. }
  66. }
  67. impl RngCore for TorInsecurePrng {
  68. // C_RUST_COUPLED: `tor_weak_random()` /src/common/util.c
  69. fn next_u32(&mut self) -> u32 {
  70. let x: u32 = self.next_i32() as u32;
  71. let y: u32 = self.next_i32() as u32;
  72. // We have to add two samples together due to modding 0x7fffffff
  73. x + y
  74. }
  75. fn next_u64(&mut self) -> u64 {
  76. impls::next_u64_via_u32(self)
  77. }
  78. fn fill_bytes(&mut self, dest: &mut [u8]) {
  79. impls::fill_bytes_via_u32(self, dest);
  80. }
  81. fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
  82. Ok(self.fill_bytes(dest))
  83. }
  84. }
  85. /// If we're running tests, it's fine to pretend this PRNG is cryptographically
  86. /// secure. (This allows us to test which require an implementation of
  87. /// `CryptoRng` without actually initialising all the OpenSSL C code.)
  88. #[cfg(test)]
  89. impl CryptoRng for TorInsecurePrng {}
  90. #[cfg(test)]
  91. mod test {
  92. use super::*;
  93. #[test]
  94. fn next_u32_shouldnt_return_same_number_twice_in_a_row() {
  95. // This test will fail 1 out of 2^{64} times (5.42 e-20), but the
  96. // probability of a particle radiating off a star and hitting your RAM
  97. // is roughly 1.4 e-15 per byte of RAM per second, so if this fails,
  98. // blame ~~Cosmic Rays~~ and not anyone named isis.
  99. let mut prng: TorInsecurePrng = TorInsecurePrng::from_seed([0xDE, 0xAD, 0x15, 0x15]);
  100. let one: u32 = prng.next_u32();
  101. let two: u32 = prng.next_u32();
  102. assert!(one != two);
  103. }
  104. #[test]
  105. fn next_u32_should_have_uniform_distribution_average() {
  106. let mut prng: TorInsecurePrng = TorInsecurePrng::from_seed([0xDE, 0xAD, 0x15, 0x15]);
  107. let mut accumulator: Vec<u32> = Vec::new();
  108. let n: u64 = 10_000;
  109. for _ in 0 .. n as usize {
  110. accumulator.push(prng.next_u32());
  111. }
  112. let total: u64 = accumulator.iter().fold(0, |acc,&x| acc + (x as u64));
  113. let average = total / n;
  114. println!("average is {:?}", average);
  115. assert!(average <= 0x7fffffff + 0xf00000);
  116. assert!(average >= 0x7fffffff - 0xf00000);
  117. }
  118. #[test]
  119. fn next_u32_shouldnt_have_bit_bias() {
  120. // Since the modulus in the mixed congruential generator isn't a power
  121. // of two, the bits should not have any statistical bias.
  122. let mut prng: TorInsecurePrng = TorInsecurePrng::from_seed([0xDE, 0xAD, 0x15, 0x15]);
  123. let mut accumulator: Vec<u32> = Vec::new();
  124. let n: u64 = 10_000;
  125. for _ in 0 .. n as usize {
  126. accumulator.push(prng.next_u32().count_ones());
  127. }
  128. let total: u64 = accumulator.iter().fold(0, |acc,&x| acc + (x as u64));
  129. let average = total / n;
  130. println!("average is {:?}", average);
  131. assert!(average == 16);
  132. }
  133. #[test]
  134. fn next_u64_shouldnt_return_same_number_twice_in_a_row() {
  135. // This test will fail 1 out of 2^{128} times (2.94 e-39), but the
  136. // probability of a particle radiating off a star and hitting your RAM
  137. // is roughly 1.4 e-15 per byte of RAM per second, so if this fails,
  138. // blame ~~Cosmic Rays~~ and not anyone named isis.
  139. let mut prng: TorInsecurePrng = TorInsecurePrng::from_seed([0xDE, 0xAD, 0x15, 0x15]);
  140. let one: u64 = prng.next_u64();
  141. let two: u64 = prng.next_u64();
  142. assert!(one != two);
  143. }
  144. #[test]
  145. fn fill_bytes_shouldnt_leave_all_zeroes() {
  146. // Again, 1 in 256^8 (5.42 e-20) chances this fails.
  147. // ~~Cosmic Rays~~, I tell you.
  148. let mut prng: TorInsecurePrng = TorInsecurePrng::from_seed([0xDE, 0xAD, 0x15, 0x15]);
  149. let mut bytes: [u8; 8] = [0u8; 8];
  150. prng.fill_bytes(&mut bytes);
  151. assert!(bytes != [0u8; 8]);
  152. }
  153. }