Skip to content

Commit b63c907

Browse files
committed
Merge tag 'keys-next-20260206' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
Pull keys update from David Howells: "This adds support for ML-DSA signatures in X.509 certificates and PKCS#7/CMS messages, thereby allowing this algorithm to be used for signing modules, kexec'able binaries, wifi regulatory data, etc.. This requires OpenSSL-3.5 at a minimum and preferably OpenSSL-4 (so that it can avoid the use of CMS signedAttrs - but that version is not cut yet). certs/Kconfig does a check to hide the signing options if OpenSSL does not list the algorithm as being available" * tag 'keys-next-20260206' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: pkcs7: Change a pr_warn() to pr_warn_once() pkcs7: Allow authenticatedAttributes for ML-DSA modsign: Enable ML-DSA module signing pkcs7, x509: Add ML-DSA support pkcs7: Allow the signing algo to do whatever digestion it wants itself pkcs7, x509: Rename ->digest to ->m x509: Separately calculate sha256 for blacklist crypto: Add ML-DSA crypto_sig support
2 parents 958f7fb + 965e9a2 commit b63c907

20 files changed

Lines changed: 473 additions & 71 deletions

File tree

Documentation/admin-guide/module-signing.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ trusted userspace bits.
2828

2929
This facility uses X.509 ITU-T standard certificates to encode the public keys
3030
involved. The signatures are not themselves encoded in any industrial standard
31-
type. The built-in facility currently only supports the RSA & NIST P-384 ECDSA
32-
public key signing standard (though it is pluggable and permits others to be
33-
used). The possible hash algorithms that can be used are SHA-2 and SHA-3 of
34-
sizes 256, 384, and 512 (the algorithm is selected by data in the signature).
31+
type. The built-in facility currently only supports the RSA, NIST P-384 ECDSA
32+
and NIST FIPS-204 ML-DSA public key signing standards (though it is pluggable
33+
and permits others to be used). For RSA and ECDSA, the possible hash
34+
algorithms that can be used are SHA-2 and SHA-3 of sizes 256, 384, and 512 (the
35+
algorithm is selected by data in the signature); ML-DSA does its own hashing,
36+
but is allowed to be used with a SHA512 hash for signed attributes.
3537

3638

3739
==========================
@@ -146,9 +148,9 @@ into vmlinux) using parameters in the::
146148

147149
file (which is also generated if it does not already exist).
148150

149-
One can select between RSA (``MODULE_SIG_KEY_TYPE_RSA``) and ECDSA
150-
(``MODULE_SIG_KEY_TYPE_ECDSA``) to generate either RSA 4k or NIST
151-
P-384 keypair.
151+
One can select between RSA (``MODULE_SIG_KEY_TYPE_RSA``), ECDSA
152+
(``MODULE_SIG_KEY_TYPE_ECDSA``) and ML-DSA (``MODULE_SIG_KEY_TYPE_MLDSA_*``) to
153+
generate an RSA 4k, a NIST P-384 keypair or an ML-DSA 44, 65 or 87 keypair.
152154

153155
It is strongly recommended that you provide your own x509.genkey file.
154156

certs/Kconfig

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,39 @@ config MODULE_SIG_KEY_TYPE_ECDSA
3939
Note: Remove all ECDSA signing keys, e.g. certs/signing_key.pem,
4040
when falling back to building Linux 5.14 and older kernels.
4141

42+
config MODULE_SIG_KEY_TYPE_MLDSA_44
43+
bool "ML-DSA-44"
44+
select CRYPTO_MLDSA
45+
depends on OPENSSL_SUPPORTS_ML_DSA
46+
help
47+
Use an ML-DSA-44 key (NIST FIPS 204) for module signing. ML-DSA
48+
support requires OpenSSL-3.5 minimum; preferably OpenSSL-4+. With
49+
the latter, the entire module body will be signed; with the former,
50+
signedAttrs will be used as it lacks support for CMS_NOATTR with
51+
ML-DSA.
52+
53+
config MODULE_SIG_KEY_TYPE_MLDSA_65
54+
bool "ML-DSA-65"
55+
select CRYPTO_MLDSA
56+
depends on OPENSSL_SUPPORTS_ML_DSA
57+
help
58+
Use an ML-DSA-65 key (NIST FIPS 204) for module signing. ML-DSA
59+
support requires OpenSSL-3.5 minimum; preferably OpenSSL-4+. With
60+
the latter, the entire module body will be signed; with the former,
61+
signedAttrs will be used as it lacks support for CMS_NOATTR with
62+
ML-DSA.
63+
64+
config MODULE_SIG_KEY_TYPE_MLDSA_87
65+
bool "ML-DSA-87"
66+
select CRYPTO_MLDSA
67+
depends on OPENSSL_SUPPORTS_ML_DSA
68+
help
69+
Use an ML-DSA-87 key (NIST FIPS 204) for module signing. ML-DSA
70+
support requires OpenSSL-3.5 minimum; preferably OpenSSL-4+. With
71+
the latter, the entire module body will be signed; with the former,
72+
signedAttrs will be used as it lacks support for CMS_NOATTR with
73+
ML-DSA.
74+
4275
endchoice
4376

4477
config SYSTEM_TRUSTED_KEYRING
@@ -154,4 +187,11 @@ config SYSTEM_BLACKLIST_AUTH_UPDATE
154187
keyring. The PKCS#7 signature of the description is set in the key
155188
payload. Blacklist keys cannot be removed.
156189

190+
config OPENSSL_SUPPORTS_ML_DSA
191+
def_bool $(success, openssl list -key-managers | grep -q ML-DSA-87)
192+
help
193+
Support for ML-DSA-44/65/87 was added in openssl-3.5, so as long
194+
as older versions are supported, the key types may only be
195+
set after testing the installed binary for support.
196+
157197
endmenu

certs/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ targets += x509_certificate_list
4343
ifeq ($(CONFIG_MODULE_SIG_KEY),certs/signing_key.pem)
4444

4545
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_ECDSA) := -newkey ec -pkeyopt ec_paramgen_curve:secp384r1
46+
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_44) := -newkey ml-dsa-44
47+
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_65) := -newkey ml-dsa-65
48+
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_87) := -newkey ml-dsa-87
4649

4750
quiet_cmd_gen_key = GENKEY $@
4851
cmd_gen_key = openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \

crypto/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,15 @@ config CRYPTO_ECRDSA
344344
One of the Russian cryptographic standard algorithms (called GOST
345345
algorithms). Only signature verification is implemented.
346346

347+
config CRYPTO_MLDSA
348+
tristate "ML-DSA (Module-Lattice-Based Digital Signature Algorithm)"
349+
select CRYPTO_SIG
350+
select CRYPTO_LIB_MLDSA
351+
help
352+
ML-DSA (Module-Lattice-Based Digital Signature Algorithm) (FIPS-204).
353+
354+
Only signature verification is implemented.
355+
347356
endmenu
348357

349358
menu "Block ciphers"

crypto/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ ecdsa_generic-y += ecdsa-p1363.o
6060
ecdsa_generic-y += ecdsasignature.asn1.o
6161
obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o
6262

63+
obj-$(CONFIG_CRYPTO_MLDSA) += mldsa.o
64+
6365
crypto_acompress-y := acompress.o
6466
crypto_acompress-y += scompress.o
6567
obj-$(CONFIG_CRYPTO_ACOMP2) += crypto_acompress.o

crypto/asymmetric_keys/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ config PKCS7_MESSAGE_PARSER
5353
This option provides support for parsing PKCS#7 format messages for
5454
signature data and provides the ability to verify the signature.
5555

56+
config PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA
57+
bool "Waive rejection of authenticatedAttributes for ML-DSA"
58+
depends on PKCS7_MESSAGE_PARSER
59+
depends on CRYPTO_MLDSA
60+
help
61+
Due to use of CMS_NOATTR with ML-DSA not being supported in
62+
OpenSSL < 4.0 (and thus any released version), enabling this
63+
allows authenticatedAttributes to be used with ML-DSA for
64+
module signing. Use of authenticatedAttributes in this
65+
context is normally rejected.
66+
5667
config PKCS7_TEST_KEY
5768
tristate "PKCS#7 testing key type"
5869
depends on SYSTEM_DATA_VERIFICATION

