diff --git a/openssl-sys/src/crypto.rs b/openssl-sys/src/crypto.rs index 63a95a289ccc47457ae31caef12f401b4c0176e6..b5b6f351edb1211fdf89b542db6b269b16630a0e 100644 --- a/openssl-sys/src/crypto.rs +++ b/openssl-sys/src/crypto.rs @@ -127,4 +127,9 @@ extern "C" { pub fn FIPS_mode_set(onoff: c_int) -> c_int; pub fn CRYPTO_memcmp(a: *const c_void, b: *const c_void, len: size_t) -> c_int; + + #[cfg(ossl300)] + pub fn OSSL_LIB_CTX_new() -> *mut OSSL_LIB_CTX; + #[cfg(ossl300)] + pub fn OSSL_LIB_CTX_free(libcts: *mut OSSL_LIB_CTX); } diff --git a/openssl-sys/src/evp.rs b/openssl-sys/src/evp.rs index 71a8d4fff0d4824a5a59cfd719015d5769f99335..3872ed9225c39aacb36e2434ab0f6447a94234b0 100644 --- a/openssl-sys/src/evp.rs +++ b/openssl-sys/src/evp.rs @@ -39,6 +39,11 @@ cfg_if! { pub fn EVP_CIPHER_get_block_size(cipher: *const EVP_CIPHER) -> c_int; pub fn EVP_CIPHER_get_iv_length(cipher: *const EVP_CIPHER) -> c_int; pub fn EVP_CIPHER_get_nid(cipher: *const EVP_CIPHER) -> c_int; + pub fn EVP_CIPHER_fetch( + ctx: *mut OSSL_LIB_CTX, + algorithm: *const c_char, + properties: *const c_char, + ) -> *mut EVP_CIPHER; pub fn EVP_CIPHER_free(cipher: *mut EVP_CIPHER); pub fn EVP_CIPHER_CTX_get0_cipher(ctx: *const EVP_CIPHER_CTX) -> *const EVP_CIPHER; diff --git a/openssl/src/cipher.rs b/openssl/src/cipher.rs index dfed73b37d4e835060f9dbc34e44ab5dee6f55fb..bae08d091bdd024e4e40f707449a68751f5ca961 100644 --- a/openssl/src/cipher.rs +++ b/openssl/src/cipher.rs @@ -1,8 +1,18 @@ //! Symmetric ciphers. +#[cfg(ossl300)] +use crate::cvt_p; +#[cfg(ossl300)] +use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::lib_ctx::LibCtxRef; use crate::nid::Nid; use cfg_if::cfg_if; use foreign_types::{ForeignTypeRef, Opaque}; +#[cfg(ossl300)] +use std::ffi::CString; +#[cfg(ossl300)] +use std::ptr; cfg_if! { if #[cfg(any(ossl110, libressl273))] { @@ -105,6 +115,33 @@ impl Cipher { } } + /// Fetches a cipher object corresponding to the specified algorithm name and properties. + /// + /// This corresponds to [`EVP_CIPHER_fetch`]. + /// + /// Requires OpenSSL 3.0.0 or newer. + /// + /// [`EVP_CIPHER_fetch`]: https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html + #[cfg(ossl300)] + pub fn fetch( + ctx: Option<&LibCtxRef>, + algorithm: &str, + properties: Option<&str>, + ) -> Result { + let algorithm = CString::new(algorithm).unwrap(); + let properties = properties.map(|s| CString::new(s).unwrap()); + + unsafe { + let ptr = cvt_p(ffi::EVP_CIPHER_fetch( + ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + algorithm.as_ptr(), + properties.map_or(ptr::null_mut(), |s| s.as_ptr()), + ))?; + + Ok(Cipher::from_ptr(ptr)) + } + } + pub fn aes_128_ecb() -> &'static CipherRef { unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ecb() as *mut _) } } diff --git a/openssl/src/cipher_ctx.rs b/openssl/src/cipher_ctx.rs index 99efd43d4122116490321ed984cff1a63ed7b9fc..081e79a94a599f1c4bcede31898946f70befb458 100644 --- a/openssl/src/cipher_ctx.rs +++ b/openssl/src/cipher_ctx.rs @@ -669,4 +669,48 @@ mod test { 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.set_padding(false); + + ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) + .unwrap(); + + 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(); + + 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); + } } diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 47ed98c699b5bbdcb08f847f5d4ea742c7742e81..091ba0f6eacdf1e76433f76c69dcd85c7da3fa79 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -151,6 +151,8 @@ pub mod ex_data; #[cfg(not(any(libressl, ossl300)))] pub mod fips; pub mod hash; +#[cfg(ossl300)] +pub mod lib_ctx; pub mod memcmp; pub mod nid; #[cfg(not(osslconf = "OPENSSL_NO_OCSP"))] diff --git a/openssl/src/lib_ctx.rs b/openssl/src/lib_ctx.rs new file mode 100644 index 0000000000000000000000000000000000000000..7bfc79121a2acb41f63ac237281bf87de60aaadf --- /dev/null +++ b/openssl/src/lib_ctx.rs @@ -0,0 +1,20 @@ +use crate::cvt_p; +use crate::error::ErrorStack; +use foreign_types::ForeignType; + +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_LIB_CTX; + fn drop = ffi::OSSL_LIB_CTX_free; + + pub struct LibCtx; + pub struct LibCtxRef; +} + +impl LibCtx { + pub fn new() -> Result { + unsafe { + let ptr = cvt_p(ffi::OSSL_LIB_CTX_new())?; + Ok(LibCtx::from_ptr(ptr)) + } + } +}