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