crypto/asymmetric_keys/asymmetric_type.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,10 +593,10 @@ static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
593593
{
594594
struct public_key_signature sig = {
595595
.s_size = params->in2_len,
596-
.digest_size = params->in_len,
596+
.m_size = params->in_len,
597597
.encoding = params->encoding,
598598
.hash_algo = params->hash_algo,
599-
.digest = (void *)in,
599+
.m = (void *)in,
600600
.s = (void *)in2,
601601
};
602602

crypto/asymmetric_keys/pkcs7_parser.c

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,29 @@ static int pkcs7_check_authattrs(struct pkcs7_message *msg)
9292
if (!sinfo)
9393
goto inconsistent;
9494

95+
#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA
96+
msg->authattrs_rej_waivable = true;
97+
#endif
98+
9599
if (sinfo->authattrs) {
96100
want = true;
97101
msg->have_authattrs = true;
102+
#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA
103+
if (strncmp(sinfo->sig->pkey_algo, "mldsa", 5) != 0)
104+
msg->authattrs_rej_waivable = false;
105+
#endif
106+
} else if (sinfo->sig->algo_takes_data) {
107+
sinfo->sig->hash_algo = "none";
98108
}
99109

100-
for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
110+
for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) {
101111
if (!!sinfo->authattrs != want)
102112
goto inconsistent;
113+
114+
if (!sinfo->authattrs &&
115+
sinfo->sig->algo_takes_data)
116+
sinfo->sig->hash_algo = "none";
117+
}
103118
return 0;
104119

105120
inconsistent:
@@ -297,6 +312,21 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
297312
ctx->sinfo->sig->pkey_algo = "ecrdsa";
298313
ctx->sinfo->sig->encoding = "raw";
299314
break;
315+
case OID_id_ml_dsa_44:
316+
ctx->sinfo->sig->pkey_algo = "mldsa44";
317+
ctx->sinfo->sig->encoding = "raw";
318+
ctx->sinfo->sig->algo_takes_data = true;
319+
break;
320+
case OID_id_ml_dsa_65:
321+
ctx->sinfo->sig->pkey_algo = "mldsa65";
322+
ctx->sinfo->sig->encoding = "raw";
323+
ctx->sinfo->sig->algo_takes_data = true;
324+
break;
325+
case OID_id_ml_dsa_87:
326+
ctx->sinfo->sig->pkey_algo = "mldsa87";
327+
ctx->sinfo->sig->encoding = "raw";
328+
ctx->sinfo->sig->algo_takes_data = true;
329+
break;
300330
default:
301331
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
302332
return -ENOPKG;
@@ -599,8 +629,8 @@ int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
599629
}
600630

601631
/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
602-
sinfo->authattrs = value - (hdrlen - 1);
603-
sinfo->authattrs_len = vlen + (hdrlen - 1);
632+
sinfo->authattrs = value - hdrlen;
633+
sinfo->authattrs_len = vlen + hdrlen;
604634
return 0;
605635
}
606636

crypto/asymmetric_keys/pkcs7_parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ struct pkcs7_message {
5555
struct pkcs7_signed_info *signed_infos;
5656
u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */
5757
bool have_authattrs; /* T if have authattrs */
58+
#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA
59+
bool authattrs_rej_waivable; /* T if authatts rejection can be waived */
60+
#endif
5861

5962
/* Content Data (or NULL) */
6063
enum OID data_type; /* Type of Data */

crypto/asymmetric_keys/pkcs7_verify.c

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,18 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
3030

3131
kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo);
3232

33+
if (!sinfo->authattrs && sig->algo_takes_data) {
34+
/* There's no intermediate digest and the signature algo
35+
* doesn't want the data prehashing.
36+
*/
37+
sig->m = (void *)pkcs7->data;
38+
sig->m_size = pkcs7->data_len;
39+
sig->m_free = false;
40+
return 0;
41+
}
42+
3343
/* The digest was calculated already. */
34-
if (sig->digest)
44+
if (sig->m)
3545
return 0;
3646

3747
if (!sinfo->sig->hash_algo)
@@ -45,12 +55,13 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
4555
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
4656

4757
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
48-
sig->digest_size = crypto_shash_digestsize(tfm);
58+
sig->m_size = crypto_shash_digestsize(tfm);
4959

5060
ret = -ENOMEM;
51-
sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
52-
if (!sig->digest)
61+
sig->m = kmalloc(umax(sinfo->authattrs_len, sig->m_size), GFP_KERNEL);
62+
if (!sig->m)
5363
goto error_no_desc;
64+
sig->m_free = true;
5465

5566
desc = kzalloc(desc_size, GFP_KERNEL);
5667
if (!desc)
@@ -59,33 +70,30 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
5970
desc->tfm = tfm;
6071

