mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
keys: Support for ML-DSA module signing
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEqG5UsNXhtOCrfGQP+7dXa6fLC2sFAmmFu8gACgkQ+7dXa6fL C2t5UA//Zz3G9/libuvGx3tVuhaub8WQS43GNBN1h5Js0xkbGfhyBfAvGcX1xwiL VCrjZZsQbIp1oijr0G7P0KsGB1aVyBOYN7phLEYLsdDvqZt7mVMNSePq0xELPjMw tF2Ca7TIWx/GOlReInl4gxnzyBlDrYAyvrBCCU1SfQyTqDWQCbVPdFQJtJY2mY6j l5q2qBZ0QB4G34D5sPjYhc23kcl8BdNLzQGe9IRjVqHfDyWa1cBqAI6eQLMX3kt4 wJp8oWVrA/89nk2IwzTPJTIRJm16df4Cpa6Frr9o4CQi+5N8uPhxpN4iEc3G6EGn eZ8ohCoNhsG7k+nd2tSDvp/1kmqL261+rPXcw1MiHs49mTKp4a4r62O4Hdd2jMf4 dR0p2/jBiqeAT2jYuc6iQxfEvzTq8D6K4u0ThlUvE5EpIb2H7Gk8HcWFn5kBbnx/ VxGTPEkzwDn1jxg1VoPg59uT/7rYWVy1MjI54EyFuWmIz7W2J/5QsKFzSSpVn9nW eGuGZvL+EqMPS9GqQimfnwa27RNQZ4oJKr58OqJVEoyaNPoeQO2XlFT1kHWfK3tb RlncfRLqbZ27qpz50InOwHQvGoEW32cnf9SQPTKQpWDXaWe2Sb1wxLcmhsyhXFah erP33Ea3P76+JsXlw385Q33xa4dB/7IQT0kytr1i0kKm4lDlpho= =KaEy -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
b63c907203
20 changed files with 472 additions and 70 deletions
|
|
@ -28,10 +28,12 @@ trusted userspace bits.
|
|||
|
||||
This facility uses X.509 ITU-T standard certificates to encode the public keys
|
||||
involved. The signatures are not themselves encoded in any industrial standard
|
||||
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 signature).
|
||||
type. The built-in facility currently only supports the RSA, NIST P-384 ECDSA
|
||||
and NIST FIPS-204 ML-DSA 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 does its own hashing,
|
||||
but is allowed to be used with a SHA512 hash for signed attributes.
|
||||
|
||||
|
||||
==========================
|
||||
|
|
@ -146,9 +148,9 @@ into vmlinux) using parameters in the::
|
|||
|
||||
file (which is also generated if it does not already exist).
|
||||
|
||||
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.
|
||||
|
||||
It is strongly recommended that you provide your own x509.genkey file.
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,39 @@ 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.
|
||||
|
||||
config MODULE_SIG_KEY_TYPE_MLDSA_44
|
||||
bool "ML-DSA-44"
|
||||
select CRYPTO_MLDSA
|
||||
depends on OPENSSL_SUPPORTS_ML_DSA
|
||||
help
|
||||
Use an ML-DSA-44 key (NIST FIPS 204) for module signing. ML-DSA
|
||||
support requires OpenSSL-3.5 minimum; preferably OpenSSL-4+. With
|
||||
the latter, the entire module body will be signed; with the former,
|
||||
signedAttrs will be used as it lacks support for CMS_NOATTR with
|
||||
ML-DSA.
|
||||
|
||||
config MODULE_SIG_KEY_TYPE_MLDSA_65
|
||||
bool "ML-DSA-65"
|
||||
select CRYPTO_MLDSA
|
||||
depends on OPENSSL_SUPPORTS_ML_DSA
|
||||
help
|
||||
Use an ML-DSA-65 key (NIST FIPS 204) for module signing. ML-DSA
|
||||
support requires OpenSSL-3.5 minimum; preferably OpenSSL-4+. With
|
||||
the latter, the entire module body will be signed; with the former,
|
||||
signedAttrs will be used as it lacks support for CMS_NOATTR with
|
||||
ML-DSA.
|
||||
|
||||
config MODULE_SIG_KEY_TYPE_MLDSA_87
|
||||
bool "ML-DSA-87"
|
||||
select CRYPTO_MLDSA
|
||||
depends on OPENSSL_SUPPORTS_ML_DSA
|
||||
help
|
||||
Use an ML-DSA-87 key (NIST FIPS 204) for module signing. ML-DSA
|
||||
support requires OpenSSL-3.5 minimum; preferably OpenSSL-4+. With
|
||||
the latter, the entire module body will be signed; with the former,
|
||||
signedAttrs will be used as it lacks support for CMS_NOATTR with
|
||||
ML-DSA.
|
||||
|
||||
endchoice
|
||||
|
||||
config SYSTEM_TRUSTED_KEYRING
|
||||
|
|
@ -154,4 +187,11 @@ config SYSTEM_BLACKLIST_AUTH_UPDATE
|
|||
keyring. The PKCS#7 signature of the description is set in the key
|
||||
payload. Blacklist keys cannot be removed.
|
||||
|
||||
config OPENSSL_SUPPORTS_ML_DSA
|
||||
def_bool $(success, openssl list -key-managers | grep -q ML-DSA-87)
|
||||
help
|
||||
Support for ML-DSA-44/65/87 was added in openssl-3.5, so as long
|
||||
as older versions are supported, the key types may only be
|
||||
set after testing the installed binary for support.
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ targets += x509_certificate_list
|
|||
ifeq ($(CONFIG_MODULE_SIG_KEY),certs/signing_key.pem)
|
||||
|
||||
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_ECDSA) := -newkey ec -pkeyopt ec_paramgen_curve:secp384r1
|
||||
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_44) := -newkey ml-dsa-44
|
||||
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_65) := -newkey ml-dsa-65
|
||||
keytype-$(CONFIG_MODULE_SIG_KEY_TYPE_MLDSA_87) := -newkey ml-dsa-87
|
||||
|
||||
quiet_cmd_gen_key = GENKEY $@
|
||||
cmd_gen_key = openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 36500 \
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
memcpy(sig->m, sinfo->authattrs, sinfo->authattrs_len);
|
||||
sig->m[0] = ASN1_CONS_BIT | ASN1_SET;
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
201
crypto/mldsa.c
Normal file
201
crypto/mldsa.c
Normal file
|
|
@ -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");
|
||||
|
|
@ -43,9 +43,11 @@ extern void public_key_free(struct public_key *key);
|
|||
struct public_key_signature {
|
||||
struct asymmetric_key_id *auth_ids[3];
|
||||
u8 *s; /* Signature */
|
||||
u8 *digest;
|
||||
u8 *m; /* Message data to pass to verifier */
|
||||
u32 s_size; /* Number of bytes in signature */
|
||||
u32 digest_size; /* Number of bytes in digest */
|
||||
u32 m_size; /* Number of bytes in ->m */
|
||||
bool m_free; /* T if ->m needs freeing */
|
||||
bool algo_takes_data; /* T if public key algo operates on data, not a hash */
|
||||
const char *pkey_algo;
|
||||
const char *hash_algo;
|
||||
const char *encoding;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
/* NIST FIPS-204 ML-DSA */
|
||||
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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
|
||||
# define USE_PKCS11_PROVIDER
|
||||
# include <openssl/provider.h>
|
||||
# include <openssl/store.h>
|
||||
|
|
@ -315,18 +315,39 @@ int main(int argc, char **argv)
|
|||
ERR(!digest_algo, "EVP_get_digestbyname");
|
||||
|
||||
#ifndef USE_PKCS7
|
||||
|
||||
unsigned int flags =
|
||||
CMS_NOCERTS |
|
||||
CMS_PARTIAL |
|
||||
CMS_BINARY |
|
||||
CMS_DETACHED |
|
||||
CMS_STREAM |
|
||||
CMS_NOSMIMECAP |
|
||||
#ifdef CMS_NO_SIGNING_TIME
|
||||
CMS_NO_SIGNING_TIME |
|
||||
#endif
|
||||
use_keyid;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L
|
||||
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")) {
|
||||
/* ML-DSA + CMS_NOATTR is not supported in openssl-3.5
|
||||
* and before.
|
||||
*/
|
||||
use_signed_attrs = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
flags |= use_signed_attrs;
|
||||
|
||||
/* Load the signature message from the digest buffer. */
|
||||
cms = CMS_sign(NULL, NULL, NULL, NULL,
|
||||
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY |
|
||||
CMS_DETACHED | CMS_STREAM);
|
||||
cms = CMS_sign(NULL, NULL, NULL, NULL, flags);
|
||||
ERR(!cms, "CMS_sign");
|
||||
|
||||
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) != 1,
|
||||
ERR(CMS_final(cms, bm, NULL, flags) != 1,
|
||||
"CMS_final");
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ int asymmetric_verify(struct key *keyring, const char *sig,
|
|||
goto out;
|
||||
}
|
||||
|
||||
pks.digest = (u8 *)data;
|
||||
pks.digest_size = datalen;
|
||||
pks.m = (u8 *)data;
|
||||
pks.m_size = datalen;
|
||||
pks.s = hdr->sig;
|
||||
pks.s_size = siglen;
|
||||
ret = verify_signature(key, &pks);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue