Skip to main content

bssl_crypto/cipher/
mod.rs

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
15extern crate alloc;
16
17use crate::{CSlice, CSliceMut};
18use alloc::{vec, vec::Vec};
19use bssl_sys::EVP_CIPHER;
20use core::marker::PhantomData;
21
22/// AES-CTR stream cipher operations.
23pub mod aes_ctr;
24
25/// AES-CBC stream cipher operations.
26pub mod aes_cbc;
27
28/// Error returned in the event of an unsuccessful cipher operation.
29#[derive(Debug)]
30pub struct CipherError;
31
32/// Synchronous stream cipher trait.
33pub 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`.
42    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.
46    fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>;
47}
48
49/// Synchronous block cipher trait.
50pub 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`.
59    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`.
62    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.
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.
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`.
76trait EvpCipherType {
77    type Key: AsRef<[u8]>;
78    type Nonce: AsRef<[u8]>;
79    fn evp_cipher() -> *const EVP_CIPHER;
80}
81
82struct EvpAes128Ctr;
83impl EvpCipherType for EvpAes128Ctr {
84    type Key = [u8; 16];
85    type Nonce = [u8; 16];
86    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
93struct EvpAes256Ctr;
94impl EvpCipherType for EvpAes256Ctr {
95    type Key = [u8; 32];
96    type Nonce = [u8; 16];
97    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
104struct EvpAes128Cbc;
105impl EvpCipherType for EvpAes128Cbc {
106    type Key = [u8; 16];
107    type Nonce = [u8; 16];
108    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
115struct EvpAes256Cbc;
116impl EvpCipherType for EvpAes256Cbc {
117    type Key = [u8; 32];
118    type Nonce = [u8; 16];
119    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
126enum CipherInitPurpose {
127    Encrypt,
128    Decrypt,
129}
130
131/// Internal cipher implementation which wraps `EVP_CIPHER_*`
132struct Cipher<C: EvpCipherType> {
133    ctx: *mut bssl_sys::EVP_CIPHER_CTX,
134    _marker: PhantomData<C>,
135}
136
137impl<C: EvpCipherType> Cipher<C> {
138    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
178    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
184    fn apply_keystream_in_place(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> {
185        // WARNING: This is not safe to reuse 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_keystream_in_place for non-CTR modes"
191        );
192        let mut cslice_buf_mut = CSliceMut::from(buffer);
193        let mut out_len = 0;
194
195        // Safety: the input and output buffer bounds are passed into `EVP_EncryptUpdate_ex`.
196        let result = unsafe {
197            bssl_sys::EVP_EncryptUpdate_ex(
198                self.ctx,
199                cslice_buf_mut.as_mut_ptr(),
200                &mut out_len,
201                cslice_buf_mut.len(),
202                cslice_buf_mut.as_mut_ptr(),
203                cslice_buf_mut.len(),
204            )
205        };
206        if result == 1 {
207            assert_eq!(out_len, cslice_buf_mut.len());
208            Ok(())
209        } else {
210            Err(CipherError)
211        }
212    }
213
214    #[allow(clippy::expect_used)]
215    fn encrypt(self, buffer: &[u8]) -> Result<Vec<u8>, CipherError> {
216        // Safety: self.ctx is initialized with a cipher in `new()`.
217        let block_size_u32 = unsafe { bssl_sys::EVP_CIPHER_CTX_block_size(self.ctx) };
218        let block_size: usize = block_size_u32
219            .try_into()
220            .expect("Block size should always fit in usize");
221        let max_encrypt_total_output_size = buffer.len() + block_size;
222        let mut output_vec = vec![0_u8; max_encrypt_total_output_size];
223        // EncryptUpdate block
224        let update_out_len = {
225            let mut cslice_out_buf_mut = CSliceMut::from(&mut output_vec[..]);
226            let mut update_out_len = 0;
227
228            let cslice_in_buf = CSlice::from(buffer);
229
230            // Safety: the input and output buffer bounds are passed into `EVP_EncryptUpdate_ex`.
231            let update_result = unsafe {
232                bssl_sys::EVP_EncryptUpdate_ex(
233                    self.ctx,
234                    cslice_out_buf_mut.as_mut_ptr(),
235                    &mut update_out_len,
236                    cslice_out_buf_mut.len(),
237                    cslice_in_buf.as_ptr(),
238                    cslice_in_buf.len(),
239                )
240            };
241            if update_result != 1 {
242                return Err(CipherError);
243            }
244            update_out_len
245        };
246
247        // EncryptFinal block
248        {
249            // Slice indexing here will not panic because we ensured `output_vec` is larger than
250            // what `EncryptUpdate` will write.
251            #[allow(clippy::indexing_slicing)]
252            let mut cslice_finalize_buf_mut = CSliceMut::from(&mut output_vec[update_out_len..]);
253            let mut final_out_len = 0;
254            // Safety: the output buffer bounds are passed into `EVP_EncryptFinal_ex2`.
255            let final_result = unsafe {
256                bssl_sys::EVP_EncryptFinal_ex2(
257                    self.ctx,
258                    cslice_finalize_buf_mut.as_mut_ptr(),
259                    &mut final_out_len,
260                    cslice_finalize_buf_mut.len(),
261                )
262            };
263            if final_result == 1 {
264                output_vec.truncate(update_out_len + final_out_len)
265            } else {
266                return Err(CipherError);
267            }
268        }
269        Ok(output_vec)
270    }
271
272    #[allow(clippy::expect_used)]
273    fn decrypt(self, in_buffer: &[u8]) -> Result<Vec<u8>, CipherError> {
274        // Safety: self.ctx is initialized with a cipher in `new()`.
275        let mut output_vec = vec![0_u8; in_buffer.len()];
276
277        // DecryptUpdate block
278        let update_out_len = {
279            let mut cslice_out_buf_mut = CSliceMut::from(&mut output_vec[..]);
280            let mut update_out_len = 0;
281
282            let cslice_in_buf = CSlice::from(in_buffer);
283
284            // Safety: the input and output buffer bounds are passed into `EVP_DecryptUpdate_ex`.
285            let update_result = unsafe {
286                bssl_sys::EVP_DecryptUpdate_ex(
287                    self.ctx,
288                    cslice_out_buf_mut.as_mut_ptr(),
289                    &mut update_out_len,
290                    cslice_out_buf_mut.len(),
291                    cslice_in_buf.as_ptr(),
292                    cslice_in_buf.len(),
293                )
294            };
295            if update_result != 1 {
296                return Err(CipherError);
297            }
298            update_out_len
299        };
300
301        // DecryptFinal block
302        {
303            // Slice indexing here will not panic because we ensured `output_vec` is larger than
304            // what `DecryptUpdate` will write.
305            #[allow(clippy::indexing_slicing)]
306            let mut cslice_final_buf_mut = CSliceMut::from(&mut output_vec[update_out_len..]);
307            let mut final_out_len = 0;
308            // Safety: the output buffer bounds are passed into `EVP_DecryptFinal_ex2`.
309            let final_result = unsafe {
310                bssl_sys::EVP_DecryptFinal_ex2(
311                    self.ctx,
312                    cslice_final_buf_mut.as_mut_ptr(),
313                    &mut final_out_len,
314                    cslice_final_buf_mut.len(),
315                )
316            };
317
318            if final_result == 1 {
319                output_vec.truncate(update_out_len + final_out_len)
320            } else {
321                return Err(CipherError);
322            }
323        }
324        Ok(output_vec)
325    }
326}
327
328impl<C: EvpCipherType> Drop for Cipher<C> {
329    fn drop(&mut self) {
330        // Safety:
331        // - `self.ctx` was allocated by `EVP_CIPHER_CTX_new` and has not yet been freed.
332        unsafe { bssl_sys::EVP_CIPHER_CTX_free(self.ctx) }
333    }
334}
335
336#[cfg(test)]
337mod test {
338    use crate::cipher::{CipherInitPurpose, EvpAes128Cbc, EvpAes128Ctr};
339
340    use super::Cipher;
341
342    #[test]
343    fn test_cipher_mode() {
344        assert_eq!(
345            Cipher::<EvpAes128Ctr>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt)
346                .cipher_mode(),
347            bssl_sys::EVP_CIPH_CTR_MODE as u32
348        );
349
350        assert_eq!(
351            Cipher::<EvpAes128Cbc>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt)
352                .cipher_mode(),
353            bssl_sys::EVP_CIPH_CBC_MODE as u32
354        );
355    }
356
357    #[should_panic]
358    #[test]
359    fn test_apply_keystream_on_cbc() {
360        let mut cipher =
361            Cipher::<EvpAes128Cbc>::new(&[0; 16], &[0; 16], CipherInitPurpose::Encrypt);
362        let mut buf = [0; 16];
363        let _ = cipher.apply_keystream_in_place(&mut buf); // This should panic
364    }
365}