public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/openssl3] epel8: Merge changes from RHEL 9.8 to pick up CVE fixes
@ 2026-07-26 15:38 Robert Scheck
  0 siblings, 0 replies; only message in thread
From: Robert Scheck @ 2026-07-26 15:38 UTC (permalink / raw)
  To: git-commits

A new commit has been pushed.

Repo   : rpms/openssl3
Branch : epel8
Commit : d13942a5a1a1f666a1d37cf86fddd5cee2609d0a
Author : Robert Scheck <robert@fedoraproject.org>
Date   : 2026-07-26T17:37:39+02:00
Stats  : +2968/-7 in 19 file(s)
URL    : https://src.fedoraproject.org/rpms/openssl3/c/d13942a5a1a1f666a1d37cf86fddd5cee2609d0a?branch=epel8

Log:
Merge changes from RHEL 9.8 to pick up CVE fixes

---
diff --git a/0058-CVE-2026-31790.patch b/0058-CVE-2026-31790.patch
new file mode 100644
index 0000000..1b556a7
--- /dev/null
+++ b/0058-CVE-2026-31790.patch
@@ -0,0 +1,185 @@
+From 001e01db3e996e13ffc72386fe79d03a6683b5ac Mon Sep 17 00:00:00 2001
+From: Nikola Pajkovsky <nikolap@openssl.org>
+Date: Thu, 19 Mar 2026 12:16:08 +0100
+Subject: [PATCH 1/2] rsa_kem: validate RSA_public_encrypt() result in RSASVE
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+RSA_public_encrypt() returns the number of bytes written on success and
+-1 on failure. With the existing `if (ret)` check, a provider-side RSA KEM
+encapsulation can incorrectly succeed when the underlying RSA public
+encrypt operation fails. In that case the code reports success, returns
+lengths as if encapsulation completed normally, and leaves the freshly
+generated secret available instead of discarding it.
+
+Tighten the success condition so RSASVE only succeeds when
+RSA_public_encrypt() returns a positive value equal to the modulus-sized
+output expected for RSA_NO_PADDING. Any other return value is treated as
+failure, and the generated secret is cleansed before returning.
+
+Fixes CVE-2026-31790
+Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
+
+Reviewed-by: Saša Nedvědický <sashan@openssl.org>
+Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
+MergeDate: Mon Apr  6 19:51:30 2026
+---
+ providers/implementations/kem/rsa_kem.c | 20 +++++++++++---------
+ 1 file changed, 11 insertions(+), 9 deletions(-)
+
+diff --git a/providers/implementations/kem/rsa_kem.c b/providers/implementations/kem/rsa_kem.c
+index f7bf368a0d..74dfafddd9 100644
+--- a/providers/implementations/kem/rsa_kem.c
++++ b/providers/implementations/kem/rsa_kem.c
+@@ -316,17 +316,19 @@ static int rsasve_generate(PROV_RSA_CTX *prsactx,
+         return 0;
+ 
+     /* Step(3): out = RSAEP((n,e), z) */
+-    ret = RSA_public_encrypt(nlen, secret, out, prsactx->rsa, RSA_NO_PADDING);
+-    if (ret) {
+-        ret = 1;
+-        if (outlen != NULL)
+-            *outlen = nlen;
+-        if (secretlen != NULL)
+-            *secretlen = nlen;
+-    } else {
++    ret = RSA_public_encrypt((int)nlen, secret, out, prsactx->rsa,
++        RSA_NO_PADDING);
++    if (ret <= 0 || ret != (int)nlen) {
+         OPENSSL_cleanse(secret, nlen);
++        return 0;
+     }
+-    return ret;
++
++    if (outlen != NULL)
++        *outlen = nlen;
++    if (secretlen != NULL)
++        *secretlen = nlen;
++
++    return 1;
+ }
+ 
+ /**
+-- 
+2.53.0
+
+
+From c61bbd3f873d28e098f503f0187459ed488977c9 Mon Sep 17 00:00:00 2001
+From: Nikola Pajkovsky <nikolap@openssl.org>
+Date: Mon, 23 Mar 2026 08:41:20 +0100
+Subject: [PATCH 2/2] rsa_kem: test RSA_public_encrypt() result in RSASVE
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+RSA_public_encrypt() returns the number of bytes written on success and
+-1 on failure.
+
+Add regression coverage in evp_extra_test using invalid RSA pubkey
+which triggers -1 in RSA_public_encrypt() using encapsulation.
+
+Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
+
+Reviewed-by: Saša Nedvědický <sashan@openssl.org>
+Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
+MergeDate: Mon Apr  6 19:51:31 2026
+---
+ test/evp_extra_test.c | 67 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 67 insertions(+)
+
+diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
+index 5ea95c0dfa..573732bfec 100644
+--- a/test/evp_extra_test.c
++++ b/test/evp_extra_test.c
+@@ -929,6 +929,32 @@ static EVP_PKEY *load_example_ec_key(void)
+ #endif
+ 
+ #ifndef OPENSSL_NO_DEPRECATED_3_0
++
++static EVP_PKEY *make_bad_rsa_pubkey(void)
++{
++    RSA *rsa = NULL;
++    BIGNUM *n = NULL, *e = NULL;
++    EVP_PKEY *pkey = NULL;
++
++    /* Deliberately invalid public key: n = 17, e = 17 */
++    if (!TEST_ptr(pkey = EVP_PKEY_new())
++        || !TEST_ptr(rsa = RSA_new())
++        || !TEST_ptr(n = BN_new())
++        || !TEST_ptr(e = BN_new())
++        || !TEST_true(BN_set_word(n, 17))
++        || !TEST_true(BN_set_word(e, 17))
++        || !TEST_true(RSA_set0_key(rsa, n, e, NULL))
++        || !EVP_PKEY_assign_RSA(pkey, rsa))
++        goto err;
++
++    return pkey;
++err:
++    BN_free(n);
++    BN_free(e);
++    RSA_free(rsa);
++    return NULL;
++}
++
+ #ifndef OPENSSL_NO_DH
+ static EVP_PKEY *load_example_dh_key(void)
+ {
+@@ -5898,6 +5924,46 @@ err:
+     return testresult;
+ }
+ 
++static int test_rsasve_kem_with_invalid_pub_key(void)
++{
++    RSA *rsa = NULL;
++    EVP_PKEY *pkey = NULL;
++    EVP_PKEY_CTX *ctx = NULL;
++    unsigned char *ct = NULL;
++    unsigned char *secret = NULL;
++    size_t ctlen = 0, secretlen = 0;
++    int testresult = 0;
++
++    if (nullprov != NULL) {
++        testresult = TEST_skip("Test does not support a non-default library context");
++        goto err;
++    }
++
++    if (!TEST_ptr(pkey = make_bad_rsa_pubkey()))
++        goto err;
++
++    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(testctx, pkey, NULL))
++        || !TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 1)
++        || !TEST_int_eq(EVP_PKEY_CTX_set_kem_op(ctx, "RSASVE"), 1)
++        || !TEST_int_eq(EVP_PKEY_encapsulate(ctx, NULL, &ctlen, NULL, &secretlen), 1)
++        || !TEST_ptr(ct = OPENSSL_malloc(ctlen))
++        || !TEST_ptr(secret = OPENSSL_malloc(secretlen)))
++        goto err;
++
++    if (!TEST_int_eq(EVP_PKEY_encapsulate(ctx, ct, &ctlen, secret, &secretlen), 0))
++        goto err;
++
++    testresult = 1;
++
++err:
++    OPENSSL_free(secret);
++    OPENSSL_free(ct);
++    EVP_PKEY_CTX_free(ctx);
++    RSA_free(rsa);
++    EVP_PKEY_free(pkey);
++    return testresult;
++}
++
+ #ifndef OPENSSL_NO_DYNAMIC_ENGINE
+ /* Test we can create a signature keys with an associated ENGINE */
+ static int test_signatures_with_engine(int tst)
+@@ -6893,6 +6959,7 @@ int setup_tests(void)
+     ADD_TEST(test_evp_md_cipher_meth);
+     ADD_TEST(test_custom_md_meth);
+     ADD_TEST(test_custom_ciph_meth);
++    ADD_TEST(test_rsasve_kem_with_invalid_pub_key);
+ 
+ #ifndef OPENSSL_NO_DYNAMIC_ENGINE
+     /* Tests only support the default libctx */
+-- 
+2.53.0
+

