| //! The symmetric encryption context. |
| //! |
| //! # Examples |
| //! |
| //! Encrypt data with AES128 CBC |
| //! |
| //! ``` |
| //! use openssl::cipher::Cipher; |
| //! use openssl::cipher_ctx::CipherCtx; |
| //! |
| //! let cipher = Cipher::aes_128_cbc(); |
| //! let data = b"Some Crypto Text"; |
| //! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; |
| //! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; |
| //! |
| //! let mut ctx = CipherCtx::new().unwrap(); |
| //! ctx.encrypt_init(Some(cipher), Some(key), Some(iv)).unwrap(); |
| //! |
| //! let mut ciphertext = vec![]; |
| //! ctx.cipher_update_vec(data, &mut ciphertext).unwrap(); |
| //! ctx.cipher_final_vec(&mut ciphertext).unwrap(); |
| //! |
| //! assert_eq!( |
| //! b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ |
| //! \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", |
| //! &ciphertext[..], |
| //! ); |
| //! ``` |
| //! |
| //! Decrypt data with AES128 CBC |
| //! |
| //! ``` |
| //! use openssl::cipher::Cipher; |
| //! use openssl::cipher_ctx::CipherCtx; |
| //! |
| //! let cipher = Cipher::aes_128_cbc(); |
| //! let data = b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\ |
| //! \x87\x4D\xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1"; |
| //! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; |
| //! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; |
| //! |
| //! let mut ctx = CipherCtx::new().unwrap(); |
| //! ctx.decrypt_init(Some(cipher), Some(key), Some(iv)).unwrap(); |
| //! |
| //! let mut plaintext = vec![]; |
| //! ctx.cipher_update_vec(data, &mut plaintext).unwrap(); |
| //! ctx.cipher_final_vec(&mut plaintext).unwrap(); |
| //! |
| //! assert_eq!(b"Some Crypto Text", &plaintext[..]); |
| //! ``` |
| #![warn(missing_docs)] |
| |
| use crate::cipher::CipherRef; |
| use crate::error::ErrorStack; |
| #[cfg(not(boringssl))] |
| use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef}; |
| use crate::{cvt, cvt_p}; |
| use cfg_if::cfg_if; |
| use foreign_types::{ForeignType, ForeignTypeRef}; |
| use libc::{c_int, c_uchar}; |
| use openssl_macros::corresponds; |
| use std::convert::{TryFrom, TryInto}; |
| use std::ptr; |
| |
| cfg_if! { |
| if #[cfg(ossl300)] { |
| use ffi::EVP_CIPHER_CTX_get0_cipher; |
| } else { |
| use ffi::EVP_CIPHER_CTX_cipher as EVP_CIPHER_CTX_get0_cipher; |
| } |
| } |
| |
| foreign_type_and_impl_send_sync! { |
| type CType = ffi::EVP_CIPHER_CTX; |
| fn drop = ffi::EVP_CIPHER_CTX_free; |
| |
| /// A context object used to perform symmetric encryption operations. |
| pub struct CipherCtx; |
| /// A reference to a [`CipherCtx`]. |
| pub struct CipherCtxRef; |
| } |
| |
| impl CipherCtx { |
| /// Creates a new context. |
| #[corresponds(EVP_CIPHER_CTX_new)] |
| pub fn new() -> Result<Self, ErrorStack> { |
| ffi::init(); |
| |
| unsafe { |
| let ptr = cvt_p(ffi::EVP_CIPHER_CTX_new())?; |
| Ok(CipherCtx::from_ptr(ptr)) |
| } |
| } |
| } |
| |
| impl CipherCtxRef { |
| /// Initializes the context for encryption. |
| /// |
| /// Normally this is called once to set all of the cipher, key, and IV. However, this process can be split up |
| /// by first setting the cipher with no key or IV and then setting the key and IV with no cipher. This can be used |
| /// to, for example, use a nonstandard IV size. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the key buffer is smaller than the key size of the cipher, the IV buffer is smaller than the IV size |
| /// of the cipher, or if a key or IV is provided before a cipher. |
| #[corresponds(EVP_EncryptInit_ex)] |
| pub fn encrypt_init( |
| &mut self, |
| type_: Option<&CipherRef>, |
| key: Option<&[u8]>, |
| iv: Option<&[u8]>, |
| ) -> Result<(), ErrorStack> { |
| self.cipher_init(type_, key, iv, ffi::EVP_EncryptInit_ex) |
| } |
| |
| /// Initializes the context for decryption. |
| /// |
| /// Normally this is called once to set all of the cipher, key, and IV. However, this process can be split up |
| /// by first setting the cipher with no key or IV and then setting the key and IV with no cipher. This can be used |
| /// to, for example, use a nonstandard IV size. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the key buffer is smaller than the key size of the cipher, the IV buffer is smaller than the IV size |
| /// of the cipher, or if a key or IV is provided before a cipher. |
| #[corresponds(EVP_DecryptInit_ex)] |
| pub fn decrypt_init( |
| &mut self, |
| type_: Option<&CipherRef>, |
| key: Option<&[u8]>, |
| iv: Option<&[u8]>, |
| ) -> Result<(), ErrorStack> { |
| self.cipher_init(type_, key, iv, ffi::EVP_DecryptInit_ex) |
| } |
| |
| fn cipher_init( |
| &mut self, |
| type_: Option<&CipherRef>, |
| key: Option<&[u8]>, |
| iv: Option<&[u8]>, |
| f: unsafe extern "C" fn( |
| *mut ffi::EVP_CIPHER_CTX, |
| *const ffi::EVP_CIPHER, |
| *mut ffi::ENGINE, |
| *const c_uchar, |
| *const c_uchar, |
| ) -> c_int, |
| ) -> Result<(), ErrorStack> { |
| if let Some(key) = key { |
| let key_len = type_.map_or_else(|| self.key_length(), |c| c.key_length()); |
| assert!(key_len <= key.len()); |
| } |
| |
| if let Some(iv) = iv { |
| let iv_len = type_.map_or_else(|| self.iv_length(), |c| c.iv_length()); |
| assert!(iv_len <= iv.len()); |
| } |
| |
| unsafe { |
| cvt(f( |
| self.as_ptr(), |
| type_.map_or(ptr::null(), |p| p.as_ptr()), |
| ptr::null_mut(), |
| key.map_or(ptr::null(), |k| k.as_ptr()), |
| iv.map_or(ptr::null(), |iv| iv.as_ptr()), |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Initializes the context to perform envelope encryption. |
| /// |
| /// Normally this is called once to set both the cipher and public keys. However, this process may be split up by |
| /// first providing the cipher with no public keys and then setting the public keys with no cipher. |
| /// |
| /// `encrypted_keys` will contain the generated symmetric key encrypted with each corresponding asymmetric private |
| /// key. The generated IV will be written to `iv`. |
| /// |
| /// # Panics |
| /// |
| /// Panics if `pub_keys` is not the same size as `encrypted_keys`, the IV buffer is smaller than the cipher's IV |
| /// size, or if an IV is provided before the cipher. |
| #[corresponds(EVP_SealInit)] |
| #[cfg(not(boringssl))] |
| pub fn seal_init<T>( |
| &mut self, |
| type_: Option<&CipherRef>, |
| pub_keys: &[PKey<T>], |
| encrypted_keys: &mut [Vec<u8>], |
| iv: Option<&mut [u8]>, |
| ) -> Result<(), ErrorStack> |
| where |
| T: HasPublic, |
| { |
| assert_eq!(pub_keys.len(), encrypted_keys.len()); |
| if !pub_keys.is_empty() { |
| let iv_len = type_.map_or_else(|| self.iv_length(), |c| c.iv_length()); |
| assert!(iv.as_ref().map_or(0, |b| b.len()) >= iv_len); |
| } |
| |
| for (pub_key, buf) in pub_keys.iter().zip(&mut *encrypted_keys) { |
| buf.resize(pub_key.size(), 0); |
| } |
| |
| let mut keys = encrypted_keys |
| .iter_mut() |
| .map(|b| b.as_mut_ptr()) |
| .collect::<Vec<_>>(); |
| let mut key_lengths = vec![0; pub_keys.len()]; |
| let pub_keys_len = i32::try_from(pub_keys.len()).unwrap(); |
| |
| unsafe { |
| cvt(ffi::EVP_SealInit( |
| self.as_ptr(), |
| type_.map_or(ptr::null(), |p| p.as_ptr()), |
| keys.as_mut_ptr(), |
| key_lengths.as_mut_ptr(), |
| iv.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), |
| pub_keys.as_ptr() as *mut _, |
| pub_keys_len, |
| ))?; |
| } |
| |
| for (buf, len) in encrypted_keys.iter_mut().zip(key_lengths) { |
| buf.truncate(len as usize); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Initializes the context to perform envelope decryption. |
| /// |
| /// Normally this is called once with all of the arguments present. However, this process may be split up by first |
| /// providing the cipher alone and then after providing the rest of the arguments in a second call. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the IV buffer is smaller than the cipher's required IV size or if the IV is provided before the |
| /// cipher. |
| #[corresponds(EVP_OpenInit)] |
| #[cfg(not(boringssl))] |
| pub fn open_init<T>( |
| &mut self, |
| type_: Option<&CipherRef>, |
| encrypted_key: &[u8], |
| iv: Option<&[u8]>, |
| priv_key: Option<&PKeyRef<T>>, |
| ) -> Result<(), ErrorStack> |
| where |
| T: HasPrivate, |
| { |
| if priv_key.is_some() { |
| let iv_len = type_.map_or_else(|| self.iv_length(), |c| c.iv_length()); |
| assert!(iv.map_or(0, |b| b.len()) >= iv_len); |
| } |
| |
| let len = c_int::try_from(encrypted_key.len()).unwrap(); |
| unsafe { |
| cvt(ffi::EVP_OpenInit( |
| self.as_ptr(), |
| type_.map_or(ptr::null(), |p| p.as_ptr()), |
| encrypted_key.as_ptr(), |
| len, |
| iv.map_or(ptr::null(), |b| b.as_ptr()), |
| priv_key.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| fn assert_cipher(&self) { |
| unsafe { |
| assert!(!EVP_CIPHER_CTX_get0_cipher(self.as_ptr()).is_null()); |
| } |
| } |
| |
| /// Returns the block size of the context's cipher. |
| /// |
| /// Stream ciphers will report a block size of 1. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| #[corresponds(EVP_CIPHER_CTX_block_size)] |
| pub fn block_size(&self) -> usize { |
| self.assert_cipher(); |
| |
| unsafe { ffi::EVP_CIPHER_CTX_block_size(self.as_ptr()) as usize } |
| } |
| |
| /// Returns the key length of the context's cipher. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| #[corresponds(EVP_CIPHER_CTX_key_length)] |
| pub fn key_length(&self) -> usize { |
| self.assert_cipher(); |
| |
| unsafe { ffi::EVP_CIPHER_CTX_key_length(self.as_ptr()) as usize } |
| } |
| |
| /// Generates a random key based on the configured cipher. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher or if the buffer is smaller than the cipher's key |
| /// length. |
| /// |
| /// This corresponds to [`EVP_CIPHER_CTX_rand_key`]. |
| /// |
| /// [`EVP_CIPHER_CTX_rand_key`]: https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_CTX_rand_key.html |
| #[corresponds(EVP_CIPHER_CTX_rand_key)] |
| #[cfg(not(boringssl))] |
| pub fn rand_key(&self, buf: &mut [u8]) -> Result<(), ErrorStack> { |
| assert!(buf.len() >= self.key_length()); |
| |
| unsafe { |
| cvt(ffi::EVP_CIPHER_CTX_rand_key( |
| self.as_ptr(), |
| buf.as_mut_ptr(), |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Sets the length of the key expected by the context. |
| /// |
| /// Only some ciphers support configurable key lengths. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| #[corresponds(EVP_CIPHER_CTX_set_key_length)] |
| pub fn set_key_length(&mut self, len: usize) -> Result<(), ErrorStack> { |
| self.assert_cipher(); |
| |
| unsafe { |
| cvt(ffi::EVP_CIPHER_CTX_set_key_length( |
| self.as_ptr(), |
| len.try_into().unwrap(), |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Returns the length of the IV expected by this context. |
| /// |
| /// Returns 0 if the cipher does not use an IV. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| #[corresponds(EVP_CIPHER_CTX_iv_length)] |
| pub fn iv_length(&self) -> usize { |
| self.assert_cipher(); |
| |
| unsafe { ffi::EVP_CIPHER_CTX_iv_length(self.as_ptr()) as usize } |
| } |
| |
| /// Returns the `num` parameter of the cipher. |
| /// |
| /// Built-in ciphers typically use this to track how much of the |
| /// current underlying block has been "used" already. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| #[corresponds(EVP_CIPHER_CTX_num)] |
| #[cfg(ossl110)] |
| pub fn num(&self) -> usize { |
| self.assert_cipher(); |
| |
| unsafe { ffi::EVP_CIPHER_CTX_num(self.as_ptr()) as usize } |
| } |
| |
| /// Sets the length of the IV expected by this context. |
| /// |
| /// Only some ciphers support configurable IV lengths. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| #[corresponds(EVP_CIHPER_CTX_ctrl)] |
| pub fn set_iv_length(&mut self, len: usize) -> Result<(), ErrorStack> { |
| self.assert_cipher(); |
| |
| let len = c_int::try_from(len).unwrap(); |
| |
| unsafe { |
| cvt(ffi::EVP_CIPHER_CTX_ctrl( |
| self.as_ptr(), |
| ffi::EVP_CTRL_GCM_SET_IVLEN, |
| len, |
| ptr::null_mut(), |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Returns the length of the authentication tag expected by this context. |
| /// |
| /// Returns 0 if the cipher is not authenticated. |
| /// |
| /// # Panics |
| /// |
| /// Panics if the context has not been initialized with a cipher. |
| /// |
| /// Requires OpenSSL 3.0.0 or newer. |
| #[corresponds(EVP_CIPHER_CTX_get_tag_length)] |
| #[cfg(ossl300)] |
| pub fn tag_length(&self) -> usize { |
| self.assert_cipher(); |
| |
| unsafe { ffi::EVP_CIPHER_CTX_get_tag_length(self.as_ptr()) as usize } |
| } |
| |
| /// Retrieves the calculated authentication tag from the context. |
| /// |
| /// This should be called after [`Self::cipher_final`], and is only supported by authenticated ciphers. |
| /// |
| /// The size of the buffer indicates the size of the tag. While some ciphers support a range of tag sizes, it is |
| /// recommended to pick the maximum size. |
| #[corresponds(EVP_CIPHER_CTX_ctrl)] |
| pub fn tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { |
| let len = c_int::try_from(tag.len()).unwrap(); |
| |
| unsafe { |
| cvt(ffi::EVP_CIPHER_CTX_ctrl( |
| self.as_ptr(), |
| ffi::EVP_CTRL_GCM_GET_TAG, |
| len, |
| tag.as_mut_ptr() as *mut _, |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Sets the length of the generated authentication tag. |
| /// |
| /// This must be called when encrypting with a cipher in CCM mode to use a tag size other than the default. |
| #[corresponds(EVP_CIPHER_CTX_ctrl)] |
| pub fn set_tag_length(&mut self, len: usize) -> Result<(), ErrorStack> { |
| let len = c_int::try_from(len).unwrap(); |
| |
| unsafe { |
| cvt(ffi::EVP_CIPHER_CTX_ctrl( |
| self.as_ptr(), |
| ffi::EVP_CTRL_GCM_SET_TAG, |
| len, |
| ptr::null_mut(), |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Sets the authentication tag for verification during decryption. |
| #[corresponds(EVP_CIPHER_CTX_ctrl)] |
| pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> { |
| let len = c_int::try_from(tag.len()).unwrap(); |
| |
| unsafe { |
| cvt(ffi::EVP_CIPHER_CTX_ctrl( |
| self.as_ptr(), |
| ffi::EVP_CTRL_GCM_SET_TAG, |
| len, |
| tag.as_ptr() as *mut _, |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Enables or disables padding. |
| /// |
| /// If padding is disabled, the plaintext must be an exact multiple of the cipher's block size. |
| #[corresponds(EVP_CIPHER_CTX_set_padding)] |
| pub fn set_padding(&mut self, padding: bool) { |
| unsafe { |
| ffi::EVP_CIPHER_CTX_set_padding(self.as_ptr(), padding as c_int); |
| } |
| } |
| |
| /// Sets the total length of plaintext data. |
| /// |
| /// This is required for ciphers operating in CCM mode. |
| #[corresponds(EVP_CipherUpdate)] |
| pub fn set_data_len(&mut self, len: usize) -> Result<(), ErrorStack> { |
| let len = c_int::try_from(len).unwrap(); |
| |
| unsafe { |
| cvt(ffi::EVP_CipherUpdate( |
| self.as_ptr(), |
| ptr::null_mut(), |
| &mut 0, |
| ptr::null(), |
| len, |
| ))?; |
| } |
| |
| Ok(()) |
| } |
| |
| /// Writes data into the context. |
| /// |
| /// Providing no output buffer will cause the input to be considered additional authenticated data (AAD). |
| /// |
| /// Returns the number of bytes written to `output`. |
| /// |
| /// # Panics |
| /// |
| /// Panics if `output` doesn't contain enough space for data to be |
| /// written as specified by [`Self::minimal_output_size`]. |
| #[corresponds(EVP_CipherUpdate)] |
| pub fn cipher_update( |
| &mut self, |
| input: &[u8], |
| output: Option<&mut [u8]>, |
| ) -> Result<usize, ErrorStack> { |
| if let Some(output) = &output { |
| let mut block_size = self.block_size(); |
| if block_size == 1 { |
| block_size = 0; |
| } |
| let min_output_size = input.len() + block_size; |
| assert!( |
| output.len() >= min_output_size, |
| "Output buffer size should be at least {} bytes.", |
| min_output_size |
| ); |
| } |
| |
| unsafe { self.cipher_update_unchecked(input, output) } |
| } |
| |
| /// Writes data into the context. |
| /// |
| /// Providing no output buffer will cause the input to be considered additional authenticated data (AAD). |
| /// |
| /// Returns the number of bytes written to `output`. |
| /// |
| /// This function is the same as [`Self::cipher_update`] but with the |
| /// output size check removed. It can be used when the exact |
| /// buffer size control is maintained by the caller. |
| /// |
| /// SAFETY: The caller is expected to provide `output` buffer |
| /// large enough to contain correct number of bytes. For streaming |
| /// ciphers the output buffer size should be at least as big as |
| /// the input buffer. For block ciphers the size of the output |
| /// buffer depends on the state of partially updated blocks. |
| #[corresponds(EVP_CipherUpdate)] |
| pub unsafe fn cipher_update_unchecked( |
| &mut self, |
| input: &[u8], |
| output: Option<&mut [u8]>, |
| ) -> Result<usize, ErrorStack> { |
| let inlen = c_int::try_from(input.len()).unwrap(); |
| |
| let mut outlen = 0; |
| |
| cvt(ffi::EVP_CipherUpdate( |
| self.as_ptr(), |
| output.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), |
| &mut outlen, |
| input.as_ptr(), |
| inlen, |
| ))?; |
| |
| Ok(outlen as usize) |
| } |
| |
| /// Like [`Self::cipher_update`] except that it appends output to a [`Vec`]. |
| pub fn cipher_update_vec( |
| &mut self, |
| input: &[u8], |
| output: &mut Vec<u8>, |
| ) -> Result<usize, ErrorStack> { |
| let base = output.len(); |
| output.resize(base + input.len() + self.block_size(), 0); |
| let len = self.cipher_update(input, Some(&mut output[base..]))?; |
| output.truncate(base + len); |
| |
| Ok(len) |
| } |
| |
| /// Finalizes the encryption or decryption process. |
| /// |
| /// Any remaining data will be written to the output buffer. |
| /// |
| /// Returns the number of bytes written to `output`. |
| /// |
| /// # Panics |
| /// |
| /// Panics if `output` is smaller than the cipher's block size. |
| #[corresponds(EVP_CipherFinal)] |
| pub fn cipher_final(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { |
| let block_size = self.block_size(); |
| if block_size > 1 { |
| assert!(output.len() >= block_size); |
| } |
| |
| unsafe { self.cipher_final_unchecked(output) } |
| } |
| |
| /// Finalizes the encryption or decryption process. |
| /// |
| /// Any remaining data will be written to the output buffer. |
| /// |
| /// Returns the number of bytes written to `output`. |
| /// |
| /// This function is the same as [`Self::cipher_final`] but with |
| /// the output buffer size check removed. |
| /// |
| /// SAFETY: The caller is expected to provide `output` buffer |
| /// large enough to contain correct number of bytes. For streaming |
| /// ciphers the output buffer can be empty, for block ciphers the |
| /// output buffer should be at least as big as the block. |
| #[corresponds(EVP_CipherFinal)] |
| pub unsafe fn cipher_final_unchecked( |
| &mut self, |
| output: &mut [u8], |
| ) -> Result<usize, ErrorStack> { |
| let mut outl = 0; |
| |
| cvt(ffi::EVP_CipherFinal( |
| self.as_ptr(), |
| output.as_mut_ptr(), |
| &mut outl, |
| ))?; |
| |
| Ok(outl as usize) |
| } |
| |
| /// Like [`Self::cipher_final`] except that it appends output to a [`Vec`]. |
| pub fn cipher_final_vec(&mut self, output: &mut Vec<u8>) -> Result<usize, ErrorStack> { |
| let base = output.len(); |
| output.resize(base + self.block_size(), 0); |
| let len = self.cipher_final(&mut output[base..])?; |
| output.truncate(base + len); |
| |
| Ok(len) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::{cipher::Cipher, rand::rand_bytes}; |
| #[cfg(not(boringssl))] |
| use std::slice; |
| |
| #[test] |
| #[cfg(not(boringssl))] |
| fn seal_open() { |
| let private_pem = include_bytes!("../test/rsa.pem"); |
| let public_pem = include_bytes!("../test/rsa.pem.pub"); |
| let private_key = PKey::private_key_from_pem(private_pem).unwrap(); |
| let public_key = PKey::public_key_from_pem(public_pem).unwrap(); |
| let cipher = Cipher::aes_256_cbc(); |
| let secret = b"My secret message"; |
| |
| let mut ctx = CipherCtx::new().unwrap(); |
| let mut encrypted_key = vec![]; |
| let mut iv = vec![0; cipher.iv_length()]; |
| let mut encrypted = vec![]; |
| ctx.seal_init( |
| Some(cipher), |
| &[public_key], |
| slice::from_mut(&mut encrypted_key), |
| Some(&mut iv), |
| ) |
| .unwrap(); |
| ctx.cipher_update_vec(secret, &mut encrypted).unwrap(); |
| ctx.cipher_final_vec(&mut encrypted).unwrap(); |
| |
| let mut decrypted = vec![]; |
| ctx.open_init(Some(cipher), &encrypted_key, Some(&iv), Some(&private_key)) |
| .unwrap(); |
| ctx.cipher_update_vec(&encrypted, &mut decrypted).unwrap(); |
| ctx.cipher_final_vec(&mut decrypted).unwrap(); |
| |
| assert_eq!(secret, &decrypted[..]); |
| } |
| |
| fn aes_128_cbc(cipher: &CipherRef) { |
| // from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf |
| let key = hex::decode("2b7e151628aed2a6abf7158809cf4f3c").unwrap(); |
| let iv = hex::decode("000102030405060708090a0b0c0d0e0f").unwrap(); |
| let pt = hex::decode("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") |
| .unwrap(); |
| let ct = hex::decode("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") |
| .unwrap(); |
| |
| let mut ctx = CipherCtx::new().unwrap(); |
| |
| ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) |
| .unwrap(); |
| ctx.set_padding(false); |
| |
| let mut buf = vec![]; |
| ctx.cipher_update_vec(&pt, &mut buf).unwrap(); |
| ctx.cipher_final_vec(&mut buf).unwrap(); |
| |
| assert_eq!(buf, ct); |
| |
| ctx.decrypt_init(Some(cipher), Some(&key), Some(&iv)) |
| .unwrap(); |
| ctx.set_padding(false); |
| |
| let mut buf = vec![]; |
| ctx.cipher_update_vec(&ct, &mut buf).unwrap(); |
| ctx.cipher_final_vec(&mut buf).unwrap(); |
| |
| assert_eq!(buf, pt); |
| } |
| |
| #[test] |
| #[cfg(ossl300)] |
| fn fetched_aes_128_cbc() { |
| let cipher = Cipher::fetch(None, "AES-128-CBC", None).unwrap(); |
| aes_128_cbc(&cipher); |
| } |
| |
| #[test] |
| fn default_aes_128_cbc() { |
| let cipher = Cipher::aes_128_cbc(); |
| aes_128_cbc(cipher); |
| } |
| |
| #[test] |
| fn test_stream_ciphers() { |
| test_stream_cipher(Cipher::aes_192_ctr()); |
| test_stream_cipher(Cipher::aes_256_ctr()); |
| } |
| |
| fn test_stream_cipher(cipher: &'static CipherRef) { |
| let mut key = vec![0; cipher.key_length()]; |
| rand_bytes(&mut key).unwrap(); |
| let mut iv = vec![0; cipher.iv_length()]; |
| rand_bytes(&mut iv).unwrap(); |
| |
| let mut ctx = CipherCtx::new().unwrap(); |
| |
| ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) |
| .unwrap(); |
| ctx.set_padding(false); |
| |
| assert_eq!( |
| 1, |
| cipher.block_size(), |
| "Need a stream cipher, not a block cipher" |
| ); |
| |
| // update cipher with non-full block |
| // this is a streaming cipher so the number of output bytes |
| // will be the same as the number of input bytes |
| let mut output = vec![0; 32]; |
| let outlen = ctx |
| .cipher_update(&[1; 15], Some(&mut output[0..15])) |
| .unwrap(); |
| assert_eq!(15, outlen); |
| |
| // update cipher with missing bytes from the previous block |
| // as previously it will output the same number of bytes as |
| // the input |
| let outlen = ctx |
| .cipher_update(&[1; 17], Some(&mut output[15..])) |
| .unwrap(); |
| assert_eq!(17, outlen); |
| |
| ctx.cipher_final_vec(&mut vec![0; 0]).unwrap(); |
| |
| // try to decrypt |
| ctx.decrypt_init(Some(cipher), Some(&key), Some(&iv)) |
| .unwrap(); |
| ctx.set_padding(false); |
| |
| // update cipher with non-full block |
| // expect that the output for stream cipher will contain |
| // the same number of bytes as the input |
| let mut output_decrypted = vec![0; 32]; |
| let outlen = ctx |
| .cipher_update(&output[0..15], Some(&mut output_decrypted[0..15])) |
| .unwrap(); |
| assert_eq!(15, outlen); |
| |
| let outlen = ctx |
| .cipher_update(&output[15..], Some(&mut output_decrypted[15..])) |
| .unwrap(); |
| assert_eq!(17, outlen); |
| |
| ctx.cipher_final_vec(&mut vec![0; 0]).unwrap(); |
| // check if the decrypted blocks are the same as input (all ones) |
| assert_eq!(output_decrypted, vec![1; 32]); |
| } |
| |
| #[test] |
| #[should_panic(expected = "Output buffer size should be at least 33 bytes.")] |
| fn full_block_updates_aes_128() { |
| output_buffer_too_small(Cipher::aes_128_cbc()); |
| } |
| |
| #[test] |
| #[should_panic(expected = "Output buffer size should be at least 33 bytes.")] |
| fn full_block_updates_aes_256() { |
| output_buffer_too_small(Cipher::aes_256_cbc()); |
| } |
| |
| #[test] |
| #[should_panic(expected = "Output buffer size should be at least 17 bytes.")] |
| fn full_block_updates_3des() { |
| output_buffer_too_small(Cipher::des_ede3_cbc()); |
| } |
| |
| fn output_buffer_too_small(cipher: &'static CipherRef) { |
| let mut key = vec![0; cipher.key_length()]; |
| rand_bytes(&mut key).unwrap(); |
| let mut iv = vec![0; cipher.iv_length()]; |
| rand_bytes(&mut iv).unwrap(); |
| |
| let mut ctx = CipherCtx::new().unwrap(); |
| |
| ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) |
| .unwrap(); |
| ctx.set_padding(false); |
| |
| let block_size = cipher.block_size(); |
| assert!(block_size > 1, "Need a block cipher, not a stream cipher"); |
| |
| ctx.cipher_update(&vec![0; block_size + 1], Some(&mut vec![0; block_size - 1])) |
| .unwrap(); |
| } |
| } |