1
//! Compatibility utilities for working with libraries that consume
2
//! older versions of rand_core.
3
//!
4
//! The dalek-crypto libraries are currently stuck on [`rand_core`]
5
//! 0.5.1, but everywhere else in Arti we want to use the latest
6
//! [`rand_core`] (0.6.2 as of this writing).  The extension trait in this
7
//! module lets us do so.
8
//!
9
//! # Example:
10
//!
11
//! As of May 2021, if you're using the current version of
12
//! [`x25519-dalek`], and the latest [`rand_core`], then you can't use
13
//! this code, because of the compatibility issue mentioned above.
14
//!
15
//! ```compile_fail
16
//! use rand_core::OsRng;
17
//! use x25519_dalek::EphemeralSecret;
18
//!
19
//! let my_secret = EphemeralSecret::new(OsRng);
20
//! ```
21
//!
22
//! But instead, you can wrap the random number generator using the
23
//! [`RngCompatExt`] extension trait.
24
//!
25
//! ```
26
//! use tor_llcrypto::util::rand_compat::RngCompatExt;
27
//! use rand_core::OsRng;
28
//! use x25519_dalek::EphemeralSecret;
29
//!
30
//! let my_secret = EphemeralSecret::new(OsRng.rng_compat());
31
//! ```
32
//!
33
//! The wrapped RNG can be used with the old version of the RngCode
34
//! trait, as well as the new one.
35

            
36
use old_rand_core::{CryptoRng as OldCryptoRng, Error as OldError, RngCore as OldRngCore};
37
use rand_core::{CryptoRng, Error, RngCore};
38

            
39
use std::convert::TryInto;
40

            
41
/// Extension trait for the _current_ versions of [`RngCore`]; adds a
42
/// compatibility-wrapper function.
43
pub trait RngCompatExt: RngCore {
44
    /// Wrapper type returned by this trait.
45
    type Wrapper: RngCore + OldRngCore;
46
    /// Return a version of this Rng that can be used with older versions
47
    /// of the rand_core and rand libraries, as well as the current version.
48
    fn rng_compat(self) -> Self::Wrapper;
49
}
50

            
51
impl<T: RngCore + Sized> RngCompatExt for T {
52
    type Wrapper = RngWrapper<T>;
53
53
    fn rng_compat(self) -> RngWrapper<Self> {
54
53
        self.into()
55
53
    }
56
}
57

            
58
/// A new-style Rng, wrapped for backward compatibility.
59
///
60
/// This object implements both the current (0.6.2) version of [`RngCore`],
61
/// as well as the version from 0.5.1 that the dalek-crypto functions expect.
62
///
63
/// To get an RngWrapper, use the [`RngCompatExt`] extension trait:
64
/// ```
65
/// use tor_llcrypto::util::rand_compat::RngCompatExt;
66
///
67
/// let mut wrapped_rng = rand::thread_rng().rng_compat();
68
/// ```
69
pub struct RngWrapper<T>(T);
70

            
71
impl<T: RngCore> From<T> for RngWrapper<T> {
72
53
    fn from(rng: T) -> RngWrapper<T> {
73
53
        RngWrapper(rng)
74
53
    }
75
}
76

            
77
impl<T: RngCore> OldRngCore for RngWrapper<T> {
78
1000
    fn next_u32(&mut self) -> u32 {
79
1000
        self.0.next_u32()
80
1000
    }
81
1000
    fn next_u64(&mut self) -> u64 {
82
1000
        self.0.next_u64()
83
1000
    }
84
1055
    fn fill_bytes(&mut self, dest: &mut [u8]) {
85
1055
        self.0.fill_bytes(dest);
86
1055
    }
87
1000
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), OldError> {
88
1000
        self.0.try_fill_bytes(dest).map_err(|e| err_to_old(&e))
89
1000
    }
90
}
91

            
92
impl<T: RngCore> RngCore for RngWrapper<T> {
93
1000
    fn next_u32(&mut self) -> u32 {
94
1000
        self.0.next_u32()
95
1000
    }
96
1000
    fn next_u64(&mut self) -> u64 {
97
1000
        self.0.next_u64()
98
1000
    }
99
1013
    fn fill_bytes(&mut self, dest: &mut [u8]) {
100
1013
        self.0.fill_bytes(dest);
101
1013
    }
102
1000
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
103
1000
        self.0.try_fill_bytes(dest)
104
1000
    }
