Skip to main content

bssl_crypto/cipher/
mod.rs

1/* Copyright 2023 The BoringSSL Authors
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
16extern 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
23/// AES-CTR stream cipher operations.
24pub mod aes_ctr;
25
26/// AES-CBC stream cipher operations.
27pub mod aes_cbc;
28
29/// Error returned in the event of an unsuccessful cipher operation.
30#[derive(Debug)]
31pub struct CipherError;
32
33/// Synchronous stream cipher trait.
34pub 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`.
43    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.
47    fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>;
48}
49
50/// Synchronous block cipher trait.
51pub 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`.
60    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`.
63    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.
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.
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`.
77trait 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        // Safety:
89        // - this just returns a constant value
90        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        // Safety:
100        // - this just returns a constant value
101        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        // Safety:
111        // - this just returns a constant value
112        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        // Safety:
122        // - this just returns a constant value
123        unsafe { bssl_sys::EVP_aes_256_cbc() }
124    }
125}
126
127enum CipherInitPurpose {
128    Encrypt,
129    Decrypt,
130}
131
132/// Internal cipher implementation which wraps `EVP_CIPHER_*`
133struct 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        // 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
179    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
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)]
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)]
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
355impl<C: EvpCipherType> Drop for Cipher<C> {
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)]
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); // This should panic
391    }
392}