Redefine sslarch for x86_64_v2 arch

This commit is contained in:
Eduard Abdullin 2026-06-11 17:21:33 +00:00 committed by root
commit ac416c8c2d
16 changed files with 2450 additions and 2 deletions

63
0060-CVE-2026-7383.patch Normal file
View File

@ -0,0 +1,63 @@
diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c
index 05b8ac8706f..7221e843b03 100644
--- a/crypto/asn1/a_mbstr.c
+++ b/crypto/asn1/a_mbstr.c
@@ -185,11 +185,27 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
break;
case MBSTRING_BMP:
+ if (nchar > INT_MAX / 2) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
+ if (free_out) {
+ ASN1_STRING_free(dest);
+ *out = NULL;
+ }
+ return -1;
+ }
outlen = nchar << 1;
cpyfunc = cpy_bmp;
break;
case MBSTRING_UNIV:
+ if (nchar > INT_MAX / 4) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
+ if (free_out) {
+ ASN1_STRING_free(dest);
+ *out = NULL;
+ }
+ return -1;
+ }
outlen = nchar << 2;
cpyfunc = cpy_univ;
break;
@@ -197,8 +213,11 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
case MBSTRING_UTF8:
outlen = 0;
ret = traverse_string(in, len, inform, out_utf8, &outlen);
- if (ret < 0) {
- ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
+ if (ret < 0) { /* error already raised in out_utf8() */
+ if (free_out) {
+ ASN1_STRING_free(dest);
+ *out = NULL;
+ }
return -1;
}
cpyfunc = cpy_utf8;
@@ -281,9 +300,15 @@ static int out_utf8(unsigned long value, void *arg)
int *outlen, len;
len = UTF8_putc(NULL, -1, value);
- if (len <= 0)
+ if (len <= 0) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_INVALID_UTF8STRING);
return len;
+ }
outlen = arg;
+ if (*outlen > INT_MAX - len) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_STRING_TOO_LONG);
+ return -1;
+ }
*outlen += len;
return 1;
}

181
0061-CVE-2026-9076.patch Normal file
View File

