1use super::CryptoResolver;
2use crate::{
3 constants::TAGLEN,
4 params::{CipherChoice, DHChoice, HashChoice},
5 types::{Cipher, Dh, Hash, Random},
6 Error,
7};
8use ring::{
9 aead::{self, LessSafeKey, UnboundKey},
10 digest,
11 rand::{SecureRandom, SystemRandom},
12};
13
14#[derive(Default)]
17pub struct RingResolver;
18
19#[cfg(feature = "ring")]
20impl CryptoResolver for RingResolver {
21 fn resolve_rng(&self) -> Option<Box<dyn Random>> {
22 Some(Box::new(RingRng::default()))
23 }
24
25 fn resolve_dh(&self, _choice: &DHChoice) -> Option<Box<dyn Dh>> {
26 None
27 }
28
29 fn resolve_hash(&self, choice: &HashChoice) -> Option<Box<dyn Hash>> {
30 match *choice {
31 HashChoice::SHA256 => Some(Box::new(HashSHA256::default())),
32 HashChoice::SHA512 => Some(Box::new(HashSHA512::default())),
33 _ => None,
34 }
35 }
36
37 fn resolve_cipher(&self, choice: &CipherChoice) -> Option<Box<dyn Cipher>> {
38 match *choice {
39 CipherChoice::AESGCM => Some(Box::new(CipherAESGCM::default())),
40 CipherChoice::ChaChaPoly => Some(Box::new(CipherChaChaPoly::default())),
41 #[cfg(feature = "xchachapoly")]
42 CipherChoice::XChaChaPoly => None,
43 }
44 }
45}
46
47struct RingRng {
48 rng: SystemRandom,
49}
50
51impl Default for RingRng {
52 fn default() -> Self {
53 Self { rng: SystemRandom::new() }
54 }
55}
56
57impl rand_core::RngCore for RingRng {
58 fn next_u32(&mut self) -> u32 {
59 rand_core::impls::next_u32_via_fill(self)
60 }
61
62 fn next_u64(&mut self) -> u64 {
63 rand_core::impls::next_u64_via_fill(self)
64 }
65
66 fn fill_bytes(&mut self, dest: &mut [u8]) {
67 self.try_fill_bytes(dest).unwrap();
68 }
69
70 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
71 self.rng.fill(dest).map_err(|e| rand_core::Error::new(e))
72 }
73}
74
75impl rand_core::CryptoRng for RingRng {}
76
77impl Random for RingRng {}
78
79struct CipherAESGCM {
80 key: LessSafeKey,
83}
84
85impl Default for CipherAESGCM {
86 fn default() -> Self {
87 CipherAESGCM {
88 key: LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, &[0u8; 32]).unwrap()),
89 }
90 }
91}
92
93impl Cipher for CipherAESGCM {
94 fn name(&self) -> &'static str {
95 "AESGCM"
96 }
97
98 fn set(&mut self, key: &[u8]) {
99 self.key = aead::LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, key).unwrap());
100 }
101
102 fn encrypt(&self, nonce: u64, authtext: &[u8], plaintext: &[u8], out: &mut [u8]) -> usize {
103 let mut nonce_bytes = [0u8; 12];
104 copy_slices!(&nonce.to_be_bytes(), &mut nonce_bytes[4..]);
105
106 out[..plaintext.len()].copy_from_slice(plaintext);
107
108 let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
109
110 let tag = self
111 .key
112 .seal_in_place_separate_tag(
113 nonce,
114 aead::Aad::from(authtext),
115 &mut out[..plaintext.len()],
116 )
117 .unwrap();
118 out[plaintext.len()..plaintext.len() + TAGLEN].copy_from_slice(tag.as_ref());
119
120 plaintext.len() + TAGLEN
121 }
122
123 fn decrypt(
124 &self,
125 nonce: u64,
126 authtext: &[u8],
127 ciphertext: &[u8],
128 out: &mut [u8],
129 ) -> Result<usize, Error> {
130 let mut nonce_bytes = [0u8; 12];
131 copy_slices!(&nonce.to_be_bytes(), &mut nonce_bytes[4..]);
132 let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
133
134 if out.len() >= ciphertext.len() {
135 let in_out = &mut out[..ciphertext.len()];
136 in_out.copy_from_slice(ciphertext);
137
138 let len = self
139 .key
140 .open_in_place(nonce, aead::Aad::from(authtext), in_out)
141 .map_err(|_| Error::Decrypt)?
142 .len();
143
144 Ok(len)
145 } else {
146 let mut in_out = ciphertext.to_vec();
147
148 let out0 = self
149 .key
150 .open_in_place(nonce, aead::Aad::from(authtext), &mut in_out)
151 .map_err(|_| Error::Decrypt)?;
152
153 out[..out0.len()].copy_from_slice(out0);
154 Ok(out0.len())
155 }
156 }
157}
158
159struct CipherChaChaPoly {
160 key: aead::LessSafeKey,
163}
164
165impl Default for CipherChaChaPoly {
166 fn default() -> Self {
167 Self {
168 key: LessSafeKey::new(UnboundKey::new(&aead::CHACHA20_POLY1305, &[0u8; 32]).unwrap()),
169 }
170 }
171}
172
173impl Cipher for CipherChaChaPoly {
174 fn name(&self) -> &'static str {
175 "ChaChaPoly"
176 }
177
178 fn set(&mut self, key: &[u8]) {
179 self.key = LessSafeKey::new(UnboundKey::new(&aead::CHACHA20_POLY1305, key).unwrap());
180 }
181
182 fn encrypt(&self, nonce: u64, authtext: &[u8], plaintext: &[u8], out: &mut [u8]) -> usize {
183 let mut nonce_bytes = [0u8; 12];
184 copy_slices!(&nonce.to_le_bytes(), &mut nonce_bytes[4..]);
185 let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
186
187 out[..plaintext.len()].copy_from_slice(plaintext);
188
189 let tag = self
190 .key
191 .seal_in_place_separate_tag(
192 nonce,
193 aead::Aad::from(authtext),
194 &mut out[..plaintext.len()],
195 )
196 .unwrap();
197 out[plaintext.len()..plaintext.len() + TAGLEN].copy_from_slice(tag.as_ref());
198
199 plaintext.len() + TAGLEN
200 }
201
202 fn decrypt(
203 &self,
204 nonce: u64,
205 authtext: &[u8],
206 ciphertext: &[u8],
207 out: &mut [u8],
208 ) -> Result<usize, Error> {
209 let mut nonce_bytes = [0u8; 12];
210 copy_slices!(&nonce.to_le_bytes(), &mut nonce_bytes[4..]);
211 let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
212
213 if out.len() >= ciphertext.len() {
214 let in_out = &mut out[..ciphertext.len()];
215 in_out.copy_from_slice(ciphertext);
216
217 let len = self
218 .key
219 .open_in_place(nonce, aead::Aad::from(authtext), in_out)
220 .map_err(|_| Error::Decrypt)?
221 .len();
222
223 Ok(len)
224 } else {
225 let mut in_out = ciphertext.to_vec();
226
227 let out0 = self
228 .key
229 .open_in_place(nonce, aead::Aad::from(authtext), &mut in_out)
230 .map_err(|_| Error::Decrypt)?;
231
232 out[..out0.len()].copy_from_slice(out0);
233 Ok(out0.len())
234 }
235 }
236}
237struct HashSHA256 {
238 context: digest::Context,
239}
240
241impl Default for HashSHA256 {
242 fn default() -> Self {
243 Self { context: digest::Context::new(&digest::SHA256) }
244 }
245}
246
247impl Hash for HashSHA256 {
248 fn name(&self) -> &'static str {
249 "SHA256"
250 }
251
252 fn block_len(&self) -> usize {
253 64
254 }
255
256 fn hash_len(&self) -> usize {
257 32
258 }
259
260 fn reset(&mut self) {
261 self.context = digest::Context::new(&digest::SHA256);
262 }
263
264 fn input(&mut self, data: &[u8]) {
265 self.context.update(data);
266 }
267
268 fn result(&mut self, out: &mut [u8]) {
269 out[..32].copy_from_slice(self.context.clone().finish().as_ref());
270 }
271}
272
273struct HashSHA512 {
274 context: digest::Context,
275}
276
277impl Default for HashSHA512 {
278 fn default() -> Self {
279 Self { context: digest::Context::new(&digest::SHA512) }
280 }
281}
282
283impl Hash for HashSHA512 {
284 fn name(&self) -> &'static str {
285 "SHA512"
286 }
287
288 fn block_len(&self) -> usize {
289 128
290 }
291
292 fn hash_len(&self) -> usize {
293 64
294 }
295
296 fn reset(&mut self) {
297 self.context = digest::Context::new(&digest::SHA512);
298 }
299
300 fn input(&mut self, data: &[u8]) {
301 self.context.update(data);
302 }
303
304 fn result(&mut self, out: &mut [u8]) {
305 out[..64].copy_from_slice(self.context.clone().finish().as_ref());
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312 use rand_core::RngCore;
313
314 #[test]
315 fn test_randomness_sanity() {
316 use std::collections::HashSet;
317
318 let mut samples = HashSet::new();
319 let mut rng = RingRng::default();
320 for _ in 0..100_000 {
321 let mut buf = vec![0u8; 128];
322 rng.fill_bytes(&mut buf);
323 assert!(samples.insert(buf));
324 }
325 }
326}