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