bssl_crypto/cipher/
mod.rs1extern crate alloc;
17
18use crate::{CSlice, CSliceMut};
19use alloc::{vec, vec::Vec};
20use bssl_sys::EVP_CIPHER;
21use core::{ffi::c_int, marker::PhantomData};
22
23pub mod aes_ctr;
25
26pub mod aes_cbc;
28
29#[derive(Debug)]
31pub struct CipherError;
32
33pub trait StreamCipher {
35 type Key: AsRef<[u8]>;
37
38 type Nonce: AsRef<[u8]>;
41
42 fn new(key: &Self::Key, iv: &Self::Nonce) -> Self;
44
45 fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>;
48}
49
50pub trait BlockCipher {
52 type Key: AsRef<[u8]>;
54
55 type Nonce: AsRef<[u8]>;
58
59 fn new_encrypt(key: &Self::Key, iv: &Self::Nonce) -> Self;
61
62 fn new_decrypt(key: &Self::Key, iv: &Self::Nonce) -> Self;
64
65 fn encrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>;
68
69 fn decrypt_padded(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError>;
72}
73
74trait EvpCipherType {
78 type Key: AsRef<[u8]>;
79 type Nonce: AsRef<[u8]>;
80 fn evp_cipher() -> *const EVP_CIPHER;
81}
82
83struct EvpAes128Ctr;
84impl EvpCipherType for EvpAes128Ctr {
85 type Key = [u8; 16];
86 type Nonce = [u8; 16];
87 fn evp_cipher() -> *const EVP_CIPHER {
88 unsafe { bssl_sys::EVP_aes_128_ctr() }
91 }
92}
93
94struct EvpAes256Ctr;
95impl EvpCipherType for EvpAes256Ctr {
96 type Key = [u8; 32];
97 type Nonce = [u8; 16];
98 fn evp_cipher() -> *const EVP_CIPHER {
99 unsafe { bssl_sys::EVP_aes_256_ctr() }
102 }
103}
104
105struct EvpAes128Cbc;
106impl EvpCipherType for EvpAes128Cbc {
107 type Key = [u8; 16];
108 type Nonce = [u8; 16];
109 fn evp_cipher() -> *const EVP_CIPHER {
110 unsafe { bssl_sys::EVP_aes_128_cbc() }
113 }
114}
115
116struct EvpAes256Cbc;
117impl EvpCipherType for EvpAes256Cbc {
118 type Key = [u8; 32];
119 type Nonce = [u8; 16];
120 fn evp_cipher() -> *const EVP_CIPHER {
121 unsafe { bssl_sys::EVP_aes_256_cbc() }
124 }
125}
126
127enum CipherInitPurpose {
128 Encrypt,
129 Decrypt,
130}
131
132struct Cipher<C: EvpCipherType> {
134 ctx: *mut bssl_sys::EVP_CIPHER_CTX,
135 _marker: PhantomData<C>,
136}
137
138impl<C: EvpCipherType> Cipher<C> {
139 fn new(key: &C::Key, iv: &C::Nonce, purpose: CipherInitPurpose) -> Self {
140 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 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
179 fn cipher_mode(&self) -> u32 {
180 unsafe { bssl_sys::EVP_CIPHER_CTX_mode(self.ctx) }
183 }
184
185 fn apply_keystream_in_place(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> {
186 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 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)]
218 fn encrypt(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> {
219 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 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 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 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 {
261 #[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)]
286 fn decrypt(self, in_buffer: &[u8]) -> Result<Vec<u8>, CipherError> {
287 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 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 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 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 {
330 #[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
355impl<C: EvpCipherType> Drop for Cipher<C> {
356 fn drop(&mut self) {
357 unsafe { bssl_sys::EVP_CIPHER_CTX_free(self.ctx) }
360 }
361}
362
363#[cfg(test)]
364mod test {
365 use crate::cipher::{CipherInitPurpose, EvpAes128Cbc, EvpAes128Ctr};
366
367 use super::Cipher;
368
369 #[test]
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]
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); }
392}