minip2p_platform/
entropy.rs1use alloc::rc::Rc;
2use core::cell::RefCell;
3use thiserror::Error;
4
5#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
12pub enum EntropyError {
13 #[error("no entropy source available: {reason}")]
18 Unavailable {
19 reason: &'static str,
21 },
22 #[error("entropy source failed: {reason}")]
27 Failed {
28 reason: &'static str,
30 code: Option<i32>,
32 },
33}
34
35impl EntropyError {
36 pub const fn unavailable(reason: &'static str) -> Self {
38 Self::Unavailable { reason }
39 }
40
41 pub const fn failed(reason: &'static str) -> Self {
43 Self::Failed { reason, code: None }
44 }
45
46 pub const fn failed_with_code(reason: &'static str, code: i32) -> Self {
48 Self::Failed {
49 reason,
50 code: Some(code),
51 }
52 }
53}
54
55pub trait EntropySource {
68 fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError>;
70
71 fn next_u64(&mut self) -> Result<u64, EntropyError> {
73 let mut bytes = [0u8; 8];
74 self.fill_bytes(&mut bytes)?;
75 Ok(u64::from_le_bytes(bytes))
76 }
77}
78
79pub struct SharedEntropy<E> {
85 inner: Rc<RefCell<E>>,
86}
87
88impl<E> SharedEntropy<E> {
89 pub fn new(source: E) -> Self {
91 Self {
92 inner: Rc::new(RefCell::new(source)),
93 }
94 }
95}
96
97impl<E> Clone for SharedEntropy<E> {
98 fn clone(&self) -> Self {
99 Self {
100 inner: Rc::clone(&self.inner),
101 }
102 }
103}
104
105impl<E: EntropySource> EntropySource for SharedEntropy<E> {
106 fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError> {
107 self.inner.borrow_mut().fill_bytes(output)
108 }
109
110 fn next_u64(&mut self) -> Result<u64, EntropyError> {
111 self.inner.borrow_mut().next_u64()
112 }
113}
114
115impl<E: EntropySource + ?Sized> EntropySource for &mut E {
116 fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError> {
117 (**self).fill_bytes(output)
118 }
119
120 fn next_u64(&mut self) -> Result<u64, EntropyError> {
121 (**self).next_u64()
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use alloc::boxed::Box;
129
130 struct Counter(u8);
132
133 impl EntropySource for Counter {
134 fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError> {
135 for byte in output.iter_mut() {
136 *byte = self.0;
137 self.0 = self.0.wrapping_add(1);
138 }
139 Ok(())
140 }
141 }
142
143 struct Broken;
144
145 impl EntropySource for Broken {
146 fn fill_bytes(&mut self, output: &mut [u8]) -> Result<(), EntropyError> {
147 if let Some(first) = output.first_mut() {
149 *first = 0xff;
150 }
151 Err(EntropyError::failed_with_code("rng offline", 5))
152 }
153 }
154
155 #[test]
156 fn fill_bytes_fills_the_whole_slice() {
157 let mut source = Counter(1);
158 let mut buffer = [0u8; 4];
159 source.fill_bytes(&mut buffer).expect("fill");
160 assert_eq!(buffer, [1, 2, 3, 4]);
161 }
162
163 #[test]
164 fn next_u64_reads_eight_little_endian_bytes() {
165 let mut source = Counter(1);
166 let value = source.next_u64().expect("draw");
167 assert_eq!(value, u64::from_le_bytes([1, 2, 3, 4, 5, 6, 7, 8]));
168 }
169
170 #[test]
171 fn shared_handles_advance_one_underlying_stream() {
172 let mut first = SharedEntropy::new(Counter(1));
173 let mut second = first.clone();
174 let mut a = [0; 2];
175 let mut b = [0; 2];
176
177 first.fill_bytes(&mut a).expect("first draw");
178 second.fill_bytes(&mut b).expect("second draw");
179
180 assert_eq!(a, [1, 2]);
181 assert_eq!(b, [3, 4]);
182 }
183
184 #[test]
185 fn failures_propagate_through_next_u64() {
186 let mut source = Broken;
187 assert_eq!(
188 source.next_u64(),
189 Err(EntropyError::Failed {
190 reason: "rng offline",
191 code: Some(5)
192 })
193 );
194 }
195
196 fn draw<E: EntropySource>(mut source: E, buffer: &mut [u8]) -> Result<u64, EntropyError> {
199 source.fill_bytes(buffer)?;
200 source.next_u64()
201 }
202
203 #[test]
204 fn mutable_reference_forwards_to_inner_source() {
205 let mut source = Counter(1);
206 let mut buffer = [0u8; 2];
207 let drawn = draw(&mut source, &mut buffer).expect("draw");
208 assert_eq!(buffer, [1, 2]);
209 assert_eq!(drawn, u64::from_le_bytes([3, 4, 5, 6, 7, 8, 9, 10]));
210 assert_eq!(source.0, 11);
211 }
212
213 #[test]
214 fn trait_is_object_safe() {
215 let mut source: Box<dyn EntropySource> = Box::new(Counter(9));
216 let mut buffer = [0u8; 2];
217 source.fill_bytes(&mut buffer).expect("fill");
218 assert_eq!(buffer, [9, 10]);
219 }
220}