Version in base suite: 3.0.20-1~deb12u1 Base version: openssl_3.0.20-1~deb12u1 Target version: openssl_3.0.20-1~deb12u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/o/openssl/openssl_3.0.20-1~deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/o/openssl/openssl_3.0.20-1~deb12u2.dsc /srv/release.debian.org/tmp/XWngczhCrm/openssl-3.0.20/debian/binary.tar |binary openssl-3.0.20/debian/changelog | 18 openssl-3.0.20/debian/patches/Add-tests-for-CVE-2026-34182.patch | 243 +++++++ openssl-3.0.20/debian/patches/Apply-the-buffered-IV-on-the-AES-OCB-EVP_Cipher-path.patch | 333 ++++++++++ openssl-3.0.20/debian/patches/Avoid-length-truncation-in-ASN1_STRING_set.patch | 88 ++ openssl-3.0.20/debian/patches/CMS-Produce-error-when-AEAD-algorithms-are-used-in-envelo.patch | 237 +++++++ openssl-3.0.20/debian/patches/Fix-handling-of-empty-ciphertext-messages-in-AES-SIV.patch | 112 +++ openssl-3.0.20/debian/patches/Fix-possible-use-after-free-in-OpenSSL-PKCS7_verify.patch | 37 + openssl-3.0.20/debian/patches/Fix-potential-NULL-dereference-processing-CMS-PasswordRec.patch | 28 openssl-3.0.20/debian/patches/Match-the-local-q-DHX-parameter-against-the-peer-s-q.patch | 36 + openssl-3.0.20/debian/patches/Reject-oversized-inputs-in-ASN1_mbstring_ncopy.patch | 103 +++ openssl-3.0.20/debian/patches/Reject-potentially-forged-encrypted-CMS-AuthEnvelopedData.patch | 53 + openssl-3.0.20/debian/patches/Test-for-CVE-2026-42766.patch | 193 +++++ openssl-3.0.20/debian/patches/Test-for-CVE-2026-45447-UAF-in-PKCS7_verify.patch | 100 +++ openssl-3.0.20/debian/patches/cms-kek_unwrap_key-Fix-out-of-bounds-read-in-check-byte-v.patch | 51 + openssl-3.0.20/debian/patches/cms-kek_unwrap_key-test-for-fix-out-of-bounds-read-in-che.patch | 110 +++ openssl-3.0.20/debian/patches/series | 14 17 files changed, 1756 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpms5azy7a/openssl_3.0.20-1~deb12u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpms5azy7a/openssl_3.0.20-1~deb12u2.dsc: no acceptable signature found Binary files /srv/release.debian.org/tmp/hkMuQgZDvM/openssl-3.0.20/debian/binary.tar and /srv/release.debian.org/tmp/XWngczhCrm/openssl-3.0.20/debian/binary.tar differ diff -Nru openssl-3.0.20/debian/changelog openssl-3.0.20/debian/changelog --- openssl-3.0.20/debian/changelog 2026-05-04 18:47:50.000000000 +0000 +++ openssl-3.0.20/debian/changelog 2026-06-06 19:56:20.000000000 +0000 @@ -1,3 +1,21 @@ +openssl (3.0.20-1~deb12u2) bookworm-security; urgency=medium + + * CVE-2026-7383 ("Possible Heap Buffer Overflow in ASN.1 Multibyte String + Conversion") + * CVE-2026-9076 ("Out-of-Bounds Read in CMS Password-Based Decryption") + * CVE-2026-34180 ("Heap Buffer Over-read in ASN.1 Content Parsing") + * CVE-2026-34182 ("CMS AuthEnvelopedData Processing May Accept Forged + Messages") + * CVE-2026-42766 ("Possible NULL Dereference in Password-Based CMS + Decryption") + * CVE-2026-42770 ("FFC-DH Peer Validation Uses Attacker-Supplied q") + * CVE-2026-45445 ("AES-OCB IV Ignored on EVP_Cipher() Path") + * CVE-2026-45446 ("Incorrect Tag Processing for Empty Messages in + AES-GCM-SIV and AES-SIV modes") + * CVE-2026-45447 ("Heap Use-After-Free in OpenSSL PKCS7_verify()") + + -- Sebastian Andrzej Siewior Sat, 06 Jun 2026 21:56:20 +0200 + openssl (3.0.20-1~deb12u1) bookworm; urgency=medium * Import 3.0.20 diff -Nru openssl-3.0.20/debian/patches/Add-tests-for-CVE-2026-34182.patch openssl-3.0.20/debian/patches/Add-tests-for-CVE-2026-34182.patch --- openssl-3.0.20/debian/patches/Add-tests-for-CVE-2026-34182.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Add-tests-for-CVE-2026-34182.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,243 @@ +From: Neil Horman +Date: Tue, 5 May 2026 09:16:29 -0400 +Subject: Add tests for CVE-2026-34182 + +Test to ensure that for a given CMS message: + +1) We do not allow the creation of a CMS message containing + AuthEnvelopedData with a non-AEAD cipher. +2) We do not accept a message containing AuthEnvelopedData with a + non-AEAD cipher specified in the AlgorithmIdentifier. +3) We do not allow tag lengths less that 4 bytes. +--- + test/cmsapitest.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 189 insertions(+), 7 deletions(-) + +diff --git a/test/cmsapitest.c b/test/cmsapitest.c +index 0a7e536bbe75..ba804ee1a60b 100644 +--- a/test/cmsapitest.c ++++ b/test/cmsapitest.c +@@ -13,7 +13,7 @@ + #include + #include + #include +- ++#include + #include "testutil.h" + + static X509 *cert = NULL; +@@ -22,6 +22,191 @@ static char *derin = NULL; + static char *too_long_iv_cms_in = NULL; + static char *pwri_kek_oob_der_in = NULL; + ++/* ++ * This is our bad cms data, it contains an AuthEnvelopedData field ++ * with a CIPHER OID set to AES-256-OFB ++ */ ++static const unsigned char bad_cms_der[452] = { ++ 0x30, 0x82, 0x01, 0xc0, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, ++ 0x01, 0x09, 0x10, 0x01, 0x17, 0xa0, 0x82, 0x01, 0xaf, 0x30, 0x82, 0x01, ++ 0xab, 0x02, 0x01, 0x00, 0x31, 0x82, 0x01, 0x44, 0x30, 0x82, 0x01, 0x40, ++ 0x02, 0x01, 0x00, 0x30, 0x28, 0x30, 0x10, 0x31, 0x0e, 0x30, 0x0c, 0x06, ++ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x52, 0x65, 0x63, 0x69, 0x70, 0x02, ++ 0x14, 0x1a, 0x5c, 0x04, 0x9b, 0x3a, 0x64, 0xff, 0xd4, 0x63, 0xde, 0x4f, ++ 0x90, 0xe5, 0x76, 0xe2, 0x18, 0xe8, 0x5c, 0x9e, 0xd7, 0x30, 0x0d, 0x06, ++ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, ++ 0x04, 0x82, 0x01, 0x00, 0x18, 0xcf, 0x9f, 0x44, 0x95, 0x79, 0xe9, 0x96, ++ 0x7d, 0x0f, 0xd1, 0xb4, 0xc2, 0x38, 0xb0, 0xc9, 0x76, 0xd5, 0xba, 0x08, ++ 0x5c, 0xbf, 0xc3, 0x30, 0xea, 0x3a, 0x68, 0xa4, 0xba, 0x99, 0x4c, 0x70, ++ 0x97, 0xb8, 0xa9, 0xce, 0x71, 0x4c, 0x54, 0xa3, 0xfd, 0x81, 0x9e, 0x15, ++ 0x63, 0xb7, 0x23, 0x46, 0x17, 0x69, 0xaf, 0x8f, 0xbd, 0xa3, 0x54, 0x23, ++ 0xf3, 0xf5, 0x35, 0xa8, 0xd4, 0x9c, 0xec, 0xe1, 0x17, 0x2c, 0x6d, 0x0b, ++ 0xad, 0xc0, 0xe9, 0x1d, 0xd1, 0x8d, 0x59, 0xd5, 0x29, 0xc6, 0x40, 0xc4, ++ 0xcd, 0x4e, 0x87, 0x70, 0x19, 0x5d, 0x88, 0x50, 0xbd, 0x4a, 0x13, 0xb3, ++ 0xef, 0x0c, 0x6d, 0x6a, 0xc5, 0x51, 0xbb, 0x5c, 0x39, 0x17, 0xda, 0xb1, ++ 0x71, 0x17, 0x88, 0xfb, 0x6a, 0xef, 0x7f, 0x85, 0xa7, 0x04, 0x71, 0xc7, ++ 0x83, 0x91, 0xb3, 0x30, 0x1b, 0x3d, 0x18, 0x7f, 0x63, 0xbf, 0x42, 0x7c, ++ 0xae, 0x6f, 0xae, 0xa1, 0x17, 0x84, 0xfd, 0x67, 0x2a, 0x4f, 0x4c, 0xe9, ++ 0x05, 0x26, 0x2c, 0xd5, 0xab, 0x0c, 0xcf, 0xdc, 0x3f, 0x24, 0xcf, 0x71, ++ 0x26, 0x7a, 0x1f, 0xf7, 0xc9, 0x92, 0x5e, 0xb6, 0x3d, 0x7f, 0xc3, 0x08, ++ 0xd3, 0xad, 0xc0, 0xc8, 0x4f, 0x42, 0x0c, 0xf3, 0xac, 0x23, 0x11, 0xdf, ++ 0x75, 0x84, 0x69, 0x8c, 0xa6, 0x59, 0x43, 0xfb, 0xf7, 0x6b, 0x62, 0xf0, ++ 0xf7, 0x35, 0x07, 0xc4, 0xf8, 0xd5, 0x12, 0x4a, 0x16, 0x62, 0xbc, 0x04, ++ 0xaa, 0x9a, 0x2e, 0xb2, 0x1a, 0xfa, 0x4c, 0x82, 0xce, 0x9e, 0xa8, 0x6d, ++ 0xc1, 0x29, 0x59, 0xe0, 0x33, 0xb5, 0xa6, 0x47, 0x09, 0x2e, 0xbf, 0x60, ++ 0xa6, 0xb3, 0x21, 0xa0, 0x15, 0xac, 0x92, 0x29, 0xb5, 0xe6, 0xe0, 0xd4, ++ 0x8b, 0xd8, 0x21, 0xe2, 0x17, 0x98, 0xd1, 0x11, 0x5d, 0xc5, 0xae, 0x24, ++ 0xe8, 0x92, 0xdb, 0x96, 0xa3, 0x5b, 0x58, 0xa7, 0x30, 0x4c, 0x06, 0x09, ++ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1d, 0x06, ++ 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2b, 0x04, 0x10, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, ++ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, ++ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, ++ 0x41, 0x41, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++}; ++ ++/* ++ * This array represents a der encoded contentinfo structure addressed to ++ * servercert.pem, with the tag value of the aes-256-gcm cipher used to encrypt ++ * the contents of the mssages down to 1 byte. Decoding it should fail ++ */ ++static const unsigned char one_byte_mac_cms_der[423] = { ++ 0x30, 0x82, 0x01, 0xa3, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, ++ 0x01, 0x09, 0x10, 0x01, 0x17, 0xa0, 0x82, 0x01, 0x92, 0x30, 0x82, 0x01, ++ 0x8e, 0x02, 0x01, 0x00, 0x31, 0x82, 0x01, 0x33, 0x30, 0x82, 0x01, 0x2f, ++ 0x02, 0x01, 0x00, 0x30, 0x17, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, ++ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, ++ 0x41, 0x02, 0x01, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, ++ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x10, ++ 0x3a, 0x8c, 0xee, 0x4e, 0xe2, 0x1f, 0xfe, 0xcc, 0x28, 0x39, 0x9e, 0x46, ++ 0xbe, 0xa7, 0xd5, 0x02, 0x2a, 0x53, 0x06, 0x5f, 0x94, 0x6b, 0x69, 0x6d, ++ 0x2d, 0xe8, 0x44, 0xa6, 0x43, 0x52, 0x82, 0x89, 0x2d, 0xf1, 0x9b, 0xb9, ++ 0x9e, 0xa4, 0x8d, 0x77, 0xf1, 0xd2, 0x8e, 0x86, 0x79, 0x06, 0x3e, 0x90, ++ 0xf0, 0xca, 0x9e, 0xb5, 0x35, 0xd5, 0x89, 0xf0, 0x7c, 0x06, 0xa0, 0x91, ++ 0xbf, 0xf4, 0x61, 0xaa, 0x5c, 0x99, 0xa3, 0x64, 0x15, 0xfd, 0xf9, 0x90, ++ 0xf0, 0xf3, 0x25, 0x5b, 0x48, 0xa1, 0xfb, 0x7a, 0xce, 0x63, 0xdc, 0xa9, ++ 0xfe, 0x7c, 0xbe, 0x9c, 0xaa, 0xd3, 0x42, 0x0e, 0x4a, 0xc3, 0x4b, 0x4e, ++ 0x76, 0x6d, 0x52, 0x54, 0x85, 0x4e, 0xab, 0x50, 0x2c, 0x5f, 0xc2, 0x8b, ++ 0x9f, 0x1f, 0x0f, 0x8a, 0x7c, 0xb3, 0x0a, 0xde, 0x50, 0x9b, 0xef, 0x89, ++ 0xf2, 0xea, 0x07, 0xca, 0x11, 0x76, 0x29, 0xaf, 0xe4, 0x59, 0x28, 0x19, ++ 0x48, 0x96, 0x67, 0xdd, 0xdd, 0x01, 0xf0, 0x14, 0xbe, 0x3d, 0xa5, 0xa3, ++ 0x83, 0x21, 0x39, 0x29, 0xb7, 0x8f, 0xb7, 0xf4, 0x85, 0x05, 0xee, 0xca, ++ 0xbb, 0xbd, 0xc0, 0xaf, 0x0d, 0xf1, 0xef, 0x5f, 0x06, 0x05, 0xeb, 0x0e, ++ 0x55, 0xf0, 0x7e, 0x13, 0x1a, 0x2a, 0x37, 0xd4, 0xba, 0x26, 0xc8, 0x2e, ++ 0x6b, 0xc3, 0xe1, 0xcf, 0x28, 0xab, 0x0d, 0xab, 0xdd, 0xa7, 0xf4, 0xd3, ++ 0x59, 0xcd, 0xc7, 0x2d, 0xa1, 0x56, 0x5f, 0x47, 0x77, 0x27, 0x17, 0x71, ++ 0xae, 0x75, 0xc8, 0x71, 0x58, 0xf9, 0xab, 0x67, 0xda, 0x23, 0x62, 0xa0, ++ 0x6d, 0xe5, 0x2d, 0x06, 0xb8, 0xc0, 0xac, 0xaa, 0x38, 0xa4, 0x0d, 0xb5, ++ 0xb2, 0xce, 0xa7, 0x26, 0x0d, 0x3a, 0x88, 0x2f, 0x8d, 0x6c, 0xa0, 0xf6, ++ 0x94, 0xf2, 0x2c, 0x37, 0x03, 0xaf, 0x67, 0x5c, 0xf3, 0x2c, 0xfb, 0xe8, ++ 0x16, 0x9e, 0x55, 0x30, 0x4f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, ++ 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1e, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, ++ 0x65, 0x03, 0x04, 0x01, 0x2e, 0x30, 0x11, 0x04, 0x0c, 0x6b, 0x1d, 0xe5, ++ 0xb2, 0x38, 0x0e, 0x17, 0x91, 0x9c, 0x9c, 0x40, 0x35, 0x02, 0x01, 0x10, ++ 0x80, 0x22, 0xa0, 0x90, 0x75, 0x74, 0xdf, 0x2d, 0xba, 0x4f, 0xce, 0x4e, ++ 0x7e, 0x52, 0xb0, 0x2e, 0x5f, 0xe0, 0x84, 0x01, 0xb1, 0x49, 0x0b, 0x69, ++ 0xc7, 0x61, 0x63, 0x84, 0x3a, 0xfc, 0xaa, 0x86, 0xfc, 0x96, 0x4e, 0x6c, ++ 0x04, 0x01, 0x92 ++}; ++ ++static int test_short_mac_on_auth_envelope_data(void) ++{ ++ int ret = 0; ++ const unsigned char *derptr = one_byte_mac_cms_der; ++ BIO *outmsgbio = BIO_new(BIO_s_mem()); ++ CMS_ContentInfo *content = d2i_CMS_ContentInfo(NULL, &derptr, OSSL_NELEM(one_byte_mac_cms_der)); ++ ++ if (!TEST_ptr(content)) ++ goto end; ++ ++ /* ++ * We expect this to fail, as the tag value in the authEnvelopedData parameter is ++ * a single byte ++ */ ++ if (!TEST_false(CMS_decrypt(content, privkey, cert, NULL, outmsgbio, CMS_TEXT))) ++ goto end; ++ ++ ret = 1; ++end: ++ BIO_free(outmsgbio); ++ CMS_ContentInfo_free(content); ++ return ret; ++} ++ ++static int test_non_aead_on_auth_envelope_dec(void) ++{ ++ int ret = 0; ++ const unsigned char *derptr = bad_cms_der; ++ BIO *outmsgbio = BIO_new(BIO_s_mem()); ++ CMS_ContentInfo *content = d2i_CMS_ContentInfo(NULL, &derptr, OSSL_NELEM(bad_cms_der)); ++ ++ if (!TEST_ptr(content)) ++ goto end; ++ ++ /* ++ * We expect this to fail ++ */ ++ if (!TEST_false(CMS_decrypt(content, privkey, cert, NULL, outmsgbio, ++ CMS_TEXT))) ++ goto end; ++ ++ ret = 1; ++end: ++ BIO_free(outmsgbio); ++ CMS_ContentInfo_free(content); ++ return ret; ++} ++ ++static int test_non_aead_on_auth_envelope_enc(void) ++{ ++ CMS_ContentInfo *content = NULL; ++ STACK_OF(X509) *certstack = sk_X509_new_null(); ++ const EVP_CIPHER *cipher = EVP_aes_128_cbc(); ++ const char *msg = "Hello world"; ++ BIO *msgbio = BIO_new_mem_buf(msg, (int)strlen(msg)); ++ BIO *outmsgbio = BIO_new(BIO_s_mem()); ++ X509 *recip; ++ int i; ++ int ret = 0; ++ ++ if (!TEST_ptr(certstack) || !TEST_ptr(msgbio) || !TEST_ptr(outmsgbio)) ++ goto end; ++ ++ if (!TEST_int_gt(sk_X509_push(certstack, cert), 0)) ++ goto end; ++ ++ /* ++ * Emulate CMS_encrypt here, but use a non AEAD cipher ++ */ ++ content = CMS_AuthEnvelopedData_create_ex(cipher, NULL, NULL); ++ ++ if (!TEST_ptr(content)) ++ goto end; ++ ++ for (i = 0; i < sk_X509_num(certstack); i++) { ++ recip = sk_X509_value(certstack, i); ++ if (!TEST_ptr(CMS_add1_recipient_cert(content, recip, CMS_TEXT))) ++ goto end; ++ } ++ ++ /* ++ * We expect this to fail as we are using a non-AEAD cipher on ++ * AuthEnvelopedData ++ */ ++ if (!TEST_int_eq(CMS_final(content, msgbio, NULL, CMS_TEXT), 0)) ++ goto end; ++ ++ ret = 1; ++end: ++ sk_X509_free(certstack); ++ BIO_free(msgbio); ++ BIO_free(outmsgbio); ++ CMS_ContentInfo_free(content); ++ return ret; ++} ++ + static int test_encrypt_decrypt(const EVP_CIPHER *cipher) + { + int testresult = 0; +@@ -46,12 +231,6 @@ static int test_encrypt_decrypt(const EVP_CIPHER *cipher) + CMS_TEXT))) + goto end; + +- if (!(EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) +- && !TEST_ptr(contentbio = CMS_EnvelopedData_decrypt(content->d.envelopedData, +- NULL, privkey, cert, NULL, +- CMS_TEXT, NULL, NULL))) +- goto end; +- + /* Check we got the message we first started with */ + if (!TEST_int_eq(BIO_gets(outmsgbio, buf, sizeof(buf)), strlen(msg)) + || !TEST_int_eq(strcmp(buf, msg), 0)) +@@ -578,6 +757,9 @@ int setup_tests(void) + ADD_TEST(test_encrypt_decrypt_aes_128_gcm); + ADD_TEST(test_encrypt_decrypt_aes_192_gcm); + ADD_TEST(test_encrypt_decrypt_aes_256_gcm); ++ ADD_TEST(test_non_aead_on_auth_envelope_enc); ++ ADD_TEST(test_non_aead_on_auth_envelope_dec); ++ ADD_TEST(test_short_mac_on_auth_envelope_data); + ADD_TEST(test_d2i_CMS_bio_NULL); + ADD_TEST(test_CMS_set1_key_mem_leak); + ADD_TEST(test_encrypted_data); diff -Nru openssl-3.0.20/debian/patches/Apply-the-buffered-IV-on-the-AES-OCB-EVP_Cipher-path.patch openssl-3.0.20/debian/patches/Apply-the-buffered-IV-on-the-AES-OCB-EVP_Cipher-path.patch --- openssl-3.0.20/debian/patches/Apply-the-buffered-IV-on-the-AES-OCB-EVP_Cipher-path.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Apply-the-buffered-IV-on-the-AES-OCB-EVP_Cipher-path.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,333 @@ +From: Viktor Dukhovni +Date: Mon, 18 May 2026 18:09:44 +1000 +Subject: Apply the buffered IV on the AES-OCB EVP_Cipher() path + +aes_ocb_cipher(), the OCB provider's OSSL_FUNC_CIPHER_CIPHER slot, +processed input without flushing the buffered IV into the OCB +context. Effective nonce was 0 regardless of the caller's IV; +EVP_*Final_ex() then emitted a tag depending only on (key, iv). +This gave (key, nonce) reuse and single-query universal forgery on +the EVP_Cipher() path. + +Apply update_iv() at the head of aes_ocb_cipher() to mirror the +streaming handler. The matching GCM one-shot does this already. + +Add a cross-driver round-trip test for AES-{GCM,CCM,OCB} and +ChaCha20-Poly1305 in test/evp_extra_test.c. Each cipher is +exercised with and without AAD; the no-AAD case is needed because +any prior EVP_CipherUpdate(NULL, aad, ...) routes through the +streaming handler and applies the IV itself, masking the bug. + +Fixes CVE-2026-45445 +--- + providers/implementations/ciphers/cipher_aes_ocb.c | 13 + + test/evp_extra_test.c | 265 +++++++++++++++++++++ + 2 files changed, 278 insertions(+) + +diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c +index 8a8eddf36eec..70c0703ddb7c 100644 +--- a/providers/implementations/ciphers/cipher_aes_ocb.c ++++ b/providers/implementations/ciphers/cipher_aes_ocb.c +@@ -516,6 +516,19 @@ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl, + return 0; + } + ++ /* ++ * Mirror the streaming handler: refuse if the key has not been set, ++ * and push the buffered IV into the OCB context before any data is ++ * processed. Without this, CRYPTO_ocb128_encrypt/decrypt runs with ++ * Offset_0 = 0 regardless of the caller's IV -- catastrophic ++ * (key, nonce) reuse, and a subsequent EVP_*Final_ex() emits a tag ++ * that is a function of (key, iv) only. ++ */ ++ if (!ctx->key_set || !update_iv(ctx)) { ++ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); ++ return 0; ++ } ++ + if (!aes_generic_ocb_cipher(ctx, in, out, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; +diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c +index 1b0f0711cb57..b0343c44810d 100644 +--- a/test/evp_extra_test.c ++++ b/test/evp_extra_test.c +@@ -5472,6 +5472,269 @@ static int test_evp_cipher_negative_length(void) + return ret; + } + ++/* ++ * Cross-driver round-trip test for AEAD one-shot vs streaming paths. ++ * ++ * The streaming path (EVP_CipherUpdate/Final, dispatched to ++ * OSSL_FUNC_CIPHER_UPDATE/_FINAL) is treated as the oracle. For each ++ * AEAD configuration we encrypt and decrypt the same (key, iv, aad, pt), ++ * driving the body in two combinations: ++ * ++ * 1. body encrypt via EVP_Cipher() (one-shot, OSSL_FUNC_CIPHER_CIPHER), ++ * body decrypt via EVP_CipherUpdate (streaming). ++ * 2. body encrypt via EVP_CipherUpdate, body decrypt via EVP_Cipher(). ++ * ++ * Both combinations must recover the plaintext and verify the tag. AAD ++ * is always fed via EVP_CipherUpdate(NULL, ...): OCB's one-shot is body ++ * only and the asymmetric "AAD streaming, body one-shot" call shape is ++ * the natural pattern a caller reaching for EVP_Cipher() for throughput ++ * would write anyway. ++ * ++ * CVE-2026-45445 (AES-OCB EVP_Cipher() ignored IV) was a silent failure ++ * in this matrix: the one-shot encrypt path produced ciphertext under ++ * Offset_0 = 0 regardless of IV, which the streaming decrypt path then ++ * could not verify. Adding this cross-check catches the same class of ++ * bug for any future AEAD whose one-shot dispatch diverges from its ++ * streaming dispatch. ++ */ ++typedef struct { ++ const char *name; /* EVP_CIPHER fetch name */ ++ size_t keylen; ++ size_t ivlen; ++ size_t taglen; ++ int is_ccm; /* needs length-up-front + tag-before-body dance */ ++} AEAD_ONESHOT_CFG; ++ ++static const AEAD_ONESHOT_CFG aead_oneshot_cfgs[] = { ++ { "AES-128-GCM", 16, 12, 16, 0 }, ++ { "AES-256-GCM", 32, 12, 16, 0 }, ++ { "AES-128-CCM", 16, 12, 16, 1 }, ++ { "AES-256-CCM", 32, 12, 16, 1 }, ++ { "AES-128-OCB", 16, 12, 16, 0 }, ++ { "AES-256-OCB", 32, 12, 16, 0 }, ++ { "ChaCha20-Poly1305", 32, 12, 16, 0 } ++}; ++ ++/* ++ * Drive an encrypt or decrypt operation. AAD always via EVP_CipherUpdate. ++ * Body via EVP_Cipher() when oneshot_body is non-zero, EVP_CipherUpdate ++ * otherwise. On encrypt, fills *out and the caller-provided tag buffer. ++ * On decrypt, reads from in and verifies tag; returns 0 if verification ++ * fails (the test asserts the expected outcome). ++ */ ++static int aead_oneshot_op(const AEAD_ONESHOT_CFG *cfg, int enc, ++ int oneshot_body, const unsigned char *key, ++ const unsigned char *iv, const unsigned char *aad, ++ size_t aad_len, const unsigned char *in, size_t in_len, ++ unsigned char *out, unsigned char *tag, const char **why) ++{ ++ EVP_CIPHER_CTX *ctx = NULL; ++ EVP_CIPHER *cipher = NULL; ++ int outl = 0, tmpl = 0; ++ int ok = 0; ++ int body_rv; ++ ++ *why = NULL; ++ ++ if (!TEST_ptr(cipher = EVP_CIPHER_fetch(testctx, cfg->name, testpropq))) { ++ *why = "CIPHER_FETCH"; ++ goto end; ++ } ++ if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())) { ++ *why = "CTX_NEW"; ++ goto end; ++ } ++ if (!TEST_true(EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, enc))) { ++ *why = "INIT_CIPHER"; ++ goto end; ++ } ++ if (!TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, ++ (int)cfg->ivlen, NULL), ++ 0)) { ++ *why = "SET_IVLEN"; ++ goto end; ++ } ++ if (cfg->is_ccm) { ++ /* Placeholder taglen on encrypt, real tag on decrypt; both before key+iv. */ ++ if (!TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, ++ (int)cfg->taglen, enc ? NULL : tag), ++ 0)) { ++ *why = "CCM_SET_TAG"; ++ goto end; ++ } ++ } ++ if (!TEST_true(EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, enc))) { ++ *why = "INIT_KEY_IV"; ++ goto end; ++ } ++ if (cfg->is_ccm) { ++ if (!TEST_true(EVP_CipherUpdate(ctx, NULL, &outl, NULL, (int)in_len))) { ++ *why = "CCM_LEN_DECL"; ++ goto end; ++ } ++ } ++ if (aad_len > 0 ++ && !TEST_true(EVP_CipherUpdate(ctx, NULL, &outl, aad, (int)aad_len))) { ++ *why = "AAD"; ++ goto end; ++ } ++ if (!enc && !cfg->is_ccm ++ && !TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, ++ (int)cfg->taglen, tag), ++ 0)) { ++ *why = "SET_TAG"; ++ goto end; ++ } ++ ++ if (oneshot_body) { ++ body_rv = EVP_Cipher(ctx, out, in, (unsigned int)in_len); ++ if (cfg->is_ccm && !enc) { ++ /* CCM decrypt: 0 means tag verify failed, < 0 means error. */ ++ if (!TEST_int_gt(body_rv, 0)) { ++ *why = "ONESHOT_DECRYPT"; ++ goto end; ++ } ++ } else { ++ if (!TEST_int_ge(body_rv, 0)) { ++ *why = "ONESHOT_BODY"; ++ goto end; ++ } ++ } ++ outl = (int)in_len; ++ } else { ++ if (!TEST_true(EVP_CipherUpdate(ctx, out, &outl, in, (int)in_len))) { ++ *why = enc ? "STREAM_BODY_ENC" : "STREAM_BODY_DEC"; ++ goto end; ++ } ++ } ++ ++ if (!cfg->is_ccm) { ++ if (!TEST_true(EVP_CipherFinal_ex(ctx, out + outl, &tmpl))) { ++ *why = enc ? "FINAL_ENC" : "FINAL_DEC"; ++ goto end; ++ } ++ } ++ ++ if (enc) { ++ if (!TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, ++ (int)cfg->taglen, tag), ++ 0)) { ++ *why = "GET_TAG"; ++ goto end; ++ } ++ } ++ ok = 1; ++end: ++ EVP_CIPHER_CTX_free(ctx); ++ EVP_CIPHER_free(cipher); ++ return ok; ++} ++ ++/* ++ * For each AEAD row we run two AAD modes, and within each AAD mode two ++ * cross-driver round trips: ++ * ++ * aad_mode 0: no AAD. Critical for catching the OCB-style bug: any ++ * EVP_CipherUpdate(NULL, aad, ...) call before the body ++ * would itself pass through the (correct) streaming ++ * handler and apply the buffered IV, masking the one-shot ++ * handler's failure to do so. With aad_len == 0 we make ++ * EVP_Cipher() the very first cipher operation on the ++ * context, which is the shape the bug requires. ++ * ++ * aad_mode 1: with AAD via streaming. Catches divergence between the ++ * drivers when AAD is in play. ++ * ++ * leg 0: encrypt-oneshot + decrypt-streaming ++ * leg 1: encrypt-streaming + decrypt-oneshot ++ * ++ * The test index encodes (cipher, aad_mode) so a failure points at both. ++ */ ++static int test_aead_oneshot_roundtrip(int idx) ++{ ++ static const unsigned char fixed_key[32] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, ++ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, ++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f ++ }; ++ static const unsigned char fixed_iv[12] = { ++ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab ++ }; ++ static const unsigned char fixed_aad[] = "extra:context"; ++ static const unsigned char fixed_pt[] = "THE QUICK BROWN FOX JUMPS OVER LAZY!!"; ++ const AEAD_ONESHOT_CFG *cfg = &aead_oneshot_cfgs[idx / 2]; ++ int with_aad = idx % 2; ++ size_t aad_len = with_aad ? sizeof(fixed_aad) - 1 : 0; ++ size_t pt_len = sizeof(fixed_pt) - 1; ++ EVP_CIPHER *probe = NULL; ++ unsigned char ct[64], pt[64]; ++ unsigned char tag_oneshot[16], tag_stream[16]; ++ const char *why = NULL; ++ int leg, ok = 0; ++ ++ /* ++ * Probe for the cipher: a build with no-ocb / no-chacha / etc. will ++ * not have it, and we treat that as a pass (nothing to test here). ++ */ ++ ERR_set_mark(); ++ probe = EVP_CIPHER_fetch(testctx, cfg->name, testpropq); ++ ERR_pop_to_mark(); ++ if (probe == NULL) { ++ TEST_info("skipping, '%s' is not available", cfg->name); ++ return 1; ++ } ++ EVP_CIPHER_free(probe); ++ ++ for (leg = 0; leg <= 1; leg++) { ++ int enc_oneshot = (leg == 0); ++ unsigned char *tag = enc_oneshot ? tag_oneshot : tag_stream; ++ ++ memset(ct, 0, sizeof(ct)); ++ memset(pt, 0, sizeof(pt)); ++ memset(tag, 0, cfg->taglen); ++ ++ if (!aead_oneshot_op(cfg, /*enc=*/1, /*oneshot_body=*/enc_oneshot, ++ fixed_key, fixed_iv, fixed_aad, aad_len, ++ fixed_pt, pt_len, ct, tag, &why)) { ++ TEST_error("%s (%s): encrypt leg %d (%s body) failed at %s", ++ cfg->name, with_aad ? "with AAD" : "no AAD", ++ leg, enc_oneshot ? "oneshot" : "stream", ++ why ? why : "?"); ++ goto end; ++ } ++ if (!aead_oneshot_op(cfg, /*enc=*/0, /*oneshot_body=*/!enc_oneshot, ++ fixed_key, fixed_iv, fixed_aad, aad_len, ++ ct, pt_len, pt, tag, &why)) { ++ TEST_error("%s (%s): decrypt leg %d (%s body) failed at %s", ++ cfg->name, with_aad ? "with AAD" : "no AAD", ++ leg, enc_oneshot ? "stream" : "oneshot", ++ why ? why : "?"); ++ goto end; ++ } ++ if (!TEST_mem_eq(pt, pt_len, fixed_pt, pt_len)) { ++ TEST_error("%s (%s): leg %d: recovered plaintext differs", ++ cfg->name, with_aad ? "with AAD" : "no AAD", leg); ++ goto end; ++ } ++ } ++ ++ /* ++ * Both legs share the same (key, iv, aad, pt) and must therefore ++ * agree on the tag bit-for-bit, regardless of which driver computed ++ * it. This catches the OCB-style failure where the one-shot path ++ * silently emits a different ciphertext/tag from the streaming path. ++ */ ++ if (!TEST_mem_eq(tag_oneshot, cfg->taglen, tag_stream, cfg->taglen)) { ++ TEST_error("%s (%s): oneshot-encrypt tag != streaming-encrypt tag", ++ cfg->name, with_aad ? "with AAD" : "no AAD"); ++ goto end; ++ } ++ ok = 1; ++end: ++ return ok; ++} ++ + int setup_tests(void) + { + OPTION_CHOICE o; +@@ -5637,6 +5900,8 @@ int setup_tests(void) + ADD_TEST(test_aes_rc4_keylen_change_cve_2023_5363); + #endif + ++ ADD_ALL_TESTS(test_aead_oneshot_roundtrip, 2 * OSSL_NELEM(aead_oneshot_cfgs)); ++ + ADD_TEST(test_invalid_ctx_for_digest); + + ADD_TEST(test_evp_cipher_negative_length); diff -Nru openssl-3.0.20/debian/patches/Avoid-length-truncation-in-ASN1_STRING_set.patch openssl-3.0.20/debian/patches/Avoid-length-truncation-in-ASN1_STRING_set.patch --- openssl-3.0.20/debian/patches/Avoid-length-truncation-in-ASN1_STRING_set.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Avoid-length-truncation-in-ASN1_STRING_set.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,88 @@ +From: Viktor Dukhovni +Date: Fri, 15 May 2026 04:19:32 +1000 +Subject: Avoid length truncation in ASN1_STRING_set + +The ASN1_STRING_set() function takes an `int` length, make sure the +argument is not inadvertently truncated when it is called from +asn1_ex_c2i(). + +Fixes CVE-2026-34180 +--- + crypto/asn1/tasn_dec.c | 24 +++++++++++++++++------- + 1 file changed, 17 insertions(+), 7 deletions(-) + +diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c +index 9138ad381f7d..4cbc6275cc35 100644 +--- a/crypto/asn1/tasn_dec.c ++++ b/crypto/asn1/tasn_dec.c +@@ -54,7 +54,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, + const ASN1_ITEM *it, + int tag, int aclass, char opt, + ASN1_TLC *ctx); +-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, ++static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len, + int utype, char *free_cont, const ASN1_ITEM *it); + + /* Table to convert tags to bit values, used for MSTRING type */ +@@ -855,19 +855,24 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, + + /* Translate ASN1 content octets into a structure */ + +-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, ++static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len, + int utype, char *free_cont, const ASN1_ITEM *it) + { + ASN1_VALUE **opval = NULL; + ASN1_STRING *stmp; + ASN1_TYPE *typ = NULL; + int ret = 0; ++ int ilen = (int)len; + const ASN1_PRIMITIVE_FUNCS *pf; + ASN1_INTEGER **tint; + pf = it->funcs; + +- if (pf && pf->prim_c2i) +- return pf->prim_c2i(pval, cont, len, utype, free_cont, it); ++ if (pf && pf->prim_c2i) { ++ if (len == (long)ilen) ++ return pf->prim_c2i(pval, cont, ilen, utype, free_cont, it); ++ ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG); ++ return 0; ++ } + /* If ANY type clear type and set pointer to internal value */ + if (it->utype == V_ASN1_ANY) { + if (*pval == NULL) { +@@ -885,7 +890,8 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, + } + switch (utype) { + case V_ASN1_OBJECT: +- if (!ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len)) ++ if (len != (long)ilen ++ || !ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, ilen)) + goto err; + break; + +@@ -940,6 +946,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, + case V_ASN1_SET: + case V_ASN1_SEQUENCE: + default: ++ if (len != (long)ilen) { ++ ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG); ++ goto err; ++ } + if (utype == V_ASN1_BMPSTRING && (len & 1)) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH); + goto err; +@@ -964,10 +974,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, + if (*free_cont) { + OPENSSL_free(stmp->data); + stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */ +- stmp->length = len; ++ stmp->length = ilen; + *free_cont = 0; + } else { +- if (!ASN1_STRING_set(stmp, cont, len)) { ++ if (!ASN1_STRING_set(stmp, cont, ilen)) { + ERR_raise(ERR_LIB_ASN1, ERR_R_MALLOC_FAILURE); + ASN1_STRING_free(stmp); + *pval = NULL; diff -Nru openssl-3.0.20/debian/patches/CMS-Produce-error-when-AEAD-algorithms-are-used-in-envelo.patch openssl-3.0.20/debian/patches/CMS-Produce-error-when-AEAD-algorithms-are-used-in-envelo.patch --- openssl-3.0.20/debian/patches/CMS-Produce-error-when-AEAD-algorithms-are-used-in-envelo.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/CMS-Produce-error-when-AEAD-algorithms-are-used-in-envelo.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,237 @@ +From: Jakub Zelenka +Date: Thu, 22 May 2025 18:40:30 +0200 +Subject: CMS: Produce error when AEAD algorithms are used in enveloped data + +Fixes GH-21414 + +Reviewed-by: Matt Caswell +Reviewed-by: Tomas Mraz +(Merged from https://github.com/openssl/openssl/pull/27772) +--- + crypto/cms/cms_enc.c | 8 ++++++-- + crypto/cms/cms_env.c | 6 +++--- + crypto/cms/cms_err.c | 4 +++- + crypto/cms/cms_local.h | 2 +- + crypto/err/openssl.txt | 3 ++- + include/crypto/cmserr.h | 2 +- + include/openssl/cmserr.h | 4 ++-- + test/cms-msg/enveloped-content-type-for-aes-gcm.pem | 7 +++++++ + test/cmsapitest.c | 6 ++++++ + test/recipes/80-test_cms.t | 12 +++++++++++- + 10 files changed, 42 insertions(+), 12 deletions(-) + create mode 100644 test/cms-msg/enveloped-content-type-for-aes-gcm.pem + +diff --git a/crypto/cms/cms_enc.c b/crypto/cms/cms_enc.c +index 8c1a15aeda71..a68bc44e5e27 100644 +--- a/crypto/cms/cms_enc.c ++++ b/crypto/cms/cms_enc.c +@@ -23,7 +23,7 @@ + /* Return BIO based on EncryptedContentInfo and key */ + + BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, +- const CMS_CTX *cms_ctx) ++ const CMS_CTX *cms_ctx, int auth) + { + BIO *b; + EVP_CIPHER_CTX *ctx; +@@ -104,6 +104,10 @@ BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, + goto err; + } + if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) { ++ if (!auth) { ++ ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA); ++ goto err; ++ } + piv = aparams.iv; + if (ec->taglen > 0 + && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, +@@ -263,5 +267,5 @@ BIO *ossl_cms_EncryptedData_init_bio(const CMS_ContentInfo *cms) + if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs) + enc->version = 2; + return ossl_cms_EncryptedContent_init_bio(enc->encryptedContentInfo, +- ossl_cms_get0_cmsctx(cms)); ++ ossl_cms_get0_cmsctx(cms), 0); + } +diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c +index 2326253b6743..d2eace4fef5d 100644 +--- a/crypto/cms/cms_env.c ++++ b/crypto/cms/cms_env.c +@@ -1099,7 +1099,7 @@ static BIO *cms_EnvelopedData_Decryption_init_bio(CMS_ContentInfo *cms) + { + CMS_EncryptedContentInfo *ec = cms->d.envelopedData->encryptedContentInfo; + BIO *contentBio = ossl_cms_EncryptedContent_init_bio(ec, +- ossl_cms_get0_cmsctx(cms)); ++ ossl_cms_get0_cmsctx(cms), 0); + EVP_CIPHER_CTX *ctx = NULL; + + if (contentBio == NULL) +@@ -1137,7 +1137,7 @@ static BIO *cms_EnvelopedData_Encryption_init_bio(CMS_ContentInfo *cms) + /* Get BIO first to set up key */ + + ec = env->encryptedContentInfo; +- ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms)); ++ ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms), 0); + + /* If error end of processing */ + if (!ret) +@@ -1189,7 +1189,7 @@ BIO *ossl_cms_AuthEnvelopedData_init_bio(CMS_ContentInfo *cms) + ec->tag = aenv->mac->data; + ec->taglen = aenv->mac->length; + } +- ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms)); ++ ret = ossl_cms_EncryptedContent_init_bio(ec, ossl_cms_get0_cmsctx(cms), 1); + + /* If error or no cipher end of processing */ + if (ret == NULL || ec->cipher == NULL) +diff --git a/crypto/cms/cms_err.c b/crypto/cms/cms_err.c +index 37e52963e16d..bc922d0ee03d 100644 +--- a/crypto/cms/cms_err.c ++++ b/crypto/cms/cms_err.c +@@ -1,6 +1,6 @@ + /* + * Generated by util/mkerr.pl DO NOT EDIT +- * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. ++ * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy +@@ -25,6 +25,8 @@ static const ERR_STRING_DATA CMS_str_reasons[] = { + "certificate has no keyid" }, + { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CERTIFICATE_VERIFY_ERROR), + "certificate verify error" }, ++ { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA), ++ "cipher aead in enveloped data" }, + { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CIPHER_AEAD_SET_TAG_ERROR), + "cipher aead set tag error" }, + { ERR_PACK(ERR_LIB_CMS, 0, CMS_R_CIPHER_GET_TAG), "cipher get tag" }, +diff --git a/crypto/cms/cms_local.h b/crypto/cms/cms_local.h +index a92a67fa8b24..d80689f64d68 100644 +--- a/crypto/cms/cms_local.h ++++ b/crypto/cms/cms_local.h +@@ -429,7 +429,7 @@ int ossl_cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert); + int ossl_cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert); + + BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, +- const CMS_CTX *ctx); ++ const CMS_CTX *ctx, int auth); + BIO *ossl_cms_EncryptedData_init_bio(const CMS_ContentInfo *cms); + int ossl_cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec, + const EVP_CIPHER *cipher, +diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt +index 756fafdfa24a..dc4e3f310f08 100644 +--- a/crypto/err/openssl.txt ++++ b/crypto/err/openssl.txt +@@ -1,4 +1,4 @@ +-# Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. ++# Copyright 1999-2026 The OpenSSL Project Authors. All Rights Reserved. + # + # Licensed under the Apache License 2.0 (the "License"). You may not use + # this file except in compliance with the License. You can obtain a copy +@@ -284,6 +284,7 @@ CMS_R_ATTRIBUTE_ERROR:161:attribute error + CMS_R_CERTIFICATE_ALREADY_PRESENT:175:certificate already present + CMS_R_CERTIFICATE_HAS_NO_KEYID:160:certificate has no keyid + CMS_R_CERTIFICATE_VERIFY_ERROR:100:certificate verify error ++CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA:182:cipher aead in enveloped data + CMS_R_CIPHER_AEAD_SET_TAG_ERROR:184:cipher aead set tag error + CMS_R_CIPHER_GET_TAG:185:cipher get tag + CMS_R_CIPHER_INITIALISATION_ERROR:101:cipher initialisation error +diff --git a/include/crypto/cmserr.h b/include/crypto/cmserr.h +index f9fd933682e5..8b896822d091 100644 +--- a/include/crypto/cmserr.h ++++ b/include/crypto/cmserr.h +@@ -1,6 +1,6 @@ + /* + * Generated by util/mkerr.pl DO NOT EDIT +- * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved. ++ * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy +diff --git a/include/openssl/cmserr.h b/include/openssl/cmserr.h +index c584b90574e2..6c0baf2362fc 100644 +--- a/include/openssl/cmserr.h ++++ b/include/openssl/cmserr.h +@@ -1,6 +1,6 @@ + /* + * Generated by util/mkerr.pl DO NOT EDIT +- * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved. ++ * Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy +@@ -17,7 +17,6 @@ + #include + + #ifndef OPENSSL_NO_CMS +- + /* + * CMS reason codes. + */ +@@ -26,6 +25,7 @@ + #define CMS_R_CERTIFICATE_ALREADY_PRESENT 175 + #define CMS_R_CERTIFICATE_HAS_NO_KEYID 160 + #define CMS_R_CERTIFICATE_VERIFY_ERROR 100 ++#define CMS_R_CIPHER_AEAD_IN_ENVELOPED_DATA 182 + #define CMS_R_CIPHER_AEAD_SET_TAG_ERROR 184 + #define CMS_R_CIPHER_GET_TAG 185 + #define CMS_R_CIPHER_INITIALISATION_ERROR 101 +diff --git a/test/cms-msg/enveloped-content-type-for-aes-gcm.pem b/test/cms-msg/enveloped-content-type-for-aes-gcm.pem +new file mode 100644 +index 000000000000..b0610a7ec8a2 +--- /dev/null ++++ b/test/cms-msg/enveloped-content-type-for-aes-gcm.pem +@@ -0,0 +1,7 @@ ++-----BEGIN PKCS7----- ++MIAGCSqGSIb3DQEHA6CAMIACAQIxNqI0AgEEMAgEBkMwRkVFMDALBglghkgBZQME ++AQUEGPN0q9rM3neSiY7HIADpnqWym33mRZC4JDCABgkqhkiG9w0BBwEwHgYJYIZI ++AWUDBAEGMBEEDIExQGiHZFSYa0ZBqQIBEKCABGNap+JL1B21Mq7ojKPzVuxtRkg3 ++LWt8khnK1EzfmV7e64l5KnTdjq9+gfbwOfbuhTavfBI7VK/ZtpH3HII4fCOe37kV ++mju8/YnYeRq2KcxESmJBySV/veMwxqmHGAw71JyHpg4AAAAAAAAAAAAA ++-----END PKCS7----- +diff --git a/test/cmsapitest.c b/test/cmsapitest.c +index d5b779007fc6..0a7e536bbe75 100644 +--- a/test/cmsapitest.c ++++ b/test/cmsapitest.c +@@ -46,6 +46,12 @@ static int test_encrypt_decrypt(const EVP_CIPHER *cipher) + CMS_TEXT))) + goto end; + ++ if (!(EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) ++ && !TEST_ptr(contentbio = CMS_EnvelopedData_decrypt(content->d.envelopedData, ++ NULL, privkey, cert, NULL, ++ CMS_TEXT, NULL, NULL))) ++ goto end; ++ + /* Check we got the message we first started with */ + if (!TEST_int_eq(BIO_gets(outmsgbio, buf, sizeof(buf)), strlen(msg)) + || !TEST_int_eq(strcmp(buf, msg), 0)) +diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t +index b6ee61464409..f573651e26bc 100644 +--- a/test/recipes/80-test_cms.t ++++ b/test/recipes/80-test_cms.t +@@ -51,7 +51,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib) + + $no_rc2 = 1 if disabled("legacy"); + +-plan tests => 23; ++plan tests => 24; + + ok(run(test(["pkcs7_test"])), "test pkcs7"); + +@@ -1054,6 +1054,16 @@ ok(!run(app(['openssl', 'cms', '-verify', + ])), + "issue#19643"); + ++# Check that users get error when using incorrect envelope type for AEAD algorithms ++ok(!run(app(['openssl', 'cms', '-decrypt', ++ '-inform', 'PEM', '-stream', ++ '-secretkey', '000102030405060708090A0B0C0D0E0F', ++ '-secretkeyid', 'C0FEE0', ++ '-in', srctop_file("test/cms-msg", ++ "enveloped-content-type-for-aes-gcm.pem") ++ ])), ++ "Error AES-GCM in enveloped content type"); ++ + # Check that kari encryption with originator does not segfault + with({ exit_checker => sub { return shift == 3; } }, + sub { diff -Nru openssl-3.0.20/debian/patches/Fix-handling-of-empty-ciphertext-messages-in-AES-SIV.patch openssl-3.0.20/debian/patches/Fix-handling-of-empty-ciphertext-messages-in-AES-SIV.patch --- openssl-3.0.20/debian/patches/Fix-handling-of-empty-ciphertext-messages-in-AES-SIV.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Fix-handling-of-empty-ciphertext-messages-in-AES-SIV.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,112 @@ +From: Dmitry Belyavskiy +Date: Wed, 13 May 2026 14:03:57 +0200 +Subject: Fix handling of empty-ciphertext messages in AES-SIV + +AES-SIV: EVP_DecryptUpdate_ex Accepts All-Zero Tag for Empty-Ciphertext +Messages on context reuse. + +Fixes CVE-2026-45446 +--- + providers/implementations/ciphers/cipher_aes_siv.c | 3 ++ + test/evp_extra_test.c | 60 ++++++++++++++++++++++ + 2 files changed, 63 insertions(+) + +diff --git a/providers/implementations/ciphers/cipher_aes_siv.c b/providers/implementations/ciphers/cipher_aes_siv.c +index 510c1581b593..c3facacfbcf4 100644 +--- a/providers/implementations/ciphers/cipher_aes_siv.c ++++ b/providers/implementations/ciphers/cipher_aes_siv.c +@@ -203,6 +203,7 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]) + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + const OSSL_PARAM *p; + unsigned int speed = 0; ++ SIV128_CONTEXT *sctx = &ctx->siv; + + if (params == NULL) + return 1; +@@ -237,6 +238,8 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]) + if (keylen != ctx->keylen) + return 0; + } ++ sctx->final_ret = -1; ++ + return 1; + } + +diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c +index b0343c44810d..12b61e4a3b3d 100644 +--- a/test/evp_extra_test.c ++++ b/test/evp_extra_test.c +@@ -5414,6 +5414,64 @@ static int test_aes_rc4_keylen_change_cve_2023_5363(void) + } + #endif + ++/* ++ * AES-SIV reuse-without-rekey: ++ * msg1: legit non-empty CT, tag verifies, final_ret=0 ++ * msg2: no reinit (or reinit with key=NULL), set forged tag, ++ * AAD only, DecryptFinal -> does stale final_ret leak through? ++ */ ++static int test_aes_siv_ctx_reuse(void) ++{ ++ unsigned char key[32] = { 7 }; /* AES-128-SIV => 2*16 */ ++ unsigned char pt[9] = "payload!"; ++ unsigned char ct[9], tagbuf[16], out[16], zero16[16] = { 0 }; ++ unsigned char aad[14] = "forged header"; ++ int outl, ret = 0; ++ EVP_CIPHER_CTX *e = NULL, *d = NULL; ++ EVP_CIPHER *c = EVP_CIPHER_fetch(NULL, "AES-128-SIV", NULL); ++ ++ if (c == NULL) { ++ return TEST_skip("AES-128-SIV cipher is not available"); ++ } ++ ++ /* produce a valid (ct,tag) for msg1 */ ++ e = EVP_CIPHER_CTX_new(); ++ if (!TEST_ptr(e) ++ || !TEST_true(EVP_EncryptInit_ex2(e, c, key, NULL, NULL)) ++ || !TEST_true(EVP_EncryptUpdate(e, NULL, &outl, (unsigned char *)"hdr1", 4)) ++ || !TEST_true(EVP_EncryptUpdate(e, ct, &outl, pt, sizeof(pt))) ++ || !TEST_true(EVP_EncryptFinal_ex(e, out, &outl)) ++ || !TEST_true(EVP_CIPHER_CTX_ctrl(e, EVP_CTRL_AEAD_GET_TAG, 16, tagbuf))) { ++ EVP_CIPHER_CTX_free(e); ++ goto err; ++ } ++ EVP_CIPHER_CTX_free(e); ++ ++ /* msg1 decrypt */ ++ d = EVP_CIPHER_CTX_new(); ++ if (!TEST_ptr(d) ++ || !TEST_true(EVP_DecryptInit_ex2(d, c, key, NULL, NULL)) ++ || !TEST_true(EVP_CIPHER_CTX_ctrl(d, EVP_CTRL_AEAD_SET_TAG, 16, tagbuf)) ++ || !TEST_true(EVP_DecryptUpdate(d, NULL, &outl, (unsigned char *)"hdr1", 4)) ++ || !TEST_true(EVP_DecryptUpdate(d, out, &outl, ct, sizeof(ct))) ++ || !TEST_true(EVP_DecryptFinal_ex(d, out, &outl))) ++ goto err; ++ ++ /* msg2 on SAME ctx, reinit with key=NULL => initkey skipped, final_ret should be reset */ ++ if (!TEST_true(EVP_DecryptInit_ex2(d, NULL, NULL, NULL, NULL)) ++ || !TEST_true(EVP_CIPHER_CTX_ctrl(d, EVP_CTRL_AEAD_SET_TAG, 16, zero16)) ++ || !TEST_true(EVP_DecryptUpdate(d, NULL, &outl, aad, sizeof(aad))) /* forged AAD */ ++ || !TEST_false(EVP_DecryptFinal_ex(d, out, &outl))) ++ goto err; ++ ++ ret = 1; ++ ++err: ++ EVP_CIPHER_CTX_free(d); ++ EVP_CIPHER_free(c); ++ return ret; ++} ++ + static int test_invalid_ctx_for_digest(void) + { + int ret; +@@ -5901,6 +5959,8 @@ int setup_tests(void) + #endif + + ADD_ALL_TESTS(test_aead_oneshot_roundtrip, 2 * OSSL_NELEM(aead_oneshot_cfgs)); ++ /* Test case for CVE-2026-45446 */ ++ ADD_TEST(test_aes_siv_ctx_reuse); + + ADD_TEST(test_invalid_ctx_for_digest); + diff -Nru openssl-3.0.20/debian/patches/Fix-possible-use-after-free-in-OpenSSL-PKCS7_verify.patch openssl-3.0.20/debian/patches/Fix-possible-use-after-free-in-OpenSSL-PKCS7_verify.patch --- openssl-3.0.20/debian/patches/Fix-possible-use-after-free-in-OpenSSL-PKCS7_verify.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Fix-possible-use-after-free-in-OpenSSL-PKCS7_verify.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,37 @@ +From: Igor Ustinov +Date: Sat, 16 May 2026 08:16:23 +0200 +Subject: Fix possible use-after-free in OpenSSL PKCS7_verify() + +Fixes CVE-2026-45447 +--- + crypto/pkcs7/pk7_smime.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c +index 001b96d31183..fadf543e9db7 100644 +--- a/crypto/pkcs7/pk7_smime.c ++++ b/crypto/pkcs7/pk7_smime.c +@@ -218,6 +218,7 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, + int i, j = 0, k, ret = 0; + BIO *p7bio = NULL; + BIO *tmpin = NULL, *tmpout = NULL; ++ BIO *next = NULL; + const PKCS7_CTX *p7_ctx; + + if (p7 == NULL) { +@@ -366,11 +367,11 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, + BIO_free(tmpout); + X509_STORE_CTX_free(cert_ctx); + OPENSSL_free(buf); +- if (tmpin == indata) { +- if (indata) +- BIO_pop(p7bio); ++ while (p7bio != NULL && p7bio != indata) { ++ next = BIO_pop(p7bio); ++ BIO_free(p7bio); ++ p7bio = next; + } +- BIO_free_all(p7bio); + sk_X509_free(signers); + return ret; + } diff -Nru openssl-3.0.20/debian/patches/Fix-potential-NULL-dereference-processing-CMS-PasswordRec.patch openssl-3.0.20/debian/patches/Fix-potential-NULL-dereference-processing-CMS-PasswordRec.patch --- openssl-3.0.20/debian/patches/Fix-potential-NULL-dereference-processing-CMS-PasswordRec.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Fix-potential-NULL-dereference-processing-CMS-PasswordRec.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,28 @@ +From: Igor Ustinov +Date: Thu, 21 May 2026 08:36:54 +0200 +Subject: Fix potential NULL dereference processing CMS PasswordRecipientInfo + +Avoid NULL dereferencing when keyDerivationAlgorithm is absent +in CMS PasswordRecipientInfo. + +Fixes CVE-2026-42766 +--- + crypto/cms/cms_pwri.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c +index 2561c5b99745..8b0ea233fd6d 100644 +--- a/crypto/cms/cms_pwri.c ++++ b/crypto/cms/cms_pwri.c +@@ -354,6 +354,11 @@ int ossl_cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms, + + /* Finish password based key derivation to setup key in "ctx" */ + ++ if (algtmp == NULL) { ++ ERR_raise_data(ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER, ++ "Missing KeyDerivationAlgorithm"); ++ goto err; ++ } + if (!EVP_PBE_CipherInit_ex(algtmp->algorithm, + (char *)pwri->pass, (int)pwri->passlen, + algtmp->parameter, kekctx, en_de, diff -Nru openssl-3.0.20/debian/patches/Match-the-local-q-DHX-parameter-against-the-peer-s-q.patch openssl-3.0.20/debian/patches/Match-the-local-q-DHX-parameter-against-the-peer-s-q.patch --- openssl-3.0.20/debian/patches/Match-the-local-q-DHX-parameter-against-the-peer-s-q.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Match-the-local-q-DHX-parameter-against-the-peer-s-q.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,36 @@ +From: Norbert Pocs +Date: Tue, 12 May 2026 15:16:04 +0200 +Subject: Match the local q DHX parameter against the peer's q + +As FFC/DH peer public key validation uses the peer's q value instead +of checking against the local q, we must also check that these +q values match when setting the peer's public key. + +Fixes CVE-2026-42770 + +Signed-off-by: Norbert Pocs +--- + providers/implementations/exchange/dh_exch.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/providers/implementations/exchange/dh_exch.c b/providers/implementations/exchange/dh_exch.c +index bb2d355c8143..668602a5e523 100644 +--- a/providers/implementations/exchange/dh_exch.c ++++ b/providers/implementations/exchange/dh_exch.c +@@ -113,12 +113,15 @@ static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[]) + static int dh_match_params(DH *priv, DH *peer) + { + int ret; ++ int ignore_q = 1; + FFC_PARAMS *dhparams_priv = ossl_dh_get0_params(priv); + FFC_PARAMS *dhparams_peer = ossl_dh_get0_params(peer); + ++ if (dhparams_priv != NULL && dhparams_priv->q != NULL) ++ ignore_q = 0; + ret = dhparams_priv != NULL + && dhparams_peer != NULL +- && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, 1); ++ && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, ignore_q); + if (!ret) + ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS); + return ret; diff -Nru openssl-3.0.20/debian/patches/Reject-oversized-inputs-in-ASN1_mbstring_ncopy.patch openssl-3.0.20/debian/patches/Reject-oversized-inputs-in-ASN1_mbstring_ncopy.patch --- openssl-3.0.20/debian/patches/Reject-oversized-inputs-in-ASN1_mbstring_ncopy.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Reject-oversized-inputs-in-ASN1_mbstring_ncopy.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,103 @@ +From: Viktor Dukhovni +Date: Wed, 29 Apr 2026 18:23:24 +1000 +Subject: Reject oversized inputs in ASN1_mbstring_ncopy() + +In ASN1_mbstring_ncopy() the destination size for BMPSTRING and +UNIVERSALSTRING output was computed by a signed left shift on an +int: + + outlen = nchar << 1; /* MBSTRING_BMP */ + outlen = nchar << 2; /* MBSTRING_UNIV */ + +For nchar large enough the result is not representable in int. In +the worst case (nchar == 0x40000000) nchar << 2 wraps to zero, +OPENSSL_malloc(1) is called, and traverse_string() then writes +4*nchar bytes into the one-byte allocation: a heap buffer +overflow. The MBSTRING_UTF8 path computes outlen by summing +per-character byte counts in out_utf8(), and that sum can overflow +the same int under similarly large inputs. + +Neither path is reachable from code that processes X.509 +certificates through the DIRSTRING_TYPE mask used by +ASN1_STRING_set_by_NID(): UNIVERSALSTRING is absent from that +mask, and the UTF-8 sum requires inputs on the order of half a +gigabyte. Reaching them needs an application that calls +ASN1_mbstring_copy()/ASN1_mbstring_ncopy() directly, or registers +a custom NID via ASN1_STRING_TABLE_add(), with an oversized +attacker-controlled input. + +Add range checks before each shift and in out_utf8(), raising +ASN1_R_STRING_TOO_LONG at the point of detection. Move the +existing ASN1_R_INVALID_UTF8STRING raise into out_utf8() too so +the two failure modes report distinct codes; the MBSTRING_UTF8 +caller is left with cleanup only and now frees dest on error, +matching the BMP/UNIV branches. + +Fixes CVE-2026-7383 +--- + crypto/asn1/a_mbstr.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c +index 531e01d33257..9329e5795a9e 100644 +--- a/crypto/asn1/a_mbstr.c ++++ b/crypto/asn1/a_mbstr.c +@@ -174,11 +174,27 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, + break; + + case MBSTRING_BMP: ++ if (nchar > INT_MAX / 2) { ++ ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); ++ if (free_out) { ++ ASN1_STRING_free(dest); ++ *out = NULL; ++ } ++ return -1; ++ } + outlen = nchar << 1; + cpyfunc = cpy_bmp; + break; + + case MBSTRING_UNIV: ++ if (nchar > INT_MAX / 4) { ++ ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); ++ if (free_out) { ++ ASN1_STRING_free(dest); ++ *out = NULL; ++ } ++ return -1; ++ } + outlen = nchar << 2; + cpyfunc = cpy_univ; + break; +@@ -186,8 +202,11 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, + case MBSTRING_UTF8: + outlen = 0; + ret = traverse_string(in, len, inform, out_utf8, &outlen); +- if (ret < 0) { +- ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING); ++ if (ret < 0) { /* error already raised in out_utf8() */ ++ if (free_out) { ++ ASN1_STRING_free(dest); ++ *out = NULL; ++ } + return -1; + } + cpyfunc = cpy_utf8; +@@ -271,9 +290,15 @@ static int out_utf8(unsigned long value, void *arg) + int *outlen, len; + + len = UTF8_putc(NULL, -1, value); +- if (len <= 0) ++ if (len <= 0) { ++ ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING); + return len; ++ } + outlen = arg; ++ if (*outlen > INT_MAX - len) { ++ ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG); ++ return -1; ++ } + *outlen += len; + return 1; + } diff -Nru openssl-3.0.20/debian/patches/Reject-potentially-forged-encrypted-CMS-AuthEnvelopedData.patch openssl-3.0.20/debian/patches/Reject-potentially-forged-encrypted-CMS-AuthEnvelopedData.patch --- openssl-3.0.20/debian/patches/Reject-potentially-forged-encrypted-CMS-AuthEnvelopedData.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Reject-potentially-forged-encrypted-CMS-AuthEnvelopedData.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,53 @@ +From: Neil Horman +Date: Fri, 17 Apr 2026 13:21:50 -0400 +Subject: Reject potentially forged encrypted CMS AuthEnvelopedData messages + +1. Adjust ossl_cms_EncryptedContent_init_bio to not accept non-AEAD +ciphers. + +If a forged CMS message with AuthEnvelopedData is received with +a non-AEAD cipher specified, we silently accept that and decrypt +the message, skipping any authentication, which violates RFC 5083. + +We also add checks to ensure we fail if we try to encrypt +AuthEnvelopedData without using an AEAD cipher. + +2. Ensure that tag lengths on cms AEAD data is the recommended size. + +RFC 5084 recommends that mac tags be at least 12 bytes for AES-GCM +and 4 bytes for AES-CCM on AuthEnvelopedData. As this code is not +algorith-specific we add a check for a minimal size and just use the +lower limit which is sufficient to prevent this attack. + +Without this check, its possible to set the tag length to 1 and within +256 guesses, forge a CMS message. + +Fixes CVE-2026-34182 +--- + crypto/cms/cms_enc.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/crypto/cms/cms_enc.c b/crypto/cms/cms_enc.c +index a68bc44e5e27..367617576175 100644 +--- a/crypto/cms/cms_enc.c ++++ b/crypto/cms/cms_enc.c +@@ -109,13 +109,15 @@ BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec, + goto err; + } + piv = aparams.iv; +- if (ec->taglen > 0 +- && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, +- ec->taglen, ec->tag) +- <= 0) { ++ ++ if (ec->taglen < 4 || ec->taglen > 16 ++ || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, (int)ec->taglen, ec->tag) <= 0) { + ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR); + goto err; + } ++ } else if (auth) { ++ ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM); ++ goto err; + } + } + len = EVP_CIPHER_CTX_get_key_length(ctx); diff -Nru openssl-3.0.20/debian/patches/Test-for-CVE-2026-42766.patch openssl-3.0.20/debian/patches/Test-for-CVE-2026-42766.patch --- openssl-3.0.20/debian/patches/Test-for-CVE-2026-42766.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Test-for-CVE-2026-42766.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,193 @@ +From: Igor Ustinov +Date: Wed, 20 May 2026 20:02:43 +0200 +Subject: Test for CVE-2026-42766 + +The script make_missing_kdf_der.py was developed by Mayank Jangid +and Kushal Khemka. + +Co-Authored-by: Mayank Jangid +Co-Authored-by: Kushal Khemka +--- + test/cms-msg/make_missing_kdf_der.py | 137 +++++++++++++++++++++++++++++++++++ + test/recipes/80-test_cms.t | 20 ++++- + 2 files changed, 156 insertions(+), 1 deletion(-) + create mode 100755 test/cms-msg/make_missing_kdf_der.py + +diff --git a/test/cms-msg/make_missing_kdf_der.py b/test/cms-msg/make_missing_kdf_der.py +new file mode 100755 +index 000000000000..5b3fc0f6eeda +--- /dev/null ++++ b/test/cms-msg/make_missing_kdf_der.py +@@ -0,0 +1,137 @@ ++#!/usr/bin/env python3 ++ ++# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. ++# ++# Licensed under the Apache License 2.0 (the "License"). You may not use ++# this file except in compliance with the License. You can obtain a copy ++# in the file LICENSE in the source distribution or at ++# https://www.openssl.org/source/license.html ++ ++# This script generates missing-kdf.der - a password-encrypted CMS message ++# without the keyDerivationAlgorithm field, which is used in the ++# “PWRI missing keyDerivationAlgorithm regression” test. ++# ++# Usage: python3 make_missing_kdf_der.py valid.der missing-kdf.der ++ ++from __future__ import annotations ++ ++import argparse ++import sys ++from dataclasses import dataclass ++from pathlib import Path ++ ++ ++@dataclass ++class Node: ++ off: int ++ tag: int ++ hdr_len: int ++ length: int ++ end: int ++ children: list["Node"] ++ ++ ++def read_len(data: bytes, off: int) -> tuple[int, int]: ++ first = data[off] ++ if first < 0x80: ++ return first, 1 ++ n = first & 0x7F ++ if n == 0 or n > 4: ++ raise ValueError(f"unsupported DER length form at {off}") ++ val = 0 ++ for b in data[off + 1 : off + 1 + n]: ++ val = (val << 8) | b ++ return val, 1 + n ++ ++ ++def parse_node(data: bytes, off: int) -> Node: ++ tag = data[off] ++ length, len_len = read_len(data, off + 1) ++ hdr_len = 1 + len_len ++ end = off + hdr_len + length ++ children: list[Node] = [] ++ if tag & 0x20: ++ cur = off + hdr_len ++ while cur < end: ++ child = parse_node(data, cur) ++ children.append(child) ++ cur = child.end ++ if cur != end: ++ raise ValueError(f"child parse ended at {cur}, expected {end}") ++ return Node(off=off, tag=tag, hdr_len=hdr_len, length=length, end=end, children=children) ++ ++ ++def encode_len(length: int, existing_len_len: int) -> bytes: ++ if existing_len_len == 1: ++ if length >= 0x80: ++ raise ValueError("new length no longer fits in short-form DER") ++ return bytes([length]) ++ payload_len = existing_len_len - 1 ++ max_len = (1 << (payload_len * 8)) - 1 ++ if length > max_len: ++ raise ValueError("new length no longer fits in existing long-form DER") ++ out = bytearray([0x80 | payload_len]) ++ for shift in range((payload_len - 1) * 8, -8, -8): ++ out.append((length >> shift) & 0xFF) ++ return bytes(out) ++ ++ ++def patch_length_field(buf: bytearray, node: Node, delta: int) -> None: ++ new_len = node.length + delta ++ if new_len < 0: ++ raise ValueError("negative patched length") ++ len_bytes = encode_len(new_len, node.hdr_len - 1) ++ start = node.off + 1 ++ end = start + len(node.hdr_len.to_bytes(1, "big")) - 1 # unused, kept for clarity ++ buf[start : start + len(len_bytes)] = len_bytes ++ ++ ++def main() -> int: ++ ap = argparse.ArgumentParser(description="Remove PWRI keyDerivationAlgorithm from a CMS DER blob.") ++ ap.add_argument("input_der") ++ ap.add_argument("output_der") ++ args = ap.parse_args() ++ ++ data = Path(args.input_der).read_bytes() ++ root = parse_node(data, 0) ++ ++ # CMS structure we expect: ++ # SEQUENCE { OID envelopedData, [0] SEQUENCE { version, SET recipientInfos, ... } } ++ ed_wrapper = root.children[1] ++ env_seq = ed_wrapper.children[0] ++ recipient_set = env_seq.children[1] ++ pwri_choice = recipient_set.children[0] # [3] ++ ++ if pwri_choice.tag != 0xA3: ++ raise ValueError(f"expected PWRI choice tag 0xA3, found 0x{pwri_choice.tag:02x}") ++ if len(pwri_choice.children) < 3: ++ raise ValueError("unexpected PWRI child count") ++ ++ version = pwri_choice.children[0] ++ maybe_kdf = pwri_choice.children[1] ++ keyenc = pwri_choice.children[2] ++ if version.tag != 0x02: ++ raise ValueError("PWRI version is not INTEGER") ++ if maybe_kdf.tag != 0xA0: ++ raise ValueError(f"PWRI child after version is not [0] keyDerivationAlgorithm: 0x{maybe_kdf.tag:02x}") ++ if keyenc.tag != 0x30: ++ raise ValueError("PWRI keyEncryptionAlgorithm is not SEQUENCE") ++ ++ remove_start = maybe_kdf.off ++ remove_end = maybe_kdf.end ++ remove_len = remove_end - remove_start ++ ++ out = bytearray(data) ++ del out[remove_start:remove_end] ++ ++ # Adjust ancestors whose length spans the removed field. ++ for node in [root, ed_wrapper, env_seq, recipient_set, pwri_choice]: ++ patch_length_field(out, node, -remove_len) ++ ++ Path(args.output_der).write_bytes(out) ++ print(f"removed {remove_len} bytes at [{remove_start}, {remove_end})") ++ return 0 ++ ++ ++if __name__ == "__main__": ++ sys.exit(main()) +diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t +index f573651e26bc..bef9be1e044d 100644 +--- a/test/recipes/80-test_cms.t ++++ b/test/recipes/80-test_cms.t +@@ -51,7 +51,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib) + + $no_rc2 = 1 if disabled("legacy"); + +-plan tests => 24; ++plan tests => 25; + + ok(run(test(["pkcs7_test"])), "test pkcs7"); + +@@ -1206,3 +1206,21 @@ subtest "encrypt to three recipients with RSA-OAEP, key only decrypt" => sub { + "decrypt with key only"); + is(compare($pt, $ptpt), 0, "compare original message with decrypted ciphertext"); + }; ++ ++# Regression test for NULL dereference in PWRI decrypt path ++# when optional keyDerivationAlgorithm is omitted. ++subtest "PWRI missing keyDerivationAlgorithm regression" => sub { ++ plan tests => 1; ++ ++ with({ exit_checker => sub { return shift == 4; } }, sub { ++ ok(run(app([ ++ "openssl", "cms", @prov, ++ "-decrypt", ++ "-inform", "DER", ++ "-in", ++ srctop_file('test', 'cms-msg', 'missing-kdf.der'), ++ "-out", "pwri-out.txt", ++ "-pwri_password", "secret"])), ++ "missing keyDerivationAlgorithm is rejected"); ++ }); ++}; diff -Nru openssl-3.0.20/debian/patches/Test-for-CVE-2026-45447-UAF-in-PKCS7_verify.patch openssl-3.0.20/debian/patches/Test-for-CVE-2026-45447-UAF-in-PKCS7_verify.patch --- openssl-3.0.20/debian/patches/Test-for-CVE-2026-45447-UAF-in-PKCS7_verify.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/Test-for-CVE-2026-45447-UAF-in-PKCS7_verify.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,100 @@ +From: Igor Ustinov +Date: Sat, 16 May 2026 08:22:53 +0200 +Subject: Test for CVE-2026-45447 (UAF in PKCS7_verify) + +The test data were created with a tool developed by +Thai Duong . +--- + test/recipes/80-test_cms.t | 19 ++++++++++++- + test/smime-eml/pkcs7-empty-digest-set.eml | 45 +++++++++++++++++++++++++++++++ + 2 files changed, 63 insertions(+), 1 deletion(-) + create mode 100644 test/smime-eml/pkcs7-empty-digest-set.eml + +diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t +index bef9be1e044d..2f850c6e0ee8 100644 +--- a/test/recipes/80-test_cms.t ++++ b/test/recipes/80-test_cms.t +@@ -51,7 +51,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib) + + $no_rc2 = 1 if disabled("legacy"); + +-plan tests => 25; ++plan tests => 26; + + ok(run(test(["pkcs7_test"])), "test pkcs7"); + +@@ -1011,6 +1011,23 @@ subtest "CMS binary input tests\n" => sub { + "verify binary input with -binary missing -crlfeol"); + }; + ++# Regression test for PKCS7_verify() ownership handling when ++# digestAlgorithms is an empty SET. ++# The malformed structure must fail cleanly without crashing or ++# triggering use-after-free behaviour. ++with({ exit_checker => sub { return shift == 4; } }, ++ sub { ++ ok(run(app([ ++ 'openssl', 'smime', ++ '-verify', ++ '-noverify', ++ '-in', ++ srctop_file('test', 'smime-eml', ++ 'pkcs7-empty-digest-set.eml'), ++ ])), ++ "Check empty digestAlgorithms SET is handled safely"); ++ }); ++ + # Test case for missing MD algorithm (must not segfault) + + with({ exit_checker => sub { return shift == 4; } }, +diff --git a/test/smime-eml/pkcs7-empty-digest-set.eml b/test/smime-eml/pkcs7-empty-digest-set.eml +new file mode 100644 +index 000000000000..a6db2c38adfa +--- /dev/null ++++ b/test/smime-eml/pkcs7-empty-digest-set.eml +@@ -0,0 +1,45 @@ ++MIME-Version: 1.0 ++Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha-256"; boundary="----E0314CC5D732C92AE2D7A3BACDCDCFCE" ++ ++This is an S/MIME signed message ++ ++------E0314CC5D732C92AE2D7A3BACDCDCFCE ++This is the content to be signed. ++ ++------E0314CC5D732C92AE2D7A3BACDCDCFCE ++Content-Type: application/x-pkcs7-signature; name="smime.p7s" ++Content-Transfer-Encoding: base64 ++Content-Disposition: attachment; filename="smime.p7s" ++ ++MIIFWgYJKoZIhvcNAQcCoIIFSzCCBUcCAQExADALBgkqhkiG9w0BBwGgggLuMIIC ++6jCCAdKgAwIBAgIUL5E46FxyhsT7C3G1NS27OtR7XAowDQYJKoZIhvcNAQELBQAw ++FTETMBEGA1UEAwwKUG9DIFNpZ25lcjAeFw0yNjA1MDgxMDIwNDhaFw0yNzA1MDgx ++MDIwNDhaMBUxEzARBgNVBAMMClBvQyBTaWduZXIwggEiMA0GCSqGSIb3DQEBAQUA ++A4IBDwAwggEKAoIBAQDSSu/gupmIlclvmTMHiqOrCqmB8NRTjAMoI//MPJrnFXYp ++FjDPMk7Y/kCcHztudaIvADkowaFtOm4oMinQFhjwCNCo5K5WrrlAitnpcd5QH2nA ++iVZXjjohQUJEd7n33AGqTwo5EGaCK+alAZL7tA7bdhNi/aZ33L3bUNYqoHbXiNsE ++u1tj8frLfIjduOt0TMPSOrrFjjEsrL3T3tg+HmxpalDHz7E6o9zJu0wlk8bcR2Xk ++mpX8RdYCu7K9m39N1F2WKa9WJh24NQLpWRfwD213jaIFK2EXy/XHePDUeiMYtVOV ++oovCSmY7OqowupA7J+4dcsnRjFqgZECctHhAfk+PAgMBAAGjMjAwMB0GA1UdDgQW ++BBRZlupXNYq4fny0SE76sr/CdQ2DUTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 ++DQEBCwUAA4IBAQANOlttTWVz620JNTrPzhiR4x9+5UiF4GSqv8BRJQFj3Xh7fsUp +++3GDs9M27f4FVh3utJsjt7Sa9ZWLpBVdgjGBwGLAtPsoYMjhnUgZTUvwEk5+aXyv ++zJxn4I7mMbDhlNCMHcVtGdtA+2UOEuvdGfuEilpzPsV8DzM1K3xU5bSWoo0BRFKK ++srHkyEfxCFPAQOcX80ZbMO6zdcXeJjC6mQXGqy2aqeQob0vuSZJ7QHZBlRjY5YHR ++wWlIqG8G3Eist16iTqdX2PQFZT1/QAEQ/LnXARTUUjUroccdci8YNASoeHDpcjRL ++MBrN+QBNZVt5qLhDogwZb2ZwqKfZ8Aqg3oAkMYICPzCCAjsCAQEwLTAVMRMwEQYD ++VQQDDApQb0MgU2lnbmVyAhQvkTjoXHKGxPsLcbU1Lbs61HtcCjANBglghkgBZQME ++AgEFAKCB5DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEP ++Fw0yNjA1MDgxMDIwNDhaMC8GCSqGSIb3DQEJBDEiBCAvyoHfycLqb8UzVPizy1uA ++o3h7tza3HebeiJaSnpIJHzB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjAL ++BglghkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMC ++AgIAgDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDANBgkq ++hkiG9w0BAQEFAASCAQBIpl7U2j4YiU1vdZHyx2dCK41ZahtTVOB4RVJcrmopgans ++fICdkSTfb0dVqc13++bYn4i1b2R2os5YIkoGxdrM5aZB7KF9r1xwgrendTF4/BwP ++gQq2khNtKebv9Yr0kOPynFIsgx5BHk99wrzfwidJUFuJJgQ9W0YOf7EGkbnZvPT+ ++hV0aeLmJAb5jjWhbDciqUjR3O23JQhzVj4U3vo2TeN7VYmNJsX+fA4sZzIbYSei9 ++ps7GZruiRcKgqgUj1l8HjIGMHqd9lccchk/BYyAGxAbgGisntvfJdPZO09wG8rHh ++eS6FYkkXAKBO49WbhE9aVLJH0zgA6gTfyEvOOOS1 ++ ++------E0314CC5D732C92AE2D7A3BACDCDCFCE-- ++ diff -Nru openssl-3.0.20/debian/patches/cms-kek_unwrap_key-Fix-out-of-bounds-read-in-check-byte-v.patch openssl-3.0.20/debian/patches/cms-kek_unwrap_key-Fix-out-of-bounds-read-in-check-byte-v.patch --- openssl-3.0.20/debian/patches/cms-kek_unwrap_key-Fix-out-of-bounds-read-in-check-byte-v.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/cms-kek_unwrap_key-Fix-out-of-bounds-read-in-check-byte-v.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,51 @@ +From: Nikola Pajkovsky +Date: Thu, 21 May 2026 11:53:09 +0200 +Subject: cms: kek_unwrap_key: Fix out-of-bounds read in check-byte validation + +the check-byte test in kek_unwrap_key() reads tmp[1] through tmp[6] +unconditionally, so the decrypted buffer must hold at least seven +octets. The pre-decryption size check enforces inlen >= 2 * blocklen, +which yields the required seven octets only when blocklen >= 4. For +a KEK cipher with a smaller block size, inlen can be as small as +2 * blocklen and the check-byte read overruns the inlen-sized tmp +allocation. + +Reject blocklen < 4 in the early sanity check. All block ciphers +appropriate for CMS PasswordRecipientInfo key wrapping have a block +size of at least 8 octets (DES/3DES = 8, AES = 16), so this only +forbids ciphers that would not be valid KEK choices anyway, and the +existing inlen >= 2 * blocklen check then guarantees the seven-octet +lower bound the check-byte test relies on. + +Fixes CVE-2026-9076 +Signed-off-by: Nikola Pajkovsky +--- + crypto/cms/cms_pwri.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c +index 46313f2bfe2c..2561c5b99745 100644 +--- a/crypto/cms/cms_pwri.c ++++ b/crypto/cms/cms_pwri.c +@@ -189,14 +189,18 @@ static int kek_unwrap_key(unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen, + EVP_CIPHER_CTX *ctx) + { +- size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx); ++ int blocklen = EVP_CIPHER_CTX_get_block_size(ctx); + unsigned char *tmp; + int outl, rv = 0; +- if (inlen < 2 * blocklen) { ++ ++ if (blocklen < 4) ++ return 0; ++ ++ if (inlen < 2 * (size_t)blocklen) { + /* too small */ + return 0; + } +- if (inlen % blocklen) { ++ if (inlen > INT_MAX || inlen % blocklen) { + /* Invalid size */ + return 0; + } diff -Nru openssl-3.0.20/debian/patches/cms-kek_unwrap_key-test-for-fix-out-of-bounds-read-in-che.patch openssl-3.0.20/debian/patches/cms-kek_unwrap_key-test-for-fix-out-of-bounds-read-in-che.patch --- openssl-3.0.20/debian/patches/cms-kek_unwrap_key-test-for-fix-out-of-bounds-read-in-che.patch 1970-01-01 00:00:00.000000000 +0000 +++ openssl-3.0.20/debian/patches/cms-kek_unwrap_key-test-for-fix-out-of-bounds-read-in-che.patch 2026-06-06 19:56:18.000000000 +0000 @@ -0,0 +1,110 @@ +From: Nikola Pajkovsky +Date: Thu, 21 May 2026 14:18:11 +0200 +Subject: cms: kek_unwrap_key: test for fix out-of-bounds read in check-byte + validation + +added EnvelopedData blob with a PasswordRecipientInfo using +id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher. CFB's 1-byte +effective block size let the inlen >= 2 * blocklen guard in +kek_unwrap_key() accept a wrapped key shorter than the seven octets +the check-byte test reads from tmp[1..6]; the encryptedKey OCTET +STRING here is only two bytes. + +Signed-off-by: Nikola Pajkovsky +--- + test/cmsapitest.c | 48 +++++++++++++++++++++++++++++++++++++++++-- + test/recipes/80-test_cmsapi.t | 3 ++- + 2 files changed, 48 insertions(+), 3 deletions(-) + +diff --git a/test/cmsapitest.c b/test/cmsapitest.c +index 7e74c5daf221..d5b779007fc6 100644 +--- a/test/cmsapitest.c ++++ b/test/cmsapitest.c +@@ -20,6 +20,7 @@ static X509 *cert = NULL; + static EVP_PKEY *privkey = NULL; + static char *derin = NULL; + static char *too_long_iv_cms_in = NULL; ++static char *pwri_kek_oob_der_in = NULL; + + static int test_encrypt_decrypt(const EVP_CIPHER *cipher) + { +@@ -484,7 +485,48 @@ static int test_cms_aesgcm_iv_too_long(void) + return ret; + } + +-OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n") ++/* ++ * CMS EnvelopedData with a single PasswordRecipientInfo using ++ * id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher ++ * (1-byte effective block size). The encryptedKey OCTET STRING is ++ * only two bytes long, so the wrapped key buffer is shorter than ++ * the seven octets read by the check-byte test in kek_unwrap_key(). ++ * Prior to CVE-2026-9076 this triggered an out-of-bounds heap read; ++ * CMS_decrypt() must now fail cleanly. ++ */ ++static int test_pwri_kek_unwrap_short_encrypted_key(void) ++{ ++ BIO *in = NULL; ++ CMS_ContentInfo *cms = NULL; ++ unsigned long err = 0; ++ int ret = 0; ++ ++ if (!TEST_ptr(in = BIO_new_file(pwri_kek_oob_der_in, "rb")) ++ || !TEST_ptr(cms = d2i_CMS_bio(in, NULL))) ++ goto end; ++ ++ /* ++ * The unwrap is attempted eagerly inside CMS_decrypt_set1_password(). ++ * It must fail cleanly (no OOB read) and report CMS_R_UNWRAP_FAILURE. ++ */ ++ if (!TEST_false(CMS_decrypt_set1_password(cms, ++ (unsigned char *)"password", -1))) ++ goto end; ++ ++ err = ERR_peek_last_error(); ++ if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS) ++ || !TEST_int_eq(ERR_GET_REASON(err), CMS_R_UNWRAP_FAILURE)) ++ goto end; ++ ++ ERR_clear_error(); ++ ret = 1; ++end: ++ CMS_ContentInfo_free(cms); ++ BIO_free(in); ++ return ret; ++} ++ ++OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer\n") + + int setup_tests(void) + { +@@ -499,7 +541,8 @@ int setup_tests(void) + if (!TEST_ptr(certin = test_get_argument(0)) + || !TEST_ptr(privkeyin = test_get_argument(1)) + || !TEST_ptr(derin = test_get_argument(2)) +- || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3))) ++ || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3)) ++ || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4))) + return 0; + + certbio = BIO_new_file(certin, "r"); +@@ -535,6 +578,7 @@ int setup_tests(void) + ADD_TEST(test_encrypted_data_aead); + ADD_ALL_TESTS(test_d2i_CMS_decode, 2); + ADD_TEST(test_cms_aesgcm_iv_too_long); ++ ADD_TEST(test_pwri_kek_unwrap_short_encrypted_key); + return 1; + } + +diff --git a/test/recipes/80-test_cmsapi.t b/test/recipes/80-test_cmsapi.t +index 8d9371e005c0..3d1dae846464 100644 +--- a/test/recipes/80-test_cmsapi.t ++++ b/test/recipes/80-test_cmsapi.t +@@ -19,5 +19,6 @@ plan tests => 1; + ok(run(test(["cmsapitest", srctop_file("test", "certs", "servercert.pem"), + srctop_file("test", "certs", "serverkey.pem"), + srctop_file("test", "recipes", "80-test_cmsapi_data", "encryptedData.der"), +- srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem")])), ++ srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem"), ++ srctop_file("test", "recipes", "80-test_cmsapi_data", "cms_pwri_kek_oob.der")])), + "running cmsapitest"); diff -Nru openssl-3.0.20/debian/patches/series openssl-3.0.20/debian/patches/series --- openssl-3.0.20/debian/patches/series 2026-05-04 18:46:40.000000000 +0000 +++ openssl-3.0.20/debian/patches/series 2026-06-06 19:56:18.000000000 +0000 @@ -7,3 +7,17 @@ Remove-the-provider-section.patch conf-Serialize-allocation-free-of-ssl_names.patch Fix-tests-for-new-default-security-level.patch +Reject-oversized-inputs-in-ASN1_mbstring_ncopy.patch +cms-kek_unwrap_key-Fix-out-of-bounds-read-in-check-byte-v.patch +cms-kek_unwrap_key-test-for-fix-out-of-bounds-read-in-che.patch +Avoid-length-truncation-in-ASN1_STRING_set.patch +CMS-Produce-error-when-AEAD-algorithms-are-used-in-envelo.patch +Reject-potentially-forged-encrypted-CMS-AuthEnvelopedData.patch +Add-tests-for-CVE-2026-34182.patch +Fix-potential-NULL-dereference-processing-CMS-PasswordRec.patch +Test-for-CVE-2026-42766.patch +Match-the-local-q-DHX-parameter-against-the-peer-s-q.patch +Apply-the-buffered-IV-on-the-AES-OCB-EVP_Cipher-path.patch +Fix-handling-of-empty-ciphertext-messages-in-AES-SIV.patch +Fix-possible-use-after-free-in-OpenSSL-PKCS7_verify.patch +Test-for-CVE-2026-45447-UAF-in-PKCS7_verify.patch