From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F7A43054EF for ; Mon, 5 Jan 2026 15:22:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626535; cv=none; b=Y41BXCDQl0gRsfDreFyP3R1bikymDoCoexEBvpizpraEVUTOBLA3Tn+ZfVgfAnkjFCaFVDp6ezm28didEqSzKkvLHSMSWyTVbKwq6QxJ+gKilj0ROp2T0RW/27egvWNSMXRSHy8DVms3vpZYgM0rCmutCmtlXX6dFXNegFvGIvM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626535; c=relaxed/simple; bh=TteiNIX0IuFSc5XzPByh/QUEuGNbipc6oZyEEMomZZ8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VdPWOSlCuCcc9ku7siJ0k14c8YLQp9tt9LYwKRtlwrUxGJp8kp/Kok2UzmeEzG5I8UzfvjDEe884q0+cZNYLEtkh0eo0LhZhC0OIEB4wfOWeUqpmeHOg5kfhK7cBtxNSbeP3tqlAAYpp72k9NesymuN8UxuAnEF85hg7xSR4PVA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=fail smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=AtNgEA+n; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="AtNgEA+n" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626532; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=odDnoE/GKlxrMrTFdFpnSHxXRmINbU9xkYsrAvJ6paM=; b=AtNgEA+ni8GgOwImyepYgF6McBu/GXr01ylh7UvK+Wfoywd8X1OF6hl2Lzl9+0gnZfRTYo 2xPpn/tF50An4We8SLF35uOkc2dsh8+3YqXrFSLUESULuXxIKVtUfHYumUUBob73c2xbJS RHk4Kw779XOUAYoPv29ub4vMm6uyfRg= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-517-M5IarOcTMteKOWQ9v_sKrA-1; Mon, 05 Jan 2026 10:22:07 -0500 X-MC-Unique: M5IarOcTMteKOWQ9v_sKrA-1 X-Mimecast-MFC-AGG-ID: M5IarOcTMteKOWQ9v_sKrA_1767626525 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 2C0371955DD1; Mon, 5 Jan 2026 15:22:04 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 49D4F30001A7; Mon, 5 Jan 2026 15:21:57 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 1/8] crypto: Add ML-DSA crypto_sig support Date: Mon, 5 Jan 2026 15:21:26 +0000 Message-ID: <20260105152145.1801972-2-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add verify-only public key crypto support for ML-DSA so that the X.509/PKCS#7 signature verification code, as used by module signing, amongst other things, can make use of it through the common crypto_sig API. Signed-off-by: David Howells cc: Eric Biggers cc: Lukas Wunner cc: Ignat Korchagin cc: Stephan Mueller cc: Herbert Xu cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org --- crypto/Kconfig | 10 +++ crypto/Makefile | 2 + crypto/mldsa.c | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) create mode 100644 crypto/mldsa.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 12a87f7cf150..8dd5c6660c5a 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -344,6 +344,16 @@ config CRYPTO_ECRDSA One of the Russian cryptographic standard algorithms (called GOST algorithms). Only signature verification is implemented. =20 +config CRYPTO_MLDSA + tristate "ML-DSA (Module-Lattice-Based Digital Signature Algorithm)" + select CRYPTO_SIG + select CRYPTO_LIB_MLDSA + select CRYPTO_LIB_SHA3 + help + ML-DSA (Module-Lattice-Based Digital Signature Algorithm) (FIPS-204). + + Only signature verification is implemented. + endmenu =20 menu "Block ciphers" diff --git a/crypto/Makefile b/crypto/Makefile index 23d3db7be425..267d5403045b 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -60,6 +60,8 @@ ecdsa_generic-y +=3D ecdsa-p1363.o ecdsa_generic-y +=3D ecdsasignature.asn1.o obj-$(CONFIG_CRYPTO_ECDSA) +=3D ecdsa_generic.o =20 +obj-$(CONFIG_CRYPTO_MLDSA) +=3D mldsa.o + crypto_acompress-y :=3D acompress.o crypto_acompress-y +=3D scompress.o obj-$(CONFIG_CRYPTO_ACOMP2) +=3D crypto_acompress.o diff --git a/crypto/mldsa.c b/crypto/mldsa.c new file mode 100644 index 000000000000..2146c774b5ca --- /dev/null +++ b/crypto/mldsa.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * crypto_sig wrapper around ML-DSA library. + */ +#include +#include +#include +#include + +struct crypto_mldsa_ctx { + u8 pk[MAX(MAX(MLDSA44_PUBLIC_KEY_SIZE, + MLDSA65_PUBLIC_KEY_SIZE), + MLDSA87_PUBLIC_KEY_SIZE)]; + unsigned int pk_len; + enum mldsa_alg strength; + u8 key_set; +}; + +static int crypto_mldsa_sign(struct crypto_sig *tfm, + const void *msg, unsigned int msg_len, + void *sig, unsigned int sig_len) +{ + return -EOPNOTSUPP; +} + +static int crypto_mldsa_verify(struct crypto_sig *tfm, + const void *sig, unsigned int sig_len, + const void *msg, unsigned int msg_len) +{ + const struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + + if (unlikely(!ctx->key_set)) + return -EINVAL; + + return mldsa_verify(ctx->strength, sig, sig_len, msg, msg_len, + ctx->pk, ctx->pk_len); +} + +static unsigned int crypto_mldsa_key_size(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + + switch (ctx->strength) { + case MLDSA44: + return MLDSA44_PUBLIC_KEY_SIZE; + case MLDSA65: + return MLDSA65_PUBLIC_KEY_SIZE; + case MLDSA87: + return MLDSA87_PUBLIC_KEY_SIZE; + default: + WARN_ON_ONCE(1); + return 0; + } +} + +static int crypto_mldsa_set_pub_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + unsigned int expected_len =3D crypto_mldsa_key_size(tfm); + + if (keylen !=3D expected_len) + return -EINVAL; + + ctx->pk_len =3D keylen; + memcpy(ctx->pk, key, keylen); + ctx->key_set =3D true; + return 0; +} + +static int crypto_mldsa_set_priv_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + return -EOPNOTSUPP; +} + +static unsigned int crypto_mldsa_max_size(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + + switch (ctx->strength) { + case MLDSA44: + return MLDSA44_SIGNATURE_SIZE; + case MLDSA65: + return MLDSA65_SIGNATURE_SIZE; + case MLDSA87: + return MLDSA87_SIGNATURE_SIZE; + default: + WARN_ON_ONCE(1); + return 0; + } +} + +static int crypto_mldsa44_alg_init(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + + ctx->strength =3D MLDSA44; + ctx->key_set =3D false; + return 0; +} + +static int crypto_mldsa65_alg_init(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + + ctx->strength =3D MLDSA65; + ctx->key_set =3D false; + return 0; +} + +static int crypto_mldsa87_alg_init(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); + + ctx->strength =3D MLDSA87; + ctx->key_set =3D false; + return 0; +} + +static void crypto_mldsa_alg_exit(struct crypto_sig *tfm) +{ +} + +static struct sig_alg crypto_mldsa_algs[] =3D { + { + .sign =3D crypto_mldsa_sign, + .verify =3D crypto_mldsa_verify, + .set_pub_key =3D crypto_mldsa_set_pub_key, + .set_priv_key =3D crypto_mldsa_set_priv_key, + .key_size =3D crypto_mldsa_key_size, + .max_size =3D crypto_mldsa_max_size, + .init =3D crypto_mldsa44_alg_init, + .exit =3D crypto_mldsa_alg_exit, + .base.cra_name =3D "mldsa44", + .base.cra_driver_name =3D "mldsa44-lib", + .base.cra_ctxsize =3D sizeof(struct crypto_mldsa_ctx), + .base.cra_module =3D THIS_MODULE, + .base.cra_priority =3D 5000, + }, { + .sign =3D crypto_mldsa_sign, + .verify =3D crypto_mldsa_verify, + .set_pub_key =3D crypto_mldsa_set_pub_key, + .set_priv_key =3D crypto_mldsa_set_priv_key, + .key_size =3D crypto_mldsa_key_size, + .max_size =3D crypto_mldsa_max_size, + .init =3D crypto_mldsa65_alg_init, + .exit =3D crypto_mldsa_alg_exit, + .base.cra_name =3D "mldsa65", + .base.cra_driver_name =3D "mldsa65-lib", + .base.cra_ctxsize =3D sizeof(struct crypto_mldsa_ctx), + .base.cra_module =3D THIS_MODULE, + .base.cra_priority =3D 5000, + }, { + .sign =3D crypto_mldsa_sign, + .verify =3D crypto_mldsa_verify, + .set_pub_key =3D crypto_mldsa_set_pub_key, + .set_priv_key =3D crypto_mldsa_set_priv_key, + .key_size =3D crypto_mldsa_key_size, + .max_size =3D crypto_mldsa_max_size, + .init =3D crypto_mldsa87_alg_init, + .exit =3D crypto_mldsa_alg_exit, + .base.cra_name =3D "mldsa87", + .base.cra_driver_name =3D "mldsa87-lib", + .base.cra_ctxsize =3D sizeof(struct crypto_mldsa_ctx), + .base.cra_module =3D THIS_MODULE, + .base.cra_priority =3D 5000, + }, +}; + +static int __init mldsa_init(void) +{ + int ret, i; + + for (i =3D 0; i < ARRAY_SIZE(crypto_mldsa_algs); i++) { + ret =3D crypto_register_sig(&crypto_mldsa_algs[i]); + if (ret < 0) + goto error; + } + return 0; + +error: + pr_err("Failed to register (%d)\n", ret); + for (i--; i >=3D 0; i--) + crypto_unregister_sig(&crypto_mldsa_algs[i]); + return ret; +} +module_init(mldsa_init); + +static void mldsa_exit(void) +{ + for (int i =3D 0; i < ARRAY_SIZE(crypto_mldsa_algs); i++) + crypto_unregister_sig(&crypto_mldsa_algs[i]); +} +module_exit(mldsa_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Crypto API support for ML-DSA signature verification"); +MODULE_ALIAS_CRYPTO("mldsa44"); +MODULE_ALIAS_CRYPTO("mldsa65"); +MODULE_ALIAS_CRYPTO("mldsa87"); From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8F8EB30B50A for ; Mon, 5 Jan 2026 15:22:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626542; cv=none; b=IzT7PxyYQdQdiujAMNpkfX84u/wSoqf9iXjdwYoB65KaO6POmBNmW+1ixGS3ra6UZMgfnp1byXETu5cqGa411egcFZqd5oY2YJ/0WqXBOC1pFWWTZUYk5073BS6cIKk3Afh+P2Waf7OSMJ264CTtwNKCeZEL+IHnEXWFXgp5HAo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626542; c=relaxed/simple; bh=UJ0JNuAjO4bhNUyDJJTmWDirBsmcCcvUnyXrgyxFwC0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=P/IIg1jFpthL0gY8WQej9+oibTuCJX0qkOiVafGiIy7lP0sYb/NpDguYJaOEPm1IAISxGrayyDngyrQGlu+VqgrDvNwjvepc4uh8o5H+D43EFpy6a1xnONY1QX0BpKgkUyaCq0W314W4+5BeDHNhgZDIVRpKKTbWvvUdPaMk/JI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=aj4t09Qp; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="aj4t09Qp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626539; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LPY+GhOJq5nNgZrUBrGTmZ1jrCpWP/ToTTWyLrp7aIg=; b=aj4t09QpYqmcr9lavHZEdLv/4LxhPYjCn6DfmWw7rtJ2HJUrc2+EwGyDPy+wg2x5GetR6x QUuq1z/QCizVLl5Q/sGJBc2Dhed1SJBt0FSTRlg9cVQNuRjJqGgryyljIw0OJIcp+NOixw ZH5RABp9IS9XMaFsAU0OHdVo0Mh7oao= Received: from mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-186-RsrvT5hTMqeqYQgZCoJKBQ-1; Mon, 05 Jan 2026 10:22:15 -0500 X-MC-Unique: RsrvT5hTMqeqYQgZCoJKBQ-1 X-Mimecast-MFC-AGG-ID: RsrvT5hTMqeqYQgZCoJKBQ_1767626532 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A7B5718005AE; Mon, 5 Jan 2026 15:22:12 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2657530001A7; Mon, 5 Jan 2026 15:22:05 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 2/8] pkcs7: Allow the signing algo to calculate the digest itself Date: Mon, 5 Jan 2026 15:21:27 +0000 Message-ID: <20260105152145.1801972-3-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" The ML-DSA public key algorithm really wants to calculate the message digest itself, rather than having the digest precalculated and fed to it separately as RSA does[*]. The kernel's PKCS#7 parser, however, is designed around the latter approach. [*] ML-DSA does allow for an "external mu", but CMS doesn't yet have that standardised. Fix this by noting in the public_key_signature struct when the signing algorithm is going to want this and then, rather than doing the digest of the authenticatedAttributes ourselves and overwriting the sig->digest with that, replace sig->digest with a copy of the contents of the authenticatedAttributes section and adjust the digest length to match. This will then be fed to the public key algorithm as normal which can do what it wants with the data. Signed-off-by: David Howells cc: Lukas Wunner cc: Ignat Korchagin cc: Stephan Mueller cc: Eric Biggers cc: Herbert Xu cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org --- crypto/asymmetric_keys/pkcs7_parser.c | 4 +-- crypto/asymmetric_keys/pkcs7_verify.c | 48 ++++++++++++++++++--------- include/crypto/public_key.h | 1 + 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys= /pkcs7_parser.c index 423d13c47545..3cdbab3b9f50 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -599,8 +599,8 @@ int pkcs7_sig_note_set_of_authattrs(void *context, size= _t hdrlen, } =20 /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ - sinfo->authattrs =3D value - (hdrlen - 1); - sinfo->authattrs_len =3D vlen + (hdrlen - 1); + sinfo->authattrs =3D value - hdrlen; + sinfo->authattrs_len =3D vlen + hdrlen; return 0; } =20 diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys= /pkcs7_verify.c index 6d6475e3a9bf..0f9f515b784d 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -70,8 +70,6 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, * digest we just calculated. */ if (sinfo->authattrs) { - u8 tag; - if (!sinfo->msgdigest) { pr_warn("Sig %u: No messageDigest\n", sinfo->index); ret =3D -EKEYREJECTED; @@ -97,20 +95,40 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, * as the contents of the digest instead. Note that we need to * convert the attributes from a CONT.0 into a SET before we * hash it. + * + * However, for certain algorithms, such as ML-DSA, the digest + * is integrated into the signing algorithm. In such a case, + * we copy the authattrs, modifying the tag type, and set that + * as the digest. */ - memset(sig->digest, 0, sig->digest_size); - - ret =3D crypto_shash_init(desc); - if (ret < 0) - goto error; - tag =3D ASN1_CONS_BIT | ASN1_SET; - ret =3D crypto_shash_update(desc, &tag, 1); - if (ret < 0) - goto error; - ret =3D crypto_shash_finup(desc, sinfo->authattrs, - sinfo->authattrs_len, sig->digest); - if (ret < 0) - goto error; + if (sig->algo_does_hash) { + kfree(sig->digest); + + ret =3D -ENOMEM; + sig->digest =3D kmalloc(umax(sinfo->authattrs_len, sig->digest_size), + GFP_KERNEL); + if (!sig->digest) + goto error_no_desc; + + sig->digest_size =3D sinfo->authattrs_len; + memcpy(sig->digest, sinfo->authattrs, sinfo->authattrs_len); + ((u8 *)sig->digest)[0] =3D ASN1_CONS_BIT | ASN1_SET; + ret =3D 0; + } else { + u8 tag =3D ASN1_CONS_BIT | ASN1_SET; + + ret =3D crypto_shash_init(desc); + if (ret < 0) + goto error; + ret =3D crypto_shash_update(desc, &tag, 1); + if (ret < 0) + goto error; + ret =3D crypto_shash_finup(desc, sinfo->authattrs + 1, + sinfo->authattrs_len - 1, + sig->digest); + if (ret < 0) + goto error; + } pr_devel("AADigest =3D [%*ph]\n", 8, sig->digest); } =20 diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 81098e00c08f..e4ec8003a3a4 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -46,6 +46,7 @@ struct public_key_signature { u8 *digest; u32 s_size; /* Number of bytes in signature */ u32 digest_size; /* Number of bytes in digest */ + bool algo_does_hash; /* Public key algo does its own hashing */ const char *pkey_algo; const char *hash_algo; const char *encoding; From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF4C8318133 for ; Mon, 5 Jan 2026 15:22:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626549; cv=none; b=JAQH/OurAd9Xg/WV2/nqcCbaJUb8Ko4Held/V7KdSNID6/mGZ3eAG6S7pCP803snrsomzV6y4CAQU8ml+ev0bMsNOwM7xftVUJDkzMBVr3S00wqzaDc5EZUjafu8CyYduK/LvFGjxPD4A0ZNN8FWue/euqvHDTOEU54MplvgDUc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626549; c=relaxed/simple; bh=V8tSt8PMyYjEGhggoNvlNowgi1WZb1NM3NMzZEhHrLk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SLh/b8tlOG3D/TnkKmyYt3X6BcfNlTCoxuWaj3JnCLV+ownIngMQ5hA/czI6eKr3eo4rqdW3Xnvu3hXQWXtpr3YmtcOggoHm5Jbq+oFlFl35uaAs4A9+6KuF1dVkC/WJYJZDtayQsGlFDIiilWkcf82IGUWRws2u9h4CB6x4BhE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Crqa20Bz; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Crqa20Bz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626544; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=sgfzh7abUFZ+TVTSv++cu2GLASWZSldE75HB9kPYrsI=; b=Crqa20BzNKJqFYEV0x2UKC0ar4yI/muBDAuJsOKVC+asOJG1EIMetuy4ykAVrQzgcq348C ke9rFkoi0VCGMiTmN5OBQEhlIiZuVjpExlx8+tBxW1yjcO2l9WKjyvlY1SJ0gCWJAdgJbm vWy1ewKmtS51I7rIn25VepOa/ZQyqVE= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-351-NW03notjPYCVFhD5r-PnWg-1; Mon, 05 Jan 2026 10:22:23 -0500 X-MC-Unique: NW03notjPYCVFhD5r-PnWg-1 X-Mimecast-MFC-AGG-ID: NW03notjPYCVFhD5r-PnWg_1767626540 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 53F75195609F; Mon, 5 Jan 2026 15:22:20 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id BC3E619560AB; Mon, 5 Jan 2026 15:22:14 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 3/8] pkcs7, x509: Add ML-DSA support Date: Mon, 5 Jan 2026 15:21:28 +0000 Message-ID: <20260105152145.1801972-4-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Add support for ML-DSA keys and signatures to the PKCS#7 and X.509 implementations. Signed-off-by: David Howells cc: Lukas Wunner cc: Ignat Korchagin cc: Stephan Mueller cc: Eric Biggers cc: Herbert Xu cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org --- crypto/asymmetric_keys/pkcs7_parser.c | 15 ++++++++++++++ crypto/asymmetric_keys/public_key.c | 7 +++++++ crypto/asymmetric_keys/x509_cert_parser.c | 24 +++++++++++++++++++++++ include/linux/oid_registry.h | 5 +++++ 4 files changed, 51 insertions(+) diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys= /pkcs7_parser.c index 3cdbab3b9f50..90c36fe1b5ed 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -297,6 +297,21 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdr= len, ctx->sinfo->sig->pkey_algo =3D "ecrdsa"; ctx->sinfo->sig->encoding =3D "raw"; break; + case OID_id_ml_dsa_44: + ctx->sinfo->sig->pkey_algo =3D "mldsa44"; + ctx->sinfo->sig->encoding =3D "raw"; + ctx->sinfo->sig->algo_does_hash =3D true; + break; + case OID_id_ml_dsa_65: + ctx->sinfo->sig->pkey_algo =3D "mldsa65"; + ctx->sinfo->sig->encoding =3D "raw"; + ctx->sinfo->sig->algo_does_hash =3D true; + break; + case OID_id_ml_dsa_87: + ctx->sinfo->sig->pkey_algo =3D "mldsa87"; + ctx->sinfo->sig->encoding =3D "raw"; + ctx->sinfo->sig->algo_does_hash =3D true; + break; default: printk("Unsupported pkey algo: %u\n", ctx->last_oid); return -ENOPKG; diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/p= ublic_key.c index e5b177c8e842..ed6b4b5ae4ef 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -142,6 +142,13 @@ software_key_determine_akcipher(const struct public_ke= y *pkey, if (strcmp(hash_algo, "streebog256") !=3D 0 && strcmp(hash_algo, "streebog512") !=3D 0) return -EINVAL; + } else if (strcmp(pkey->pkey_algo, "mldsa44") =3D=3D 0 || + strcmp(pkey->pkey_algo, "mldsa65") =3D=3D 0 || + strcmp(pkey->pkey_algo, "mldsa87") =3D=3D 0) { + if (strcmp(encoding, "raw") !=3D 0) + return -EINVAL; + if (!hash_algo) + return -EINVAL; } else { /* Unknown public key algorithm */ return -ENOPKG; diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_= keys/x509_cert_parser.c index b37cae914987..5ab5b4e5f1b4 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -257,6 +257,15 @@ int x509_note_sig_algo(void *context, size_t hdrlen, u= nsigned char tag, case OID_gost2012Signature512: ctx->cert->sig->hash_algo =3D "streebog512"; goto ecrdsa; + case OID_id_ml_dsa_44: + ctx->cert->sig->pkey_algo =3D "mldsa44"; + goto ml_dsa; + case OID_id_ml_dsa_65: + ctx->cert->sig->pkey_algo =3D "mldsa65"; + goto ml_dsa; + case OID_id_ml_dsa_87: + ctx->cert->sig->pkey_algo =3D "mldsa87"; + goto ml_dsa; } =20 rsa_pkcs1: @@ -274,6 +283,12 @@ int x509_note_sig_algo(void *context, size_t hdrlen, u= nsigned char tag, ctx->cert->sig->encoding =3D "x962"; ctx->sig_algo =3D ctx->last_oid; return 0; +ml_dsa: + ctx->cert->sig->algo_does_hash =3D true; + ctx->cert->sig->hash_algo =3D ctx->cert->sig->pkey_algo; + ctx->cert->sig->encoding =3D "raw"; + ctx->sig_algo =3D ctx->last_oid; + return 0; } =20 /* @@ -524,6 +539,15 @@ int x509_extract_key_data(void *context, size_t hdrlen, return -ENOPKG; } break; + case OID_id_ml_dsa_44: + ctx->cert->pub->pkey_algo =3D "mldsa44"; + break; + case OID_id_ml_dsa_65: + ctx->cert->pub->pkey_algo =3D "mldsa65"; + break; + case OID_id_ml_dsa_87: + ctx->cert->pub->pkey_algo =3D "mldsa87"; + break; default: return -ENOPKG; } diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 6de479ebbe5d..30821a6a4f72 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -145,6 +145,11 @@ enum OID { OID_id_rsassa_pkcs1_v1_5_with_sha3_384, /* 2.16.840.1.101.3.4.3.15 */ OID_id_rsassa_pkcs1_v1_5_with_sha3_512, /* 2.16.840.1.101.3.4.3.16 */ =20 + /* NIST FIPS-204 ML-DSA (Dilithium) */ + OID_id_ml_dsa_44, /* 2.16.840.1.101.3.4.3.17 */ + OID_id_ml_dsa_65, /* 2.16.840.1.101.3.4.3.18 */ + OID_id_ml_dsa_87, /* 2.16.840.1.101.3.4.3.19 */ + OID__NR }; From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 131CA31B11E for ; Mon, 5 Jan 2026 15:22:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626554; cv=none; b=HxNPL7K9DBu0PqQkmckYJ5LePkWYKq8Z1AQZ9hyqhf97FnO+W83z5fk2OlGWEBo9bhiIgB4lkLi9tJWPnaD/QwwCOZgbLagMPm0pKKtp7ycNJ91uG0ZbMa3HlivfRtr/w+XamBHMeIiL23B1tJxGWC++I0a5lfKFeI65ee/PGHg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626554; c=relaxed/simple; bh=G0jblK+IJlne+fEoSFxvc42mNaO9eL7zru181fO5a/Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=o8WKnLkdhO4Z6nX6+z8SMt4HJeoUKIAVBX23fpEHTvSoQEIWTBCt5humkNSwz8XddsHFx8eqw59hB0Vxc0HElGysvhMDg1CpKk8/5jEIE4shr7Ls40AELmzIoCqDHTRu2Mr07CoJk0aLBov35Ylfuswmux8mALUAbvJQaH4fRn0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=eVafxcJc; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="eVafxcJc" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626552; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=NpvBgHZrvzdFBd1yH1Fy4oCgJ0BEjj+474Jwv8vdQk8=; b=eVafxcJcUuiXtwJvmZ73VECQP1E+TqPsfGETF8H147AAdipGUuJg0PlCVN+4cg7Fwvy2jP So2L3rehj2kOBbXOrgtD0gm5WKFiv5ju4pHhdHudJ2IlS4iYNGwjxMcyZQBr5QDjRddW1V 5jHDi9Ddsai4xycDiUG5F0j9M7cc4TI= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-527-EWtQLbh9MPm8wKfRhaMxYw-1; Mon, 05 Jan 2026 10:22:29 -0500 X-MC-Unique: EWtQLbh9MPm8wKfRhaMxYw-1 X-Mimecast-MFC-AGG-ID: EWtQLbh9MPm8wKfRhaMxYw_1767626546 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CBE4B18009C0; Mon, 5 Jan 2026 15:22:25 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id CC390180049F; Mon, 5 Jan 2026 15:22:21 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 4/8] modsign: Enable ML-DSA module signing Date: Mon, 5 Jan 2026 15:21:29 +0000 Message-ID: <20260105152145.1801972-5-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Allow ML-DSA module signing to be enabled. Note that openssl's CMS_*() function suite does not, as of openssl-3.5.1, support the use of CMS_NOATTR with ML-DSA, so the prohibition against using authenticatedAttributes with module signing has to be removed. The selected digest then applies only to the algorithm used to calculate the digest stored in the messageDigest attribute. The ML-DSA algorithm uses its own internal choice of digest (SHAKE256) without regard to what's specified in the CMS message. This is, in theory, configurable, but there's currently no hook in the crypto_sig API to do that, though possibly it could be done by parameterising the name of the algorithm, e.g. ("mldsa87(sha512)"). Signed-off-by: David Howells cc: Eric Biggers cc: Lukas Wunner cc: Ignat Korchagin cc: Stephan Mueller cc: Herbert Xu cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org --- Documentation/admin-guide/module-signing.rst | 15 +++++------ certs/Kconfig | 21 ++++++++++++++++ certs/Makefile | 3 +++ crypto/asymmetric_keys/pkcs7_verify.c | 4 --- scripts/sign-file.c | 26 ++++++++++++++------ 5 files changed, 50 insertions(+), 19 deletions(-) diff --git a/Documentation/admin-guide/module-signing.rst b/Documentation/a= dmin-guide/module-signing.rst index a8667a777490..1a055c0b3356 100644 --- a/Documentation/admin-guide/module-signing.rst +++ b/Documentation/admin-guide/module-signing.rst @@ -28,10 +28,11 @@ trusted userspace bits. =20 This facility uses X.509 ITU-T standard certificates to encode the public = keys involved. The signatures are not themselves encoded in any industrial sta= ndard -type. The built-in facility currently only supports the RSA & NIST P-384 = ECDSA -public key signing standard (though it is pluggable and permits others to = be -used). The possible hash algorithms that can be used are SHA-2 and SHA-3 = of -sizes 256, 384, and 512 (the algorithm is selected by data in the signatur= e). +type. The built-in facility currently only supports the RSA, NIST P-384 E= CDSA +and NIST FIPS-204 ML-DSA (Dilithium) public key signing standards (though = it is +pluggable and permits others to be used). For RSA and ECDSA, the possible= hash +algorithms that can be used are SHA-2 and SHA-3 of sizes 256, 384, and 512= (the +algorithm is selected by data in the signature); ML-DSA uses SHAKE256. =20 =20 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D @@ -146,9 +147,9 @@ into vmlinux) using parameters in the:: =20 file (which is also generated if it does not already exist). =20 -One can select between RSA (``MODULE_SIG_KEY_TYPE_RSA``) and ECDSA -(``MODULE_SIG_KEY_TYPE_ECDSA``) to generate either RSA 4k or NIST -P-384 keypair. +One can select between RSA (``MODULE_SIG_KEY_TYPE_RSA``), ECDSA +(``MODULE_SIG_KEY_TYPE_ECDSA``) and ML-DSA (``MODULE_SIG_KEY_TYPE_MLDSA_*`= `) to +generate an RSA 4k, a NIST P-384 keypair or an ML-DSA 44, 65 or 87 keypair. =20 It is strongly recommended that you provide your own x509.genkey file. =20 diff --git a/certs/Kconfig b/certs/Kconfig index 78307dc25559..94b086684d07 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -39,6 +39,27 @@ config MODULE_SIG_KEY_TYPE_ECDSA Note: Remove all ECDSA signing keys, e.g. certs/signing_key.pem, when falling back to building Linux 5.14 and older kernels. =20 +config MODULE_SIG_KEY_TYPE_MLDSA_44 + bool "ML-DSA (Dilithium) 44" + select CRYPTO_MLDSA + help + Use an ML-DSA (Dilithium) 44 key (NIST FIPS 204) for module signing + with a SHAKE256 'hash' of the authenticatedAttributes. + +config MODULE_SIG_KEY_TYPE_MLDSA_65 + bool "ML-DSA (Dilithium) 65" + select CRYPTO_MLDSA + help + Use an ML-DSA (Dilithium) 65 key (NIST FIPS 204) for module signing + with a SHAKE256 'hash' of the authenticatedAttributes. + +config MODULE_SIG_KEY_TYPE_MLDSA_87 + bool "ML-DSA (Dilithium) 87" + select CRYPTO_MLDSA + help + Use an ML-DSA (Dilithium) 87 key (NIST FIPS 204) for module signing + with a SHAKE256 'hash' of the authenticatedAttributes. + endchoice =20 config SYSTEM_TRUSTED_KEYRING diff --git a/certs/Makefile b/certs/Makefile index f6fa4d8d75e0..3ee1960f9f4a 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -43,6 +43,9 @@ targets +=3D x509_certificate_list ifeq ($(CONFIG_MODULE_SIG_KEY),certs/signing_key.pem) =20 keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_ECDSA) :=3D -newkey ec -pkeyopt ec_pa= ramgen_curve:secp384r1 +keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_44) :=3D -newkey ml-dsa-44 +keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_65) :=3D -newkey ml-dsa-65 +keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_87) :=3D -newkey ml-dsa-87 =20 quiet_cmd_gen_key =3D GENKEY $@ cmd_gen_key =3D openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_H= ASH) -days 36500 \ diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys= /pkcs7_verify.c index 0f9f515b784d..f7ea1d41771d 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -424,10 +424,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, pr_warn("Invalid module sig (not pkcs7-data)\n"); return -EKEYREJECTED; } - if (pkcs7->have_authattrs) { - pr_warn("Invalid module sig (has authattrs)\n"); - return -EKEYREJECTED; - } break; case VERIFYING_FIRMWARE_SIGNATURE: if (pkcs7->data_type !=3D OID_data) { diff --git a/scripts/sign-file.c b/scripts/sign-file.c index 7070245edfc1..b726581075f9 100644 --- a/scripts/sign-file.c +++ b/scripts/sign-file.c @@ -315,18 +315,28 @@ int main(int argc, char **argv) ERR(!digest_algo, "EVP_get_digestbyname"); =20 #ifndef USE_PKCS7 + + unsigned int flags =3D + CMS_NOCERTS | + CMS_PARTIAL | + CMS_BINARY | + CMS_DETACHED | + CMS_STREAM | + CMS_NOSMIMECAP | + CMS_NO_SIGNING_TIME | + use_keyid; + if (!EVP_PKEY_is_a(private_key, "ML-DSA-44") && + !EVP_PKEY_is_a(private_key, "ML-DSA-65") && + !EVP_PKEY_is_a(private_key, "ML-DSA-87")) + flags |=3D use_signed_attrs; + /* Load the signature message from the digest buffer. */ - cms =3D CMS_sign(NULL, NULL, NULL, NULL, - CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | - CMS_DETACHED | CMS_STREAM); + cms =3D CMS_sign(NULL, NULL, NULL, NULL, flags); ERR(!cms, "CMS_sign"); =20 - ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, - CMS_NOCERTS | CMS_BINARY | - CMS_NOSMIMECAP | use_keyid | - use_signed_attrs), + ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, flags), "CMS_add1_signer"); - ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) !=3D 1, + ERR(CMS_final(cms, bm, NULL, flags) !=3D 1, "CMS_final"); =20 #else From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 455AE32B9A0 for ; Mon, 5 Jan 2026 15:22:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626561; cv=none; b=t3rSBiqnWrqesv4vTPjnF4f8izlqCFqKxs5WksuDUrdaX4gsxRZ1U7jQKt7JBlw3eUjzrfa0uId/FJb44G6aWf/d6a/ppJPfVgXhZ0ZltXjkk7EM2+DevPrp+gKPUZ9cYz+NsoASH2paUM+Behh0MDS2gdKwHR0xiE7JU/7inkE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626561; c=relaxed/simple; bh=etaZqOKFMGtXwhlWsJ58ECbFo7hIgPmNbU1aucbF0js=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VstyzGnoJcr2GARRK1N7ooUlyT3M7werkVsq2fmXSV6AIXqWfjk09XNzHTepcNmud6WMXTrScn1y2yXrnNpfgrjdAaSnE6opztYD4NUrV++OwKrDud5TpclXFnmwrqpFEni0E2m+jyBVzf+7P1jOEZIsdxjV6DD0sX/yE0dOtbo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=ByGcBnyl; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="ByGcBnyl" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626556; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=GCn25R94Rl0DRvlggJ7ZY6hRgZZolDQZGjV04TCBpFE=; b=ByGcBnylZY2o8rUmA9UxBLzAcoZXLsOjljdm8DiYtKK6fgPpp5o2a16mM3dsC5PbyssCmA xhj5pxHCIG9mRaromcc5T0N9PcNQpxyd8cpQeHf7S59zjRWWKJr/+DeLMuQ2OK4uGgXFGy QMtBH8oxPQjxHGKkbPildPYybyAiHRA= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-581-Axg0A-dyNYCX8khA4TESZw-1; Mon, 05 Jan 2026 10:22:33 -0500 X-MC-Unique: Axg0A-dyNYCX8khA4TESZw-1 X-Mimecast-MFC-AGG-ID: Axg0A-dyNYCX8khA4TESZw_1767626551 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 45AA31801211; Mon, 5 Jan 2026 15:22:31 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3B7D6180049F; Mon, 5 Jan 2026 15:22:27 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org, "David S. Miller" Subject: [PATCH v11 5/8] crypto: Add supplementary info param to asymmetric key signature verification Date: Mon, 5 Jan 2026 15:21:30 +0000 Message-ID: <20260105152145.1801972-6-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Content-Type: text/plain; charset="utf-8" Add a supplementary information parameter to the asymmetric key signature verification API, in particular crypto_sig_verify() and sig_alg::verify. This takes the form of a printable string containing of key=3Dval elements. This is needed as some algorithms require additional metadata (e.g. RSASSA-PSS) and this extra metadata is included in the X.509 certificates and PKCS#7 messages. Furthermore, keyctl(KEYCTL_PKEY_VERIFY) already allows for this to be passed to the kernel, as do the _SIGN, _ENCRYPT and _DECRYPT keyctls. Signed-off-by: David Howells cc: Herbert Xu cc: "David S. Miller" cc: Lukas Wunner cc: Ignat Korchagin cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org Reviewed-by: Ignat Korchagin --- crypto/asymmetric_keys/asymmetric_type.c | 1 + crypto/asymmetric_keys/public_key.c | 2 +- crypto/asymmetric_keys/signature.c | 1 + crypto/ecdsa-p1363.c | 5 +++-- crypto/ecdsa-x962.c | 5 +++-- crypto/ecdsa.c | 3 ++- crypto/ecrdsa.c | 3 ++- crypto/mldsa.c | 3 ++- crypto/rsassa-pkcs1.c | 3 ++- crypto/sig.c | 3 ++- include/crypto/public_key.h | 1 + include/crypto/sig.h | 9 ++++++--- 12 files changed, 26 insertions(+), 13 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_k= eys/asymmetric_type.c index 348966ea2175..dad4f0edfa25 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -596,6 +596,7 @@ static int asymmetric_key_verify_signature(struct kerne= l_pkey_params *params, .digest_size =3D params->in_len, .encoding =3D params->encoding, .hash_algo =3D params->hash_algo, + .info =3D params->info, .digest =3D (void *)in, .s =3D (void *)in2, }; diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/p= ublic_key.c index ed6b4b5ae4ef..61dc4f626620 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -433,7 +433,7 @@ int public_key_verify_signature(const struct public_key= *pkey, goto error_free_key; =20 ret =3D crypto_sig_verify(tfm, sig->s, sig->s_size, - sig->digest, sig->digest_size); + sig->digest, sig->digest_size, sig->info); =20 error_free_key: kfree_sensitive(key); diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/si= gnature.c index 041d04b5c953..26c0c0112ac4 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -29,6 +29,7 @@ void public_key_signature_free(struct public_key_signatur= e *sig) kfree(sig->auth_ids[i]); kfree(sig->s); kfree(sig->digest); + kfree(sig->info); kfree(sig); } } diff --git a/crypto/ecdsa-p1363.c b/crypto/ecdsa-p1363.c index e0c55c64711c..fa987dba1213 100644 --- a/crypto/ecdsa-p1363.c +++ b/crypto/ecdsa-p1363.c @@ -18,7 +18,8 @@ struct ecdsa_p1363_ctx { =20 static int ecdsa_p1363_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen) + const void *digest, unsigned int dlen, + const char *info) { struct ecdsa_p1363_ctx *ctx =3D crypto_sig_ctx(tfm); unsigned int keylen =3D DIV_ROUND_UP_POW2(crypto_sig_keysize(ctx->child), @@ -32,7 +33,7 @@ static int ecdsa_p1363_verify(struct crypto_sig *tfm, ecc_digits_from_bytes(src, keylen, sig.r, ndigits); ecc_digits_from_bytes(src + keylen, keylen, sig.s, ndigits); =20 - return crypto_sig_verify(ctx->child, &sig, sizeof(sig), digest, dlen); + return crypto_sig_verify(ctx->child, &sig, sizeof(sig), digest, dlen, inf= o); } =20 static unsigned int ecdsa_p1363_key_size(struct crypto_sig *tfm) diff --git a/crypto/ecdsa-x962.c b/crypto/ecdsa-x962.c index ee71594d10a0..5d7f1078989c 100644 --- a/crypto/ecdsa-x962.c +++ b/crypto/ecdsa-x962.c @@ -75,7 +75,8 @@ int ecdsa_get_signature_s(void *context, size_t hdrlen, u= nsigned char tag, =20 static int ecdsa_x962_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen) + const void *digest, unsigned int dlen, + const char *info) { struct ecdsa_x962_ctx *ctx =3D crypto_sig_ctx(tfm); struct ecdsa_x962_signature_ctx sig_ctx; @@ -89,7 +90,7 @@ static int ecdsa_x962_verify(struct crypto_sig *tfm, return err; =20 return crypto_sig_verify(ctx->child, &sig_ctx.sig, sizeof(sig_ctx.sig), - digest, dlen); + digest, dlen, info); } =20 static unsigned int ecdsa_x962_key_size(struct crypto_sig *tfm) diff --git a/crypto/ecdsa.c b/crypto/ecdsa.c index ce8e4364842f..144fd6b9168b 100644 --- a/crypto/ecdsa.c +++ b/crypto/ecdsa.c @@ -65,7 +65,8 @@ static int _ecdsa_verify(struct ecc_ctx *ctx, const u64 *= hash, const u64 *r, con */ static int ecdsa_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen) + const void *digest, unsigned int dlen, + const char *info) { struct ecc_ctx *ctx =3D crypto_sig_ctx(tfm); size_t bufsize =3D ctx->curve->g.ndigits * sizeof(u64); diff --git a/crypto/ecrdsa.c b/crypto/ecrdsa.c index 2c0602f0cd40..59f2d5bb3be4 100644 --- a/crypto/ecrdsa.c +++ b/crypto/ecrdsa.c @@ -69,7 +69,8 @@ static const struct ecc_curve *get_curve_by_oid(enum OID = oid) =20 static int ecrdsa_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen) + const void *digest, unsigned int dlen, + const char *info) { struct ecrdsa_ctx *ctx =3D crypto_sig_ctx(tfm); unsigned int ndigits =3D dlen / sizeof(u64); diff --git a/crypto/mldsa.c b/crypto/mldsa.c index 2146c774b5ca..ba071d030ab0 100644 --- a/crypto/mldsa.c +++ b/crypto/mldsa.c @@ -25,7 +25,8 @@ static int crypto_mldsa_sign(struct crypto_sig *tfm, =20 static int crypto_mldsa_verify(struct crypto_sig *tfm, const void *sig, unsigned int sig_len, - const void *msg, unsigned int msg_len) + const void *msg, unsigned int msg_len, + const char *info) { const struct crypto_mldsa_ctx *ctx =3D crypto_sig_ctx(tfm); =20 diff --git a/crypto/rsassa-pkcs1.c b/crypto/rsassa-pkcs1.c index 94fa5e9600e7..6283050e609a 100644 --- a/crypto/rsassa-pkcs1.c +++ b/crypto/rsassa-pkcs1.c @@ -215,7 +215,8 @@ static int rsassa_pkcs1_sign(struct crypto_sig *tfm, =20 static int rsassa_pkcs1_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen) + const void *digest, unsigned int dlen, + const char *info) { struct sig_instance *inst =3D sig_alg_instance(tfm); struct rsassa_pkcs1_inst_ctx *ictx =3D sig_instance_ctx(inst); diff --git a/crypto/sig.c b/crypto/sig.c index beba745b6405..c56fea3a53ae 100644 --- a/crypto/sig.c +++ b/crypto/sig.c @@ -92,7 +92,8 @@ static int sig_default_sign(struct crypto_sig *tfm, =20 static int sig_default_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *dst, unsigned int dlen) + const void *dst, unsigned int dlen, + const char *info) { return -ENOSYS; } diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index e4ec8003a3a4..1e9a1e4e9916 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -47,6 +47,7 @@ struct public_key_signature { u32 s_size; /* Number of bytes in signature */ u32 digest_size; /* Number of bytes in digest */ bool algo_does_hash; /* Public key algo does its own hashing */ + char *info; /* Supplementary parameters */ const char *pkey_algo; const char *hash_algo; const char *encoding; diff --git a/include/crypto/sig.h b/include/crypto/sig.h index fa6dafafab3f..885fa6487780 100644 --- a/include/crypto/sig.h +++ b/include/crypto/sig.h @@ -56,7 +56,8 @@ struct sig_alg { void *dst, unsigned int dlen); int (*verify)(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen); + const void *digest, unsigned int dlen, + const char *info); int (*set_pub_key)(struct crypto_sig *tfm, const void *key, unsigned int keylen); int (*set_priv_key)(struct crypto_sig *tfm, @@ -209,16 +210,18 @@ static inline int crypto_sig_sign(struct crypto_sig *= tfm, * @slen: source length * @digest: digest * @dlen: digest length + * @info: Additional parameters as a set of k=3Dv * * Return: zero on verification success; error code in case of error. */ static inline int crypto_sig_verify(struct crypto_sig *tfm, const void *src, unsigned int slen, - const void *digest, unsigned int dlen) + const void *digest, unsigned int dlen, + const char *info) { struct sig_alg *alg =3D crypto_sig_alg(tfm); =20 - return alg->verify(tfm, src, slen, digest, dlen); + return alg->verify(tfm, src, slen, digest, dlen, info); } =20 /** From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AE6E031D759 for ; Mon, 5 Jan 2026 15:22:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626564; cv=none; b=rbBf9PoZu1/g26JQ8RbLqCAwSsR+US2zjmtB3RnR8OsRz0E/PGtyvE8JJCxNAQxVpPn6DIfgo59X0NfT6igCJU8bLeflkJojJfTB4MFSXpgDei07b5BZjmmLe08o5e4mLLmURt8wfPF5Zd4CnRJrXyO5NKZMDR2gjq9cNGpPDUY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626564; c=relaxed/simple; bh=29+zVS0tgtqqF3cVesuBT1xRNCZ2+6ZdgjDFR0HAkdE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XFdJ2iHAsIQdCvooa0p2IwFqsyT0c9Ng7Nn5095r5VMsvF1vuqULbEkOvmT59rne97Vov5HItr7Uj8EtPBt8HBMFhbKpaj46K2Kb5vy613NM+qjNuGfhjAppFOsppKHPTH3Z5tnjg9f8Uylg/IyXzYrubG5PAvnI6rvD1wdoifM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=X1Hmnrju; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="X1Hmnrju" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626561; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=vhawQNAPU6OTYdoB9wFt5+F0BuK2jty/lSNfz8PUknc=; b=X1HmnrjuxP3HwdSMirHQq72P0/iGhmNSjfJVAeGIj6hdtH2Q8N3l7hsKkG49CoTDwFLHWK GifVCXOa1dp1hLrll+flKjZBhNIcEkU+tyaAJAPlTFIrcFSLVhQoKrJjQx9MmwZrrJ4LLf acg7aqQpDIF3exg55T8ZEGbzvgaEnyA= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-679-2eo9spiYPry86dmrGMcz4w-1; Mon, 05 Jan 2026 10:22:40 -0500 X-MC-Unique: 2eo9spiYPry86dmrGMcz4w-1 X-Mimecast-MFC-AGG-ID: 2eo9spiYPry86dmrGMcz4w_1767626557 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 97A39195FCE9; Mon, 5 Jan 2026 15:22:37 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 11004180035A; Mon, 5 Jan 2026 15:22:32 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org, Tadeusz Struk , "David S. Miller" Subject: [PATCH v11 6/8] crypto: Add RSASSA-PSS support Date: Mon, 5 Jan 2026 15:21:31 +0000 Message-ID: <20260105152145.1801972-7-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Add support for RSASSA-PSS [RFC8017 sec 8.1] signature verification support to the RSA driver in crypto/. Note that signing support is not provided. The verification function requires an info string formatted as a space-separated list of key=3Dvalue pairs. The following parameters need to be provided: (1) sighash=3D The hash algorithm to be used to digest the data. (2) pss_mask=3D,... The mask generation function (MGF) and its parameters. (3) pss_salt=3D The length of the salt used. The only MGF currently supported is "mgf1". This takes an additional parameter indicating the mask-generating hash (which need not be the same as the data hash). E.g.: "sighash=3Dsha256 pss_mask=3Dmgf1,sha256 pss_salt=3D32" Signed-off-by: David Howells cc: Tadeusz Struk cc: Herbert Xu cc: David S. Miller cc: Lukas Wunner cc: Ignat Korchagin cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org --- crypto/Makefile | 1 + crypto/rsa.c | 8 + crypto/rsassa-pss.c | 397 ++++++++++++++++++++++++++++++++++ include/crypto/internal/rsa.h | 2 + 4 files changed, 408 insertions(+) create mode 100644 crypto/rsassa-pss.c diff --git a/crypto/Makefile b/crypto/Makefile index 267d5403045b..5c91440d1751 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -50,6 +50,7 @@ rsa_generic-y +=3D rsa.o rsa_generic-y +=3D rsa_helper.o rsa_generic-y +=3D rsa-pkcs1pad.o rsa_generic-y +=3D rsassa-pkcs1.o +rsa_generic-y +=3D rsassa-pss.o obj-$(CONFIG_CRYPTO_RSA) +=3D rsa_generic.o =20 $(obj)/ecdsasignature.asn1.o: $(obj)/ecdsasignature.asn1.c $(obj)/ecdsasig= nature.asn1.h diff --git a/crypto/rsa.c b/crypto/rsa.c index 6c7734083c98..189a09d54c16 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include =20 @@ -414,8 +415,14 @@ static int __init rsa_init(void) if (err) goto err_unregister_rsa_pkcs1pad; =20 + err =3D crypto_register_sig(&rsassa_pss_alg); + if (err) + goto err_rsassa_pss; + return 0; =20 +err_rsassa_pss: + crypto_unregister_template(&rsassa_pkcs1_tmpl); err_unregister_rsa_pkcs1pad: crypto_unregister_template(&rsa_pkcs1pad_tmpl); err_unregister_rsa: @@ -425,6 +432,7 @@ static int __init rsa_init(void) =20 static void __exit rsa_exit(void) { + crypto_unregister_sig(&rsassa_pss_alg); crypto_unregister_template(&rsassa_pkcs1_tmpl); crypto_unregister_template(&rsa_pkcs1pad_tmpl); crypto_unregister_akcipher(&rsa); diff --git a/crypto/rsassa-pss.c b/crypto/rsassa-pss.c new file mode 100644 index 000000000000..7f27e8fa6fa7 --- /dev/null +++ b/crypto/rsassa-pss.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RSA Signature Scheme combined with EMSA-PSS encoding (RFC 8017 sec 8.2) + * + * https://www.rfc-editor.org/rfc/rfc8017#section-8.1 + * + * Copyright (c) 2025 Red Hat + */ + +#define pr_fmt(fmt) "RSAPSS: "fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rsassa_pss_ctx { + struct crypto_akcipher *rsa; + unsigned int key_size; + unsigned int salt_len; + char *pss_hash; + char *mgf1_hash; +}; + +enum { + rsassa_pss_verify_hash_algo, + rsassa_pss_verify_pss_mask, + rsassa_pss_verify_pss_salt, +}; + +static const match_table_t rsassa_pss_verify_params =3D { + { rsassa_pss_verify_hash_algo, "sighash=3D%s" }, + { rsassa_pss_verify_pss_mask, "pss_mask=3D%s" }, + { rsassa_pss_verify_pss_salt, "pss_salt=3D%u" }, + {} +}; + +/* + * Parse the signature parameters out of the info string. + */ +static int rsassa_pss_vinfo_parse(struct rsassa_pss_ctx *ctx, + char *info) +{ + substring_t args[MAX_OPT_ARGS]; + char *p, *q; + + ctx->pss_hash =3D NULL; + ctx->mgf1_hash =3D NULL; + ctx->salt_len =3D 0; + + for (p =3D info; p && *p;) { + if (isspace(*p)) { + p++; + continue; + } + q =3D p++; + while (*p && !isspace(*p)) + p++; + + if (!*p) + p =3D NULL; + else + *p++ =3D 0; + + switch (match_token(q, rsassa_pss_verify_params, args)) { + case rsassa_pss_verify_hash_algo: + *args[0].to =3D 0; + ctx->pss_hash =3D args[0].from; + break; + case rsassa_pss_verify_pss_mask: + if (memcmp(args[0].from, "mgf1", 4) !=3D 0) + return -ENOPKG; + if (args[0].from[4] !=3D ',') + return -EINVAL; + args[0].from +=3D 5; + if (args[0].from >=3D args[0].to) + return -EINVAL; + *args[0].to =3D 0; + ctx->mgf1_hash =3D args[0].from; + break; + case rsassa_pss_verify_pss_salt: + if (match_uint(&args[0], &ctx->salt_len) < 0) + return -EINVAL; + break; + default: + pr_debug("Unknown info param\n"); + return -EINVAL; /* Ignoring it might be better. */ + } + } + + if (!ctx->pss_hash || + !ctx->mgf1_hash || + !ctx->salt_len) + return -EINVAL; + return 0; +} + +DEFINE_FREE(crypto_free_shash, struct crypto_shash*, + if (!IS_ERR_OR_NULL(_T)) { crypto_free_shash(_T); }); + +/* + * Perform mask =3D MGF1(mgfSeed, masklen) - RFC8017 appendix B.2.1. + */ +static int MGF1(struct rsassa_pss_ctx *ctx, + const u8 *mgfSeed, unsigned int mgfSeed_len, + u8 *mask, unsigned int maskLen) +{ + struct crypto_shash *hash_tfm __free(crypto_free_shash) =3D NULL; + struct shash_desc *Hash __free(kfree) =3D NULL; + unsigned int counter, count_to, hLen, T_len; + __be32 *C; + int err; + u8 *T, *t, *to_hash; + + hash_tfm =3D crypto_alloc_shash(ctx->mgf1_hash, 0, 0); + if (IS_ERR(hash_tfm)) + return PTR_ERR(hash_tfm); + + hLen =3D crypto_shash_digestsize(hash_tfm); + count_to =3D DIV_ROUND_UP(maskLen, hLen); + T_len =3D hLen * count_to; + + Hash =3D kmalloc(roundup(sizeof(struct shash_desc) + + crypto_shash_descsize(hash_tfm), 64) + + roundup(T_len, 64) + /* T */ + roundup(mgfSeed_len + 4, 64), /* mgfSeed||C */ + GFP_KERNEL); + if (!Hash) + return -ENOMEM; + + Hash->tfm =3D hash_tfm; + + /* 2: Let T be the empty octet string. */ + T =3D (void *)Hash + + roundup(sizeof(struct shash_desc) + + crypto_shash_descsize(hash_tfm), 64); + + /* 3: Generate the mask. */ + to_hash =3D T + roundup(T_len, 64); + memcpy(to_hash, mgfSeed, mgfSeed_len); + C =3D (__be32 *)(to_hash + mgfSeed_len); + + t =3D T; + for (counter =3D 0; counter < count_to; counter++) { + /* 3A: C =3D I2OSP(counter, 4). */ + put_unaligned_be32(counter, C); + + /* 3B: T =3D T || Hash(mgfSeed || C). */ + err =3D crypto_shash_digest(Hash, to_hash, mgfSeed_len + 4, t); + if (err < 0) + return err; + + t +=3D hLen; + } + + /* 4: Output T to mask */ + memcpy(mask, T, maskLen); + return 0; +} + +/* + * Perform EMSA-PSS-VERIFY(M, EM, emBits) - RFC8017 sec 9.1.2. + */ +static int emsa_pss_verify(struct rsassa_pss_ctx *ctx, + const u8 *M, unsigned int M_len, + const u8 *EM, unsigned int emLen) +{ + struct crypto_shash *hash_tfm __free(crypto_free_shash); + struct shash_desc *Hash __free(kfree) =3D NULL; + unsigned int emBits, hLen, sLen, DB_len; + const u8 *maskedDB, *H; + u8 *mHash, *dbMask, *DB, *salt, *Mprime, *Hprime; + int err, i; + + emBits =3D 8 - fls(EM[0]); + emBits =3D emLen * 8 - emBits; + + hash_tfm =3D crypto_alloc_shash(ctx->pss_hash, 0, 0); + if (IS_ERR(hash_tfm)) + return PTR_ERR(hash_tfm); + + hLen =3D crypto_shash_digestsize(hash_tfm); + sLen =3D ctx->salt_len; + + if (sLen > 65536 || + emBits < 8 * (hLen + sLen) + 9) + return -EBADMSG; + + DB_len =3D emLen - hLen - 1; + + Hash =3D kmalloc(roundup(sizeof(struct shash_desc) + + crypto_shash_descsize(hash_tfm), 64) + + roundup(hLen, 64) + /* mHash */ + roundup(DB_len, 64) + /* DB and dbMask */ + roundup(8 + hLen + sLen, 64) + /* M' */ + roundup(hLen, 64), /* H' */ + GFP_KERNEL); + if (!Hash) + return -ENOMEM; + + Hash->tfm =3D hash_tfm; + + mHash =3D (void *)Hash + + roundup(sizeof(struct shash_desc) + + crypto_shash_descsize(hash_tfm), 64); + DB =3D dbMask =3D mHash + roundup(hLen, 64); + Mprime =3D dbMask + roundup(DB_len, 64); + Hprime =3D Mprime + roundup(8 + hLen + sLen, 64); + + /* 1. Check len M against hash input limitation. */ + /* The standard says ~2EiB for SHA1, so I think we can ignore this. */ + + /* 2. mHash =3D Hash(M). + * In theory, we would do: + * err =3D crypto_shash_digest(Hash, M, M_len, mHash); + * but the caller is assumed to already have done that for us. + */ + if (M_len !=3D hLen) + return -EINVAL; + memcpy(mHash, M, hLen); + + /* 3. Check emLen against hLen + sLen + 2. */ + if (emLen < hLen + sLen + 2) + return -EBADMSG; + + /* 4. Validate EM. */ + if (EM[emLen - 1] !=3D 0xbc) + return -EKEYREJECTED; + + /* 5. Pick maskedDB and H. */ + maskedDB =3D EM; + H =3D EM + DB_len; + + /* 6. Check leftmost 8emLen-emBits bits of maskedDB are 0. */ + /* Can only find emBits by counting the zeros on the Left. */ + + /* 7. Let dbMask =3D MGF(H, emLen - hLen - 1). */ + err =3D MGF1(ctx, H, hLen, dbMask, DB_len); + if (err < 0) + return err; + + /* 8. Let DB =3D maskedDB XOR dbMask. */ + for (i =3D 0; i < DB_len; i++) + DB[i] =3D maskedDB[i] ^ dbMask[i]; + + /* 9. Set leftmost bits in DB to zero. */ + int z =3D 8 * emLen - emBits; + if (z > 0) { + if (z >=3D 8) { + DB[0] =3D 0; + } else { + z =3D 8 - z; + DB[0] &=3D (1 << z) - 1; + } + } + + /* 10. Check the left part of DB is {0,0,...,1}. */ + for (i =3D 0; i < emLen - hLen - sLen - 2; i++) + if (DB[i] !=3D 0) + return -EKEYREJECTED; + if (DB[i] !=3D 0x01) + return -EKEYREJECTED; + + /* 11. Let salt be the last sLen octets of DB. */ + salt =3D DB + DB_len - sLen; + + /* 12. Let M' be 00 00 00 00 00 00 00 00 || mHash || salt. */ + memset(Mprime, 0, 8); + memcpy(Mprime + 8, mHash, hLen); + memcpy(Mprime + 8 + hLen, salt, sLen); + + /* 13. Let H' =3D Hash(M'). */ + err =3D crypto_shash_digest(Hash, Mprime, 8 + hLen + sLen, Hprime); + if (err < 0) + return err; + + /* 14. Check H =3D H'. */ + if (memcmp(H, Hprime, hLen) !=3D 0) + return -EKEYREJECTED; + return 0; +} + +/* + * Perform RSASSA-PSS-VERIFY((n,e),M,S) - RFC8017 sec 8.1.2. + */ +static int rsassa_pss_verify(struct crypto_sig *tfm, + const void *src, unsigned int slen, + const void *digest, unsigned int dlen, + const char *info) +{ + struct akcipher_request *rsa_req __free(kfree) =3D NULL; + struct rsassa_pss_ctx *ctx =3D crypto_sig_ctx(tfm); + struct crypto_wait cwait; + struct scatterlist sg; + unsigned int rsa_reqsize =3D crypto_akcipher_reqsize(ctx->rsa); + char *str __free(kfree) =3D NULL; + u8 *EM; + int err; + + if (!info) + return -EINVAL; + + str =3D kstrdup(info, GFP_KERNEL); + if (!str) + return -ENOMEM; + + err =3D rsassa_pss_vinfo_parse(ctx, str); + if (err < 0) + return err; + + /* RFC8017 sec 8.1.2 step 1 - length checking */ + if (!ctx->key_size || slen !=3D ctx->key_size) + return -EINVAL; + + /* RFC8017 sec 8.1.2 step 2 - RSA verification */ + rsa_req =3D kmalloc(sizeof(*rsa_req) + rsa_reqsize + ctx->key_size, + GFP_KERNEL); + if (!rsa_req) + return -ENOMEM; + + EM =3D (u8 *)(rsa_req + 1) + rsa_reqsize; + memcpy(EM, src, slen); + + crypto_init_wait(&cwait); + sg_init_one(&sg, EM, slen); + akcipher_request_set_tfm(rsa_req, ctx->rsa); + akcipher_request_set_crypt(rsa_req, &sg, &sg, slen, slen); + akcipher_request_set_callback(rsa_req, CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &cwait); + + err =3D crypto_akcipher_encrypt(rsa_req); + err =3D crypto_wait_req(err, &cwait); + if (err) + return err; + + /* RFC 8017 sec 8.1.2 step 3 - EMSA-PSS(M, EM, modbits-1) */ + return emsa_pss_verify(ctx, digest, dlen, EM, slen); +} + +static unsigned int rsassa_pss_key_size(struct crypto_sig *tfm) +{ + struct rsassa_pss_ctx *ctx =3D crypto_sig_ctx(tfm); + + return ctx->key_size * BITS_PER_BYTE; +} + +static int rsassa_pss_set_pub_key(struct crypto_sig *tfm, + const void *key, unsigned int keylen) +{ + struct rsassa_pss_ctx *ctx =3D crypto_sig_ctx(tfm); + + return rsa_set_key(ctx->rsa, &ctx->key_size, RSA_PUB, key, keylen); +} + +static int rsassa_pss_init_tfm(struct crypto_sig *tfm) +{ + struct crypto_akcipher *rsa; + struct rsassa_pss_ctx *ctx =3D crypto_sig_ctx(tfm); + + rsa =3D crypto_alloc_akcipher("rsa", 0, 0); + if (IS_ERR(rsa)) + return PTR_ERR(rsa); + + ctx->rsa =3D rsa; + return 0; +} + +static void rsassa_pss_exit_tfm(struct crypto_sig *tfm) +{ + struct rsassa_pss_ctx *ctx =3D crypto_sig_ctx(tfm); + + crypto_free_akcipher(ctx->rsa); +} + +struct sig_alg rsassa_pss_alg =3D { + .verify =3D rsassa_pss_verify, + .set_pub_key =3D rsassa_pss_set_pub_key, + .key_size =3D rsassa_pss_key_size, + .init =3D rsassa_pss_init_tfm, + .exit =3D rsassa_pss_exit_tfm, + .base =3D { + .cra_name =3D "rsassa-pss", + .cra_driver_name =3D "rsassa-pss-generic", + .cra_priority =3D 100, + .cra_module =3D THIS_MODULE, + .cra_ctxsize =3D sizeof(struct rsassa_pss_ctx), + }, +}; + +MODULE_ALIAS_CRYPTO("rsassa-pss"); diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h index 071a1951b992..d7f38a273949 100644 --- a/include/crypto/internal/rsa.h +++ b/include/crypto/internal/rsa.h @@ -83,4 +83,6 @@ static inline int rsa_set_key(struct crypto_akcipher *chi= ld, =20 extern struct crypto_template rsa_pkcs1pad_tmpl; extern struct crypto_template rsassa_pkcs1_tmpl; +extern struct sig_alg rsassa_pss_alg; + #endif From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 821F0330320 for ; Mon, 5 Jan 2026 15:22:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626572; cv=none; b=nrRQHUwfZEGCt8cifeX0QjNtb8I8jd4He41Owp9qJTCvpShy2Glbji+0twYWi5mwx0m3GIO9dJKkPpFBBkszp9J4a6ZTFQBuYpuuRSEDYsnJbMHz4Q5r0ur2t+SQyBM3eWom+zCKyVn4ZHPlFh8J/z8mOCG6yDj8ILY+0BGGTvQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626572; c=relaxed/simple; bh=OvX/sy/CyoJ7z56fS1TP2A6tzhdziac52amoDCvk/AM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uUrZKMMNl6aMK+3TwIkcKwQKqUOkEZlY+7bAZfhVjMjqxMRVYcBqNmqVmwX4eniKcO005whA15/79FljWh2VY3hF9Pe7jAgmDlH1Z1IKTIItG37iIAxW6zTYSpT7YvfmEwVaw1sTxpQdql4+qMTKhJoxouJvB51ARdth60KJMRw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=eBmvgi1a; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="eBmvgi1a" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626568; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=dzLPjf1BJ4I1gH9XJaalIbwUkJls+c/KgNF/cINQpyU=; b=eBmvgi1as9/2Y0cL+j5ZnnJ5OS7Xh29MFc4cqB0wkpq0g8tg8KdqTwbbU/Hk3y5iV5zwJE Esk7GPpH93szRMdqtep8H9PLkw4IGuD3jjMT8qDf8FYg+yI34U/jz2KCEcKMP35VXaxD8I 4P/U+p6M/Uc+Z6B0LuR2mUSvXYpdX9w= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-244-uFOzPDS8PVKxH8sNS7lNiQ-1; Mon, 05 Jan 2026 10:22:45 -0500 X-MC-Unique: uFOzPDS8PVKxH8sNS7lNiQ-1 X-Mimecast-MFC-AGG-ID: uFOzPDS8PVKxH8sNS7lNiQ_1767626563 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 19FBB1800669; Mon, 5 Jan 2026 15:22:43 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0B126180035A; Mon, 5 Jan 2026 15:22:38 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 7/8] pkcs7, x509: Add RSASSA-PSS support Date: Mon, 5 Jan 2026 15:21:32 +0000 Message-ID: <20260105152145.1801972-8-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 Content-Type: text/plain; charset="utf-8" Add support for RSASSA-PSS keys and signatures to the PKCS#7 and X.509 implementations. This requires adding support for algorithm parameters for keys and signatures as RSASSA-PSS needs metadata. The ASN.1 encoded data is converted into a printable key=3Dvalue list string and passed to the verification code. Signed-off-by: David Howells cc: Lukas Wunner cc: Ignat Korchagin cc: Herbert Xu cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org --- crypto/asymmetric_keys/Makefile | 12 +- crypto/asymmetric_keys/mgf1_params.asn1 | 12 ++ crypto/asymmetric_keys/pkcs7.asn1 | 2 +- crypto/asymmetric_keys/pkcs7_parser.c | 113 ++++++----- crypto/asymmetric_keys/public_key.c | 10 + crypto/asymmetric_keys/rsassa_params.asn1 | 25 +++ crypto/asymmetric_keys/rsassa_parser.c | 233 ++++++++++++++++++++++ crypto/asymmetric_keys/rsassa_parser.h | 25 +++ crypto/asymmetric_keys/x509.asn1 | 2 +- crypto/asymmetric_keys/x509_cert_parser.c | 96 +++++---- crypto/asymmetric_keys/x509_parser.h | 33 ++- crypto/asymmetric_keys/x509_public_key.c | 28 ++- include/linux/oid_registry.h | 2 + 13 files changed, 490 insertions(+), 103 deletions(-) create mode 100644 crypto/asymmetric_keys/mgf1_params.asn1 create mode 100644 crypto/asymmetric_keys/rsassa_params.asn1 create mode 100644 crypto/asymmetric_keys/rsassa_parser.c create mode 100644 crypto/asymmetric_keys/rsassa_parser.h diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makef= ile index bc65d3b98dcb..c5aed382ee8a 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -21,7 +21,11 @@ x509_key_parser-y :=3D \ x509_akid.asn1.o \ x509_cert_parser.o \ x509_loader.o \ - x509_public_key.o + x509_public_key.o \ + rsassa_params.asn1.o \ + rsassa_parser.o \ + mgf1_params.asn1.o + obj-$(CONFIG_FIPS_SIGNATURE_SELFTEST) +=3D x509_selftest.o x509_selftest-y +=3D selftest.o x509_selftest-$(CONFIG_FIPS_SIGNATURE_SELFTEST_RSA) +=3D selftest_rsa.o @@ -31,8 +35,14 @@ $(obj)/x509_cert_parser.o: \ $(obj)/x509.asn1.h \ $(obj)/x509_akid.asn1.h =20 +$(obj)/rsassa_parser.o: \ + $(obj)/rsassa_params.asn1.h \ + $(obj)/mgf1_params.asn1.h + $(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h +$(obj)/rsassa_params.asn1.o: $(obj)/rsassa_params.asn1.c $(obj)/rsassa_par= ams.asn1.h +$(obj)/mgf1_params.asn1.o: $(obj)/mgf1_params.asn1.c $(obj)/mgf1_params.as= n1.h =20 # # PKCS#8 private key handling diff --git a/crypto/asymmetric_keys/mgf1_params.asn1 b/crypto/asymmetric_ke= ys/mgf1_params.asn1 new file mode 100644 index 000000000000..c3bc4643e72c --- /dev/null +++ b/crypto/asymmetric_keys/mgf1_params.asn1 @@ -0,0 +1,12 @@ +-- SPDX-License-Identifier: BSD-3-Clause +-- +-- Copyright (C) 2009 IETF Trust and the persons identified as authors +-- of the code +-- +-- +-- https://datatracker.ietf.org/doc/html/rfc4055 Section 6. + +AlgorithmIdentifier ::=3D SEQUENCE { + algorithm OBJECT IDENTIFIER ({ mgf1_note_OID }), + parameters ANY OPTIONAL +} diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkc= s7.asn1 index 28e1f4a41c14..03c2248f23bc 100644 --- a/crypto/asymmetric_keys/pkcs7.asn1 +++ b/crypto/asymmetric_keys/pkcs7.asn1 @@ -124,7 +124,7 @@ UnauthenticatedAttribute ::=3D SEQUENCE { =20 DigestEncryptionAlgorithmIdentifier ::=3D SEQUENCE { algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), - parameters ANY OPTIONAL + parameters ANY OPTIONAL ({ pkcs7_sig_note_algo_params }) } =20 EncryptedDigest ::=3D OCTET STRING ({ pkcs7_sig_note_signature }) diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys= /pkcs7_parser.c index 90c36fe1b5ed..81996b60c1f1 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -14,6 +14,7 @@ #include #include #include "pkcs7_parser.h" +#include "rsassa_parser.h" #include "pkcs7.asn1.h" =20 MODULE_DESCRIPTION("PKCS#7 parser"); @@ -30,6 +31,8 @@ struct pkcs7_parse_context { enum OID last_oid; /* Last OID encountered */ unsigned x509_index; unsigned sinfo_index; + unsigned algo_params_size; + const void *algo_params; const void *raw_serial; unsigned raw_serial_size; unsigned raw_issuer_size; @@ -225,45 +228,29 @@ int pkcs7_sig_note_digest_algo(void *context, size_t = hdrlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx =3D context; + const char *algo; =20 - switch (ctx->last_oid) { - case OID_sha1: - ctx->sinfo->sig->hash_algo =3D "sha1"; - break; - case OID_sha256: - ctx->sinfo->sig->hash_algo =3D "sha256"; - break; - case OID_sha384: - ctx->sinfo->sig->hash_algo =3D "sha384"; - break; - case OID_sha512: - ctx->sinfo->sig->hash_algo =3D "sha512"; - break; - case OID_sha224: - ctx->sinfo->sig->hash_algo =3D "sha224"; - break; - case OID_sm3: - ctx->sinfo->sig->hash_algo =3D "sm3"; - break; - case OID_gost2012Digest256: - ctx->sinfo->sig->hash_algo =3D "streebog256"; - break; - case OID_gost2012Digest512: - ctx->sinfo->sig->hash_algo =3D "streebog512"; - break; - case OID_sha3_256: - ctx->sinfo->sig->hash_algo =3D "sha3-256"; - break; - case OID_sha3_384: - ctx->sinfo->sig->hash_algo =3D "sha3-384"; - break; - case OID_sha3_512: - ctx->sinfo->sig->hash_algo =3D "sha3-512"; - break; - default: - printk("Unsupported digest algo: %u\n", ctx->last_oid); + algo =3D oid_to_hash(ctx->last_oid); + if (!algo) { + pr_notice("Unsupported digest algo: %u\n", ctx->last_oid); return -ENOPKG; } + + ctx->sinfo->sig->hash_algo =3D algo; + return 0; +} + +/* + * Note the parameters for the signature. + */ +int pkcs7_sig_note_algo_params(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx =3D context; + + ctx->algo_params =3D value - hdrlen; + ctx->algo_params_size =3D vlen + hdrlen; return 0; } =20 @@ -275,12 +262,16 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hd= rlen, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx =3D context; + struct public_key_signature *sig =3D ctx->sinfo->sig; + int err; =20 switch (ctx->last_oid) { case OID_rsaEncryption: - ctx->sinfo->sig->pkey_algo =3D "rsa"; - ctx->sinfo->sig->encoding =3D "pkcs1"; + sig->pkey_algo =3D "rsa"; + sig->encoding =3D "pkcs1"; break; + case OID_id_rsassa_pss: + goto rsassa_pss; case OID_id_ecdsa_with_sha1: case OID_id_ecdsa_with_sha224: case OID_id_ecdsa_with_sha256: @@ -289,34 +280,52 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hd= rlen, case OID_id_ecdsa_with_sha3_256: case OID_id_ecdsa_with_sha3_384: case OID_id_ecdsa_with_sha3_512: - ctx->sinfo->sig->pkey_algo =3D "ecdsa"; - ctx->sinfo->sig->encoding =3D "x962"; + sig->pkey_algo =3D "ecdsa"; + sig->encoding =3D "x962"; break; case OID_gost2012PKey256: case OID_gost2012PKey512: - ctx->sinfo->sig->pkey_algo =3D "ecrdsa"; - ctx->sinfo->sig->encoding =3D "raw"; + sig->pkey_algo =3D "ecrdsa"; + sig->encoding =3D "raw"; break; case OID_id_ml_dsa_44: - ctx->sinfo->sig->pkey_algo =3D "mldsa44"; - ctx->sinfo->sig->encoding =3D "raw"; - ctx->sinfo->sig->algo_does_hash =3D true; + sig->pkey_algo =3D "mldsa44"; + sig->encoding =3D "raw"; + sig->algo_does_hash =3D true; break; case OID_id_ml_dsa_65: - ctx->sinfo->sig->pkey_algo =3D "mldsa65"; - ctx->sinfo->sig->encoding =3D "raw"; - ctx->sinfo->sig->algo_does_hash =3D true; + sig->pkey_algo =3D "mldsa65"; + sig->encoding =3D "raw"; + sig->algo_does_hash =3D true; break; case OID_id_ml_dsa_87: - ctx->sinfo->sig->pkey_algo =3D "mldsa87"; - ctx->sinfo->sig->encoding =3D "raw"; - ctx->sinfo->sig->algo_does_hash =3D true; + sig->pkey_algo =3D "mldsa87"; + sig->encoding =3D "raw"; + sig->algo_does_hash =3D true; break; default: - printk("Unsupported pkey algo: %u\n", ctx->last_oid); + pr_notice("Unsupported pkey algo: %u\n", ctx->last_oid); return -ENOPKG; } + +out: + ctx->algo_params =3D NULL; + ctx->algo_params_size =3D 0; return 0; + +rsassa_pss: + if (!ctx->algo_params || !ctx->algo_params_size) { + pr_debug("RSASSA-PSS sig algo without parameters\n"); + return -EBADMSG; + } + + err =3D rsassa_parse_sig_params(sig, ctx->algo_params, ctx->algo_params_s= ize); + if (err < 0) + return err; + + sig->pkey_algo =3D "rsa"; + sig->encoding =3D "emsa-pss"; + goto out; } =20 /* diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/p= ublic_key.c index 61dc4f626620..13a5616becaa 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -100,6 +100,16 @@ software_key_determine_akcipher(const struct public_ke= y *pkey, } return n >=3D CRYPTO_MAX_ALG_NAME ? -EINVAL : 0; } + if (strcmp(encoding, "emsa-pss") =3D=3D 0) { + if (op !=3D kernel_pkey_sign && + op !=3D kernel_pkey_verify) + return -EINVAL; + *sig =3D true; + if (!hash_algo) + hash_algo =3D "none"; + n =3D snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "rsassa-pss"); + return n >=3D CRYPTO_MAX_ALG_NAME ? -EINVAL : 0; + } if (strcmp(encoding, "raw") !=3D 0) return -EINVAL; /* diff --git a/crypto/asymmetric_keys/rsassa_params.asn1 b/crypto/asymmetric_= keys/rsassa_params.asn1 new file mode 100644 index 000000000000..95a4e5f0dcd5 --- /dev/null +++ b/crypto/asymmetric_keys/rsassa_params.asn1 @@ -0,0 +1,25 @@ +-- SPDX-License-Identifier: BSD-3-Clause +-- +-- Copyright (C) 2009 IETF Trust and the persons identified as authors +-- of the code +-- +-- +-- https://datatracker.ietf.org/doc/html/rfc4055 Section 6. + +RSASSA-PSS-params ::=3D SEQUENCE { + hashAlgorithm [0] HashAlgorithm, + maskGenAlgorithm [1] MaskGenAlgorithm, + saltLength [2] INTEGER ({ rsassa_note_salt_length }), + trailerField [3] TrailerField OPTIONAL +} + +TrailerField ::=3D INTEGER ({ rsassa_note_trailer }) +-- { trailerFieldBC(1) } + +HashAlgorithm ::=3D AlgorithmIdentifier ({ rsassa_note_hash_algo }) +MaskGenAlgorithm ::=3D AlgorithmIdentifier ({ rsassa_note_maskgen_algo }) + +AlgorithmIdentifier ::=3D SEQUENCE { + algorithm OBJECT IDENTIFIER ({ rsassa_note_OID }), + parameters ANY OPTIONAL ({ rsassa_note_params }) +} diff --git a/crypto/asymmetric_keys/rsassa_parser.c b/crypto/asymmetric_key= s/rsassa_parser.c new file mode 100644 index 000000000000..8c598517f785 --- /dev/null +++ b/crypto/asymmetric_keys/rsassa_parser.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* RSASSA-PSS ASN.1 parameter parser + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "RSAPSS: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "x509_parser.h" +#include "rsassa_parser.h" +#include "rsassa_params.asn1.h" +#include "mgf1_params.asn1.h" + +struct rsassa_parse_context { + struct rsassa_parameters *rsassa; /* The parsed parameters */ + unsigned long data; /* Start of data */ + const void *params; /* Algo parameters */ + unsigned int params_len; /* Length of algo parameters */ + enum OID last_oid; /* Last OID encountered */ + enum OID mgf1_last_oid; /* Last OID encountered in MGF1 */ +}; + +/* + * Parse an RSASSA parameter block. + */ +struct rsassa_parameters *rsassa_params_parse(const void *data, size_t dat= alen) +{ + struct rsassa_parse_context ctx =3D {}; + struct rsassa_parameters *rsassa __free(kfree); + long ret; + + rsassa =3D kzalloc(sizeof(*rsassa), GFP_KERNEL); + if (!rsassa) + return ERR_PTR(-ENOMEM); + + ctx.rsassa =3D rsassa; + ctx.data =3D (unsigned long)data; + + /* Attempt to decode the parameters */ + ret =3D asn1_ber_decoder(&rsassa_params_decoder, &ctx, data, datalen); + if (ret < 0) { + pr_debug("RSASSA parse failed %ld\n", ret); + return ERR_PTR(ret); + } + + return no_free_ptr(rsassa); +} + +/* + * Note an OID when we find one for later processing when we know how + * to interpret it. + */ +int rsassa_note_OID(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx =3D context; + + ctx->last_oid =3D look_up_OID(value, vlen); + if (ctx->last_oid =3D=3D OID__NR) { + char buffer[56]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("Unknown OID: %s\n", buffer); + } + return 0; +} + +/* + * Parse trailerField. We only accept trailerFieldBC. +*/ +int rsassa_note_trailer(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + if (vlen !=3D 1 || *(u8 *)value !=3D 0x01) { + pr_debug("Unknown trailerField\n"); + return -EINVAL; + } + return 0; +} + +int rsassa_note_hash_algo(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx =3D context; + + ctx->rsassa->hash_algo =3D ctx->last_oid; + pr_debug("HASH-ALGO %u %u\n", ctx->rsassa->hash_algo, ctx->params_len); + ctx->params =3D NULL; + return 0; +} + +int rsassa_note_maskgen_algo(void *context, size_t hdrlen, unsigned char t= ag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx =3D context; + int ret; + + ctx->rsassa->maskgen_algo =3D ctx->last_oid; + pr_debug("MGF-ALGO %u %u\n", ctx->rsassa->maskgen_algo, ctx->params_len); + + switch (ctx->rsassa->maskgen_algo) { + case OID_id_mgf1: + if (!vlen) { + pr_debug("MGF1 missing parameters\n"); + return -EBADMSG; + } + + ret =3D asn1_ber_decoder(&mgf1_params_decoder, ctx, + ctx->params, ctx->params_len); + if (ret < 0) { + pr_debug("MGF1 parse failed %d\n", ret); + return ret; + } + ctx->rsassa->maskgen_hash =3D ctx->mgf1_last_oid; + break; + + default: + pr_debug("Unsupported MaskGenAlgorithm %d\n", ret); + return -ENOPKG; + } + + ctx->params =3D NULL; + return 0; +} + +int rsassa_note_salt_length(void *context, size_t hdrlen, unsigned char ta= g, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx =3D context; + u32 salt_len =3D 0; + + if (!vlen) { + pr_debug("Salt len bad integer\n"); + return -EBADMSG; + } + if (vlen > 4) { + pr_debug("Salt len too long %zu\n", vlen); + return -EBADMSG; + } + if (((u8 *)value)[0] & 0x80) { + pr_debug("Salt len negative\n"); + return -EBADMSG; + } + + for (size_t i =3D 0; i < vlen; i++) { + salt_len <<=3D 8; + salt_len |=3D ((u8 *)value)[i]; + } + + ctx->rsassa->salt_len =3D salt_len; + pr_debug("Salt-Len %u\n", salt_len); + return 0; +} + +/* + * Extract arbitrary parameters. + */ +int rsassa_note_params(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx =3D context; + + ctx->params =3D value - hdrlen; + ctx->params_len =3D vlen + hdrlen; + return 0; +} + +/* + * Note an OID when we find one for later processing when we know how to + * interpret it. + */ +int mgf1_note_OID(void *context, size_t hdrlen, unsigned char tag, + const void *value, size_t vlen) +{ + struct rsassa_parse_context *ctx =3D context; + + ctx->mgf1_last_oid =3D look_up_OID(value, vlen); + if (ctx->mgf1_last_oid =3D=3D OID__NR) { + char buffer[56]; + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_debug("Unknown MGF1 OID: %s\n", buffer); + } + return 0; +} + +/* + * Parse the signature parameter block and generate a suitable info string= from + * it. + */ +int rsassa_parse_sig_params(struct public_key_signature *sig, + const u8 *sig_params, unsigned int sig_params_size) +{ + struct rsassa_parameters *rsassa __free(rsassa_params_free) =3D NULL; + const char *mf, *mh; + + rsassa =3D rsassa_params_parse(sig_params, sig_params_size); + if (IS_ERR(rsassa)) + return PTR_ERR(rsassa); + + sig->hash_algo =3D oid_to_hash(rsassa->hash_algo); + if (!sig->hash_algo) { + pr_notice("Unsupported hash: %u\n", rsassa->hash_algo); + return -ENOPKG; + } + + switch (rsassa->maskgen_algo) { + case OID_id_mgf1: + mf =3D "mgf1"; + break; + default: + pr_notice("Unsupported maskgen algo: %u\n", rsassa->maskgen_algo); + return -ENOPKG; + } + + mh =3D oid_to_hash(rsassa->maskgen_hash); + if (!mh) { + pr_notice("Unsupported MGF1 hash: %u\n", rsassa->maskgen_hash); + return -ENOPKG; + } + + sig->info =3D kasprintf(GFP_KERNEL, "sighash=3D%s pss_mask=3D%s,%s pss_sa= lt=3D%u", + sig->hash_algo, mf, mh, rsassa->salt_len); + if (!sig->info) + return -ENOMEM; + pr_debug("Info string: %s\n", sig->info); + return 0; +} diff --git a/crypto/asymmetric_keys/rsassa_parser.h b/crypto/asymmetric_key= s/rsassa_parser.h new file mode 100644 index 000000000000..b80401a3de8f --- /dev/null +++ b/crypto/asymmetric_keys/rsassa_parser.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* RSASSA-PSS parameter parsing context + * + * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#include + +struct rsassa_parameters { + enum OID hash_algo; /* Hash algorithm identifier */ + enum OID maskgen_algo; /* Mask gen algorithm identifier */ + enum OID maskgen_hash; /* Mask gen hash algorithm identifier */ + u32 salt_len; +}; + +struct rsassa_parameters *rsassa_params_parse(const void *data, size_t dat= alen); +int rsassa_parse_sig_params(struct public_key_signature *sig, + const u8 *sig_params, unsigned int sig_params_size); + +static inline void rsassa_params_free(struct rsassa_parameters *params) +{ + kfree(params); +} +DEFINE_FREE(rsassa_params_free, struct rsassa_parameters*, rsassa_params_= free(_T)) diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509= .asn1 index feb9573cacce..453b72eba1fe 100644 --- a/crypto/asymmetric_keys/x509.asn1 +++ b/crypto/asymmetric_keys/x509.asn1 @@ -29,7 +29,7 @@ CertificateSerialNumber ::=3D INTEGER =20 AlgorithmIdentifier ::=3D SEQUENCE { algorithm OBJECT IDENTIFIER ({ x509_note_OID }), - parameters ANY OPTIONAL ({ x509_note_params }) + parameters ANY OPTIONAL ({ x509_note_algo_id_params }) } =20 Name ::=3D SEQUENCE OF RelativeDistinguishedName diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_= keys/x509_cert_parser.c index 5ab5b4e5f1b4..6c431bf181f2 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -15,28 +15,7 @@ #include "x509_parser.h" #include "x509.asn1.h" #include "x509_akid.asn1.h" - -struct x509_parse_context { - struct x509_certificate *cert; /* Certificate being constructed */ - unsigned long data; /* Start of data */ - const void *key; /* Key data */ - size_t key_size; /* Size of key data */ - const void *params; /* Key parameters */ - size_t params_size; /* Size of key parameters */ - 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 */ - u8 o_size; /* Size of organizationName (O) */ - u8 cn_size; /* Size of commonName (CN) */ - u8 email_size; /* Size of emailAddress */ - u16 o_offset; /* Offset of organizationName (O) */ - u16 cn_offset; /* Offset of commonName (CN) */ - u16 email_offset; /* Offset of emailAddress */ - unsigned raw_akid_size; - const void *raw_akid; /* Raw authorityKeyId in ASN.1 */ - const void *akid_raw_issuer; /* Raw directoryName in authorityKeyId */ - unsigned akid_raw_issuer_size; -}; +#include "rsassa_parser.h" =20 /* * Free an X.509 certificate @@ -104,15 +83,15 @@ struct x509_certificate *x509_cert_parse(const void *d= ata, size_t datalen) =20 cert->pub->keylen =3D ctx->key_size; =20 - cert->pub->params =3D kmemdup(ctx->params, ctx->params_size, GFP_KERNEL); + cert->pub->params =3D kmemdup(ctx->key_params, ctx->key_params_size, GFP_= KERNEL); if (!cert->pub->params) return ERR_PTR(-ENOMEM); =20 - cert->pub->paramlen =3D ctx->params_size; + cert->pub->paramlen =3D ctx->key_params_size; cert->pub->algo =3D ctx->key_algo; =20 /* Grab the signature bits */ - ret =3D x509_get_sig_params(cert); + ret =3D x509_get_sig_params(cert, ctx); if (ret < 0) return ERR_PTR(ret); =20 @@ -146,7 +125,7 @@ int x509_note_OID(void *context, size_t hdrlen, =20 ctx->last_oid =3D look_up_OID(value, vlen); if (ctx->last_oid =3D=3D OID__NR) { - char buffer[50]; + char buffer[56]; sprint_oid(value, vlen, buffer, sizeof(buffer)); pr_debug("Unknown OID: [%lu] %s\n", (unsigned long)value - ctx->data, buffer); @@ -179,6 +158,7 @@ int x509_note_sig_algo(void *context, size_t hdrlen, un= signed char tag, const void *value, size_t vlen) { struct x509_parse_context *ctx =3D context; + int err; =20 pr_debug("PubKey Algo: %u\n", ctx->last_oid); =20 @@ -210,6 +190,9 @@ int x509_note_sig_algo(void *context, size_t hdrlen, un= signed char tag, ctx->cert->sig->hash_algo =3D "sha1"; goto ecdsa; =20 + case OID_id_rsassa_pss: + goto rsassa_pss; + case OID_id_rsassa_pkcs1_v1_5_with_sha3_256: ctx->cert->sig->hash_algo =3D "sha3-256"; goto rsa_pkcs1; @@ -268,6 +251,24 @@ int x509_note_sig_algo(void *context, size_t hdrlen, u= nsigned char tag, goto ml_dsa; } =20 +rsassa_pss: + if (!ctx->algo_params || !ctx->algo_params_size) { + pr_debug("RSASSA-PSS sig algo without parameters\n"); + return -EBADMSG; + } + + err =3D rsassa_parse_sig_params(ctx->cert->sig, + ctx->algo_params, ctx->algo_params_size); + if (err < 0) + return err; + + ctx->cert->sig->pkey_algo =3D "rsa"; + ctx->cert->sig->encoding =3D "emsa-pss"; + ctx->sig_algo =3D ctx->last_oid; + ctx->algo_params =3D NULL; + ctx->algo_params_size =3D 0; + return 0; + rsa_pkcs1: ctx->cert->sig->pkey_algo =3D "rsa"; ctx->cert->sig->encoding =3D "pkcs1"; @@ -324,8 +325,8 @@ int x509_note_signature(void *context, size_t hdrlen, vlen--; } =20 - ctx->cert->raw_sig =3D value; - ctx->cert->raw_sig_size =3D vlen; + ctx->sig =3D value; + ctx->sig_size =3D vlen; return 0; } =20 @@ -479,23 +480,16 @@ int x509_note_subject(void *context, size_t hdrlen, } =20 /* - * Extract the parameters for the public key + * Extract the parameters for an AlgorithmIdentifier. */ -int x509_note_params(void *context, size_t hdrlen, - unsigned char tag, - const void *value, size_t vlen) +int x509_note_algo_id_params(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) { struct x509_parse_context *ctx =3D context; =20 - /* - * AlgorithmIdentifier is used three times in the x509, we should skip - * first and ignore third, using second one which is after subject and - * before subjectPublicKey. - */ - if (!ctx->cert->raw_subject || ctx->key) - return 0; - ctx->params =3D value - hdrlen; - ctx->params_size =3D vlen + hdrlen; + ctx->algo_params =3D value - hdrlen; + ctx->algo_params_size =3D vlen + hdrlen; return 0; } =20 @@ -514,12 +508,28 @@ int x509_extract_key_data(void *context, size_t hdrle= n, case OID_rsaEncryption: ctx->cert->pub->pkey_algo =3D "rsa"; break; + case OID_id_rsassa_pss: + /* Parameters are optional for the key itself. */ + if (ctx->algo_params_size) { + struct rsassa_parameters *params __free(rsassa_params_free) =3D NULL; + ctx->key_params =3D ctx->algo_params; + ctx->key_params_size =3D ctx->algo_params_size; + ctx->algo_params =3D NULL; + ctx->algo_params_size =3D 0; + + params =3D rsassa_params_parse(ctx->key_params, ctx->key_params_size); + if (IS_ERR(params)) + return PTR_ERR(params); + break; + } + ctx->cert->pub->pkey_algo =3D "rsa"; + break; case OID_gost2012PKey256: case OID_gost2012PKey512: ctx->cert->pub->pkey_algo =3D "ecrdsa"; break; case OID_id_ecPublicKey: - if (parse_OID(ctx->params, ctx->params_size, &oid) !=3D 0) + if (parse_OID(ctx->algo_params, ctx->algo_params_size, &oid) !=3D 0) return -EBADMSG; =20 switch (oid) { @@ -557,6 +567,8 @@ int x509_extract_key_data(void *context, size_t hdrlen, return -EBADMSG; ctx->key =3D value + 1; ctx->key_size =3D vlen - 1; + ctx->algo_params =3D NULL; + ctx->algo_params_size =3D 0; return 0; } =20 diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/= x509_parser.h index 0688c222806b..be2e1f6cb9f5 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -23,8 +23,6 @@ struct x509_certificate { time64_t valid_to; const void *tbs; /* Signed data */ unsigned tbs_size; /* Size of signed data */ - unsigned raw_sig_size; /* Size of signature */ - const void *raw_sig; /* Signature data */ const void *raw_serial; /* Raw serial number in ASN.1 */ unsigned raw_serial_size; unsigned raw_issuer_size; @@ -41,6 +39,34 @@ struct x509_certificate { bool blacklisted; }; =20 +struct x509_parse_context { + struct x509_certificate *cert; /* Certificate being constructed */ + unsigned long data; /* Start of data */ + const void *key; /* Key data */ + size_t key_size; /* Size of key data */ + const void *algo_params; /* AlgorithmIdentifier: parameters */ + size_t algo_params_size; /* AlgorithmIdentifier: parameters size */ + const void *key_params; /* Key parameters */ + size_t key_params_size; /* Size of key parameters */ + const void *sig_params; /* Signature parameters */ + unsigned int sig_params_size; /* Size of sig parameters */ + unsigned int sig_size; /* Size of signature */ + const void *sig; /* Signature data */ + 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 */ + u8 o_size; /* Size of organizationName (O) */ + u8 cn_size; /* Size of commonName (CN) */ + u8 email_size; /* Size of emailAddress */ + u16 o_offset; /* Offset of organizationName (O) */ + u16 cn_offset; /* Offset of commonName (CN) */ + u16 email_offset; /* Offset of emailAddress */ + unsigned raw_akid_size; + const void *raw_akid; /* Raw authorityKeyId in ASN.1 */ + const void *akid_raw_issuer; /* Raw directoryName in authorityKeyId */ + unsigned akid_raw_issuer_size; +}; + /* * x509_cert_parser.c */ @@ -55,5 +81,6 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, /* * x509_public_key.c */ -extern int x509_get_sig_params(struct x509_certificate *cert); +extern const char *oid_to_hash(enum OID oid); +extern int x509_get_sig_params(struct x509_certificate *cert, struct x509_= parse_context *parse); extern int x509_check_for_self_signed(struct x509_certificate *cert); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_k= eys/x509_public_key.c index 12e3341e806b..b2f8542accc4 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -17,11 +17,32 @@ #include "asymmetric_keys.h" #include "x509_parser.h" =20 +/* + * Translate OIDs to hash algorithm names. + */ +const char *oid_to_hash(enum OID oid) +{ + switch (oid) { + case OID_sha1: return "sha1"; + case OID_sha256: return "sha256"; + case OID_sha384: return "sha384"; + case OID_sha512: return "sha512"; + case OID_sha224: return "sha224"; + case OID_sm3: return "sm3"; + case OID_gost2012Digest256: return "streebog256"; + case OID_gost2012Digest512: return "streebog512"; + case OID_sha3_256: return "sha3-256"; + case OID_sha3_384: return "sha3-384"; + case OID_sha3_512: return "sha3-512"; + default: return NULL; + } +} + /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. */ -int x509_get_sig_params(struct x509_certificate *cert) +int x509_get_sig_params(struct x509_certificate *cert, struct x509_parse_c= ontext *parse) { struct public_key_signature *sig =3D cert->sig; struct crypto_shash *tfm; @@ -31,11 +52,11 @@ int x509_get_sig_params(struct x509_certificate *cert) =20 pr_devel("=3D=3D>%s()\n", __func__); =20 - sig->s =3D kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL); + sig->s =3D kmemdup(parse->sig, parse->sig_size, GFP_KERNEL); if (!sig->s) return -ENOMEM; =20 - sig->s_size =3D cert->raw_sig_size; + sig->s_size =3D parse->sig_size; =20 /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. @@ -43,6 +64,7 @@ int x509_get_sig_params(struct x509_certificate *cert) tfm =3D crypto_alloc_shash(sig->hash_algo, 0, 0); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) =3D=3D -ENOENT) { + pr_debug("Unsupported hash %s\n", sig->hash_algo); cert->unsupported_sig =3D true; return 0; } diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 30821a6a4f72..d546ea7999b9 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -31,6 +31,8 @@ enum OID { /* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)= } */ OID_rsaEncryption, /* 1.2.840.113549.1.1.1 */ OID_sha1WithRSAEncryption, /* 1.2.840.113549.1.1.5 */ + OID_id_mgf1, /* 1.2.840.113549.1.1.8 */ + OID_id_rsassa_pss, /* 1.2.840.113549.1.1.10 */ OID_sha256WithRSAEncryption, /* 1.2.840.113549.1.1.11 */ OID_sha384WithRSAEncryption, /* 1.2.840.113549.1.1.12 */ OID_sha512WithRSAEncryption, /* 1.2.840.113549.1.1.13 */ From nobody Sat Feb 7 15:15:19 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C440633372E for ; Mon, 5 Jan 2026 15:22:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626581; cv=none; b=AxK/w+hEfnlIWC2bUcolQfYvbCqKBIt39WR7hfi4h2d0Wyx/7BJ9/B2Gk1ygpvDRvqA5MqyCxBBE5XoWiw21TeNWpJzF7P4IffWYGTPue+dj0x6WF5PUP92ilvkQcE7uATTJIyvmO51mmUloSyjOX1U+3b78+OmHsnXspoHk04I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1767626581; c=relaxed/simple; bh=dbX9nHJ5REzRp8QaFOHK2ee2n38HwBnQPeTWNbT3JoQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=vFm9+5yiLCkjs0JC2TBftLWXc163yBnJ9j9kkfZNIw4vSNZgn6Ae+yYNUZ1VIihXTDamfw/IOw7S6cyxb+YGHrjstURWA3vMiFg7Wqf+K9goiklS9xsfc4MNA3seecoyQZ0xD9yQ9PGm4A+AnFzpaR571B3cmzadohX1o0LrxbQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Tz3Dvoa+; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Tz3Dvoa+" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1767626576; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=e9kgzsLwkkfNxmetAJNcral3H3dTIGoaCL0v9z96wpg=; b=Tz3Dvoa+Dx5dgHNs+uj5TT0wbTps1tX7rRrExt/81oUCWdzhmHohRhWcqaguJ3gZlfjSnf w259p1WSFQQ/M3YddNX75dImP9gldss+Deg0U1d8hVRU2yvkCUpA3hpwA9fHAFgqIcNmtt b8EVjh/gg1stBrdln0SyjucVMkTIx8w= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-26-KQWT_SDsNDmINssT8d_tdA-1; Mon, 05 Jan 2026 10:22:50 -0500 X-MC-Unique: KQWT_SDsNDmINssT8d_tdA-1 X-Mimecast-MFC-AGG-ID: KQWT_SDsNDmINssT8d_tdA_1767626568 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 90468195609D; Mon, 5 Jan 2026 15:22:48 +0000 (UTC) Received: from warthog.procyon.org.uk.com (unknown [10.42.28.4]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id AE50419560AB; Mon, 5 Jan 2026 15:22:44 +0000 (UTC) From: David Howells To: Lukas Wunner , Ignat Korchagin Cc: David Howells , Jarkko Sakkinen , Herbert Xu , Eric Biggers , Luis Chamberlain , Petr Pavlu , Daniel Gomez , Sami Tolvanen , "Jason A . Donenfeld" , Ard Biesheuvel , Stephan Mueller , linux-crypto@vger.kernel.org, keyrings@vger.kernel.org, linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v11 8/8] modsign: Enable RSASSA-PSS module signing Date: Mon, 5 Jan 2026 15:21:33 +0000 Message-ID: <20260105152145.1801972-9-dhowells@redhat.com> In-Reply-To: <20260105152145.1801972-1-dhowells@redhat.com> References: <20260105152145.1801972-1-dhowells@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Add support for RSASSA-PSS signatures (RFC8017) for use with module signing and other public key cryptography done by the kernel. Note that only signature verification is supported by the kernel. Note further that this alters some of the same code as the MLDSA support, so that needs to be applied first to avoid conflicts. Signed-off-by: David Howells cc: Lukas Wunner cc: Ignat Korchagin cc: Herbert Xu cc: keyrings@vger.kernel.org cc: linux-crypto@vger.kernel.org Reviewed-by: Ignat Korchagin --- certs/Kconfig | 6 ++++++ certs/Makefile | 1 + scripts/sign-file.c | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/certs/Kconfig b/certs/Kconfig index 94b086684d07..beb8991ad761 100644 --- a/certs/Kconfig +++ b/certs/Kconfig @@ -27,6 +27,12 @@ config MODULE_SIG_KEY_TYPE_RSA help Use an RSA key for module signing. =20 +config MODULE_SIG_KEY_TYPE_RSASSA_PSS + bool "RSASSA-PSS" + select CRYPTO_RSA + help + Use an RSASSA-PSS key for module signing. + config MODULE_SIG_KEY_TYPE_ECDSA bool "ECDSA" select CRYPTO_ECDSA diff --git a/certs/Makefile b/certs/Makefile index 3ee1960f9f4a..3b5a3a303f4c 100644 --- a/certs/Makefile +++ b/certs/Makefile @@ -42,6 +42,7 @@ targets +=3D x509_certificate_list # boolean option and we unfortunately can't make it depend on !RANDCONFIG. ifeq ($(CONFIG_MODULE_SIG_KEY),certs/signing_key.pem) =20 +keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_RSASSA_PSS) :=3D -newkey rsassa-pss keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_ECDSA) :=3D -newkey ec -pkeyopt ec_pa= ramgen_curve:secp384r1 keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_44) :=3D -newkey ml-dsa-44 keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_65) :=3D -newkey ml-dsa-65 diff --git a/scripts/sign-file.c b/scripts/sign-file.c index b726581075f9..ca605095194e 100644 --- a/scripts/sign-file.c +++ b/scripts/sign-file.c @@ -233,6 +233,7 @@ int main(int argc, char **argv) EVP_PKEY *private_key; #ifndef USE_PKCS7 CMS_ContentInfo *cms =3D NULL; + CMS_SignerInfo *signer; unsigned int use_keyid =3D 0; #else PKCS7 *pkcs7 =3D NULL; @@ -329,13 +330,47 @@ int main(int argc, char **argv) !EVP_PKEY_is_a(private_key, "ML-DSA-65") && !EVP_PKEY_is_a(private_key, "ML-DSA-87")) flags |=3D use_signed_attrs; + if (EVP_PKEY_is_a(private_key, "RSASSA-PSS")) + flags |=3D CMS_KEY_PARAM; + if (EVP_PKEY_is_a(private_key, "RSASSA-PSS")) { + EVP_PKEY_CTX *pkctx; + char mdname[1024] =3D {}; + + pkctx =3D EVP_PKEY_CTX_new(private_key, NULL); + + ERR(!EVP_PKEY_sign_init(pkctx), "EVP_PKEY_sign_init"); + ERR(!EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING), + "EVP_PKEY_CTX_set_rsa_padding"); + ERR(!EVP_PKEY_CTX_set_rsa_mgf1_md_name(pkctx, hash_algo, NULL), + "EVP_PKEY_CTX_set_rsa_mgf1_md_name"); + + ERR(!EVP_PKEY_CTX_get_rsa_mgf1_md_name(pkctx, mdname, sizeof(mdname)), + "EVP_PKEY_CTX_get_rsa_mgf1_md_name"); + printf("RSASSA-PSS %s\n", mdname); + } =20 /* Load the signature message from the digest buffer. */ cms =3D CMS_sign(NULL, NULL, NULL, NULL, flags); ERR(!cms, "CMS_sign"); =20 - ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, flags), - "CMS_add1_signer"); + signer =3D CMS_add1_signer(cms, x509, private_key, digest_algo, flags); + ERR(!signer, "CMS_add1_signer"); + + if (EVP_PKEY_is_a(private_key, "RSASSA-PSS")) { + EVP_PKEY_CTX *pkctx; + char mdname[1024] =3D {}; + + pkctx =3D CMS_SignerInfo_get0_pkey_ctx(signer); + ERR(!EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING), + "EVP_PKEY_CTX_set_rsa_padding"); + ERR(!EVP_PKEY_CTX_set_rsa_mgf1_md_name(pkctx, hash_algo, NULL), + "EVP_PKEY_CTX_set_rsa_mgf1_md_name"); + + ERR(!EVP_PKEY_CTX_get_rsa_mgf1_md_name(pkctx, mdname, sizeof(mdname)), + "EVP_PKEY_CTX_get_rsa_mgf1_md_name"); + printf("RSASSA-PSS %s\n", mdname); + } + ERR(CMS_final(cms, bm, NULL, flags) !=3D 1, "CMS_final");