• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2023, Google Inc.
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 AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 
16 extern crate alloc;
17 
18 use crate::{CSlice, CSliceMut};
19 use alloc::{vec, vec::Vec};
20 use bssl_sys::EVP_CIPHER;
21 use core::{ffi::c_int, marker::PhantomData};
22 
23 /// AES-CTR stream cipher operations.
24 pub mod aes_ctr;
25 
26 /// AES-CBC stream cipher operations.
27 pub mod aes_cbc;
28 
29 /// Error returned in the event of an unsuccessful cipher operation.
30 #[derive(Debug)]
31 pub struct CipherError;
32 
33 /// Synchronous stream cipher trait.
34 pub trait StreamCipher {
35     /// The byte array key type which specifies the size of the key used to instantiate the cipher.
36     type Key: AsRef<[u8]>;
37 
38     /// The byte array nonce type which specifies the size of the nonce used in the cipher
39     /// operations.
40     type Nonce: AsRef<[u8]>;
41 
42     /// Instantiate a new instance of a stream cipher from a `key` and `iv`.
new(key: &Self::Key, iv: &Self::Nonce) -> Self43     fn new(key: &Self::Key, iv: &Self::Nonce) -> Self;
44 
45     /// Applies the cipher keystream to `buffer` in place, returning CipherError on an unsuccessful
46     /// operation.
apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>47     fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>;
48 }
49 
50 /// Synchronous block cipher trait.
51 pub trait BlockCipher {
52     /// The byte array key type which specifies the size of the key used to instantiate the cipher.
53     type Key: AsRef<[u8]>;
54 
55     /// The byte array nonce type which specifies the size of the nonce used in the cipher
56     /// operations.
57     type Nonce: AsRef<[u8]>;
58 
59     /// Instantiate a new instance of a block cipher for encryption from a `key` and `iv`.
new_encrypt(key: &Self::Key, iv: &Self::Nonce) -> Self60     fn new_encrypt(key: &Self::Key, iv: &Self::Nonce) -> Self;
61 
62     /// Instantiate a new instance of a block cipher for decryption from a `key` and `iv`.
new_decrypt(key: &Self::Key, iv: &Self::Nonce) -> Self63     fn new_decrypt(key: &Self::Key, iv: &Self::Nonce) -> Self;
64 
65     /// Encrypts the given data in `buffer`, and returns the result (with padding) in a newly
66     /// allocated vector, or a [`CipherError`] if the operation was unsuccessful.
encrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>67     fn encrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>;
68 
69     /// Decrypts the given data in a `buffer`, and returns the result (with padding removed) in a
70     /// newly allocated vector, or a [`CipherError`] if the operation was unsuccessful.
decrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>71     fn decrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>;
72 }
73 
74 /// A cipher type, where `Key` is the size of the Key and `Nonce` is the size of the nonce or IV.
75 /// This must only be exposed publicly by types who ensure that `Key` is the correct size for the
76 /// given CipherType. This can be checked via `bssl_sys::EVP_CIPHER_key_length`.
77 trait EvpCipherType {
78     type Key: AsRef<[u8]>;
79     type Nonce: AsRef<[u8]>;
evp_cipher() -> *const EVP_CIPHER80     fn evp_cipher() -> *const EVP_CIPHER;
81 }
82 
83 struct EvpAes128Ctr;
84 impl EvpCipherType for EvpAes128Ctr {
85     type Key = [u8; 16];
86     type Nonce = [u8; 16];
evp_cipher() -> *const EVP_CIPHER87     fn evp_cipher() -> *const EVP_CIPHER {
88         // Safety:
89         // - this just returns a constant value
90         unsafe { bssl_sys::EVP_aes_128_ctr() }
91     }
92 }
93 
94 struct EvpAes256Ctr;
95 impl EvpCipherType for EvpAes256Ctr {
96     type Key = [u8; 32];
97     type Nonce = [u8; 16];
evp_cipher() -> *const EVP_CIPHER98     fn evp_cipher() -> *const EVP_CIPHER {
99         // Safety:
100         // - this just returns a constant value
101         unsafe { bssl_sys::EVP_aes_256_ctr() }
102     }
103 }
104 
105 struct EvpAes128Cbc;
106 impl EvpCipherType for EvpAes128Cbc {
107     type Key = [u8; 16];
108     type Nonce = [u8; 16];
evp_cipher() -> *const EVP_CIPHER109     fn evp_cipher() -> *const EVP_CIPHER {
110         // Safety:
111         // - this just returns a constant value
112         unsafe { bssl_sys::EVP_aes_128_cbc() }
113     }
114 }
115 
116 struct EvpAes256Cbc;
117 impl EvpCipherType for EvpAes256Cbc {
118     type Key = [u8; 32];
119     type Nonce = [u8; 16];
evp_cipher() -> *const EVP_CIPHER120     fn evp_cipher() -> *const EVP_CIPHER {
121         // Safety:
122         // - this just returns a constant value
123         unsafe { bssl_sys::EVP_aes_256_cbc() }
124     }
125 }
126 
127 enum CipherInitPurpose {
128     Encrypt,
129     Decrypt,
130 }
131 
132 /// Internal cipher implementation which wraps `EVP_CIPHER_*`
133 struct Cipher<C: EvpCipherType> {
134     ctx: *mut bssl_sys::EVP_CIPHER_CTX,
135     _marker: PhantomData<C>,
136 }
137 
138 impl<C: EvpCipherType> Cipher<C> {
new(key: &C::Key, iv: &C::Nonce, purpose: CipherInitPurpose) -> Self139     fn new(key: &C::Key, iv: &C::Nonce, purpose: CipherInitPurpose) -> Self {
140         // Safety:
141         // - Panics on allocation failure.
142         let ctx = unsafe { bssl_sys::EVP_CIPHER_CTX_new() };
143         assert!(!ctx.is_null());
144 
145         let key_cslice = CSlice::from(key.as_ref());
146         let iv_cslice = CSlice::from(iv.as_ref());
147 
148         // Safety:
149         // - Key size and iv size must be properly set by the higher level wrapper types.
150         // - Panics on allocation failure.
151         let result = match purpose {
152             CipherInitPurpose::Encrypt => unsafe {
153                 bssl_sys::EVP_EncryptInit_ex(
154                     ctx,
155                     C::evp_cipher(),
156                     core::ptr::null_mut(),
157                     key_cslice.as_ptr(),
158                     iv_cslice.as_ptr(),
159                 )
160             },
161             CipherInitPurpose::Decrypt => unsafe {
162                 bssl_sys::EVP_DecryptInit_ex(
163                     ctx,
164                     C::evp_cipher(),
165                     core::ptr::null_mut(),
166                     key_cslice.as_ptr(),
167                     iv_cslice.as_ptr(),
168                 )
169             },
170         };
171         assert_eq!(result, 1);
172 
173         Self {
174             ctx,
175             _marker: Default::default(),
176         }
177     }
178 
cipher_mode(&self) -> u32179     fn cipher_mode(&self) -> u32 {
180         // Safety:
181         // - The cipher context is initialized with EVP_EncryptInit_ex in `new`
182         unsafe { bssl_sys::EVP_CIPHER_CTX_mode(self.ctx) }
183     }
184 
apply_keystream_in_place(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>185     fn apply_keystream_in_place(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> {
186         // WARNING: This is not safe to re-use for the CBC mode of operation since it is applying
187         // the key stream in-place.
188         assert_eq!(
189             self.cipher_mode(),
190             bssl_sys::EVP_CIPH_CTR_MODE as u32,
191             "Cannot use apply_keystraem_in_place for non-CTR modes"
192         );
193         let mut cslice_buf_mut = CSliceMut::from(buffer);
194         let mut out_len = 0;
195 
196         let buff_len_int = c_int::try_from(cslice_buf_mut.len()).map_err(|_| CipherError)?;
197 
198         // Safety:
199         // - The output buffer provided is always large enough for an in-place operation.
200         let result = unsafe {
201             bssl_sys::EVP_EncryptUpdate(
202                 self.ctx,
203                 cslice_buf_mut.as_mut_ptr(),
204                 &mut out_len,
205                 cslice_buf_mut.as_mut_ptr(),
206                 buff_len_int,
207             )
208         };
209         if result == 1 {
210             assert_eq!(out_len as usize, cslice_buf_mut.len());
211             Ok(())
212         } else {
213             Err(CipherError)
214         }
215     }
216 
217     #[allow(clippy::expect_used)]
encrypt(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>218     fn encrypt(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> {
219         // Safety: self.ctx is initialized with a cipher in `new()`.
220         let block_size_u32 = unsafe { bssl_sys::EVP_CIPHER_CTX_block_size(self.ctx) };
221         let block_size: usize = block_size_u32
222             .try_into()
223             .expect("Block size should always fit in usize");
224         // Allocate an output vec that is large enough for both EncryptUpdate and EncryptFinal
225         // operations
226         let max_encrypt_update_output_size = buffer.len() + block_size - 1;
227         let max_encrypt_final_output_size = block_size;
228         let mut output_vec =
229             vec![0_u8; max_encrypt_update_output_size + max_encrypt_final_output_size];
230         // EncryptUpdate block
231         let update_out_len_usize = {
232             let mut cslice_out_buf_mut = CSliceMut::from(&mut output_vec[..]);
233             let mut update_out_len = 0;
234 
235             let cslice_in_buf = CSlice::from(buffer);
236             let in_buff_len_int = c_int::try_from(cslice_in_buf.len()).map_err(|_| CipherError)?;
237 
238             // Safety:
239             // - `EVP_EncryptUpdate` requires that "The number of output bytes may be up to `in_len`
240             //   plus the block length minus one and `out` must have sufficient space". This is the
241             //   `max_encrypt_update_output_size` part of the output_vec's capacity.
242             let update_result = unsafe {
243                 bssl_sys::EVP_EncryptUpdate(
244                     self.ctx,
245                     cslice_out_buf_mut.as_mut_ptr(),
246                     &mut update_out_len,
247                     cslice_in_buf.as_ptr(),
248                     in_buff_len_int,
249                 )
250             };
251             if update_result != 1 {
252                 return Err(CipherError);
253             }
254             update_out_len
255                 .try_into()
256                 .expect("Output length should always fit in usize")
257         };
258 
259         // EncryptFinal block
260         {
261             // Slice indexing here will not panic because we ensured `output_vec` is larger than
262             // what `EncryptUpdate` will write.
263             #[allow(clippy::indexing_slicing)]
264             let mut cslice_finalize_buf_mut =
265                 CSliceMut::from(&mut output_vec[update_out_len_usize..]);
266             let mut final_out_len = 0;
267             let final_result = unsafe {
268                 bssl_sys::EVP_EncryptFinal_ex(
269                     self.ctx,
270                     cslice_finalize_buf_mut.as_mut_ptr(),
271                     &mut final_out_len,
272                 )
273             };
274             let final_put_len_usize =
275                 <usize>::try_from(final_out_len).expect("Output length should always fit in usize");
276             if final_result == 1 {
277                 output_vec.truncate(update_out_len_usize + final_put_len_usize)
278             } else {
279                 return Err(CipherError);
280             }
281         }
282         Ok(output_vec)
283     }
284 
285     #[allow(clippy::expect_used)]
decrypt(self, in_buffer: &[u8]) -> Result<Vec<u8>, CipherError>286     fn decrypt(self, in_buffer: &[u8]) -> Result<Vec<u8>, CipherError> {
287         // Safety: self.ctx is initialized with a cipher in `new()`.
288         let block_size_u32 = unsafe { bssl_sys::EVP_CIPHER_CTX_block_size(self.ctx) };
289         let block_size: usize = block_size_u32
290             .try_into()
291             .expect("Block size should always fit in usize");
292         // Allocate an output vec that is large enough for both DecryptUpdate and DecryptFinal
293         // operations
294         let max_decrypt_update_output_size = in_buffer.len() + block_size - 1;
295         let max_decrypt_final_output_size = block_size;
296         let mut output_vec =
297             vec![0_u8; max_decrypt_update_output_size + max_decrypt_final_output_size];
298 
299         // DecryptUpdate block
300         let update_out_len_usize = {
301             let mut cslice_out_buf_mut = CSliceMut::from(&mut output_vec[..]);
302             let mut update_out_len = 0;
303 
304             let cslice_in_buf = CSlice::from(in_buffer);
305             let in_buff_len_int = c_int::try_from(cslice_in_buf.len()).map_err(|_| CipherError)?;
306 
307             // Safety:
308             // - `EVP_DecryptUpdate` requires that "The number of output bytes may be up to `in_len`
309             //   plus the block length minus one and `out` must have sufficient space". This is the
310             //   `max_decrypt_update_output_size` part of the output_vec's capacity.
311             let update_result = unsafe {
312                 bssl_sys::EVP_DecryptUpdate(
313                     self.ctx,
314                     cslice_out_buf_mut.as_mut_ptr(),
315                     &mut update_out_len,
316                     cslice_in_buf.as_ptr(),
317                     in_buff_len_int,
318                 )
319             };
320             if update_result != 1 {
321                 return Err(CipherError);
322             }
323             update_out_len
324                 .try_into()
325                 .expect("Output length should always fit in usize")
326         };
327 
328         // DecryptFinal block
329         {
330             // Slice indexing here will not panic because we ensured `output_vec` is larger than
331             // what `DecryptUpdate` will write.
332             #[allow(clippy::indexing_slicing)]
333             let mut cslice_final_buf_mut = CSliceMut::from(&mut output_vec[update_out_len_usize..]);
334             let mut final_out_len = 0;
335             let final_result = unsafe {
336                 bssl_sys::EVP_DecryptFinal_ex(
337                     self.ctx,
338                     cslice_final_buf_mut.as_mut_ptr(),
339                     &mut final_out_len,
340                 )
341             };
342             let final_put_len_usize =
343                 <usize>::try_from(final_out_len).expect("Output length should always fit in usize");
344 
345             if final_result == 1 {
346                 output_vec.truncate(update_out_len_usize + final_put_len_usize)
347             } else {
348                 return Err(CipherError);
349             }
350         }
351         Ok(output_vec)
352     }
353 }
354 
355 impl<C: EvpCipherType> Drop for Cipher<C> {
drop(&mut self)356     fn drop(&mut self) {
357         // Safety:
358         // - `self.ctx` was allocated by `EVP_CIPHER_CTX_new` and has not yet been freed.
359         unsafe { bssl_sys::EVP_CIPHER_CTX_free(self.ctx) }
360     }
361 }
362 
363 #[cfg(test)]
364 mod test {
365     use crate::cipher::{CipherInitPurpose, EvpAes128Cbc, EvpAes128Ctr};
366 
367     use super::Cipher;
368 
369     #[test]
test_cipher_mode()370     fn test_cipher_mode() {
371         assert_eq!(
372             Cipher::<EvpAes128Ctr>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt)
373                 .cipher_mode(),
374             bssl_sys::EVP_CIPH_CTR_MODE as u32
375         );
376 
377         assert_eq!(
378             Cipher::<EvpAes128Cbc>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt)
379                 .cipher_mode(),
380             bssl_sys::EVP_CIPH_CBC_MODE as u32
381         );
382     }
383 
384     #[should_panic]
385     #[test]
test_apply_keystream_on_cbc()386     fn test_apply_keystream_on_cbc() {
387         let mut cipher =
388             Cipher::<EvpAes128Cbc>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt);
389         let mut buf = [0; 16];
390         let _ = cipher.apply_keystream_in_place(&mut buf); // This should panic
391     }
392 }
393