diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf5d11519..f144ba965 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -182,6 +182,8 @@ jobs: version: 338f44af3c92ef665bf740a8127a2d69c872b52a - name: openssl version: vendored + - name: openssl + version: 4.0.0 - name: openssl version: 3.6.0 - name: openssl @@ -209,17 +211,17 @@ jobs: bindgen: true library: name: libressl - version: 4.0.1 + version: 4.1.2 - target: x86_64-unknown-linux-gnu bindgen: true library: name: libressl - version: 4.1.2 + version: 4.2.1 - target: x86_64-unknown-linux-gnu bindgen: true library: name: libressl - version: 4.2.1 + version: 4.3.1 - target: x86_64-unknown-linux-gnu bindgen: false library: @@ -229,17 +231,17 @@ jobs: bindgen: false library: name: libressl - version: 4.0.1 + version: 4.1.2 - target: x86_64-unknown-linux-gnu bindgen: false library: name: libressl - version: 4.1.2 + version: 4.2.1 - target: x86_64-unknown-linux-gnu bindgen: false library: name: libressl - version: 4.2.1 + version: 4.3.1 - target: x86_64-unknown-linux-gnu bindgen: false library: @@ -261,6 +263,7 @@ jobs: id: rust-version - run: rustup target add ${{ matrix.target }} - name: Install packages + timeout-minutes: 3 run: | case "${{ matrix.target }}" in "x86_64-unknown-linux-gnu") diff --git a/openssl-sys/CHANGELOG.md b/openssl-sys/CHANGELOG.md index 5fd7b91d8..cba658bcb 100644 --- a/openssl-sys/CHANGELOG.md +++ b/openssl-sys/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +## [v0.9.114] - 2026-04-19 + +### Added + +* Added support for OpenSSL 4.x. +* Added support for LibreSSL 4.3.x. + +### Changed + +* Marked `BIO_get_mem_data` as `unsafe` on AWS-LC -- this matches other backends. +* `X509_NAME_ENTRY_get_data`, `X509_NAME_ENTRY_get_object`, and `X509_CRL_get_issuer` now return `*const` pointers under `ossl400` to match OpenSSL 4. + ## [v0.9.113] - 2026-04-12 ### Added @@ -719,7 +731,8 @@ Fixed builds against OpenSSL built with `no-cast`. * Added `X509_verify` and `X509_REQ_verify`. * Added `EVP_MD_type` and `EVP_GROUP_get_curve_name`. -[Unreleased]: https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.113..master +[Unreleased]: https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.114..master +[v0.9.114]: https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.113...openssl-sys-v0.9.114 [v0.9.113]: https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.112...openssl-sys-v0.9.113 [v0.9.112]: https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.111...openssl-sys-v0.9.112 [v0.9.111]: https://github.com/rust-openssl/rust-openssl/compare/openssl-sys-v0.9.110...openssl-sys-v0.9.111 diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index adc02ddd8..c7cbf4d89 100644 --- a/openssl-sys/Cargo.toml +++ b/openssl-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openssl-sys" -version = "0.9.113" +version = "0.9.114" authors = [ "Alex Crichton ", "Steven Fackler ", diff --git a/openssl-sys/build/cfgs.rs b/openssl-sys/build/cfgs.rs index b5527929d..a544a8b53 100644 --- a/openssl-sys/build/cfgs.rs +++ b/openssl-sys/build/cfgs.rs @@ -93,6 +93,9 @@ pub fn get(openssl_version: Option, libressl_version: Option) -> Vec<& if openssl_version >= 0x3_05_00_00_0 { cfgs.push("ossl350"); } + if openssl_version >= 0x4_00_00_00_0 { + cfgs.push("ossl400"); + } } cfgs diff --git a/openssl-sys/build/main.rs b/openssl-sys/build/main.rs index 3f9eae069..0dbbd7258 100644 --- a/openssl-sys/build/main.rs +++ b/openssl-sys/build/main.rs @@ -19,6 +19,7 @@ mod run_bindgen; #[derive(PartialEq)] enum Version { + Openssl4xx, Openssl3xx, Openssl11x, Libressl, @@ -180,6 +181,7 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(ossl320)"); println!("cargo:rustc-check-cfg=cfg(ossl330)"); println!("cargo:rustc-check-cfg=cfg(ossl340)"); + println!("cargo:rustc-check-cfg=cfg(ossl400)"); check_ssl_kind(); @@ -226,7 +228,9 @@ fn main() { } } None => match version { - Version::Openssl3xx | Version::Openssl11x if target.contains("windows-msvc") => { + Version::Openssl4xx | Version::Openssl3xx | Version::Openssl11x + if target.contains("windows-msvc") => + { vec!["libssl", "libcrypto"] } _ => vec!["ssl", "crypto"], @@ -270,7 +274,7 @@ fn main() { } // https://github.com/openssl/openssl/pull/15086 - if version == Version::Openssl3xx + if (version == Version::Openssl3xx || version == Version::Openssl4xx) && kind == "static" && (env::var("CARGO_CFG_TARGET_OS").unwrap() == "linux" || env::var("CARGO_CFG_TARGET_OS").unwrap() == "android") @@ -425,6 +429,7 @@ See rust-openssl documentation for more information: (4, 1, 0) => ('4', '1', '0'), (4, 1, _) => ('4', '1', 'x'), (4, 2, _) => ('4', '2', 'x'), + (4, 3, _) => ('4', '3', 'x'), _ => version_error(), }; @@ -436,8 +441,10 @@ See rust-openssl documentation for more information: let openssl_version = openssl_version.unwrap(); println!("cargo:version_number={openssl_version:x}"); - if openssl_version >= 0x4_00_00_00_0 { + if openssl_version >= 0x5_00_00_00_0 { version_error() + } else if openssl_version >= 0x4_00_00_00_0 { + Version::Openssl4xx } else if openssl_version >= 0x3_00_00_00_0 { Version::Openssl3xx } else if openssl_version >= 0x1_01_01_00_0 { @@ -460,8 +467,8 @@ fn version_error() -> ! { panic!( " -This crate is only compatible with OpenSSL (version 1.1.0, 1.1.1, or 3.x), or LibreSSL 3.5.0 -through 4.2.x, but a different version of OpenSSL was found. The build is now aborting +This crate is only compatible with OpenSSL (version 1.1.0, 1.1.1, 3.x, or 4.x), or LibreSSL 3.5.0 +through 4.3.x, but a different version of OpenSSL was found. The build is now aborting due to this version mismatch. " diff --git a/openssl-sys/src/handwritten/x509.rs b/openssl-sys/src/handwritten/x509.rs index b103030f6..bd3e38dd0 100644 --- a/openssl-sys/src/handwritten/x509.rs +++ b/openssl-sys/src/handwritten/x509.rs @@ -332,7 +332,13 @@ extern "C" { pub fn X509_CRL_get_REVOKED(crl: *mut X509_CRL) -> *mut stack_st_X509_REVOKED; pub fn X509_CRL_get0_nextUpdate(x: *const X509_CRL) -> *const ASN1_TIME; pub fn X509_CRL_get0_lastUpdate(x: *const X509_CRL) -> *const ASN1_TIME; - pub fn X509_CRL_get_issuer(x: *const X509_CRL) -> *mut X509_NAME; +} +const_ptr_api! { + extern "C" { + pub fn X509_CRL_get_issuer(x: *const X509_CRL) -> #[const_ptr_if(ossl400)] X509_NAME; + } +} +extern "C" { #[cfg(ossl110)] pub fn X509_get0_extensions(req: *const X509) -> *const stack_st_X509_EXTENSION; @@ -370,8 +376,8 @@ const_ptr_api! { set: c_int, ) -> c_int; pub fn i2d_X509_NAME(n: #[const_ptr_if(ossl300)] X509_NAME, buf: *mut *mut u8) -> c_int; - pub fn X509_NAME_ENTRY_get_object(ne: *const X509_NAME_ENTRY) -> *mut ASN1_OBJECT; - pub fn X509_NAME_ENTRY_get_data(ne: *const X509_NAME_ENTRY) -> *mut ASN1_STRING; + pub fn X509_NAME_ENTRY_get_object(ne: *const X509_NAME_ENTRY) -> #[const_ptr_if(ossl400)] ASN1_OBJECT; + pub fn X509_NAME_ENTRY_get_data(ne: *const X509_NAME_ENTRY) -> #[const_ptr_if(ossl400)] ASN1_STRING; } } extern "C" { diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 3ac3ff1ce..719c0f16e 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -52,8 +52,8 @@ mod aws_lc { } // BIO_get_mem_data is a C preprocessor macro by definition - #[allow(non_snake_case, clippy::not_unsafe_ptr_arg_deref)] - pub fn BIO_get_mem_data(b: *mut BIO, pp: *mut *mut c_char) -> c_long { + #[allow(non_snake_case)] + pub unsafe fn BIO_get_mem_data(b: *mut BIO, pp: *mut *mut c_char) -> c_long { unsafe { BIO_ctrl(b, BIO_CTRL_INFO, 0, pp.cast::()) } } diff --git a/openssl-sys/src/ssl.rs b/openssl-sys/src/ssl.rs index 596fd1e53..326a2f9ec 100644 --- a/openssl-sys/src/ssl.rs +++ b/openssl-sys/src/ssl.rs @@ -82,8 +82,13 @@ cfg_if! { } #[cfg(ossl110)] pub const SSL_OP_SAFARI_ECDHE_ECDSA_BUG: ssl_op_type!() = 0x00000040; +#[cfg(ossl300)] +pub const SSL_OP_IGNORE_UNEXPECTED_EOF: ssl_op_type!() = 0x00000080; +#[cfg(not(libressl430))] pub const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: ssl_op_type!() = 0x00000800; +#[cfg(libressl430)] +pub const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: ssl_op_type!() = 0x00000000; pub const SSL_OP_NO_QUERY_MTU: ssl_op_type!() = 0x00001000; pub const SSL_OP_COOKIE_EXCHANGE: ssl_op_type!() = 0x00002000; diff --git a/openssl/CHANGELOG.md b/openssl/CHANGELOG.md index 7c0b3b1a6..7666fb56d 100644 --- a/openssl/CHANGELOG.md +++ b/openssl/CHANGELOG.md @@ -2,6 +2,26 @@ ## [Unreleased] +## [v0.10.78] - 2026-04-19 + +### Added + +* Added support for OpenSSL 4.x. +* Added support for LibreSSL 4.3.x. + +### Fixed + +* Fixed several soundness issues where safe Rust callers could trigger out-of-bounds reads or writes: + * `MdCtxRef::digest_final` now returns an error when the output buffer is shorter than the digest size. + * `PkeyCtxRef::derive` now checks the output buffer length on OpenSSL 1.1.x and LibreSSL, where some key types (X25519, X448, HKDF-extract) ignore the caller-supplied length. + * Callbacks for key-loading passwords and SSL PSK and cookie generation now reject values longer than the length of the slice. + * Fixed a dangling stack pointer in the SSL custom extension callback when using a fixed-length array. + * Fixed an inverted bounds assertion in AES key unwrap. +* `Crypter::new` now panics, as documented, when an IV is required by the cipher but not provided (previously it silently used an all-zero IV). +* Avoided a panic when formatting overlong OIDs; the value is now truncated with trailing dots. +* Fixed Suite B flag assignments in `X509VerifyParam`. +* Handle errors on `OPENSSL_malloc` in `PkeyCtxRef::set_rsa_oaep_label`. + ## [v0.10.77] - 2026-04-12 ### Added @@ -1049,7 +1069,8 @@ Look at the [release tags] for information about older releases. -[Unreleased]: https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.77...master +[Unreleased]: https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.78...master +[v0.10.78]: https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.77...openssl-v0.10.78 [v0.10.77]: https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.76...openssl-v0.10.77 [v0.10.76]: https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.75...openssl-v0.10.76 [v0.10.75]: https://github.com/rust-openssl/rust-openssl/compare/openssl-v0.10.74...openssl-v0.10.75 diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index 933d04dfa..7b31bf191 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openssl" -version = "0.10.77" +version = "0.10.78" authors = ["Steven Fackler "] license = "Apache-2.0" description = "OpenSSL bindings" @@ -33,7 +33,7 @@ libc = "0.2" once_cell = "1.5.2" openssl-macros = { version = "0.1.1", path = "../openssl-macros" } -ffi = { package = "openssl-sys", version = "0.9.113", path = "../openssl-sys" } +ffi = { package = "openssl-sys", version = "0.9.114", path = "../openssl-sys" } [dev-dependencies] hex = "0.4" diff --git a/openssl/build.rs b/openssl/build.rs index d2ff88be5..feeeb40e9 100644 --- a/openssl/build.rs +++ b/openssl/build.rs @@ -36,6 +36,7 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(libressl400)"); println!("cargo:rustc-check-cfg=cfg(libressl410)"); println!("cargo:rustc-check-cfg=cfg(libressl420)"); + println!("cargo:rustc-check-cfg=cfg(libressl430)"); println!("cargo:rustc-check-cfg=cfg(ossl101)"); println!("cargo:rustc-check-cfg=cfg(ossl102)"); @@ -50,6 +51,7 @@ fn main() { println!("cargo:rustc-check-cfg=cfg(ossl330)"); println!("cargo:rustc-check-cfg=cfg(ossl340)"); println!("cargo:rustc-check-cfg=cfg(ossl350)"); + println!("cargo:rustc-check-cfg=cfg(ossl400)"); if env::var("DEP_OPENSSL_LIBRESSL").is_ok() { println!("cargo:rustc-cfg=libressl"); @@ -112,6 +114,9 @@ fn main() { if version >= 0x4_02_00_00_0 { println!("cargo:rustc-cfg=libressl420"); } + if version >= 0x4_03_00_00_0 { + println!("cargo:rustc-cfg=libressl430"); + } } if let Ok(vars) = env::var("DEP_OPENSSL_CONF") { @@ -155,5 +160,8 @@ fn main() { if version >= 0x3_05_00_00_0 { println!("cargo:rustc-cfg=ossl350"); } + if version >= 0x4_00_00_00_0 { + println!("cargo:rustc-cfg=ossl400"); + } } } diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs index 25de83d5c..c2594cb84 100644 --- a/openssl/src/aes.rs +++ b/openssl/src/aes.rs @@ -243,7 +243,7 @@ pub fn unwrap_key( in_: &[u8], ) -> Result { unsafe { - assert!(out.len() + 8 <= in_.len()); + assert!(out.len() + 8 >= in_.len()); let written = ffi::AES_unwrap_key( &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. @@ -296,6 +296,74 @@ mod test { assert_eq!(pt_actual, pt); } + // out is larger than in_.len() - 8 but still valid; should succeed. + #[test] + fn test_unwrap_key_out_oversized() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let key_data = Vec::from_hex("00112233445566778899AABBCCDDEEFF").unwrap(); + let wrapped = Vec::from_hex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap(); + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + + let mut out = vec![0u8; 32]; // larger than the 16 bytes that will be written + let n = unwrap_key(&dec_key, None, &mut out, &wrapped).unwrap(); + assert_eq!(n, 16); + assert_eq!(&out[..16], &key_data[..]); + } + + // out is smaller than in_.len() - 8; must panic. + #[test] + #[should_panic] + fn test_unwrap_key_out_too_small_panics() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let wrapped = Vec::from_hex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap(); + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + + let mut out = vec![0u8; 8]; // too small: needs 16 bytes + let _ = unwrap_key(&dec_key, None, &mut out, &wrapped); + } + + // Verify that unwrap_key returns Err when the ciphertext has been tampered with. + #[test] + fn test_unwrap_key_tampered_ciphertext() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let mut wrapped = + Vec::from_hex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap(); + // Flip a byte so the integrity check fails + wrapped[0] ^= 0xFF; + + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + let mut out = [0u8; 16]; + assert!( + unwrap_key(&dec_key, None, &mut out, &wrapped).is_err(), + "expected Err for tampered ciphertext" + ); + } + + // Verify that wrap/unwrap round-trips correctly with an explicit IV. + #[test] + fn test_wrap_unwrap_with_iv() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let key_data = Vec::from_hex("00112233445566778899AABBCCDDEEFF").unwrap(); + let iv: [u8; 8] = [0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6]; + + let enc_key = AesKey::new_encrypt(&raw_key).unwrap(); + let mut wrapped = [0u8; 24]; + wrap_key(&enc_key, Some(iv), &mut wrapped, &key_data).unwrap(); + + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + let mut unwrapped = [0u8; 16]; + unwrap_key(&dec_key, Some(iv), &mut unwrapped, &wrapped).unwrap(); + assert_eq!(&unwrapped[..], &key_data[..]); + + // Using a different IV must fail + let wrong_iv: [u8; 8] = [0x00; 8]; + let mut unwrapped2 = [0u8; 16]; + assert!( + unwrap_key(&dec_key, Some(wrong_iv), &mut unwrapped2, &wrapped).is_err(), + "expected Err when IV does not match" + ); + } + // from the RFC https://tools.ietf.org/html/rfc3394#section-2.2.3 #[test] fn test_wrap_unwrap() { diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index 44a3277b7..f03485dd6 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -723,15 +723,30 @@ impl fmt::Display for Asn1ObjectRef { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { unsafe { let mut buf = [0; 80]; - let len = ffi::OBJ_obj2txt( + let mut clamped = false; + let mut len = ffi::OBJ_obj2txt( buf.as_mut_ptr() as *mut _, buf.len() as c_int, self.as_ptr(), 0, ); + if len <= 0 { + return fmt.write_str("OBJ_obj2txt error"); + } + if len > buf.len() as i32 { + // omit trailing NUL + len = (buf.len() - 1) as i32; + clamped = true; + } match str::from_utf8(&buf[..len as usize]) { Err(_) => fmt.write_str("error"), - Ok(s) => fmt.write_str(s), + Ok(s) => { + if clamped { + fmt.write_str(&(s.to_owned() + "...")) + } else { + fmt.write_str(s) + } + } } } } @@ -893,6 +908,13 @@ mod tests { .expect_err("parsing invalid OID should fail"); } + #[test] + fn very_long_object() { + let fifty_ones = "1.".repeat(49) + "1"; + let object = Asn1Object::from_str(&fifty_ones).unwrap(); + assert_eq!(object.as_ref().to_string(), "1.".repeat(40) + ".."); + } + #[test] #[cfg(ossl111)] fn object_to_slice() { diff --git a/openssl/src/derive.rs b/openssl/src/derive.rs index e0d35720b..34838d8f3 100644 --- a/openssl/src/derive.rs +++ b/openssl/src/derive.rs @@ -131,6 +131,26 @@ impl<'a> Deriver<'a> { /// Returns the number of bytes written. #[corresponds(EVP_PKEY_derive)] pub fn derive(&mut self, buf: &mut [u8]) -> Result { + // See the matching comment in `PkeyCtxRef::derive`. On 1.1.x some + // pmeths ignore *keylen and write the full natural output + // (X25519/X448), while others (default ECDH) deliberately truncate. + // Derive into a temp buffer when the probed size exceeds the + // caller's buffer to prevent OOB writes while preserving the + // truncation semantics. + #[cfg(any(all(ossl110, not(ossl300)), libressl))] + { + let required = self.len()?; + if required != usize::MAX && buf.len() < required { + let mut temp = vec![0u8; required]; + let mut len = required; + unsafe { + cvt(ffi::EVP_PKEY_derive(self.0, temp.as_mut_ptr(), &mut len))?; + } + let copy_len = buf.len().min(len); + buf[..copy_len].copy_from_slice(&temp[..copy_len]); + return Ok(copy_len); + } + } let mut len = buf.len(); unsafe { cvt(ffi::EVP_PKEY_derive( @@ -195,6 +215,26 @@ mod test { assert!(!shared.is_empty()); } + #[test] + #[cfg(any(ossl111, libressl370))] + fn derive_undersized_buffer() { + // Without the temp-buffer fallback in this crate, X25519 on 1.1.x + // would OOB into a 4-byte buffer because it ignores *keylen. + // On 1.1.x / LibreSSL the fallback kicks in and we return the + // truncated prefix. On 3.0+ the provider rejects undersized + // buffers before any write happens, so the call errors out. + let pkey = PKey::generate_x25519().unwrap(); + let pkey2 = PKey::generate_x25519().unwrap(); + let mut deriver = Deriver::new(&pkey).unwrap(); + deriver.set_peer(&pkey2).unwrap(); + let mut buf = [0u8; 4]; + let result = deriver.derive(&mut buf); + #[cfg(any(all(ossl110, not(ossl300)), libressl))] + assert_eq!(result.unwrap(), 4); + #[cfg(all(ossl300, not(libressl)))] + assert!(result.is_err()); + } + #[test] #[cfg(ossl300)] fn test_ec_key_derive_ex() { diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs index 38a481fa4..6b19c47b1 100644 --- a/openssl/src/ec.rs +++ b/openssl/src/ec.rs @@ -1098,6 +1098,7 @@ mod test { use super::*; use crate::bn::{BigNum, BigNumContext}; use crate::nid::Nid; + use crate::symm::Cipher; #[test] fn key_new_by_curve_name() { @@ -1110,6 +1111,25 @@ mod test { EcKey::generate(&group).unwrap(); } + #[test] + fn test_password_callback_oversize_return_is_rejected() { + // The password callback trampoline must reject a user-returned + // length that exceeds the size of the buffer it handed out. + // Otherwise some versions of OpenSSL read past the buffer when + // deriving the decryption key. + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let encrypted = key + .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"correct-pw") + .unwrap(); + + let result = EcKey::private_key_from_pem_callback(&encrypted, |buf| { + buf[..10].copy_from_slice(b"correct-pw"); + Ok(buf.len() * 10) + }); + assert!(result.is_err()); + } + #[test] fn ec_group_from_components() { // parameters are from secp256r1 diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index eddf5d5bc..f80f99645 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -1,7 +1,7 @@ //! Bindings to OpenSSL //! //! This crate provides a safe interface to the popular OpenSSL cryptography library. OpenSSL versions 1.0.2 through -//! 3.x.x and LibreSSL versions 3.5 through 4.2.x are supported. +//! 4.x.x and LibreSSL versions 3.5 through 4.3.x are supported. //! //! # Building //! diff --git a/openssl/src/md_ctx.rs b/openssl/src/md_ctx.rs index 58c672c2c..8f48805e8 100644 --- a/openssl/src/md_ctx.rs +++ b/openssl/src/md_ctx.rs @@ -242,6 +242,10 @@ impl MdCtxRef { pub fn digest_final(&mut self, out: &mut [u8]) -> Result { let mut len = u32::try_from(out.len()).unwrap_or(u32::MAX); + if self.size() > len as usize { + return Err(ErrorStack::get()); + } + unsafe { cvt(ffi::EVP_DigestFinal( self.as_ptr(), @@ -549,4 +553,13 @@ mod test { // Validate result of digest of "World" assert_eq!(reset_result, world_expected); } + + #[test] + fn digest_final_checks_length() { + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_init(Md::sha256()).unwrap(); + ctx.digest_update(b"Some Crypto Text").unwrap(); + let mut digest = [0; 16]; + assert!(ctx.digest_final(&mut digest).is_err()); + } } diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index b85876c25..f3c9d42fc 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -644,7 +644,7 @@ impl PkeyCtxRef { let len = LenType::try_from(label.len()).unwrap(); unsafe { - let p = ffi::OPENSSL_malloc(label.len() as _); + let p = cvt_p(ffi::OPENSSL_malloc(label.len() as _))?; ptr::copy_nonoverlapping(label.as_ptr(), p as *mut _, label.len()); let r = cvt(ffi::EVP_PKEY_CTX_set0_rsa_oaep_label( @@ -820,7 +820,57 @@ impl PkeyCtxRef { /// /// If `buf` is set to `None`, an upper bound on the number of bytes required for the buffer will be returned. #[corresponds(EVP_PKEY_derive)] - pub fn derive(&mut self, buf: Option<&mut [u8]>) -> Result { + #[allow(unused_mut)] + pub fn derive(&mut self, mut buf: Option<&mut [u8]>) -> Result { + // On OpenSSL 1.1.x some pmeths ignore *keylen and unconditionally + // write the full natural output size (X25519, X448, HKDF-extract), + // which can overflow a shorter caller-provided buffer. Others honor + // *keylen by truncating the output (notably the default ECDH + // EVP_PKEY_EC pmeth, where the OpenSSL source explicitly documents + // that *keylen below the natural size "is not an error, the result + // is truncated"). + // + // We can't distinguish those two groups from the probe alone, so + // when the probe reports a natural size larger than the caller's + // buffer, derive into a temporary buffer of the probed size and + // copy the leading bytes out. This prevents the OOB write for the + // ignore-*keylen group and produces the same bytes for the + // honor-*keylen group (ECDH_compute_key copies leading bytes of + // the shared secret either way). + // + // Some pmeths (HKDF extract-and-expand and expand-only on 1.1.x) + // don't support the NULL-out probe and fail it with an empty error + // stack; those honor *keylen during derivation, so clear the + // errors and proceed with the direct path. usize::MAX is a + // sentinel some pmeths use when *keylen is caller-chosen. + // + // 3.0+ providers check the buffer size themselves, so this whole + // dance is cfg-gated to 1.1.x and LibreSSL. + #[cfg(any(all(ossl110, not(ossl300)), libressl))] + { + if let Some(b) = buf.as_deref_mut() { + let mut required = 0; + let probe_ok = unsafe { + ffi::EVP_PKEY_derive(self.as_ptr(), ptr::null_mut(), &mut required) == 1 + }; + if !probe_ok { + let _ = ErrorStack::get(); + } else if required != usize::MAX && b.len() < required { + let mut temp = vec![0u8; required]; + let mut len = required; + unsafe { + cvt(ffi::EVP_PKEY_derive( + self.as_ptr(), + temp.as_mut_ptr(), + &mut len, + ))?; + } + let copy_len = b.len().min(len); + b[..copy_len].copy_from_slice(&temp[..copy_len]); + return Ok(copy_len); + } + } + } let mut len = buf.as_ref().map_or(0, |b| b.len()); unsafe { cvt(ffi::EVP_PKEY_derive( @@ -1042,6 +1092,29 @@ mod test { ctx.derive_to_vec(&mut buf).unwrap(); } + #[test] + #[cfg(any(ossl111, libressl370))] + fn derive_undersized_buffer() { + // Without the temp-buffer fallback in this crate, X25519 on 1.1.x + // would OOB into a 4-byte buffer because it ignores *keylen. + // On 1.1.x / LibreSSL the fallback kicks in and we return the + // truncated prefix. On 3.0+ the provider rejects undersized + // buffers before any write happens, so the call errors out. + let key1 = PKey::generate_x25519().unwrap(); + let key2 = PKey::generate_x25519().unwrap(); + + let mut ctx = PkeyCtx::new(&key1).unwrap(); + ctx.derive_init().unwrap(); + ctx.derive_set_peer(&key2).unwrap(); + + let mut buf = [0u8; 4]; + let result = ctx.derive(Some(&mut buf)); + #[cfg(any(all(ossl110, not(ossl300)), libressl))] + assert_eq!(result.unwrap(), 4); + #[cfg(all(ossl300, not(libressl)))] + assert!(result.is_err()); + } + #[test] #[cfg(not(any(boringssl, awslc)))] fn cmac_keygen() { diff --git a/openssl/src/ssl/callbacks.rs b/openssl/src/ssl/callbacks.rs index eba57b14a..6d14d0f76 100644 --- a/openssl/src/ssl/callbacks.rs +++ b/openssl/src/ssl/callbacks.rs @@ -84,8 +84,10 @@ where let identity_sl = util::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize); #[allow(clippy::unnecessary_cast)] let psk_sl = util::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize); + let psk_cap = psk_sl.len(); match (*callback)(ssl, hint, identity_sl, psk_sl) { - Ok(psk_len) => psk_len as u32, + Ok(psk_len) if psk_len <= psk_cap => psk_len as u32, + Ok(_) => 0, Err(e) => { e.put(); 0 @@ -123,8 +125,10 @@ where // Give the callback mutable slices into which it can write the psk. #[allow(clippy::unnecessary_cast)] let psk_sl = util::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize); + let psk_cap = psk_sl.len(); match (*callback)(ssl, identity, psk_sl) { - Ok(psk_len) => psk_len as u32, + Ok(psk_len) if psk_len <= psk_cap => psk_len as u32, + Ok(_) => 0, Err(e) => { e.put(); 0 @@ -392,11 +396,13 @@ where .expect("BUG: stateless cookie generate callback missing") as *const F; #[allow(clippy::unnecessary_cast)] let slice = util::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize); + let cap = slice.len(); match (*callback)(ssl, slice) { - Ok(len) => { + Ok(len) if len <= cap => { *cookie_len = len as size_t; 1 } + Ok(_) => 0, Err(e) => { e.put(); 0 @@ -443,11 +449,13 @@ where #[allow(clippy::unnecessary_cast)] let slice = util::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1); + let cap = slice.len(); match (*callback)(ssl, slice) { - Ok(len) => { + Ok(len) if len <= cap => { *cookie_len = len as c_uint; 1 } + Ok(_) => 0, Err(e) => { e.put(); 0 @@ -515,9 +523,6 @@ where match (*callback)(ssl, ectx, cert) { Ok(None) => 0, Ok(Some(buf)) => { - *outlen = buf.as_ref().len(); - *out = buf.as_ref().as_ptr(); - let idx = Ssl::cached_ex_index::>(); let mut buf = Some(buf); let new = match ssl.ex_data_mut(idx) { @@ -530,6 +535,14 @@ where if new { ssl.set_ex_data(idx, CustomExtAddState(buf)); } + + // Capture the out pointer AFTER buf has been moved into ex_data. + // The move invalidates any previous pointer into buf. + let stored = ssl.ex_data(idx).unwrap(); + let data = stored.0.as_ref().unwrap().as_ref(); + *outlen = data.len(); + *out = data.as_ptr(); + 1 } Err(alert) => { diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index b77e94c90..8a66338da 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -150,6 +150,11 @@ bitflags! { /// Disables a countermeasure against an SSLv3/TLSv1.0 vulnerability affecting CBC ciphers. const DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS as SslOptionsRepr; + /// If set, a peer closing the connection without sending a close_notify alert is + /// treated as a normal EOF rather than an error. + #[cfg(ossl300)] + const IGNORE_UNEXPECTED_EOF = ffi::SSL_OP_IGNORE_UNEXPECTED_EOF as SslOptionsRepr; + /// A "reasonable default" set of options which enables compatibility flags. #[cfg(not(any(boringssl, awslc)))] const ALL = ffi::SSL_OP_ALL as SslOptionsRepr; @@ -3731,6 +3736,10 @@ impl SslStream { /// then the first `n` bytes of `buf` are guaranteed to be initialized. #[corresponds(SSL_read_ex)] pub fn ssl_read_uninit(&mut self, buf: &mut [MaybeUninit]) -> Result { + if buf.is_empty() { + return Ok(0); + } + cfg_if! { if #[cfg(any(ossl111, libressl))] { let mut readbytes = 0; @@ -3749,10 +3758,6 @@ impl SslStream { Err(self.make_error(ret)) } } else { - if buf.is_empty() { - return Ok(0); - } - let len = usize::min(c_int::MAX as usize, buf.len()) as c_int; let ret = unsafe { ffi::SSL_read(self.ssl().as_ptr(), buf.as_mut_ptr().cast(), len) @@ -3772,6 +3777,10 @@ impl SslStream { /// OpenSSL is waiting on read or write readiness. #[corresponds(SSL_write_ex)] pub fn ssl_write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + return Ok(0); + } + cfg_if! { if #[cfg(any(ossl111, libressl))] { let mut written = 0; @@ -3790,10 +3799,6 @@ impl SslStream { Err(self.make_error(ret)) } } else { - if buf.is_empty() { - return Ok(0); - } - let len = usize::min(c_int::MAX as usize, buf.len()) as c_int; let ret = unsafe { ffi::SSL_write(self.ssl().as_ptr(), buf.as_ptr().cast(), len) diff --git a/openssl/src/ssl/test/mod.rs b/openssl/src/ssl/test/mod.rs index f4a58c203..68c1be5b4 100644 --- a/openssl/src/ssl/test/mod.rs +++ b/openssl/src/ssl/test/mod.rs @@ -675,6 +675,8 @@ fn default_verify_paths() { let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); ctx.set_default_verify_paths().unwrap(); ctx.set_verify(SslVerifyMode::PEER); + #[cfg(ossl400)] + ctx.set_options(super::SslOptions::IGNORE_UNEXPECTED_EOF); let ctx = ctx.build(); let s = match TcpStream::connect("google.com:443") { Ok(s) => s, @@ -965,7 +967,7 @@ fn cert_store() { } #[test] -#[cfg_attr(any(boringssl, awslc), ignore)] +#[cfg_attr(any(boringssl, awslc, ossl400), ignore)] fn tmp_dh_callback() { static CALLED_BACK: AtomicBool = AtomicBool::new(false); @@ -989,7 +991,7 @@ fn tmp_dh_callback() { } #[test] -#[cfg_attr(any(boringssl, awslc), ignore)] +#[cfg_attr(any(boringssl, awslc, ossl400), ignore)] fn tmp_dh_callback_ssl() { static CALLED_BACK: AtomicBool = AtomicBool::new(false); @@ -1250,6 +1252,44 @@ fn custom_extensions() { assert!(FOUND_EXTENSION.load(Ordering::SeqCst)); } +#[test] +#[cfg(ossl111)] +fn custom_extensions_inline_buffer() { + static FOUND_EXTENSION: AtomicBool = AtomicBool::new(false); + const EXPECTED: [u8; 128] = [0xAB; 128]; + + let mut server = Server::builder(); + server + .ctx() + .add_custom_ext( + 12345, + ExtensionContext::CLIENT_HELLO, + |_, _, _| -> Result, _> { unreachable!() }, + |_, _, data, _| { + FOUND_EXTENSION.store(data == EXPECTED, Ordering::SeqCst); + Ok(()) + }, + ) + .unwrap(); + + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .add_custom_ext( + 12345, + ssl::ExtensionContext::CLIENT_HELLO, + move |_, _, _| Ok(Some(EXPECTED)), + |_, _, _, _| unreachable!(), + ) + .unwrap(); + + client.connect(); + + assert!(FOUND_EXTENSION.load(Ordering::SeqCst)); +} + fn _check_kinds() { fn is_send() {} fn is_sync() {} @@ -1258,86 +1298,68 @@ fn _check_kinds() { is_sync::>(); } -#[test] #[cfg(ossl111)] -fn stateless() { - use super::SslOptions; - - #[derive(Debug)] - struct MemoryStream { - incoming: io::Cursor>, - outgoing: Vec, - } - - impl MemoryStream { - pub fn new() -> Self { - Self { - incoming: io::Cursor::new(Vec::new()), - outgoing: Vec::new(), - } - } - - pub fn extend_incoming(&mut self, data: &[u8]) { - self.incoming.get_mut().extend_from_slice(data); - } +#[derive(Debug)] +struct MemoryStream { + incoming: io::Cursor>, + outgoing: Vec, +} - pub fn take_outgoing(&mut self) -> Outgoing<'_> { - Outgoing(&mut self.outgoing) +#[cfg(ossl111)] +impl MemoryStream { + fn new() -> Self { + Self { + incoming: io::Cursor::new(Vec::new()), + outgoing: Vec::new(), } } - impl Read for MemoryStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let n = self.incoming.read(buf)?; - if self.incoming.position() == self.incoming.get_ref().len() as u64 { - self.incoming.set_position(0); - self.incoming.get_mut().clear(); - } - if n == 0 { - return Err(io::Error::new( - io::ErrorKind::WouldBlock, - "no data available", - )); - } - Ok(n) - } + fn extend_incoming(&mut self, data: &[u8]) { + self.incoming.get_mut().extend_from_slice(data); } - impl Write for MemoryStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.outgoing.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } + fn take_outgoing(&mut self) -> Vec { + mem::take(&mut self.outgoing) } +} - pub struct Outgoing<'a>(&'a mut Vec); - - impl Drop for Outgoing<'_> { - fn drop(&mut self) { - self.0.clear(); +#[cfg(ossl111)] +impl Read for MemoryStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let n = self.incoming.read(buf)?; + if self.incoming.position() == self.incoming.get_ref().len() as u64 { + self.incoming.set_position(0); + self.incoming.get_mut().clear(); } - } - - impl ::std::ops::Deref for Outgoing<'_> { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0 + if n == 0 { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "no data available", + )); } + Ok(n) } +} - impl AsRef<[u8]> for Outgoing<'_> { - fn as_ref(&self) -> &[u8] { - self.0 - } +#[cfg(ossl111)] +impl Write for MemoryStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.outgoing.write(buf) } - fn send(from: &mut MemoryStream, to: &mut MemoryStream) { - to.extend_incoming(&from.take_outgoing()); + fn flush(&mut self) -> io::Result<()> { + Ok(()) } +} +#[cfg(ossl111)] +fn send(from: &mut MemoryStream, to: &mut MemoryStream) { + to.extend_incoming(&from.take_outgoing()); +} + +#[test] +#[cfg(ossl111)] +fn stateless() { // // Setup // @@ -1427,6 +1449,149 @@ fn psk_ciphers() { assert!(CLIENT_CALLED.load(Ordering::SeqCst)); } +// Regression tests: the PSK/cookie trampolines used to forward the callback's +// returned `usize` to OpenSSL without checking it against the slice length. + +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] +#[cfg(target_pointer_width = "64")] +#[test] +fn psk_client_cb_oversize_psk_len_rejected() { + // Without the fix, `psk_len as u32` truncates the returned length; the low + // 32 bits match `PSK.len()` and slip past OpenSSL's `> PSK_MAX_PSK_LEN` + // check. (Rust's slice length equals `PSK_MAX_PSK_LEN`, so truncation is + // the only way to differentiate — hence the 64-bit guard.) + const CIPHER: &str = "PSK-AES256-CBC-SHA"; + const PSK: &[u8] = b"thisisaverysecurekey"; + const CLIENT_IDENT: &[u8] = b"thisisaclient"; + + let mut server = Server::builder(); + server.ctx().set_cipher_list(CIPHER).unwrap(); + server.ctx().set_psk_server_callback(|_, _identity, psk| { + psk[..PSK.len()].copy_from_slice(PSK); + Ok(PSK.len()) + }); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + #[cfg(any(boringssl, ossl111, awslc))] + client.ctx().set_options(SslOptions::NO_TLSV1_3); + client.ctx().set_cipher_list(CIPHER).unwrap(); + client + .ctx() + .set_psk_client_callback(move |_, _, identity, psk| { + identity[..CLIENT_IDENT.len()].copy_from_slice(CLIENT_IDENT); + identity[CLIENT_IDENT.len()] = 0; + psk[..PSK.len()].copy_from_slice(PSK); + Ok((u32::MAX as usize) + 1 + PSK.len()) + }); + + client.connect_err(); +} + +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] +#[cfg(target_pointer_width = "64")] +#[test] +fn psk_server_cb_oversize_psk_len_rejected() { + // Server-side counterpart — same `as u32` truncation bypass. + const CIPHER: &str = "PSK-AES256-CBC-SHA"; + const PSK: &[u8] = b"thisisaverysecurekey"; + const CLIENT_IDENT: &[u8] = b"thisisaclient"; + + let mut server = Server::builder(); + server.ctx().set_cipher_list(CIPHER).unwrap(); + server.ctx().set_psk_server_callback(|_, _identity, psk| { + psk[..PSK.len()].copy_from_slice(PSK); + Ok((u32::MAX as usize) + 1 + PSK.len()) + }); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + #[cfg(any(boringssl, ossl111, awslc))] + client.ctx().set_options(SslOptions::NO_TLSV1_3); + client.ctx().set_cipher_list(CIPHER).unwrap(); + client + .ctx() + .set_psk_client_callback(move |_, _, identity, psk| { + identity[..CLIENT_IDENT.len()].copy_from_slice(CLIENT_IDENT); + identity[CLIENT_IDENT.len()] = 0; + psk[..PSK.len()].copy_from_slice(PSK); + Ok(PSK.len()) + }); + + client.connect_err(); +} + +#[test] +#[cfg(ossl111)] +fn stateless_cookie_cb_oversize_length_rejected() { + // Callback claims a length past the slice end. The fix makes the + // trampoline report failure so stateless() errors cleanly. + let mut client_ctx = SslContext::builder(SslMethod::tls()).unwrap(); + client_ctx.clear_options(SslOptions::ENABLE_MIDDLEBOX_COMPAT); + let mut client_stream = + SslStream::new(Ssl::new(&client_ctx.build()).unwrap(), MemoryStream::new()).unwrap(); + + let mut server_ctx = SslContext::builder(SslMethod::tls()).unwrap(); + server_ctx + .set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + server_ctx + .set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + server_ctx.set_stateless_cookie_generate_cb(|_, buf| Ok(buf.len() + 1)); + server_ctx.set_stateless_cookie_verify_cb(|_, _| true); + let mut server_stream = + SslStream::new(Ssl::new(&server_ctx.build()).unwrap(), MemoryStream::new()).unwrap(); + + client_stream.connect().unwrap_err(); + send(client_stream.get_mut(), server_stream.get_mut()); + assert!(server_stream.stateless().is_err()); +} + +#[test] +#[cfg(not(any(boringssl, awslc)))] +fn dtls_cookie_generate_cb_oversize_length_rejected() { + // Rust hands the callback `DTLS1_COOKIE_LENGTH - 1` bytes but OpenSSL's + // internal cookie buffer is `DTLS1_COOKIE_LENGTH`; returning `buf.len() + 1` + // passes OpenSSL's `cookie_leni > sizeof(s->d1->cookie)` check. Without the + // fix, the server sends a HelloVerifyRequest containing one unwritten byte + // and the verify callback fires on the client's echo. + static VERIFY_CALLED: AtomicBool = AtomicBool::new(false); + VERIFY_CALLED.store(false, Ordering::SeqCst); + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let server = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_options(SslOptions::COOKIE_EXCHANGE); + ctx.set_cookie_generate_cb(|_, buf| Ok(buf.len() + 1)); + ctx.set_cookie_verify_cb(|_, _| { + VERIFY_CALLED.store(true, Ordering::SeqCst); + true + }); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let _ = ssl.accept(stream); + }); + + let stream = TcpStream::connect(addr).unwrap(); + let ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let _ = ssl.connect(stream); + + server.join().unwrap(); + assert!(!VERIFY_CALLED.load(Ordering::SeqCst)); +} + #[test] fn sni_callback_swapped_ctx() { static CALLED_BACK: AtomicBool = AtomicBool::new(false); diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index 43fb7294b..232779689 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -620,6 +620,10 @@ impl Crypter { key: &[u8], iv: Option<&[u8]>, ) -> Result { + assert!( + iv.is_some() || t.iv_len().is_none(), + "an IV is required for this cipher" + ); let mut ctx = CipherCtx::new()?; let f = match mode { @@ -1754,4 +1758,28 @@ mod tests { } assert_eq!(ct1, &r[..count]); } + + #[test] + #[should_panic(expected = "an IV is required for this cipher")] + fn test_crypter_panics_for_missing_iv_cbc() { + let key = [0u8; 16]; + let _ = Crypter::new( + super::Cipher::aes_128_cbc(), + super::Mode::Encrypt, + &key, + None, + ); + } + + #[test] + #[should_panic(expected = "an IV is required for this cipher")] + fn test_crypter_panics_for_missing_iv_gcm() { + let key = [0u8; 16]; + let _ = Crypter::new( + super::Cipher::aes_128_gcm(), + super::Mode::Encrypt, + &key, + None, + ); + } } diff --git a/openssl/src/util.rs b/openssl/src/util.rs index c903a3209..61574f3b6 100644 --- a/openssl/src/util.rs +++ b/openssl/src/util.rs @@ -55,6 +55,7 @@ where })); match result { + Ok(Ok(len)) if len > size as usize => 0, Ok(Ok(len)) => len as c_int, Ok(Err(_)) => { // FIXME restore error stack diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index cd08d9614..5d03dde62 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1324,7 +1324,7 @@ impl X509NameEntryRef { pub fn data(&self) -> &Asn1StringRef { unsafe { let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr()); - Asn1StringRef::from_ptr(data) + Asn1StringRef::from_ptr(data as *mut _) } } @@ -1334,7 +1334,7 @@ impl X509NameEntryRef { pub fn object(&self) -> &Asn1ObjectRef { unsafe { let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr()); - Asn1ObjectRef::from_ptr(object) + Asn1ObjectRef::from_ptr(object as *mut _) } } } @@ -1873,7 +1873,7 @@ impl X509CrlRef { unsafe { let name = X509_CRL_get_issuer(self.as_ptr()); assert!(!name.is_null()); - X509NameRef::from_ptr(name) + X509NameRef::from_ptr(name as *mut _) } } diff --git a/openssl/src/x509/verify.rs b/openssl/src/x509/verify.rs index 78026f9db..8759e6a92 100644 --- a/openssl/src/x509/verify.rs +++ b/openssl/src/x509/verify.rs @@ -51,9 +51,9 @@ bitflags! { #[cfg(ossl110)] const SUITEB_128_LOS_ONLY = ffi::X509_V_FLAG_SUITEB_128_LOS_ONLY; #[cfg(ossl110)] - const SUITEB_192_LOS = ffi::X509_V_FLAG_SUITEB_128_LOS; + const SUITEB_128_LOS = ffi::X509_V_FLAG_SUITEB_128_LOS; #[cfg(ossl110)] - const SUITEB_128_LOS = ffi::X509_V_FLAG_SUITEB_192_LOS; + const SUITEB_192_LOS = ffi::X509_V_FLAG_SUITEB_192_LOS; const PARTIAL_CHAIN = ffi::X509_V_FLAG_PARTIAL_CHAIN as _; const NO_ALT_CHAINS = ffi::X509_V_FLAG_NO_ALT_CHAINS as _; const NO_CHECK_TIME = ffi::X509_V_FLAG_NO_CHECK_TIME as _;