@ -0,0 +1,181 @@
From ae233967cf40b15fe80c70c17438d391158171b3 Mon Sep 17 00:00:00 2001
From: Nikola Pajkovsky <nikolap@openssl.org>
Date: Thu, 21 May 2026 11:53:09 +0200
Subject: [PATCH 1/2] cms: kek_unwrap_key: Fix out-of-bounds read in check-byte
validation
the check-byte test in kek_unwrap_key() reads tmp[1] through tmp[6]
unconditionally, so the decrypted buffer must hold at least seven
octets. The pre-decryption size check enforces inlen >= 2 * blocklen,
which yields the required seven octets only when blocklen >= 4. For
a KEK cipher with a smaller block size, inlen can be as small as
2 * blocklen and the check-byte read overruns the inlen-sized tmp
allocation.
Reject blocklen < 4 in the early sanity check. All block ciphers
appropriate for CMS PasswordRecipientInfo key wrapping have a block
size of at least 8 octets (DES/3DES = 8, AES = 16), so this only
forbids ciphers that would not be valid KEK choices anyway, and the
existing inlen >= 2 * blocklen check then guarantees the seven-octet
lower bound the check-byte test relies on.
Fixes CVE-2026-9076
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
---
crypto/cms/cms_pwri.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
index d62dbbde881..4bb06b406eb 100644
--- a/crypto/cms/cms_pwri.c
+++ b/crypto/cms/cms_pwri.c
@@ -200,18 +200,18 @@ static int kek_unwrap_key(unsigned char *out, size_t *outlen,
const unsigned char *in, size_t inlen,
EVP_CIPHER_CTX *ctx)
{
- size_t blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
+ int blocklen = EVP_CIPHER_CTX_get_block_size(ctx);
unsigned char *tmp;
int outl, rv = 0;
- if (blocklen == 0)
+ if (blocklen < 4)
return 0;
- if (inlen < 2 * blocklen) {
+ if (inlen < 2 * (size_t)blocklen) {
/* too small */
return 0;
}
- if (inlen % blocklen) {
+ if (inlen > INT_MAX || inlen % blocklen) {
/* Invalid size */
return 0;
}
From 4a35e3c194ceb0af30fcbe42d65c95e08d5758a5 Mon Sep 17 00:00:00 2001
From: Nikola Pajkovsky <nikolap@openssl.org>
Date: Thu, 21 May 2026 14:18:11 +0200
Subject: [PATCH 2/2] cms: kek_unwrap_key: test for fix out-of-bounds read in
check-byte validation
added EnvelopedData blob with a PasswordRecipientInfo using
id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher. CFB's 1-byte
effective block size let the inlen >= 2 * blocklen guard in
kek_unwrap_key() accept a wrapped key shorter than the seven octets
the check-byte test reads from tmp[1..6]; the encryptedKey OCTET
STRING here is only two bytes.
Signed-off-by: Nikola Pajkovsky <nikolap@openssl.org>
---
test/cmsapitest.c | 48 +++++++++++++++++-
test/recipes/80-test_cmsapi.t | 3 +-
.../80-test_cmsapi_data/cms_pwri_kek_oob.der | Bin 0 -> 193 bytes
3 files changed, 48 insertions(+), 3 deletions(-)
create mode 100644 test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der
diff --git a/test/cmsapitest.c b/test/cmsapitest.c
index 0752d14df09..d908bc6fc4c 100644
--- a/test/cmsapitest.c
+++ b/test/cmsapitest.c
@@ -21,6 +21,7 @@ static X509 *cert = NULL;
static EVP_PKEY *privkey = NULL;
static char *derin = NULL;
static char *too_long_iv_cms_in = NULL;
+static char *pwri_kek_oob_der_in = NULL;
static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
{
@@ -512,7 +513,48 @@ static int test_cms_aesgcm_iv_too_long(void)
return ret;
}
-OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n")
+/*
+ * CMS EnvelopedData with a single PasswordRecipientInfo using
+ * id-alg-PWRI-KEK and an AES-128-CFB key encryption cipher
+ * (1-byte effective block size). The encryptedKey OCTET STRING is
+ * only two bytes long, so the wrapped key buffer is shorter than
+ * the seven octets read by the check-byte test in kek_unwrap_key().
+ * Prior to CVE-2026-9076 this triggered an out-of-bounds heap read;
+ * CMS_decrypt() must now fail cleanly.
+ */
+static int test_pwri_kek_unwrap_short_encrypted_key(void)
+{
+ BIO *in = NULL;
+ CMS_ContentInfo *cms = NULL;
+ unsigned long err = 0;
+ int ret = 0;
+
+ if (!TEST_ptr(in = BIO_new_file(pwri_kek_oob_der_in, "rb"))
+ || !TEST_ptr(cms = d2i_CMS_bio(in, NULL)))
+ goto end;
+
+ /*
+ * The unwrap is attempted eagerly inside CMS_decrypt_set1_password().
+ * It must fail cleanly (no OOB read) and report CMS_R_UNWRAP_FAILURE.
+ */
+ if (!TEST_false(CMS_decrypt_set1_password(cms,
+ (unsigned char *)"password", -1)))
+ goto end;
+
+ err = ERR_peek_last_error();
+ if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS)
+ || !TEST_int_eq(ERR_GET_REASON(err), CMS_R_UNWRAP_FAILURE))
+ goto end;
+
+ ERR_clear_error();
+ ret = 1;
+end:
+ CMS_ContentInfo_free(cms);
+ BIO_free(in);
+ return ret;
+}
+
+OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile tooLongIVpem pwriKekOobDer\n")
int setup_tests(void)
{
@@ -527,7 +569,8 @@ int setup_tests(void)
if (!TEST_ptr(certin = test_get_argument(0))
|| !TEST_ptr(privkeyin = test_get_argument(1))
|| !TEST_ptr(derin = test_get_argument(2))
- || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3)))
+ || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3))
+ || !TEST_ptr(pwri_kek_oob_der_in = test_get_argument(4)))
return 0;
certbio = BIO_new_file(certin, "r");
@@ -564,6 +607,7 @@ int setup_tests(void)
ADD_TEST(test_encrypted_data_aead);
ADD_ALL_TESTS(test_d2i_CMS_decode, 2);
ADD_TEST(test_cms_aesgcm_iv_too_long);
+ ADD_TEST(test_pwri_kek_unwrap_short_encrypted_key);
return 1;
}
diff --git a/test/recipes/80-test_cmsapi.t b/test/recipes/80-test_cmsapi.t
index 8d9371e005c..3d1dae84646 100644
--- a/test/recipes/80-test_cmsapi.t
+++ b/test/recipes/80-test_cmsapi.t
@@ -19,5 +19,6 @@ plan tests => 1;
ok(run(test(["cmsapitest", srctop_file("test", "certs", "servercert.pem"),
srctop_file("test", "certs", "serverkey.pem"),
srctop_file("test", "recipes", "80-test_cmsapi_data", "encryptedData.der"),
- srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem")])),
+ srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem"),
+ srctop_file("test", "recipes", "80-test_cmsapi_data", "cms_pwri_kek_oob.der")])),
"running cmsapitest");
diff --git a/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der b/test/recipes/80-test_cmsapi_data/cms_pwri_kek_oob.der
new file mode 100644
index 0000000000000000000000000000000000000000..c3ef3abd10e6b0a57b1eef985b0c8c43cb057371
GIT binary patch
literal 193
zcmXqL+{ebL)#lOmotKfFc|qd_gT}Q?jLe2!i#?ba85StRC0Th4#8?FEa~A$hV(x0n
zFrRcQQS~(+6B7r6ffO4z)C5ieW=;ccHqL}L55`nx7Dg5pCI$wB7`P$qj0Um@Stb?%
z*}W&;-kY#&W8?d>8TmW9IT{Q$uJ-6FX$+dodfRzpO2J!$Gg}?F()_mBI35*0&tMe5
VHoNtcLX?S)y&B`}phcqR3jwDUJP7~*
literal 0
HcmV?d00001

75
0062-CVE-2026-34180.patch Normal file
View File

@ -0,0 +1,75 @@
diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c
index 70ea5f08798..197fd241054 100644
--- a/crypto/asn1/tasn_dec.c
+++ b/crypto/asn1/tasn_dec.c
@@ -54,7 +54,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
const ASN1_ITEM *it,
int tag, int aclass, char opt,
ASN1_TLC *ctx);
-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len,
int utype, char *free_cont, const ASN1_ITEM *it);
/* Table to convert tags to bit values, used for MSTRING type */
@@ -855,19 +855,24 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
/* Translate ASN1 content octets into a structure */
-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
+static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len,
int utype, char *free_cont, const ASN1_ITEM *it)
{
ASN1_VALUE **opval = NULL;
ASN1_STRING *stmp;
ASN1_TYPE *typ = NULL;
int ret = 0;
+ int ilen = (int)len;
const ASN1_PRIMITIVE_FUNCS *pf;
ASN1_INTEGER **tint;
pf = it->funcs;
- if (pf && pf->prim_c2i)
- return pf->prim_c2i(pval, cont, len, utype, free_cont, it);
+ if (pf && pf->prim_c2i) {
+ if (len == (long)ilen)
+ return pf->prim_c2i(pval, cont, ilen, utype, free_cont, it);
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG);
+ return 0;
+ }
/* If ANY type clear type and set pointer to internal value */
if (it->utype == V_ASN1_ANY) {
if (*pval == NULL) {
@@ -885,7 +890,8 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
}
switch (utype) {
case V_ASN1_OBJECT:
- if (!ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len))
+ if (len != (long)ilen
+ || !ossl_c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, ilen))
goto err;
break;
@@ -940,6 +946,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
case V_ASN1_SET:
case V_ASN1_SEQUENCE:
default:
+ if (len != (long)ilen) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG);
+ goto err;
+ }
if (utype == V_ASN1_BMPSTRING && (len & 1)) {
ERR_raise(ERR_LIB_ASN1, ASN1_R_BMPSTRING_IS_WRONG_LENGTH);
goto err;
@@ -970,10 +980,10 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
}
/* If we've already allocated a buffer use it */
if (*free_cont) {
- ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, len);
+ ASN1_STRING_set0(stmp, (unsigned char *)cont /* UGLY CAST! */, ilen);
*free_cont = 0;
} else {
- if (!ASN1_STRING_set(stmp, cont, len)) {
+ if (!ASN1_STRING_set(stmp, cont, ilen)) {
ERR_raise(ERR_LIB_ASN1, ERR_R_ASN1_LIB);
ASN1_STRING_free(stmp);
*pval = NULL;

208
0063-CVE-2026-34181.patch Normal file
View File

@ -0,0 +1,208 @@
From 5a9e9489f9e5f7f7eb2f65c9b9fc75566cdcbc01 Mon Sep 17 00:00:00 2001
From: Alicja Kario <hkario@redhat.com>
Date: Wed, 29 Apr 2026 16:29:35 +0200
Subject: [PATCH] pkcs12: verify that the pbmac1 key length is safe
Short mac keys (as short as 1 byte) can be used to probe the
system under attack to accept a PKCS#12 file created by an attacker
even if the attacker doesn't know the password used for MAC protection.
Fixes CVE-2026-34181
(also update the reference to the PBMAC1 PKCS#12 RFC)
Signed-off-by: Alicja Kario <hkario@redhat.com>
---
crypto/pkcs12/p12_mutl.c | 8 +++++---
test/recipes/80-test_pkcs12.t | 13 ++++++++-----
.../pbmac1_256_256.bad-key-len.p12 | Bin 0 -> 2803 bytes
.../pbmac1_256_256.good-shorter-key-len.p12 | Bin 0 -> 2803 bytes
4 files changed, 13 insertions(+), 8 deletions(-)
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-key-len.p12
create mode 100644 test/recipes/80-test_pkcs12_data/pbmac1_256_256.good-shorter-key-len.p12
diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c
index 01956252df7..15072e12f26 100644
--- a/crypto/pkcs12/p12_mutl.c
+++ b/crypto/pkcs12/p12_mutl.c
@@ -144,11 +144,13 @@ static int PBMAC1_PBKDF2_HMAC(OSSL_LIB_CTX *ctx, const char *propq,
}
pbkdf2_salt = pbkdf2_param->salt->value.octet_string;
- /* RFC 9579 specifies missing key length as invalid */
+ /* RFC 9879 specifies missing key length as invalid */
if (pbkdf2_param->keylength != NULL)
keylen = ASN1_INTEGER_get(pbkdf2_param->keylength);
- if (keylen <= 0 || keylen > EVP_MAX_MD_SIZE) {
- ERR_raise(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR);
+ /* RFC 9879 specifies too short key length as untrustworthy too */
+ if (keylen < 20 || keylen > EVP_MAX_MD_SIZE) {
+ ERR_raise_data(ERR_LIB_PKCS12, PKCS12_R_PARSE_ERROR,
+ "Invalid Key length (%d is not in the range 20..64)", keylen);
goto err;
}
diff --git a/test/recipes/80-test_pkcs12.t b/test/recipes/80-test_pkcs12.t
index d258b7eb0e4..56ab93803e7 100644
--- a/test/recipes/80-test_pkcs12.t
+++ b/test/recipes/80-test_pkcs12.t
@@ -56,7 +56,7 @@ $ENV{OPENSSL_WIN32_UTF8}=1;
my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0);
-plan tests => $no_fips ? 53 : 59;
+plan tests => $no_fips ? 55 : 61;
# Test different PKCS#12 formats
ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats");
@@ -205,8 +205,11 @@ for my $instance (sort keys %pbmac1_tests) {
}
}
-# Test pbmac1 pkcs12 good files, RFC 9579
-for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12", "pbmac1_512_512.good.p12")
+# Test pbmac1 pkcs12 good files, RFC 9579, and one extra with shorter key
+# length
+for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12",
+ "pbmac1_512_512.good.p12",
+ "pbmac1_256_256.good-shorter-key-len.p12")
{
my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file);
ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])),
@@ -235,12 +238,12 @@ unless ($no_fips) {
}
}
-# Test pbmac1 pkcs12 bad files, RFC 9579 and CVE-2025-11187
+# Test pbmac1 pkcs12 bad files, RFC 9579, CVE-2025-11187 and CVE-2026-34181
for my $file ("pbmac1_256_256.bad-iter.p12", "pbmac1_256_256.bad-salt.p12",
"pbmac1_256_256.no-len.p12", "pbmac1_256_256.bad-len.p12",
"pbmac1_256_256.bad-salt-type.p12", "pbmac1_256_256.negative-len.p12",
"pbmac1_256_256.no-salt.p12", "pbmac1_256_256.very-big-len.p12",
- "pbmac1_256_256.zero-len.p12")
+ "pbmac1_256_256.zero-len.p12", "pbmac1_256_256.bad-key-len.p12")
{
my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file);
with({ exit_checker => sub { return shift == 1; } },
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-key-len.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.bad-key-len.p12
new file mode 100644
index 0000000000000000000000000000000000000000..7162fd1871790e3ab9cbc4e00fb73cdf63d5e64b
GIT binary patch
literal 2803
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
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_pkcs12_data/pbmac1_256_256.good-shorter-key-len.p12 b/test/recipes/80-test_pkcs12_data/pbmac1_256_256.good-shorter-key-len.p12
new file mode 100644
index 0000000000000000000000000000000000000000..3c202dd58d7fa1d3cd4f9d7252565aff2145ec96
GIT binary patch
literal 2803
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
literal 0
HcmV?d00001

536
0064-CVE-2026-34183.patch Normal file
View File

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

17
0065-CVE-2026-42764.patch Normal file
View File

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

241
0066-CVE-2026-42766.patch Normal file
View File

@ -0,0 +1,241 @@
From df32fc58bd726eecf88ac67b22fef0c8ff5eeef8 Mon Sep 17 00:00:00 2001
From: Igor Ustinov <igus@openssl.foundation>
Date: Thu, 21 May 2026 08:36:54 +0200
Subject: [PATCH 1/2] Fix potential NULL dereference processing CMS
PasswordRecipientInfo
Avoid NULL dereferencing when keyDerivationAlgorithm is absent
in CMS PasswordRecipientInfo.
Fixes CVE-2026-42766
---
crypto/cms/cms_pwri.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/crypto/cms/cms_pwri.c b/crypto/cms/cms_pwri.c
index ac869a37f93..206ecd85e69 100644
--- a/crypto/cms/cms_pwri.c
+++ b/crypto/cms/cms_pwri.c
@@ -368,6 +368,11 @@ int ossl_cms_RecipientInfo_pwri_crypt(const CMS_ContentInfo *cms,
/* Finish password based key derivation to setup key in "ctx" */
+ if (algtmp == NULL) {
+ ERR_raise_data(ERR_LIB_CMS, CMS_R_INVALID_KEY_ENCRYPTION_PARAMETER,
+ "Missing KeyDerivationAlgorithm");
+ goto err;
+ }
if (!EVP_PBE_CipherInit_ex(algtmp->algorithm,
(char *)pwri->pass, (int)pwri->passlen,
algtmp->parameter, kekctx, en_de,
From 1eb593cce1e05599d22de6ab158a23c755146fae Mon Sep 17 00:00:00 2001
From: Igor Ustinov <igus@openssl.foundation>
Date: Wed, 20 May 2026 20:02:43 +0200
Subject: [PATCH 2/2] Test for CVE-2026-42766
The script make_missing_kdf_der.py was developed by Mayank Jangid
and Kushal Khemka.
Co-Authored-by: Mayank Jangid <mayank.jangid.moon@gmail.com>
Co-Authored-by: Kushal Khemka <kushalkhemka559@gmail.com>
---
test/cms-msg/make_missing_kdf_der.py | 137 +++++++++++++++++++++++++++
test/cms-msg/missing-kdf.der | Bin 0 -> 190 bytes
test/recipes/80-test_cms.t | 21 +++-
3 files changed, 157 insertions(+), 1 deletion(-)
create mode 100755 test/cms-msg/make_missing_kdf_der.py
create mode 100644 test/cms-msg/missing-kdf.der
diff --git a/test/cms-msg/make_missing_kdf_der.py b/test/cms-msg/make_missing_kdf_der.py
new file mode 100755
index 00000000000..5b3fc0f6eed
--- /dev/null
+++ b/test/cms-msg/make_missing_kdf_der.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+
+# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+# This script generates missing-kdf.der - a password-encrypted CMS message
+# without the keyDerivationAlgorithm field, which is used in the
+# “PWRI missing keyDerivationAlgorithm regression” test.
+#
+# Usage: python3 make_missing_kdf_der.py valid.der missing-kdf.der
+
+from __future__ import annotations
+
+import argparse
+import sys
+from dataclasses import dataclass
+from pathlib import Path
+
+
+@dataclass
+class Node:
+ off: int
+ tag: int
+ hdr_len: int
+ length: int
+ end: int
+ children: list["Node"]
+
+
+def read_len(data: bytes, off: int) -> tuple[int, int]:
+ first = data[off]
+ if first < 0x80:
+ return first, 1
+ n = first & 0x7F
+ if n == 0 or n > 4:
+ raise ValueError(f"unsupported DER length form at {off}")
+ val = 0
+ for b in data[off + 1 : off + 1 + n]:
+ val = (val << 8) | b
+ return val, 1 + n
+
+
+def parse_node(data: bytes, off: int) -> Node:
+ tag = data[off]
+ length, len_len = read_len(data, off + 1)
+ hdr_len = 1 + len_len
+ end = off + hdr_len + length
+ children: list[Node] = []
+ if tag & 0x20:
+ cur = off + hdr_len
+ while cur < end:
+ child = parse_node(data, cur)
+ children.append(child)
+ cur = child.end
+ if cur != end:
+ raise ValueError(f"child parse ended at {cur}, expected {end}")
+ return Node(off=off, tag=tag, hdr_len=hdr_len, length=length, end=end, children=children)
+
+
+def encode_len(length: int, existing_len_len: int) -> bytes:
+ if existing_len_len == 1:
+ if length >= 0x80:
+ raise ValueError("new length no longer fits in short-form DER")
+ return bytes([length])
+ payload_len = existing_len_len - 1
+ max_len = (1 << (payload_len * 8)) - 1
+ if length > max_len:
+ raise ValueError("new length no longer fits in existing long-form DER")
+ out = bytearray([0x80 | payload_len])
+ for shift in range((payload_len - 1) * 8, -8, -8):
+ out.append((length >> shift) & 0xFF)
+ return bytes(out)
+
+
+def patch_length_field(buf: bytearray, node: Node, delta: int) -> None:
+ new_len = node.length + delta
+ if new_len < 0:
+ raise ValueError("negative patched length")
+ len_bytes = encode_len(new_len, node.hdr_len - 1)
+ start = node.off + 1
+ end = start + len(node.hdr_len.to_bytes(1, "big")) - 1 # unused, kept for clarity
+ buf[start : start + len(len_bytes)] = len_bytes
+
+
+def main() -> int:
+ ap = argparse.ArgumentParser(description="Remove PWRI keyDerivationAlgorithm from a CMS DER blob.")
+ ap.add_argument("input_der")
+ ap.add_argument("output_der")
+ args = ap.parse_args()
+
+ data = Path(args.input_der).read_bytes()
+ root = parse_node(data, 0)
+
+ # CMS structure we expect:
+ # SEQUENCE { OID envelopedData, [0] SEQUENCE { version, SET recipientInfos, ... } }
+ ed_wrapper = root.children[1]
+ env_seq = ed_wrapper.children[0]
+ recipient_set = env_seq.children[1]
+ pwri_choice = recipient_set.children[0] # [3]
+
+ if pwri_choice.tag != 0xA3:
+ raise ValueError(f"expected PWRI choice tag 0xA3, found 0x{pwri_choice.tag:02x}")
+ if len(pwri_choice.children) < 3:
+ raise ValueError("unexpected PWRI child count")
+
+ version = pwri_choice.children[0]
+ maybe_kdf = pwri_choice.children[1]
+ keyenc = pwri_choice.children[2]
+ if version.tag != 0x02:
+ raise ValueError("PWRI version is not INTEGER")
+ if maybe_kdf.tag != 0xA0:
+ raise ValueError(f"PWRI child after version is not [0] keyDerivationAlgorithm: 0x{maybe_kdf.tag:02x}")
+ if keyenc.tag != 0x30:
+ raise ValueError("PWRI keyEncryptionAlgorithm is not SEQUENCE")
+
+ remove_start = maybe_kdf.off
+ remove_end = maybe_kdf.end
+ remove_len = remove_end - remove_start
+
+ out = bytearray(data)
+ del out[remove_start:remove_end]
+
+ # Adjust ancestors whose length spans the removed field.
+ for node in [root, ed_wrapper, env_seq, recipient_set, pwri_choice]:
+ patch_length_field(out, node, -remove_len)
+
+ Path(args.output_der).write_bytes(out)
+ print(f"removed {remove_len} bytes at [{remove_start}, {remove_end})")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/test/cms-msg/missing-kdf.der b/test/cms-msg/missing-kdf.der
new file mode 100644
index 0000000000000000000000000000000000000000..3db602e47c23b76a9a55a707da18c5059ef38c9e
GIT binary patch
literal 190
zcmXqL+|9<R)#lOmotKfFc|qe^gT_@%jLe3OX^R_^nHU)iblA9|(wqX!oCdONoC$3n
zjH%2lj9M%LjeqC+*0CO$x3i6Lf!y{7Pgo2D?Y_1wJ@|E12X~D{Lpyt#=(Ru5SM)zF
zT>UakXma9=|F&g+-%70RE0WJSs>3LeamK&~VLc<7>0T^O8mGQjr>lr=GC8iQ@UuZ+
V#SA9-u!z04PD_OT{BCf}9snrZM1BAO
literal 0
HcmV?d00001
diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
index 152a1a55a0a..160ad81ae38 100644
--- a/test/recipes/80-test_cms.t
+++ b/test/recipes/80-test_cms.t
@@ -56,7 +56,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
$no_rc2 = 1 if disabled("legacy");
-plan tests => 31;
+plan tests => 32;
ok(run(test(["pkcs7_test"])), "test pkcs7");
@@ -1702,3 +1702,22 @@ subtest "ML-KEM KEMRecipientInfo tests for CMS" => sub {
"accept CMS verify with SLH-DSA-SHAKE-256s");
}
};
+
+# Regression test for NULL dereference in PWRI decrypt path
+# when optional keyDerivationAlgorithm is omitted.
+subtest "PWRI missing keyDerivationAlgorithm regression" => sub {
+ plan tests => 1;
+
+ with({ exit_checker => sub { return shift == 4; } }, sub {
+ ok(run(app([
+ "openssl", "cms", @prov,
+ "-decrypt",
+ "-inform", "DER",
+ "-in",
+ srctop_file('test', 'cms-msg', 'missing-kdf.der'),
+ "-out", "pwri-out.txt",
+ "-pwri_password", "secret"])),
+ "missing keyDerivationAlgorithm is rejected");
+ });
+};
+

29
0067-CVE-2026-42767.patch Normal file
View File

@ -0,0 +1,29 @@
diff --git a/crypto/crmf/crmf_lib.c b/crypto/crmf/crmf_lib.c
index d5ad51e4503..5747ab18569 100644
--- a/crypto/crmf/crmf_lib.c
+++ b/crypto/crmf/crmf_lib.c
@@ -762,6 +762,7 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *
EVP_CIPHER *cipher = NULL; /* used cipher */
int cikeysize = 0; /* key size from cipher */
unsigned char *iv = NULL; /* initial vector for symmetric encryption */
+ int iv_len; /* iv length */
unsigned char *out = NULL; /* decryption output buffer */
int n, ret = 0;
EVP_PKEY_CTX *pkctx = NULL; /* private key context */
@@ -811,11 +812,12 @@ unsigned char *OSSL_CRMF_ENCRYPTEDVALUE_decrypt(const OSSL_CRMF_ENCRYPTEDVALUE *
} else {
goto end;
}
- if ((iv = OPENSSL_malloc(EVP_CIPHER_get_iv_length(cipher))) == NULL)
+ iv_len = EVP_CIPHER_get_iv_length(cipher);
+ if ((iv = OPENSSL_malloc(iv_len)) == NULL)
goto end;
- if (ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv,
- EVP_CIPHER_get_iv_length(cipher))
- != EVP_CIPHER_get_iv_length(cipher)) {
+ if (enc->symmAlg->parameter == NULL
+ || ASN1_TYPE_get_octetstring(enc->symmAlg->parameter, iv, iv_len)
+ != iv_len) {
ERR_raise(ERR_LIB_CRMF, CRMF_R_MALFORMED_IV);
goto end;
}

86
0068-CVE-2026-42768.patch Normal file
View File

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

48
0069-CVE-2026-42769.patch Normal file
View File

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

21
0070-CVE-2026-42770.patch Normal file
View File

@ -0,0 +1,21 @@
diff --git a/providers/implementations/exchange/dh_exch.c b/providers/implementations/exchange/dh_exch.c
index 64215208212..b7c21526590 100644
--- a/providers/implementations/exchange/dh_exch.c
+++ b/providers/implementations/exchange/dh_exch.c
@@ -155,12 +155,15 @@ static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[])
static int dh_match_params(DH *priv, DH *peer)
{
int ret;
+ int ignore_q = 1;
FFC_PARAMS *dhparams_priv = ossl_dh_get0_params(priv);
FFC_PARAMS *dhparams_peer = ossl_dh_get0_params(peer);
+ if (dhparams_priv != NULL && dhparams_priv->q != NULL)
+ ignore_q = 0;
ret = dhparams_priv != NULL
&& dhparams_peer != NULL
- && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, 1);
+ && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, ignore_q);
if (!ret)
ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
return ret;

307
0071-CVE-2026-45445.patch Normal file
View File

@ -0,0 +1,307 @@
diff --git a/providers/implementations/ciphers/cipher_aes_ocb.c b/providers/implementations/ciphers/cipher_aes_ocb.c
index 1f6da4b2122..329d4bfa3e1 100644
--- a/providers/implementations/ciphers/cipher_aes_ocb.c
+++ b/providers/implementations/ciphers/cipher_aes_ocb.c
@@ -498,6 +498,19 @@ static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl,
return 0;
}
+ /*
+ * Mirror the streaming handler: refuse if the key has not been set,
+ * and push the buffered IV into the OCB context before any data is
+ * processed. Without this, CRYPTO_ocb128_encrypt/decrypt runs with
+ * Offset_0 = 0 regardless of the caller's IV -- catastrophic
+ * (key, nonce) reuse, and a subsequent EVP_*Final_ex() emits a tag
+ * that is a function of (key, iv) only.
+ */
+ if (!ctx->key_set || !update_iv(ctx)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
if (!aes_generic_ocb_cipher(ctx, in, out, inl)) {
ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
return 0;
diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
index f4bfeae2a1d..5cdf3122050 100644
--- a/test/evp_extra_test.c
+++ b/test/evp_extra_test.c
@@ -7119,6 +7119,269 @@ static int test_evp_cipher_negative_length(void)
return ret;
}
+/*
+ * Cross-driver round-trip test for AEAD one-shot vs streaming paths.
+ *
+ * The streaming path (EVP_CipherUpdate/Final, dispatched to
+ * OSSL_FUNC_CIPHER_UPDATE/_FINAL) is treated as the oracle. For each
+ * AEAD configuration we encrypt and decrypt the same (key, iv, aad, pt),
+ * driving the body in two combinations:
+ *
+ * 1. body encrypt via EVP_Cipher() (one-shot, OSSL_FUNC_CIPHER_CIPHER),
+ * body decrypt via EVP_CipherUpdate (streaming).
+ * 2. body encrypt via EVP_CipherUpdate, body decrypt via EVP_Cipher().
+ *
+ * Both combinations must recover the plaintext and verify the tag. AAD
+ * is always fed via EVP_CipherUpdate(NULL, ...): OCB's one-shot is body
+ * only and the asymmetric "AAD streaming, body one-shot" call shape is
+ * the natural pattern a caller reaching for EVP_Cipher() for throughput
+ * would write anyway.
+ *
+ * CVE-2026-45445 (AES-OCB EVP_Cipher() ignored IV) was a silent failure
+ * in this matrix: the one-shot encrypt path produced ciphertext under
+ * Offset_0 = 0 regardless of IV, which the streaming decrypt path then
+ * could not verify. Adding this cross-check catches the same class of
+ * bug for any future AEAD whose one-shot dispatch diverges from its
+ * streaming dispatch.
+ */
+typedef struct {
+ const char *name; /* EVP_CIPHER fetch name */
+ size_t keylen;
+ size_t ivlen;
+ size_t taglen;
+ int is_ccm; /* needs length-up-front + tag-before-body dance */
+} AEAD_ONESHOT_CFG;
+
+static const AEAD_ONESHOT_CFG aead_oneshot_cfgs[] = {
+ { "AES-128-GCM", 16, 12, 16, 0 },
+ { "AES-256-GCM", 32, 12, 16, 0 },
+ { "AES-128-CCM", 16, 12, 16, 1 },
+ { "AES-256-CCM", 32, 12, 16, 1 },
+ { "AES-128-OCB", 16, 12, 16, 0 },
+ { "AES-256-OCB", 32, 12, 16, 0 },
+ { "ChaCha20-Poly1305", 32, 12, 16, 0 }
+};
+
+/*
+ * Drive an encrypt or decrypt operation. AAD always via EVP_CipherUpdate.
+ * Body via EVP_Cipher() when oneshot_body is non-zero, EVP_CipherUpdate
+ * otherwise. On encrypt, fills *out and the caller-provided tag buffer.
+ * On decrypt, reads from in and verifies tag; returns 0 if verification
+ * fails (the test asserts the expected outcome).
+ */
+static int aead_oneshot_op(const AEAD_ONESHOT_CFG *cfg, int enc,
+ int oneshot_body, const unsigned char *key,
+ const unsigned char *iv, const unsigned char *aad,
+ size_t aad_len, const unsigned char *in, size_t in_len,
+ unsigned char *out, unsigned char *tag, const char **why)
+{
+ EVP_CIPHER_CTX *ctx = NULL;
+ EVP_CIPHER *cipher = NULL;
+ int outl = 0, tmpl = 0;
+ int ok = 0;
+ int body_rv;
+
+ *why = NULL;
+
+ if (!TEST_ptr(cipher = EVP_CIPHER_fetch(testctx, cfg->name, testpropq))) {
+ *why = "CIPHER_FETCH";
+ goto end;
+ }
+ if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new())) {
+ *why = "CTX_NEW";
+ goto end;
+ }
+ if (!TEST_true(EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, enc))) {
+ *why = "INIT_CIPHER";
+ goto end;
+ }
+ if (!TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
+ (int)cfg->ivlen, NULL),
+ 0)) {
+ *why = "SET_IVLEN";
+ goto end;
+ }
+ if (cfg->is_ccm) {
+ /* Placeholder taglen on encrypt, real tag on decrypt; both before key+iv. */
+ if (!TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
+ (int)cfg->taglen, enc ? NULL : tag),
+ 0)) {
+ *why = "CCM_SET_TAG";
+ goto end;
+ }
+ }
+ if (!TEST_true(EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, enc))) {
+ *why = "INIT_KEY_IV";
+ goto end;
+ }
+ if (cfg->is_ccm) {
+ if (!TEST_true(EVP_CipherUpdate(ctx, NULL, &outl, NULL, (int)in_len))) {
+ *why = "CCM_LEN_DECL";
+ goto end;
+ }
+ }
+ if (aad_len > 0
+ && !TEST_true(EVP_CipherUpdate(ctx, NULL, &outl, aad, (int)aad_len))) {
+ *why = "AAD";
+ goto end;
+ }
+ if (!enc && !cfg->is_ccm
+ && !TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
+ (int)cfg->taglen, tag),
+ 0)) {
+ *why = "SET_TAG";
+ goto end;
+ }
+
+ if (oneshot_body) {
+ body_rv = EVP_Cipher(ctx, out, in, (unsigned int)in_len);
+ if (cfg->is_ccm && !enc) {
+ /* CCM decrypt: 0 means tag verify failed, < 0 means error. */
+ if (!TEST_int_gt(body_rv, 0)) {
+ *why = "ONESHOT_DECRYPT";
+ goto end;
+ }
+ } else {
+ if (!TEST_int_ge(body_rv, 0)) {
+ *why = "ONESHOT_BODY";
+ goto end;
+ }
+ }
+ outl = (int)in_len;
+ } else {
+ if (!TEST_true(EVP_CipherUpdate(ctx, out, &outl, in, (int)in_len))) {
+ *why = enc ? "STREAM_BODY_ENC" : "STREAM_BODY_DEC";
+ goto end;
+ }
+ }
+
+ if (!cfg->is_ccm) {
+ if (!TEST_true(EVP_CipherFinal_ex(ctx, out + outl, &tmpl))) {
+ *why = enc ? "FINAL_ENC" : "FINAL_DEC";
+ goto end;
+ }
+ }
+
+ if (enc) {
+ if (!TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG,
+ (int)cfg->taglen, tag),
+ 0)) {
+ *why = "GET_TAG";
+ goto end;
+ }
+ }
+ ok = 1;
+end:
+ EVP_CIPHER_CTX_free(ctx);
+ EVP_CIPHER_free(cipher);
+ return ok;
+}
+
+/*
+ * For each AEAD row we run two AAD modes, and within each AAD mode two
+ * cross-driver round trips:
+ *
+ * aad_mode 0: no AAD. Critical for catching the OCB-style bug: any
+ * EVP_CipherUpdate(NULL, aad, ...) call before the body
+ * would itself pass through the (correct) streaming
+ * handler and apply the buffered IV, masking the one-shot
+ * handler's failure to do so. With aad_len == 0 we make
+ * EVP_Cipher() the very first cipher operation on the
+ * context, which is the shape the bug requires.
+ *
+ * aad_mode 1: with AAD via streaming. Catches divergence between the
+ * drivers when AAD is in play.
+ *
+ * leg 0: encrypt-oneshot + decrypt-streaming
+ * leg 1: encrypt-streaming + decrypt-oneshot
+ *
+ * The test index encodes (cipher, aad_mode) so a failure points at both.
+ */
+static int test_aead_oneshot_roundtrip(int idx)
+{
+ static const unsigned char fixed_key[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+ };
+ static const unsigned char fixed_iv[12] = {
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab
+ };
+ static const unsigned char fixed_aad[] = "extra:context";
+ static const unsigned char fixed_pt[] = "THE QUICK BROWN FOX JUMPS OVER LAZY!!";
+ const AEAD_ONESHOT_CFG *cfg = &aead_oneshot_cfgs[idx / 2];
+ int with_aad = idx % 2;
+ size_t aad_len = with_aad ? sizeof(fixed_aad) - 1 : 0;
+ size_t pt_len = sizeof(fixed_pt) - 1;
+ EVP_CIPHER *probe = NULL;
+ unsigned char ct[64], pt[64];
+ unsigned char tag_oneshot[16], tag_stream[16];
+ const char *why = NULL;
+ int leg, ok = 0;
+
+ /*
+ * Probe for the cipher: a build with no-ocb / no-chacha / etc. will
+ * not have it, and we treat that as a pass (nothing to test here).
+ */
+ ERR_set_mark();
+ probe = EVP_CIPHER_fetch(testctx, cfg->name, testpropq);
+ ERR_pop_to_mark();
+ if (probe == NULL) {
+ TEST_info("skipping, '%s' is not available", cfg->name);
+ return 1;
+ }
+ EVP_CIPHER_free(probe);
+
+ for (leg = 0; leg <= 1; leg++) {
+ int enc_oneshot = (leg == 0);
+ unsigned char *tag = enc_oneshot ? tag_oneshot : tag_stream;
+
+ memset(ct, 0, sizeof(ct));
+ memset(pt, 0, sizeof(pt));
+ memset(tag, 0, cfg->taglen);
+
+ if (!aead_oneshot_op(cfg, /*enc=*/1, /*oneshot_body=*/enc_oneshot,
+ fixed_key, fixed_iv, fixed_aad, aad_len,
+ fixed_pt, pt_len, ct, tag, &why)) {
+ TEST_error("%s (%s): encrypt leg %d (%s body) failed at %s",
+ cfg->name, with_aad ? "with AAD" : "no AAD",
+ leg, enc_oneshot ? "oneshot" : "stream",
+ why ? why : "?");
+ goto end;
+ }
+ if (!aead_oneshot_op(cfg, /*enc=*/0, /*oneshot_body=*/!enc_oneshot,
+ fixed_key, fixed_iv, fixed_aad, aad_len,
+ ct, pt_len, pt, tag, &why)) {
+ TEST_error("%s (%s): decrypt leg %d (%s body) failed at %s",
+ cfg->name, with_aad ? "with AAD" : "no AAD",
+ leg, enc_oneshot ? "stream" : "oneshot",
+ why ? why : "?");
+ goto end;
+ }
+ if (!TEST_mem_eq(pt, pt_len, fixed_pt, pt_len)) {
+ TEST_error("%s (%s): leg %d: recovered plaintext differs",
+ cfg->name, with_aad ? "with AAD" : "no AAD", leg);
+ goto end;
+ }
+ }
+
+ /*
+ * Both legs share the same (key, iv, aad, pt) and must therefore
+ * agree on the tag bit-for-bit, regardless of which driver computed
+ * it. This catches the OCB-style failure where the one-shot path
+ * silently emits a different ciphertext/tag from the streaming path.
+ */
+ if (!TEST_mem_eq(tag_oneshot, cfg->taglen, tag_stream, cfg->taglen)) {
+ TEST_error("%s (%s): oneshot-encrypt tag != streaming-encrypt tag",
+ cfg->name, with_aad ? "with AAD" : "no AAD");
+ goto end;
+ }
+ ok = 1;
+end:
+ return ok;
+}
+
static int test_aes_rc4_keylen_change_cve_2023_5363(void)
{
/* RC4 test data obtained from RFC 6229 */
@@ -8030,6 +8294,8 @@ int setup_tests(void)
ADD_TEST(test_aes_rc4_keylen_change_cve_2023_5363);
#endif
+ ADD_ALL_TESTS(test_aead_oneshot_roundtrip, 2 * OSSL_NELEM(aead_oneshot_cfgs));
+
ADD_TEST(test_invalid_ctx_for_digest);
ADD_TEST(test_evp_cipher_pipeline);

231
0072-CVE-2026-45446.patch Normal file
View File

@ -0,0 +1,231 @@
diff --git a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
index 4b2117a94ca..452c9f00fea 100644
--- a/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
+++ b/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
@@ -58,6 +58,9 @@ static int aes_gcm_siv_initkey(void *vctx)
memset(&data, 0, sizeof(data));
memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE);
+ ctx->generated_tag = 0;
+ memset(ctx->tag, 0, TAG_SIZE);
+
/* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */
/* counter is stored little-endian */
for (i = 0; i < BLOCK_SIZE; i += 8) {
@@ -134,17 +137,6 @@ static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx,
return 1;
}
-static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
-{
- int ret = 0;
-
- if (ctx->enc)
- return ctx->generated_tag;
- ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
- ret &= ctx->have_user_tag;
- return ret;
-}
-
static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
unsigned char *out, size_t len)
{
@@ -271,6 +263,19 @@ static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *i
return !error;
}
+static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
+{
+ int ret = 0;
+
+ if (ctx->enc)
+ return ctx->generated_tag;
+ if (!ctx->generated_tag)
+ aes_gcm_siv_decrypt(ctx, NULL, NULL, 0);
+ ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
+ ret &= ctx->have_user_tag;
+ return ret;
+}
+
static int aes_gcm_siv_cipher(void *vctx, unsigned char *out,
const unsigned char *in, size_t len)
{
diff --git a/providers/implementations/ciphers/cipher_aes_siv.c b/providers/implementations/ciphers/cipher_aes_siv.c
index 38f6977bf77..f67c015f864 100644
--- a/providers/implementations/ciphers/cipher_aes_siv.c
+++ b/providers/implementations/ciphers/cipher_aes_siv.c
@@ -193,6 +193,7 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
const OSSL_PARAM *p;
unsigned int speed = 0;
+ SIV128_CONTEXT *sctx = &ctx->siv;
if (ossl_param_is_empty(params))
return 1;
@@ -226,6 +227,8 @@ static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
if (keylen != ctx->keylen)
return 0;
}
+ sctx->final_ret = -1;
+
return 1;
}
diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
index f4bfeae2a1d..ebecb36cc93 100644
--- a/test/evp_extra_test.c
+++ b/test/evp_extra_test.c
@@ -7062,6 +7062,142 @@ static int test_aes_rc4_keylen_change_cve_2023_5363(void)
}
#endif
+static int test_aes_gcm_siv_empty_data(void)
+{
+ unsigned char key[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 };
+ unsigned char nonce[12] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
+ 0x22, 0x33, 0x44, 0x55 };
+ unsigned char aad[33] = "this AAD was never authenticated";
+ unsigned char zero_tag[16] = { 0 };
+ unsigned char real_tag[16];
+ unsigned char out[16];
+ int outl, ret = 0;
+ EVP_CIPHER_CTX *ctx = NULL;
+ EVP_CIPHER *c = EVP_CIPHER_fetch(NULL, "AES-128-GCM-SIV", NULL);
+
+ if (c == NULL) {
+ return TEST_skip("AES-128-GCM-SIV cipher is not available");
+ }
+
+ /* Compute the CORRECT tag for (key,nonce,aad,pt="") via encrypt */
+ ctx = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(ctx)
+ || !TEST_true(EVP_EncryptInit_ex2(ctx, c, key, nonce, NULL))
+ || !TEST_true(EVP_EncryptUpdate(ctx, NULL, &outl, aad, sizeof(aad))) /* AAD */
+ || !TEST_true(EVP_EncryptUpdate(ctx, out, &outl, aad, 0)) /* empty PT, out!=NULL */
+ || !TEST_true(EVP_EncryptFinal_ex(ctx, out, &outl))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, real_tag)))
+ goto err;
+ EVP_CIPHER_CTX_free(ctx);
+
+ /* SANITY: decrypt with CORRECT tag and an explicit empty-PT Update */
+ ctx = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(ctx)
+ || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, real_tag))
+ || !TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad)))
+ || !TEST_true(EVP_DecryptUpdate(ctx, out, &outl, aad, 0)) /* force aes_gcm_siv_decrypt(len=0) */
+ || !TEST_true(EVP_DecryptFinal_ex(ctx, out, &outl)))
+ goto err;
+ EVP_CIPHER_CTX_free(ctx);
+
+ /* FORGERY A: AAD only, NO ciphertext Update, ALL-ZERO tag */
+ ctx = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(ctx)
+ || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, zero_tag))
+ || !TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad))) /* AAD only, out==NULL */
+ || !TEST_false(EVP_DecryptFinal_ex(ctx, out, &outl)))
+ goto err;
+ EVP_CIPHER_CTX_free(ctx);
+
+ /* FORGERY B: no AAD, no Update at all, ALL-ZERO tag */
+ ctx = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(ctx)
+ || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, zero_tag))
+ || !TEST_false(EVP_DecryptFinal_ex(ctx, out, &outl)))
+ goto err;
+ EVP_CIPHER_CTX_free(ctx);
+
+ /* CONTROL: AAD only, NO ciphertext Update, CORRECT tag */
+ ctx = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(ctx)
+ || !TEST_true(EVP_DecryptInit_ex2(ctx, c, key, nonce, NULL))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, real_tag))
+ || !TEST_true(EVP_DecryptUpdate(ctx, NULL, &outl, aad, sizeof(aad)))
+ || !TEST_true(EVP_DecryptFinal_ex(ctx, out, &outl)))
+ goto err;
+ EVP_CIPHER_CTX_free(ctx);
+ ctx = NULL;
+
+ ret = 1;
+err:
+ EVP_CIPHER_CTX_free(ctx);
+
+ EVP_CIPHER_free(c);
+ return ret;
+}
+
+/*
+ * AES-SIV reuse-without-rekey:
+ * msg1: legit non-empty CT, tag verifies, final_ret=0
+ * msg2: no reinit (or reinit with key=NULL), set forged tag,
+ * AAD only, DecryptFinal -> does stale final_ret leak through?
+ */
+static int test_aes_siv_ctx_reuse(void)
+{
+ unsigned char key[32] = { 7 }; /* AES-128-SIV => 2*16 */
+ unsigned char pt[9] = "payload!";
+ unsigned char ct[9], tagbuf[16], out[16], zero16[16] = { 0 };
+ unsigned char aad[14] = "forged header";
+ int outl, ret = 0;
+ EVP_CIPHER_CTX *e = NULL, *d = NULL;
+ EVP_CIPHER *c = EVP_CIPHER_fetch(NULL, "AES-128-SIV", NULL);
+
+ if (c == NULL) {
+ return TEST_skip("AES-128-SIV cipher is not available");
+ }
+
+ /* produce a valid (ct,tag) for msg1 */
+ e = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(e)
+ || !TEST_true(EVP_EncryptInit_ex2(e, c, key, NULL, NULL))
+ || !TEST_true(EVP_EncryptUpdate(e, NULL, &outl, (unsigned char *)"hdr1", 4))
+ || !TEST_true(EVP_EncryptUpdate(e, ct, &outl, pt, sizeof(pt)))
+ || !TEST_true(EVP_EncryptFinal_ex(e, out, &outl))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(e, EVP_CTRL_AEAD_GET_TAG, 16, tagbuf))) {
+ EVP_CIPHER_CTX_free(e);
+ goto err;
+ }
+ EVP_CIPHER_CTX_free(e);
+
+ /* msg1 decrypt */
+ d = EVP_CIPHER_CTX_new();
+ if (!TEST_ptr(d)
+ || !TEST_true(EVP_DecryptInit_ex2(d, c, key, NULL, NULL))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(d, EVP_CTRL_AEAD_SET_TAG, 16, tagbuf))
+ || !TEST_true(EVP_DecryptUpdate(d, NULL, &outl, (unsigned char *)"hdr1", 4))
+ || !TEST_true(EVP_DecryptUpdate(d, out, &outl, ct, sizeof(ct)))
+ || !TEST_true(EVP_DecryptFinal_ex(d, out, &outl)))
+ goto err;
+
+ /* msg2 on SAME ctx, reinit with key=NULL => initkey skipped, final_ret should be reset */
+ if (!TEST_true(EVP_DecryptInit_ex2(d, NULL, NULL, NULL, NULL))
+ || !TEST_true(EVP_CIPHER_CTX_ctrl(d, EVP_CTRL_AEAD_SET_TAG, 16, zero16))
+ || !TEST_true(EVP_DecryptUpdate(d, NULL, &outl, aad, sizeof(aad))) /* forged AAD */
+ || !TEST_false(EVP_DecryptFinal_ex(d, out, &outl)))
+ goto err;
+
+ ret = 1;
+
+err:
+ EVP_CIPHER_CTX_free(d);
+ EVP_CIPHER_free(c);
+ return ret;
+}
+
static int test_invalid_ctx_for_digest(void)
{
int ret;
@@ -8030,6 +8166,10 @@ int setup_tests(void)
ADD_TEST(test_aes_rc4_keylen_change_cve_2023_5363);
#endif
+ /* Test cases for CVE-2026-45446 */
+ ADD_TEST(test_aes_gcm_siv_empty_data);
+ ADD_TEST(test_aes_siv_ctx_reuse);
+
ADD_ALL_TESTS(test_aead_oneshot_roundtrip, 2 * OSSL_NELEM(aead_oneshot_cfgs));
ADD_TEST(test_invalid_ctx_for_digest);

139
0073-CVE-2026-45447.patch Normal file
View File

@ -0,0 +1,139 @@
From 16aaeaab46d407f739fc74e3d89ae4fc43ef77c2 Mon Sep 17 00:00:00 2001
From: Igor Ustinov <igus@openssl.foundation>
Date: Sat, 16 May 2026 08:16:23 +0200
Subject: [PATCH 1/2] Fix possible use-after-free in OpenSSL PKCS7_verify()
Fixes CVE-2026-45447
---
crypto/pkcs7/pk7_smime.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/crypto/pkcs7/pk7_smime.c b/crypto/pkcs7/pk7_smime.c
index 4bf26331c1a..49129690deb 100644
--- a/crypto/pkcs7/pk7_smime.c
+++ b/crypto/pkcs7/pk7_smime.c
@@ -221,6 +221,7 @@ int PKCS7_verify(PKCS7 *p7, const STACK_OF(X509) *certs, X509_STORE *store,
int i, j = 0, k, ret = 0;
BIO *p7bio = NULL;
BIO *tmpout = NULL;
+ BIO *next = NULL;
const PKCS7_CTX *p7_ctx;
if (p7 == NULL) {
@@ -351,9 +352,11 @@ int PKCS7_verify(PKCS7 *p7, const STACK_OF(X509) *certs, X509_STORE *store,
BIO_free(tmpout);
X509_STORE_CTX_free(cert_ctx);
OPENSSL_free(buf);
- if (indata != NULL)
- BIO_pop(p7bio);
- BIO_free_all(p7bio);
+ while (p7bio != NULL && p7bio != indata) {
+ next = BIO_pop(p7bio);
+ BIO_free(p7bio);
+ p7bio = next;
+ }
sk_X509_free(signers);
sk_X509_free(untrusted);
return ret;
From a6622a0503575097f1faefc0781f5b3916bb3ffc Mon Sep 17 00:00:00 2001
From: Igor Ustinov <igus@openssl.foundation>
Date: Sat, 16 May 2026 08:22:53 +0200
Subject: [PATCH 2/2] Test for CVE-2026-45447 (UAF in PKCS7_verify)
The test data were created with a tool developed by
Thai Duong <thai@calif.io>.
---
test/recipes/80-test_cms.t | 19 +++++++++-
test/smime-eml/pkcs7-empty-digest-set.eml | 45 +++++++++++++++++++++++
2 files changed, 63 insertions(+), 1 deletion(-)
create mode 100644 test/smime-eml/pkcs7-empty-digest-set.eml
diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
index 152a1a55a0a..cf76537200e 100644
--- a/test/recipes/80-test_cms.t
+++ b/test/recipes/80-test_cms.t
@@ -56,7 +56,7 @@ my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
$no_rc2 = 1 if disabled("legacy");
-plan tests => 32;
+plan tests => 33;
ok(run(test(["pkcs7_test"])), "test pkcs7");
@@ -1263,6 +1263,23 @@ subtest "CMS code signing test" => sub {
"fail verify CMS signature with code signing certificate for purpose smime_sign");
};
+# Regression test for PKCS7_verify() ownership handling when
+# digestAlgorithms is an empty SET.
+# The malformed structure must fail cleanly without crashing or
+# triggering use-after-free behaviour.
+with({ exit_checker => sub { return shift == 4; } },
+ sub {
+ ok(run(app([
+ 'openssl', 'smime',
+ '-verify',
+ '-noverify',
+ '-in',
+ srctop_file('test', 'smime-eml',
+ 'pkcs7-empty-digest-set.eml'),
+ ])),
+ "Check empty digestAlgorithms SET is handled safely");
+ });
+
# Test case for missing MD algorithm (must not segfault)
with({ exit_checker => sub { return shift == 4; } },
diff --git a/test/smime-eml/pkcs7-empty-digest-set.eml b/test/smime-eml/pkcs7-empty-digest-set.eml
new file mode 100644
index 00000000000..a6db2c38adf
--- /dev/null
+++ b/test/smime-eml/pkcs7-empty-digest-set.eml
@@ -0,0 +1,45 @@
+MIME-Version: 1.0
+Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha-256"; boundary="----E0314CC5D732C92AE2D7A3BACDCDCFCE"
+
+This is an S/MIME signed message
+
+------E0314CC5D732C92AE2D7A3BACDCDCFCE
+This is the content to be signed.
+
+------E0314CC5D732C92AE2D7A3BACDCDCFCE
+Content-Type: application/x-pkcs7-signature; name="smime.p7s"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment; filename="smime.p7s"
+
+MIIFWgYJKoZIhvcNAQcCoIIFSzCCBUcCAQExADALBgkqhkiG9w0BBwGgggLuMIIC
+6jCCAdKgAwIBAgIUL5E46FxyhsT7C3G1NS27OtR7XAowDQYJKoZIhvcNAQELBQAw
+FTETMBEGA1UEAwwKUG9DIFNpZ25lcjAeFw0yNjA1MDgxMDIwNDhaFw0yNzA1MDgx
+MDIwNDhaMBUxEzARBgNVBAMMClBvQyBTaWduZXIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDSSu/gupmIlclvmTMHiqOrCqmB8NRTjAMoI//MPJrnFXYp
+FjDPMk7Y/kCcHztudaIvADkowaFtOm4oMinQFhjwCNCo5K5WrrlAitnpcd5QH2nA
+iVZXjjohQUJEd7n33AGqTwo5EGaCK+alAZL7tA7bdhNi/aZ33L3bUNYqoHbXiNsE
+u1tj8frLfIjduOt0TMPSOrrFjjEsrL3T3tg+HmxpalDHz7E6o9zJu0wlk8bcR2Xk
+mpX8RdYCu7K9m39N1F2WKa9WJh24NQLpWRfwD213jaIFK2EXy/XHePDUeiMYtVOV
+oovCSmY7OqowupA7J+4dcsnRjFqgZECctHhAfk+PAgMBAAGjMjAwMB0GA1UdDgQW
+BBRZlupXNYq4fny0SE76sr/CdQ2DUTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBCwUAA4IBAQANOlttTWVz620JNTrPzhiR4x9+5UiF4GSqv8BRJQFj3Xh7fsUp
++3GDs9M27f4FVh3utJsjt7Sa9ZWLpBVdgjGBwGLAtPsoYMjhnUgZTUvwEk5+aXyv
+zJxn4I7mMbDhlNCMHcVtGdtA+2UOEuvdGfuEilpzPsV8DzM1K3xU5bSWoo0BRFKK
+srHkyEfxCFPAQOcX80ZbMO6zdcXeJjC6mQXGqy2aqeQob0vuSZJ7QHZBlRjY5YHR
+wWlIqG8G3Eist16iTqdX2PQFZT1/QAEQ/LnXARTUUjUroccdci8YNASoeHDpcjRL
+MBrN+QBNZVt5qLhDogwZb2ZwqKfZ8Aqg3oAkMYICPzCCAjsCAQEwLTAVMRMwEQYD
+VQQDDApQb0MgU2lnbmVyAhQvkTjoXHKGxPsLcbU1Lbs61HtcCjANBglghkgBZQME
+AgEFAKCB5DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEP
+Fw0yNjA1MDgxMDIwNDhaMC8GCSqGSIb3DQEJBDEiBCAvyoHfycLqb8UzVPizy1uA
+o3h7tza3HebeiJaSnpIJHzB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjAL
+BglghkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMC
+AgIAgDANBggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDANBgkq
+hkiG9w0BAQEFAASCAQBIpl7U2j4YiU1vdZHyx2dCK41ZahtTVOB4RVJcrmopgans
+fICdkSTfb0dVqc13++bYn4i1b2R2os5YIkoGxdrM5aZB7KF9r1xwgrendTF4/BwP
+gQq2khNtKebv9Yr0kOPynFIsgx5BHk99wrzfwidJUFuJJgQ9W0YOf7EGkbnZvPT+
+hV0aeLmJAb5jjWhbDciqUjR3O23JQhzVj4U3vo2TeN7VYmNJsX+fA4sZzIbYSei9
+ps7GZruiRcKgqgUj1l8HjIGMHqd9lccchk/BYyAGxAbgGisntvfJdPZO09wG8rHh
+eS6FYkkXAKBO49WbhE9aVLJH0zgA6gTfyEvOOOS1
+
+------E0314CC5D732C92AE2D7A3BACDCDCFCE--
+

230
0074-CVE-2026-34182.patch Normal file
View File

@ -0,0 +1,230 @@
diff --git a/crypto/cms/cms_enc.c b/crypto/cms/cms_enc.c
index 08afb5ab114..ba7082cebd7 100644
--- a/crypto/cms/cms_enc.c
+++ b/crypto/cms/cms_enc.c
@@ -109,13 +109,15 @@ BIO *ossl_cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec,
goto err;
}
piv = aparams.iv;
- if (ec->taglen > 0
- && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
- ec->taglen, ec->tag)
- <= 0) {
+
+ if (ec->taglen < 4 || ec->taglen > 16
+ || EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, (int)ec->taglen, ec->tag) <= 0) {
ERR_raise(ERR_LIB_CMS, CMS_R_CIPHER_AEAD_SET_TAG_ERROR);
goto err;
}
+ } else if (auth) {
+ ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_CONTENT_ENCRYPTION_ALGORITHM);
+ goto err;
}
}
len = EVP_CIPHER_CTX_get_key_length(ctx);
diff --git a/test/cmsapitest.c b/test/cmsapitest.c
index 0752d14df09..01c0c738bdc 100644
--- a/test/cmsapitest.c
+++ b/test/cmsapitest.c
@@ -22,6 +22,191 @@ static EVP_PKEY *privkey = NULL;
static char *too_long_iv_cms_in = NULL;
static char *pwri_kek_oob_der_in = NULL;
+/*
+ * This is our bad cms data, it contains an AuthEnvelopedData field
+ * with a CIPHER OID set to AES-256-OFB
+ */
+static const unsigned char bad_cms_der[452] = {
+ 0x30, 0x82, 0x01, 0xc0, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x10, 0x01, 0x17, 0xa0, 0x82, 0x01, 0xaf, 0x30, 0x82, 0x01,
+ 0xab, 0x02, 0x01, 0x00, 0x31, 0x82, 0x01, 0x44, 0x30, 0x82, 0x01, 0x40,
+ 0x02, 0x01, 0x00, 0x30, 0x28, 0x30, 0x10, 0x31, 0x0e, 0x30, 0x0c, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x52, 0x65, 0x63, 0x69, 0x70, 0x02,
+ 0x14, 0x1a, 0x5c, 0x04, 0x9b, 0x3a, 0x64, 0xff, 0xd4, 0x63, 0xde, 0x4f,
+ 0x90, 0xe5, 0x76, 0xe2, 0x18, 0xe8, 0x5c, 0x9e, 0xd7, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x04, 0x82, 0x01, 0x00, 0x18, 0xcf, 0x9f, 0x44, 0x95, 0x79, 0xe9, 0x96,
+ 0x7d, 0x0f, 0xd1, 0xb4, 0xc2, 0x38, 0xb0, 0xc9, 0x76, 0xd5, 0xba, 0x08,
+ 0x5c, 0xbf, 0xc3, 0x30, 0xea, 0x3a, 0x68, 0xa4, 0xba, 0x99, 0x4c, 0x70,
+ 0x97, 0xb8, 0xa9, 0xce, 0x71, 0x4c, 0x54, 0xa3, 0xfd, 0x81, 0x9e, 0x15,
+ 0x63, 0xb7, 0x23, 0x46, 0x17, 0x69, 0xaf, 0x8f, 0xbd, 0xa3, 0x54, 0x23,
+ 0xf3, 0xf5, 0x35, 0xa8, 0xd4, 0x9c, 0xec, 0xe1, 0x17, 0x2c, 0x6d, 0x0b,
+ 0xad, 0xc0, 0xe9, 0x1d, 0xd1, 0x8d, 0x59, 0xd5, 0x29, 0xc6, 0x40, 0xc4,
+ 0xcd, 0x4e, 0x87, 0x70, 0x19, 0x5d, 0x88, 0x50, 0xbd, 0x4a, 0x13, 0xb3,
+ 0xef, 0x0c, 0x6d, 0x6a, 0xc5, 0x51, 0xbb, 0x5c, 0x39, 0x17, 0xda, 0xb1,
+ 0x71, 0x17, 0x88, 0xfb, 0x6a, 0xef, 0x7f, 0x85, 0xa7, 0x04, 0x71, 0xc7,
+ 0x83, 0x91, 0xb3, 0x30, 0x1b, 0x3d, 0x18, 0x7f, 0x63, 0xbf, 0x42, 0x7c,
+ 0xae, 0x6f, 0xae, 0xa1, 0x17, 0x84, 0xfd, 0x67, 0x2a, 0x4f, 0x4c, 0xe9,
+ 0x05, 0x26, 0x2c, 0xd5, 0xab, 0x0c, 0xcf, 0xdc, 0x3f, 0x24, 0xcf, 0x71,
+ 0x26, 0x7a, 0x1f, 0xf7, 0xc9, 0x92, 0x5e, 0xb6, 0x3d, 0x7f, 0xc3, 0x08,
+ 0xd3, 0xad, 0xc0, 0xc8, 0x4f, 0x42, 0x0c, 0xf3, 0xac, 0x23, 0x11, 0xdf,
+ 0x75, 0x84, 0x69, 0x8c, 0xa6, 0x59, 0x43, 0xfb, 0xf7, 0x6b, 0x62, 0xf0,
+ 0xf7, 0x35, 0x07, 0xc4, 0xf8, 0xd5, 0x12, 0x4a, 0x16, 0x62, 0xbc, 0x04,
+ 0xaa, 0x9a, 0x2e, 0xb2, 0x1a, 0xfa, 0x4c, 0x82, 0xce, 0x9e, 0xa8, 0x6d,
+ 0xc1, 0x29, 0x59, 0xe0, 0x33, 0xb5, 0xa6, 0x47, 0x09, 0x2e, 0xbf, 0x60,
+ 0xa6, 0xb3, 0x21, 0xa0, 0x15, 0xac, 0x92, 0x29, 0xb5, 0xe6, 0xe0, 0xd4,
+ 0x8b, 0xd8, 0x21, 0xe2, 0x17, 0x98, 0xd1, 0x11, 0x5d, 0xc5, 0xae, 0x24,
+ 0xe8, 0x92, 0xdb, 0x96, 0xa3, 0x5b, 0x58, 0xa7, 0x30, 0x4c, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1d, 0x06,
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2b, 0x04, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x20, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * This array represents a der encoded contentinfo structure addressed to
+ * servercert.pem, with the tag value of the aes-256-gcm cipher used to encrypt
+ * the contents of the mssages down to 1 byte. Decoding it should fail
+ */
+static const unsigned char one_byte_mac_cms_der[423] = {
+ 0x30, 0x82, 0x01, 0xa3, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x10, 0x01, 0x17, 0xa0, 0x82, 0x01, 0x92, 0x30, 0x82, 0x01,
+ 0x8e, 0x02, 0x01, 0x00, 0x31, 0x82, 0x01, 0x33, 0x30, 0x82, 0x01, 0x2f,
+ 0x02, 0x01, 0x00, 0x30, 0x17, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x07, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x41, 0x02, 0x01, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x10,
+ 0x3a, 0x8c, 0xee, 0x4e, 0xe2, 0x1f, 0xfe, 0xcc, 0x28, 0x39, 0x9e, 0x46,
+ 0xbe, 0xa7, 0xd5, 0x02, 0x2a, 0x53, 0x06, 0x5f, 0x94, 0x6b, 0x69, 0x6d,
+ 0x2d, 0xe8, 0x44, 0xa6, 0x43, 0x52, 0x82, 0x89, 0x2d, 0xf1, 0x9b, 0xb9,
+ 0x9e, 0xa4, 0x8d, 0x77, 0xf1, 0xd2, 0x8e, 0x86, 0x79, 0x06, 0x3e, 0x90,
+ 0xf0, 0xca, 0x9e, 0xb5, 0x35, 0xd5, 0x89, 0xf0, 0x7c, 0x06, 0xa0, 0x91,
+ 0xbf, 0xf4, 0x61, 0xaa, 0x5c, 0x99, 0xa3, 0x64, 0x15, 0xfd, 0xf9, 0x90,
+ 0xf0, 0xf3, 0x25, 0x5b, 0x48, 0xa1, 0xfb, 0x7a, 0xce, 0x63, 0xdc, 0xa9,
+ 0xfe, 0x7c, 0xbe, 0x9c, 0xaa, 0xd3, 0x42, 0x0e, 0x4a, 0xc3, 0x4b, 0x4e,
+ 0x76, 0x6d, 0x52, 0x54, 0x85, 0x4e, 0xab, 0x50, 0x2c, 0x5f, 0xc2, 0x8b,
+ 0x9f, 0x1f, 0x0f, 0x8a, 0x7c, 0xb3, 0x0a, 0xde, 0x50, 0x9b, 0xef, 0x89,
+ 0xf2, 0xea, 0x07, 0xca, 0x11, 0x76, 0x29, 0xaf, 0xe4, 0x59, 0x28, 0x19,
+ 0x48, 0x96, 0x67, 0xdd, 0xdd, 0x01, 0xf0, 0x14, 0xbe, 0x3d, 0xa5, 0xa3,
+ 0x83, 0x21, 0x39, 0x29, 0xb7, 0x8f, 0xb7, 0xf4, 0x85, 0x05, 0xee, 0xca,
+ 0xbb, 0xbd, 0xc0, 0xaf, 0x0d, 0xf1, 0xef, 0x5f, 0x06, 0x05, 0xeb, 0x0e,
+ 0x55, 0xf0, 0x7e, 0x13, 0x1a, 0x2a, 0x37, 0xd4, 0xba, 0x26, 0xc8, 0x2e,
+ 0x6b, 0xc3, 0xe1, 0xcf, 0x28, 0xab, 0x0d, 0xab, 0xdd, 0xa7, 0xf4, 0xd3,
+ 0x59, 0xcd, 0xc7, 0x2d, 0xa1, 0x56, 0x5f, 0x47, 0x77, 0x27, 0x17, 0x71,
+ 0xae, 0x75, 0xc8, 0x71, 0x58, 0xf9, 0xab, 0x67, 0xda, 0x23, 0x62, 0xa0,
+ 0x6d, 0xe5, 0x2d, 0x06, 0xb8, 0xc0, 0xac, 0xaa, 0x38, 0xa4, 0x0d, 0xb5,
+ 0xb2, 0xce, 0xa7, 0x26, 0x0d, 0x3a, 0x88, 0x2f, 0x8d, 0x6c, 0xa0, 0xf6,
+ 0x94, 0xf2, 0x2c, 0x37, 0x03, 0xaf, 0x67, 0x5c, 0xf3, 0x2c, 0xfb, 0xe8,
+ 0x16, 0x9e, 0x55, 0x30, 0x4f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x07, 0x01, 0x30, 0x1e, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x65, 0x03, 0x04, 0x01, 0x2e, 0x30, 0x11, 0x04, 0x0c, 0x6b, 0x1d, 0xe5,
+ 0xb2, 0x38, 0x0e, 0x17, 0x91, 0x9c, 0x9c, 0x40, 0x35, 0x02, 0x01, 0x10,
+ 0x80, 0x22, 0xa0, 0x90, 0x75, 0x74, 0xdf, 0x2d, 0xba, 0x4f, 0xce, 0x4e,
+ 0x7e, 0x52, 0xb0, 0x2e, 0x5f, 0xe0, 0x84, 0x01, 0xb1, 0x49, 0x0b, 0x69,
+ 0xc7, 0x61, 0x63, 0x84, 0x3a, 0xfc, 0xaa, 0x86, 0xfc, 0x96, 0x4e, 0x6c,
+ 0x04, 0x01, 0x92
+};
+
+static int test_short_mac_on_auth_envelope_data(void)
+{
+ int ret = 0;
+ const unsigned char *derptr = one_byte_mac_cms_der;
+ BIO *outmsgbio = BIO_new(BIO_s_mem());
+ CMS_ContentInfo *content = d2i_CMS_ContentInfo(NULL, &derptr, OSSL_NELEM(one_byte_mac_cms_der));
+
+ if (!TEST_ptr(content))
+ goto end;
+
+ /*
+ * We expect this to fail, as the tag value in the authEnvelopedData parameter is
+ * a single byte
+ */
+ if (!TEST_false(CMS_decrypt(content, privkey, cert, NULL, outmsgbio, CMS_TEXT)))
+ goto end;
+
+ ret = 1;
+end:
+ BIO_free(outmsgbio);
+ CMS_ContentInfo_free(content);
+ return ret;
+}
+
+static int test_non_aead_on_auth_envelope_dec(void)
+{
+ int ret = 0;
+ const unsigned char *derptr = bad_cms_der;
+ BIO *outmsgbio = BIO_new(BIO_s_mem());
+ CMS_ContentInfo *content = d2i_CMS_ContentInfo(NULL, &derptr, OSSL_NELEM(bad_cms_der));
+
+ if (!TEST_ptr(content))
+ goto end;
+
+ /*
+ * We expect this to fail
+ */
+ if (!TEST_false(CMS_decrypt(content, privkey, cert, NULL, outmsgbio,
+ CMS_TEXT)))
+ goto end;
+
+ ret = 1;
+end:
+ BIO_free(outmsgbio);
+ CMS_ContentInfo_free(content);
+ return ret;
+}
+
+static int test_non_aead_on_auth_envelope_enc(void)
+{
+ CMS_ContentInfo *content = NULL;
+ STACK_OF(X509) *certstack = sk_X509_new_null();
+ const EVP_CIPHER *cipher = EVP_aes_128_cbc();
+ const char *msg = "Hello world";
+ BIO *msgbio = BIO_new_mem_buf(msg, (int)strlen(msg));
+ BIO *outmsgbio = BIO_new(BIO_s_mem());
+ X509 *recip;
+ int i;
+ int ret = 0;
+
+ if (!TEST_ptr(certstack) || !TEST_ptr(msgbio) || !TEST_ptr(outmsgbio))
+ goto end;
+
+ if (!TEST_int_gt(sk_X509_push(certstack, cert), 0))
+ goto end;
+
+ /*
+ * Emulate CMS_encrypt here, but use a non AEAD cipher
+ */
+ content = CMS_AuthEnvelopedData_create_ex(cipher, NULL, NULL);
+
+ if (!TEST_ptr(content))
+ goto end;
+
+ for (i = 0; i < sk_X509_num(certstack); i++) {
+ recip = sk_X509_value(certstack, i);
+ if (!TEST_ptr(CMS_add1_recipient_cert(content, recip, CMS_TEXT)))
+ goto end;
+ }
+
+ /*
+ * We expect this to fail as we are using a non-AEAD cipher on
+ * AuthEnvelopedData
+ */
+ if (!TEST_int_eq(CMS_final(content, msgbio, NULL, CMS_TEXT), 0))
+ goto end;
+
+ ret = 1;
+end:
+ sk_X509_free(certstack);
+ BIO_free(msgbio);
+ BIO_free(outmsgbio);
+ CMS_ContentInfo_free(content);
+ return ret;
+}
+
static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
{
int testresult = 0;
@@ -557,6 +742,9 @@ int setup_tests(void)
ADD_TEST(test_encrypt_decrypt_aes_128_gcm);
ADD_TEST(test_encrypt_decrypt_aes_192_gcm);
ADD_TEST(test_encrypt_decrypt_aes_256_gcm);
+ ADD_TEST(test_non_aead_on_auth_envelope_enc);
+ ADD_TEST(test_non_aead_on_auth_envelope_dec);
+ ADD_TEST(test_short_mac_on_auth_envelope_data);
ADD_TEST(test_CMS_add1_cert);
ADD_TEST(test_d2i_CMS_bio_NULL);
ADD_TEST(test_CMS_set1_key_mem_leak);

View File

@ -29,7 +29,7 @@ print(string.sub(hash, 0, 16))
Summary: Utilities from the general purpose cryptography library with TLS implementation
Name: openssl
Version: 3.5.5
Release: 3%{?dist}.alma.1
Release: 4%{?dist}.alma.1
Epoch: 1
Source0: openssl-%{version}.tar.gz
Source1: fips-hmacify.sh
@ -101,6 +101,21 @@ Patch0056: 0056-Add-targets-to-skip-build-of-non-installable-program.patch
Patch0057: 0057-Disable-RSA-PKCS1.5-FIPS-POST-not-relevant-for-RHEL.patch
Patch0058: 0058-CVE-2026-31790.patch
Patch0059: 0059-CVE-2026-28390.patch
Patch0060: 0060-CVE-2026-7383.patch
Patch0061: 0061-CVE-2026-9076.patch
Patch0062: 0062-CVE-2026-34180.patch
Patch0063: 0063-CVE-2026-34181.patch
Patch0064: 0064-CVE-2026-34183.patch
Patch0065: 0065-CVE-2026-42764.patch
Patch0066: 0066-CVE-2026-42766.patch
Patch0067: 0067-CVE-2026-42767.patch
Patch0068: 0068-CVE-2026-42768.patch
Patch0069: 0069-CVE-2026-42769.patch
Patch0070: 0070-CVE-2026-42770.patch
Patch0071: 0071-CVE-2026-45445.patch
Patch0072: 0072-CVE-2026-45446.patch
Patch0073: 0073-CVE-2026-45447.patch
Patch0074: 0074-CVE-2026-34182.patch
License: Apache-2.0
URL: http://www.openssl.org/
@ -464,9 +479,30 @@ touch $RPM_BUILD_ROOT/%{_prefix}/include/openssl/engine.h
%ldconfig_scriptlets libs
%changelog
* Mon Jun 01 2026 Eduard Abdullin <eabdullin@almalinux.org> - 1:3.5.5-3.alma.1
* Thu Jun 11 2026 Eduard Abdullin <eabdullin@almalinux.org> - 1:3.5.5-4.alma.1
- Redefine sslarch for x86_64_v2 arch
* Mon Jun 01 2026 Dmitry Belyavskiy <dbelyavs@redhat.com> - 1:3.5.5-4
Fix CVE-2026-7383, CVE-2026-9076, CVE-2026-34180, CVE-2026-34181,
CVE-2026-34183, CVE-2026-42764, CVE-2026-42766, CVE-2026-42767, CVE-2026-42768,
CVE-2026-42769, CVE-2026-42770, CVE-2026-45445, CVE-2026-45446, CVE-2026-45447,
CVE-2026-34182.
Resolves: RHEL-179267
Resolves: RHEL-179281
Resolves: RHEL-179537
Resolves: RHEL-179542
Resolves: RHEL-179545
Resolves: RHEL-179550
Resolves: RHEL-179553
Resolves: RHEL-179626
Resolves: RHEL-179658
Resolves: RHEL-179675
Resolves: RHEL-179682
Resolves: RHEL-179685
Resolves: RHEL-179689
Resolves: RHEL-179693
Resolves: RHEL-179697
* Wed May 13 2026 Pavol Žáčik <pzacik@redhat.com> - 1:3.5.5-3
- Fix CVE-2026-28390
Resolves: RHEL-165705