mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Laveesh Bansal <laveeshb@laveeshbansal.com>
To: David Howells <dhowells@redhat.com>,
	Herbert Xu <herbert@gondor.apana.org.au>
Cc: Lukas Wunner <lukas@wunner.de>, Ignat Korchagin <ignat@linux.win>,
	"David S . Miller" <davem@davemloft.net>,
	Denis Kenzior <denkenz@gmail.com>,
	Maximilian Stahlberg <maximilian.stahlberg@uni-bremen.de>,
	keyrings@vger.kernel.org, linux-crypto@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 2/2] X.509: Add KUnit tests for the certificate parser
Date: Wed, 11 Nov 2026 23:42:46 -0800	[thread overview]
Message-ID: <20261112074246.99421-3-laveeshb@laveeshbansal.com> (raw)
In-Reply-To: <20261112074246.99421-1-laveeshb@laveeshbansal.com>

Add a KUnit test for x509_cert_parse() that builds a small certificate
in memory with different Subject and Issuer names. It checks that a
certificate with an empty Subject parses, and that a Subject with a
member that is not a SET and an empty Issuer are rejected.

Signed-off-by: Laveesh Bansal <laveeshb@laveeshbansal.com>
---
Without patch 1, x509_empty_subject fails and the other three cases pass;
with it, all four pass (arm64 QEMU/KVM guest, cbbddb6b08cc, 7.3.0).
 crypto/asymmetric_keys/Kconfig            |  11 ++
 crypto/asymmetric_keys/Makefile           |   1 +
 crypto/asymmetric_keys/x509_parser_test.c | 160 ++++++++++++++++++++++
 3 files changed, 172 insertions(+)
 create mode 100644 crypto/asymmetric_keys/x509_parser_test.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index c73979b2e98e..ca2c11f82e32 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -126,4 +126,15 @@ config VERIFY_PEFILE_KUNIT_TEST
 
 	  If unsure, say N.
 
+config X509_PARSER_KUNIT_TEST
+	tristate "KUnit tests for X.509 certificate parsing" if !KUNIT_ALL_TESTS
+	depends on KUNIT && X509_CERTIFICATE_PARSER
+	default KUNIT_ALL_TESTS
+	help
+	  Enable KUnit tests for the X.509 certificate parser.  The tests build
+	  small certificates in memory and check which Subject and Issuer
+	  names x509_cert_parse() accepts.
+
+	  If unsure, say N.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 35172685c533..1ce91737ac89 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -22,6 +22,7 @@ x509_key_parser-y := \
 	x509_cert_parser.o \
 	x509_loader.o \
 	x509_public_key.o
+obj-$(CONFIG_X509_PARSER_KUNIT_TEST) += x509_parser_test.o
 obj-$(CONFIG_FIPS_SIGNATURE_SELFTEST) += x509_selftest.o
 x509_selftest-y += selftest.o
 x509_selftest-$(CONFIG_FIPS_SIGNATURE_SELFTEST_RSA) += selftest_rsa.o
