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-08-27 18:48:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openssl-3 (Old)
 and      /work/SRC/openSUSE:Factory/.openssl-3.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openssl-3"

Thu Aug 27 18:48:43 2026 rev:53 rq:1373819 version:3.5.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/openssl-3/openssl-3.changes      2026-07-24 
22:05:02.545080055 +0200
+++ /work/SRC/openSUSE:Factory/.openssl-3.new.1265/openssl-3.changes    
2026-08-27 18:48:44.417069434 +0200
@@ -1,0 +2,36 @@
+Mon Aug 24 10:15:53 UTC 2026 - Pedro Monreal <[email protected]>
+
+- Security fix:
+  * CVE-2026-75803: openssl: AEAD Forgeries with Empty Ciphertext
+    When Using EVP_Cipher() (bsc#1275837)
+  * Add openssl-CVE-2026-75803.patch
+
+-------------------------------------------------------------------
+Mon Aug 17 10:22:20 UTC 2026 - Pedro Monreal <[email protected]>
+
+- Security fixes in August 2026 release: (bsc#1274774)
+  * CVE-2026-14456: Unbounded Memory Growth in QUIC Server Incoming
+    Channel Queue (bsc#1274791)
+  * CVE-2026-14457: RPK Server Signature Algorithm Selection Can
+    Dereference a Missing Certificate (bsc#1274792)
+  * CVE-2026-18798: QUIC Server May Trigger Double Free When Processing
+    INITIAL Packet (bsc#1274777)
+  * CVE-2026-34181: PKCS#12 Files with PBMAC1 Are Accepted with Short
+    HMAC Keys (bsc#1266343)
+  * CVE-2026-54874: Excessive Memory Use Buffering DTLS Records for
+    a Future Epoch (bsc#1274795)
+  * CVE-2026-63072: Heap Buffer Overflow in CMS Key Unwrapping (bsc#1274788)
+  * CVE-2026-63073: Untrusted Sender DN Used as Format String in CMP
+    Response Validation (bsc#1274796)
+  * CVE-2026-63074: CMP Indefinite Cache Growth of ExtraCerts (bsc#1274797)
+  * CVE-2026-63075: QUIC ACK-only Packet Retention Can Cause Memory
+    Exhaustion (bsc#1274798)
+  * CVE-2026-63076: Invalid Pointer Dereference in CMP Server via Crafted
+    protectionAlg (bsc#1274790)
+  * Add patches: openssl-CVE-2026-14456.patch openssl-CVE-2026-14457.patch
+    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
+    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
+    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
+    openssl-CVE-2026-63075.patch openssl-CVE-2026-63076.patch
+
+-------------------------------------------------------------------

New:
----
  openssl-CVE-2026-14456.patch
  openssl-CVE-2026-14457.patch
  openssl-CVE-2026-18798.patch
  openssl-CVE-2026-34181.patch
  openssl-CVE-2026-54874.patch
  openssl-CVE-2026-63072.patch
  openssl-CVE-2026-63073.patch
  openssl-CVE-2026-63074.patch
  openssl-CVE-2026-63075.patch
  openssl-CVE-2026-63076.patch
  openssl-CVE-2026-75803.patch

----------(New B)----------
  New:    protectionAlg (bsc#1274790)
  * Add patches: openssl-CVE-2026-14456.patch openssl-CVE-2026-14457.patch
    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
  New:    protectionAlg (bsc#1274790)
  * Add patches: openssl-CVE-2026-14456.patch openssl-CVE-2026-14457.patch
    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
  New:  * Add patches: openssl-CVE-2026-14456.patch openssl-CVE-2026-14457.patch
    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
  New:  * Add patches: openssl-CVE-2026-14456.patch openssl-CVE-2026-14457.patch
    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
  New:    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
  New:    openssl-CVE-2026-18798.patch openssl-CVE-2026-34181.patch
    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
  New:    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
    openssl-CVE-2026-63075.patch openssl-CVE-2026-63076.patch
  New:    openssl-CVE-2026-54874.patch openssl-CVE-2026-63072.patch
    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
    openssl-CVE-2026-63075.patch openssl-CVE-2026-63076.patch
  New:    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
    openssl-CVE-2026-63075.patch openssl-CVE-2026-63076.patch
  New:    openssl-CVE-2026-63073.patch openssl-CVE-2026-63074.patch
    openssl-CVE-2026-63075.patch openssl-CVE-2026-63076.patch
  New:    When Using EVP_Cipher() (bsc#1275837)
  * Add openssl-CVE-2026-75803.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ openssl-3.spec ++++++
--- /var/tmp/diff_new_pack.keqMPQ/_old  2026-08-27 18:48:47.333171371 +0200
+++ /var/tmp/diff_new_pack.keqMPQ/_new  2026-08-27 18:48:47.335171441 +0200
@@ -208,6 +208,28 @@
 Patch90:        openssl-CVE-2026-34180.patch
 # PATCH-FIX-UPSTREAM: Grow the init_buf incrementally as we receive data 
(bsc#1271712)
 Patch91:        openssl-HollowByte.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-14456: Unbounded Memory Growth in QUIC Server 
Incoming Channel Queue (bsc#1274791)
+Patch92:        openssl-CVE-2026-14456.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-14457: RPK Server Signature Algorithm Selection 
Can Dereference a Missing Certificate (bsc#1274792)
+Patch93:        openssl-CVE-2026-14457.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-18798: QUIC Server May Trigger Double Free When 
Processing INITIAL Packet (bsc#1274777)
+Patch94:        openssl-CVE-2026-18798.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-34181: PKCS#12 Files with PBMAC1 Are Accepted 
with Short HMAC Keys (bsc#1266343)
+Patch95:        openssl-CVE-2026-34181.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-54874: Excessive Memory Use Buffering DTLS 
Records for a Future Epoch (bsc#1274795)
+Patch96:        openssl-CVE-2026-54874.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-63072: Heap Buffer Overflow in CMS Key 
Unwrapping (bsc#1274788)
+Patch97:        openssl-CVE-2026-63072.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-63073: Untrusted Sender DN Used as Format 
String in CMP Response Validation (bsc#1274796)
+Patch98:        openssl-CVE-2026-63073.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-63074: CMP Indefinite Cache Growth of 
ExtraCerts (bsc#1274797)
+Patch99:        openssl-CVE-2026-63074.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-63075: QUIC ACK-only Packet Retention Can Cause 
Memory Exhaustion (bsc#1274798)
+Patch100:       openssl-CVE-2026-63075.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-63076: Invalid Pointer Dereference in CMP 
Server via Crafted protectionAlg (bsc#1274790)
+Patch101:       openssl-CVE-2026-63076.patch
+# PATCH-FIX-UPSTREAM: CVE-2026-75803: AEAD Forgeries with Empty Ciphertext 
When Using EVP_Cipher() (bsc#1275837)
+Patch102:       openssl-CVE-2026-75803.patch
 
 # ulp-macros is available according to SUSE version.
 %if 0%{?sle_version} >= 150400 || 0%{?suse_version} >= 1540


++++++ openssl-CVE-2026-14456.patch ++++++
>From 08e7756c3900bcfd77a720e7b74e27d6e4ed01a9 Mon Sep 17 00:00:00 2001
From: Alexandr Nedvedicky <[email protected]>
Date: Thu, 23 Jul 2026 09:38:02 +0200
Subject: [PATCH] QUIC server: limit number of pending QUIC
 channels/connections

Currently, there is no limit for pending QUIC connections.  The port
default packet handler creates channel for every valid initial packet
which does belong to existing channel (a.k.a. connection).  The newly
created channel is inserted to list of pending channels where it waits
to be accepted by local application by call
to SSL_accept_connection(3ossl).

This change introduces a limit for pending connection.  The pending
queue is limited to 256 pending connections.  Applications may change
the limit by calling SSL_set_feature_request_uint(3ossl)
on SSL server listener object with configurable value
SSL_VALUE_QUIC_MAX_PENDING_CONNS.

Fixes: CVE-2026-14456

Reviewed-by: Eugene Syromiatnikov <[email protected]>
Reviewed-by: Andrew Dinh <[email protected]>
Reviewed-by: Neil Horman <[email protected]>
MergeDate: Wed Aug 12 15:00:25 2026
(Merged from https://github.com/openssl/openssl/pull/32052)

(cherry picked from commit 9416706d408bb84deb7cee4647bff3045d2dc7ba)
(cherry picked from commit 4084152e040329ca0194c4c1750b9b46d00a5b6b)
---
 doc/man3/SSL_get_value_uint.pod | 20 +++++++++++++++--
 include/internal/quic_port.h    |  4 ++++
 include/openssl/ssl.h.in        |  1 +
 ssl/quic/quic_impl.c            | 38 +++++++++++++++++++++++++++++++++
 ssl/quic/quic_port.c            | 16 ++++++++++++++
 ssl/quic/quic_port_local.h      |  1 +
 util/other.syms                 |  1 +
 7 files changed, 79 insertions(+), 2 deletions(-)

Index: openssl-3.5.0/doc/man3/SSL_get_value_uint.pod
===================================================================
--- openssl-3.5.0.orig/doc/man3/SSL_get_value_uint.pod
+++ openssl-3.5.0/doc/man3/SSL_get_value_uint.pod
@@ -12,6 +12,7 @@ SSL_VALUE_CLASS_FEATURE_REQUEST, SSL_VAL
 SSL_VALUE_CLASS_FEATURE_NEGOTIATED, SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL,
 SSL_VALUE_QUIC_STREAM_BIDI_REMOTE_AVAIL, SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL,
 SSL_VALUE_QUIC_STREAM_UNI_REMOTE_AVAIL, SSL_VALUE_QUIC_IDLE_TIMEOUT,
+SSL_VALUE_QUIC_MAX_PENDING_CONNS,
 SSL_VALUE_EVENT_HANDLING_MODE,
 SSL_VALUE_EVENT_HANDLING_MODE_INHERIT,
 SSL_VALUE_EVENT_HANDLING_MODE_EXPLICIT,
@@ -45,6 +46,7 @@ manage negotiable features and configura
  #define SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL
  #define SSL_VALUE_QUIC_STREAM_UNI_REMOTE_AVAIL
  #define SSL_VALUE_QUIC_IDLE_TIMEOUT
+ #define SSL_VALUE_QUIC_MAX_PENDING_CONNS
 
  #define SSL_VALUE_EVENT_HANDLING_MODE
  #define SSL_VALUE_EVENT_HANDLING_MODE_INHERIT
@@ -168,6 +170,16 @@ changed.
 This release of OpenSSL uses a default value of 30 seconds. This default value
 may change between releases of OpenSSL.
 
+=item B<SSL_VALUE_QUIC_MAX_PENDING_CONNS> (listener object)
+
+Generic value, sets the limit on channels (connection objects) which a QUIC 
server can
+insert into the list of pending connections. A pending connection is a 
connection
+which the local application needs to accept (L<SSL_accept_connection(3)>) in 
order to retrieve
+an SSL connection object. The connection is removed from the pending queue by 
a call
+to L<SSL_accept_connection(3)>. The default limit for pending connections is 
256. An INITIAL
+QUIC packet, which is received by a QUIC server with a full pending connections
+queue, is silently discarded. Setting the value to zero disables the limit.
+
 =item B<SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL> (connection object)
 
 Generic read-only statistical value. The number of bidirectional,
@@ -335,11 +347,15 @@ time.
 L<SSL_ctrl(3)>, L<SSL_get_accept_stream_queue_len(3)>,
 L<SSL_get_stream_read_state(3)>, L<SSL_get_stream_write_state(3)>,
 L<SSL_get_stream_read_error_code(3)>, L<SSL_get_stream_write_error_code(3)>,
-L<SSL_set_default_stream_mode(3)>, L<SSL_set_incoming_stream_policy(3)>
+L<SSL_set_default_stream_mode(3)>, L<SSL_set_incoming_stream_policy(3)>,
+L<SSL_accept_connection(3)>
 
 =head1 HISTORY
 
-These functions were added in OpenSSL 3.3.
+The value SSL_VALUE_QUIC_MAX_PENDING_CONNS has been added in OpenSSL 4.1
+and ported to older releases 4.0.2, 3.6.4 and 3.5.8.
+
+The remaining functions and values described here were all added in OpenSSL 
3.3.
 
 =head1 COPYRIGHT
 
Index: openssl-3.5.0/include/internal/quic_port.h
===================================================================
--- openssl-3.5.0.orig/include/internal/quic_port.h
+++ openssl-3.5.0/include/internal/quic_port.h
@@ -189,4 +189,8 @@ void ossl_quic_port_raise_net_error(QUIC
 
 # endif
 
+uint64_t ossl_quic_port_get_max_pending_channels(const QUIC_PORT *port);
+
+void ossl_quic_port_set_max_pending_channels(QUIC_PORT *port, uint64_t 
max_pending_channels);
+
 #endif
Index: openssl-3.5.0/include/openssl/ssl.h.in
===================================================================
--- openssl-3.5.0.orig/include/openssl/ssl.h.in
+++ openssl-3.5.0/include/openssl/ssl.h.in
@@ -2439,6 +2439,7 @@ __owur int SSL_get_conn_close_info(SSL *
 # define SSL_VALUE_STREAM_WRITE_BUF_SIZE            7
 # define SSL_VALUE_STREAM_WRITE_BUF_USED            8
 # define SSL_VALUE_STREAM_WRITE_BUF_AVAIL           9
+# define SSL_VALUE_QUIC_MAX_PENDING_CONNS 16
 
 # define SSL_VALUE_EVENT_HANDLING_MODE_INHERIT      0
 # define SSL_VALUE_EVENT_HANDLING_MODE_IMPLICIT     1
Index: openssl-3.5.0/ssl/quic/quic_impl.c
===================================================================
--- openssl-3.5.0.orig/ssl/quic/quic_impl.c
+++ openssl-3.5.0/ssl/quic/quic_impl.c
@@ -383,6 +383,11 @@ static int expect_quic_cs(const SSL *s,
     return expect_quic_as(s, ctx, QCTX_C | QCTX_S);
 }
 
+static int expect_quic_cl(const SSL *s, QCTX *ctx)
+{
+    return expect_quic_as(s, ctx, QCTX_C | QCTX_L);
+}
+
 static int expect_quic_csl(const SSL *s, QCTX *ctx)
 {
     return expect_quic_as(s, ctx, QCTX_C | QCTX_S | QCTX_L);
@@ -3591,6 +3596,33 @@ err:
 }
 
 QUIC_TAKES_LOCK
+static int qc_getset_max_pending_channels(QCTX *ctx, uint32_t class_,
+    uint64_t *p_value_out, uint64_t *p_value_in)
+{
+    int ret = 0;
+    uint64_t value_out = 0;
+
+    qctx_lock(ctx);
+
+    if (class_ == SSL_VALUE_CLASS_GENERIC && ctx->is_listener) {
+        value_out = ossl_quic_port_get_max_pending_channels(ctx->ql->port);
+        if (p_value_in != NULL)
+            ossl_quic_port_set_max_pending_channels(ctx->ql->port, 
*p_value_in);
+        ret = 1;
+    } else {
+        QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_UNSUPPORTED_CONFIG_VALUE_CLASS, 
NULL);
+        ret = 0;
+    }
+
+    qctx_unlock(ctx);
+
+    if (ret && p_value_out != NULL)
+        *p_value_out = value_out;
+
+    return ret;
+}
+
+QUIC_TAKES_LOCK
 static int qc_get_stream_avail(QCTX *ctx, uint32_t class_,
                                int is_uni, int is_remote,
                                uint64_t *value)
@@ -3723,6 +3755,8 @@ static int expect_quic_for_value(SSL *s,
     case SSL_VALUE_STREAM_WRITE_BUF_USED:
     case SSL_VALUE_STREAM_WRITE_BUF_AVAIL:
         return expect_quic_cs(s, ctx);
+    case SSL_VALUE_QUIC_MAX_PENDING_CONNS:
+        return expect_quic_cl(s, ctx);
     default:
         return expect_quic_conn_only(s, ctx);
     }
@@ -3744,6 +3778,8 @@ int ossl_quic_get_value_uint(SSL *s, uin
     switch (id) {
     case SSL_VALUE_QUIC_IDLE_TIMEOUT:
         return qc_getset_idle_timeout(&ctx, class_, value, NULL);
+    case SSL_VALUE_QUIC_MAX_PENDING_CONNS:
+        return qc_getset_max_pending_channels(&ctx, class_, value, NULL);
 
     case SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL:
         return qc_get_stream_avail(&ctx, class_, /*uni=*/0, /*remote=*/0, 
value);
@@ -3790,6 +3826,8 @@ int ossl_quic_set_value_uint(SSL *s, uin
 
     case SSL_VALUE_EVENT_HANDLING_MODE:
         return qc_getset_event_handling(&ctx, class_, NULL, &value);
+    case SSL_VALUE_QUIC_MAX_PENDING_CONNS:
+        return qc_getset_max_pending_channels(&ctx, class_, NULL, &value);
 
     default:
         return QUIC_RAISE_NON_NORMAL_ERROR(&ctx,
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
@@ -93,6 +93,8 @@ typedef struct validation_token {
  */
 #define ENCRYPTED_TOKEN_MAX_LEN (MARSHALLED_TOKEN_MAX_LEN + 16 + 12)
 
+#define DEFAULT_MAX_PENDING_CONNS 256
+
 DEFINE_LIST_OF_IMPL(ch, QUIC_CHANNEL);
 DEFINE_LIST_OF_IMPL(incoming_ch, QUIC_CHANNEL);
 DEFINE_LIST_OF_IMPL(port, QUIC_PORT);
@@ -110,6 +112,7 @@ QUIC_PORT *ossl_quic_port_new(const QUIC
     port->validate_addr = args->do_addr_validation;
     port->get_conn_user_ssl = args->get_conn_user_ssl;
     port->user_ssl_arg = args->user_ssl_arg;
+    port->max_pending_channels = DEFAULT_MAX_PENDING_CONNS;
 
     if (!port_init(port)) {
         OPENSSL_free(port);
@@ -1560,6 +1563,9 @@ static void port_default_packet_handler(
     if (hdr.type != QUIC_PKT_TYPE_INITIAL)
         goto undesirable;
 
+    if (port->max_pending_channels > 0 && 
ossl_list_incoming_ch_num(&port->incoming_channel_list) >= 
port->max_pending_channels)
+        goto undesirable;
+
     odcid.id_len = 0;
 
     /*
@@ -1738,3 +1744,13 @@ void ossl_quic_port_restore_err_state(co
     ERR_clear_error();
     OSSL_ERR_STATE_restore(port->err_state);
 }
+
+uint64_t ossl_quic_port_get_max_pending_channels(const QUIC_PORT *port)
+{
+    return port->max_pending_channels;
+}
+
+void ossl_quic_port_set_max_pending_channels(QUIC_PORT *port, uint64_t 
max_pending_channels)
+{
+    port->max_pending_channels = max_pending_channels;
+}
Index: openssl-3.5.0/ssl/quic/quic_port_local.h
===================================================================
--- openssl-3.5.0.orig/ssl/quic/quic_port_local.h
+++ openssl-3.5.0/ssl/quic/quic_port_local.h
@@ -116,6 +116,7 @@ struct quic_port_st {
 
     /* AES-256 GCM context for token encryption */
     EVP_CIPHER_CTX *token_ctx;
+    uint64_t max_pending_channels;
 };
 
 # endif
Index: openssl-3.5.0/util/other.syms
===================================================================
--- openssl-3.5.0.orig/util/other.syms
+++ openssl-3.5.0/util/other.syms
@@ -781,6 +781,7 @@ SSL_VALUE_CLASS_FEATURE_REQUEST
 SSL_VALUE_CLASS_FEATURE_PEER_REQUEST    define
 SSL_VALUE_CLASS_FEATURE_NEGOTIATED      define
 SSL_VALUE_QUIC_IDLE_TIMEOUT             define
+SSL_VALUE_QUIC_MAX_PENDING_CONNS        define
 SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL  define
 SSL_VALUE_QUIC_STREAM_BIDI_REMOTE_AVAIL define
 SSL_VALUE_QUIC_STREAM_UNI_LOCAL_AVAIL   define

++++++ openssl-CVE-2026-14457.patch ++++++
commit 796b30a3e4db85b64c61a86a8ac30aa34ac29860
Author: Viktor Dukhovni <[email protected]>
Date:   Sat Jun 27 01:02:53 2026 +1000

    Handle signature_algorithms_cert extension in key-only context
    
    Servers or clients that configure only a private key in
    expectation of always negotiating use of RFC7250 raw public keys
    failed to handle the "signature_algorithms_cert" extension.
    
    The issue is now resolved and the RPK tests now check that
    key-only configurations are robust also when the extension
    is sent by the peer.
    
    Key-only configurations are quite uncommon.  As a best practice,
    RPK-capable servers and clients pair their private key with a
    (possibly self-signed) certificate, enabling fallback to X.509
    handshakes with non-RPK peers.
    
    Fixes CVE-2026-14457

Index: openssl-3.5.0/ssl/t1_lib.c
===================================================================
--- openssl-3.5.0.orig/ssl/t1_lib.c
+++ openssl-3.5.0/ssl/t1_lib.c
@@ -4545,6 +4545,20 @@ static int check_cert_usable(SSL_CONNECT
         return 0;
 
     /*
+     * When RPK is negotiated there are no certificate signatures to
+     * constrain, and there may not even be a certificate configured.
+     */
+    if (TLSEXT_cert_type_rpk == (s->server ? s->ext.server_cert_type : 
s->ext.client_cert_type))
+        return 1;
+
+    /*
+     * RPK was enabled, adding candidate private-key-only slots, but was not
+     * negotiated, so the key-only slot is not usable.
+     */
+    if (x == NULL)
+        return 0;
+
+    /*
      * The TLS 1.3 signature_algorithms_cert extension places restrictions
      * on the sigalg with which the certificate was signed (by its issuer).
      */
Index: openssl-3.5.0/test/rpktest.c
===================================================================
--- openssl-3.5.0.orig/test/rpktest.c
+++ openssl-3.5.0/test/rpktest.c
@@ -38,6 +38,37 @@ static OSSL_PROVIDER *defctxnull = NULL;
 static const unsigned char cert_type_rpk[] = { TLSEXT_cert_type_rpk, 
TLSEXT_cert_type_x509 };
 static const unsigned char SID_CTX[] = { 'r', 'p', 'k' };
 
+/*
+ * Wire form of a SignatureSchemeList that lists rsa_pkcs1_sha256
+ * and ed448 -- between them they cover the issuer signature on
+ * every cert this file loads from test/certs
+ * (sha256WithRSAEncryption for the RSA/ECDSA/Ed25519 leaves and
+ * ED448 for the Ed448 leaf), so the extension is harmless when
+ * the handshake is non-RPK and the server's check_cert_usable()
+ * has to walk the list against a real cert.  When RPK is
+ * negotiated check_cert_usable() returns early without inspecting
+ * the list, and when the slot is an RPK-listed key-only slot but
+ * X509 was negotiated check_cert_usable() returns 0 on the x ==
+ * NULL path -- the inevitable outcome, now discovered earlier.
+ *
+ * Payload: length, rsa_pkcs1_sha256, ed448
+ */
+static const unsigned char sigalgs_cert_payload[] = {
+    0x00, 0x04,
+    0x04, 0x01,
+    0x08, 0x08
+};
+
+static int sigalgs_cert_add_cb(SSL *s, unsigned int ext_type,
+    unsigned int context,
+    const unsigned char **out, size_t *outlen,
+    X509 *x, size_t chainidx, int *al, void *add_arg)
+{
+    *out = sigalgs_cert_payload;
+    *outlen = sizeof(sigalgs_cert_payload);
+    return 1;
+}
+
 static int rpk_verify_client_cb(int ok, X509_STORE_CTX *ctx)
 {
     int err = X509_STORE_CTX_get_error(ctx);
@@ -255,18 +318,43 @@ static int test_rpk(int idx)
     /* NEW */
     SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, rpk_verify_client_cb);
 
-    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
-                                      NULL, NULL)))
+    /*
+     * Send signature_algorithms_cert in every ClientHello, and in
+     * every TLS 1.3 CertificateRequest.  The OpenSSL stack doesn't
+     * construct this extension by default in either direction, so
+     * register a custom add hook on both ends.  This exercises the
+     * three distinct paths through check_cert_usable() on whichever
+     * side receives the extension:
+     *   - RPK was negotiated for this side's cert -- early return 1,
+     *     list contents ignored.
+     *   - RPK was offered but X509 was negotiated and this side's
+     *     slot holds only a private key -- x == NULL, return 0
+     *     (any peer-sent signature_algorithms_cert against a key-only
+     *     slot would otherwise trigger a crash).
+     *   - X509 negotiated with a real cert -- walk the list, find
+     *     a match against the issuer's signature algorithm.
+     * The server's registration only fires on TLS 1.3 connections
+     * where the server requests a client certificate (case 2, 9,
+     * 10 etc.); on TLS 1.2 the sigalgs travel inside the
+     * CertificateRequest body, not as a separate extension.
+     */
+    if (!TEST_true(SSL_CTX_add_custom_ext(cctx,
+            TLSEXT_TYPE_signature_algorithms_cert,
+            SSL_EXT_CLIENT_HELLO,
+            sigalgs_cert_add_cb, NULL, NULL,
+            NULL, NULL))
+        || !TEST_true(SSL_CTX_add_custom_ext(sctx,
+            TLSEXT_TYPE_signature_algorithms_cert,
+            SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
+            sigalgs_cert_add_cb, NULL, NULL,
+            NULL, NULL))
+        || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
+            NULL, NULL))
+        || !TEST_int_gt(SSL_dane_enable(serverssl, NULL), 0)
+        || !TEST_int_gt(SSL_dane_enable(clientssl, "example.com"), 0)
+        || !TEST_int_eq(SSL_use_PrivateKey_file(serverssl, privkey_file, 
SSL_FILETYPE_PEM), 1))
         goto end;
 
-    if (!TEST_int_gt(SSL_dane_enable(serverssl, NULL), 0))
-        goto end;
-    if (!TEST_int_gt(SSL_dane_enable(clientssl, "example.com"), 0))
-        goto end;
-
-    /* Set private key and certificate */
-    if (!TEST_int_eq(SSL_use_PrivateKey_file(serverssl, privkey_file, 
SSL_FILETYPE_PEM), 1))
-        goto end;
     /* Only a private key */
     if (idx == 1) {
         if (idx_server_server_rpk == 0 || idx_client_server_rpk == 0) {

++++++ openssl-CVE-2026-18798.patch ++++++
commit 7446ec3f872d035cf3b18056594c6262a42cd843
Author: Alexandr Nedvedicky <[email protected]>
Date:   Wed Aug 5 00:56:25 2026 +0200

    Avoid double free of qrx in port_default_packet_handler()
    
    port_default_packet_handler() may perform double free of qrx
    when channel creation fails. The port_default_packet_handler()
    transfers ownership of qrx to channel/connection via call to
    port_bind_channel(). The port_bind_channel() however may
    release the qrx when channel can not be bound. The error is
    then detected in port_default_packet_handler() which then agains
    releases qrx for the second time.
    
    The fix is to add a reference counter to QRX object so transfer
    of ownership between port_default_packet_handler() and QUIC_CHANNEL
    can be handled safely.
    
    Fixes CVE-2026-18798

Index: openssl-3.5.3/include/internal/quic_record_rx.h
===================================================================
--- openssl-3.5.3.orig/include/internal/quic_record_rx.h
+++ openssl-3.5.3/include/internal/quic_record_rx.h
@@ -51,8 +51,9 @@ typedef struct ossl_qrx_args_st {
 OSSL_QRX *ossl_qrx_new(const OSSL_QRX_ARGS *args);
 
 /*
- * Frees the QRX. All packets obtained using ossl_qrx_read_pkt must already
- * have been released by calling ossl_qrx_release_pkt.
+ * Frees the QRX/reference to QRX. Frees the QRX object, if all references are
+ * gone. All packets obtained using ossl_qrx_read_pkt must already have been
+ * released by calling ossl_qrx_release_pkt.
  *
  * You do not need to call ossl_qrx_remove_dst_conn_id first; this function 
will
  * unregister the QRX from the demuxer for all registered destination 
connection
@@ -60,6 +61,12 @@ OSSL_QRX *ossl_qrx_new(const OSSL_QRX_AR
  */
 void ossl_qrx_free(OSSL_QRX *qrx);
 
+/*
+ * Obtains a new reference to QRX object. Returns NULL if reference can not
+ * be obtained.
+ */
+OSSL_QRX *ossl_qrx_newref(OSSL_QRX *qrx);
+
 /* Setters for the msg_callback and msg_callback_arg */
 void ossl_qrx_set_msg_callback(OSSL_QRX *qrx, ossl_msg_cb msg_callback,
                                SSL *msg_callback_ssl);
Index: openssl-3.5.3/ssl/quic/quic_port.c
===================================================================
--- openssl-3.5.3.orig/ssl/quic/quic_port.c
+++ openssl-3.5.3/ssl/quic/quic_port.c
@@ -530,8 +530,10 @@ static QUIC_CHANNEL *port_make_channel(Q
      * start by allocation and provisioning as much of the channel as we can
      */
     ch = ossl_quic_channel_alloc(&args);
-    if (ch == NULL)
+    if (ch == NULL) {
+        ossl_qrx_free(qrx);
         return NULL;
+    }
 
     /*
      * Fixup the channel tls connection here before we init the channel
@@ -1485,7 +1487,7 @@ static void port_default_packet_handler(
     QUIC_CHANNEL *ch = NULL, *new_ch = NULL;
     QUIC_CONN_ID odcid, scid;
     uint8_t gen_new_token = 0;
-    OSSL_QRX *qrx = NULL;
+    OSSL_QRX *qrx = NULL, *qrx_ref;
     OSSL_QRX *qrx_src = NULL;
     OSSL_QRX_ARGS qrx_args = {0};
     uint64_t cause_flags = 0;
@@ -1671,8 +1673,22 @@ static void port_default_packet_handler(
         }
     }
 
+    qrx_ref = NULL;
+    if (qrx != NULL) {
+        /*
+         * if we are here, then client is validated via retry packet
+         * (client sent a valid token). In this case the qrx has valid
+         * secrets set for QUIC initial level encryption. We can pass
+         * reference to qrx to newly created channel.
+         *
+         * Note: port_bind_channel()/channel becomes owner of qrx_ref.
+         */
+        qrx_ref = ossl_qrx_newref(qrx);
+        if (qrx_ref == NULL)
+            goto undesirable;
+    }
     port_bind_channel(port, &e->peer, &scid, &hdr.dst_conn_id,
-                      &odcid, qrx, &new_ch);
+                      &odcid, qrx_ref, &new_ch);
 
     /*
      * if packet validates it gets moved to channel, we've just bound
@@ -1687,19 +1703,19 @@ static void port_default_packet_handler(
     if (gen_new_token == 1)
         generate_new_token(new_ch, &e->peer);
 
-    if (qrx != NULL) {
+    if (qrx_src != NULL) {
         /*
-         * The qrx belongs to channel now, so don't free it.
-         */
-        qrx = NULL;
-    } else {
-        /*
-         * We still need to salvage packets from almost forgotten qrx
-         * and pass them to channel.
+         * Time to reinject packets from qrx to channel before
+         * qrx will be destroyed here.
          */
         while (ossl_qrx_read_pkt(qrx_src, &qrx_pkt) == 1)
             ossl_quic_channel_inject_pkt(new_ch, qrx_pkt);
         ossl_qrx_update_pn_space(qrx_src, new_ch->qrx);
+        /*
+         * transfer ownership back to qrx;
+         */
+        qrx = qrx_src;
+        qrx_src = NULL;
     }
 
     /*
@@ -1716,7 +1732,7 @@ static void port_default_packet_handler(
      */
 
 undesirable:
-    ossl_qrx_free(qrx);
+    ossl_qrx_free(qrx); /* releases reference */
     ossl_qrx_free(qrx_src);
     ossl_quic_demux_release_urxe(port->demux, e);
 }
Index: openssl-3.5.3/ssl/quic/quic_record_rx.c
===================================================================
--- openssl-3.5.3.orig/ssl/quic/quic_record_rx.c
+++ openssl-3.5.3/ssl/quic/quic_record_rx.c
@@ -171,6 +171,8 @@ struct ossl_qrx_st {
     ossl_msg_cb msg_callback;
     void *msg_callback_arg;
     SSL *msg_callback_ssl;
+
+    uint32_t refcount;
 };
 
 static RXE *qrx_ensure_free_rxe(OSSL_QRX *qrx, size_t alloc_len);
@@ -212,6 +214,7 @@ OSSL_QRX *ossl_qrx_new(const OSSL_QRX_AR
     qrx->short_conn_id_len      = args->short_conn_id_len;
     qrx->init_key_phase_bit     = args->init_key_phase_bit;
     qrx->max_deferred           = args->max_deferred;
+    qrx->refcount = 1;
     return qrx;
 }
 
@@ -247,13 +250,10 @@ void ossl_qrx_update_pn_space(OSSL_QRX *
     return;
 }
 
-void ossl_qrx_free(OSSL_QRX *qrx)
+static void qrx_destroy(OSSL_QRX *qrx)
 {
     uint32_t i;
 
-    if (qrx == NULL)
-        return;
-
     /* Free RXE queue data. */
     qrx_cleanup_rxl(&qrx->rx_free);
     qrx_cleanup_rxl(&qrx->rx_pending);
@@ -267,6 +267,30 @@ void ossl_qrx_free(OSSL_QRX *qrx)
     OPENSSL_free(qrx);
 }
 
+void ossl_qrx_free(OSSL_QRX *qrx)
+{
+    if (qrx == NULL)
+        return;
+
+    qrx->refcount--;
+    if (qrx->refcount == 0)
+        qrx_destroy(qrx);
+}
+
+OSSL_QRX *ossl_qrx_newref(OSSL_QRX *qrx)
+{
+    OSSL_QRX *rv_qrx;
+
+    if (qrx != NULL && qrx->refcount != (uint32_t)~0) {
+        qrx->refcount++;
+        rv_qrx = qrx;
+    } else {
+        rv_qrx = NULL;
+    }
+
+    return rv_qrx;
+}
+
 void ossl_qrx_inject_urxe(OSSL_QRX *qrx, QUIC_URXE *urxe)
 {
     /* Initialize our own fields inside the URXE and add to the pending list. 
*/

++++++ openssl-CVE-2026-34181.patch ++++++
>From afe522fec8038db9a6c8b99b0fd2b1ebd49b5592 Mon Sep 17 00:00:00 2001
From: Alicja Kario <[email protected]>
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 <[email protected]>
---
 crypto/pkcs12/p12_mutl.c                         |   7 ++++---
 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, 12 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

Index: openssl-3.5.0/crypto/pkcs12/p12_mutl.c
===================================================================
--- openssl-3.5.0.orig/crypto/pkcs12/p12_mutl.c
+++ openssl-3.5.0/crypto/pkcs12/p12_mutl.c
@@ -144,11 +144,13 @@ static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_C
     }
     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;
     }
 

++++++ openssl-CVE-2026-54874.patch ++++++
commit 5cf71fc0fef60fabe39c5b8eabb2b668a0d9c395
Author: Matt Caswell <[email protected]>
Date:   Tue Jun 23 11:53:17 2026 +0100

    Avoid full read buffer allocation when buffering DTLS next-epoch records
    
    dtls_rlayer_buffer_record() buffers records that arrive early for the
    next epoch while a handshake is in progress. It did this by taking
    ownership of the entire live read buffer (sized for the largest
    possible record, ~16.7KB) and allocating a brand new one to carry on
    reading, regardless of how small the buffered record actually was.
    With the queue capped at 100 entries, a peer could send around 100
    tiny bogus next-epoch records (~14 bytes each on the wire) and force
    around 1.7MB of heap allocation per connection.
    
    Instead, copy only the record's own on-wire bytes (header and
    ciphertext) into the queue entry, and leave the live read buffer
    untouched. Memory use is now proportional to what the peer actually
    sends.
    
    Fixes CVE-2026-54874
    
    Assisted-by: Claude:claude-sonnet-4-6

Index: openssl-3.5.0/ssl/record/methods/dtls_meth.c
===================================================================
--- openssl-3.5.0.orig/ssl/record/methods/dtls_meth.c
+++ openssl-3.5.0/ssl/record/methods/dtls_meth.c
@@ -285,7 +285,7 @@ static int dtls_rlayer_buffer_record(OSS
     pitem *item;
 
     /* Limit the size of the queue to prevent DOS attacks */
-    if (pqueue_size(queue) >= 100)
+    if (pqueue_size(queue) >= 16)
         return 0;
 
     rdata = OPENSSL_malloc(sizeof(*rdata));
@@ -297,29 +297,26 @@ static int dtls_rlayer_buffer_record(OSS
         return -1;
     }
 
-    rdata->packet = rl->packet;
+    /*
+     * Take a copy of just this record's on-wire bytes (header + ciphertext)
+     * rather than the whole (much larger) read buffer. The live rl->rbuf is
+     * left untouched and continues to be used for subsequent reads.
+     */
     rdata->packet_length = rl->packet_length;
-    memcpy(&(rdata->rbuf), &rl->rbuf, sizeof(TLS_BUFFER));
-    memcpy(&(rdata->rrec), &rl->rrec[0], sizeof(TLS_RL_RECORD));
-
-    item->data = rdata;
-
-    rl->packet = NULL;
-    rl->packet_length = 0;
-    memset(&rl->rbuf, 0, sizeof(TLS_BUFFER));
-    memset(&rl->rrec[0], 0, sizeof(rl->rrec[0]));
-
-    if (!tls_setup_read_buffer(rl)) {
-        /* RLAYERfatal() already called */
-        OPENSSL_free(rdata->rbuf.buf);
+    rdata->packet = OPENSSL_memdup(rl->packet, rl->packet_length);
+    if (rdata->packet == NULL) {
         OPENSSL_free(rdata);
         pitem_free(item);
+        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_CRYPTO_LIB);
         return -1;
     }
+    memcpy(&(rdata->rrec), &rl->rrec[0], sizeof(TLS_RL_RECORD));
+
+    item->data = rdata;
 
     if (pqueue_insert(queue, item) == NULL) {
         /* Must be a duplicate so ignore it */
-        OPENSSL_free(rdata->rbuf.buf);
+        OPENSSL_free(rdata->packet);
         OPENSSL_free(rdata);
         pitem_free(item);
     }
@@ -327,44 +324,6 @@ static int dtls_rlayer_buffer_record(OSS
     return 1;
 }
 
-/* copy buffered record into OSSL_RECORD_LAYER structure */
-static int dtls_copy_rlayer_record(OSSL_RECORD_LAYER *rl, pitem *item)
-{
-    DTLS_RLAYER_RECORD_DATA *rdata;
-
-    rdata = (DTLS_RLAYER_RECORD_DATA *)item->data;
-
-    ossl_tls_buffer_release(&rl->rbuf);
-
-    rl->packet = rdata->packet;
-    rl->packet_length = rdata->packet_length;
-    memcpy(&rl->rbuf, &(rdata->rbuf), sizeof(TLS_BUFFER));
-    memcpy(&rl->rrec[0], &(rdata->rrec), sizeof(TLS_RL_RECORD));
-
-    /* Set proper sequence number for mac calculation */
-    memcpy(&(rl->sequence[2]), &(rdata->packet[5]), 6);
-
-    return 1;
-}
-
-static int dtls_retrieve_rlayer_buffered_record(OSSL_RECORD_LAYER *rl,
-                                                struct pqueue_st *queue)
-{
-    pitem *item;
-
-    item = pqueue_pop(queue);
-    if (item) {
-        dtls_copy_rlayer_record(rl, item);
-
-        OPENSSL_free(item->data);
-        pitem_free(item);
-
-        return 1;
-    }
-
-    return 0;
-}
-
 /*-
  * Call this to get a new input record.
  * It will return <= 0 if more data is needed, normally due to an error
@@ -398,12 +357,6 @@ int dtls_get_more_records(OSSL_RECORD_LA
     }
 
  again:
-    /* if we're renegotiating, then there may be buffered records */
-    if (dtls_retrieve_rlayer_buffered_record(rl, rl->processed_rcds)) {
-        rl->num_recs = 1;
-        return OSSL_RECORD_RETURN_SUCCESS;
-    }
-
     /* get something from the wire */
 
     /* check if we have the header */
@@ -601,23 +554,13 @@ static int dtls_free(OSSL_RECORD_LAYER *
             /* Push to the next record layer */
             ret &= BIO_write_ex(rl->next, rdata->packet, rdata->packet_length,
                                 &written);
-            OPENSSL_free(rdata->rbuf.buf);
+            OPENSSL_free(rdata->packet);
             OPENSSL_free(item->data);
             pitem_free(item);
         }
         pqueue_free(rl->unprocessed_rcds);
     }
 
-    if (rl->processed_rcds!= NULL) {
-        while ((item = pqueue_pop(rl->processed_rcds)) != NULL) {
-            rdata = (DTLS_RLAYER_RECORD_DATA *)item->data;
-            OPENSSL_free(rdata->rbuf.buf);
-            OPENSSL_free(item->data);
-            pitem_free(item);
-        }
-        pqueue_free(rl->processed_rcds);
-    }
-
     return tls_free(rl) && ret;
 }
 
@@ -647,10 +590,8 @@ dtls_new_record_layer(OSSL_LIB_CTX *libc
         return ret;
 
     (*retrl)->unprocessed_rcds = pqueue_new();
-    (*retrl)->processed_rcds = pqueue_new();
 
-    if ((*retrl)->unprocessed_rcds == NULL
-            || (*retrl)->processed_rcds == NULL) {
+    if ((*retrl)->unprocessed_rcds == NULL) {
         dtls_free(*retrl);
         *retrl = NULL;
         ERR_raise(ERR_LIB_SSL, ERR_R_SSL_LIB);
Index: openssl-3.5.0/ssl/record/methods/recmethod_local.h
===================================================================
--- openssl-3.5.0.orig/ssl/record/methods/recmethod_local.h
+++ openssl-3.5.0/ssl/record/methods/recmethod_local.h
@@ -345,9 +345,8 @@ struct ossl_record_layer_st {
 
     size_t taglen;
 
-    /* DTLS received handshake records (processed and unprocessed) */
+    /* DTLS received handshake records awaiting the next epoch */
     struct pqueue_st *unprocessed_rcds;
-    struct pqueue_st *processed_rcds;
 
     /* records being received in the current epoch */
     DTLS_BITMAP bitmap;
@@ -375,7 +374,6 @@ struct ossl_record_layer_st {
 typedef struct dtls_rlayer_record_data_st {
     unsigned char *packet;
     size_t packet_length;
-    TLS_BUFFER rbuf;
     TLS_RL_RECORD rrec;
 } DTLS_RLAYER_RECORD_DATA;
 

++++++ openssl-CVE-2026-63072.patch ++++++
commit cf7f399052febacbd6a1ff7f33021dfc2db2fc07
Author: Daniel Kubec <[email protected]>
Date:   Thu Jul 23 11:09:55 2026 +0200

    Add test for CVE-2026-63072
    
    Assisted-by: Claude:claude-fable-5

Index: openssl-3.5.0/test/cmsapitest.c
===================================================================
--- openssl-3.5.0.orig/test/cmsapitest.c
+++ openssl-3.5.0/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 *ec_recip_in = NULL;
 
 static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
 {
@@ -418,7 +419,102 @@ end:
     return ret;
 }
 
-OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n")
+
+
+#ifndef OPENSSL_NO_EC
+
+/*
+ * Regression test for CVE-2026-63072: an 8-byte out-of-bounds heap write
+ * reachable through CMS_decrypt() when a KeyAgreeRecipientInfo names an
+ * id-aesNNN-wrap-pad key-wrap OID. CMS sizes the unwrap output buffer from
+ * the cipher's length query (inlen - 8), but AES-WRAP-PAD unwrap cleanses
+ * inlen bytes of it on every RFC 5649 integrity-failure path.
+ *
+ * We build a valid ECDH KARI message (which uses non-padded id-aes256-wrap),
+ * flip the single OID byte an attacker would flip on the wire to turn it into
+ * id-aes256-wrap-pad (key length unchanged), and decrypt with the matching
+ * private key. The unwrap must fail its integrity check without writing past
+ * the CMS-allocated buffer; CMS_decrypt() must fail cleanly.  Under a
+ * memory-checking build (e.g. valgrind) the overflow is flagged directly.
+ */
+static int test_kari_wrap_pad_unwrap_overflow(void)
+{
+    /* DER encoding of the id-aes256-wrap OID (2.16.840.1.101.3.4.1.45). */
+    static const unsigned char aes256_wrap_oid[] = {
+        0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2d
+    };
+    int ret = 0;
+    X509 *eccert = NULL;
+    EVP_PKEY *eckey = NULL;
+    BIO *certbio = NULL, *keybio = NULL, *msgbio = NULL, *outbio = NULL;
+    STACK_OF(X509) *recips = NULL;
+    CMS_ContentInfo *cms = NULL, *cms2 = NULL;
+    unsigned char *der = NULL;
+    const unsigned char *p;
+    int derlen, i, patched = 0;
+    const char *msg = "secret content for kari";
+
+    if ((certbio = BIO_new_file(ec_recip_in, "r")) == NULL
+        || PEM_read_bio_X509(certbio, &eccert, NULL, NULL) == NULL
+        || (keybio = BIO_new_file(ec_recip_in, "r")) == NULL
+        || PEM_read_bio_PrivateKey(keybio, &eckey, NULL, NULL) == NULL) {
+        goto end;
+    }
+
+    if (!TEST_ptr(recips = sk_X509_new_null())
+        || !TEST_int_gt(sk_X509_push(recips, eccert), 0))
+        goto end;
+
+    /* Build a normal ECDH KARI message; it uses non-padded id-aes256-wrap. */
+    if (!TEST_ptr(msgbio = BIO_new_mem_buf(msg, (int)strlen(msg)))
+        || !TEST_ptr(cms = CMS_encrypt(recips, msgbio, EVP_aes_256_cbc(),
+                         CMS_BINARY)))
+        goto end;
+
+    if (!TEST_int_gt(derlen = i2d_CMS_ContentInfo(cms, &der), 0))
+        goto end;
+
+    /* Swap id-aes256-wrap -> id-aes256-wrap-pad (0x2d -> 0x30). */
+    for (i = 0; i + (int)sizeof(aes256_wrap_oid) <= derlen; i++) {
+        if (memcmp(der + i, aes256_wrap_oid, sizeof(aes256_wrap_oid)) == 0) {
+            der[i + sizeof(aes256_wrap_oid) - 1] = 0x30;
+            patched = 1;
+            break;
+        }
+    }
+    if (!TEST_true(patched))
+        goto end;
+
+    p = der;
+    if (!TEST_ptr(cms2 = d2i_CMS_ContentInfo(NULL, &p, derlen)))
+        goto end;
+
+    /*
+     * The wrap-pad unwrap fails the AIV check; with the fix it does so without
+     * writing past the CMS-allocated buffer.  CMS_decrypt() must fail cleanly.
+     */
+    if (!TEST_ptr(outbio = BIO_new(BIO_s_mem()))
+        || !TEST_false(CMS_decrypt(cms2, eckey, eccert, NULL, outbio, 0)))
+        goto end;
+
+    ret = 1;
+end:
+    ERR_clear_error();
+    OPENSSL_free(der);
+    sk_X509_free(recips);
+    CMS_ContentInfo_free(cms);
+    CMS_ContentInfo_free(cms2);
+    BIO_free(certbio);
+    BIO_free(keybio);
+    BIO_free(msgbio);
+    BIO_free(outbio);
+    X509_free(eccert);
+    EVP_PKEY_free(eckey);
+    return ret;
+}
+#endif
+
+OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile ecrecip\n")
 
 int setup_tests(void)
 {
@@ -433,7 +529,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(ec_recip_in = test_get_argument(4)))
         return 0;
 
     certbio = BIO_new_file(certin, "r");
@@ -467,6 +564,9 @@ int setup_tests(void)
     ADD_TEST(test_d2i_CMS_bio_NULL);
     ADD_ALL_TESTS(test_d2i_CMS_decode, 2);
     ADD_TEST(test_cms_aesgcm_iv_too_long);
+#ifndef OPENSSL_NO_EC
+    ADD_TEST(test_kari_wrap_pad_unwrap_overflow);
+#endif
     return 1;
 }
 
Index: openssl-3.5.0/test/recipes/80-test_cmsapi.t
===================================================================
--- openssl-3.5.0.orig/test/recipes/80-test_cmsapi.t
+++ openssl-3.5.0/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", "smime-certs", "smec1.pem")])),
              "running cmsapitest");
Index: openssl-3.5.0/crypto/cms/cms_kari.c
===================================================================
--- openssl-3.5.0.orig/crypto/cms/cms_kari.c
+++ openssl-3.5.0/crypto/cms/cms_kari.c
@@ -217,6 +217,7 @@ static int cms_kek_cipher(unsigned char
     int rv = 0;
     unsigned char *out = NULL;
     int outlen;
+    size_t outsize;
 
     keklen = EVP_CIPHER_CTX_get_key_length(kari->ctx);
     if (keklen > EVP_MAX_KEY_LENGTH)
@@ -230,7 +231,13 @@ static int cms_kek_cipher(unsigned char
     /* obtain output length of ciphered key */
     if (!EVP_CipherUpdate(kari->ctx, NULL, &outlen, in, inlen))
         goto err;
-    out = OPENSSL_malloc(outlen);
+    /*
+     * On its integrity-failure paths that primitive writes and cleanses up to
+     * inlen bytes of the output buffer. Size the buffer for that worst case so
+     * a failed unwrap cannot write past the allocation.
+     */
+    outsize = (size_t)outlen < inlen ? inlen : (size_t)outlen;
+    out = OPENSSL_malloc(outsize);
     if (out == NULL)
         goto err;
     if (!EVP_CipherUpdate(kari->ctx, out, &outlen, in, inlen))

++++++ openssl-CVE-2026-63073.patch ++++++
commit 69cc259407c14c689801ba677b292a1366ef90d7
Author: Norbert Pocs <[email protected]>
Date:   Mon Jul 20 14:10:47 2026 +0200

    CMP unexpected sender DN used as format string in ERR_raise_data()
    
    ossl_cmp_msg_check_update() converts an unexpected CMP response sender DN 
with
    X509_NAME_oneline() and passes that peer-controlled string directly as the
    format argument to ERR_raise_data(). Printable percent characters survive 
the
    DN conversion, so a sender such as CN=%s%n reaches vsnprintf() as active 
format
    syntax without matching varargs.
    
    Fixes: CVE-2026-63073
    
    Original patch by: Filipe Casal of Trail of Bits in collaboration with 
OpenAI
    
    Signed-off-by: Norbert Pocs <[email protected]>

Index: openssl-3.5.0/crypto/cmp/cmp_vfy.c
===================================================================
--- openssl-3.5.0.orig/crypto/cmp/cmp_vfy.c
+++ openssl-3.5.0/crypto/cmp/cmp_vfy.c
@@ -733,7 +733,7 @@ int ossl_cmp_msg_check_update(OSSL_CMP_C
                         "expected sender", expected_sender)) {
             str = X509_NAME_oneline(actual_sender, NULL, 0);
             ERR_raise_data(ERR_LIB_CMP, CMP_R_UNEXPECTED_SENDER,
-                           str != NULL ? str : "<unknown>");
+                           "%s", str != NULL ? str : "<unknown>");
             OPENSSL_free(str);
             return 0;
         }
Index: openssl-3.5.0/test/cmp_vfy_test.c
===================================================================
--- openssl-3.5.0.orig/test/cmp_vfy_test.c
+++ openssl-3.5.0/test/cmp_vfy_test.c
@@ -574,6 +574,55 @@ static int test_msg_check_recipient_nonc
 }
 #endif
 
+/* Regression test for CVE-2026-63073 */
+static int execute_msg_check_update_malicious_sender(CMP_VFY_TEST_FIXTURE 
*fixture)
+{
+    const char *data = NULL;
+    unsigned long err;
+
+    if (!TEST_int_eq(ossl_cmp_msg_check_update(fixture->cmp_ctx, fixture->msg, 
NULL, 0), 0)
+        || !TEST_int_ne((err = ERR_peek_last_error_all(NULL, NULL, NULL, 
&data, NULL)), 0)
+        || !TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMP)
+        || !TEST_int_eq(ERR_GET_REASON(err), CMP_R_UNEXPECTED_SENDER)
+        || !TEST_ptr(data)
+        || !TEST_str_eq(data, "/CN=%n"))
+        return 0;
+    return 1;
+}
+
+static int test_msg_check_update_malicious_sender(void)
+{
+    OSSL_CMP_PKIHEADER *hdr;
+    X509_NAME *expected = X509_NAME_new();
+    X509_NAME *actual = X509_NAME_new();
+
+    if (expected == NULL || actual == NULL) {
+        X509_NAME_free(expected);
+        return 0;
+    }
+
+    SETUP_TEST_FIXTURE(CMP_VFY_TEST_FIXTURE, set_up);
+    if (!TEST_ptr(fixture->msg = load_pkimsg(ir_protected_f, libctx))
+        || !TEST_ptr(hdr = OSSL_CMP_MSG_get0_header(fixture->msg))
+        || !TEST_int_eq(X509_NAME_add_entry_by_txt(expected, "CN", 
MBSTRING_ASC,
+                            (unsigned char *)"%n", -1, -1, 0),
+            1)
+        || !TEST_int_eq(X509_NAME_add_entry_by_txt(actual, "CN", MBSTRING_ASC,
+                            (unsigned char *)"actual", -1, -1, 0),
+            1)
+        || !TEST_int_eq(ossl_cmp_hdr_set1_sender(hdr, expected), 1)
+        || !TEST_int_eq(OSSL_CMP_CTX_set1_expected_sender(fixture->cmp_ctx, 
actual), 1)) {
+        X509_NAME_free(expected);
+        X509_NAME_free(actual);
+        tear_down(fixture);
+        return 0;
+    }
+    EXECUTE_TEST(execute_msg_check_update_malicious_sender, tear_down);
+    X509_NAME_free(expected);
+    X509_NAME_free(actual);
+    return result;
+}
+
 void cleanup_tests(void)
 {
     X509_free(srvcert);
@@ -714,6 +763,7 @@ int setup_tests(void)
 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     ADD_TEST(test_msg_check_recipient_nonce_bad);
 #endif
+    ADD_TEST(test_msg_check_update_malicious_sender);
 
     return 1;
 

++++++ openssl-CVE-2026-63074.patch ++++++
commit d2730faae84443914ed04b105a34e1aaa9be5cc7
Author: Neil Horman <[email protected]>
Date:   Tue Jun 30 14:52:18 2026 -0400

    Add a test for restricting growth in cmp cert cache
    
    Test to ensure that if certs are rejected we don't add them unboundedly
    to the cmp contexts cert cache.
    
    Assisted-by: Claude sonnet 4.6

diff --git a/test/build.info b/test/build.info
index 439a18fb51..1fb08ca8fa 100644
--- a/test/build.info
+++ b/test/build.info
@@ -811,7 +811,7 @@ IF[{- !$disabled{tests} -}]
   IF[{- !$disabled{cmp} -}]
     PROGRAMS{noinst}=cmp_asn_test cmp_ctx_test cmp_status_test cmp_hdr_test \
                      cmp_protect_test cmp_msg_test cmp_vfy_test \
-                     cmp_server_test cmp_client_test
+                     cmp_server_test cmp_client_test cmp_extracerts_dos_test
   ENDIF
 
   SOURCE[cmp_asn_test]=cmp_asn_test.c helpers/cmp_testlib.c
@@ -838,6 +838,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[cmp_msg_test]=.. ../include ../apps/include
   DEPEND[cmp_msg_test]=../libcrypto.a libtestutil.a
 
+  SOURCE[cmp_extracerts_dos_test]=cmp_extracerts_dos_test.c 
helpers/cmp_testlib.c
+  INCLUDE[cmp_extracerts_dos_test]=.. ../include ../apps/include
+  DEPEND[cmp_extracerts_dos_test]=../libcrypto.a libtestutil.a
+
   SOURCE[cmp_vfy_test]=cmp_vfy_test.c helpers/cmp_testlib.c
   INCLUDE[cmp_vfy_test]=.. ../include ../apps/include
   DEPEND[cmp_vfy_test]=../libcrypto.a libtestutil.a
diff --git a/test/cmp_extracerts_dos_test.c b/test/cmp_extracerts_dos_test.c
new file mode 100644
index 0000000000..273281c943
--- /dev/null
+++ b/test/cmp_extracerts_dos_test.c
@@ -0,0 +1,338 @@
+/*
+ * 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
+ */
+
+/*
+ * Regression test for: CMP server unauthenticated memory/CPU DoS via
+ * cached extraCerts on failed protection checks.
+ *
+ * Root cause (crypto/cmp/cmp_vfy.c, ossl_cmp_msg_check_update(), current
+ * master as of this writing):
+ *
+ *   res = ossl_x509_add_certs_new(&ctx->untrusted, msg->extraCerts, ...);
+ *   ...
+ *   res = OSSL_CMP_validate_msg(ctx, msg) || (cb...);   // may be 0 (rejected)
+ *
+ *   if (ctx->noCacheExtraCerts)                          // <-- rollback is
+ *       while (num_added-- > 0)                          //  gated on this
+ *           X509_free(sk_X509_shift(ctx->untrusted));    //  flag only, NOT
+ *                                                          //  on the
+ *                                                          //  validation
+ *                                                          //  result (res)
+ *
+ *   if (!res) { ...; return 0; }   // certs from a REJECTED msg are kept
+ *
+ * This test exercises ossl_cmp_msg_check_update() directly -- no sockets,
+ * no HTTP server, no apps/cmp.c -- and asserts on the resulting size of
+ * ctx->untrusted. It builds a genuinely PBM-protected OSSL_CMP_MSG using
+ * the project's own internal message-creation function
+ * (ossl_cmp_genm_new(), same one exercised in test/cmp_msg_test.c) so the
+ * message is not hand-crafted to "look" rejectable -- it is rejected for a
+ * real reason (the receiving ctx has no matching secret configured), the
+ * same way OSSL_CMP_validate_msg() would reject any unauthenticated CMP
+ * request in the field.
+ *
+ * Expected results:
+ *   - BEFORE the fix: untrusted_count_after == untrusted_count_before + N
+ *     (every rejected message's extraCerts persist)
+ *   - AFTER the fix:  untrusted_count_after == untrusted_count_before
+ *     (rejected messages leave no residue)
+ */
+
+#include "helpers/cmp_testlib.h"
+
+#define NUM_REJECTED_REQUESTS 25 /* "attacker" sends this many distinct certs 
*/
+
+typedef struct test_fixture {
+    const char *test_case_name;
+    OSSL_CMP_CTX *server_ctx; /* long-lived ctx under test, mirrors 
srv_ctx->ctx */
+} CMP_DOS_TEST_FIXTURE;
+
+static OSSL_LIB_CTX *libctx = NULL;
+
+static CMP_DOS_TEST_FIXTURE *set_up(const char *const test_case_name)
+{
+    CMP_DOS_TEST_FIXTURE *fixture;
+
+    if (!TEST_ptr(fixture = OPENSSL_zalloc(sizeof(*fixture))))
+        return NULL;
+    fixture->test_case_name = test_case_name;
+
+    if (!TEST_ptr(fixture->server_ctx = OSSL_CMP_CTX_new(libctx, NULL))) {
+        OPENSSL_free(fixture);
+        return NULL;
+    }
+    /*
+     * Deliberately do NOT call OSSL_CMP_CTX_set1_secretValue() on the
+     * server ctx. Per OSSL_CMP_validate_msg() (crypto/cmp/cmp_vfy.c):
+     *   case NID_id_PasswordBasedMAC:
+     *     if (ctx->secretValue == NULL) {
+     *         ossl_cmp_info(ctx, "no secret available for verifying..");
+     *         ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_VALIDATING_PROTECTION);
+     *         return 0;
+     *     }
+     * so every PBM-protected message this ctx receives is unconditionally
+     * rejected -- a deterministic, content-independent rejection path that
+     * models "missing or invalid protection" from the report's repro
+     * steps, without needing to forge a bad MAC by hand.
+     * ctx->noCacheExtraCerts is left at its default (0), exactly as in the
+     * vulnerable deployment ("not setting -no_cache_extracerts").
+     */
+    return fixture;
+}
+
+static void tear_down(CMP_DOS_TEST_FIXTURE *fixture)
+{
+    if (fixture == NULL)
+        return;
+    OSSL_CMP_CTX_free(fixture->server_ctx);
+    OPENSSL_free(fixture);
+}
+
+/* Generates a throwaway EC P-256 keypair; cheap, and key strength is
+ * irrelevant to this test. */
+static EVP_PKEY *generate_throwaway_keypair(void)
+{
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkey = NULL;
+
+    if (!TEST_ptr(pctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL)))
+        return NULL;
+    if (!TEST_int_gt(EVP_PKEY_keygen_init(pctx), 0)
+        || !TEST_int_gt(EVP_PKEY_CTX_set_group_name(pctx, "P-256"), 0)
+        || !TEST_int_gt(EVP_PKEY_generate(pctx, &pkey), 0))
+        pkey = NULL;
+    EVP_PKEY_CTX_free(pctx);
+    return pkey;
+}
+
+/*
+ * Builds a minimal, self-signed, syntactically valid X509 with a unique
+ * subject/issuer per index, so X509_ADD_FLAG_NO_DUP cannot collapse it
+ * with any other generated cert (matching the report's exploitation
+ * requirement of "unique certificates across requests").
+ */
+static X509 *generate_unique_self_signed_cert(EVP_PKEY *pkey, int index)
+{
+    X509 *cert = NULL;
+    X509_NAME *name = NULL;
+    ASN1_INTEGER *serial = NULL;
+    char cn[64];
+
+    BIO_snprintf(cn, sizeof(cn), "attacker-cert-%d", index);
+
+    if (!TEST_ptr(cert = X509_new())
+        || !TEST_true(X509_set_version(cert, X509_VERSION_3)))
+        goto err;
+
+    if (!TEST_ptr(serial = ASN1_INTEGER_new())
+        || !TEST_true(ASN1_INTEGER_set(serial, 1000L + index))
+        || !TEST_true(X509_set_serialNumber(cert, serial)))
+        goto err;
+
+    if (!TEST_ptr(X509_gmtime_adj(X509_getm_notBefore(cert), 0))
+        || !TEST_ptr(X509_gmtime_adj(X509_getm_notAfter(cert),
+            60L * 60L * 24L * 365L)))
+        goto err;
+
+    if (!TEST_true(X509_set_pubkey(cert, pkey)))
+        goto err;
+
+    if (!TEST_ptr(name = X509_NAME_new())
+        || !TEST_true(X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+            (unsigned char *)"cmp-dos-test",
+            -1, -1, 0))
+        || !TEST_true(X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+            (unsigned char *)cn,
+            -1, -1, 0))
+        || !TEST_true(X509_set_subject_name(cert, name))
+        || !TEST_true(X509_set_issuer_name(cert, name)))
+        goto err;
+
+    if (!TEST_int_gt(X509_sign(cert, pkey, EVP_sha256()), 0))
+        goto err;
+
+    X509_NAME_free(name);
+    ASN1_INTEGER_free(serial);
+    return cert;
+
+err:
+    X509_NAME_free(name);
+    ASN1_INTEGER_free(serial);
+    X509_free(cert);
+    return NULL;
+}
+
+/*
+ * Builds a real, internally consistent, PBM-protected CMP GenMsg carrying
+ * exactly one never-before-seen self-signed cert as its sole extraCert.
+ * Uses a throwaway *client*-side OSSL_CMP_CTX purely to drive message
+ * creation/protection (ossl_cmp_genm_new() both builds the body and calls
+ * ossl_cmp_msg_protect() internally, same as in test/cmp_msg_test.c). The
+ * client ctx's secret is intentionally never shared with the server ctx
+ * under test, so the message is protected (syntactically well-formed,
+ * non-empty protection field) but NOT verifiable by the receiver -- this
+ * is what "missing or invalid protection" means for a real attacker who
+ * has no credentials, not an empty/garbage protection field.
+ */
+static OSSL_CMP_MSG *build_rejectable_msg_with_unique_cert(int index)
+{
+    OSSL_CMP_CTX *client_ctx = NULL;
+    OSSL_CMP_MSG *msg = NULL;
+    EVP_PKEY *pkey = NULL;
+    X509 *fresh_cert = NULL;
+    STACK_OF(X509) *extra = NULL;
+    unsigned char ref[16], secret[16];
+
+    if (!TEST_ptr(client_ctx = OSSL_CMP_CTX_new(libctx, NULL)))
+        goto err;
+
+    if (!TEST_ptr(pkey = generate_throwaway_keypair())
+        || !TEST_ptr(fresh_cert = generate_unique_self_signed_cert(pkey, 
index)))
+        goto err;
+
+    if (!TEST_ptr(extra = sk_X509_new_null())
+        || !TEST_true(sk_X509_push(extra, fresh_cert)))
+        goto err;
+    fresh_cert = NULL; /* ownership now with the stack */
+
+    if (!TEST_true(OSSL_CMP_CTX_set1_extraCertsOut(client_ctx, extra)))
+        goto err;
+
+    /* PBM protection with a secret the server ctx will never be given */
+    memset(ref, (unsigned char)(0xA0 + (index & 0x0F)), sizeof(ref));
+    memset(secret, (unsigned char)(0x50 + (index & 0x0F)), sizeof(secret));
+    if (!TEST_true(OSSL_CMP_CTX_set_option(client_ctx,
+            OSSL_CMP_OPT_UNPROTECTED_SEND, 0))
+        || !TEST_true(OSSL_CMP_CTX_set1_referenceValue(client_ctx, ref,
+            sizeof(ref)))
+        || !TEST_true(OSSL_CMP_CTX_set1_secretValue(client_ctx, secret,
+            sizeof(secret))))
+        goto err;
+
+    /* GenMsg is the lightest standard body type for this purpose */
+    if (!TEST_ptr(msg = ossl_cmp_genm_new(client_ctx)))
+        goto err;
+
+    sk_X509_pop_free(extra, X509_free);
+    X509_free(fresh_cert);
+    EVP_PKEY_free(pkey);
+    OSSL_CMP_CTX_free(client_ctx);
+    return msg;
+
+err:
+    sk_X509_pop_free(extra, X509_free);
+    X509_free(fresh_cert);
+    EVP_PKEY_free(pkey);
+    OSSL_CMP_CTX_free(client_ctx);
+    OSSL_CMP_MSG_free(msg);
+    return NULL;
+}
+
+/*
+ * Core assertion: N distinct rejected requests must not grow
+ * server_ctx->untrusted at all.
+ *
+ * Before the fix this fails with e.g.:
+ *   ERROR: untrusted count after (25) != count before (0)
+ */
+static int execute_no_unbounded_growth_test(CMP_DOS_TEST_FIXTURE *fixture)
+{
+    OSSL_CMP_CTX *server_ctx = fixture->server_ctx;
+    int count_before, count_after, i;
+
+    count_before = sk_X509_num(OSSL_CMP_CTX_get0_untrusted(server_ctx));
+    if (count_before < 0)
+        count_before = 0;
+
+    for (i = 0; i < NUM_REJECTED_REQUESTS; i++) {
+        OSSL_CMP_MSG *msg = build_rejectable_msg_with_unique_cert(i);
+        int check_result;
+
+        if (!TEST_ptr(msg))
+            return 0;
+
+        check_result = ossl_cmp_msg_check_update(server_ctx, msg, NULL, 0);
+        OSSL_CMP_MSG_free(msg);
+
+        if (!TEST_int_eq(check_result, 0)) {
+            TEST_note("expected request #%d to be rejected (server ctx has"
+                      " no matching PBM secret) but it was accepted -- test"
+                      " setup is wrong, not exercising the rejection path",
+                i);
+            return 0;
+        }
+    }
+
+    count_after = sk_X509_num(OSSL_CMP_CTX_get0_untrusted(server_ctx));
+    if (count_after < 0)
+        count_after = 0;
+
+    if (!TEST_int_eq(count_after, count_before)) {
+        TEST_note("server_ctx->untrusted grew from %d to %d after %d"
+                  " rejected requests -- failed-request extraCerts caching"
+                  " bug is present (see ossl_cmp_msg_check_update() in"
+                  " crypto/cmp/cmp_vfy.c)",
+            count_before, count_after,
+            NUM_REJECTED_REQUESTS);
+        return 0;
+    }
+    return 1;
+}
+
+/*
+ * Single-request variant of the same check, useful in isolation since it
+ * pins down that even ONE rejected request leaves no residue -- ruling out
+ * X509_ADD_FLAG_NO_DUP coincidentally masking the bug in the N-request test.
+ */
+static int execute_single_rejected_request_test(CMP_DOS_TEST_FIXTURE *fixture)
+{
+    OSSL_CMP_CTX *server_ctx = fixture->server_ctx;
+    OSSL_CMP_MSG *msg = build_rejectable_msg_with_unique_cert(999);
+    int count_before, count_after;
+
+    if (!TEST_ptr(msg))
+        return 0;
+
+    count_before = sk_X509_num(OSSL_CMP_CTX_get0_untrusted(server_ctx));
+    if (count_before < 0)
+        count_before = 0;
+
+    if (!TEST_int_eq(ossl_cmp_msg_check_update(server_ctx, msg, NULL, 0), 0)) {
+        OSSL_CMP_MSG_free(msg);
+        return 0;
+    }
+    OSSL_CMP_MSG_free(msg);
+
+    count_after = sk_X509_num(OSSL_CMP_CTX_get0_untrusted(server_ctx));
+    if (count_after < 0)
+        count_after = 0;
+
+    return TEST_int_eq(count_after, count_before);
+}
+
+static int test_single_rejected_request_leaves_no_residue(void)
+{
+    SETUP_TEST_FIXTURE(CMP_DOS_TEST_FIXTURE, set_up);
+    EXECUTE_TEST(execute_single_rejected_request_test, tear_down);
+    return result;
+}
+
+static int test_no_unbounded_growth_on_rejected_requests(void)
+{
+    SETUP_TEST_FIXTURE(CMP_DOS_TEST_FIXTURE, set_up);
+    EXECUTE_TEST(execute_no_unbounded_growth_test, tear_down);
+    return result;
+}
+
+int setup_tests(void)
+{
+    ADD_TEST(test_single_rejected_request_leaves_no_residue);
+    ADD_TEST(test_no_unbounded_growth_on_rejected_requests);
+    return 1;
+}
diff --git a/test/recipes/65-test_cmp_msg.t b/test/recipes/65-test_cmp_msg.t
index d104576a9d..19c17efca4 100644
--- a/test/recipes/65-test_cmp_msg.t
+++ b/test/recipes/65-test_cmp_msg.t
@@ -20,17 +20,22 @@ use lib srctop_dir('Configurations');
 use lib bldtop_dir('.');
 
 my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0);
+my $no_ec = disabled('ec');
 
 plan skip_all => "This test is not supported in a no-cmp build"
     if disabled("cmp");
 
-plan tests => 2 + ($no_fips ? 0 : 1); #fips test
+plan tests => 2 + ($no_fips ? 0 : 1) + ($no_ec ? 0 : 1); #fips test and ec test
 
 my @basic_cmd = ("cmp_msg_test",
                  data_file("new.key"),
                  data_file("server.crt"),
                  data_file("pkcs10.der"));
 
+unless ($no_ec) {
+    ok(run(test(["cmp_extracerts_dos_test"])));
+}
+
 ok(run(test([@basic_cmd, "none"])));
 
 ok(run(test([@basic_cmd, "default", srctop_file("test", "default.cnf")])));

commit 7b6da0756cf3df5e2d0537d586a9187b2c2dfb28
Author: Neil Horman <[email protected]>
Date:   Tue Jun 30 15:09:01 2026 -0400

    Fix unbounded cert cache growth in cmp
    
    If a remote user sends cmp messages to a server with a list of
    extraCerts and the message is rejected, the extraCerts from the message
    remain in the server contexts untrusted certificate stack.  This exposes
    servers with long lived ctx objects to denial of service attacks in
    which an attacker sends messages intending to be rejected with a large
    list of additional cerificated repeatedly, forcing the server to store
    them indefinately.
    
    Fix it by rolling back the added extra certs if the message is rejected,
    using the same method we do when the context is configured to not do
    caching at all.
    
    Fixes openssl/srt#224
    
    Fixes CVE-2026-63074

diff --git a/crypto/cmp/cmp_vfy.c b/crypto/cmp/cmp_vfy.c
index c69b0ffb57..b49edb7231 100644
--- a/crypto/cmp/cmp_vfy.c
+++ b/crypto/cmp/cmp_vfy.c
@@ -801,8 +801,13 @@ int ossl_cmp_msg_check_update(OSSL_CMP_CTX *ctx, const 
OSSL_CMP_MSG *msg,
     res = 1; /* support more aggressive fuzzing by letting invalid msg pass */
 #endif
 
-    /* remove extraCerts again if not caching */
-    if (ctx->noCacheExtraCerts)
+    /*
+     * remove extraCerts again if not caching
+     * or if we failed validation above, lest a remote user
+     * starts sending us lots of certificates in invalid messages
+     * leading to a DOS from unbounded certificate stack growth
+     */
+    if (ctx->noCacheExtraCerts || res != 1)
         while (num_added-- > 0)
             X509_free(sk_X509_shift(ctx->untrusted));


++++++ openssl-CVE-2026-63075.patch ++++++
commit 1928eaf0b12d0feed4dade5d9ee1b4b65af78534
Author: Norbert Pocs <[email protected]>
Date:   Tue Aug 4 08:46:11 2026 +0200

    Don't store ACK-only frames in TX history for QUIC.
    
    When QUIC sends an ACK-only frame, there is no expectation that the
    peer will ack that ack (i.e. it is itself not ack-eliciting).  However,
    our implementation stores these frames in the TX history regardless.  In and
    of itself thats ok, but if a malicious client establishes a connection,
    and then drives the connection such that ack-only frames are forced from
    the peer (i.e. by sending numerous ping frames), and then withholding
    any subseqent acks for ack-eliciting data, like legitimate data, said
    malicious client can force inappropriate memory growth on the server,
    leading to potential DOS attacks.
    
    Don't store any ACK-only frames in the TX history to address this.  Record 
it in
    our TX history so that the send window moves forward appropriately, but for
    ack-only frames, immediately remove it, since we don't expect to get an ack 
for
    them anyway.
    
    Initially authored by Opal Wright <[email protected]>
    
    The initial proposal had some shortcommings in which the highest pn
    acked value was not accounted for which I have fixed with the assistance
    of Claude
    
    Assisted-by: Anthopic Sonnet 5
    
    Fixes: https://github.com/openssl/srt/issues/229
    
    Original patch by Neil Horman <[email protected]>
    
    Signed-off-by: Norbert Pocs <[email protected]>

Index: openssl-3.5.0/include/internal/quic_ackm.h
===================================================================
--- openssl-3.5.0.orig/include/internal/quic_ackm.h
+++ openssl-3.5.0/include/internal/quic_ackm.h
@@ -129,6 +129,11 @@ struct ossl_ackm_tx_pkt_st {
 };
 
 int ossl_ackm_on_tx_packet(OSSL_ACKM *ackm, OSSL_ACKM_TX_PKT *pkt);
+
+/*
+ * Records transmission of a packet containing only ACK frames. The packet
+ */
+int ossl_ackm_on_tx_ack_only_packet(OSSL_ACKM *ackm, OSSL_ACKM_TX_PKT *pkt);
 int ossl_ackm_on_rx_datagram(OSSL_ACKM *ackm, size_t num_bytes);
 
 #  define OSSL_ACKM_ECN_NONE      0
Index: openssl-3.5.0/ssl/quic/quic_ackm.c
===================================================================
--- openssl-3.5.0.orig/ssl/quic/quic_ackm.c
+++ openssl-3.5.0/ssl/quic/quic_ackm.c
@@ -1112,6 +1112,38 @@ int ossl_ackm_on_tx_packet(OSSL_ACKM *ac
     return 1;
 }
 
+int ossl_ackm_on_tx_ack_only_packet(OSSL_ACKM *ackm, OSSL_ACKM_TX_PKT *pkt)
+{
+    struct tx_pkt_history_st *h;
+    unsigned int pkt_space;
+
+    if (pkt == NULL || pkt->pkt_space >= QUIC_PN_SPACE_NUM)
+        return 0;
+
+    /*
+     * A packet containing only an ACK frame must not be treated as
+     * in-flight or ack-eliciting; if it were, ossl_ackm_on_tx_packet()
+     * below would (correctly) perform bytes-in-flight/timer/CC bookkeeping
+     * for a packet we are about to discard from history, which would be
+     * incorrect.
+     */
+    if (pkt->is_inflight || pkt->is_ack_eliciting)
+        return 0;
+
+    pkt_space = pkt->pkt_space;
+
+    /*
+     * No one can expect ACK for packet which carries ACK frames only
+     * (ack_only packet). The ACKM does not need to keep record for ack_only
+     * packet. For ack_only packet the ACKM manager must be updated by the
+     * highest packet number which got sent.
+     */
+    h = get_tx_history(ackm, pkt_space);
+    h->highest_sent = pkt->pkt_num;
+
+    return 1;
+}
+
 int ossl_ackm_on_rx_datagram(OSSL_ACKM *ackm, size_t num_bytes)
 {
     /* No-op on the client. */
Index: openssl-3.5.0/ssl/quic/quic_txp.c
===================================================================
--- openssl-3.5.0.orig/ssl/quic/quic_txp.c
+++ openssl-3.5.0/ssl/quic/quic_txp.c
@@ -2950,6 +2950,20 @@ fatal_err:
     return TXP_ERR_INTERNAL;
 }
 
+static int txp_pkt_is_ack_only(const QUIC_TXPIM_PKT *tpkt)
+{
+    return tpkt->had_ack_frame
+        && !tpkt->ackm_pkt.is_inflight
+        && !tpkt->ackm_pkt.is_ack_eliciting
+        && !tpkt->had_handshake_done_frame
+        && !tpkt->had_max_data_frame
+        && !tpkt->had_max_streams_bidi_frame
+        && !tpkt->had_max_streams_uni_frame
+        && !tpkt->had_conn_close
+        && tpkt->retx_head == NULL
+        && ossl_quic_txpim_pkt_get_num_chunks(tpkt) == 0;
+}
+
 /*
  * Commits and queues a packet for transmission. There is no backing out after
  * this.
@@ -2958,8 +2972,9 @@ fatal_err:
  *
  *   - Sends the packet to the QTX for encryption and transmission;
  *
- *   - Records the packet as having been transmitted in FIFM. ACKM is informed,
- *     etc. and the TXPIM record is filed.
+ *   - Records non-ACK-only packets as having been transmitted in FIFM. ACKM is
+ *     informed, etc. and the TXPIM record is filed only when later callbacks
+ *     need it.
  *
  *   - Informs various subsystems of frames that were sent and clears frame
  *     wanted flags so that we do not generate the same frames again.
@@ -2986,7 +3001,7 @@ static int txp_pkt_commit(OSSL_QUIC_TX_P
                           uint32_t archetype,
                           int *txpim_pkt_reffed)
 {
-    int rc = 1;
+    int ack_only, rc = 1;
     uint32_t enc_level = pkt->h.enc_level;
     uint32_t pn_space = ossl_quic_enc_level_to_pn_space(enc_level);
     QUIC_TXPIM_PKT *tpkt = pkt->tpkt;
@@ -3029,28 +3044,35 @@ static int txp_pkt_commit(OSSL_QUIC_TX_P
                 return 0; /* alloc error */
         }
 
-    /* Dispatch to FIFD. */
-    if (!ossl_quic_fifd_pkt_commit(&txp->fifd, tpkt))
+    ack_only = txp_pkt_is_ack_only(tpkt);
+
+    /* Dispatch packets that need loss/retransmit callbacks to FIFD. */
+    if (!ack_only && !ossl_quic_fifd_pkt_commit(&txp->fifd, tpkt))
         return 0;
 
     /*
      * Transmission and Post-Packet Generation Bookkeeping
      * ===================================================
      *
-     * No backing out anymore - at this point the ACKM has recorded the packet
-     * as having been sent, so we need to increment our next PN counter, or
-     * the ACKM will complain when we try to record a duplicate packet with
-     * the same PN later. At this point actually sending the packet may still
-     * fail. In this unlikely event it will simply be handled as though it
-     * were a lost packet.
+     * No backing out anymore - at this point we need to increment our next PN
+     * counter, or the ACKM will complain when we try to record a duplicate
+     * packet with the same PN later. Non-ACK-only packets have also been
+     * recorded in ACKM, so if QTX write fails they are handled as though they
+     * were lost. ACK-only packets are not recorded and will be cleaned up by
+     * the caller.
      */
     ++txp->next_pn[pn_space];
-    *txpim_pkt_reffed = 1;
+    if (!ack_only)
+        *txpim_pkt_reffed = 1;
 
     /* Send the packet. */
     if (!ossl_qtx_write_pkt(txp->args.qtx, &txpkt))
         return 0;
 
+    if (ack_only
+        && !ossl_ackm_on_tx_ack_only_packet(txp->args.ackm, &tpkt->ackm_pkt))
+        rc = 0;
+
     /*
      * Record FC and stream abort frames as sent; deactivate streams which no
      * longer have anything to do.

++++++ openssl-CVE-2026-63076.patch ++++++
commit 616348493eceb7abd368cdbbfe0b89974d5d0c93
Author: Daniel Kubec <[email protected]>
Date:   Tue Jul 21 11:19:29 2026 +0200

    Fix Remote NULL deref in ossl_cmp_calc_protection() via crafted 
protectionAlg
    
    ossl_cmp_calc_protection() only checked whether the protectionAlg parameter
    (ppval) was NULL before treating it as a PBMParameter ASN1_STRING.
    
    X509_ALGOR_get0() does not validate the ASN.1 type of the parameter against 
what
    the caller expects. For id-PasswordBasedMAC, a crafted message can encode 
the
    parameter as a BOOLEAN instead of the expected PBMParameter SEQUENCE. 
Because
    the ASN1_TYPE value union overlays the boolean int on the pointer field, 
ppval
    comes back as a bogus non-NULL pointer (e.g. 0xff).
    
    Fixes CVE-2026-63076

diff --git a/crypto/cmp/cmp_protect.c b/crypto/cmp/cmp_protect.c
index 974cb1d270..ba8cc99a07 100644
--- a/crypto/cmp/cmp_protect.c
+++ b/crypto/cmp/cmp_protect.c
@@ -59,7 +59,7 @@ ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX 
*ctx,
             ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PBM_SECRET);
             return NULL;
         }
-        if (ppval == NULL) {
+        if (pptype != V_ASN1_SEQUENCE || ppval == NULL) {
             ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION);
             return NULL;
         }

commit 3668788aff100aaed5079b46e279592c97b46ccd
Author: Daniel Kubec <[email protected]>
Date:   Tue Jul 21 11:18:53 2026 +0200

    Add test for CVE-2026-63076
    
    Assisted-by: Claude:claude-fable-5

diff --git a/test/cmp_protect_test.c b/test/cmp_protect_test.c
index a8d673e5e1..a537e9d019 100644
--- a/test/cmp_protect_test.c
+++ b/test/cmp_protect_test.c
@@ -185,6 +185,38 @@ static int test_cmp_calc_protection_pbmac(void)
     EXECUTE_TEST(execute_calc_protection_pbmac_test, tear_down);
     return result;
 }
+
+/*
+ * Regression test for the ossl_cmp_calc_protection() protectionAlg
+ * type-confusion DoS: a PKIMessage whose protectionAlg has the
+ * id-PasswordBasedMAC OID but carries a BOOLEAN parameter instead of the
+ * expected PBMParameter SEQUENCE. X509_ALGOR_get0() then returns the boolean's
+ * union member (0xff) via ppval; the unpatched code took the non-NULL ppval as
+ * a valid ASN1_STRING * and dereferenced 0xff, crashing with a near-NULL
+ * access.  The fixed code must reject the malformed parameter and return NULL.
+ */
+static int test_cmp_calc_protection_pbmac_bad_alg_param(void)
+{
+    unsigned char sec_insta[] = { 'i', 'n', 's', 't', 'a' };
+    X509_ALGOR *alg = NULL;
+
+    SETUP_TEST_FIXTURE(CMP_PROTECT_TEST_FIXTURE, set_up);
+    if (!TEST_true(OSSL_CMP_CTX_set1_secretValue(fixture->cmp_ctx,
+            sec_insta, sizeof(sec_insta)))
+        || !TEST_ptr(fixture->msg = load_pkimsg(ip_PBM_f, libctx))
+        || !TEST_ptr(alg = X509_ALGOR_new())
+        || !TEST_true(X509_ALGOR_set0(alg, 
OBJ_nid2obj(NID_id_PasswordBasedMAC),
+            V_ASN1_BOOLEAN, (void *)1))) {
+        X509_ALGOR_free(alg);
+        tear_down(fixture);
+        fixture = NULL;
+    } else {
+        X509_ALGOR_free(fixture->msg->header->protectionAlg);
+        fixture->msg->header->protectionAlg = alg;
+    }
+    EXECUTE_TEST(execute_calc_protection_fails_test, tear_down);
+    return result;
+}
 static int execute_MSG_protect_test(CMP_PROTECT_TEST_FIXTURE *fixture)
 {
     return TEST_int_eq(fixture->expected,
@@ -609,6 +641,7 @@ int setup_tests(void)
     ADD_TEST(test_cmp_calc_protection_pkey_Ed);
 #endif
     ADD_TEST(test_cmp_calc_protection_pbmac);
+    ADD_TEST(test_cmp_calc_protection_pbmac_bad_alg_param);
 
     ADD_TEST(test_MSG_protect_with_msg_sig_alg_protection_plus_rsa_key);
     ADD_TEST(test_MSG_protect_with_certificate_and_key);

++++++ openssl-CVE-2026-75803.patch ++++++
>From bdeb0cd994d915342787f117ee75044f0dc36f34 Mon Sep 17 00:00:00 2001
From: Billy Brumley <[email protected]>
Date: Tue, 4 Aug 2026 07:35:48 -0400
Subject: [PATCH] Check the tag on EVP_Cipher() finalize: Poly1305 and OCB
 AEADs

For the affected OpenSSL built-in provider AEAD implementations,
EVP_Cipher(ctx, out, NULL, 0) reaches the ccipher callback as a
NULL-input terminal call. OCB and ChaCha20-Poly1305 took an early exit
on an empty message, with or without AAD, and returned success without
comparing an explicitly supplied tag. Consequently a corrupted tag was
accepted before this change.

Make these built-in callbacks perform their terminal tag operation,
aligning their explicit-tag handling with the streaming Final path
without defining NULL input as part of the generic EVP_Cipher()
contract.

AES-GCM-SIV also failed to generate a tag when Final was its first
empty-message operation. Generate the tag in that case and propagate
failures from the matching empty-message decrypt operation.

The stable ChaCha20-Poly1305 implementation aliases Update to the
one-shot cipher callback, so this backport introduces a dedicated Update
callback to preserve zero-length Update as a no-op.

Follow-up to #31555
Fixes #32258
Fixes CVE-2026-75803

Assisted-by: Claude:claude-opus-4-8
Assisted-by: Codex:gpt-5.6-sol

(cherry picked from commit 5741d29a5f356e05262cd0936a472a9961398d53)

Co-authored-by: Mounir IDRASSI <[email protected]>
Reviewed-by: Bob Beck <[email protected]>
Reviewed-by: Tomas Mraz <[email protected]>
Merge-date: Wed Aug 19 17:41:16 2026
Merged-from: https://github.com/openssl/openssl/pull/32416
---
 .../ciphers/cipher_aes_gcm_siv_hw.c           | 14 +++++++---
 .../implementations/ciphers/cipher_aes_ocb.c  |  4 +++
 .../ciphers/cipher_chacha20_poly1305.c        | 27 ++++++++++++++-----
 3 files changed, 34 insertions(+), 11 deletions(-)

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
@@ -267,11 +267,17 @@ static int aes_gcm_siv_finish(PROV_AES_G
 {
     int ret = 0;
 
-    if (ctx->enc)
+    if (ctx->enc) {
+        /* Generate the tag when Final is the first empty-message operation. */
+        if (ctx->generated_tag == 0
+            && aes_gcm_siv_encrypt(ctx, NULL, NULL, 0) == 0)
+            return 0;
         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));
+    }
+    if (ctx->generated_tag == 0
+        && aes_gcm_siv_decrypt(ctx, NULL, NULL, 0) == 0)
+        return 0;
+    ret = CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag)) == 0;
     ret &= ctx->have_user_tag;
     return ret;
 }
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
@@ -511,6 +511,10 @@ static int aes_ocb_cipher(void *vctx, un
     if (!ossl_prov_is_running())
         return 0;
 
+    /* NULL input indicates Final, which must generate or check the tag. */
+    if (in == NULL)
+        return aes_ocb_block_final(vctx, out, outl, outsize);
+
     if (outsize < inl) {
         ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
         return 0;
Index: 
openssl-3.5.3/providers/implementations/ciphers/cipher_chacha20_poly1305.c
===================================================================
--- 
openssl-3.5.3.orig/providers/implementations/ciphers/cipher_chacha20_poly1305.c
+++ openssl-3.5.3/providers/implementations/ciphers/cipher_chacha20_poly1305.c
@@ -30,11 +30,11 @@ static OSSL_FUNC_cipher_get_params_fn ch
 static OSSL_FUNC_cipher_get_ctx_params_fn chacha20_poly1305_get_ctx_params;
 static OSSL_FUNC_cipher_set_ctx_params_fn chacha20_poly1305_set_ctx_params;
 static OSSL_FUNC_cipher_cipher_fn chacha20_poly1305_cipher;
+static OSSL_FUNC_cipher_update_fn chacha20_poly1305_update;
 static OSSL_FUNC_cipher_final_fn chacha20_poly1305_final;
 static OSSL_FUNC_cipher_gettable_ctx_params_fn 
chacha20_poly1305_gettable_ctx_params;
 static OSSL_FUNC_cipher_settable_ctx_params_fn 
chacha20_poly1305_settable_ctx_params;
 #define chacha20_poly1305_gettable_params ossl_cipher_generic_gettable_params
-#define chacha20_poly1305_update chacha20_poly1305_cipher
 
 static void *chacha20_poly1305_newctx(void *provctx)
 {
@@ -307,11 +307,6 @@ static int chacha20_poly1305_cipher(void
     if (!ossl_prov_is_running())
         return 0;
 
-    if (inl == 0) {
-        *outl = 0;
-        return 1;
-    }
-
     if (outsize < inl) {
         ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
         return 0;
@@ -323,6 +318,24 @@ static int chacha20_poly1305_cipher(void
     return 1;
 }
 
+static int chacha20_poly1305_update(void *vctx, unsigned char *out,
+    size_t *outl, size_t outsize,
+    const unsigned char *in, size_t inl)
+{
+    /*
+     * A zero-length update is a no-op. Only EVP_Cipher() and Final produce or
+     * check the authentication tag.
+     */
+    if (inl == 0) {
+        if (!ossl_prov_is_running())
+            return 0;
+        *outl = 0;
+        return 1;
+    }
+
+    return chacha20_poly1305_cipher(vctx, out, outl, outsize, in, inl);
+}
+
 static int chacha20_poly1305_final(void *vctx, unsigned char *out, size_t 
*outl,
                                    size_t outsize)
 {

Reply via email to