diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-10 09:32:30 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-10 09:32:30 -0800 |
| commit | b63c90720348578631cda74285958c3ad3169ce9 (patch) | |
| tree | e79503d20dda1e113f6b369e46b4235a9b289694 /crypto | |
| parent | 958f7fb68c6be4e2d9dcb5bf31bfe746f6744aa3 (diff) | |
| parent | 965e9a2cf23b066d8bdeb690dff9cd7089c5f667 (diff) | |
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
Diffstat (limited to 'crypto')
| -rw-r--r-- | crypto/Kconfig | 9 | ||||
| -rw-r--r-- | crypto/Makefile | 2 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/Kconfig | 11 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 4 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.c | 36 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.h | 3 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/pkcs7_verify.c | 78 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/public_key.c | 13 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/signature.c | 3 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 27 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 2 | ||||
| -rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 42 | ||||
| -rw-r--r-- | crypto/mldsa.c | 201 |
13 files changed, 380 insertions, 51 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig index db6b0c2fb50e..e2b4106ac961 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -344,6 +344,15 @@ config CRYPTO_ECRDSA One of the Russian cryptographic standard algorithms (called GOST algorithms). Only signature verification is implemented. +config CRYPTO_MLDSA + tristate "ML-DSA (Module-Lattice-Based Digital Signature Algorithm)" + select CRYPTO_SIG + select CRYPTO_LIB_MLDSA + help + ML-DSA (Module-Lattice-Based Digital Signature Algorithm) (FIPS-204). + + Only signature verification is implemented. + endmenu menu "Block ciphers" diff --git a/crypto/Makefile b/crypto/Makefile index 65a2c3478814..5179662e2ed1 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -60,6 +60,8 @@ ecdsa_generic-y += ecdsa-p1363.o ecdsa_generic-y += ecdsasignature.asn1.o obj-$(CONFIG_CRYPTO_ECDSA) += ecdsa_generic.o +obj-$(CONFIG_CRYPTO_MLDSA) += mldsa.o + crypto_acompress-y := acompress.o crypto_acompress-y += scompress.o obj-$(CONFIG_CRYPTO_ACOMP2) += crypto_acompress.o diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index e1345b8f39f1..1dae2232fe9a 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -53,6 +53,17 @@ config PKCS7_MESSAGE_PARSER This option provides support for parsing PKCS#7 format messages for signature data and provides the ability to verify the signature. +config PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA + bool "Waive rejection of authenticatedAttributes for ML-DSA" + depends on PKCS7_MESSAGE_PARSER + depends on CRYPTO_MLDSA + help + Due to use of CMS_NOATTR with ML-DSA not being supported in + OpenSSL < 4.0 (and thus any released version), enabling this + allows authenticatedAttributes to be used with ML-DSA for + module signing. Use of authenticatedAttributes in this + context is normally rejected. + config PKCS7_TEST_KEY tristate "PKCS#7 testing key type" depends on SYSTEM_DATA_VERIFICATION diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 348966ea2175..2326743310b1 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -593,10 +593,10 @@ static int asymmetric_key_verify_signature(struct kernel_pkey_params *params, { struct public_key_signature sig = { .s_size = params->in2_len, - .digest_size = params->in_len, + .m_size = params->in_len, .encoding = params->encoding, .hash_algo = params->hash_algo, - .digest = (void *)in, + .m = (void *)in, .s = (void *)in2, }; diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 423d13c47545..db1c90ca6fc1 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -92,14 +92,29 @@ static int pkcs7_check_authattrs(struct pkcs7_message *msg) if (!sinfo) goto inconsistent; +#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA + msg->authattrs_rej_waivable = true; +#endif + if (sinfo->authattrs) { want = true; msg->have_authattrs = true; +#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA + if (strncmp(sinfo->sig->pkey_algo, "mldsa", 5) != 0) + msg->authattrs_rej_waivable = false; +#endif + } else if (sinfo->sig->algo_takes_data) { + sinfo->sig->hash_algo = "none"; } - for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) + for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) { if (!!sinfo->authattrs != want) goto inconsistent; + + if (!sinfo->authattrs && + sinfo->sig->algo_takes_data) + sinfo->sig->hash_algo = "none"; + } return 0; inconsistent: @@ -297,6 +312,21 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, ctx->sinfo->sig->pkey_algo = "ecrdsa"; ctx->sinfo->sig->encoding = "raw"; break; + case OID_id_ml_dsa_44: + ctx->sinfo->sig->pkey_algo = "mldsa44"; + ctx->sinfo->sig->encoding = "raw"; + ctx->sinfo->sig->algo_takes_data = true; + break; + case OID_id_ml_dsa_65: + ctx->sinfo->sig->pkey_algo = "mldsa65"; + ctx->sinfo->sig->encoding = "raw"; + ctx->sinfo->sig->algo_takes_data = true; + break; + case OID_id_ml_dsa_87: + ctx->sinfo->sig->pkey_algo = "mldsa87"; + ctx->sinfo->sig->encoding = "raw"; + ctx->sinfo->sig->algo_takes_data = true; + break; default: printk("Unsupported pkey algo: %u\n", ctx->last_oid); return -ENOPKG; @@ -599,8 +629,8 @@ int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, } /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ - sinfo->authattrs = value - (hdrlen - 1); - sinfo->authattrs_len = vlen + (hdrlen - 1); + sinfo->authattrs = value - hdrlen; + sinfo->authattrs_len = vlen + hdrlen; return 0; } diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index e17f7ce4fb43..6ef9f335bb17 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -55,6 +55,9 @@ struct pkcs7_message { struct pkcs7_signed_info *signed_infos; u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */ bool have_authattrs; /* T if have authattrs */ +#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA + bool authattrs_rej_waivable; /* T if authatts rejection can be waived */ +#endif /* Content Data (or NULL) */ enum OID data_type; /* Type of Data */ diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 6d6475e3a9bf..474e2c1ae21b 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -30,8 +30,18 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo); + if (!sinfo->authattrs && sig->algo_takes_data) { + /* There's no intermediate digest and the signature algo + * doesn't want the data prehashing. + */ + sig->m = (void *)pkcs7->data; + sig->m_size = pkcs7->data_len; + sig->m_free = false; + return 0; + } + /* The digest was calculated already. */ - if (sig->digest) + if (sig->m) return 0; if (!sinfo->sig->hash_algo) @@ -45,12 +55,13 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - sig->digest_size = crypto_shash_digestsize(tfm); + sig->m_size = crypto_shash_digestsize(tfm); ret = -ENOMEM; - sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); - if (!sig->digest) + sig->m = kmalloc(umax(sinfo->authattrs_len, sig->m_size), GFP_KERNEL); + if (!sig->m) goto error_no_desc; + sig->m_free = true; desc = kzalloc(desc_size, GFP_KERNEL); if (!desc) @@ -59,33 +70,30 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, desc->tfm = tfm; /* Digest the message [RFC2315 9.3] */ - ret = crypto_shash_digest(desc, pkcs7->data, pkcs7->data_len, - sig->digest); + ret = crypto_shash_digest(desc, pkcs7->data, pkcs7->data_len, sig->m); if (ret < 0) goto error; - pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest); + pr_devel("MsgDigest = [%*ph]\n", 8, sig->m); /* However, if there are authenticated attributes, there must be a * message digest attribute amongst them which corresponds to the * digest we just calculated. */ if (sinfo->authattrs) { - u8 tag; - if (!sinfo->msgdigest) { pr_warn("Sig %u: No messageDigest\n", sinfo->index); ret = -EKEYREJECTED; goto error; } - if (sinfo->msgdigest_len != sig->digest_size) { + if (sinfo->msgdigest_len != sig->m_size) { pr_warn("Sig %u: Invalid digest size (%u)\n", sinfo->index, sinfo->msgdigest_len); ret = -EBADMSG; goto error; } - if (memcmp(sig->digest, sinfo->msgdigest, + if (memcmp(sig->m, sinfo->msgdigest, sinfo->msgdigest_len) != 0) { pr_warn("Sig %u: Message digest doesn't match\n", sinfo->index); @@ -97,21 +105,26 @@ 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 = crypto_shash_init(desc); - if (ret < 0) - goto error; - tag = ASN1_CONS_BIT | ASN1_SET; - ret = crypto_shash_update(desc, &tag, 1); - if (ret < 0) - goto error; - ret = crypto_shash_finup(desc, sinfo->authattrs, - sinfo->authattrs_len, sig->digest); - if (ret < 0) - goto error; - pr_devel("AADigest = [%*ph]\n", 8, sig->digest); + memcpy(sig->m, sinfo->authattrs, sinfo->authattrs_len); + sig->m[0] = ASN1_CONS_BIT | ASN1_SET; + + if (sig->algo_takes_data) { + sig->m_size = sinfo->authattrs_len; + ret = 0; + } else { + ret = crypto_shash_digest(desc, sig->m, + sinfo->authattrs_len, + sig->m); + if (ret < 0) + goto error; + } + pr_devel("AADigest = [%*ph]\n", 8, sig->m); } error: @@ -137,9 +150,14 @@ int pkcs7_get_digest(struct pkcs7_message *pkcs7, const u8 **buf, u32 *len, ret = pkcs7_digest(pkcs7, sinfo); if (ret) return ret; + if (!sinfo->sig->m_free) { + pr_notice_once("%s: No digest available\n", __func__); + return -EINVAL; /* TODO: MLDSA doesn't necessarily calculate an + * intermediate digest. */ + } - *buf = sinfo->sig->digest; - *len = sinfo->sig->digest_size; + *buf = sinfo->sig->m; + *len = sinfo->sig->m_size; i = match_string(hash_algo_name, HASH_ALGO__LAST, sinfo->sig->hash_algo); @@ -407,6 +425,12 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, return -EKEYREJECTED; } if (pkcs7->have_authattrs) { +#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA + if (pkcs7->authattrs_rej_waivable) { + pr_warn_once("Waived invalid module sig (has authattrs)\n"); + break; + } +#endif pr_warn("Invalid module sig (has authattrs)\n"); return -EKEYREJECTED; } diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index e5b177c8e842..09a0b83d5d77 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -142,6 +142,16 @@ software_key_determine_akcipher(const struct public_key *pkey, if (strcmp(hash_algo, "streebog256") != 0 && strcmp(hash_algo, "streebog512") != 0) return -EINVAL; + } else if (strcmp(pkey->pkey_algo, "mldsa44") == 0 || + strcmp(pkey->pkey_algo, "mldsa65") == 0 || + strcmp(pkey->pkey_algo, "mldsa87") == 0) { + if (strcmp(encoding, "raw") != 0) + return -EINVAL; + if (!hash_algo) + return -EINVAL; + if (strcmp(hash_algo, "none") != 0 && + strcmp(hash_algo, "sha512") != 0) + return -EINVAL; } else { /* Unknown public key algorithm */ return -ENOPKG; @@ -425,8 +435,7 @@ int public_key_verify_signature(const struct public_key *pkey, if (ret) goto error_free_key; - ret = crypto_sig_verify(tfm, sig->s, sig->s_size, - sig->digest, sig->digest_size); + ret = crypto_sig_verify(tfm, sig->s, sig->s_size, sig->m, sig->m_size); error_free_key: kfree_sensitive(key); diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 041d04b5c953..a5ac7a53b670 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -28,7 +28,8 @@ void public_key_signature_free(struct public_key_signature *sig) for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++) kfree(sig->auth_ids[i]); kfree(sig->s); - kfree(sig->digest); + if (sig->m_free) + kfree(sig->m); kfree(sig); } } diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index b37cae914987..2fe094f5caf3 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, unsigned char tag, case OID_gost2012Signature512: ctx->cert->sig->hash_algo = "streebog512"; goto ecrdsa; + case OID_id_ml_dsa_44: + ctx->cert->sig->pkey_algo = "mldsa44"; + goto ml_dsa; + case OID_id_ml_dsa_65: + ctx->cert->sig->pkey_algo = "mldsa65"; + goto ml_dsa; + case OID_id_ml_dsa_87: + ctx->cert->sig->pkey_algo = "mldsa87"; + goto ml_dsa; } rsa_pkcs1: @@ -274,6 +283,12 @@ ecdsa: ctx->cert->sig->encoding = "x962"; ctx->sig_algo = ctx->last_oid; return 0; +ml_dsa: + ctx->cert->sig->algo_takes_data = true; + ctx->cert->sig->hash_algo = "none"; + ctx->cert->sig->encoding = "raw"; + ctx->sig_algo = ctx->last_oid; + return 0; } /* @@ -300,7 +315,8 @@ int x509_note_signature(void *context, size_t hdrlen, if (strcmp(ctx->cert->sig->pkey_algo, "rsa") == 0 || strcmp(ctx->cert->sig->pkey_algo, "ecrdsa") == 0 || - strcmp(ctx->cert->sig->pkey_algo, "ecdsa") == 0) { + strcmp(ctx->cert->sig->pkey_algo, "ecdsa") == 0 || + strncmp(ctx->cert->sig->pkey_algo, "mldsa", 5) == 0) { /* Discard the BIT STRING metadata */ if (vlen < 1 || *(const u8 *)value != 0) return -EBADMSG; @@ -524,6 +540,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 = "mldsa44"; + break; + case OID_id_ml_dsa_65: + ctx->cert->pub->pkey_algo = "mldsa65"; + break; + case OID_id_ml_dsa_87: + ctx->cert->pub->pkey_algo = "mldsa87"; + break; default: return -ENOPKG; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 0688c222806b..b7aeebdddb36 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -9,12 +9,14 @@ #include <linux/time.h> #include <crypto/public_key.h> #include <keys/asymmetric-type.h> +#include <crypto/sha2.h> struct x509_certificate { struct x509_certificate *next; struct x509_certificate *signer; /* Certificate that signed this one */ struct public_key *pub; /* Public key details */ struct public_key_signature *sig; /* Signature parameters */ + u8 sha256[SHA256_DIGEST_SIZE]; /* Hash for blacklist purposes */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ struct asymmetric_key_id *id; /* Issuer + Serial number */ diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 12e3341e806b..27b4fea37845 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -31,12 +31,33 @@ int x509_get_sig_params(struct x509_certificate *cert) pr_devel("==>%s()\n", __func__); + /* Calculate a SHA256 hash of the TBS and check it against the + * blacklist. + */ + sha256(cert->tbs, cert->tbs_size, cert->sha256); + ret = is_hash_blacklisted(cert->sha256, sizeof(cert->sha256), + BLACKLIST_HASH_X509_TBS); + if (ret == -EKEYREJECTED) { + pr_err("Cert %*phN is blacklisted\n", + (int)sizeof(cert->sha256), cert->sha256); + cert->blacklisted = true; + ret = 0; + } + sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL); if (!sig->s) return -ENOMEM; sig->s_size = cert->raw_sig_size; + if (sig->algo_takes_data) { + /* The signature algorithm does whatever passes for hashing. */ + sig->m = (u8 *)cert->tbs; + sig->m_size = cert->tbs_size; + sig->m_free = false; + goto out; + } + /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ @@ -50,12 +71,13 @@ int x509_get_sig_params(struct x509_certificate *cert) } desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - sig->digest_size = crypto_shash_digestsize(tfm); + sig->m_size = crypto_shash_digestsize(tfm); ret = -ENOMEM; - sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); - if (!sig->digest) + sig->m = kmalloc(sig->m_size, GFP_KERNEL); + if (!sig->m) goto error; + sig->m_free = true; desc = kzalloc(desc_size, GFP_KERNEL); if (!desc) @@ -63,25 +85,15 @@ int x509_get_sig_params(struct x509_certificate *cert) desc->tfm = tfm; - ret = crypto_shash_digest(desc, cert->tbs, cert->tbs_size, - sig->digest); - + ret = crypto_shash_digest(desc, cert->tbs, cert->tbs_size, sig->m); if (ret < 0) goto error_2; - ret = is_hash_blacklisted(sig->digest, sig->digest_size, - BLACKLIST_HASH_X509_TBS); - if (ret == -EKEYREJECTED) { - pr_err("Cert %*phN is blacklisted\n", - sig->digest_size, sig->digest); - cert->blacklisted = true; - ret = 0; - } - error_2: kfree(desc); error: crypto_free_shash(tfm); +out: pr_devel("<==%s() = %d\n", __func__, ret); return ret; } diff --git a/crypto/mldsa.c b/crypto/mldsa.c new file mode 100644 index 000000000000..d8de082cc67a --- /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 <linux/init.h> +#include <linux/module.h> +#include <crypto/internal/sig.h> +#include <crypto/mldsa.h> + +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; + bool 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 = 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 = 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 = crypto_sig_ctx(tfm); + unsigned int expected_len = crypto_mldsa_key_size(tfm); + + if (keylen != expected_len) + return -EINVAL; + + ctx->pk_len = keylen; + memcpy(ctx->pk, key, keylen); + ctx->key_set = 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 = 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 = crypto_sig_ctx(tfm); + + ctx->strength = MLDSA44; + ctx->key_set = false; + return 0; +} + +static int crypto_mldsa65_alg_init(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm); + + ctx->strength = MLDSA65; + ctx->key_set = false; + return 0; +} + +static int crypto_mldsa87_alg_init(struct crypto_sig *tfm) +{ + struct crypto_mldsa_ctx *ctx = crypto_sig_ctx(tfm); + + ctx->strength = MLDSA87; + ctx->key_set = false; + return 0; +} + +static void crypto_mldsa_alg_exit(struct crypto_sig *tfm) +{ +} + +static struct sig_alg crypto_mldsa_algs[] = { + { + .sign = crypto_mldsa_sign, + .verify = crypto_mldsa_verify, + .set_pub_key = crypto_mldsa_set_pub_key, + .set_priv_key = crypto_mldsa_set_priv_key, + .key_size = crypto_mldsa_key_size, + .max_size = crypto_mldsa_max_size, + .init = crypto_mldsa44_alg_init, + .exit = crypto_mldsa_alg_exit, + .base.cra_name = "mldsa44", + .base.cra_driver_name = "mldsa44-lib", + .base.cra_ctxsize = sizeof(struct crypto_mldsa_ctx), + .base.cra_module = THIS_MODULE, + .base.cra_priority = 5000, + }, { + .sign = crypto_mldsa_sign, + .verify = crypto_mldsa_verify, + .set_pub_key = crypto_mldsa_set_pub_key, + .set_priv_key = crypto_mldsa_set_priv_key, + .key_size = crypto_mldsa_key_size, + .max_size = crypto_mldsa_max_size, + .init = crypto_mldsa65_alg_init, + .exit = crypto_mldsa_alg_exit, + .base.cra_name = "mldsa65", + .base.cra_driver_name = "mldsa65-lib", + .base.cra_ctxsize = sizeof(struct crypto_mldsa_ctx), + .base.cra_module = THIS_MODULE, + .base.cra_priority = 5000, + }, { + .sign = crypto_mldsa_sign, + .verify = crypto_mldsa_verify, + .set_pub_key = crypto_mldsa_set_pub_key, + .set_priv_key = crypto_mldsa_set_priv_key, + .key_size = crypto_mldsa_key_size, + .max_size = crypto_mldsa_max_size, + .init = crypto_mldsa87_alg_init, + .exit = crypto_mldsa_alg_exit, + .base.cra_name = "mldsa87", + .base.cra_driver_name = "mldsa87-lib", + .base.cra_ctxsize = sizeof(struct crypto_mldsa_ctx), + .base.cra_module = THIS_MODULE, + .base.cra_priority = 5000, + }, +}; + +static int __init mldsa_init(void) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(crypto_mldsa_algs); i++) { + ret = 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 >= 0; i--) + crypto_unregister_sig(&crypto_mldsa_algs[i]); + return ret; +} +module_init(mldsa_init); + +static void mldsa_exit(void) +{ + for (int i = 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"); |
