| 1 | // Copyright 2015-2016 Brian Smith. |
| 2 | // |
| 3 | // Permission to use, copy, modify, and/or distribute this software for any |
| 4 | // purpose with or without fee is hereby granted, provided that the above |
| 5 | // copyright notice and this permission notice appear in all copies. |
| 6 | // |
| 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
| 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY |
| 10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 14 | |
| 15 | //! Cryptographic pseudo-random number generation. |
| 16 | //! |
| 17 | //! *ring* functions that generate random bytes take a `&dyn SecureRandom` |
| 18 | //! parameter to make it clear which functions are non-deterministic. |
| 19 | |
| 20 | use crate::error; |
| 21 | |
| 22 | /// A secure random number generator. |
| 23 | pub trait SecureRandom: sealed::SecureRandom { |
| 24 | /// Fills `dest` with random bytes. |
| 25 | fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>; |
| 26 | } |
| 27 | |
| 28 | impl<T> SecureRandom for T |
| 29 | where |
| 30 | T: sealed::SecureRandom, |
| 31 | { |
| 32 | #[inline (always)] |
| 33 | fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> { |
| 34 | self.fill_impl(dest) |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | /// A random value constructed from a `SecureRandom` that hasn't been exposed |
| 39 | /// through any safe Rust interface. |
| 40 | /// |
| 41 | /// Intentionally does not implement any traits other than `Sized`. |
| 42 | pub struct Random<T: RandomlyConstructable>(T); |
| 43 | |
| 44 | impl<T: RandomlyConstructable> Random<T> { |
| 45 | /// Expose the random value. |
| 46 | #[inline ] |
| 47 | pub fn expose(self) -> T { |
| 48 | self.0 |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | /// Generate the new random value using `rng`. |
| 53 | #[inline ] |
| 54 | pub fn generate<T: RandomlyConstructable>( |
| 55 | rng: &dyn SecureRandom, |
| 56 | ) -> Result<Random<T>, error::Unspecified> { |
| 57 | let mut r: T = T::zero(); |
| 58 | rng.fill(dest:r.as_mut_bytes())?; |
| 59 | Ok(Random(r)) |
| 60 | } |
| 61 | |
| 62 | pub(crate) mod sealed { |
| 63 | use crate::error; |
| 64 | |
| 65 | pub trait SecureRandom: core::fmt::Debug { |
| 66 | /// Fills `dest` with random bytes. |
| 67 | fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>; |
| 68 | } |
| 69 | |
| 70 | pub trait RandomlyConstructable: Sized { |
| 71 | fn zero() -> Self; // `Default::default()` |
| 72 | fn as_mut_bytes(&mut self) -> &mut [u8]; // `AsMut<[u8]>::as_mut` |
| 73 | } |
| 74 | |
| 75 | impl<const N: usize> RandomlyConstructable for [u8; N] { |
| 76 | #[inline ] |
| 77 | fn zero() -> Self { |
| 78 | [0; N] |
| 79 | } |
| 80 | |
| 81 | #[inline ] |
| 82 | fn as_mut_bytes(&mut self) -> &mut [u8] { |
| 83 | &mut self[..] |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | /// A type that can be returned by `ring::rand::generate()`. |
| 89 | pub trait RandomlyConstructable: sealed::RandomlyConstructable {} |
| 90 | impl<T> RandomlyConstructable for T where T: sealed::RandomlyConstructable {} |
| 91 | |
| 92 | /// A secure random number generator where the random values come directly |
| 93 | /// from the operating system. |
| 94 | /// |
| 95 | /// "Directly from the operating system" here presently means "whatever the |
| 96 | /// `getrandom` crate does" but that may change in the future. That roughly |
| 97 | /// means calling libc's `getrandom` function or whatever is analogous to that; |
| 98 | /// see the `getrandom` crate's documentation for more info. |
| 99 | /// |
| 100 | /// A single `SystemRandom` may be shared across multiple threads safely. |
| 101 | /// |
| 102 | /// `new()` is guaranteed to always succeed and to have low latency; it won't |
| 103 | /// try to open or read from a file or do similar things. The first call to |
| 104 | /// `fill()` may block a substantial amount of time since any and all |
| 105 | /// initialization is deferred to it. Therefore, it may be a good idea to call |
| 106 | /// `fill()` once at a non-latency-sensitive time to minimize latency for |
| 107 | /// future calls. |
| 108 | #[derive (Clone, Debug)] |
| 109 | pub struct SystemRandom(()); |
| 110 | |
| 111 | impl SystemRandom { |
| 112 | /// Constructs a new `SystemRandom`. |
| 113 | #[inline (always)] |
| 114 | pub fn new() -> Self { |
| 115 | Self(()) |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | impl crate::sealed::Sealed for SystemRandom {} |
| 120 | |
| 121 | // Use the `getrandom` crate whenever it is using the environment's (operating |
| 122 | // system's) CSPRNG. Avoid using it on targets where it uses the `rdrand` |
| 123 | // implementation. |
| 124 | #[cfg (any( |
| 125 | all(feature = "less-safe-getrandom-custom-or-rdrand" , target_os = "none" ), |
| 126 | all(feature = "less-safe-getrandom-espidf" , target_os = "espidf" ), |
| 127 | target_os = "aix" , |
| 128 | target_os = "android" , |
| 129 | target_os = "dragonfly" , |
| 130 | target_os = "freebsd" , |
| 131 | target_os = "fuchsia" , |
| 132 | target_os = "haiku" , |
| 133 | target_os = "hermit" , |
| 134 | target_os = "hurd" , |
| 135 | target_os = "horizon" , |
| 136 | target_os = "illumos" , |
| 137 | target_os = "linux" , |
| 138 | target_os = "netbsd" , |
| 139 | target_os = "openbsd" , |
| 140 | target_os = "redox" , |
| 141 | target_os = "solaris" , |
| 142 | target_os = "vita" , |
| 143 | target_os = "windows" , |
| 144 | all( |
| 145 | target_vendor = "apple" , |
| 146 | any( |
| 147 | target_os = "ios" , |
| 148 | target_os = "macos" , |
| 149 | target_os = "tvos" , |
| 150 | target_os = "visionos" , |
| 151 | target_os = "watchos" , |
| 152 | ) |
| 153 | ), |
| 154 | all( |
| 155 | target_arch = "wasm32" , |
| 156 | any( |
| 157 | target_os = "wasi" , |
| 158 | all(target_os = "unknown" , feature = "wasm32_unknown_unknown_js" ) |
| 159 | ) |
| 160 | ), |
| 161 | ))] |
| 162 | impl sealed::SecureRandom for SystemRandom { |
| 163 | #[inline (always)] |
| 164 | fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> { |
| 165 | getrandom::getrandom(dest).map_err(|_| error::Unspecified) |
| 166 | } |
| 167 | } |
| 168 | |