208 lines
7.4 KiB
Diff
208 lines
7.4 KiB
Diff
From 190ba58c0a1d995d4da8b017054d4b74d138291c Mon Sep 17 00:00:00 2001
|
|
From: Igor Ustinov <igus68@gmail.com>
|
|
Date: Mon, 12 Jan 2026 12:13:35 +0100
|
|
Subject: [PATCH 1/3] Correct handling of AEAD-encrypted CMS with inadmissibly
|
|
long IV
|
|
|
|
Fixes CVE-2025-15467
|
|
---
|
|
crypto/evp/evp_lib.c | 5 ++---
|
|
1 file changed, 2 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c
|
|
index 9eae1d421c2..58fa7ce43b4 100644
|
|
--- a/crypto/evp/evp_lib.c
|
|
+++ b/crypto/evp/evp_lib.c
|
|
@@ -228,10 +228,9 @@ int evp_cipher_get_asn1_aead_params(EVP_CIPHER_CTX *c, ASN1_TYPE *type,
|
|
if (type == NULL || asn1_params == NULL)
|
|
return 0;
|
|
|
|
- i = ossl_asn1_type_get_octetstring_int(type, &tl, NULL, EVP_MAX_IV_LENGTH);
|
|
- if (i <= 0)
|
|
+ i = ossl_asn1_type_get_octetstring_int(type, &tl, iv, EVP_MAX_IV_LENGTH);
|
|
+ if (i <= 0 || i > EVP_MAX_IV_LENGTH)
|
|
return -1;
|
|
- ossl_asn1_type_get_octetstring_int(type, &tl, iv, i);
|
|
|
|
memcpy(asn1_params->iv, iv, i);
|
|
asn1_params->iv_len = i;
|
|
|
|
From 6fb47957bfb0aef2deaa7df7aebd4eb52ffe20ce Mon Sep 17 00:00:00 2001
|
|
From: Igor Ustinov <igus68@gmail.com>
|
|
Date: Mon, 12 Jan 2026 12:15:42 +0100
|
|
Subject: [PATCH 2/3] Some comments to clarify functions usage
|
|
|
|
---
|
|
crypto/asn1/evp_asn1.c | 20 ++++++++++++++++++++
|
|
1 file changed, 20 insertions(+)
|
|
|
|
diff --git a/crypto/asn1/evp_asn1.c b/crypto/asn1/evp_asn1.c
|
|
index 382576364be..e73bda64e3d 100644
|
|
--- a/crypto/asn1/evp_asn1.c
|
|
+++ b/crypto/asn1/evp_asn1.c
|
|
@@ -60,6 +60,12 @@ static ossl_inline void asn1_type_init_oct(ASN1_OCTET_STRING *oct,
|
|
oct->flags = 0;
|
|
}
|
|
|
|
+/*
|
|
+ * This function copies 'anum' to 'num' and the data of 'oct' to 'data'.
|
|
+ * If the length of 'data' > 'max_len', copies only the first 'max_len'
|
|
+ * bytes, but returns the full length of 'oct'; this allows distinguishing
|
|
+ * whether all the data was copied.
|
|
+ */
|
|
static int asn1_type_get_int_oct(ASN1_OCTET_STRING *oct, int32_t anum,
|
|
long *num, unsigned char *data, int max_len)
|
|
{
|
|
@@ -106,6 +112,13 @@ int ASN1_TYPE_set_int_octetstring(ASN1_TYPE *a, long num, unsigned char *data,
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * This function decodes an int-octet sequence and copies the integer to 'num'
|
|
+ * and the data of octet to 'data'.
|
|
+ * If the length of 'data' > 'max_len', copies only the first 'max_len'
|
|
+ * bytes, but returns the full length of 'oct'; this allows distinguishing
|
|
+ * whether all the data was copied.
|
|
+ */
|
|
int ASN1_TYPE_get_int_octetstring(const ASN1_TYPE *a, long *num,
|
|
unsigned char *data, int max_len)
|
|
{
|
|
@@ -162,6 +175,13 @@ int ossl_asn1_type_set_octetstring_int(ASN1_TYPE *a, long num,
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * This function decodes an octet-int sequence and copies the data of octet
|
|
+ * to 'data' and the integer to 'num'.
|
|
+ * If the length of 'data' > 'max_len', copies only the first 'max_len'
|
|
+ * bytes, but returns the full length of 'oct'; this allows distinguishing
|
|
+ * whether all the data was copied.
|
|
+ */
|
|
int ossl_asn1_type_get_octetstring_int(const ASN1_TYPE *a, long *num,
|
|
unsigned char *data, int max_len)
|
|
{
|
|
|
|
From 1e8f5c7cd2c46b25a2877e8f3f4bbf954fbcdf77 Mon Sep 17 00:00:00 2001
|
|
From: Igor Ustinov <igus68@gmail.com>
|
|
Date: Sun, 11 Jan 2026 11:35:15 +0100
|
|
Subject: [PATCH 3/3] Test for handling of AEAD-encrypted CMS with inadmissibly
|
|
long IV
|
|
|
|
---
|
|
test/cmsapitest.c | 39 ++++++++++++++++++-
|
|
test/recipes/80-test_cmsapi.t | 3 +-
|
|
.../encDataWithTooLongIV.pem | 11 ++++++
|
|
3 files changed, 50 insertions(+), 3 deletions(-)
|
|
create mode 100644 test/recipes/80-test_cmsapi_data/encDataWithTooLongIV.pem
|
|
|
|
diff --git a/test/cmsapitest.c b/test/cmsapitest.c
|
|
index 88d519fd148..472d30c9e5d 100644
|
|
--- a/test/cmsapitest.c
|
|
+++ b/test/cmsapitest.c
|
|
@@ -9,10 +9,10 @@
|
|
|
|
#include <string.h>
|
|
|
|
+#include <openssl/pem.h>
|
|
#include <openssl/cms.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/x509.h>
|
|
-#include <openssl/pem.h>
|
|
#include "../crypto/cms/cms_local.h" /* for d.signedData and d.envelopedData */
|
|
|
|
#include "testutil.h"
|
|
@@ -20,6 +20,7 @@
|
|
static X509 *cert = NULL;
|
|
static EVP_PKEY *privkey = NULL;
|
|
static char *derin = NULL;
|
|
+static char *too_long_iv_cms_in = NULL;
|
|
|
|
static int test_encrypt_decrypt(const EVP_CIPHER *cipher)
|
|
{
|
|
@@ -479,6 +480,38 @@ static int test_encrypted_data_aead(void)
|
|
return ret;
|
|
}
|
|
|
|
+static int test_cms_aesgcm_iv_too_long(void)
|
|
+{
|
|
+ int ret = 0;
|
|
+ BIO *cmsbio = NULL, *out = NULL;
|
|
+ CMS_ContentInfo *cms = NULL;
|
|
+ unsigned long err = 0;
|
|
+
|
|
+ if (!TEST_ptr(cmsbio = BIO_new_file(too_long_iv_cms_in, "r")))
|
|
+ goto end;
|
|
+
|
|
+ if (!TEST_ptr(cms = PEM_read_bio_CMS(cmsbio, NULL, NULL, NULL)))
|
|
+ goto end;
|
|
+
|
|
+ /* Must fail cleanly (no crash) */
|
|
+ if (!TEST_false(CMS_decrypt(cms, privkey, cert, NULL, out, 0)))
|
|
+ goto end;
|
|
+ err = ERR_peek_last_error();
|
|
+ if (!TEST_ulong_ne(err, 0))
|
|
+ goto end;
|
|
+ if (!TEST_int_eq(ERR_GET_LIB(err), ERR_LIB_CMS))
|
|
+ goto end;
|
|
+ if (!TEST_int_eq(ERR_GET_REASON(err), CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR))
|
|
+ goto end;
|
|
+
|
|
+ ret = 1;
|
|
+end:
|
|
+ CMS_ContentInfo_free(cms);
|
|
+ BIO_free(cmsbio);
|
|
+ BIO_free(out);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
OPT_TEST_DECLARE_USAGE("certfile privkeyfile derfile\n")
|
|
|
|
int setup_tests(void)
|
|
@@ -493,7 +526,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(derin = test_get_argument(2))
|
|
+ || !TEST_ptr(too_long_iv_cms_in = test_get_argument(3)))
|
|
return 0;
|
|
|
|
certbio = BIO_new_file(certin, "r");
|
|
@@ -529,6 +563,7 @@ int setup_tests(void)
|
|
ADD_TEST(test_CMS_add1_cert);
|
|
ADD_TEST(test_d2i_CMS_bio_NULL);
|
|
ADD_ALL_TESTS(test_d2i_CMS_decode, 2);
|
|
+ ADD_TEST(test_cms_aesgcm_iv_too_long);
|
|
return 1;
|
|
}
|
|
|
|
diff --git a/test/recipes/80-test_cmsapi.t b/test/recipes/80-test_cmsapi.t
|
|
index af00355a9d6..182629e71a0 100644
|
|
--- a/test/recipes/80-test_cmsapi.t
|
|
+++ b/test/recipes/80-test_cmsapi.t
|
|
@@ -18,5 +18,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", "encryptedData.der"),
|
|
+ srctop_file("test", "recipes", "80-test_cmsapi_data", "encDataWithTooLongIV.pem")])),
|
|
"running cmsapitest");
|
|
diff --git a/test/recipes/80-test_cmsapi_data/encDataWithTooLongIV.pem b/test/recipes/80-test_cmsapi_data/encDataWithTooLongIV.pem
|
|
new file mode 100644
|
|
index 00000000000..4323cd2fb0c
|
|
--- /dev/null
|
|
+++ b/test/recipes/80-test_cmsapi_data/encDataWithTooLongIV.pem
|
|
@@ -0,0 +1,11 @@
|
|
+-----BEGIN CMS-----
|
|
+MIIBmgYLKoZIhvcNAQkQARegggGJMIIBhQIBADGCATMwggEvAgEAMBcwEjEQMA4G
|
|
+A1UEAwwHUm9vdCBDQQIBAjANBgkqhkiG9w0BAQEFAASCAQC8ZqP1OqbletcUre1V
|
|
+b4XOobZzQr6wKMSsdjtGzVbZowUVv5DkOn9VOefrpg4HxMq/oi8IpzVYj8ZiKRMV
|
|
+NTJ+/d8FwwBwUUNNP/IDnfEpX+rT1+pGS5zAa7NenLoZgGBNjPy5I2OHP23fPnEd
|
|
+sm8YkFjzubkhAD1lod9pEOEqB3V2kTrTTiwzSNtMHggna1zPox6TkdZwFmMnp8d2
|
|
+CVa6lIPGx26gFwCuIDSaavmQ2URJ615L8gAvpYUlpsDqjFsabWsbaOFbMz3bIGJu
|
|
+GkrX2ezX7CpuC1wjix26ojlTySJHv+L0IrpcaIzLlC5lB1rqtuija8dGm3rBNm/P
|
|
+AAUNMDcGCSqGSIb3DQEHATAjBglghkgBZQMEAQYwFgQRzxwoRQzOHVooVn3CpaWl
|
|
+paUCARCABUNdolo6BBA55E9hYaYO2S8C/ZnD8dRO
|
|
+-----END CMS-----
|