Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package openssl-3 for openSUSE:Factory checked in at 2026-06-13 18:46:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openssl-3 (Old) and /work/SRC/openSUSE:Factory/.openssl-3.new.1981 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "openssl-3" Sat Jun 13 18:46:41 2026 rev:51 rq:1358678 version:3.5.3 Changes: -------- --- /work/SRC/openSUSE:Factory/openssl-3/openssl-3.changes 2026-05-16 19:24:04.204919759 +0200 +++ /work/SRC/openSUSE:Factory/.openssl-3.new.1981/openssl-3.changes 2026-06-13 18:48:03.531227609 +0200 @@ -1,0 +2,34 @@ +Mon Jun 8 13:21:41 UTC 2026 - Angel Yankov <[email protected]> + +- Security fixes: + * CVE-2026-45447: Heap Use-After-Free in OpenSSL PKCS7_verify() (bsc#1266357) + * CVE-2026-45446: Incorrect Tag Processing for Empty Messages in AES-GCM-SIV and AES-SIV modes (bsc#1266356) + * CVE-2026-42770: FFC-DH Peer Validation Uses Attacker-Supplied q (bsc#1266353) + * CVE-2026-45445: AES-OCB IV Ignored on EVP_Cipher() Path (bsc#1266355) + * CVE-2026-42767: NULL Pointer Dereference in CRMF EncryptedValue Decryption (bsc#1266350) + * CVE-2026-42768: Multi-RecipientInfo Bleichenbacher Oracle in CMS_decrypt() and PKCS7_decrypt() (bsc#1266351) + * CVE-2026-42769: Trust-Anchor Substitution via cert/issuer Typo in CMP rootCaKeyUpdate (bsc#1266352) + * CVE-2026-42766: Possible NULL Dereference in Password-Based CMS Decryption (bsc#1266349) + * CVE-2026-34183: Unbounded Memory Growth in the QUIC PATH_CHALLENGE Handler (bsc#1266345) + * CVE-2026-42764: NULL pointer dereference in QUIC server initial packet handling (bsc#1266347) + * CVE-2026-34182: CMS AuthEnvelopedData Processing May Accept Forged Messages (bsc#1266344) + * CVE-2026-9076: Out-of-Bounds Read in CMS Password-Based Decryption (bsc#1266341) + * CVE-2026-7383: Possible Heap Buffer Overflow in ASN.1 Multibyte String Conversion (bsc#1266340) + * CVE-2026-34180: Heap Buffer Over-read in ASN.1 Content Parsing (bsc#1266342) + * Add patches: + openssl-CVE-2026-45447.patch + openssl-CVE-2026-45446.patch + openssl-CVE-2026-42770.patch + openssl-CVE-2026-45445.patch + openssl-CVE-2026-42767.patch + openssl-CVE-2026-42768.patch + openssl-CVE-2026-42769.patch + openssl-CVE-2026-42766.patch + openssl-CVE-2026-34183.patch + openssl-CVE-2026-42764.patch + openssl-CVE-2026-34182.patch + openssl-CVE-2026-9076.patch + openssl-CVE-2026-7383.patch + openssl-CVE-2026-34180.patch + +------------------------------------------------------------------- New: ---- openssl-CVE-2026-34180.patch openssl-CVE-2026-34182.patch openssl-CVE-2026-34183.patch openssl-CVE-2026-42764.patch openssl-CVE-2026-42766.patch openssl-CVE-2026-42767.patch openssl-CVE-2026-42768.patch openssl-CVE-2026-42769.patch openssl-CVE-2026-42770.patch openssl-CVE-2026-45445.patch openssl-CVE-2026-45446.patch openssl-CVE-2026-45447.patch openssl-CVE-2026-7383.patch openssl-CVE-2026-9076.patch ----------(New B)---------- New: openssl-CVE-2026-7383.patch openssl-CVE-2026-34180.patch New: openssl-CVE-2026-42764.patch openssl-CVE-2026-34182.patch openssl-CVE-2026-9076.patch New: openssl-CVE-2026-42766.patch openssl-CVE-2026-34183.patch openssl-CVE-2026-42764.patch New: openssl-CVE-2026-34183.patch openssl-CVE-2026-42764.patch openssl-CVE-2026-34182.patch New: openssl-CVE-2026-42769.patch openssl-CVE-2026-42766.patch openssl-CVE-2026-34183.patch New: openssl-CVE-2026-45445.patch openssl-CVE-2026-42767.patch openssl-CVE-2026-42768.patch New: openssl-CVE-2026-42767.patch openssl-CVE-2026-42768.patch openssl-CVE-2026-42769.patch New: openssl-CVE-2026-42768.patch openssl-CVE-2026-42769.patch openssl-CVE-2026-42766.patch New: openssl-CVE-2026-45446.patch openssl-CVE-2026-42770.patch openssl-CVE-2026-45445.patch New: openssl-CVE-2026-42770.patch openssl-CVE-2026-45445.patch openssl-CVE-2026-42767.patch New: openssl-CVE-2026-45447.patch openssl-CVE-2026-45446.patch openssl-CVE-2026-42770.patch New: * Add patches: openssl-CVE-2026-45447.patch openssl-CVE-2026-45446.patch New: openssl-CVE-2026-9076.patch openssl-CVE-2026-7383.patch openssl-CVE-2026-34180.patch New: openssl-CVE-2026-34182.patch openssl-CVE-2026-9076.patch openssl-CVE-2026-7383.patch ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openssl-3.spec ++++++ --- /var/tmp/diff_new_pack.hYFhHK/_old 2026-06-13 18:48:09.695483703 +0200 +++ /var/tmp/diff_new_pack.hYFhHK/_new 2026-06-13 18:48:09.699483869 +0200 @@ -178,6 +178,34 @@ Patch74: openssl-CVE-2026-28390.patch # PATCH-FIX-UPSTREAM: Optimized MLKEM NTT, supports p8 (ISA 2.07) and above architectures (jsc#PED-14569) Patch75: openssl-ppc64le-Optimized-MLKEM-NTT-supports-p8-ISA-2.07-and-above-architectures.patch +# PATCH-FIX-UPSTREAM: CVE-2026-45447 Heap Use-After-Free in OpenSSL PKCS7_verify() (bsc#1266357) +Patch76: openssl-CVE-2026-45447.patch +# PATCH-FIX-UPSTREAM: CVE-2026-45446 Incorrect Tag Processing for Empty Messages in AES-GCM-SIV and AES-SIV modes (bsc#1266356) +Patch77: openssl-CVE-2026-45446.patch +# PATCH-FIX-UPSTREAM: CVE-2026-42770 FFC-DH Peer Validation Uses Attacker-Supplied q (bsc#1266353) +Patch78: openssl-CVE-2026-42770.patch +# PATCH-FIX-UPSTREAM: CVE-2026-45445 AES-OCB IV Ignored on EVP_Cipher() Path (bsc#1266355) +Patch79: openssl-CVE-2026-45445.patch +# PATCH-FIX-UPSTREAM: CVE-2026-42767 NULL Pointer Dereference in CRMF EncryptedValue Decryption (bsc#1266350) +Patch80: openssl-CVE-2026-42767.patch +# PATCH-FIX-UPSTREAM: CVE-2026-42768 Multi-RecipientInfo Bleichenbacher Oracle in CMS_decrypt() and PKCS7_decrypt() (bsc#1266351) +Patch81: openssl-CVE-2026-42768.patch +# PATCH-FIX-UPSTREAM: CVE-2026-42769 Trust-Anchor Substitution via cert/issuer Typo in CMP rootCaKeyUpdate (bsc#1266352) +Patch82: openssl-CVE-2026-42769.patch +# PATCH-FIX-UPSTREAM: CVE-2026-42766 Possible NULL Dereference in Password-Based CMS Decryption (bsc#1266349) +Patch83: openssl-CVE-2026-42766.patch +# PATCH-FIX-UPSTREAM: CVE-2026-34183 Unbounded Memory Growth in the QUIC PATH_CHALLENGE Handler (bsc#1266345) +Patch84: openssl-CVE-2026-34183.patch +# PATCH-FIX-UPSTREAM: CVE-2026-42764 NULL pointer dereference in QUIC server initial packet handling (bsc#1266347) +Patch85: openssl-CVE-2026-42764.patch +# PATCH-FIX-UPSTREAM: CVE-2026-34182 CMS AuthEnvelopedData Processing May Accept Forged Messages (bsc#1266344) +Patch86: openssl-CVE-2026-34182.patch +# PATCH-FIX-UPSTREAM: CVE-2026-9076 Out-of-Bounds Read in CMS Password-Based Decryption (bsc#1266341) +Patch88: openssl-CVE-2026-9076.patch +# PATCH-FIX-UPSTREAM: CVE-2026-7383 Possible Heap Buffer Overflow in ASN.1 Multibyte String Conversion (bsc#1266340) +Patch89: openssl-CVE-2026-7383.patch +# PATCH-FIX-UPSTREAM: CVE-2026-34180 Heap Buffer Over-read in ASN.1 Content Parsing (bsc#1266342) +Patch90: openssl-CVE-2026-34180.patch # ulp-macros is available according to SUSE version. %if 0%{?sle_version} >= 150400 || 0%{?suse_version} >= 1540 ++++++ openssl-CVE-2026-34180.patch ++++++ commit e2087fe2faba50ec14f5722c7952262b7f0cc4d2 Author: Viktor Dukhovni <[email protected]> Date: Tue Apr 7 22:40:55 2026 +1000 Avoid length truncation in ASN1_STRING_set The ASN1_STRING_set() function takes an `int` length, make sure the argument is not inadvertently truncated when it is called from asn1_ex_c2i(). Fixes CVE-2026-34180 Index: openssl-3.5.0/crypto/asn1/tasn_dec.c =================================================================== --- openssl-3.5.0.orig/crypto/asn1/tasn_dec.c +++ openssl-3.5.0/crypto/asn1/tasn_dec.c @@ -59,7 +59,7 @@ static int asn1_d2i_ex_primitive(ASN1_VA 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 */ @@ -828,19 +828,24 @@ static int asn1_d2i_ex_primitive(ASN1_VA /* 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) { @@ -858,7 +863,8 @@ static int asn1_ex_c2i(ASN1_VALUE **pval } 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; @@ -913,6 +919,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval 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; @@ -943,10 +953,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval } /* 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; ++++++ openssl-CVE-2026-34182.patch ++++++ commit e36e3314518f0eaa5bf31e1ee16941e6758baac5 Author: Neil Horman <[email protected]> Date: Fri Apr 17 13:21:50 2026 -0400 Reject potentially forged encrypted CMS AuthEnvelopedData messages 1. Adjust ossl_cms_EncryptedContent_init_bio to not accept non-AEAD ciphers. If a forged CMS message with AuthEnvelopedData is received with a non-AEAD cipher specified, we silently accept that and decrypt the message, skipping any authentication, which violates RFC 5083. We also add checks to ensure we fail if we try to encrypt AuthEnvelopedData without using an AEAD cipher. 2. Ensure that tag lengths on cms AEAD data is the recommended size. RFC 5084 recommends that mac tags be at least 12 bytes for AES-GCM and 4 bytes for AES-CCM on AuthEnvelopedData. As this code is not algorith-specific we add a check for a minimal size and just use the lower limit which is sufficient to prevent this attack. Without this check, its possible to set the tag length to 1 and within 256 guesses, forge a CMS message. Fixes CVE-2026-34182 Index: openssl-3.5.3/crypto/cms/cms_enc.c =================================================================== --- openssl-3.5.3.orig/crypto/cms/cms_enc.c +++ openssl-3.5.3/crypto/cms/cms_enc.c @@ -105,9 +105,10 @@ BIO *ossl_cms_EncryptedContent_init_bio( } if ((EVP_CIPHER_get_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) { 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; } ++++++ openssl-CVE-2026-34183.patch ++++++ commit 932bf91b183ac0e107c27df162876afa0f3c6401 Author: Alexandr Nedvedicky <[email protected]> Date: Thu Mar 26 14:24:32 2026 +0100 QUIC stack must limit the number of PATH_CHALLENGE frames processed in RX Currently local QUIC stack allocates PATH_RESPONSE frame for every PATH_CHALLENGE frame it receives in single packet from its remote peer. The memory with PATH_RESPONSE frame is released after local QUIC stack receives an ACK which confirms reception of PATH_RESPONSE by remote peer. This gives remote peer too much control over memory resources local QUIC stack may consume. Quoting RFC 9000 section 9.2.1: ...an endpoint SHOULD NOT send multiple PATH_CHALLENGE frames in a single packet. Limiting the number of PATCH_CHALLENGE frames to 1 per QUIC packet received helps to reduce heap memory overhead required to process PATH_CHALLENGE frame. Currently QUIC ACKM (ACK-manager) keeps all frames in retransmission buffer until ACK is received. It can be changed such frames which don't need to be ACKed don't need to be kept in retrans buffer, those can be released right after transmission. Fixes CVE-2026-34183 Index: openssl-3.5.3/include/internal/quic_cfq.h =================================================================== --- openssl-3.5.3.orig/include/internal/quic_cfq.h +++ openssl-3.5.3/include/internal/quic_cfq.h @@ -151,4 +151,5 @@ QUIC_CFQ_ITEM *ossl_quic_cfq_item_get_pr # endif +int ossl_quic_cfq_discard_unreliable(QUIC_CFQ *cfq, QUIC_CFQ_ITEM *item); #endif Index: openssl-3.5.3/include/internal/quic_channel.h =================================================================== --- openssl-3.5.3.orig/include/internal/quic_channel.h +++ openssl-3.5.3/include/internal/quic_channel.h @@ -472,4 +472,5 @@ int ossl_quic_bind_channel(QUIC_CHANNEL # endif +void ossl_ch_reset_rx_state(QUIC_CHANNEL *ch); #endif Index: openssl-3.5.3/include/internal/quic_fifd.h =================================================================== --- openssl-3.5.3.orig/include/internal/quic_fifd.h +++ openssl-3.5.3/include/internal/quic_fifd.h @@ -85,4 +85,5 @@ void ossl_quic_fifd_set_qlog_cb(QUIC_FIF # endif +void ossl_quic_fifd_pkt_discard_unreliable(QUIC_FIFD *fifd, QUIC_TXPIM_PKT *tpkt); #endif Index: openssl-3.5.3/ssl/quic/quic_cfq.c =================================================================== --- openssl-3.5.3.orig/ssl/quic/quic_cfq.c +++ openssl-3.5.3/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" @@ -306,6 +307,20 @@ void ossl_quic_cfq_mark_lost(QUIC_CFQ *c } } +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. Index: openssl-3.5.3/ssl/quic/quic_channel.c =================================================================== --- openssl-3.5.3.orig/ssl/quic/quic_channel.c +++ openssl-3.5.3/ssl/quic/quic_channel.c @@ -2219,6 +2219,12 @@ static void ch_rx_check_forged_pkt_limit "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) { Index: openssl-3.5.3/ssl/quic/quic_channel_local.h =================================================================== --- openssl-3.5.3.orig/ssl/quic/quic_channel_local.h +++ openssl-3.5.3/ssl/quic/quic_channel_local.h @@ -13,6 +13,28 @@ # 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 * ====================== * @@ -458,6 +480,19 @@ 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 +502,11 @@ 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; }; # endif Index: openssl-3.5.3/ssl/quic/quic_fifd.c =================================================================== --- openssl-3.5.3.orig/ssl/quic/quic_fifd.c +++ openssl-3.5.3/ssl/quic/quic_fifd.c @@ -310,3 +310,46 @@ void ossl_quic_fifd_set_qlog_cb(QUIC_FIF 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; + } + } +} Index: openssl-3.5.3/ssl/quic/quic_rx_depack.c =================================================================== --- openssl-3.5.3.orig/ssl/quic/quic_rx_depack.c +++ openssl-3.5.3/ssl/quic/quic_rx_depack.c @@ -932,6 +932,12 @@ static int depack_do_frame_retire_conn_i 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--; + OPENSSL_free(buf); } @@ -952,33 +958,39 @@ static int depack_do_frame_path_challeng 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 (!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 (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 (!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++; + } return 1; @@ -1432,7 +1444,7 @@ int ossl_quic_handle_frames(QUIC_CHANNEL 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)); Index: openssl-3.5.3/ssl/quic/quic_txp.c =================================================================== --- openssl-3.5.3.orig/ssl/quic/quic_txp.c +++ openssl-3.5.3/ssl/quic/quic_txp.c @@ -3147,6 +3147,8 @@ static int txp_pkt_commit(OSSL_QUIC_TX_P --probe_info->pto[pn_space]; } + ossl_quic_fifd_pkt_discard_unreliable(&txp->fifd, tpkt); + return rc; } ++++++ openssl-CVE-2026-42764.patch ++++++ commit dad76b993a1a9f93c2ba39c013c138d3ff239db3 Author: Alexandr Nedvedicky <[email protected]> Date: Tue May 12 16:12:40 2026 +0200 Fix NULL dereference in QUIC address validation QUIC server crashes when address validation (RFC 9000, Section 8.1) is disabled and client sends initial packet with invalid token. Issue reported and fix submitted by Sunwoo Lee (KENTECH), Hyuk Lim (KENTECH) and Seunghyun Yoon (KENTECH) Fixes CVE-2026-42764 Index: openssl-3.5.0/ssl/quic/quic_port.c =================================================================== --- openssl-3.5.0.orig/ssl/quic/quic_port.c +++ openssl-3.5.0/ssl/quic/quic_port.c @@ -1651,8 +1651,10 @@ static void port_default_packet_handler( * 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, ++++++ openssl-CVE-2026-42766.patch ++++++ commit df32fc58bd726eecf88ac67b22fef0c8ff5eeef8 Author: Igor Ustinov <[email protected]> Date: Thu May 21 08:36:54 2026 +0200 Fix potential NULL dereference processing CMS PasswordRecipientInfo Avoid NULL dereferencing when keyDerivationAlgorithm is absent in CMS PasswordRecipientInfo. Fixes CVE-2026-42766 Index: openssl-3.5.0/crypto/cms/cms_pwri.c =================================================================== --- openssl-3.5.0.orig/crypto/cms/cms_pwri.c +++ openssl-3.5.0/crypto/cms/cms_pwri.c @@ -368,6 +368,11 @@ int ossl_cms_RecipientInfo_pwri_crypt(co algtmp = pwri->keyDerivationAlgorithm; /* 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, pwri->passlen, ++++++ openssl-CVE-2026-42767.patch ++++++ commit 43806050d3239fb99396858977e74de0ce8b69e4 Author: Igor Ustinov <[email protected]> Date: Mon May 11 16:29:47 2026 +0200 Fix potential NULL dereference in OSSL_CRMF_ENCRYPTEDVALUE_decrypt() Check that 'parameter' != NULL before dereferencing in OSSL_CRMF_ENCRYPTEDVALUE_decrypt(). Fixes CVE-2026-42767 Index: openssl-3.5.0/crypto/crmf/crmf_lib.c =================================================================== --- openssl-3.5.0.orig/crypto/crmf/crmf_lib.c +++ openssl-3.5.0/crypto/crmf/crmf_lib.c @@ -773,6 +773,7 @@ unsigned char 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 */ @@ -826,11 +827,13 @@ unsigned char } 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; } ++++++ openssl-CVE-2026-42768.patch ++++++ commit ffb3f5572387e360f6c0861161eff44b15c11bea Author: Dmitry Belyavskiy <[email protected]> Date: Fri May 15 14:09:17 2026 +0200 Enforce implicit rejection for CMS/PKCS#7 decryption Drop the disablement of the implicit rejection for RSA PKCS#1 v1.5 decryption. Fixes CVE-2026-42768 Index: openssl-3.5.0/crypto/cms/cms_env.c =================================================================== --- openssl-3.5.0.orig/crypto/cms/cms_env.c +++ openssl-3.5.0/crypto/cms/cms_env.c @@ -624,13 +624,6 @@ static int cms_RecipientInfo_ktri_decryp 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) <= 0) Index: openssl-3.5.0/crypto/pkcs7/pk7_doit.c =================================================================== --- openssl-3.5.0.orig/crypto/pkcs7/pk7_doit.c +++ openssl-3.5.0/crypto/pkcs7/pk7_doit.c @@ -205,13 +205,6 @@ static int pkcs7_decrypt_rinfo(unsigned 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) Index: openssl-3.5.0/doc/man3/CMS_decrypt.pod =================================================================== --- openssl-3.5.0.orig/doc/man3/CMS_decrypt.pod +++ openssl-3.5.0/doc/man3/CMS_decrypt.pod @@ -68,7 +68,7 @@ then the above behaviour is modified and 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 t =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 Index: openssl-3.5.0/doc/man3/PKCS7_decrypt.pod =================================================================== --- openssl-3.5.0.orig/doc/man3/PKCS7_decrypt.pod +++ openssl-3.5.0/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 @@ -43,7 +51,7 @@ mentioned in PKCS7_sign() also applies t =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 ++++++ openssl-CVE-2026-42769.patch ++++++ commit 092f35598ea935c0808a97637f8f8f30256359af Author: Bob Beck <[email protected]> Date: Fri Apr 17 14:09:52 2026 -0600 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 Index: openssl-3.5.0/crypto/cmp/cmp_genm.c =================================================================== --- openssl-3.5.0.orig/crypto/cmp/cmp_genm.c +++ openssl-3.5.0/crypto/cmp/cmp_genm.c @@ -204,7 +204,7 @@ static int selfsigned_verify_cb(int ok, 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; } @@ -237,6 +237,7 @@ static int verify_ss_cert(OSSL_LIB_CTX * 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; @@ -255,7 +256,8 @@ verify_ss_cert_trans(OSSL_CMP_CTX *ctx, 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; ++++++ openssl-CVE-2026-42770.patch ++++++ commit 00c4cdbe4f9578741be6979fc2fa9190980af857 Author: Norbert Pocs <[email protected]> Date: Tue May 12 15:16:04 2026 +0200 Match the local q DHX parameter against the peer's q As FFC/DH peer public key validation uses the peer's q value instead of checking against the local q, we must also check that these q values match when setting the peer's public key. Fixes CVE-2026-42770 Signed-off-by: Norbert Pocs <[email protected]> Index: openssl-3.5.3/providers/implementations/exchange/dh_exch.c =================================================================== --- openssl-3.5.3.orig/providers/implementations/exchange/dh_exch.c +++ openssl-3.5.3/providers/implementations/exchange/dh_exch.c @@ -122,7 +122,6 @@ static int digest_check(PROV_DH_CTX *ctx static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[]) { PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; - if (!ossl_prov_is_running() || pdhctx == NULL || vdh == NULL @@ -146,12 +145,15 @@ static int dh_init(void *vpdhctx, void * 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; ++++++ openssl-CVE-2026-45445.patch ++++++ commit 5ca975d368639e0f2dd99ce319dbd413d5fc8b8e Author: Viktor Dukhovni <[email protected]> Date: Mon May 18 18:09:44 2026 +1000 Apply the buffered IV on the AES-OCB EVP_Cipher() path aes_ocb_cipher(), the OCB provider's OSSL_FUNC_CIPHER_CIPHER slot, processed input without flushing the buffered IV into the OCB context. Effective nonce was 0 regardless of the caller's IV; EVP_*Final_ex() then emitted a tag depending only on (key, iv). This gave (key, nonce) reuse and single-query universal forgery on the EVP_Cipher() path. Apply update_iv() at the head of aes_ocb_cipher() to mirror the streaming handler. The matching GCM one-shot does this already. Add a cross-driver round-trip test for AES-{GCM,CCM,OCB} and ChaCha20-Poly1305 in test/evp_extra_test.c. Each cipher is exercised with and without AAD; the no-AAD case is needed because any prior EVP_CipherUpdate(NULL, aad, ...) routes through the streaming handler and applies the IV itself, masking the bug. Fixes CVE-2026-45445 Index: openssl-3.5.3/providers/implementations/ciphers/cipher_aes_ocb.c =================================================================== --- openssl-3.5.3.orig/providers/implementations/ciphers/cipher_aes_ocb.c +++ openssl-3.5.3/providers/implementations/ciphers/cipher_aes_ocb.c @@ -516,6 +516,19 @@ static int aes_ocb_cipher(void *vctx, un 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; Index: openssl-3.5.3/test/evp_extra_test.c =================================================================== --- openssl-3.5.3.orig/test/evp_extra_test.c +++ openssl-3.5.3/test/evp_extra_test.c @@ -4523,6 +4523,270 @@ err: 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; +} + #ifndef OPENSSL_NO_DES static int test_evp_iv_des(int idx) { @@ -7141,6 +7405,8 @@ int setup_tests(void) 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); ADD_TEST(test_evp_cipher_pipeline); ++++++ openssl-CVE-2026-45446.patch ++++++ commit e3171808327b6656e3d3a40e8880329efcd7c28c Author: Dmitry Belyavskiy <[email protected]> Date: Wed May 13 11:45:51 2026 +0200 Fix handling of empty-ciphertext messages in AES-GCM-SIV and AES-SIV AES-GCM-SIV: EVP_DecryptFinal_ex Accepts All-Zero Tag for Empty-Ciphertext Messages. AES-SIV: EVP_DecryptUpdate_ex Accepts All-Zero Tag for Empty-Ciphertext Messages on context reuse. Fixes CVE-2026-45446 Index: openssl-3.5.3/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c =================================================================== --- openssl-3.5.3.orig/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c +++ openssl-3.5.3/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c @@ -58,6 +58,9 @@ static int aes_gcm_siv_initkey(void *vct 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_ 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_ 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) { Index: openssl-3.5.3/providers/implementations/ciphers/cipher_aes_siv.c =================================================================== --- openssl-3.5.3.orig/providers/implementations/ciphers/cipher_aes_siv.c +++ openssl-3.5.3/providers/implementations/ciphers/cipher_aes_siv.c @@ -192,6 +192,7 @@ static int aes_siv_set_ctx_params(void * 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 * if (keylen != ctx->keylen) return 0; } + sctx->final_ret = -1; + return 1; } Index: openssl-3.5.3/test/evp_extra_test.c =================================================================== --- openssl-3.5.3.orig/test/evp_extra_test.c +++ openssl-3.5.3/test/evp_extra_test.c @@ -6579,6 +6579,142 @@ static int test_aes_rc4_keylen_change_cv } #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; @@ -7001,6 +7137,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_TEST(test_invalid_ctx_for_digest); ADD_TEST(test_evp_cipher_pipeline); ++++++ openssl-CVE-2026-45447.patch ++++++ commit 16aaeaab46d407f739fc74e3d89ae4fc43ef77c2 Author: Igor Ustinov <[email protected]> Date: Sat May 16 08:16:23 2026 +0200 Fix possible use-after-free in OpenSSL PKCS7_verify() Fixes CVE-2026-45447 diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c index 4bf26331c1..49129690de 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 @@ err: 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; ++++++ openssl-CVE-2026-7383.patch ++++++ >From f68e868d27cfd9ad8ef29db97cf8271bcaf4c541 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni <[email protected]> Date: Wed, 29 Apr 2026 18:23:24 +1000 Subject: [PATCH] Reject oversized inputs in ASN1_mbstring_ncopy() In ASN1_mbstring_ncopy() the destination size for BMPSTRING and UNIVERSALSTRING output was computed by a signed left shift on an int: outlen = nchar << 1; /* MBSTRING_BMP */ outlen = nchar << 2; /* MBSTRING_UNIV */ For nchar large enough the result is not representable in int. In the worst case (nchar == 0x40000000) nchar << 2 wraps to zero, OPENSSL_malloc(1) is called, and traverse_string() then writes 4*nchar bytes into the one-byte allocation: a heap buffer overflow. The MBSTRING_UTF8 path computes outlen by summing per-character byte counts in out_utf8(), and that sum can overflow the same int under similarly large inputs. Neither path is reachable from code that processes X.509 certificates through the DIRSTRING_TYPE mask used by ASN1_STRING_set_by_NID(): UNIVERSALSTRING is absent from that mask, and the UTF-8 sum requires inputs on the order of half a gigabyte. Reaching them needs an application that calls ASN1_mbstring_copy()/ASN1_mbstring_ncopy() directly, or registers a custom NID via ASN1_STRING_TABLE_add(), with an oversized attacker-controlled input. Add range checks before each shift and in out_utf8(), raising ASN1_R_STRING_TOO_LONG at the point of detection. Move the existing ASN1_R_INVALID_UTF8STRING raise into out_utf8() too so the two failure modes report distinct codes; the MBSTRING_UTF8 caller is left with cleanup only and now frees dest on error, matching the BMP/UNIV branches. Fixes CVE-2026-7383 --- crypto/asn1/a_mbstr.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) Index: openssl-3.5.3/crypto/asn1/a_mbstr.c =================================================================== --- openssl-3.5.3.orig/crypto/asn1/a_mbstr.c +++ openssl-3.5.3/crypto/asn1/a_mbstr.c @@ -81,9 +81,11 @@ int ASN1_mbstring_ncopy(ASN1_STRING **ou nchar = 0; /* This counts the characters and does utf8 syntax checking */ ret = traverse_string(in, len, MBSTRING_UTF8, in_utf8, &nchar); - if (ret < 0) { - ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING); - return -1; + if (ret < 0) { /* error already raised in out_utf8() */ + if (free_out) { + ASN1_STRING_free(dest); + *out = NULL; + } } break; @@ -171,11 +173,27 @@ int ASN1_mbstring_ncopy(ASN1_STRING **ou 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; @@ -263,9 +281,15 @@ static int out_utf8(unsigned long value, 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; } ++++++ openssl-CVE-2026-9076.patch ++++++ commit 21c9274e92faa25753c87ea874d4bd33e33a221f Author: Nikola Pajkovsky <[email protected]> Date: Thu May 21 11:53:09 2026 +0200 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 <[email protected]> Index: openssl-3.5.0/crypto/cms/cms_pwri.c =================================================================== --- openssl-3.5.0.orig/crypto/cms/cms_pwri.c +++ openssl-3.5.0/crypto/cms/cms_pwri.c @@ -206,7 +206,7 @@ static int kek_unwrap_key(unsigned char unsigned char *tmp; int outl, rv = 0; - if (blocklen == 0) + if (blocklen < 4) return 0; if (inlen < 2 * blocklen) {