105
}
106

            
107
impl<T: CryptoRng> OldCryptoRng for RngWrapper<T> {}
108
impl<T: CryptoRng> CryptoRng for RngWrapper<T> {}
109

            
110
/// Convert a new-ish Rng error into the error type that rng_core 0.5.1
111
/// would deliver.
112
fn err_to_old(e: &Error) -> OldError {
113
    use std::num::NonZeroU32;
114
    if let Some(code) = e.code() {
115
        code.into()
116
    } else {
117
        // CUSTOM_START is defined to be a nonzero value in rand_core,
118
        // so this conversion will succeed, so this unwrap can't panic.
119
        #[allow(clippy::unwrap_used)]
120
        let nz: NonZeroU32 = OldError::CUSTOM_START.try_into().unwrap();
121
        nz.into()
122
    }
123
}
124

            
125
#[cfg(test)]
126
mod test {
127
    #![allow(clippy::unwrap_used)]
128
    use super::*;
129

            
130
    /// OR every byte of src into dest.
131
    ///
132
    /// Requires that they have the same length.
133
    fn or_into(dst: &mut [u8], src: &[u8]) {
134
        assert_eq!(dst.len(), src.len());
135
        for i in 0..dst.len() {
136
            dst[i] |= src[i];
137
        }
138
    }
139

            
140
    /// AND every byte of src into dest.
141
    ///
142
    /// Requires that they have the same length.
143
    fn and_into(dst: &mut [u8], src: &[u8]) {
144
        assert_eq!(dst.len(), src.len());
145
        for i in 0..dst.len() {
146
            dst[i] &= src[i];
147
        }
148
    }
149

            
150
    #[test]
151
    fn test_wrapper_as_old() {
152
        let mut wrapped = rand::thread_rng().rng_compat();
153

            
154
        let mut z64 = 0xffffffffffffffff_u64;
155
        let mut z32 = 0xffffffff_u32;
156
        let mut z17 = [0xff_u8; 17];
157
        let mut z17_try = [0xff_u8; 17];
158
        let mut o64 = 0_u64;
159
        let mut o32 = 0_u32;
160
        let mut o17 = [0_u8; 17];
161
        let mut o17_try = [0_u8; 17];
162
        for _ in 0..1000 {
163
            let u = OldRngCore::next_u64(&mut wrapped);
164
            z64 &= u;
165
            o64 |= u;
166

            
167
            let u = OldRngCore::next_u32(&mut wrapped);
168
            z32 &= u;
169
            o32 |= u;
170

            
171
            let mut bytes = [0; 17];
172
            OldRngCore::fill_bytes(&mut wrapped, &mut bytes);
173
            and_into(&mut z17, &bytes);
174
            or_into(&mut o17, &bytes);
175

            
176
            let mut bytes = [0; 17];
177
            OldRngCore::try_fill_bytes(&mut wrapped, &mut bytes).unwrap();
178
            and_into(&mut z17_try, &bytes);
179
            or_into(&mut o17_try, &bytes);
180
        }
181
        assert_eq!(z64, 0);
182
        assert_eq!(z32, 0);
183

            
184
        assert_eq!(o64, 0xffffffffffffffff_u64);
185
        assert_eq!(o32, 0xffffffff_u32);
186

            
187
        assert_eq!(z17, [0; 17]);
188
        assert_eq!(z17_try, [0; 17]);
189
        assert_eq!(o17, [0xff; 17]);
190
        assert_eq!(o17_try, [0xff; 17]);
191
    }
192

            
193
    #[test]
194
    fn test_wrapper_as_new() {
195
        let mut wrapped = rand::thread_rng().rng_compat();
196

            
197
        let mut z64 = 0xffffffffffffffff_u64;
198
        let mut z32 = 0xffffffff_u32;
199
        let mut z17 = [0xff_u8; 17];
200
        let mut z17_try = [0xff_u8; 17];
201
        let mut o64 = 0_u64;
202
        let mut o32 = 0_u32;
203
        let mut o17 = [0_u8; 17];
204
        let mut o17_try = [0_u8; 17];
205
        for _ in 0..1000 {
206
            let u = RngCore::next_u64(&mut wrapped);
207
            z64 &= u;
208
            o64 |= u;
209

            
210
            let u = RngCore::next_u32(&mut wrapped);
211
            z32 &= u;
212
            o32 |= u;
213

            
214
            let mut bytes = [0; 17];
215
            RngCore::fill_bytes(&mut wrapped, &mut bytes);
216
            and_into(&mut z17, &bytes);
217
            or_into(&mut o17, &bytes);
218

            
219
            let mut bytes = [0; 17];
220
            RngCore::try_fill_bytes(&mut wrapped, &mut bytes).unwrap();
221
            and_into(&mut z17_try, &bytes);
222
            or_into(&mut o17_try, &bytes);
223
        }
224
        assert_eq!(z64, 0);
225
        assert_eq!(z32, 0);
226

            
227
        assert_eq!(o64, 0xffffffffffffffff_u64);
228
        assert_eq!(o32, 0xffffffff_u32);
229

            
230
        assert_eq!(z17, [0; 17]);
231
        assert_eq!(z17_try, [0; 17]);
232
        assert_eq!(o17, [0xff; 17]);
233
        assert_eq!(o17_try, [0xff; 17]);
234
    }
235
}