diff --git a/crypto/asymmetric_keys/x509_parser_test.c b/crypto/asymmetric_keys/x509_parser_test.c
new file mode 100644
index 000000000000..c005b6fe68a6
--- /dev/null
+++ b/crypto/asymmetric_keys/x509_parser_test.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for the X.509 certificate parser.
+ *
+ * The cases build a minimal certificate in memory, vary the Subject or Issuer
+ * name, and call x509_cert_parse().  The key and signature are dummies, so
+ * Issuer and Subject must differ or the parser tries to verify the signature
+ * as a self-signed certificate.
+ */
+#include <kunit/test.h>
+#include <linux/cleanup.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "x509_parser.h"
+
+/* Name DERs: empty, CN=x, CN=y, and one whose member is not a SET. */
+static const u8 name_empty[] = { 0x30, 0x00 };
+static const u8 name_cn[] = {
+	0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03,
+	0x0c, 0x01, 0x78
+};
+
+static const u8 name_other_cn[] = {
+	0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03,
+	0x0c, 0x01, 0x79
+};
+
+static const u8 name_not_a_set[] = { 0x30, 0x03, 0x02, 0x01, 0x00 };
+
+static const u8 cert_version[] = { 0xa0, 0x03, 0x02, 0x01, 0x02 };
+static const u8 cert_serial[] = { 0x02, 0x01, 0x01 };
+/* sha256WithRSAEncryption, NULL parameters */
+static const u8 cert_sig_algo[] = {
+	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+	0x0b, 0x05, 0x00
+};
+
+/* 2026-01-01 to 2036-01-01 */
+static const u8 cert_validity[] = {
+	0x30, 0x1e,
+	0x17, 0x0d, '2', '6', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z',
+	0x17, 0x0d, '3', '6', '0', '1', '0', '1', '0', '0', '0', '0', '0', '0', 'Z'
+};
+
+/* rsaEncryption with a dummy 4-byte key; the key is not parsed here */
+static const u8 cert_spki[] = {
+	0x30, 0x16,
+	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+	0x01, 0x05, 0x00,
+	0x03, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04
+};
+
+static const u8 cert_signature[] = { 0x03, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04 };
+
+/* Write tag, length (up to 255) and @len bytes of @val at @out. */
+static u8 *put_tlv(u8 *out, u8 tag, const u8 *val, size_t len)
+{
+	*out++ = tag;
+	if (len > 127)
+		*out++ = 0x81;
+	*out++ = len;
+	memcpy(out, val, len);
+	return out + len;
+}
+
+static u8 *put_raw(u8 *out, const u8 *val, size_t len)
+{
+	memcpy(out, val, len);
+	return out + len;
+}
+
+static struct x509_certificate *parse_cert(struct kunit *test,
+					   const u8 *issuer, size_t issuer_len,
+					   const u8 *subject, size_t subject_len)
+{
+	/* The parsed certificate keeps pointers into this buffer. */
+	u8 *cert = kunit_kzalloc(test, 256, GFP_KERNEL);
+	u8 tbs[160], body[256], *p = tbs, *q = body;
+
+	KUNIT_ASSERT_NOT_NULL(test, cert);
+	/* Keep tbs[] and body[] large enough for the fixed parts plus the names. */
+	KUNIT_ASSERT_LE(test, issuer_len + subject_len, 64);
+
+	p = put_raw(p, cert_version, sizeof(cert_version));
+	p = put_raw(p, cert_serial, sizeof(cert_serial));
+	p = put_raw(p, cert_sig_algo, sizeof(cert_sig_algo));
+	p = put_raw(p, issuer, issuer_len);
+	p = put_raw(p, cert_validity, sizeof(cert_validity));
+	p = put_raw(p, subject, subject_len);
+	p = put_raw(p, cert_spki, sizeof(cert_spki));
+
+	q = put_tlv(q, 0x30, tbs, p - tbs);
+	q = put_raw(q, cert_sig_algo, sizeof(cert_sig_algo));
+	q = put_raw(q, cert_signature, sizeof(cert_signature));
+	p = put_tlv(cert, 0x30, body, q - body);
+
+	return x509_cert_parse(cert, p - cert);
+}
+
+/* RFC 5280 allows an empty Subject, so the certificate must parse. */
+static void x509_empty_subject(struct kunit *test)
+{
+	struct x509_certificate *cert __free(x509_free_certificate) =
+		parse_cert(test, name_other_cn, sizeof(name_other_cn),
+			   name_empty, sizeof(name_empty));
+
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cert);
+	/* raw_subject_size counts the contents, not the tag and length. */
+	KUNIT_EXPECT_EQ(test, cert->raw_subject_size, 0);
+}
+
+/* Control: the same certificate with a one-RDN Subject parses. */
+static void x509_named_subject(struct kunit *test)
+{
+	struct x509_certificate *cert __free(x509_free_certificate) =
+		parse_cert(test, name_other_cn, sizeof(name_other_cn),
+			   name_cn, sizeof(name_cn));
+
+	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cert);
+	KUNIT_EXPECT_EQ(test, cert->raw_subject_size, sizeof(name_cn) - 2);
+}
+
+/* A Subject member that is not a SET must be rejected, not looped over. */
+static void x509_subject_not_a_set(struct kunit *test)
+{
+	struct x509_certificate *cert __free(x509_free_certificate) =
+		parse_cert(test, name_other_cn, sizeof(name_other_cn),
+			   name_not_a_set, sizeof(name_not_a_set));
+
+	KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(cert), -EBADMSG);
+}
+
+/* The Issuer must not be empty. */
+static void x509_empty_issuer(struct kunit *test)
+{
+	struct x509_certificate *cert __free(x509_free_certificate) =
+		parse_cert(test, name_empty, sizeof(name_empty),
+			   name_cn, sizeof(name_cn));
+
+	KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(cert), -EBADMSG);
+}
+
+static struct kunit_case x509_parser_cases[] = {
+	KUNIT_CASE(x509_empty_subject),
+	KUNIT_CASE(x509_named_subject),
+	KUNIT_CASE(x509_subject_not_a_set),
+	KUNIT_CASE(x509_empty_issuer),
+	{}
+};
+
+static struct kunit_suite x509_parser_suite = {
+	.name = "x509_parser",
+	.test_cases = x509_parser_cases,
+};
+
+kunit_test_suite(x509_parser_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit tests for the X.509 certificate parser");
-- 
2.54.0


      parent reply	other threads:[~2026-11-12  7:45 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12  7:42 [PATCH 0/2] X.509: Accept a certificate with an empty subject Laveesh Bansal
2026-11-12  7:42 ` [PATCH 1/2] " Laveesh Bansal
2026-11-12  8:09   ` Eric Biggers
2026-11-12  7:42 ` Laveesh Bansal [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261112074246.99421-3-laveeshb@laveeshbansal.com \
    --to=laveeshb@laveeshbansal.com \
    --cc=davem@davemloft.net \
    --cc=denkenz@gmail.com \
    --cc=dhowells@redhat.com \
    --cc=herbert@gondor.apana.org.au \
    --cc=ignat@linux.win \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=maximilian.stahlberg@uni-bremen.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®