File leancrypto-CVE-2026-34610.patch of Package leancrypto
From 5cdcbe12bd6c3d6e87e969972a580b44a74c3a6a Mon Sep 17 00:00:00 2001
From: Stephan Mueller <smueller@chronox.de>
Date: Sun, 29 Mar 2026 19:14:41 +0200
Subject: [PATCH] X.509: subject parser - Overflow in size parser of a subject
name component
When parsing subject and issue name components, use size_t to store the
length of the string. This prevents an overflow the parsing of both component
types. Yet, as the issue string is checked against a maximum size, the
subject string is not checked against an overflow. Therefore, this change
also fixes a wrap in the size parsing which may be used by an attacker
to craft an X.509 certificate with a long subject string where only a
sub-part of the component is matched. This may be used to impersonate
as a different subjects with a DN longer than 256 bytes.
This change eliminates also all double management of the subject/issuer
string.
Reported-by: Sunwoo Lee
Reported-by: Seunghyun Yoon
Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
CHANGES.md | 2 +
SECURITY.md | 19 ++-
apps/src/lc_x509_generator_checker.c | 62 +++++++---
asn1/api/lc_x509_common.h | 5 +-
asn1/src/x509_cert_parser.c | 169 +++------------------------
asn1/src/x509_cert_parser.h | 6 -
6 files changed, 81 insertions(+), 182 deletions(-)
Index: leancrypto-1.6.0/SECURITY.md
===================================================================
--- leancrypto-1.6.0.orig/SECURITY.md
+++ leancrypto-1.6.0/SECURITY.md
@@ -8,12 +8,16 @@ basis.
If you detect any new security issues, please file a bug report or send
a private email to <smueller@chronox.de>.
-## 2024-01-25
+## 2026-03-29
-Integrate PQClean patch 3b43bc6fe46fe47be38f87af5019a7f1462ae6dd
+X.509 Subject parser: Overflow in size parser of a subject name component
-* Kyber used division operations that might leak side-channel information.
-[PR #534](https://github.com/PQClean/PQClean/pull/534) addressed this for the `clean` and `avx2` implementations.
+* With this, an attacker can craft a certificate where only a sub-part of a name
+ component is matched instead of the full component string. Therefore an
+ impersonation with a wrongly crafted certificate that has a valid signature is
+ possible.
+
+* Credits: Sunwoo Lee and Seunghyun Yoon (Korea Institute of Energy Technology, KENTECH).
## 2024-06-03
@@ -23,3 +27,10 @@ Integrate https://github.com/pq-crystals
* Fixed secret-dependent branch in poly_frommsg introduced by recent
versions of clang with some flags (Thanks to Antoon Purnal for pointing
this out!)
+
+## 2024-01-25
+
+Integrate PQClean patch 3b43bc6fe46fe47be38f87af5019a7f1462ae6dd
+
+* Kyber used division operations that might leak side-channel information.
+[PR #534](https://github.com/PQClean/PQClean/pull/534) addressed this for the `clean` and `avx2` implementations.
Index: leancrypto-1.6.0/apps/src/lc_x509_generator_checker.c
===================================================================
--- leancrypto-1.6.0.orig/apps/src/lc_x509_generator_checker.c
+++ leancrypto-1.6.0/apps/src/lc_x509_generator_checker.c
@@ -29,6 +29,18 @@
* X.509 tests
******************************************************************************/
+static void
+print_x509_name_component(const struct lc_x509_certificate_name_component *comp)
+{
+ char buf[LC_ASN1_MAX_ISSUER_NAME + 1] = { 0 };
+
+ if (!comp->size)
+ return;
+
+ memcpy(buf, comp->value, min_size(comp->size, LC_ASN1_MAX_ISSUER_NAME));
+ printf("%s", buf);
+}
+
int apply_checks_x509(const struct lc_x509_certificate *x509,
const struct x509_checker_options *parsed_opts)
{
@@ -173,8 +185,10 @@ int apply_checks_x509(const struct lc_x5
if (parsed_opts->issuer_cn) {
if (strncmp(x509->issuer, parsed_opts->issuer_cn,
sizeof(x509->issuer))) {
- printf("Issuers mismatch, expected %s, actual %s\n",
- parsed_opts->issuer_cn, x509->issuer);
+ printf("Issuers mismatch, expected %s, actual ",
+ parsed_opts->issuer_cn);
+ print_x509_name_component(&x509->issuer_segments.cn);
+ printf("\n");
return -EINVAL;
} else {
printf("Issuer matches expected value\n");
@@ -541,8 +555,17 @@ int apply_checks_pkcs7(const struct lc_p
int found = 0;
while (x509) {
- if (!strncmp(x509->issuer, parsed_opts->issuer_cn,
- sizeof(x509->issuer))) {
+ struct lc_x509_certificate_name
+ search_name = { .cn = {
+ .value = parsed_opts->issuer_cn,
+ .size = strlen(
+ parsed_opts->issuer_cn),
+ } };
+
+ if (lc_x509_policy_cert_subject_match(
+ x509, &search_name,
+ lc_x509_policy_cert_subject_match_issuer_only) ==
+ LC_X509_POL_TRUE) {
found = 1;
break;
}
@@ -562,8 +585,17 @@ int apply_checks_pkcs7(const struct lc_p
int found = 0;
while (x509) {
- if (!strncmp(x509->subject, parsed_opts->subject_cn,
- sizeof(x509->subject))) {
+ struct lc_x509_certificate_name
+ search_name = { .cn = {
+ .value = parsed_opts->subject_cn,
+ .size = strlen(
+ parsed_opts->subject_cn),
+ } };
+
+ if (lc_x509_policy_cert_subject_match(
+ x509, &search_name,
+ lc_x509_policy_cert_subject_match_dn_only) ==
+ LC_X509_POL_TRUE) {
found = 1;
break;
}
Index: leancrypto-1.6.0/asn1/api/lc_x509_common.h
===================================================================
--- leancrypto-1.6.0.orig/asn1/api/lc_x509_common.h
+++ leancrypto-1.6.0/asn1/api/lc_x509_common.h
@@ -246,7 +246,7 @@ struct lc_public_key_signature {
struct lc_x509_certificate_name_component {
const char *value;
- uint8_t size;
+ size_t size;
};
struct lc_x509_certificate_name {
@@ -331,9 +331,6 @@ struct lc_x509_certificate {
size_t raw_akid_size;
const uint8_t *raw_akid; /* authority key Id binary format */
unsigned int index;
- char issuer[LC_ASN1_MAX_ISSUER_NAME + 1]; /* Name of certificate issuer */
- char subject[LC_ASN1_MAX_ISSUER_NAME +
- 1]; /* Name of certificate subject */
uint8_t x509_version; /* X.509 Version of certificate */
unsigned int seen : 1; /* Infinite recursion prevention */
Index: leancrypto-1.6.0/asn1/src/x509_cert_parser.c
===================================================================
--- leancrypto-1.6.0.orig/asn1/src/x509_cert_parser.c
+++ leancrypto-1.6.0/asn1/src/x509_cert_parser.c
@@ -261,34 +261,28 @@ int x509_extract_name_segment(void *cont
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (ctx->last_oid) {
case OID_commonName:
- ctx->cn_size = (uint8_t)vlen;
- ctx->cn_offset = (uint16_t)(value - ctx->data);
name->cn.value = (char *)value;
- name->cn.size = (uint8_t)vlen;
+ name->cn.size = vlen;
break;
case OID_organizationName:
- ctx->o_size = (uint8_t)vlen;
- ctx->o_offset = (uint16_t)(value - ctx->data);
name->o.value = (char *)value;
- name->o.size = (uint8_t)vlen;
+ name->o.size = vlen;
break;
case OID_email_address:
- ctx->email_size = (uint8_t)vlen;
- ctx->email_offset = (uint16_t)(value - ctx->data);
name->email.value = (char *)value;
- name->email.size = (uint8_t)vlen;
+ name->email.size = vlen;
break;
case OID_countryName:
name->c.value = (char *)value;
- name->c.size = (uint8_t)vlen;
+ name->c.size = vlen;
break;
case OID_stateOrProvinceName:
name->st.value = (char *)value;
- name->st.size = (uint8_t)vlen;
+ name->st.size = vlen;
break;
case OID_organizationUnitName:
name->ou.value = (char *)value;
- name->ou.size = (uint8_t)vlen;
+ name->ou.size = vlen;
break;
default:
break;
@@ -324,131 +318,6 @@ int x509_attribute_value_continue(void *
return 0;
}
-/*
- * Fabricate and save the issuer and subject names
- */
-static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
- unsigned char tag,
- char _name[LC_ASN1_MAX_ISSUER_NAME], size_t vlen,
- int subject)
-{
- const uint8_t *name, *data = (const void *)ctx->data;
- struct lc_x509_certificate *cert = ctx->cert;
- size_t namesize;
- int ret = 0;
-
- (void)hdrlen;
- (void)tag;
- (void)vlen;
-
- /*
- * A SAN takes precedence over the DN for identifying the certificate
- * and marking its subject.
- */
- if (subject && cert->san_dns_len) {
- namesize = min_size(cert->san_dns_len, LC_ASN1_MAX_ISSUER_NAME);
- memcpy(_name, cert->san_dns, namesize);
- _name[namesize] = '\0';
-
- return 0;
- }
-
- if (subject && cert->san_ip_len) {
- if (cert->san_ip_len == 4) {
- /* IPv4 Address */
- snprintf(_name, LC_ASN1_MAX_ISSUER_NAME, "%u.%u.%u.%u",
- cert->san_ip[0], cert->san_ip[1],
- cert->san_ip[2], cert->san_ip[3]);
-
- } else if (cert->san_ip_len == 16) {
- /* IPv6 Address */
- size_t i, offset;
-
- for (i = 0; i < cert->san_ip_len; i++) {
- offset = i * 3;
- snprintf(_name + offset,
- LC_ASN1_MAX_ISSUER_NAME - offset,
- "%.02x:", cert->san_ip[i]);
- }
- /* Eliminate the last ":" and place a NULL terminator */
- _name[(i * 3) - 1] = '\0';
-
- } else {
- /*
- * Something else, do a best-effort by converting it
- * into Hex.
- */
- lc_bin2hex(cert->san_ip, cert->san_ip_len, _name,
- LC_ASN1_MAX_ISSUER_NAME, 1);
- _name[min_size(cert->san_ip_len,
- LC_ASN1_MAX_ISSUER_NAME)] = '\0';
- }
-
- return 0;
- }
-
- /* Empty name string if no material */
- if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
- _name[0] = 0;
- goto out;
- }
-
- if (ctx->cn_size && ctx->o_size) {
- /* Consider combining O and CN, but use only the CN if it is
- * prefixed by the O, or a significant portion thereof.
- */
- namesize = ctx->cn_size;
- name = data + ctx->cn_offset;
- if (ctx->cn_size >= ctx->o_size &&
- lc_memcmp_secure(data + ctx->cn_offset, ctx->cn_size,
- data + ctx->o_offset, ctx->o_size) == 0)
- goto single_component;
- if (ctx->cn_size >= 7 && ctx->o_size >= 7 &&
- lc_memcmp_secure(data + ctx->cn_offset, 7,
- data + ctx->o_offset, 7) == 0)
- goto single_component;
-
- if (ctx->o_size + 2 + ctx->cn_size + 1 >=
- LC_ASN1_MAX_ISSUER_NAME) {
- ret = -EOVERFLOW;
- goto out;
- }
-
- memcpy(_name, data + ctx->o_offset, ctx->o_size);
- _name[ctx->o_size + 0] = ':';
- _name[ctx->o_size + 1] = ' ';
- memcpy(_name + ctx->o_size + 2, data + ctx->cn_offset,
- ctx->cn_size);
- _name[ctx->o_size + 2 + ctx->cn_size] = '\0';
-
- goto out;
-
- } else if (ctx->cn_size) {
- namesize = ctx->cn_size;
- name = data + ctx->cn_offset;
- } else if (ctx->o_size) {
- namesize = ctx->o_size;
- name = data + ctx->o_offset;
- } else {
- namesize = ctx->email_size;
- name = data + ctx->email_offset;
- }
-
-single_component:
- if (namesize >= LC_ASN1_MAX_ISSUER_NAME) {
- ret = -EOVERFLOW;
- goto out;
- }
- memcpy(_name, name, namesize);
- _name[namesize] = '\0';
-
-out:
- ctx->cn_size = 0;
- ctx->o_size = 0;
- ctx->email_size = 0;
- return ret;
-}
-
int x509_note_issuer(void *context, size_t hdrlen, unsigned char tag,
const uint8_t *value, size_t vlen)
{
@@ -457,6 +326,9 @@ int x509_note_issuer(void *context, size
struct lc_public_key_signature *sig = &cert->sig;
int ret = 0;
+ (void)hdrlen;
+ (void)tag;
+
cert->raw_issuer = value;
cert->raw_issuer_size = vlen;
@@ -465,8 +337,6 @@ int x509_note_issuer(void *context, size
NULL, 0));
}
- CKINT(x509_fabricate_name(ctx, hdrlen, tag, cert->issuer, vlen, 0));
-
out:
return ret;
}
@@ -480,9 +350,12 @@ int x509_note_subject(void *context, siz
struct x509_parse_context *ctx = context;
struct lc_x509_certificate *cert = ctx->cert;
+ (void)hdrlen;
+ (void)tag;
+
cert->raw_subject = value;
cert->raw_subject_size = vlen;
- return x509_fabricate_name(ctx, hdrlen, tag, cert->subject, vlen, 1);
+ return 0;
}
/*
@@ -653,7 +526,7 @@ int x509_san_dns(void *context, size_t h
cert->san_dns = (char *)value;
cert->san_dns_len = vlen;
- return x509_fabricate_name(ctx, hdrlen, tag, cert->subject, vlen, 1);
+ return 0;
}
/*
Index: leancrypto-1.6.0/asn1/src/x509_cert_parser.h
===================================================================
--- leancrypto-1.6.0.orig/asn1/src/x509_cert_parser.h
+++ leancrypto-1.6.0/asn1/src/x509_cert_parser.h
@@ -42,15 +42,9 @@ struct x509_parse_context {
const uint8_t *akid_raw_issuer; /* Raw directoryName in authorityKeyId */
size_t akid_raw_issuer_size;
unsigned int extension_critical : 1;
- uint16_t o_offset; /* Offset of organizationName (O) */
- uint16_t cn_offset; /* Offset of commonName (CN) */
- uint16_t email_offset; /* Offset of emailAddress */
enum OID key_algo; /* Algorithm used by the cert's key */
enum OID last_oid; /* Last OID encountered */
enum OID sig_algo; /* Algorithm used to sign the cert */
- uint8_t o_size; /* Size of organizationName (O) */
- uint8_t cn_size; /* Size of commonName (CN) */
- uint8_t email_size; /* Size of emailAddress */
};
struct x509_flag_name {