From 272f8929b0c059dfa90484bbb76e972e8b4ac8c1 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Sat, 16 May 2026 00:47:28 +1000 Subject: [PATCH] Update to 49.0.0 Resolves: RHEL-172409 --- .gitignore | 2 + 14819.patch | 207 -------------------------------- changelog | 3 + python-cryptography.spec | 6 +- sources | 4 +- stepdown-cffi-and-maturin.patch | 10 +- 6 files changed, 13 insertions(+), 219 deletions(-) delete mode 100644 14819.patch diff --git a/.gitignore b/.gitignore index 4c1a214..bfe1f56 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ /cryptography-43.0.0-vendor.tar.bz2 /cryptography-48.0.0.tar.gz /cryptography-48.0.0-vendor.tar.bz2 +/cryptography-49.0.0.tar.gz +/cryptography-49.0.0-vendor.tar.bz2 diff --git a/14819.patch b/14819.patch deleted file mode 100644 index fb5bd9d..0000000 --- a/14819.patch +++ /dev/null @@ -1,207 +0,0 @@ -From 744e9671dc8f2cc248b89a7b03bdbfa2b0299efe Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Sat, 9 May 2026 08:43:33 +0300 -Subject: [PATCH] fix: fall back to fresh context when EVP_CIPHER_CTX_copy - fails - -EvpCipherAead pre-initialises a cipher context with the key and -clones it per operation via EVP_CIPHER_CTX_copy. When an older FIPS -provider (e.g. OpenSSL 3.0.7 FIPS module) is loaded alongside a newer -main library (>= 3.2), EVP_CIPHER_CTX_copy may fail because that -provider version does not support copying a pre-keyed context. - -Add a copy_fallback field to EvpCipherAead that stores the cipher type -and key. If the per-operation copy() fails and a fallback is present, -encrypt_into/decrypt_into re-initialise a fresh context from the stored -parameters instead of propagating the error. - -AesGcm on OpenSSL >= 3.2 uses new_with_copy_fallback() to populate the -field. Other EvpCipherAead users (AESSIV, AESOCB3, AESGCMSIV, -ChaCha20Poly1305) are unaffected: they are never used under FIPS so -copy() never fails for them, and copy_fallback remains None. - -AES_GCM_TAG_LEN replaces the magic 16 literals throughout the AesGcm -methods. - -Fixes: https://github.com/pyca/cryptography/issues/14795 - -Assisted-by: Claude Code (Sonnet 4.6) -Signed-off-by: Alexander Bokovoy ---- - src/rust/src/backend/aead.rs | 122 ++++++++++++++++++++++++++++++++++- - 1 files changed, 119 insertions(+), 3 deletions(-) - -diff --git a/src/rust/src/backend/aead.rs b/src/rust/src/backend/aead.rs -index 842ef5fab2d9..79fdb7b9ab5a 100644 ---- a/src/rust/src/backend/aead.rs -+++ b/src/rust/src/backend/aead.rs -@@ -31,9 +31,14 @@ pub(crate) struct EvpCipherAead { - base_decryption_ctx: openssl::cipher_ctx::CipherCtx, - tag_len: usize, - tag_first: bool, -+ // Cipher and key stored to reinitialise a fresh context when -+ // EVP_CIPHER_CTX_copy fails (e.g. an older FIPS provider loaded alongside -+ // a newer main library). None for algorithms where copy always succeeds. -+ copy_fallback: Option<(&'static openssl::cipher::CipherRef, Vec)>, - } - - impl EvpCipherAead { -+ #[cfg(not(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC)))] - pub(crate) fn new( - cipher: &openssl::cipher::CipherRef, - key: &[u8], -@@ -50,6 +55,33 @@ impl EvpCipherAead { - base_decryption_ctx, - tag_len, - tag_first, -+ copy_fallback: None, -+ }) -+ } -+ -+ #[cfg(any( -+ CRYPTOGRAPHY_OPENSSL_320_OR_GREATER, -+ CRYPTOGRAPHY_IS_BORINGSSL, -+ CRYPTOGRAPHY_IS_LIBRESSL, -+ CRYPTOGRAPHY_IS_AWSLC -+ ))] -+ pub(crate) fn new_with_copy_fallback( -+ cipher: &'static openssl::cipher::CipherRef, -+ key: &[u8], -+ tag_len: usize, -+ tag_first: bool, -+ ) -> CryptographyResult { -+ let mut base_encryption_ctx = openssl::cipher_ctx::CipherCtx::new()?; -+ base_encryption_ctx.encrypt_init(Some(cipher), Some(key), None)?; -+ let mut base_decryption_ctx = openssl::cipher_ctx::CipherCtx::new()?; -+ base_decryption_ctx.decrypt_init(Some(cipher), Some(key), None)?; -+ -+ Ok(EvpCipherAead { -+ base_encryption_ctx, -+ base_decryption_ctx, -+ tag_len, -+ tag_first, -+ copy_fallback: Some((cipher, key.to_vec())), - }) - } - -@@ -138,7 +170,13 @@ impl EvpCipherAead { - buf: &mut [u8], - ) -> CryptographyResult<()> { - let mut ctx = openssl::cipher_ctx::CipherCtx::new()?; -- ctx.copy(&self.base_encryption_ctx)?; -+ let copy_result = ctx.copy(&self.base_encryption_ctx); -+ if copy_result.is_err() { -+ match &self.copy_fallback { -+ Some((cipher, key)) => ctx.encrypt_init(Some(*cipher), Some(key), None)?, -+ None => copy_result?, -+ } -+ } - - Self::encrypt_with_context( - ctx, -@@ -203,7 +241,13 @@ impl EvpCipherAead { - buf: &mut [u8], - ) -> CryptographyResult<()> { - let mut ctx = openssl::cipher_ctx::CipherCtx::new()?; -- ctx.copy(&self.base_decryption_ctx)?; -+ let copy_result = ctx.copy(&self.base_decryption_ctx); -+ if copy_result.is_err() { -+ match &self.copy_fallback { -+ Some((cipher, key)) => ctx.decrypt_init(Some(*cipher), Some(key), None)?, -+ None => copy_result?, -+ } -+ } - - Self::decrypt_with_context( - ctx, -@@ -260,6 +304,73 @@ impl EvpCipherAead { - } - } - -+#[cfg(test)] -+impl EvpCipherAead { -+ // Build an instance whose base contexts are uninitialised so that -+ // EVP_CIPHER_CTX_copy fails with EVP_R_INPUT_NOT_INITIALIZED, forcing -+ // the encrypt_into / decrypt_into fallback branches (lines 175-177, -+ // 246-248) to be exercised. -+ fn with_copy_failure( -+ copy_fallback: Option<(&'static openssl::cipher::CipherRef, Vec)>, -+ ) -> CryptographyResult { -+ Ok(EvpCipherAead { -+ base_encryption_ctx: openssl::cipher_ctx::CipherCtx::new()?, -+ base_decryption_ctx: openssl::cipher_ctx::CipherCtx::new()?, -+ tag_len: 16, -+ tag_first: false, -+ copy_fallback, -+ }) -+ } -+} -+ -+#[cfg(test)] -+mod evp_cipher_aead_tests { -+ use super::*; -+ -+ // Exercise the Some branch of the copy-fallback match (lines 176, 247): -+ // copy() fails → fallback reinitialises the context → round-trip succeeds. -+ #[test] -+ fn test_copy_fallback_some_roundtrip() { -+ let key = vec![0u8; 16]; -+ let aead = -+ EvpCipherAead::with_copy_failure(Some((openssl::cipher::Cipher::aes_128_gcm(), key))) -+ .unwrap_or_else(|_| panic!("with_copy_failure")); -+ -+ let nonce = [0u8; 12]; -+ let plaintext = b"hello fallback"; -+ // _py is unused inside encrypt_into / decrypt_into; the GIL token is -+ // only there for API symmetry with LazyEvpCipherAead. -+ let py = unsafe { pyo3::Python::assume_attached() }; -+ -+ let mut enc_buf = vec![0u8; plaintext.len() + 16]; -+ aead.encrypt_into(py, plaintext, None, Some(&nonce), &mut enc_buf) -+ .unwrap_or_else(|_| panic!("encrypt_into")); -+ -+ let mut dec_buf = vec![0u8; plaintext.len()]; -+ aead.decrypt_into(py, &enc_buf, None, Some(&nonce), &mut dec_buf) -+ .unwrap_or_else(|_| panic!("decrypt_into")); -+ assert_eq!(&dec_buf, plaintext); -+ } -+ -+ // Exercise the None branch of the copy-fallback match (lines 177, 248): -+ // copy() fails and there is no fallback → the error must propagate. -+ #[test] -+ fn test_copy_fallback_none_propagates_error() { -+ let aead = -+ EvpCipherAead::with_copy_failure(None).unwrap_or_else(|_| panic!("with_copy_failure")); -+ let nonce = [0u8; 12]; -+ let py = unsafe { pyo3::Python::assume_attached() }; -+ -+ let mut buf = vec![0u8; 20]; -+ assert!(aead -+ .encrypt_into(py, b"data", None, Some(&nonce), &mut buf) -+ .is_err()); -+ assert!(aead -+ .decrypt_into(py, b"ciphertext+tag__", None, Some(&nonce), &mut buf) -+ .is_err()); -+ } -+} -+ - struct LazyEvpCipherAead { - cipher: &'static openssl::cipher::CipherRef, - key: pyo3::Py, -@@ -676,7 +787,12 @@ impl AesGcm { - CRYPTOGRAPHY_IS_AWSLC - ))] { - Ok(AesGcm { -- ctx: EvpCipherAead::new(cipher, key_buf.as_bytes(), 16, false)?, -+ ctx: EvpCipherAead::new_with_copy_fallback( -+ cipher, -+ key_buf.as_bytes(), -+ 16, -+ false, -+ )?, - }) - } else { - Ok(AesGcm { --- -2.47.0 - diff --git a/changelog b/changelog index 2d85bdc..5918143 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,6 @@ +* Tue Jun 16 2026 Fraser Tweedale - 49.0.0-1 +- Update to 49.0.0, resolves RHEL-172409 + * Sat May 16 2026 Fraser Tweedale - 48.0.0-1 - Update to 48.0.0, resolves RHEL-172409 - Fix AES-GCM with FIPS provider version mismatch, resolves RHEL-173746 diff --git a/python-cryptography.spec b/python-cryptography.spec index b5cdd12..d839aad 100644 --- a/python-cryptography.spec +++ b/python-cryptography.spec @@ -5,7 +5,7 @@ %global srcname cryptography Name: python-%{srcname} -Version: 48.0.0 +Version: 49.0.0 Release: %autorelease Summary: PyCA's cryptography library @@ -22,10 +22,6 @@ Source2: conftest-skipper.py # RHEL 10 only has python3-cffi 1.16 and maturin 1.4.0, step down requirements Patch: stepdown-cffi-and-maturin.patch -# Fix AES-GCM with FIPS provider version mismatch (RHEL-173746) -# https://github.com/pyca/cryptography/pull/14819 -Patch: 14819.patch - ExclusiveArch: %{rust_arches} BuildRequires: openssl-devel diff --git a/sources b/sources index 994ce71..af8ede9 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (cryptography-48.0.0.tar.gz) = b38d0ae952bd33149c5358bb1fe9c875b55cc438f62ab0c0ab4d30d651e9d44f2895b39194906e7e9503294ca95a19eba6d97b32d319ed36bb38fa05faf89c6b -SHA512 (cryptography-48.0.0-vendor.tar.bz2) = fddc63507ce64f6e4a6174b043ed789d8b6f5aea7f6110d95a460eb2327ca39a3bef9a87d1e17732d7782a929358ac821c2b84f39682419cfbb101dbdaa94363 +SHA512 (cryptography-49.0.0.tar.gz) = 6a4435e90b981dfe1cc90479846041f800d618b8a673351ae55e308eeb7c152959ee56cb7a7188a7b46ae3228577192c651bc9801796f62fed55e7f8c3258f33 +SHA512 (cryptography-49.0.0-vendor.tar.bz2) = dc908b8b44ffc3205481fedcbc9e2a94331ee54b9b54a878755170437eff47d2d92b766fa268a916d525808a649fac8d7b8155692e28eef99e90c8a2ae3e434f diff --git a/stepdown-cffi-and-maturin.patch b/stepdown-cffi-and-maturin.patch index 191ed4d..2d0f316 100644 --- a/stepdown-cffi-and-maturin.patch +++ b/stepdown-cffi-and-maturin.patch @@ -1,13 +1,13 @@ --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,8 @@ foreign-types-shared = "0.1" - openssl = "0.10.79" - openssl-sys = "0.9.115" + openssl = "0.10.80" + openssl-sys = "0.9.116" pem = { version = "3", default-features = false } --pyo3 = { version = "0.28", features = ["abi3"] } +-pyo3 = { version = "0.29", features = ["abi3"] } +# Disable abi3 for maturin 1.4.0 compatibility - build for specific Python version -+pyo3 = { version = "0.28" } - pyo3-build-config = { version = "0.28" } ++pyo3 = { version = "0.29" } + pyo3-build-config = { version = "0.29" } self_cell = "1" --- a/pyproject.toml