6172
/* Digest the message [RFC2315 9.3] */
62-
ret = crypto_shash_digest(desc, pkcs7->data, pkcs7->data_len,
63-
sig->digest);
73+
ret = crypto_shash_digest(desc, pkcs7->data, pkcs7->data_len, sig->m);
6474
if (ret < 0)
6575
goto error;
66-
pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest);
76+
pr_devel("MsgDigest = [%*ph]\n", 8, sig->m);
6777

6878
/* However, if there are authenticated attributes, there must be a
6979
* message digest attribute amongst them which corresponds to the
7080
* digest we just calculated.
7181
*/
7282
if (sinfo->authattrs) {
73-
u8 tag;
74-
7583
if (!sinfo->msgdigest) {
7684
pr_warn("Sig %u: No messageDigest\n", sinfo->index);
7785
ret = -EKEYREJECTED;
7886
goto error;
7987
}
8088

81-
if (sinfo->msgdigest_len != sig->digest_size) {
89+
if (sinfo->msgdigest_len != sig->m_size) {
8290
pr_warn("Sig %u: Invalid digest size (%u)\n",
8391
sinfo->index, sinfo->msgdigest_len);
8492
ret = -EBADMSG;
8593
goto error;
8694
}
8795

88-
if (memcmp(sig->digest, sinfo->msgdigest,
96+
if (memcmp(sig->m, sinfo->msgdigest,
8997
sinfo->msgdigest_len) != 0) {
9098
pr_warn("Sig %u: Message digest doesn't match\n",
9199
sinfo->index);
@@ -97,21 +105,26 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7,
97105
* as the contents of the digest instead. Note that we need to
98106
* convert the attributes from a CONT.0 into a SET before we
99107
* hash it.
108+
*
109+
* However, for certain algorithms, such as ML-DSA, the digest
110+
* is integrated into the signing algorithm. In such a case,
111+
* we copy the authattrs, modifying the tag type, and set that
112+
* as the digest.
100113
*/
101-
memset(sig->digest, 0, sig->digest_size);
102-
103-
ret = crypto_shash_init(desc);
104-
if (ret < 0)
105-
goto error;
106-
tag = ASN1_CONS_BIT | ASN1_SET;
107-
ret = crypto_shash_update(desc, &tag, 1);
108-
if (ret < 0)
109-
goto error;
110-
ret = crypto_shash_finup(desc, sinfo->authattrs,
111-
sinfo->authattrs_len, sig->digest);
112-
if (ret < 0)
113-
goto error;
114-
pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
114+
memcpy(sig->m, sinfo->authattrs, sinfo->authattrs_len);
115+
sig->m[0] = ASN1_CONS_BIT | ASN1_SET;
116+
117+
if (sig->algo_takes_data) {
118+
sig->m_size = sinfo->authattrs_len;
119+
ret = 0;
120+
} else {
121+
ret = crypto_shash_digest(desc, sig->m,
122+
sinfo->authattrs_len,
123+
sig->m);
124+
if (ret < 0)
125+
goto error;
126+
}
127+
pr_devel("AADigest = [%*ph]\n", 8, sig->m);
115128
}
116129

117130
error:
@@ -137,9 +150,14 @@ int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf, u32 *len,
137150
ret = pkcs7_digest(pkcs7, sinfo);
138151
if (ret)
139152
return ret;
153+
if (!sinfo->sig->m_free) {
154+
pr_notice_once("%s: No digest available\n", __func__);
155+
return -EINVAL; /* TODO: MLDSA doesn't necessarily calculate an
156+
* intermediate digest. */
157+
}
140158

141-
*buf = sinfo->sig->digest;
142-
*len = sinfo->sig->digest_size;
159+
*buf = sinfo->sig->m;
160+
*len = sinfo->sig->m_size;
143161

144162
i = match_string(hash_algo_name, HASH_ALGO__LAST,
145163
sinfo->sig->hash_algo);
@@ -407,6 +425,12 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
407425
return -EKEYREJECTED;
408426
}
409427
if (pkcs7->have_authattrs) {
428+
#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA
429+
if (pkcs7->authattrs_rej_waivable) {
430+
pr_warn_once("Waived invalid module sig (has authattrs)\n");
431+
break;
432+
}
433+
#endif
410434
pr_warn("Invalid module sig (has authattrs)\n");
411435
return -EKEYREJECTED;
412436
}

0 commit comments

Comments
 (0)