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