diff --git a/0059-CVE-2026-28390.patch b/0059-CVE-2026-28390.patch
new file mode 100644
index 0000000..5b85f8b
--- /dev/null
+++ b/0059-CVE-2026-28390.patch
@@ -0,0 +1,93 @@
+From 2e39b7a6993be445fddb9fbce316fa756e0397b6 Mon Sep 17 00:00:00 2001
+From: Neil Horman <nhorman@openssl.org>
+Date: Wed, 1 Apr 2026 10:56:44 +0200
+Subject: [PATCH] Fix NULL deref in rsa_cms_decrypt
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Very simmilar to CVE-2026-28389, ensure that if we are missing
+parameters in RSA-OAEP SourceFunc in CMS KeyTransportRecipientInfo,
+we don't segfault when decrypting.
+
+Co-authored-by: Tomas Mraz <tomas@openssl.foundation>
+
+Fixes CVE-2026-28390
+
+Reviewed-by: Saša Nedvědický <sashan@openssl.org>
+Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
+MergeDate: Mon Apr  6 19:06:14 2026
+---
+ crypto/cms/cms_rsa.c | 31 +++++++++++++++++++------------
+ 1 file changed, 19 insertions(+), 12 deletions(-)
+
+diff --git a/crypto/cms/cms_rsa.c b/crypto/cms/cms_rsa.c
+index 6b65842cc1..34c739a982 100644
+--- a/crypto/cms/cms_rsa.c
++++ b/crypto/cms/cms_rsa.c
+@@ -42,10 +42,13 @@ static int rsa_cms_decrypt(CMS_RecipientInfo *ri)
+     X509_ALGOR *cmsalg;
+     int nid;
+     int rv = -1;
+-    unsigned char *label = NULL;
++    const unsigned char *label = NULL;
+     int labellen = 0;
+     const EVP_MD *mgf1md = NULL, *md = NULL;
+     RSA_OAEP_PARAMS *oaep;
++    const ASN1_OBJECT *aoid;
++    const void *parameter = NULL;
++    int ptype = 0;
+ 
+     pkctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
+     if (pkctx == NULL)
+@@ -75,21 +78,19 @@ static int rsa_cms_decrypt(CMS_RecipientInfo *ri)
+         goto err;
+ 
+     if (oaep->pSourceFunc != NULL) {
+-        X509_ALGOR *plab = oaep->pSourceFunc;
++        X509_ALGOR_get0(&aoid, &ptype, &parameter, oaep->pSourceFunc);
+ 
+-        if (OBJ_obj2nid(plab->algorithm) != NID_pSpecified) {
++        if (OBJ_obj2nid(aoid) != NID_pSpecified) {
+             ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_LABEL_SOURCE);
+             goto err;
+         }
+-        if (plab->parameter->type != V_ASN1_OCTET_STRING) {
++        if (ptype != V_ASN1_OCTET_STRING) {
+             ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_LABEL);
+             goto err;
+         }
+ 
+-        label = plab->parameter->value.octet_string->data;
+-        /* Stop label being freed when OAEP parameters are freed */
+-        plab->parameter->value.octet_string->data = NULL;
+-        labellen = plab->parameter->value.octet_string->length;
++        label = ASN1_STRING_get0_data(parameter);
++        labellen = ASN1_STRING_length(parameter);
+     }
+ 
+     if (EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING) <= 0)
+@@ -98,10 +99,16 @@ static int rsa_cms_decrypt(CMS_RecipientInfo *ri)
+         goto err;
+     if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, mgf1md) <= 0)
+         goto err;
+-    if (label != NULL
+-        && EVP_PKEY_CTX_set0_rsa_oaep_label(pkctx, label, labellen) <= 0) {
+-        OPENSSL_free(label);
+-        goto err;
++    if (label != NULL) {
++        unsigned char *dup_label = OPENSSL_memdup(label, labellen);
++
++        if (dup_label == NULL)
++            goto err;
++
++        if (EVP_PKEY_CTX_set0_rsa_oaep_label(pkctx, dup_label, labellen) <= 0) {
++            OPENSSL_free(dup_label);
++            goto err;
++        }
+     }
+     /* Carry on */
+     rv = 1;
+-- 
+2.53.0
+

diff --git a/0060-CVE-2026-7383.patch b/0060-CVE-2026-7383.patch
new file mode 100644
index 0000000..7e7e978
--- /dev/null
+++ b/0060-CVE-2026-7383.patch
@@ -0,0 +1,63 @@
+diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c
+index 05b8ac8706f..7221e843b03 100644
+--- a/crypto/asn1/a_mbstr.c
++++ b/crypto/asn1/a_mbstr.c
+@@ -185,11 +185,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;
+@@ -197,8 +213,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;
+@@ -281,9 +300,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 --git a/0061-CVE-2026-9076.patch b/0061-CVE-2026-9076.patch
new file mode 100644
index 0000000..f60a6d0
--- /dev/null
+++ b/0061-CVE-2026-9076.patch
@@ -0,0 +1,181 @@
+From ae233967cf40b15fe80c70c17438d391158171b3 Mon Sep 17 00:00:00 2001
+From: Nikola Pajkovsky <nikolap@openssl.org>
+Date: Thu, 21 May 2026 11:53:09 +0200
+Subject: [PATCH 1/2] 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 <nikolap@openssl.org>
+---
+ crypto/cms/cms_pwri.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
+index d62dbbde881..4bb06b406eb 100644
+--- a/crypto/cms/cms_pwri.c
++++ b/crypto/cms/cms_pwri.c
+@@ -200,18 +200,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 (blocklen == 0)
++    if (blocklen < 4)
+         return 0;
+ 
+-    if (inlen < 2 * blocklen) {
++    if (inlen < 2 * (size_t)blocklen) {
+         /* too small */
+         return 0;
+     }
+-    if (inlen % blocklen) {
++    if (inlen > INT_MAX || inlen % blocklen) {
+         /* Invalid size */
+         return 0;
+     }
+
+From 4a35e3c194ceb0af30fcbe42d65c95e08d5758a5 Mon Sep 17 00:00:00 2001
+From: Nikola Pajkovsky <nikolap@openssl.org>
+Date: Thu, 21 May 2026 14:18:11 +0200
+Subject: [PATCH 2/2] 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 <nikolap@openssl.org>
+---
+ test/cmsapitest.c                             |  48 +++++++++++++++++-
+ test/recipes/80-test_cmsapi.t                 |   3 +-
+ .../80-test_cmsapi_data/cms_pwri_kek_oob.der  | Bin 0 -> 193 bytes
+ 3 files changed, 48 insertions(+), 3 deletions(-)
+ create mode 100644 test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der
+
+diff --git a/test/cmsapitest.c b/test/cmsapitest.c
+index 0752d14df09..d908bc6fc4c 100644
+--- a/test/cmsapitest.c
++++ b/test/cmsapitest.c
+@@ -21,6 +21,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)
+ {
+@@ -512,7 +513,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)
+ {
+@@ -527,7 +569,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");
+@@ -564,6 +607,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 8d9371e005c..3d1dae84646 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 --git a/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der b/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der
+new file mode 100644
+index 0000000000000000000000000000000000000000..c3ef3abd10e6b0a57b1eef985b0c8c43cb057371
+GIT binary patch
+literal 193
+zcmXqL+{ebL)#lOmotKfFc|qd_gT}Q?jLe2!i#?ba85StRC0Th4#8?FEa~A$hV(x0n
+zFrRcQQS~(+6B7r6ffO4z)C5ieW=;ccHqL}L55`nx7Dg5pCI$wB7`P$qj0Um@Stb?%
+z*}W&;-kY#&W8?d>8TmW9IT{Q$uJ-6FX$+dodfRzpO2J!$Gg}?F()_mBI35*0&tMe5
+VHoNtcLX?S)y&B`}phcqR3jwDUJP7~*
+
+literal 0
+HcmV?d00001
+

diff --git a/0062-CVE-2026-34180.patch b/0062-CVE-2026-34180.patch
new file mode 100644
index 0000000..23950d9
--- /dev/null
+++ b/0062-CVE-2026-34180.patch
@@ -0,0 +1,75 @@
+diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c
+index 70ea5f08798..197fd241054 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;
+@@ -970,10 +980,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+         }
+         /* If we've already allocated a buffer use it */
+         if (*free_cont) {
+-            ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, len);
++            ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, 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_ASN1_LIB);
+                 ASN1_STRING_free(stmp);
+                 *pval = NULL;

diff --git a/0063-CVE-2026-34181.patch b/0063-CVE-2026-34181.patch
new file mode 100644
index 0000000..c6c8bb2
--- /dev/null
+++ b/0063-CVE-2026-34181.patch
@@ -0,0 +1,208 @@
+From 5a9e9489f9e5f7f7eb2f65c9b9fc75566cdcbc01 Mon Sep 17 00:00:00 2001
+From: Alicja Kario <hkario@redhat.com>
+Date: Wed, 29 Apr 2026 16:29:35 +0200
+Subject: [PATCH] pkcs12: verify that the pbmac1 key length is safe
+
+Short mac keys (as short as 1 byte) can be used to probe the
+system under attack to accept a PKCS#12 file created by an attacker
+even if the attacker doesn't know the password used for MAC protection.
+
+Fixes CVE-2026-34181
+
+(also update the reference to the PBMAC1 PKCS#12 RFC)
+
+Signed-off-by: Alicja Kario <hkario@redhat.com>
+---
+ crypto/pkcs12/p12_mutl.c                         |   8 +++++---
+ test/recipes/80-test_pkcs12.t                    |  13 ++++++++-----
+ .../pbmac1_256_256.bad-key-len.p12               | Bin 0 -> 2803 bytes
+ .../pbmac1_256_256.good-shorter-key-len.p12      | Bin 0 -> 2803 bytes
+ 4 files changed, 13 insertions(+), 8 deletions(-)
+ create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-key-len.p12
+ create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.good-shorter-key-len.p12
+
+diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c
+index 01956252df7..15072e12f26 100644
+--- a/crypto/pkcs12/p12_mutl.c
++++ b/crypto/pkcs12/p12_mutl.c
+@@ -144,11 +144,13 @@ static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_CTX *ctx, const char *propq,
+     }
+     pbkdf2_salt = pbkdf2_param->salt->value.octet_string;
+ 
+-    /* RFC 9579 specifies missing key length as invalid */
++    /* RFC 9879 specifies missing key length as invalid */
+     if (pbkdf2_param->keylength != NULL)
+         keylen = ASN1_INTEGER_get(pbkdf2_param->keylength);
+-    if (keylen <= 0 || keylen > EVP_MAX_MD_SIZE) {
+-        ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR);
++    /* RFC 9879 specifies too short key length as untrustworthy too */
++    if (keylen < 20 || keylen > EVP_MAX_MD_SIZE) {
++        ERR_raise_data(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR,
++            "Invalid Key length (%d is not in the range 20..64)", keylen);
+         goto err;
+     }
+ 
+diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
+index d258b7eb0e4..56ab93803e7 100644
+--- a/test/recipes/80-test_pkcs12.t
++++ b/test/recipes/80-test_pkcs12.t
+@@ -56,7 +56,7 @@ $ENV{OPENSSL_WIN32_UTF8}=1;
+ 
+ my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0);
+ 
+-plan tests => $no_fips ? 53 : 59;
++plan tests => $no_fips ? 55 : 61;
+ 
+ # Test different PKCS#12 formats
+ ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats");
+@@ -205,8 +205,11 @@ for my $instance (sort keys %pbmac1_tests) {
+     }
+ }
+ 
+-# Test pbmac1 pkcs12 good files, RFC 9579
+-for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12", "pbmac1_512_512.good.p12")
++# Test pbmac1 pkcs12 good files, RFC 9579, and one extra with shorter key
++# length
++for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12",
++              "pbmac1_512_512.good.p12",
++              "pbmac1_256_256.good-shorter-key-len.p12")
+ {
+     my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file);
+     ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])),
+@@ -235,12 +238,12 @@ unless ($no_fips) {
+     }
+ }
+ 
+-# Test pbmac1 pkcs12 bad files, RFC 9579 and CVE-2025-11187
++# Test pbmac1 pkcs12 bad files, RFC 9579, CVE-2025-11187 and CVE-2026-34181
+ for my $file ("pbmac1_256_256.bad-iter.p12", "pbmac1_256_256.bad-salt.p12",
+               "pbmac1_256_256.no-len.p12", "pbmac1_256_256.bad-len.p12",
+               "pbmac1_256_256.bad-salt-type.p12", "pbmac1_256_256.negative-len.p12",
+               "pbmac1_256_256.no-salt.p12", "pbmac1_256_256.very-big-len.p12",
+-              "pbmac1_256_256.zero-len.p12")
++              "pbmac1_256_256.zero-len.p12", "pbmac1_256_256.bad-key-len.p12")
+ {
+     my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file);
+     with({ exit_checker => sub { return shift == 1; } },
+diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-key-len.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-key-len.p12
+new file mode 100644
+index 0000000000000000000000000000000000000000..7162fd1871790e3ab9cbc4e00fb73cdf63d5e64b
+GIT binary patch
+literal 2803
+zcmai$c{J4j8pj7S7+D6R$Pyux<vWZuSq4!k+1G4^3_@gUG{!Q>_CuDoVPuU#vj4`K
+zrOhb6nPiOySwfgH64&qCd+OYK?m53dp7T8K_xm}Y&wsD;064BSRu(n@$K}WlJ(*;d
+z^qq%=gQW<^bq<W<GC8mf0US8>cf?+V1IHX#5v(krgBJWdVFBEK2?!5h4Iq9?Za@YM
+z^Wii0br`!Ggequ;YZ{@XSXnti05?15zkg+gLO_5xJJch|jO8jDnB^oGR^g?cgEne4
+zl#mJ8=oQ6AV{u?m%k~t4Ew}NVWar5_U5=ge+lx9QYm}<dHg;Sftaj*P<o(eSec?`H
+zU2mymJ?DUiSa`vT*s7r}FM|}E)^ld3Oy$ACxieq#T6brnY{pIvT<}X2xhZkT7SF%<
+zPz>&}CFACR(%&P|)_f&6#ox(3INg_3?z5>ssUB-tL!RtNV(&KePwl0m`q6R&m{~6}
+z4{YPaTkY<M(k`o{xXYCZeWZLnvEzX%y(gxG7r@v$ma5Zx78~ogne1@fd8q)zW#?h3
+zM&Bz%bJ!zKjugm#wj&#?-}^ufjTP)wlQ+yyPcxBg;RK;mvbvG8SD?H)K_qr)CLX8*
+zB|H|IVCKBlmOvZ*z_gJ{ynkrb%<;l=_qR+$-R9+#seM%fm)1r&M~NNE&+P{?)!oQN
+zXMII471qQn!7gY&9WS(EmyLj=gpbTU<`9=ZzV~AByp}_6v<GZMd&n^@*ea&YvzV^6
+zz*Z|{Utv|8vA->*zyE=@#w<r(04-@ox!kSMVPg#I4A-|$KZD8_@*97%8I3cWQjTWW
+zho0z5oeUCCz~&Hug{w5Z?2Zn#3mg8lJICz`+xdcz5(rsWKTGuoOUjC?z9;bD5&W(6
+z0t(M&9;>vmdJNX=Ps%k7`mk&2TiZ|z-IjaYDcoDj@l6}0+j$1OAH_46$v(lD^b1*m
+zHG|TuS370~5tU~Z^(W_iuA}0F-4bl(hkWbbeIE^~t|&LXItR@iQAIgOZ-p|+4buxc
+zq6JGqbW9g;5gO;Ab2>_fj5eI0oBBATyW(cGYVpG>Cvd4sZQMwg=sTH<Pq!^mZDXmK
+z3Kg^WlzjD6MVr_zb_~?Beqw~)UI$s^i=Yg0^>)G0{r$E^TcIcE(He>AOe*)b^|+#a
+zPuC)Avt)nDJmSev@QaupE~2g+uyCpL`wIW*Ui9tArpx#{3$+OWMytIN(0ok%6b@}c
+zb-e%M{0Cx5?>n!`edT6!Yt_#JjZ>+onzXjwKf3-NpH80$$s|Y&FeRH4qU6xk*3QiG
+zr{kUl`C-jFGq*Ia9LAR}@X#5tn6lY!`mF0D--D98$f6rc?`^^l8^7+@X|0jD#i8&r
+zpgLM#UU$45I*n|)sPu_*mGcw-K=tB@LPLiQ+)I=vC@w4=8+Hm8)E0U7{jt@3jVk;3
+z%KhG`W+XFy)UQvbu};`p<Yt}<bEey!Z?kA$eb%aj+<x~>adp{%j-w?eL7ekZcbvBR
+z!idUT&JbYHyt<sm1vh~=Z{M42N>?syvBA~f{~FqY&(JgTK7|*1bgp|jB=$w7a2%Vc
+zm>uyczrs50skoi0&aTsOcZ~VB%7k*)O8CCs4hC6?gJdq{vhmheFps9uwDSNQr1lqA
+z{stAK2#kX~IIs&2DwqAxe;CHi#lm_JY9E;D{{$vkr>;B##UN?0gx>G)Dy#4x!PNN0
+zL{h{2*dNSe3DcCSS{*PBJRF`Sm}Pc0a$+K^hOL)qFYzWvv@zuHU-t@r!WxubxDjq~
+zwPi#a1J@}ML+&=2qS5-^PMBnL^lrqfH^f8XvG6@efM2*)D)k9tR8B1?$4pE0Xw%^W
+zj-Gj{;?9)RpwTH^2JOdC+SJIpYR{sY;?kXmZnNY*3B;xySK^CURBXodmbOB;9Cy7}
+zBf(_Gh9a0CmWGX?hEkpM@$U0C#3N{yD!$!N|8kdkfmq|cjJQ>uTD7Vhck;o|YUh>l
+zcha&%##owt=1ZD)lO!)Q+V6wHQUa~8)Poqbp)~-ZX(7$}iAj&ikWN`3n4r1YhrV4a
+z#rAnHQN3@ycxkMvd2*hbVSeQNiJz4G2X)yED|bWVp3Q@a#v`4+yzShS<A$@!e>_6S
+zx)gQpu}iHWn0(nX`)iTP8_Pck;pbd8zC_M%TAOPNRYy76OCr4kQ7z6glZfS*XS+#m
+z*(JJM7sTD;Mne4R<b(5{HNX_DU?XgSm@@viD$_!WZC1m8NYMTD5UUZnz$#UK)Ws=!
+zX_KUGy@-8io8g=S`BgW}4??`p`-<c|`?i{yJzO+5hq%>rY}mxsdU^qlHQ+Is{j=cF
+z(}_rQ(X!XwVw8L9+7${pr4<ZZlI2n^AvfMSJLn?#t|mRU%2QsRo^E=mOOrCdpfcgR
+zhfG)9ndA}2=L&;semgEPHyCu_MraFCf!r=k!ux^p6|7xq+{{zHs#<AmR$3fxQ5pw4
+z>7~0RTVrxYu<M#T`9}$iuPdn)p0rh`&c!(i+YY5qW&E9E0~dWb&^e!m>*B}s9C?;l
+zPC)7feZDy_R=|gLEY^`OizwiGs!nyX=oi#$mtq0`F`<d)RDCk%xZ^#SHAC2XWro$z
+z=%j9yc2pomw75~XF^AVXo#}k#RZrpM&pfZIqXQo}mnujwLwm;OCZRt+=D!T=5nrrL
+zuMctMQj56mWy$v9%eDfcfidG|PRVVnf9RQH(eh~1^w9mF3c+;-CyC?~p61rK({*ks
+zAr$J<X`uH^+H3WtpwiRR2Jt^;#Xid2p%C_^jTJ?xwb`*@&b~^m5wFz(&E!445tKR$
+za+>F3X8Thwg2!wt1yqPLT92;<iR>BQEM(<-LOn9#y%ciore+uj$kmZjbxxMOTwX?+
+z3W(R`aNUI+`)nHb_}WAERLrtwMAl^|Hf-~go255dFcFX=@zPlP>dacq>FL%rd#)`U
+z(Cgv<aMFI?GgK~L9=rP`BU!8#Y-FtD>f=Jhw^2WyIrVv+{(f}o;W|}A&Gn>qhPbGu
+z*~0Wrpyrtj4elMRhAHl$=cvtsexbxS_mGf`_FNnJJq}B&ZM++lCDHxC*Dh&J+xG>x
+zNI{P^bJcG}2<+mKXHmxjYjULSAL;FRDXwTE+@<M7Wq8baD4lx^&dKiee^dBr!&_WH
+zH2nKtNXs=gf_1o1$LipfFit|UQHRqvpBcx1xkmr|0JDnzZ>E{VDVs8LUO?E%;nm=r
+zqcCRD*DPNnq@RSg`(#CxZCFo(UaqB$W;~a-_xBVv<c-obN(|(0=l4F(mr!5hUS>XA
+zXP07<Tq;RqDqepgCl+ebqQmrc!TahTIwRIrt8r5EsM6V_tdPO4X~<Cu?T3&1YY#_)
+z2Eq)G`UP4j908XQ00n@qgS<hW2i@;rx&aCUq<^mnAr6D#-QKOdscUvVkO`sO0&ZlD
+z;n8$CATAvU2QK~Egzy5!0P?rw2IRo7pNaG1juN9xl?Gm7QSl#|e>0esMeu*_!~eP#
+p!IEkKDV_8&u%4!IcN?uKd-cjZ4l9KP8|r=hepF<b$L9Y(`!C3!5kvq0
+
+literal 0
+HcmV?d00001
+
+diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.good-shorter-key-len.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.good-shorter-key-len.p12
+new file mode 100644
+index 0000000000000000000000000000000000000000..3c202dd58d7fa1d3cd4f9d7252565aff2145ec96
+GIT binary patch
+literal 2803
+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
+
+literal 0
+HcmV?d00001
+

diff --git a/0064-CVE-2026-34183.patch b/0064-CVE-2026-34183.patch
new file mode 100644
index 0000000..4da896a
--- /dev/null
+++ b/0064-CVE-2026-34183.patch
@@ -0,0 +1,536 @@
+diff --git a/include/internal/quic_cfq.h b/include/internal/quic_cfq.h
+index 0b2a3a4cb2d..96c8d89eb60 100644
+--- a/include/internal/quic_cfq.h
++++ b/include/internal/quic_cfq.h
+@@ -149,6 +149,7 @@ QUIC_CFQ_ITEM *ossl_quic_cfq_get_priority_head(const QUIC_CFQ *cfq,
+ QUIC_CFQ_ITEM *ossl_quic_cfq_item_get_priority_next(const QUIC_CFQ_ITEM *item,
+     uint32_t pn_space);
+ 
++int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item);
+ #endif
+ 
+ #endif
+diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h
+index b08e63e148a..d333bc3e5e5 100644
+--- a/include/internal/quic_channel.h
++++ b/include/internal/quic_channel.h
+@@ -466,6 +466,9 @@ uint64_t ossl_quic_channel_get_max_idle_timeout_actual(const QUIC_CHANNEL *ch);
+     const QUIC_CONN_ID *scid, const QUIC_CONN_ID *dcid,
+     const QUIC_CONN_ID *odcid);
+ 
++void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch);
++uint64_t ossl_quic_channel_get_path_challenge_count(const QUIC_CHANNEL *ch);
++uint64_t ossl_quic_channel_get_path_response_count(const QUIC_CHANNEL *ch);
+ #endif
+ 
+ #endif
+diff --git a/include/internal/quic_fifd.h b/include/internal/quic_fifd.h
+index 4ea7a2e0d22..afa330cbc4a 100644
+--- a/include/internal/quic_fifd.h
++++ b/include/internal/quic_fifd.h
+@@ -83,6 +83,7 @@ int ossl_quic_fifd_pkt_commit(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt);
+ void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg),
+     void *arg);
+ 
++void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *tpkt);
+ #endif
+ 
+ #endif
+diff --git a/ssl/quic/quic_cfq.c b/ssl/quic/quic_cfq.c
+index 3c59234ff0f..16818e55f57 100644
+--- a/ssl/quic/quic_cfq.c
++++ b/ssl/quic/quic_cfq.c
+@@ -7,6 +7,7 @@
+  * https://www.openssl.org/source/license.html
+  */
+ 
++#include "internal/quic_channel.h"
+ #include "internal/quic_cfq.h"
+ #include "internal/numbers.h"
+ 
+@@ -307,6 +308,20 @@ void ossl_quic_cfq_mark_lost(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item,
+     }
+ }
+ 
++int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item)
++{
++    int discarded;
++
++    if (ossl_quic_cfq_item_is_unreliable(item)) {
++        ossl_quic_cfq_release(cfq, item);
++        discarded = 1;
++    } else {
++        discarded = 0;
++    }
++
++    return discarded;
++}
++
+ /*
+  * Releases a CFQ item. The item may be in either state (NEW or TX) prior to the
+  * call. The QUIC_CFQ_ITEM pointer must not be used following this call.
+diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
+index 631f289b391..9534cbcaf7f 100644
+--- a/ssl/quic/quic_channel.c
++++ b/ssl/quic/quic_channel.c
+@@ -2213,6 +2213,12 @@ static void ch_rx_check_forged_pkt_limit(QUIC_CHANNEL *ch)
+         "forgery limit");
+ }
+ 
++void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch)
++{
++    ch->did_crypto_frame = 0;
++    ch->seen_path_challenge = 0;
++}
++
+ /* Process queued incoming packets and handle frames, if any. */
+ static int ch_rx(QUIC_CHANNEL *ch, int channel_only, int *notify_other_threads)
+ {
+@@ -4060,3 +4066,13 @@ uint64_t ossl_quic_channel_get_max_idle_timeout_actual(const QUIC_CHANNEL *ch)
+ {
+     return ch->max_idle_timeout;
+ }
++
++uint64_t ossl_quic_channel_get_path_challenge_count(const QUIC_CHANNEL *ch)
++{
++    return ch->path_challenge_rx;
++}
++
++uint64_t ossl_quic_channel_get_path_response_count(const QUIC_CHANNEL *ch)
++{
++    return ch->path_response_tx;
++}
+diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h
+index ae443fccca1..eb082d6cea7 100644
+--- a/ssl/quic/quic_channel_local.h
++++ b/ssl/quic/quic_channel_local.h
+@@ -12,6 +12,28 @@
+ #include "internal/quic_stream_map.h"
+ #include "internal/quic_tls.h"
+ 
++/*
++ * This is a part of PATH_CHALLENGE flood [1] mitigation. This limits the
++ * number of PATH_CHALLENGE frames  QUIC stack is willing to process for
++ * connection. Local QUIC stack creates PATH_RESPONSE frame for PATH_CHALLENGE
++ * frame it receives from remote peer. The response frame is put Control Frame
++ * Queue waiting to be dispatched. The PATH_RESPONSE frame is removed from CFQ
++ * after it is dispatched. The QUIC_PATH_RESPONSE_QLEN limits the number of
++ * PATH_RESPONSE frames waiting to be dispatched. No new PATH_RESPONSE frames
++ * are inserted into CFQ if queue limit is exceeded.
++ *
++ * QUIC implementations use different limits for PATH_RESPONSE queue lengths:
++ *    quic-go defines maxPathResponses as 256
++ *    quiche from cloadflare sets DEFAULT_MAX_PATH_CHALLENGE_RX_QUEUE_LEN to 3
++ *    t-quic from tencent chooses MAX_PATH_CHALS_RECV to be 8
++ *
++ * OpenSSL here introduces QUIC_PATH_RESPONSE_QLEN as 32.
++ *
++ * [1] https://www.ietf.org/archive/id/draft-chen-quic-logical-vuln-mitigations-00.txt
++ *     (section 4.2)
++ */
++#define QUIC_PATH_RESPONSE_QLEN 32
++
+ /*
+  * QUIC Channel Structure
+  * ======================
+@@ -457,6 +479,18 @@ struct quic_channel_st {
+ 
+     /* Has qlog been requested? */
+     unsigned int is_tserver_ch : 1;
++    /*
++     * RFC 9000 Section 9.2.1 says:
++     *      However, an endpoint SHOULD NOT send multiple
++     *      PATH_CHALLENGE frames in a single packet.
++     * The counter here allows us to detect multiple presence
++     * of PATH_CHALLENGE frame in packet. We process only the
++     * first PATH_CHALLENGE frame found in packet. Remaining PATH_CHALLENGE
++     * frames are ignored.
++     * seen_path_challenge flag is always reset before
++     * ossl_quic_handle_frames() gets called.
++     */
++    unsigned int seen_path_challenge : 1;
+ 
+     /* Saved error stack in case permanent error was encountered */
+     ERR_STATE *err_state;
+@@ -467,6 +501,15 @@ struct quic_channel_st {
+ 
+     /* Title for qlog purposes. We own this copy. */
+     char *qlog_title;
++    /*
++     * number of path responses waiting to be dispatched
++     * from control frame queue (CFQ)
++     */
++    unsigned int path_response_limit;
++    /* number of path challenge frames received */
++    unsigned int path_challenge_rx;
++    /* number of path response frames sent */
++    unsigned int path_response_tx;
+ };
+ 
+ #endif
+diff --git a/ssl/quic/quic_fifd.c b/ssl/quic/quic_fifd.c
+index 03b8cebd305..e80483b501d 100644
+--- a/ssl/quic/quic_fifd.c
++++ b/ssl/quic/quic_fifd.c
+@@ -310,3 +310,46 @@ void ossl_quic_fifd_set_qlog_cb(QUIC_FIFD *fifd, QLOG *(*get_qlog_cb)(void *arg)
+     fifd->get_qlog_cb = get_qlog_cb;
+     fifd->get_qlog_cb_arg = get_qlog_cb_arg;
+ }
++
++static void txpim_pkt_remove_cfq_item(QUIC_TXPIM_PKT *pkt, QUIC_CFQ_ITEM *cfq_item)
++{
++    QUIC_CFQ_ITEM *prev = cfq_item->pkt_prev;
++
++    if (prev != NULL) {
++        prev->pkt_next = cfq_item->pkt_next;
++    } else {
++        pkt->retx_head = cfq_item->pkt_next;
++    }
++
++    if (cfq_item->pkt_next != NULL)
++        cfq_item->pkt_next->pkt_prev = prev;
++
++    cfq_item->pkt_prev = NULL;
++    cfq_item->pkt_next = NULL;
++}
++
++void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *pkt)
++{
++    QUIC_CFQ_ITEM *cfq_item, *cfq_next;
++
++    /*
++     * The packet has been written to network. We can discard frames we don't
++     * retransmit when loss is detected.
++     */
++    cfq_item = pkt->retx_head;
++    while (cfq_item != NULL) {
++        /*
++         * Discarded items are moved to free list. If item
++         * got moved to free list we must also remove it from
++         * cfq list kept in pkt, so ACKM does not find it when
++         * receives an ACK for pkt.
++         */
++        if (ossl_quic_cfq_discard_unreliable(fifd->cfq, cfq_item)) {
++            cfq_next = cfq_item->pkt_next;
++            txpim_pkt_remove_cfq_item(pkt, cfq_item);
++            cfq_item = cfq_next;
++        } else {
++            cfq_item = cfq_item->pkt_next;
++        }
++    }
++}
+diff --git a/ssl/quic/quic_rx_depack.c b/ssl/quic/quic_rx_depack.c
+index 786af9b4c22..7ab59f01a1c 100644
+--- a/ssl/quic/quic_rx_depack.c
++++ b/ssl/quic/quic_rx_depack.c
+@@ -931,6 +931,22 @@ static int depack_do_frame_retire_conn_id(PACKET *pkt,
+ 
+ static void free_path_response(unsigned char *buf, size_t buf_len, void *arg)
+ {
++    QUIC_CHANNEL *ch = (QUIC_CHANNEL *)arg;
++
++    assert(ch->path_response_limit > 0);
++
++    ch->path_response_limit--;
++
++    /*
++     * Assume path response frame is being freed on behalf of
++     * finished TX operation. This is for unit testing purposes
++     * only. The counter is also bumped when channel is being
++     * destroyed and CFQ (control frame queue) is freed.
++     * This currently does not matter for check_pc_flood
++     * in test/radix/quic_tests.c.
++     */
++    ch->path_response_tx++;
++
+     OPENSSL_free(buf);
+ }
+ 
+@@ -951,33 +967,41 @@ static int depack_do_frame_path_challenge(PACKET *pkt,
+         return 0;
+     }
+ 
+-    /*
+-     * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint MUST
+-     * respond by echoing the data contained in the PATH_CHALLENGE frame in a
+-     * PATH_RESPONSE frame.
+-     *
+-     * TODO(QUIC FUTURE): We should try to avoid allocation here in the future.
+-     */
+-    encoded_len = sizeof(uint64_t) + 1;
+-    if ((encoded = OPENSSL_malloc(encoded_len)) == NULL)
+-        goto err;
++    if (ch->seen_path_challenge == 0
++        && ch->path_response_limit < QUIC_PATH_RESPONSE_QLEN) {
++        /*
++         * RFC 9000 s. 8.2.2: On receiving a PATH_CHALLENGE frame, an endpoint
++         * MUST respond by echoing the data contained in the PATH_CHALLENGE
++         * frame in a PATH_RESPONSE frame.
++         *
++         * TODO(QUIC FUTURE): We should try to avoid allocation here in the
++         * future.
++         */
++        encoded_len = sizeof(uint64_t) + 1;
++        if ((encoded = OPENSSL_malloc(encoded_len)) == NULL)
++            goto err;
+ 
+-    if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0))
+-        goto err;
++        if (!WPACKET_init_static_len(&wpkt, encoded, encoded_len, 0))
++            goto err;
+ 
+-    if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) {
+-        WPACKET_cleanup(&wpkt);
+-        goto err;
+-    }
++        if (!ossl_quic_wire_encode_frame_path_response(&wpkt, frame_data)) {
++            WPACKET_cleanup(&wpkt);
++            goto err;
++        }
+ 
+-    WPACKET_finish(&wpkt);
++        WPACKET_finish(&wpkt);
+ 
+-    if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP,
+-            OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE,
+-            QUIC_CFQ_ITEM_FLAG_UNRELIABLE,
+-            encoded, encoded_len,
+-            free_path_response, NULL))
+-        goto err;
++        if (!ossl_quic_cfq_add_frame(ch->cfq, 0, QUIC_PN_SPACE_APP,
++                OSSL_QUIC_FRAME_TYPE_PATH_RESPONSE,
++                QUIC_CFQ_ITEM_FLAG_UNRELIABLE,
++                encoded, encoded_len,
++                free_path_response, ch))
++            goto err;
++        ch->seen_path_challenge = 1;
++        ch->path_response_limit++;
++    }
++
++    ch->path_challenge_rx++;
+ 
+     return 1;
+ 
+@@ -1432,7 +1456,7 @@ int ossl_quic_handle_frames(QUIC_CHANNEL *ch, OSSL_QRX_PKT *qpacket)
+     if (ch == NULL)
+         return 0;
+ 
+-    ch->did_crypto_frame = 0;
++    ossl_ch_reset_rx_state(ch);
+ 
+     /* Initialize |ackm_data| (and reinitialize |ok|)*/
+     memset(&ackm_data, 0, sizeof(ackm_data));
+diff --git a/ssl/quic/quic_txp.c b/ssl/quic/quic_txp.c
+index 44aaad868d2..b2565c1a9fe 100644
+--- a/ssl/quic/quic_txp.c
++++ b/ssl/quic/quic_txp.c
+@@ -3133,6 +3133,8 @@ static int txp_pkt_commit(OSSL_QUIC_TX_PACKETISER *txp,
+             --probe_info->pto[pn_space];
+     }
+ 
++    ossl_quic_fifd_pkt_discard_unreliable(&txp->fifd, tpkt);
++
+     return rc;
+ }
+ 
+diff --git a/test/radix/quic_tests.c b/test/radix/quic_tests.c
+index 181ba1226b8..48b1df6c1d4 100644
+--- a/test/radix/quic_tests.c
++++ b/test/radix/quic_tests.c
+@@ -294,6 +294,188 @@ DEF_SCRIPT(check_cwm, "check stream obeys cwm")
+     OP_WRITE_FAIL(C);
+ }
+ 
++struct mutcbk_ctx {
++    QUIC_PKT_HDR mutctx_qhdrin;
++    OSSL_QTX_IOVEC mutctx_iov;
++    const unsigned char *mutctx_inject;
++    size_t mutctx_inject_sz;
++    int mutctx_done;
++};
++
++static int mutcbk_inject_frames(const QUIC_PKT_HDR *hdrin,
++    const OSSL_QTX_IOVEC *iovecin, size_t numin, QUIC_PKT_HDR **hdrout,
++    const OSSL_QTX_IOVEC **iovecout, size_t *numout, void *arg)
++{
++    struct mutcbk_ctx *mutctx = (struct mutcbk_ctx *)arg;
++    size_t i;
++    size_t grow_allowance = 1200; /* QUIC_MIN_INITIAL_DGRAM_LEN */
++    size_t bufsz = 0;
++    char *buf;
++
++    /*
++     * make injection callback a one shot event,
++     * callback is invoked for every packet we
++     * want to modify only one packet here.
++     */
++    if (mutctx->mutctx_done)
++        return 0;
++
++    mutctx->mutctx_done = 1;
++
++    for (i = 0; i < numin; i++)
++        bufsz += iovecin[i].buf_len;
++
++    mutctx->mutctx_iov.buf_len = bufsz; /* keeps old size */
++    grow_allowance -= (bufsz < grow_allowance) ? bufsz : grow_allowance;
++    /* AEAD tag (16 bytes) + long header (14 bytes) */
++    grow_allowance -= (30 < grow_allowance) ? 30 : grow_allowance;
++
++    grow_allowance -= (hdrin->dst_conn_id.id_len < grow_allowance) ? hdrin->dst_conn_id.id_len : grow_allowance;
++    grow_allowance -= (hdrin->src_conn_id.id_len < grow_allowance) ? hdrin->src_conn_id.id_len : grow_allowance;
++
++    if (grow_allowance == 0) {
++        TEST_info("mutcbk_inject_frames() not enough space to inject");
++        return 0;
++    }
++    bufsz += grow_allowance;
++
++    /* discard const */
++    OPENSSL_free((char *)mutctx->mutctx_iov.buf);
++    mutctx->mutctx_iov.buf = OPENSSL_malloc(bufsz);
++    /* discard const */
++    buf = (char *)mutctx->mutctx_iov.buf;
++    if (buf == NULL) {
++        TEST_info("mutcbk_inject_frames() OPENSSL_malloc() failed");
++        return 0;
++    }
++
++    for (i = 0; i < numin; i++) {
++        memcpy(buf, iovecin[i].buf, iovecin[i].buf_len);
++        buf += iovecin[i].buf_len;
++    }
++
++    /* discard const */
++    buf = (char *)mutctx->mutctx_iov.buf;
++    if (mutctx->mutctx_inject != NULL) {
++        memmove(buf + mutctx->mutctx_inject_sz, buf,
++            mutctx->mutctx_iov.buf_len);
++        memcpy(buf, mutctx->mutctx_inject, mutctx->mutctx_inject_sz);
++    }
++    /*
++     * perhaps needed to have not looked at yet
++     */
++    mutctx->mutctx_qhdrin = *hdrin;
++    *hdrout = &mutctx->mutctx_qhdrin;
++    mutctx->mutctx_iov.buf_len += mutctx->mutctx_inject_sz;
++    *iovecout = &mutctx->mutctx_iov;
++    *numout = 1;
++
++    return 1;
++}
++
++static void mutcbk_finish_injecct_frames(void *arg)
++{
++    struct mutcbk_ctx *mutctx = (struct mutcbk_ctx *)arg;
++
++    OPENSSL_free((char *)mutctx->mutctx_iov.buf);
++    mutctx->mutctx_iov.buf = NULL;
++}
++
++/* 16 path challenge frames */
++#define PATH_CHALLENGE_FRAMES \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"                \
++    "\x1a"                    \
++    "ABCDEFGH"
++
++DEF_FUNC(mount_flood)
++{
++    int ok = 0;
++    SSL *ssl;
++    QUIC_CHANNEL *ch;
++    static struct mutcbk_ctx mutctx = { 0 };
++    static const unsigned char *inject_frames = (const unsigned char *)PATH_CHALLENGE_FRAMES;
++
++    mutctx.mutctx_inject = inject_frames;
++    mutctx.mutctx_inject_sz = sizeof(PATH_CHALLENGE_FRAMES) - 1;
++    REQUIRE_SSL(ssl);
++    ch = ossl_quic_conn_get_channel(ssl);
++    if (!TEST_ptr(ch))
++        goto err;
++
++    if (!TEST_true(ossl_quic_channel_set_mutator(ch, mutcbk_inject_frames,
++            mutcbk_finish_injecct_frames, &mutctx)))
++        goto err;
++    ok = 1;
++err:
++    return ok;
++}
++
++DEF_FUNC(check_flood_stats)
++{
++    int ok = 0;
++    SSL *ssl;
++    QUIC_CHANNEL *ch;
++    uint64_t path_response_count;
++    uint64_t path_challenge_count;
++
++    REQUIRE_SSL(ssl);
++    ch = ossl_quic_conn_get_channel(ssl);
++    if (!TEST_ptr(ch))
++        goto err;
++
++    path_challenge_count = ossl_quic_channel_get_path_challenge_count(ch);
++    path_response_count = ossl_quic_channel_get_path_response_count(ch);
++
++    if (TEST_uint64_t_ne(path_challenge_count, 16))
++        goto err;
++    if (TEST_uint64_t_ne(path_response_count, 1))
++        goto err;
++
++    ok = 1;
++err:
++    return ok;
++}
++
++DEF_SCRIPT(check_pc_flood, "check path challenge flood")
++{
++    OP_SIMPLE_PAIR_CONN();
++    OP_SELECT_SSL(0, C);
++    OP_FUNC(mount_flood);
++    OP_ACCEPT_CONN_WAIT(L, S, 0);
++    OP_WRITE_B(C, "attack");
++    OP_SELECT_SSL(0, S);
++    OP_FUNC(check_flood_stats);
++}
++
+ /*
+  * List of Test Scripts
+  * ============================================================================
+@@ -302,5 +484,6 @@ static SCRIPT_INFO *const scripts[] = {
+     USE(simple_conn)
+         USE(simple_thread)
+             USE(ssl_poll)
+-                USE(check_cwm)
++    USE(check_cwm)
++    USE(check_pc_flood)
+ };

diff --git a/0065-CVE-2026-42764.patch b/0065-CVE-2026-42764.patch
new file mode 100644
index 0000000..daf7c59
--- /dev/null
+++ b/0065-CVE-2026-42764.patch
@@ -0,0 +1,17 @@
+diff --git a/ssl/quic/quic_port.c b/ssl/quic/quic_port.c
+index a1b134d9b16..b821b774b38 100644
+--- a/ssl/quic/quic_port.c
++++ b/ssl/quic/quic_port.c
+@@ -1806,8 +1806,10 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg,
+          * forget qrx so channel can create a new one
+          * with valid initial encryption level keys.
+          */
+-        qrx_src = qrx;
+-        qrx = NULL;
++        if (qrx != NULL) {
++            qrx_src = qrx;
++            qrx = NULL;
++        }
+     }
+ 
+     port_bind_channel(port, &e->peer, &scid, &hdr.dst_conn_id,

diff --git a/0066-CVE-2026-42766.patch b/0066-CVE-2026-42766.patch
new file mode 100644
index 0000000..bd1a052
--- /dev/null
+++ b/0066-CVE-2026-42766.patch
@@ -0,0 +1,241 @@
+From df32fc58bd726eecf88ac67b22fef0c8ff5eeef8 Mon Sep 17 00:00:00 2001
+From: Igor Ustinov <igus@openssl.foundation>
+Date: Thu, 21 May 2026 08:36:54 +0200
+Subject: [PATCH 1/2] 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 ac869a37f93..206ecd85e69 100644
+--- a/crypto/cms/cms_pwri.c
++++ b/crypto/cms/cms_pwri.c
+@@ -368,6 +368,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,
+
+From 1eb593cce1e05599d22de6ab158a23c755146fae Mon Sep 17 00:00:00 2001
+From: Igor Ustinov <igus@openssl.foundation>
+Date: Wed, 20 May 2026 20:02:43 +0200
+Subject: [PATCH 2/2] 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 <mayank.jangid.moon@gmail.com>
+Co-Authored-by: Kushal Khemka <kushalkhemka559@gmail.com>
+---
+ test/cms-msg/make_missing_kdf_der.py | 137 +++++++++++++++++++++++++++
+ test/cms-msg/missing-kdf.der         | Bin 0 -> 190 bytes
+ test/recipes/80-test_cms.t           |  21 +++-
+ 3 files changed, 157 insertions(+), 1 deletion(-)
+ create mode 100755 test/cms-msg/make_missing_kdf_der.py
+ create mode 100644 test/cms-msg/missing-kdf.der
+
+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 00000000000..5b3fc0f6eed
+--- /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/cms-msg/missing-kdf.der b/test/cms-msg/missing-kdf.der
+new file mode 100644
+index 0000000000000000000000000000000000000000..3db602e47c23b76a9a55a707da18c5059ef38c9e
+GIT binary patch
+literal 190
+zcmXqL+|9<R)#lOmotKfFc|qe^gT_@%jLe3OX^R_^nHU)iblA9|(wqX!oCdONoC$3n
+zjH%2lj9M%LjeqC+*0CO$x3i6Lf!y{7Pgo2D?Y_1wJ@|E12X~D{Lpyt#=(Ru5SM)zF
+zT>UakXma9=|F&g+-%70RE0WJSs>3LeamK&~VLc<7>0T^O8mGQjr>lr=GC8iQ@UuZ+
+V#SA9-u!z04PD_OT{BCf}9snrZM1BAO
+
+literal 0
+HcmV?d00001
+
+diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
+index 152a1a55a0a..160ad81ae38 100644
+--- a/test/recipes/80-test_cms.t
++++ b/test/recipes/80-test_cms.t
+@@ -56,7 +56,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
+ 
+ $no_rc2 = 1 if disabled("legacy");
+ 
+-plan tests => 31;
++plan tests => 32;
+ 
+ ok(run(test(["pkcs7_test"])), "test pkcs7");
+ 
+@@ -1702,3 +1702,22 @@ subtest "ML-KEM KEMRecipientInfo tests for CMS" => sub {
+            "accept CMS verify with SLH-DSA-SHAKE-256s");
+     }
+ };
++
++# 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 --git a/0067-CVE-2026-42767.patch b/0067-CVE-2026-42767.patch
new file mode 100644
index 0000000..68d5ef0
--- /dev/null
+++ b/0067-CVE-2026-42767.patch
@@ -0,0 +1,29 @@
+diff --git a/crypto/crmf/crmf_lib.c b/crypto/crmf/crmf_lib.c
+index d5ad51e4503..5747ab18569 100644
+--- a/crypto/crmf/crmf_lib.c
++++ b/crypto/crmf/crmf_lib.c
+@@ -762,6 +762,7 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *
+     EVP_CIPHER *cipher = NULL; /* used cipher */
+     int cikeysize = 0; /* key size from cipher */
+     unsigned char *iv = NULL; /* initial vector for symmetric encryption */
++    int iv_len; /* iv length */
+     unsigned char *out = NULL; /* decryption output buffer */
+     int n, ret = 0;
+     EVP_PKEY_CTX *pkctx = NULL; /* private key context */
+@@ -811,11 +812,12 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *
+     } else {
+         goto end;
+     }
+-    if ((iv = OPENSSL_malloc(EVP_CIPHER_get_iv_length(cipher))) == NULL)
++    iv_len = EVP_CIPHER_get_iv_length(cipher);
++    if ((iv = OPENSSL_malloc(iv_len)) == NULL)
+         goto end;
+-    if (ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv,
+-            EVP_CIPHER_get_iv_length(cipher))
+-        != EVP_CIPHER_get_iv_length(cipher)) {
++    if (enc->symmAlg->parameter == NULL
++        || ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv, iv_len)
++            != iv_len) {
+         ERR_raise(ERR_LIB_CRMF, CRMF_R_MALFORMED_IV);
+         goto end;
+     }

diff --git a/0068-CVE-2026-42768.patch b/0068-CVE-2026-42768.patch
new file mode 100644
index 0000000..aae774e
--- /dev/null
+++ b/0068-CVE-2026-42768.patch
@@ -0,0 +1,86 @@
+diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c
+index 3b0d5070ce5..e702703758a 100644
+--- a/crypto/cms/cms_env.c
++++ b/crypto/cms/cms_env.c
+@@ -644,13 +644,6 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
+     if (!ossl_cms_env_asn1_ctrl(ri, 1))
+         goto err;
+ 
+-    if (EVP_PKEY_is_a(pkey, "RSA"))
+-        /* upper layer CMS code incorrectly assumes that a successful RSA
+-         * decryption means that the key matches ciphertext (which never
+-         * was the case, implicit rejection or not), so to make it work
+-         * disable implicit rejection for RSA keys */
+-        EVP_PKEY_CTX_ctrl_str(ktri->pctx, "rsa_pkcs1_implicit_rejection", "0");
+-
+     if (evp_pkey_decrypt_alloc(ktri->pctx, &ek, &eklen, fixlen,
+             ktri->encryptedKey->data,
+             ktri->encryptedKey->length)
+diff --git a/crypto/pkcs7/pk7_doit.c b/crypto/pkcs7/pk7_doit.c
+index bc8028e1b19..7b6a3b36b4b 100644
+--- a/crypto/pkcs7/pk7_doit.c
++++ b/crypto/pkcs7/pk7_doit.c
+@@ -197,13 +197,6 @@ static int pkcs7_decrypt_rinfo(unsigned char **pek, int *peklen,
+     if (EVP_PKEY_decrypt_init(pctx) <= 0)
+         goto err;
+ 
+-    if (EVP_PKEY_is_a(pkey, "RSA"))
+-        /* upper layer pkcs7 code incorrectly assumes that a successful RSA
+-         * decryption means that the key matches ciphertext (which never
+-         * was the case, implicit rejection or not), so to make it work
+-         * disable implicit rejection for RSA keys */
+-        EVP_PKEY_CTX_ctrl_str(pctx, "rsa_pkcs1_implicit_rejection", "0");
+-
+     ret = evp_pkey_decrypt_alloc(pctx, &ek, &eklen, fixlen,
+         ri->enc_key->data, ri->enc_key->length);
+     if (ret <= 0)
+diff --git a/doc/man3/CMS_decrypt.pod b/doc/man3/CMS_decrypt.pod
+index 121b74a30a1..66a94287b6f 100644
+--- a/doc/man3/CMS_decrypt.pod
++++ b/doc/man3/CMS_decrypt.pod
+@@ -68,7 +68,7 @@ then the above behaviour is modified and an error B<is> returned if no
+ recipient encrypted key can be decrypted B<without> generating a random
+ content encryption key. Applications should use this flag with
+ B<extreme caution> especially in automated gateways as it can leave them
+-open to attack.
++open to attack. See L<EVP_PKEY_decrypt(3)> for more details.
+ 
+ It is possible to determine the correct recipient key by other means (for
+ example looking them up in a database) and setting them in the CMS structure
+@@ -103,7 +103,7 @@ mentioned in CMS_verify() also applies to CMS_decrypt().
+ 
+ =head1 SEE ALSO
+ 
+-L<ERR_get_error(3)>, L<CMS_encrypt(3)>
++L<ERR_get_error(3)>, L<CMS_encrypt(3)>, L<EVP_PKEY_decrypt(3)>
+ 
+ =head1 HISTORY
+ 
+diff --git a/doc/man3/PKCS7_decrypt.pod b/doc/man3/PKCS7_decrypt.pod
+index 5ba3cf1c17c..3534559d595 100644
+--- a/doc/man3/PKCS7_decrypt.pod
++++ b/doc/man3/PKCS7_decrypt.pod
+@@ -22,6 +22,14 @@ B<flags> is an optional set of flags.
+ Although the recipients certificate is not needed to decrypt the data it is needed
+ to locate the appropriate (of possible several) recipients in the PKCS#7 structure.
+ 
++When RSA PKCS#1 v1.5 Key Transport is in use, the invoked EVP_PKEY_decrypt()
++will use implicit rejection mechanism. It always returns the result of RSA
++decryption of the symmetric key to avoid Marvin attack. This result is
++deterministic and can happen to match the symmetric cipher used for the content
++encryption. In case when the certificate is not provided, the last
++RecipientInfo producing the key looking valid will be used. It may cause
++getting garbage content on decryption.
++
+ The following flags can be passed in the B<flags> parameter.
+ 
+ If the B<PKCS7_TEXT> flag is set MIME headers for type B<text/plain> are deleted
+@@ -40,7 +48,7 @@ be better if it could look up the correct key and certificate from a database.
+ 
+ =head1 SEE ALSO
+ 
+-L<ERR_get_error(3)>, L<PKCS7_encrypt(3)>
++L<ERR_get_error(3)>, L<PKCS7_encrypt(3)>, L<EVP_PKEY_decrypt(3)>
+ 
+ =head1 COPYRIGHT
+ 

diff --git a/0069-CVE-2026-42769.patch b/0069-CVE-2026-42769.patch
new file mode 100644
index 0000000..490229e
--- /dev/null
+++ b/0069-CVE-2026-42769.patch
@@ -0,0 +1,48 @@
+From 092f35598ea935c0808a97637f8f8f30256359af Mon Sep 17 00:00:00 2001
+From: Bob Beck <beck@openssl.org>
+Date: Fri, 17 Apr 2026 14:09:52 -0600
+Subject: [PATCH] Use the correct issuer when validating rootCAKeyUpdate
+
+This correctly uses the existing root, and not the same certificate
+as the root of the chain to validate.
+
+While we are here, we also turn on self signed certificate signature
+checking as this case is actually bringing in trust anchors as
+self signed certs, and fix a possible NULL deref.
+
+Fixes CVE-2026-42769
+---
+ crypto/cmp/cmp_genm.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/crypto/cmp/cmp_genm.c b/crypto/cmp/cmp_genm.c
+index 86bad3a7445..44e5eef291b 100644
+--- a/crypto/cmp/cmp_genm.c
++++ b/crypto/cmp/cmp_genm.c
+@@ -222,7 +222,7 @@ static int selfsigned_verify_cb(int ok, X509_STORE_CTX *store_ctx)
+         for (i = 0; i < sk_X509_num(trust); i++) {
+             issuer = sk_X509_value(trust, i);
+             if ((*check_issued)(store_ctx, cert, issuer)) {
+-                if (X509_add_cert(chain, cert, X509_ADD_FLAG_UP_REF))
++                if (X509_add_cert(chain, issuer, X509_ADD_FLAG_UP_REF))
+                     ok = 1;
+                 break;
+             }
+@@ -255,6 +255,7 @@ static int verify_ss_cert(OSSL_LIB_CTX *libctx, const char *propq,
+     if ((csc = X509_STORE_CTX_new_ex(libctx, propq)) == NULL
+         || !X509_STORE_CTX_init(csc, ts, target, untrusted))
+         goto err;
++    X509_STORE_CTX_set_flags(csc, X509_V_FLAG_CHECK_SS_SIGNATURE);
+     X509_STORE_CTX_set_verify_cb(csc, selfsigned_verify_cb);
+     ok = X509_verify_cert(csc) > 0;
+ 
+@@ -273,7 +274,8 @@ verify_ss_cert_trans(OSSL_CMP_CTX *ctx, X509 *trusted /* may be NULL */,
+     int res = 0;
+ 
+     if (trusted != NULL) {
+-        X509_VERIFY_PARAM *vpm = X509_STORE_get0_param(ts);
++        X509_VERIFY_PARAM *vpm = (ts == NULL) ? NULL
++                                              : X509_STORE_get0_param(ts);
+ 
+         if ((ts = X509_STORE_new()) == NULL)
+             return 0;

diff --git a/0070-CVE-2026-42770.patch b/0070-CVE-2026-42770.patch
new file mode 100644
index 0000000..4e8d2e6
--- /dev/null
+++ b/0070-CVE-2026-42770.patch
@@ -0,0 +1,21 @@
+diff --git a/providers/implementations/exchange/dh_exch.c b/providers/implementations/exchange/dh_exch.c
+index 64215208212..b7c21526590 100644
+--- a/providers/implementations/exchange/dh_exch.c
++++ b/providers/implementations/exchange/dh_exch.c
+@@ -155,12 +155,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 --git a/0071-CVE-2026-45445.patch b/0071-CVE-2026-45445.patch
new file mode 100644
index 0000000..13ff461
--- /dev/null
+++ b/0071-CVE-2026-45445.patch
@@ -0,0 +1,307 @@
+diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c
+index 1f6da4b2122..329d4bfa3e1 100644
+--- a/providers/implementations/ciphers/cipher_aes_ocb.c
++++ b/providers/implementations/ciphers/cipher_aes_ocb.c
+@@ -498,6 +498,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 f4bfeae2a1d..5cdf3122050 100644
+--- a/test/evp_extra_test.c
++++ b/test/evp_extra_test.c
+@@ -7119,6 +7119,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;
++}
++
+ static int test_aes_rc4_keylen_change_cve_2023_5363(void)
+ {
+     /* RC4 test data obtained from RFC 6229 */
+@@ -8030,6 +8294,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_pipeline);

diff --git a/0072-CVE-2026-45446.patch b/0072-CVE-2026-45446.patch
new file mode 100644
index 0000000..9e9c398
--- /dev/null
+++ b/0072-CVE-2026-45446.patch
@@ -0,0 +1,231 @@
+diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
+index 4b2117a94ca..452c9f00fea 100644
+--- a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
++++ b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
+@@ -58,6 +58,9 @@ static int aes_gcm_siv_initkey(void *vctx)
+     memset(&data, 0, sizeof(data));
+     memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE);
+ 
++    ctx->generated_tag = 0;
++    memset(ctx->tag, 0, TAG_SIZE);
++
+     /* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */
+     /* counter is stored little-endian */
+     for (i = 0; i < BLOCK_SIZE; i += 8) {
+@@ -134,17 +137,6 @@ static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx,
+     return 1;
+ }
+ 
+-static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
+-{
+-    int ret = 0;
+-
+-    if (ctx->enc)
+-        return ctx->generated_tag;
+-    ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
+-    ret &= ctx->have_user_tag;
+-    return ret;
+-}
+-
+ static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
+     unsigned char *out, size_t len)
+ {
+@@ -271,6 +263,19 @@ static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i
+     return !error;
+ }
+ 
++static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
++{
++    int ret = 0;
++
++    if (ctx->enc)
++        return ctx->generated_tag;
++    if (!ctx->generated_tag)
++        aes_gcm_siv_decrypt(ctx, NULL, NULL, 0);
++    ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
++    ret &= ctx->have_user_tag;
++    return ret;
++}
++
+ static int aes_gcm_siv_cipher(void *vctx, unsigned char *out,
+     const unsigned char *in, size_t len)
+ {
+diff --git a/providers/implementations/ciphers/cipher_aes_siv.c b/providers/implementations/ciphers/cipher_aes_siv.c
+index 38f6977bf77..f67c015f864 100644
+--- a/providers/implementations/ciphers/cipher_aes_siv.c
++++ b/providers/implementations/ciphers/cipher_aes_siv.c
+@@ -193,6 +193,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 (ossl_param_is_empty(params))
+         return 1;
+@@ -226,6 +227,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 f4bfeae2a1d..ebecb36cc93 100644
+--- a/test/evp_extra_test.c
++++ b/test/evp_extra_test.c
+@@ -7062,6 +7062,142 @@ static int test_aes_rc4_keylen_change_cve_2023_5363(void)
+ }
+ #endif
+ 
++static int test_aes_gcm_siv_empty_data(void)
++{
++    unsigned char key[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
++        0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
++    unsigned char nonce[12] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
++        0x22, 0x33, 0x44, 0x55 };
++    unsigned char aad[33] = "this AAD was never authenticated";
++    unsigned char zero_tag[16] = { 0 };
++    unsigned char real_tag[16];
++    unsigned char out[16];
++    int outl, ret = 0;
++    EVP_CIPHER_CTX *ctx = NULL;
++    EVP_CIPHER *c = EVP_CIPHER_fetch(NULL, "AES-128-GCM-SIV", NULL);
++
++    if (c == NULL) {
++        return TEST_skip("AES-128-GCM-SIV cipher is not available");
++    }
++
++    /* Compute the CORRECT tag for (key,nonce,aad,pt="") via encrypt */
++    ctx = EVP_CIPHER_CTX_new();
++    if (!TEST_ptr(ctx)
++        || !TEST_true(EVP_EncryptInit_ex2(ctx, c, key, nonce, NULL))
++        || !TEST_true(EVP_EncryptUpdate(ctx, NULL, &outl, aad, sizeof(aad))) /* AAD */
++        || !TEST_true(EVP_EncryptUpdate(ctx, out, &outl, aad, 0)) /* empty PT, out!=NULL */
++        || !TEST_true(EVP_EncryptFinal_ex(ctx, out, &outl))
++        || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, real_tag)))
++        goto err;
++    EVP_CIPHER_CTX_free(ctx);
++
++    /* SANITY: decrypt with CORRECT tag and an explicit empty-PT Update */
++    ctx = EVP_CIPHER_CTX_new();
++    if (!TEST_ptr(ctx)
++        || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
++        || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, real_tag))
++        || !TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad)))
++        || !TEST_true(EVP_DecryptUpdate(ctx, out, &outl, aad, 0)) /* force aes_gcm_siv_decrypt(len=0) */
++        || !TEST_true(EVP_DecryptFinal_ex(ctx, out, &outl)))
++        goto err;
++    EVP_CIPHER_CTX_free(ctx);
++
++    /* FORGERY A: AAD only, NO ciphertext Update, ALL-ZERO tag */
++    ctx = EVP_CIPHER_CTX_new();
++    if (!TEST_ptr(ctx)
++        || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
++        || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, zero_tag))
++        || !TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad))) /* AAD only, out==NULL */
++        || !TEST_false(EVP_DecryptFinal_ex(ctx, out, &outl)))
++        goto err;
++    EVP_CIPHER_CTX_free(ctx);
++
++    /* FORGERY B: no AAD, no Update at all, ALL-ZERO tag */
++    ctx = EVP_CIPHER_CTX_new();
++    if (!TEST_ptr(ctx)
++        || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
++        || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, zero_tag))
++        || !TEST_false(EVP_DecryptFinal_ex(ctx, out, &outl)))
++        goto err;
++    EVP_CIPHER_CTX_free(ctx);
++
++    /* CONTROL: AAD only, NO ciphertext Update, CORRECT tag */
++    ctx = EVP_CIPHER_CTX_new();
++    if (!TEST_ptr(ctx)
++        || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
++        || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, real_tag))
++        || !TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad)))
++        || !TEST_true(EVP_DecryptFinal_ex(ctx, out, &outl)))
++        goto err;
++    EVP_CIPHER_CTX_free(ctx);
++    ctx = NULL;
++
++    ret = 1;
++err:
++    EVP_CIPHER_CTX_free(ctx);
++
++    EVP_CIPHER_free(c);
++    return ret;
++}
++
++/*
++ * 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;
+@@ -8030,6 +8166,10 @@ int setup_tests(void)
+     ADD_TEST(test_aes_rc4_keylen_change_cve_2023_5363);
+ #endif
+ 
++    /* Test cases for CVE-2026-45446 */
++    ADD_TEST(test_aes_gcm_siv_empty_data);
++    ADD_TEST(test_aes_siv_ctx_reuse);
++
+     ADD_ALL_TESTS(test_aead_oneshot_roundtrip, 2 * OSSL_NELEM(aead_oneshot_cfgs));
+
+     ADD_TEST(test_invalid_ctx_for_digest);

diff --git a/0073-CVE-2026-45447.patch b/0073-CVE-2026-45447.patch
new file mode 100644
index 0000000..c7295a1
--- /dev/null
+++ b/0073-CVE-2026-45447.patch
@@ -0,0 +1,139 @@
+From 16aaeaab46d407f739fc74e3d89ae4fc43ef77c2 Mon Sep 17 00:00:00 2001
+From: Igor Ustinov <igus@openssl.foundation>
+Date: Sat, 16 May 2026 08:16:23 +0200
+Subject: [PATCH 1/2] Fix possible use-after-free in OpenSSL PKCS7_verify()
+
+Fixes CVE-2026-45447
+---
+ crypto/pkcs7/pk7_smime.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c
+index 4bf26331c1a..49129690deb 100644
+--- a/crypto/pkcs7/pk7_smime.c
++++ b/crypto/pkcs7/pk7_smime.c
+@@ -221,6 +221,7 @@ int PKCS7_verify(PKCS7 *p7, const STACK_OF(X509) *certs, X509_STORE *store,
+     int i, j = 0, k, ret = 0;
+     BIO *p7bio = NULL;
+     BIO *tmpout = NULL;
++    BIO *next = NULL;
+     const PKCS7_CTX *p7_ctx;
+ 
+     if (p7 == NULL) {
+@@ -351,9 +352,11 @@ int PKCS7_verify(PKCS7 *p7, const STACK_OF(X509) *certs, X509_STORE *store,
+         BIO_free(tmpout);
+     X509_STORE_CTX_free(cert_ctx);
+     OPENSSL_free(buf);
+-    if (indata != NULL)
+-        BIO_pop(p7bio);
+-    BIO_free_all(p7bio);
++    while (p7bio != NULL && p7bio != indata) {
++        next = BIO_pop(p7bio);
++        BIO_free(p7bio);
++        p7bio = next;
++    }
+     sk_X509_free(signers);
+     sk_X509_free(untrusted);
+     return ret;
+
+From a6622a0503575097f1faefc0781f5b3916bb3ffc Mon Sep 17 00:00:00 2001
+From: Igor Ustinov <igus@openssl.foundation>
+Date: Sat, 16 May 2026 08:22:53 +0200
+Subject: [PATCH 2/2] Test for CVE-2026-45447 (UAF in PKCS7_verify)
+
+The test data were created with a tool developed by
+Thai Duong <thai@calif.io>.
+---
+ 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 152a1a55a0a..cf76537200e 100644
+--- a/test/recipes/80-test_cms.t
++++ b/test/recipes/80-test_cms.t
+@@ -56,7 +56,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
+ 
+ $no_rc2 = 1 if disabled("legacy");
+ 
+-plan tests => 32;
++plan tests => 33;
+ 
+ ok(run(test(["pkcs7_test"])), "test pkcs7");
+ 
+@@ -1263,6 +1263,23 @@ subtest "CMS code signing test" => sub {
+        "fail verify CMS signature with code signing certificate for purpose smime_sign");
+ };
+ 
++# 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 00000000000..a6db2c38adf
+--- /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 --git a/0074-CVE-2026-34182.patch b/0074-CVE-2026-34182.patch
new file mode 100644
index 0000000..2aaa9b1
--- /dev/null
+++ b/0074-CVE-2026-34182.patch
@@ -0,0 +1,230 @@
+diff --git a/crypto/cms/cms_enc.c b/crypto/cms/cms_enc.c
+index 08afb5ab114..ba7082cebd7 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 --git a/test/cmsapitest.c b/test/cmsapitest.c
+index 0752d14df09..01c0c738bdc 100644
+--- a/test/cmsapitest.c
++++ b/test/cmsapitest.c
+@@ -22,6 +22,191 @@ static EVP_PKEY *privkey = 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;
+@@ -557,6 +742,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_CMS_add1_cert);
+     ADD_TEST(test_d2i_CMS_bio_NULL);
+     ADD_TEST(test_CMS_set1_key_mem_leak);

diff --git a/0075-asn1_d2i_read_bio-blocking.patch b/0075-asn1_d2i_read_bio-blocking.patch
new file mode 100644
index 0000000..24fa094
--- /dev/null
+++ b/0075-asn1_d2i_read_bio-blocking.patch
@@ -0,0 +1,215 @@
+From 738688d7620613d35db5bf80dbac56f16fd33b5e Mon Sep 17 00:00:00 2001
+From: Daniel Sands <dnsands@sandia.gov>
+Date: Thu, 12 Mar 2026 11:59:13 -0600
+Subject: [PATCH 1/3] Add intelligence to asn1_d2i_read_bio for reading entire
+ header without blocking for extra data
+
+Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
+Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
+
+(cherry picked from commit 35852da1d9e24cb74034b2f418cef3a58203b127)
+(cherry picked from commit 445b06163b0c466abebac6c2ac06917c3b9ec5db)
+(cherry picked from commit 53b2a52c9b337d8b8b5259758f7b7ea21f799131)
+
+Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
+MergeDate: Thu May  7 12:17:41 2026
+(Merged from https://github.com/openssl/openssl/pull/30986)
+---
+ crypto/asn1/a_d2i_fp.c | 57 +++++++++++++++++++++++++++++++++++++-----
+ crypto/asn1/asn1_lib.c |  2 +-
+ 2 files changed, 52 insertions(+), 7 deletions(-)
+
+diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
+index 82d2f7cc90..5892024687 100644
+--- a/crypto/asn1/a_d2i_fp.c
++++ b/crypto/asn1/a_d2i_fp.c
+@@ -104,7 +104,7 @@ void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
+ }
+ #endif
+ 
+-#define HEADER_SIZE 8
++#define HEADER_SIZE 2
+ #define ASN1_CHUNK_INITIAL_SIZE (16 * 1024)
+ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+ {
+@@ -138,7 +138,7 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+                 goto err;
+             }
+             i = BIO_read(in, &(b->data[len]), want);
+-            if (i < 0 && diff == 0) {
++            if (i <= 0) {
+                 ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA);
+                 goto err;
+             }
+@@ -154,12 +154,58 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+         }
+         /* else data already loaded */
+ 
++        /* make sure there is enough data for a complete header */
+         p = (unsigned char *)&(b->data[off]);
+         q = p;
+         diff = len - off;
+-        if (diff == 0)
++        if (diff < 2) {
++            /* Failed sanity check */
++            ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA);
+             goto err;
+-        inf = ASN1_get_object(&q, &slen, &tag, &xclass, diff);
++        }
++
++        diff--;
++        if ((*(q++) & V_ASN1_PRIMITIVE_TAG) == V_ASN1_PRIMITIVE_TAG) {
++            /* Multi-byte tag.  See if we have the whole thing yet */
++            do {
++                diff--;
++            } while (diff > 0 && *(q++) & 0x80);
++
++            if (diff == 0) {
++                /*
++                 * End of current data, will need at least 1 more byte for
++                 * length.  2 if the tag is still incomplete
++                 */
++                want = q - p + 2;
++                if (*q & 0x80) {
++                    want++;
++                }
++                continue;
++            }
++        }
++
++        /* Check the length.  This should also work for indefinite length */
++        diff--;
++        if (*q & 0x80) {
++            unsigned int n = *q & 0x7f;
++
++            if (n > sizeof(long)) {
++                ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG);
++                goto err;
++            }
++            if (n > diff) {
++                want = q - p + n + 1;
++                continue;
++            }
++        }
++
++        /*
++         * We have a complete header now, assuming we didn't hit EOF. Parse the
++         * tag and length
++         */
++        q = p;
++        diff = len - off;
++        inf = ASN1_get_object(&q, &slen, &tag, &xclass, (int)diff);
+         if (inf & 0x80) {
+             unsigned long e;
+ 
+@@ -169,8 +215,7 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+             ERR_pop_to_mark();
+             ERR_set_mark();
+         }
+-        i = q - p; /* header length */
+-        off += i; /* end of data */
++        off += q - p; /* end of data */
+ 
+         if (inf & 1) {
+             /* no data body so go round again */
+diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
+index ae234bb5ea..b2c796888f 100644
+--- a/crypto/asn1/asn1_lib.c
++++ b/crypto/asn1/asn1_lib.c
+@@ -129,7 +129,7 @@ static int asn1_get_length(const unsigned char **pp, int *inf, long *rl,
+         *inf = 0;
+         i = *p & 0x7f;
+         if (*p++ & 0x80) {
+-            if (max < i + 1)
++            if (max < i)
+                 return 0;
+             /* Skip leading zeroes */
+             while (i > 0 && *p == 0) {
+-- 
+2.54.0
+
+
+From d1ae010b2976c4a296e660de16a2a1236de6078f Mon Sep 17 00:00:00 2001
+From: Tomas Mraz <tomas@openssl.foundation>
+Date: Tue, 5 May 2026 17:01:42 +0200
+Subject: [PATCH 2/3] The tag value must fit into int
+
+We cannot allow an unbounded tag value as this is an O(n^2) algorithm
+and the tag cannot be larger than INT_MAX anyway.
+Fixes 35852da1d9e24cb74034b2f418cef3a58203b127
+
+Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
+Reviewed-by: Matt Caswell <matt@openssl.foundation>
+Reviewed-by: Simo Sorce <simo@redhat.com>
+MergeDate: Thu May  7 12:21:15 2026
+(Merged from https://github.com/openssl/openssl/pull/31091)
+---
+ crypto/asn1/a_d2i_fp.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
+index 5892024687..eed4bdf9cf 100644
+--- a/crypto/asn1/a_d2i_fp.c
++++ b/crypto/asn1/a_d2i_fp.c
+@@ -166,8 +166,15 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+ 
+         diff--;
+         if ((*(q++) & V_ASN1_PRIMITIVE_TAG) == V_ASN1_PRIMITIVE_TAG) {
++            unsigned int i = 0;
+             /* Multi-byte tag.  See if we have the whole thing yet */
+             do {
++                if (i > 4) {
++                    /* The tag value must fit into int */
++                    ERR_raise(ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG);
++                    goto err;
++                }
++                ++i;
+                 diff--;
+             } while (diff > 0 && *(q++) & 0x80);
+ 
+-- 
+2.54.0
+
+
+From 74cd48ff31ba6220e98e6ec601684e81f1ec768f Mon Sep 17 00:00:00 2001
+From: Tomas Mraz <tomas@openssl.foundation>
+Date: Thu, 7 May 2026 20:42:57 +0200
+Subject: [PATCH 3/3] asn1_d2i_read_bio(): Fix shadowing of declaration of i
+
+Rename inner i declaration to n.
+
+Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
+Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
+Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
+MergeDate: Thu May  7 19:04:13 2026
+(Merged from https://github.com/openssl/openssl/pull/31110)
+---
+ crypto/asn1/a_d2i_fp.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c
+index eed4bdf9cf..cf5901eed0 100644
+--- a/crypto/asn1/a_d2i_fp.c
++++ b/crypto/asn1/a_d2i_fp.c
+@@ -166,15 +166,15 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
+ 
+         diff--;
+         if ((*(q++) & V_ASN1_PRIMITIVE_TAG) == V_ASN1_PRIMITIVE_TAG) {
+-            unsigned int i = 0;
++            unsigned int n = 0;
+             /* Multi-byte tag.  See if we have the whole thing yet */
+             do {
+-                if (i > 4) {
++                if (n > 4) {
+                     /* The tag value must fit into int */
+                     ERR_raise(ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG);
+                     goto err;
+                 }
+-                ++i;
++                ++n;
+                 diff--;
+             } while (diff > 0 && *(q++) & 0x80);
+ 
+-- 
+2.54.0
+

diff --git a/openssl3.spec b/openssl3.spec
index 57a7437..ace4da4 100644
--- a/openssl3.spec
+++ b/openssl3.spec
@@ -29,7 +29,7 @@ print(string.sub(hash, 0, 16))
 Summary: Utilities from the general purpose cryptography library with TLS implementation
 Name: openssl3
 Version: 3.5.5
-Release: 1.1%{?dist}
+Release: 5%{?dist}
 Source0: openssl-%{version}.tar.gz
 Source1: fips-hmacify.sh
 Source3: genpatches
@@ -97,6 +97,24 @@ Patch0054: 0054-Temporarily-disable-SLH-DSA-FIPS-self-tests.patch
 Patch0055: 0055-Add-a-define-to-disable-symver-attributes.patch
 Patch0056: 0056-Add-targets-to-skip-build-of-non-installable-program.patch
 Patch0057: 0057-Disable-RSA-PKCS1.5-FIPS-POST-not-relevant-for-RHEL.patch
+Patch0058: 0058-CVE-2026-31790.patch
+Patch0059: 0059-CVE-2026-28390.patch
+Patch0060: 0060-CVE-2026-7383.patch
+Patch0061: 0061-CVE-2026-9076.patch
+Patch0062: 0062-CVE-2026-34180.patch
+Patch0063: 0063-CVE-2026-34181.patch
+Patch0064: 0064-CVE-2026-34183.patch
+Patch0065: 0065-CVE-2026-42764.patch
+Patch0066: 0066-CVE-2026-42766.patch
+Patch0067: 0067-CVE-2026-42767.patch
+Patch0068: 0068-CVE-2026-42768.patch
+Patch0069: 0069-CVE-2026-42769.patch
+Patch0070: 0070-CVE-2026-42770.patch
+Patch0071: 0071-CVE-2026-45445.patch
+Patch0072: 0072-CVE-2026-45446.patch
+Patch0073: 0073-CVE-2026-45447.patch
+Patch0074: 0074-CVE-2026-34182.patch
+Patch0075: 0075-asn1_d2i_read_bio-blocking.patch
 
 #The patches that are different for RHEL9 and 10 start here
 Patch0100: 0100-RHEL9-Allow-SHA1-in-seclevel-2-if-rh-allow-sha1-signatures.patch
@@ -115,6 +133,8 @@ BuildRequires: perl(Module::Load::Conditional), perl(File::Temp)
 BuildRequires: perl(Time::HiRes), perl(Time::Piece), perl(IPC::Cmd), perl(Pod::Html), perl(Digest::SHA)
 BuildRequires: perl(FindBin), perl(lib), perl(File::Compare), perl(File::Copy), perl(bigint)
 BuildRequires: git-core
+# Required for test/recipes/01-test_symbol_presence.t
+BuildRequires: gcc-toolset-12
 Requires: coreutils
 Requires: %{name}-libs%{?_isa} = %{version}-%{release}
 
@@ -158,6 +178,9 @@ from other formats to the formats used by the OpenSSL toolkit.
 %autosetup -S git -n openssl-%{version}
 
 %build
+# Required for test/recipes/01-test_symbol_presence.t
+. /opt/rh/gcc-toolset-12/enable
+
 # Figure out which flags we want to use.
 # default
 sslarch=%{_os}-%{_target_cpu}
@@ -251,12 +274,10 @@ for i in libcrypto.pc libssl.pc openssl.pc ; do
 done
 
 %check
-# Verify that what was compiled actually works.
+# Required for test/recipes/01-test_symbol_presence.t
+. /opt/rh/gcc-toolset-12/enable
 
-# Hack - some of these tests fail on epel8
-# we used to revert commit f2a49ef424f831aac988356fc8b2b910e443dc42 but
-# that no longer reverts cleanly
-rm test/recipes/01-test_symbol_presence.t
+# Verify that what was compiled actually works.
 
 # Hack - either enable SCTP AUTH chunks in kernel or disable sctp for check
 (sysctl net.sctp.addip_enable=1 && sysctl net.sctp.auth_enable=1) || \
@@ -366,7 +387,6 @@ install -m644 %{SOURCE9} \
 ln -s /etc/crypto-policies/back-ends/openssl_fips.config $RPM_BUILD_ROOT%{_sysconfdir}/pki/tls/fips_local.cnf
 
 # Transformation for openssl3
-# see openssl11.spec
 mkdir -p $RPM_BUILD_ROOT{%{_libdir},%{_includedir}}/%{name}/
 rm -f $RPM_BUILD_ROOT%{_sysconfdir}/pki/tls/openssl.cnf
 rm -f $RPM_BUILD_ROOT%{_sysconfdir}/pki/tls/ct_log_list.cnf
@@ -443,6 +463,42 @@ rm -f $RPM_BUILD_ROOT%{_bindir}/{make,renew}-dummy-cert
 %ldconfig_scriptlets libs
 
 %changelog
+* Sun Jul 26 2026 Robert Scheck <robert@fedoraproject.org> - 3.5.5-5
+- Merge changes from RHEL 9.8 to pick up CVE fixes
+
+* Thu Jun 25 2026 Pavol Žáčik <pzacik@redhat.com> - 1:3.5.5-5
+- Patch asn1_d2i_read_bio to read headers without blocking
+  Resolves: RHEL-178642
+
+* Mon Jun 01 2026 Dmitry Belyavskiy <dbelyavs@redhat.com> - 1:3.5.5-4
+Fix CVE-2026-7383, CVE-2026-9076, CVE-2026-34180, CVE-2026-34181,
+CVE-2026-34183, CVE-2026-42764, CVE-2026-42766, CVE-2026-42767, CVE-2026-42768,
+CVE-2026-42769, CVE-2026-42770, CVE-2026-45445, CVE-2026-45446, CVE-2026-45447,
+CVE-2026-34182.
+Resolves: RHEL-179274
+Resolves: RHEL-179287
+Resolves: RHEL-179539
+Resolves: RHEL-179544
+Resolves: RHEL-179547
+Resolves: RHEL-179552
+Resolves: RHEL-179556
+Resolves: RHEL-179633
+Resolves: RHEL-179661
+Resolves: RHEL-179678
+Resolves: RHEL-179684
+Resolves: RHEL-179688
+Resolves: RHEL-179691
+Resolves: RHEL-179696
+Resolves: RHEL-179699
+
+* Wed May 13 2026 Pavol Žáčik <pzacik@redhat.com> - 1:3.5.5-3
+- Fix CVE-2026-28390
+  Resolves: RHEL-165870
+
+* Thu Apr 09 2026 Pavol Žáčik <pzacik@redhat.com> - 1:3.5.5-2
+- Fix CVE-2026-31790
+  Resolves: RHEL-161586
+
 * Mon Feb 02 2026 Michel Lind <salimma@fedoraproject.org> - 3.5.5-1.1
 - Merge c9s openssl changes to pick up CVE fixes
   Resolves: RHBZ#2400650

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-26 15:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-26 15:38 [rpms/openssl3] epel8: Merge changes from RHEL 9.8 to pick up CVE fixes Robert Scheck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox