diff options
Diffstat (limited to 'vendor/openssl/src/cipher_ctx.rs')
-rw-r--r-- | vendor/openssl/src/cipher_ctx.rs | 1106 |
1 files changed, 1106 insertions, 0 deletions
diff --git a/vendor/openssl/src/cipher_ctx.rs b/vendor/openssl/src/cipher_ctx.rs new file mode 100644 index 0000000..f9031d2 --- /dev/null +++ b/vendor/openssl/src/cipher_ctx.rs @@ -0,0 +1,1106 @@ +//! 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}; +#[cfg(ossl102)] +use bitflags::bitflags; +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; +} + +#[cfg(ossl102)] +bitflags! { + /// Flags for `EVP_CIPHER_CTX`. + pub struct CipherCtxFlags : c_int { + /// The flag used to opt into AES key wrap ciphers. + const FLAG_WRAP_ALLOW = ffi::EVP_CIPHER_CTX_FLAG_WRAP_ALLOW; + } +} + +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 { + #[corresponds(EVP_CIPHER_CTX_copy)] + pub fn copy(&mut self, src: &CipherCtxRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_CIPHER_CTX_copy(self.as_ptr(), src.as_ptr()))?; + Ok(()) + } + } + + /// 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_CIPHER_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(()) + } + + /// Set ctx flags. + /// + /// This function is currently used to enable AES key wrap feature supported by OpenSSL 1.0.2 or newer. + #[corresponds(EVP_CIPHER_CTX_set_flags)] + #[cfg(ossl102)] + pub fn set_flags(&mut self, flags: CipherCtxFlags) { + unsafe { + ffi::EVP_CIPHER_CTX_set_flags(self.as_ptr(), flags.bits()); + } + } + + /// 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) + } + + /// Like [`Self::cipher_update`] except that it writes output into the + /// `data` buffer. The `inlen` parameter specifies the number of bytes in + /// `data` that are considered the input. For streaming ciphers, the size of + /// `data` must be at least the input size. Otherwise, it must be at least + /// an additional block size larger. + /// + /// Note: Use [`Self::cipher_update`] with no output argument to write AAD. + /// + /// # Panics + /// + /// This function panics if the input size cannot be represented as `int` or + /// exceeds the buffer size, or if the output buffer does not contain enough + /// additional space. + #[corresponds(EVP_CipherUpdate)] + pub fn cipher_update_inplace( + &mut self, + data: &mut [u8], + inlen: usize, + ) -> Result<usize, ErrorStack> { + assert!(inlen <= data.len(), "Input size may not exceed buffer size"); + let block_size = self.block_size(); + if block_size != 1 { + assert!( + data.len() >= inlen + block_size, + "Output buffer size must be at least {} bytes.", + inlen + block_size + ); + } + + let inlen = c_int::try_from(inlen).unwrap(); + let mut outlen = 0; + unsafe { + cvt(ffi::EVP_CipherUpdate( + self.as_ptr(), + data.as_mut_ptr(), + &mut outlen, + data.as_ptr(), + inlen, + )) + }?; + + Ok(outlen as usize) + } + + /// 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(); + + // encrypt again, but use in-place encryption this time + // First reset the IV + ctx.encrypt_init(None, None, Some(&iv)).unwrap(); + ctx.set_padding(false); + let mut data_inplace: [u8; 32] = [1; 32]; + let outlen = ctx + .cipher_update_inplace(&mut data_inplace[0..15], 15) + .unwrap(); + assert_eq!(15, outlen); + + let outlen = ctx + .cipher_update_inplace(&mut data_inplace[15..32], 17) + .unwrap(); + assert_eq!(17, outlen); + + ctx.cipher_final(&mut [0u8; 0]).unwrap(); + + // Check that the resulting data is encrypted in the same manner + assert_eq!(data_inplace.as_slice(), output.as_slice()); + + // 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]); + + // decrypt again, but now the output in-place + ctx.decrypt_init(None, None, Some(&iv)).unwrap(); + ctx.set_padding(false); + + let outlen = ctx.cipher_update_inplace(&mut output[0..15], 15).unwrap(); + assert_eq!(15, outlen); + + let outlen = ctx.cipher_update_inplace(&mut output[15..], 17).unwrap(); + assert_eq!(17, outlen); + + ctx.cipher_final_vec(&mut vec![0; 0]).unwrap(); + assert_eq!(output_decrypted, output); + } + + #[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(); + } + + #[cfg(ossl102)] + fn cipher_wrap_test(cipher: &CipherRef, pt: &str, ct: &str, key: &str, iv: Option<&str>) { + let pt = hex::decode(pt).unwrap(); + let key = hex::decode(key).unwrap(); + let expected = hex::decode(ct).unwrap(); + let iv = iv.map(|v| hex::decode(v).unwrap()); + let padding = 8 - pt.len() % 8; + let mut computed = vec![0; pt.len() + padding + cipher.block_size() * 2]; + let mut ctx = CipherCtx::new().unwrap(); + + ctx.set_flags(CipherCtxFlags::FLAG_WRAP_ALLOW); + ctx.encrypt_init(Some(cipher), Some(&key), iv.as_deref()) + .unwrap(); + + let count = ctx.cipher_update(&pt, Some(&mut computed)).unwrap(); + let rest = ctx.cipher_final(&mut computed[count..]).unwrap(); + computed.truncate(count + rest); + + if computed != expected { + println!("Computed: {}", hex::encode(&computed)); + println!("Expected: {}", hex::encode(&expected)); + if computed.len() != expected.len() { + println!( + "Lengths differ: {} in computed vs {} expected", + computed.len(), + expected.len() + ); + } + panic!("test failure"); + } + } + + #[test] + #[cfg(ossl102)] + fn test_aes128_wrap() { + let pt = "00112233445566778899aabbccddeeff"; + let ct = "7940ff694448b5bb5139c959a4896832e55d69aa04daa27e"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "0001020304050607"; + + cipher_wrap_test(Cipher::aes_128_wrap(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl102)] + fn test_aes128_wrap_default_iv() { + let pt = "00112233445566778899aabbccddeeff"; + let ct = "38f1215f0212526f8a70b51955b9fbdc9fe3041d9832306e"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + + cipher_wrap_test(Cipher::aes_128_wrap(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl110)] + fn test_aes128_wrap_pad() { + let pt = "00112233445566778899aabbccddee"; + let ct = "f13998f5ab32ef82a1bdbcbe585e1d837385b529572a1e1b"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "00010203"; + + cipher_wrap_test(Cipher::aes_128_wrap_pad(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl110)] + fn test_aes128_wrap_pad_default_iv() { + let pt = "00112233445566778899aabbccddee"; + let ct = "3a501085fb8cf66f4186b7df851914d471ed823411598add"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + + cipher_wrap_test(Cipher::aes_128_wrap_pad(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl102)] + fn test_aes192_wrap() { + let pt = "9f6dee187d35302116aecbfd059657efd9f7589c4b5e7f5b"; + let ct = "83b89142dfeeb4871e078bfb81134d33e23fedc19b03a1cf689973d3831b6813"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "0001020304050607"; + + cipher_wrap_test(Cipher::aes_192_wrap(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl102)] + fn test_aes192_wrap_default_iv() { + let pt = "9f6dee187d35302116aecbfd059657efd9f7589c4b5e7f5b"; + let ct = "c02c2cf11505d3e4851030d5534cbf5a1d7eca7ba8839adbf239756daf1b43e6"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + + cipher_wrap_test(Cipher::aes_192_wrap(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl110)] + fn test_aes192_wrap_pad() { + let pt = "00112233445566778899aabbccddee"; + let ct = "b4f6bb167ef7caf061a74da82b36ad038ca057ab51e98d3a"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "00010203"; + + cipher_wrap_test(Cipher::aes_192_wrap_pad(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl110)] + fn test_aes192_wrap_pad_default_iv() { + let pt = "00112233445566778899aabbccddee"; + let ct = "b2c37a28cc602753a7c944a4c2555a2df9c98b2eded5312e"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + + cipher_wrap_test(Cipher::aes_192_wrap_pad(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl102)] + fn test_aes256_wrap() { + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"; + let ct = "cc05da2a7f56f7dd0c144231f90bce58648fa20a8278f5a6b7d13bba6aa57a33229d4333866b7fd6"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "0001020304050607"; + + cipher_wrap_test(Cipher::aes_256_wrap(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl102)] + fn test_aes256_wrap_default_iv() { + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"; + let ct = "0b24f068b50e52bc6987868411c36e1b03900866ed12af81eb87cef70a8d1911731c1d7abf789d88"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + + cipher_wrap_test(Cipher::aes_256_wrap(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl110)] + fn test_aes256_wrap_pad() { + let pt = "00112233445566778899aabbccddee"; + let ct = "91594e044ccc06130d60e6c84a996aa4f96a9faff8c5f6e7"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "00010203"; + + cipher_wrap_test(Cipher::aes_256_wrap_pad(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl110)] + fn test_aes256_wrap_pad_default_iv() { + let pt = "00112233445566778899aabbccddee"; + let ct = "dc3c166a854afd68aea624a4272693554bf2e4fcbae602cd"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + + cipher_wrap_test(Cipher::aes_256_wrap_pad(), pt, ct, key, None); + } +} |