* Re: [PATCH v10 5/8] crypto: Add ML-DSA crypto_sig support
From: Eric Biggers @ 2025-11-24 16:49 UTC (permalink / raw)
To: David Howells
Cc: Herbert Xu, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <3374841.1763975577@warthog.procyon.org.uk>
On Mon, Nov 24, 2025 at 09:12:57AM +0000, David Howells wrote:
> 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.
> + */
Still not really sure what the point is. There's only one user of
crypto_sig, and it could just call the ML-DSA functions directly.
- Eric
^ permalink raw reply
* Re: [PATCH v3 0/9] module: Introduce hash-based integrity checking
From: Thomas Weißschuh @ 2025-11-24 9:41 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: James Bottomley, Masahiro Yamada, Nathan Chancellor,
Arnd Bergmann, Luis Chamberlain, Petr Pavlu, Sami Tolvanen,
Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <20251123170502.Ai5Ig66Z@breakpoint.cc>
Hi Sebastian,
On 2025-11-23 18:05:02+0100, Sebastian Andrzej Siewior wrote:
> On 2025-11-19 16:48:34 [+0100], Sebastian Andrzej Siewior wrote:
> > I fully agree with this approach. I don't like the big hash array but I
> > have an idea how to optimize that part. So I don't see a problem in the
> > long term.
>
> The following PoC creates a merkle tree from a set files ending with .ko
> within the specified directory. It will write a .hash files containing
> the required hash for each file for its validation. The root hash is
> saved as "hash_root" and "hash_root.h" in the directory.
Thanks a lot!
> The Debian kernel shipps 4256 modules:
>
> | $ time ./compute_hashes mods_deb
> | Files 4256 levels: 13 root hash: 97f8f439d63938ed74f48ec46dbd75c2b5e5b49f012a414e89b6f0e0f06efe84
> |
> | real 0m0,732s
> | user 0m0,304s
> | sys 0m0,427s
>
> This computes the hashes for all the modules it found in the mods_deb
> folder.
> The kernel needs the root hash (for sha256 32 bytes) and the depth of
> the tree (4 bytes). That are 36 bytes regardless of the number of
> modules that are built.
> In this case, the attached hash for each module is 420 bytes. This is 4
> bytes (position in the tree) + 13 (depth) * 32.
> The verification process requires 13 hash operation to hash through the
> tree and verify against the root hash.
We'll need to store the proof together with the modules somewhere.
Regular module signatures are stored as PKCS#7 and appended to the module
file. If we can also encode the merkle proof as PKCS#7, the integration
into the existing infrastructure should be much easier.
It will require some changes to this series, but honestly the Merkle
tree aproach looks like the clear winner here.
> For convience, the following PoC can also be found at
> https://git.kernel.org/pub/scm/linux/kernel/git/bigeasy/mtree-hashed-mods.git/
>
> which also includes a small testsuite.
(...)
Thomas
^ permalink raw reply
* Re: [PATCH v10 5/8] crypto: Add ML-DSA crypto_sig support
From: David Howells @ 2025-11-24 9:12 UTC (permalink / raw)
Cc: dhowells, Herbert Xu, Eric Biggers, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251120104439.2620205-6-dhowells@redhat.com>
Meh. I forgot to git add crypto/mldsa.c.
David
---
diff --git a/crypto/Kconfig b/crypto/Kconfig
index bf8b8a60a0c0..45e376af02dc 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.
+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
menu "Block ciphers"
diff --git a/crypto/Makefile b/crypto/Makefile
index 093c56a45d3f..b181f8a54099 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/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 <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;
+ 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 = 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");
^ permalink raw reply related
* Re: [PATCH v3 7/9] module: Move lockdown check into generic module loader
From: Sebastian Andrzej Siewior @ 2025-11-23 17:10 UTC (permalink / raw)
To: Paul Moore
Cc: Thomas Weißschuh, Masahiro Yamada, Nathan Chancellor,
Arnd Bergmann, Luis Chamberlain, Petr Pavlu, Sami Tolvanen,
Daniel Gomez, James Morris, Serge E. Hallyn, Jonathan Corbet,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy, Naveen N Rao, Mimi Zohar, Roberto Sassu,
Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <CAHC9VhTuf1u4B3uybZxdojcmz5sFG+_JHUCC=C0N=9gFDmurHg@mail.gmail.com>
On 2025-11-19 14:55:47 [-0500], Paul Moore wrote:
> On Wed, Nov 19, 2025 at 6:20 AM Sebastian Andrzej Siewior
> <bigeasy@linutronix.de> wrote:
> > On 2025-04-29 15:04:34 [+0200], Thomas Weißschuh wrote:
> > > The lockdown check buried in module_sig_check() will not compose well
> > > with the introduction of hash-based module validation.
> >
> > An explanation of why would be nice.
>
> /me shrugs
>
> I thought the explanation was sufficient.
Okay. So if it is just me and everyone is well aware then okay.
Sebastian
^ permalink raw reply
* Re: [PATCH v3 0/9] module: Introduce hash-based integrity checking
From: Sebastian Andrzej Siewior @ 2025-11-23 17:05 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: James Bottomley, Masahiro Yamada, Nathan Chancellor,
Arnd Bergmann, Luis Chamberlain, Petr Pavlu, Sami Tolvanen,
Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <20251119154834.A-tQsLzh@linutronix.de>
On 2025-11-19 16:48:34 [+0100], Sebastian Andrzej Siewior wrote:
> I fully agree with this approach. I don't like the big hash array but I
> have an idea how to optimize that part. So I don't see a problem in the
> long term.
The following PoC creates a merkle tree from a set files ending with .ko
within the specified directory. It will write a .hash files containing
the required hash for each file for its validation. The root hash is
saved as "hash_root" and "hash_root.h" in the directory.
The Debian kernel shipps 4256 modules:
| $ time ./compute_hashes mods_deb
| Files 4256 levels: 13 root hash: 97f8f439d63938ed74f48ec46dbd75c2b5e5b49f012a414e89b6f0e0f06efe84
|
| real 0m0,732s
| user 0m0,304s
| sys 0m0,427s
This computes the hashes for all the modules it found in the mods_deb
folder.
The kernel needs the root hash (for sha256 32 bytes) and the depth of
the tree (4 bytes). That are 36 bytes regardless of the number of
modules that are built.
In this case, the attached hash for each module is 420 bytes. This is 4
bytes (position in the tree) + 13 (depth) * 32.
The verification process requires 13 hash operation to hash through the
tree and verify against the root hash.
For convience, the following PoC can also be found at
https://git.kernel.org/pub/scm/linux/kernel/git/bigeasy/mtree-hashed-mods.git/
which also includes a small testsuite.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000..e4a35c15f0a94
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+CC := gcc
+CFLAGS := -O2 -g -Wall
+LDLIBS := -lcrypto
+
+all: compute_hashes mk-files verify_hash
+test: compute_hashes mk-files verify_hash
+ ./verify_test.sh
diff --git a/compute_hashes.c b/compute_hashes.c
new file mode 100644
index 0000000000000..da61b214137b8
--- /dev/null
+++ b/compute_hashes.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Compute hashes for individual files and build a merkle tree.
+ *
+ * Author: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ *
+ */
+#define _GNU_SOURCE 1
+#include <ftw.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/evp.h>
+
+#include "helpers.h"
+
+struct file_entry {
+ char *name;
+ size_t fsize;
+ unsigned int pos;
+ unsigned char hash[EVP_MAX_MD_SIZE];
+};
+
+static struct file_entry *fh_list;
+static size_t num_files;
+
+struct leaf_hash {
+ unsigned char hash[EVP_MAX_MD_SIZE];
+};
+
+struct mtree {
+ struct leaf_hash **l;
+ unsigned int *entries;
+ unsigned int levels;
+};
+
+static unsigned int get_pow2(unsigned int val)
+{
+ return 31 - __builtin_clz(val);
+}
+
+static unsigned int roundup_pow2(unsigned int val)
+{
+ return 1 << (get_pow2(val - 1) + 1);
+}
+
+static unsigned int log2_roundup(unsigned int val)
+{
+ return get_pow2(roundup_pow2(val));
+}
+
+static int str_endswith(const char *s, const char *suffix)
+{
+ size_t ls, lf;
+
+ ls = strlen(s);
+ lf = strlen(suffix);
+
+ if (ls <= lf)
+ return -1;
+ return strcmp(s + ls - lf, suffix);
+}
+
+static void __print_hash(unsigned char *h)
+{
+ int i;
+
+ for (i = 0; i < hash_size; i++)
+ printf("%02x", h[i]);
+}
+
+static void print_hash(unsigned char *h)
+{
+ __print_hash(h);
+ printf("\n");
+}
+
+static int hash_file(struct file_entry *fe)
+{
+ void *mem;
+ int fd;
+
+ fd = open(fe->name, O_RDONLY);
+ if (fd < 0) {
+ printf("Failed to open %s: %m\n", fe->name);
+ exit(1);
+ }
+
+ mem = mmap(NULL, fe->fsize, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (mem == MAP_FAILED) {
+ printf("Failed to mmap %s: %m\n", fe->name);
+ exit(1);
+ }
+
+ hash_data(mem, fe->pos, fe->fsize, fe->hash);
+
+ munmap(mem, fe->fsize);
+ return 0;
+}
+
+static int add_files_cb(const char *fpath, const struct stat *sb, int tflag,
+ struct FTW *ftwbuf)
+{
+ if (tflag != FTW_F)
+ return 0;
+
+ if (str_endswith(fpath, ".ko"))
+ return 0;
+
+ fh_list = xrealloc(fh_list, (num_files + 1) * sizeof (struct file_entry));
+
+ fh_list[num_files].name = strdup(fpath);
+ if (!fh_list[num_files].name) {
+ printf("Failed to allocate memory\n");
+ exit(1);
+ }
+
+ fh_list[num_files].fsize = sb->st_size;
+
+ num_files++;
+ return 0;
+}
+
+static int cmp_file_entry(const void *p1, const void *p2)
+{
+ const struct file_entry *f1, *f2;
+
+ f1 = p1;
+ f2 = p2;
+
+ return strcmp(f1->name, f2->name);
+}
+
+static struct mtree *build_merkle(struct file_entry *fh, size_t num)
+{
+ unsigned int i, le;
+ struct mtree *mt;
+
+ mt = xmalloc(sizeof(struct mtree));
+ mt->levels = log2_roundup(num);
+ mt->l = xcalloc(sizeof(struct leaf_hash *), mt->levels);
+
+ mt->entries = xcalloc(sizeof(unsigned int), mt->levels);
+ le = num / 2;
+ if (num & 1)
+ le++;
+ mt->entries[0] = le;
+ mt->l[0] = xcalloc(sizeof(struct leaf_hash), le);
+
+ /* First level of pairs */
+ for (i = 0; i < num; i+= 2) {
+ if (i == num - 1) {
+ /* Odd number of files, no pair. Hash with itself */
+ hash_entry(fh[i].hash, fh[i].hash, mt->l[0][i/2].hash);
+ } else {
+ hash_entry(fh[i].hash, fh[i + 1].hash, mt->l[0][i/2].hash);
+ }
+ }
+ for (i = 1; i < mt->levels; i++) {
+ int n;
+ int odd = 0;
+
+ if (le & 1) {
+ le++;
+ odd++;
+ }
+
+ mt->entries[i] = le / 2;
+ mt->l[i] = xcalloc(sizeof(struct leaf_hash), le);
+
+ for (n = 0; n < le; n += 2) {
+ if (n == le - 2 && odd) {
+ /* Odd number of pairs, no pair. Hash with itself */
+ hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n].hash,
+ mt->l[i][n/2].hash);
+ } else {
+ hash_entry(mt->l[i - 1][n].hash, mt->l[i - 1][n +1].hash,
+ mt->l[i][n/2].hash);
+ }
+ }
+ le = mt->entries[i];
+ }
+ return mt;
+}
+
+static void free_mtree(struct mtree *mt)
+{
+ int i;
+
+ for (i = 0; i < mt->levels; i++)
+ free(mt->l[i]);
+
+ free(mt->l);
+ free(mt->entries);
+ free(mt);
+}
+
+static void write_be_int(int fd, unsigned int v)
+{
+ unsigned int be_val = host_to_be32(v);
+
+ if (write(fd, &be_val, sizeof(be_val)) != sizeof(be_val)) {
+ printf("Failed writting to file: %m\n");
+ exit(1);
+ }
+}
+
+static void write_hash(int fd, const void *h)
+{
+ ssize_t wr;
+
+ wr = write(fd, h, hash_size);
+ if (wr != hash_size) {
+ printf("Failed writting to file: %m\n");
+ exit(1);
+ }
+}
+
+static void build_proof(struct mtree *mt, unsigned int n, int fd)
+{
+ unsigned char cur[EVP_MAX_MD_SIZE];
+ unsigned char tmp[EVP_MAX_MD_SIZE];
+ struct file_entry *fe, *fe_sib;
+ unsigned int i;
+
+ fe = &fh_list[n];
+
+ if ((n & 1) == 0) {
+ /* No pair, hash with itself */
+ if (n + 1 == num_files)
+ fe_sib = fe;
+ else
+ fe_sib = &fh_list[n + 1];
+ } else {
+ fe_sib = &fh_list[n - 1];
+ }
+ /* First comes the node position into the file */
+ write_be_int(fd, n);
+
+ if ((n & 1) == 0)
+ hash_entry(fe->hash, fe_sib->hash, cur);
+ else
+ hash_entry(fe_sib->hash, fe->hash, cur);
+
+ /* Next is the sibling hash, followed by hashes in the tree */
+ write_hash(fd, fe_sib->hash);
+
+ for (i = 0; i < mt->levels - 1; i++) {
+ n >>= 1;
+ if ((n & 1) == 0) {
+ void *h;
+
+ /* No pair, hash with itself */
+ if (n + 1 == mt->entries[i])
+ h = cur;
+ else
+ h = mt->l[i][n + 1].hash;
+
+ hash_entry(cur, h, tmp);
+ write_hash(fd, h);
+ } else {
+ hash_entry(mt->l[i][n - 1].hash, cur, tmp);
+ write_hash(fd, mt->l[i][n - 1].hash);
+ }
+ memcpy(cur, tmp, hash_size);
+ }
+
+ /* After all that, the end hash should match the root hash */
+ if (memcmp(cur, mt->l[mt->levels - 1][0].hash, hash_size))
+ printf("MISS-MATCH\n");
+}
+
+static void write_merkle_root(struct mtree *mt, const char *fp)
+{
+ char buf[1024];
+ int fd;
+
+ if (snprintf(buf, sizeof(buf), "%s/hash_root", fp) >= sizeof(buf)) {
+ printf("Root dir too long\n");
+ exit(1);
+ }
+ fd = open(buf, O_WRONLY | O_CREAT | O_TRUNC, DEF_F_PERM);
+ if (fd < 0) {
+ printf("Failed to create %s: %m\n", buf);
+ exit(1);
+ }
+
+ write_be_int(fd, mt->levels);
+ write_hash(fd, mt->l[mt->levels - 1][0].hash);
+ close(fd);
+ printf("Files %ld levels: %d root hash: ", num_files, mt->levels);
+ print_hash(mt->l[mt->levels - 1][0].hash);
+}
+
+static void write_merkle_root_h(struct mtree *mt, const char *fp)
+{
+ char buf[1024];
+ unsigned int i;
+ unsigned char *h;
+ FILE *f;
+
+ if (snprintf(buf, sizeof(buf), "%s/hash_root.h", fp) >= sizeof(buf)) {
+ printf("Root dir too long\n");
+ exit(1);
+ }
+ f = fopen(buf, "w");
+ if (!f) {
+ printf("Failed to create %s: %m\n", buf);
+ exit(1);
+ }
+ h = mt->l[mt->levels - 1][0].hash;
+
+ fprintf(f, "#ifndef __HASH_ROOT_TREE_H__\n");
+ fprintf(f, "#define __HASH_ROOT_TREE_H__\n\n");
+ fprintf(f, "unsigned int hashed_mods_levels = %u;\n", mt->levels);
+ fprintf(f, "unsigned char hashed_mods_root[%d] = {", hash_size);
+ for (i = 0; i < hash_size; i++) {
+ char *space = "";
+
+ if (!(i % 8))
+ fprintf(f, "\n\t");
+
+ if ((i + 1) % 8)
+ space = " ";
+
+ fprintf(f, "0x%02x,%s", h[i], space);
+ }
+ fprintf(f, "\n};\n#endif\n");
+ fclose(f);
+}
+
+int main(int argc, char *argv[])
+{
+ const EVP_MD *hash_evp;
+ char *fp;
+ struct mtree *mt;
+ int i;
+
+ ctx = EVP_MD_CTX_new();
+ if (!ctx)
+ goto err_ossl;
+
+ if (argc != 2) {
+ printf("%s: folder\n", argv[0]);
+ return 1;
+ }
+ fp = argv[1];
+
+ hash_evp = EVP_sha256();
+ hash_size = EVP_MD_get_size(hash_evp);
+ if (hash_size <= 0)
+ goto err_ossl;
+
+ if (EVP_DigestInit_ex(ctx, hash_evp, NULL) != 1)
+ goto err_ossl;
+
+ nftw(fp, add_files_cb, 64, 0);
+
+ qsort(fh_list, num_files, sizeof(struct file_entry), cmp_file_entry);
+
+ for (i = 0; i < num_files; i++) {
+ fh_list[i].pos = i;
+ hash_file(&fh_list[i]);
+ }
+
+ mt = build_merkle(fh_list, num_files);
+ write_merkle_root(mt, fp);
+ write_merkle_root_h(mt, fp);
+ for (i = 0; i < num_files; i++) {
+ char signame[1024];
+ int fd;
+ int ret;
+
+ ret = snprintf(signame, sizeof(signame), "%s.hash", fh_list[i].name);
+ if (ret >= sizeof(signame)) {
+ printf("path + name too long\n");
+ return 1;
+ }
+ fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, DEF_F_PERM);
+ if (fd < 0) {
+ printf("Can't create %s: %m\n", signame);
+ return 1;
+ }
+ build_proof(mt, i, fd);
+ close(fd);
+ }
+
+ free_mtree(mt);
+ for (i = 0; i < num_files; i++)
+ free(fh_list[i].name);
+ free(fh_list);
+
+ EVP_MD_CTX_free(ctx);
+ return 0;
+
+err_ossl:
+ printf("libssl operation failed\n");
+ return 1;
+}
diff --git a/helpers.h b/helpers.h
new file mode 100644
index 0000000000000..f52ad3543f890
--- /dev/null
+++ b/helpers.h
@@ -0,0 +1,109 @@
+#ifndef __HELPERS_H__
+#define __HELPERS_H__
+
+static EVP_MD_CTX *ctx;
+static int hash_size;
+
+#define DEF_F_PERM (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) /* 0644*/
+#define DEF_D_PERM (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) /* 0755*/
+
+static unsigned int host_to_be32(unsigned int v)
+{
+#if __BYTE_ORDER__ == __LITTLE_ENDIAN
+ return __builtin_bswap32(v);
+#elif __BYTE_ORDER__ == __BIG_ENDIAN
+ return v;
+#else
+#error Missing endian define
+#endif
+}
+
+static inline void *xcalloc(size_t n, size_t size)
+{
+ void *p;
+
+ p = calloc(n, size);
+ if (p)
+ return p;
+ printf("Memory allocation failed\n");
+ exit(1);
+}
+
+static void *xmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p)
+ return p;
+ printf("Memory allocation failed\n");
+ exit(1);
+}
+
+static inline void *xrealloc(void *oldp, size_t size)
+{
+ void *p;
+
+ p = realloc(oldp, size);
+ if (p)
+ return p;
+ printf("Memory allocation failed\n");
+ exit(1);
+}
+
+static void hash_data(void *p, unsigned int pos, size_t size, void *ret_hash)
+{
+ unsigned char magic = 0x01;
+ unsigned int pos_be;
+
+ pos_be = host_to_be32(pos);
+ if (EVP_DigestInit_ex(ctx, NULL, NULL) != 1)
+ goto err;
+
+ if (EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1)
+ goto err;
+
+ if (EVP_DigestUpdate(ctx, &pos_be, sizeof(pos_be)) != 1)
+ goto err;
+
+ if (EVP_DigestUpdate(ctx, p, size) != 1)
+ goto err;
+
+ if (EVP_DigestFinal_ex(ctx, ret_hash, NULL) != 1)
+ goto err;
+
+ return;
+
+err:
+ printf("libssl operation failed\n");
+ exit(1);
+}
+static void hash_entry(void *left, void *right, void *ret_hash)
+{
+ unsigned char magic = 0x02;
+
+ if (EVP_DigestInit_ex(ctx, NULL, NULL) != 1)
+ goto err;
+
+ if (EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1)
+ goto err;
+
+ if (EVP_DigestUpdate(ctx, left, hash_size) != 1)
+ goto err;
+
+ if (EVP_DigestUpdate(ctx, right, hash_size) != 1)
+ goto err;
+
+ if (EVP_DigestFinal_ex(ctx, ret_hash, NULL) != 1)
+ goto err;
+
+ return;
+
+err:
+ printf("libssl operation failed\n");
+ exit(1);
+}
+
+
+
+#endif
diff --git a/verify_hash.c b/verify_hash.c
new file mode 100644
index 0000000000000..0a842f27f1ebc
--- /dev/null
+++ b/verify_hash.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Verify a file against and its hash against a merkle tree hash.
+ *
+ * Author: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ *
+ */
+#define _GNU_SOURCE 1
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/evp.h>
+
+#include "helpers.h"
+
+struct hash_root {
+ unsigned int level;
+ unsigned char hash[EVP_MAX_MD_SIZE];
+};
+
+struct verify_sig {
+ unsigned int pos;
+ char hash_sigs[];
+};
+
+static int hash_file(const char *f, unsigned char *hash, unsigned int pos)
+{
+ struct stat sb;
+ int fd, ret;
+ void *mem;
+
+ fd = open(f, O_RDONLY);
+ if (fd < 0) {
+ printf("Failed to open %s: %m\n", f);
+ exit(1);
+ }
+
+ ret = fstat(fd, &sb);
+ if (ret) {
+ printf("stat failed %m\n");
+ exit(1);
+ }
+
+ mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (mem == MAP_FAILED) {
+ printf("Failed to mmap file: %m\n");
+ exit(1);
+ }
+
+ hash_data(mem, pos, sb.st_size, hash);
+
+ munmap(mem, sb.st_size);
+ return 0;
+}
+
+static void verify_hash(struct hash_root *hr, struct verify_sig *vs, unsigned char *cur,
+ const char *fn)
+{
+ unsigned char tmp[EVP_MAX_MD_SIZE];
+ unsigned sig_ofs = 0;
+ unsigned int i, n;
+
+ n = vs->pos;
+ if ((n & 1) == 0)
+ hash_entry(cur, &vs->hash_sigs[sig_ofs], tmp);
+ else
+ hash_entry(&vs->hash_sigs[sig_ofs], cur, tmp);
+
+ memcpy(cur, tmp, hash_size);
+ sig_ofs += hash_size;
+ for (i = 0; i < hr->level - 1; i++) {
+ n >>= 1;
+ if ((n & 1) == 0) {
+ hash_entry(cur, &vs->hash_sigs[sig_ofs], tmp);
+ } else {
+ hash_entry(&vs->hash_sigs[sig_ofs], cur, tmp);
+ }
+ memcpy(cur, tmp, hash_size);
+ sig_ofs += hash_size;
+ }
+
+ if (!memcmp(cur, hr->hash, hash_size)) {
+ exit(0);
+ } else {
+ printf("MISS-MATCH on %s\n", fn);
+ exit(1);
+ }
+}
+
+static void read_be_int(int fd, unsigned int *val)
+{
+ unsigned int val_be;
+
+ if (read(fd, &val_be, sizeof(val_be)) != sizeof(val_be)) {
+ printf("Can't read from file\n");
+ exit(1);
+ }
+ *val = host_to_be32(val_be);
+}
+
+struct hash_root *read_root_hash(const char *f)
+{
+ int fd;
+ struct hash_root *hr;
+
+ hr = xmalloc(sizeof(*hr));
+ fd = open(f, O_RDONLY);
+ if (fd < 0) {
+ printf("Can't open %s: %m\n", f);
+ exit(1);
+ }
+ read_be_int(fd, &hr->level);
+ if (read(fd, hr->hash, hash_size) != hash_size) {
+ printf("Can't read complete hash (%u): %m\n",
+ hash_size);
+ exit(1);
+ }
+ close(fd);
+ return hr;
+}
+
+static void load_hash_sig(const char *f, struct verify_sig *verify_sig,
+ unsigned int sig_num)
+{
+ ssize_t total_hash_size;
+ struct stat sb;
+ char buf[1024];
+ int fd;
+ int ret;
+
+ total_hash_size = sig_num * hash_size;
+
+ ret = snprintf(buf, sizeof(buf), "%s.hash", f);
+ if (ret >= sizeof(buf)) {
+ printf("Too long\n");
+ exit(1);
+ }
+ fd = open(buf, O_RDONLY);
+ if (fd < 0) {
+ printf("Failed to open %s\n", buf);
+ exit(1);
+ }
+ read_be_int(fd, &verify_sig->pos);
+
+ ret = fstat(fd, &sb);
+ if (ret < 0) {
+ printf("Failed to stat %s: %m\n", f);
+ exit(1);
+ }
+
+ if (sb.st_size != total_hash_size + 4) {
+ printf("Unexpected signature size: Expected %ld vs found %ld\n",
+ total_hash_size + 4, sb.st_size);
+ exit(1);
+ }
+ if (read(fd, verify_sig->hash_sigs, total_hash_size) != total_hash_size) {
+ printf("Failed to read the signature: %m\n");
+ exit(1);
+ }
+ close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+ struct hash_root *hash_root;
+ struct verify_sig *vsig;
+ unsigned char fhash[EVP_MAX_MD_SIZE];
+ const EVP_MD *hash_evp;
+
+ ctx = EVP_MD_CTX_new();
+ if (!ctx)
+ goto err;
+
+ if (argc != 3) {
+ printf("%s: hash_root module\n", argv[0]);
+ return 1;
+ }
+
+ hash_evp = EVP_sha256();
+ hash_size = EVP_MD_get_size(hash_evp);
+ if (hash_size <= 0)
+ goto err;
+
+ if (EVP_DigestInit_ex(ctx, hash_evp, NULL) != 1)
+ goto err;
+
+ hash_root = read_root_hash(argv[1]);
+ vsig = xmalloc(sizeof(struct verify_sig) + hash_root->level * hash_size);
+
+ load_hash_sig(argv[2], vsig, hash_root->level);
+ hash_file(argv[2], fhash, vsig->pos);
+ verify_hash(hash_root, vsig, fhash, argv[2]);
+
+ EVP_MD_CTX_free(ctx);
+ return 0;
+err:
+ printf("libssl operation failed\n");
+ return 1;
+}
--
2.51.0
Sebastian
^ permalink raw reply related
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Eric Biggers @ 2025-11-21 22:48 UTC (permalink / raw)
To: Lukas Wunner
Cc: David Howells, linux-crypto, Herbert Xu, Luis Chamberlain,
Petr Pavlu, Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld,
Ard Biesheuvel, Stephan Mueller, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <aSDnvNGos9qM2Uj5@wunner.de>
On Fri, Nov 21, 2025 at 11:29:16PM +0100, Lukas Wunner wrote:
> On Fri, Nov 21, 2025 at 10:23:09PM +0000, Eric Biggers wrote:
> > That list actually includes the same three files that use -EKEYREJECTED.
> > It looks like if the signature verification fails "early" it's -EBADMSG,
> > whereas if it fails "late" it's -EKEYREJECTED?
>
> -EBADMSG denotes malformed data (e.g. incorrectly formatted ASN.1 payload).
>
> -EKEYREJECTED denotes a well-formed, but incorrect signature (e.g. made
> by a wrong key).
>
> I think it's important and useful to be able to differentiate that.
I guess. The pseudocode in the ML-DSA specification is clear that
signature verification returns a boolean, regardless of whether the
signature is invalid due to the ctilde check, the coefficients of the
reponse vector being out of range, or the encoded hint vector being
malformed. But if we really think it's useful we could disregard that
and use EKEYREJECTED for the ctilde check and EBADMSG for the other
cases. I think that would align with what you're suggesting. This is
inconsistent with the kernel's symmetric crypto code, but oh well.
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Lukas Wunner @ 2025-11-21 22:29 UTC (permalink / raw)
To: Eric Biggers
Cc: David Howells, linux-crypto, Herbert Xu, Luis Chamberlain,
Petr Pavlu, Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld,
Ard Biesheuvel, Stephan Mueller, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <20251121222309.GA3300186@google.com>
On Fri, Nov 21, 2025 at 10:23:09PM +0000, Eric Biggers wrote:
> That list actually includes the same three files that use -EKEYREJECTED.
> It looks like if the signature verification fails "early" it's -EBADMSG,
> whereas if it fails "late" it's -EKEYREJECTED?
-EBADMSG denotes malformed data (e.g. incorrectly formatted ASN.1 payload).
-EKEYREJECTED denotes a well-formed, but incorrect signature (e.g. made
by a wrong key).
I think it's important and useful to be able to differentiate that.
Thanks,
Lukas
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Eric Biggers @ 2025-11-21 22:23 UTC (permalink / raw)
To: David Howells
Cc: linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <3067069.1763761171@warthog.procyon.org.uk>
On Fri, Nov 21, 2025 at 09:39:31PM +0000, David Howells wrote:
> Eric Biggers <ebiggers@kernel.org> wrote:
>
> > > > + if (memcmp(ws->ctildeprime, ctilde, params->ctilde_len) != 0)
> > > > + return -EBADMSG;
> > >
> > > Actually, this should return -EKEYREJECTED, not -EBADMSG.
> >
> > Who/what decided that?
>
> I did. When I added RSA support in 2012 for module signing. Note that it
> was originally added as part of crypto/asymmetric_keys/ and was not covered by
> a crypto API. The RSA code has since been moved to crypto/ and is now
> accessed through the crypto API, but it has retained this error code and this
> is also used by other public key algos.
>
> > A lot of the crypto code uses -EBADMSG already.
> > crypto_aead uses it, for example.
>
> ecdsa.c:60: return -EKEYREJECTED;
> ecrdsa.c:111: return -EKEYREJECTED;
> ecrdsa.c:139: return -EKEYREJECTED;
> ecrdsa.c:239: return -EKEYREJECTED;
> rsassa-pkcs1.c:293: return -EKEYREJECTED;
> rsassa-pkcs1.c:295: return -EKEYREJECTED;
crypto/aegis128-core.c:442: return -EBADMSG;
crypto/aegis128-core.c:499: return -EBADMSG;
crypto/algif_aead.c:313: if (err == -EIOCBQUEUED || err == -EBADMSG || !ret)
crypto/authenc.c:223: return -EBADMSG;
crypto/authencesn.c:220: return -EBADMSG;
crypto/ccm.c:336: err = -EBADMSG;
crypto/ccm.c:384: return -EBADMSG;
crypto/chacha20poly1305.c:90: return -EBADMSG;
crypto/dh.c:207: ret = -EBADMSG;
crypto/dh.c:221: ret = -EBADMSG;
crypto/dh.c:242: ret = -EBADMSG;
crypto/ecdsa.c:37: return -EBADMSG;
crypto/ecrdsa.c:101: return -EBADMSG;
crypto/gcm.c:471: return crypto_memneq(iauth_tag, auth_tag, authsize) ? -EBADMSG : 0;
crypto/krb5enc.c:259: return -EBADMSG;
crypto/rsa.c:150: ret = -EBADMSG;
crypto/rsa.c:189: ret = -EBADMSG;
crypto/rsassa-pkcs1.c:275: return -EBADMSG;
crypto/rsassa-pkcs1.c:282: return -EBADMSG;
crypto/rsassa-pkcs1.c:286: return -EBADMSG;
crypto/rsassa-pkcs1.c:288: return -EBADMSG;
crypto/testmgr.c:90: * algorithm might result in EINVAL rather than EBADMSG, due to other
crypto/testmgr.c:2179: (err != vec->crypt_error && !(err == -EBADMSG && vec->novrfy))) {
crypto/testmgr.c:2183: vec->crypt_error != 0 && vec->crypt_error != -EBADMSG)
crypto/testmgr.c:2184: sprintf(expected_error, "-EBADMSG or %d",
crypto/testmgr.c:2187: sprintf(expected_error, "-EBADMSG");
include/crypto/aead.h:37: * operation is that the caller should explicitly check for -EBADMSG of the
include/crypto/aead.h:39: * a breach in the integrity of the message. In essence, that -EBADMSG error
include/crypto/aead.h:375: * Return: 0 if the cipher operation was successful; -EBADMSG: The AEAD
That list actually includes the same three files that use -EKEYREJECTED.
It looks like if the signature verification fails "early" it's -EBADMSG,
whereas if it fails "late" it's -EKEYREJECTED? I'm skeptical that
that's a meaningful difference. And it's not like this is documented
either; crypto_sig_verify() just says "error code in case of error".
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: David Howells @ 2025-11-21 21:39 UTC (permalink / raw)
To: Eric Biggers
Cc: dhowells, linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <20251121005017.GD3532564@google.com>
Eric Biggers <ebiggers@kernel.org> wrote:
> > > + if (memcmp(ws->ctildeprime, ctilde, params->ctilde_len) != 0)
> > > + return -EBADMSG;
> >
> > Actually, this should return -EKEYREJECTED, not -EBADMSG.
>
> Who/what decided that?
I did. When I added RSA support in 2012 for module signing. Note that it
was originally added as part of crypto/asymmetric_keys/ and was not covered by
a crypto API. The RSA code has since been moved to crypto/ and is now
accessed through the crypto API, but it has retained this error code and this
is also used by other public key algos.
> A lot of the crypto code uses -EBADMSG already.
> crypto_aead uses it, for example.
ecdsa.c:60: return -EKEYREJECTED;
ecrdsa.c:111: return -EKEYREJECTED;
ecrdsa.c:139: return -EKEYREJECTED;
ecrdsa.c:239: return -EKEYREJECTED;
rsassa-pkcs1.c:293: return -EKEYREJECTED;
rsassa-pkcs1.c:295: return -EKEYREJECTED;
David
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: David Howells @ 2025-11-21 17:41 UTC (permalink / raw)
To: Eric Biggers
Cc: dhowells, linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <20251121171421.GA1737@sol>
Eric Biggers <ebiggers@kernel.org> wrote:
> I could write it another way that wouldn't assume familiarity with open
> interval notation, like [0, q - 1] or 0 <= val < q.
"[0, q-1]" would be less prone to confusion, thanks - and editors flagging the
bracket mismatch.
David
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Eric Biggers @ 2025-11-21 17:14 UTC (permalink / raw)
To: David Howells
Cc: linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <2755899.1763728901@warthog.procyon.org.uk>
On Fri, Nov 21, 2025 at 12:41:41PM +0000, David Howells wrote:
> Eric Biggers <ebiggers@kernel.org> wrote:
>
> > On Thu, Nov 20, 2025 at 01:55:18PM +0000, David Howells wrote:
> > > Eric Biggers <ebiggers@kernel.org> wrote:
> > >
> > > > + /* Compute d = (c mod 2^32) * (q^-1 mod 2^32). */
> > > > + s32 d = (s32)c * QINV_MOD_R;
> > >
> > > Hmmm... is "(s32)c" actually "(c mod 2^32)"? Should that be:
> > >
> > > u32 d = (u32)c * QINV_MOD_R;
> > >
> > > This is followed up by casting 'd' to "s64". I don't think that should
> > > sign-extend it, but...
> >
> > It selects the representative in the range [INT32_MIN, INT32_MAX],
> > rather than the representative in the range [0, UINT32_MAX]. The sign
> > extension is intentional.
>
> I'm concerned about the basis on which it becomes positive or negative. It
> looks like the sign bit ends up being chosen arbitrarily.
Right, it's unrelated to the sign of the s64 value, unless the s64 value
happens to fit in a s32. And that's okay: the worst-case analysis,
which considers the largest possible absolute value that can be built
up, assumes the signs happen to match repeatedly.
By the way, lest you think I'd doing anything particularly novel here,
the Dilithium reference code also uses this same (very-nearly-symmetric)
Montgomery reduction formula including the sign extension, where it made
its way into leancrypto and your patchset too. It also appears in FIPS
204 as Algorithm 49, MontgomeryReduce. There are other ways to
implement this stuff, but they would be less efficient.
However, unfortunately neither source explains it properly, and they
actually provide incorrect information. The comment in the reference
code says the the input can be in "-2^{31}Q <= a <= Q*2^31", which isn't
quite correct; the upper bound is actually exclusive. In my code, I
correctly document the upper bound as being exclusive.
FIPS 204 documents the same incorrect interval, but then sort of gets
around it by only claiming that the output is less than 2q in absolute
value (rather than q) and also by not clarifying whether sign extension
is done. They may have thought that sign extension shouldn't be done,
as you seem to have thought. Either way, their explanation is
misleading. The very-nearly-symmetric version that produces an output
less than q in absolute value is the logical version when working with
signed values, and it seems to be what the Dilithium authors intended.
Anyway, it's clear that my code comments still didn't explain it
properly either, so I'll work on that.
> > > > + /* Reduce to [0, q), then tmp = w'_1 = UseHint(h, w'_Approx) */
> > >
> > > Bracket mismatch. "[0, q]"
> >
> > It's intentional, since it denotes a mathematical range. Elsewhere I
> > used the words "the range" explicitly, so I'll add that above too. (Or
> > maybe reword it differently.)
>
> I meant you have an opening square bracket and a closing round bracket in
> "[0, q)".
That means the lower end is inclusive and the upper end is exclusive.
We're taking mod q, so we do *not* want q to be included.
I could write it another way that wouldn't assume familiarity with open
interval notation, like [0, q - 1] or 0 <= val < q.
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: David Howells @ 2025-11-21 12:41 UTC (permalink / raw)
To: Eric Biggers
Cc: dhowells, linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <20251121005017.GD3532564@google.com>
Eric Biggers <ebiggers@kernel.org> wrote:
> On Thu, Nov 20, 2025 at 01:55:18PM +0000, David Howells wrote:
> > Eric Biggers <ebiggers@kernel.org> wrote:
> >
> > > + /* Compute d = (c mod 2^32) * (q^-1 mod 2^32). */
> > > + s32 d = (s32)c * QINV_MOD_R;
> >
> > Hmmm... is "(s32)c" actually "(c mod 2^32)"? Should that be:
> >
> > u32 d = (u32)c * QINV_MOD_R;
> >
> > This is followed up by casting 'd' to "s64". I don't think that should
> > sign-extend it, but...
>
> It selects the representative in the range [INT32_MIN, INT32_MAX],
> rather than the representative in the range [0, UINT32_MAX]. The sign
> extension is intentional.
I'm concerned about the basis on which it becomes positive or negative. It
looks like the sign bit ends up being chosen arbitrarily.
> > > + /* Reduce to [0, q), then tmp = w'_1 = UseHint(h, w'_Approx) */
> >
> > Bracket mismatch. "[0, q]"
>
> It's intentional, since it denotes a mathematical range. Elsewhere I
> used the words "the range" explicitly, so I'll add that above too. (Or
> maybe reword it differently.)
I meant you have an opening square bracket and a closing round bracket in
"[0, q)".
David
^ permalink raw reply
* Re: [PATCH 0/4] lib/crypto: ML-DSA verification support
From: Eric Biggers @ 2025-11-21 6:16 UTC (permalink / raw)
To: David Howells
Cc: linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <2528923.1763626276@warthog.procyon.org.uk>
On Thu, Nov 20, 2025 at 08:11:16AM +0000, David Howells wrote:
> Note that your emailed patches seem corrupt somehow, but I can fetch the git
> branch.
Works for me:
git checkout -d 10a1140107e0b98bd67d37ae7af72989dd7df00b
b4 am 20251120003653.335863-1-ebiggers@kernel.org
git am ./20251119_ebiggers_lib_crypto_ml_dsa_verification_support.mbx
10a1140107e0b98bd67d37ae7af72989dd7df00b is the base-commit listed in
the cover letter, via the --base option to 'git format-patch'. It's the
current head of libcrypto-next and is also in linux-next. Maybe you
tried applying the series to a different commit.
Anyway, using the git repo is fine too. This sort of stuff is exactly
why I usually provide a git link too.
- Eric
^ permalink raw reply
* Re: Pick up keys-pqc branch for linux-next?
From: Eric Biggers @ 2025-11-21 2:48 UTC (permalink / raw)
To: Stephen Rothwell
Cc: David Howells, Mark Brown, Herbert Xu, linux-crypto, keyrings,
linux-modules, linux-next, linux-kernel
In-Reply-To: <20251120205341.7e28927b@canb.auug.org.au>
On Thu, Nov 20, 2025 at 08:53:41PM +1100, Stephen Rothwell wrote:
> Hi David,
>
> On Mon, 17 Nov 2025 09:11:26 -0800 Eric Biggers <ebiggers@kernel.org> wrote:
> >
> > On Mon, Nov 17, 2025 at 03:22:03PM +0000, David Howells wrote:
> > >
> > > Can you pick up my keys-pqc branch for linux-next please? It can be found at:
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/
> > > keys-pqc
> > >
> > > Note that it's based on Eric Bigger's libcrypto/lbcrypto-next branch which I
> > > believe you already have in order to get SHA-3/SHAKE support.
> >
> > I don't really see the point yet, since this isn't going to be ready for
> > the next merge window anyway.
>
> Yeah, if it is not going into the next merge window, then I don't want
> it until after that merge window closes, OK?
Makes sense to me. David, with
https://lore.kernel.org/r/20251120003653.335863-2-ebiggers@kernel.org
we're getting closer, but it's still too late for 6.19. We need proper
tests to be added at the same time as the ML-DSA implementation itself.
The higher-level parts such as crypto/asymmetric_keys/ need review too.
Also, lib/crypto/ changes should go through the libcrypto tree.
wycheproof has negative test vectors for ML-DSA edge cases. We maybe
could borrow those. Note that ML-DSA keys and signatures are very
large, which results in huge files for the test vectors. I'd also like
to keep the size down, but we might not have much choice there.
I'm also working to get another crypto-oriented developer, who doesn't
normally work on the Linux kernel, to review my ML-DSA code. (I don't
think I was going to have any chance at getting anyone to look at the
original 4800-line submission, but with the 600-line one it's feasible.)
It's also worth noting that this is ML-DSA, not ML-KEM. The
cryptographic community generally views upgrading to quantum-resistant
key encapsulation (e.g. ML-KEM) as more urgent than upgrading to
quantum-resistant signatures (e.g. ML-DSA). I assume you have a reason
why you want the signatures. That's fine, and I'm okay with ML-DSA
support being added. But we shouldn't rush it in.
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Eric Biggers @ 2025-11-21 2:15 UTC (permalink / raw)
To: David Howells
Cc: linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <2534431.1763626496@warthog.procyon.org.uk>
On Thu, Nov 20, 2025 at 08:14:56AM +0000, David Howells wrote:
> You need to add something like:
>
> #include <linux/module.h>
> ...
> MODULE_DESCRIPTION("ML-DSA signature verification");
> MODULE_LICENSE("GPL");
>
> David
Yep. Sorry, I usually build my test kernels with CONFIG_MODULES=n
because it's easier.
- Eric
^ permalink raw reply
* Re: [PATCH v9 2/9] crypto: Add ML-DSA/Dilithium verify support
From: Eric Biggers @ 2025-11-21 1:37 UTC (permalink / raw)
To: David Howells
Cc: Herbert Xu, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251117145606.2155773-3-dhowells@redhat.com>
On Mon, Nov 17, 2025 at 02:55:51PM +0000, David Howells wrote:
> +/*
> + * @brief poly_uniform - Sample polynomial with uniformly random coefficients
> + * in [0,Q-1] by performing rejection sampling on the
> + * output stream of SHAKE128(seed|nonce).
> + *
> + * @param [out] a pointer to output polynomial
> + * @param [in] seed byte array with seed of length DILITHIUM_SEEDBYTES
> + * @param [in] nonce 2-byte nonce
> + */
> +void poly_uniform(poly *a, const uint8_t seed[DILITHIUM_SEEDBYTES],
> + __le16 nonce, void *ws_buf)
> +{
> + struct shake_ctx hash_ctx;
> + unsigned int i, ctr, off;
> + unsigned int buflen = POLY_UNIFORM_NBLOCKS * SHAKE128_BLOCK_SIZE;
> + uint8_t *buf = ws_buf;
> +
> + shake128_init(&hash_ctx);
> + shake_update(&hash_ctx, seed, DILITHIUM_SEEDBYTES);
> + shake_update(&hash_ctx, (uint8_t *)&nonce, sizeof(nonce));
> + shake_squeeze(&hash_ctx, buf, buflen);
> +
> + ctr = rej_uniform(a->coeffs, DILITHIUM_N, buf, buflen);
> +
> + while (ctr < DILITHIUM_N) {
> + off = buflen % 3;
> + for (i = 0; i < off; ++i)
> + buf[i] = buf[buflen - off + i];
> +
> + shake_squeeze(&hash_ctx, buf + off, SHAKE128_BLOCK_SIZE);
> + buflen = DILITHIUM_SEEDBYTES + off;
> + ctr += rej_uniform(a->coeffs + ctr, DILITHIUM_N - ctr, buf,
> + buflen);
> + }
> +
> + shake_zeroize_ctx(&hash_ctx);
> +}
By the way, the above has a bug. In the second and later squeezes, it
squeezes SHAKE128_BLOCK_SIZE (168) bytes, but then it uses only the
first DILITHIUM_SEEDBYTES (32) bytes.
Now, that 32 is on top of the 840-byte first squeeze, so there are 872
correct bytes which is enough for 290 samples. So an incorrect matrix
would be generated only if more than 290 samples happen to be required
to get the 256 coefficients. q / 2^23 = ~99.9% of coefficients are
accepted, so that number of rejections would be pretty unlikely.
Still, it's a bug. Anyway, we're not going to use this code (we'll use
my code that does this correctly and in a simpler way), but I thought
I'd point it out so that Stephan can fix it. This seems to be a
"leancrypto" specific bug.
Note: this feedback should not be taken as implying that I've reviewed
the entire 4800 lines of code. I just happened to notice this.
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Eric Biggers @ 2025-11-21 0:50 UTC (permalink / raw)
To: David Howells
Cc: linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <2624664.1763646918@warthog.procyon.org.uk>
On Thu, Nov 20, 2025 at 01:55:18PM +0000, David Howells wrote:
> Eric Biggers <ebiggers@kernel.org> wrote:
>
> > + /* Compute d = (c mod 2^32) * (q^-1 mod 2^32). */
> > + s32 d = (s32)c * QINV_MOD_R;
>
> Hmmm... is "(s32)c" actually "(c mod 2^32)"? Should that be:
>
> u32 d = (u32)c * QINV_MOD_R;
>
> This is followed up by casting 'd' to "s64". I don't think that should
> sign-extend it, but...
It selects the representative in the range [INT32_MIN, INT32_MAX],
rather than the representative in the range [0, UINT32_MAX]. The sign
extension is intentional. This makes the reduction more symmetric so
that the range of supported unreduced products is roughly symmetric.
I'll update the comments to clarify this.
> > + for (int m = 0, len = 128; len >= 1; len /= 2) {
>
> Can you put "int m = 0" outside of the for-statement? I know putting it
> inside saves a line or two, but 'm' is not the loop counter - which it seems
> like it should be by virtue of being listed first.
>
> > + for (int m = 256, len = 1; len < 256; len *= 2) {
>
> Ditto.
Sure.
>
> > +static const u8 *decode_t1_elem(struct mldsa_ring_elem *out,
> > + const u8 *t1_encoded)
>
> I think this is (more or less) pkDecode()? Can you put something like:
>
> * Decode the vector 't1' from the public key.
> * Reference: FIPS 204 Algorithm 23, sigDecode.
>
> in the comment before it?
Sure.
> > +/*
> > + * Use @seed to generate a ring element @c with coefficients in {-1, 0, 1},
> > + * exactly @tau of them nonzero. Reference: FIPS 204 Algorithm 29, SampleInBall
> > + */
> > +static void sample_in_ball(struct mldsa_ring_elem *c, const u8 *seed,
> > + size_t seed_len, int tau, struct shake_ctx *shake)
>
> Should "seed" actually be labelled "rho"? I know a seed is what it is, but
> the algo description has a different label - and the caller passes it ctilde,
> not rho:-/.
FIPS 204 Algorithm 29 SampleInBall uses the variable rho for the seed,
while also calling it a "seed" in the descriptive text. However,
elsewhere rho refers specifically to the public key's random seed. I
think just calling it "seed" makes sense here.
> > + u8 (*h)[N]; /* The signer's hint vector, length k */
> > + h = (u8 (*)[N])&ws->z[l];
>
> C is weird sometimes.
We could make it a 'u8 *', but then we'd have to use array indices like
h[i*k + j] rather than h[i][j]. May be worth it anyway, to avoid the
slightly-unusual syntax.
> > + /* Reduce to [0, q), then tmp = w'_1 = UseHint(h, w'_Approx) */
>
> Bracket mismatch. "[0, q]"
It's intentional, since it denotes a mathematical range. Elsewhere I
used the words "the range" explicitly, so I'll add that above too. (Or
maybe reword it differently.)
>
> > + /* w1Encode(w'_1) */
> > + w1_pos = 0;
> > ...
>
> Given you put the decode functions into helpers, don't you want to do that
> with this?
Sure, I'll move the w1Encode part into a helper function.
> > + if (memcmp(ws->ctildeprime, ctilde, params->ctilde_len) != 0)
> > + return -EBADMSG;
>
> Actually, this should return -EKEYREJECTED, not -EBADMSG.
Who/what decided that? A lot of the crypto code uses -EBADMSG already.
crypto_aead uses it, for example.
> I guess you don't need to use crypto_memneq() as timing doesn't matter.
Correct.
> The maths look okay, I think. You can add:
>
> Reviewed-by: David Howells <dhowells@redhat.com>
Thanks,
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: Eric Biggers @ 2025-11-21 0:09 UTC (permalink / raw)
To: David Howells
Cc: linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <2590973.1763629800@warthog.procyon.org.uk>
On Thu, Nov 20, 2025 at 09:10:00AM +0000, David Howells wrote:
> Eric Biggers <ebiggers@kernel.org> wrote:
>
> > - Is about 600 lines of source code instead of 4800.
>
> There's less shareable code for other algos that I'm sure people are going to
> ask for, but that's probably fine.
The "advanced" verification features that people could conceivably want
in the future (public key preloading, nonempty contexts, HashML-DSA,
external mu, incremental message hashing) would all be fairly
straightforward to add, in the event that that they ever become needed.
Signing support would of course be challenging. But that's expected,
and we should try to keep that out of the kernel anyway.
> > - Generates about 4 KB of object code instead of 28 KB.
> > - Uses 9-13 KB of memory to verify a signature instead of 31-84 KB.
>
> That's definitely good.
>
> > - Is 3-5% faster, depending on the ML-DSA parameter set.
>
> That's not quite what I see. For Leancrypto:
>
> # benchmark_mldsa44: 8672 ops/s
> # benchmark_mldsa65: 5470 ops/s
> # benchmark_mldsa87: 3350 ops/s
>
> For your implementation:
>
> # benchmark_mldsa44: 8707 ops/s
> # benchmark_mldsa65: 5423 ops/s
> # benchmark_mldsa87: 3352 ops/s
>
> This may reflect differences in CPU (mine's an i3-4170).
>
> The numbers are pretty stable with the cpu frequency governor set to
> performance and without rebooting betweentimes.
>
> Interesting that your mldsa44 is consistently faster, but your mldsa65 is
> consistently slower. mldsa87 is consistently about the same.
>
> I don't think the time differences are particularly significant.
Sure, I had just tested one CPU. Slightly different results on
different CPUs are expected. It's also expected that the ops/s for
verification in a loop is still in roughly the same ballpark as your
integration of leancrypto (or the Dilithium reference code which
leancrypto seems to be based on, for that matter). There aren't too
many ways to implement the most time-consuming parts. Generally,
arch-optimized code would be needed to do significantly better.
Of course, the greatly reduced icache and dcache usage is much more
important for performance. But that doesn't show up in the "just verify
the same signature in a loop repeatedly" benchmark.
I'll clarify that part of the commit message accordingly.
- Eric
^ permalink raw reply
* Re: [PATCH 1/4] lib/crypto: Add ML-DSA verification support
From: David Howells @ 2025-11-20 13:55 UTC (permalink / raw)
To: Eric Biggers
Cc: dhowells, linux-crypto, Herbert Xu, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, keyrings,
linux-modules, linux-kernel
In-Reply-To: <20251120003653.335863-2-ebiggers@kernel.org>
Eric Biggers <ebiggers@kernel.org> wrote:
> + /* Compute d = (c mod 2^32) * (q^-1 mod 2^32). */
> + s32 d = (s32)c * QINV_MOD_R;
Hmmm... is "(s32)c" actually "(c mod 2^32)"? Should that be:
u32 d = (u32)c * QINV_MOD_R;
This is followed up by casting 'd' to "s64". I don't think that should
sign-extend it, but...
> + for (int m = 0, len = 128; len >= 1; len /= 2) {
Can you put "int m = 0" outside of the for-statement? I know putting it
inside saves a line or two, but 'm' is not the loop counter - which it seems
like it should be by virtue of being listed first.
> + for (int m = 256, len = 1; len < 256; len *= 2) {
Ditto.
> +static const u8 *decode_t1_elem(struct mldsa_ring_elem *out,
> + const u8 *t1_encoded)
I think this is (more or less) pkDecode()? Can you put something like:
* Decode the vector 't1' from the public key.
* Reference: FIPS 204 Algorithm 23, sigDecode.
in the comment before it?
> +/*
> + * Use @seed to generate a ring element @c with coefficients in {-1, 0, 1},
> + * exactly @tau of them nonzero. Reference: FIPS 204 Algorithm 29, SampleInBall
> + */
> +static void sample_in_ball(struct mldsa_ring_elem *c, const u8 *seed,
> + size_t seed_len, int tau, struct shake_ctx *shake)
Should "seed" actually be labelled "rho"? I know a seed is what it is, but
the algo description has a different label - and the caller passes it ctilde,
not rho:-/.
> + u8 (*h)[N]; /* The signer's hint vector, length k */
> + h = (u8 (*)[N])&ws->z[l];
C is weird sometimes.
> + /* Reduce to [0, q), then tmp = w'_1 = UseHint(h, w'_Approx) */
Bracket mismatch. "[0, q]"
> + /* w1Encode(w'_1) */
> + w1_pos = 0;
> ...
Given you put the decode functions into helpers, don't you want to do that
with this?
> + if (memcmp(ws->ctildeprime, ctilde, params->ctilde_len) != 0)
> + return -EBADMSG;
Actually, this should return -EKEYREJECTED, not -EBADMSG.
I guess you don't need to use crypto_memneq() as timing doesn't matter.
The maths look okay, I think. You can add:
Reviewed-by: David Howells <dhowells@redhat.com>
David
^ permalink raw reply
* Re: [PATCH] module/decompress: Avoid open-coded kvrealloc()
From: Petr Pavlu @ 2025-11-20 12:31 UTC (permalink / raw)
To: Kees Cook
Cc: Luis Chamberlain, Sami Tolvanen, Daniel Gomez, linux-modules,
Daniel Gomez, linux-kernel, linux-hardening
In-Reply-To: <20251119225439.work.741-kees@kernel.org>
On 11/19/25 11:54 PM, Kees Cook wrote:
> Replace open-coded allocate/copy with kvrealloc().
>
> Signed-off-by: Kees Cook <kees@kernel.org>
> ---
> Cc: Petr Pavlu <petr.pavlu@suse.com>
> Cc: Luis Chamberlain <mcgrof@kernel.org>
> Cc: Sami Tolvanen <samitolvanen@google.com>
> Cc: Daniel Gomez <da.gomez@samsung.com>
> Cc: <linux-modules@vger.kernel.org>
> ---
> kernel/module/decompress.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/module/decompress.c b/kernel/module/decompress.c
> index 474e68f0f063..36f52a232a12 100644
> --- a/kernel/module/decompress.c
> +++ b/kernel/module/decompress.c
> @@ -17,16 +17,16 @@
> static int module_extend_max_pages(struct load_info *info, unsigned int extent)
> {
> struct page **new_pages;
> + unsigned int new_max = info->max_pages + extent;
>
> - new_pages = kvmalloc_array(info->max_pages + extent,
> - sizeof(info->pages), GFP_KERNEL);
> + new_pages = kvrealloc(info->pages,
> + size_mul(new_max, sizeof(*info->pages)),
> + GFP_KERNEL);
Nit: Using the array_size() alias of size_mul() might be clearer in this
context.
It looks ok to me in any case.
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
--
Thanks,
Petr
^ permalink raw reply
* [PATCH v10 8/8] modsign: Enable ML-DSA module signing
From: David Howells @ 2025-11-20 10:44 UTC (permalink / raw)
To: Herbert Xu
Cc: David Howells, Eric Biggers, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251120104439.2620205-1-dhowells@redhat.com>
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 <dhowells@redhat.com>
cc: Eric Biggers <ebiggers@kernel.org>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Stephan Mueller <smueller@chronox.de>
cc: Herbert Xu <herbert@gondor.apana.org.au>
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/admin-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.
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 (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.
==========================
@@ -146,9 +147,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.
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.
+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
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 += 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 \
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 != 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");
#ifndef USE_PKCS7
+
+ unsigned int flags =
+ 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 |= 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
^ permalink raw reply related
* [PATCH v10 7/8] pkcs7, x509: Add ML-DSA support
From: David Howells @ 2025-11-20 10:44 UTC (permalink / raw)
To: Herbert Xu
Cc: David Howells, Eric Biggers, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251120104439.2620205-1-dhowells@redhat.com>
Add support for ML-DSA keys and signatures to the PKCS#7 and X.509
implementations.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Stephan Mueller <smueller@chronox.de>
cc: Eric Biggers <ebiggers@kernel.org>
cc: Herbert Xu <herbert@gondor.apana.org.au>
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 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_does_hash = true;
+ break;
+ case OID_id_ml_dsa_65:
+ ctx->sinfo->sig->pkey_algo = "mldsa65";
+ ctx->sinfo->sig->encoding = "raw";
+ ctx->sinfo->sig->algo_does_hash = true;
+ break;
+ case OID_id_ml_dsa_87:
+ ctx->sinfo->sig->pkey_algo = "mldsa87";
+ ctx->sinfo->sig->encoding = "raw";
+ ctx->sinfo->sig->algo_does_hash = 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/public_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_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;
} 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 8df3fa60a44f..d14584eca452 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 @@ int x509_note_sig_algo(void *context, size_t hdrlen, unsigned char tag,
ctx->cert->sig->encoding = "x962";
ctx->sig_algo = ctx->last_oid;
return 0;
+ml_dsa:
+ ctx->cert->sig->algo_does_hash = true;
+ ctx->cert->sig->hash_algo = ctx->cert->sig->pkey_algo;
+ ctx->cert->sig->encoding = "raw";
+ ctx->sig_algo = ctx->last_oid;
+ return 0;
}
/*
@@ -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 = "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/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 */
+ /* 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
};
^ permalink raw reply related
* [PATCH v10 6/8] pkcs7: Allow the signing algo to calculate the digest itself
From: David Howells @ 2025-11-20 10:44 UTC (permalink / raw)
To: Herbert Xu
Cc: David Howells, Eric Biggers, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251120104439.2620205-1-dhowells@redhat.com>
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 <dhowells@redhat.com>
cc: Lukas Wunner <lukas@wunner.de>
cc: Ignat Korchagin <ignat@cloudflare.com>
cc: Stephan Mueller <smueller@chronox.de>
cc: Eric Biggers <ebiggers@kernel.org>
cc: Herbert Xu <herbert@gondor.apana.org.au>
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,
}
/* 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_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 = -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 = 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;
+ if (sig->algo_does_hash) {
+ kfree(sig->digest);
+
+ ret = -ENOMEM;
+ sig->digest = kmalloc(umax(sinfo->authattrs_len, sig->digest_size),
+ GFP_KERNEL);
+ if (!sig->digest)
+ goto error_no_desc;
+
+ sig->digest_size = sinfo->authattrs_len;
+ memcpy(sig->digest, sinfo->authattrs, sinfo->authattrs_len);
+ ((u8 *)sig->digest)[0] = ASN1_CONS_BIT | ASN1_SET;
+ ret = 0;
+ } else {
+ u8 tag = ASN1_CONS_BIT | ASN1_SET;
+
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+ ret = crypto_shash_update(desc, &tag, 1);
+ if (ret < 0)
+ goto error;
+ ret = crypto_shash_finup(desc, sinfo->authattrs + 1,
+ sinfo->authattrs_len - 1,
+ sig->digest);
+ if (ret < 0)
+ goto error;
+ }
pr_devel("AADigest = [%*ph]\n", 8, sig->digest);
}
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;
^ permalink raw reply related
* [PATCH v10 2/8] lib/crypto: tests: Add KUnit tests for ML-DSA
From: David Howells @ 2025-11-20 10:44 UTC (permalink / raw)
To: Herbert Xu
Cc: David Howells, Eric Biggers, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251120104439.2620205-1-dhowells@redhat.com>
Add a KUnit test suite for ML-DSA, including:
- The ML-DSA-44 "Pure rejection tests" from Stephan Mueller's Leancrypto
- An ML-DSA-44 benchmark
Later patches will add test cases for ML-DSA-65 and ML-DSA-87.
(This needs some work. For one, there are no negative tests yet.)
Signed-off-by: David Howells <dhowells@redhat.com>
Co-developed-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: David Howells <dhowells@redhat.com>
---
lib/crypto/tests/Kconfig | 10 +
lib/crypto/tests/Makefile | 1 +
lib/crypto/tests/mldsa_kunit.c | 92 ++++
.../tests/mldsa_pure_rejection_vectors_44.h | 489 ++++++++++++++++++
4 files changed, 592 insertions(+)
create mode 100644 lib/crypto/tests/mldsa_kunit.c
create mode 100644 lib/crypto/tests/mldsa_pure_rejection_vectors_44.h
diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig
index 61d435c450bb..214c23d994e6 100644
--- a/lib/crypto/tests/Kconfig
+++ b/lib/crypto/tests/Kconfig
@@ -38,6 +38,16 @@ config CRYPTO_LIB_MD5_KUNIT_TEST
KUnit tests for the MD5 cryptographic hash function and its
corresponding HMAC.
+config CRYPTO_LIB_MLDSA_KUNIT_TEST
+ tristate "KUnit tests for ML-DSA" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS
+ select CRYPTO_LIB_BENCHMARK_VISIBLE
+ select CRYPTO_LIB_MLDSA
+ help
+ KUnit tests for ML-DSA (Module-Lattice-Based Digital Signature
+ Algorithm).
+
config CRYPTO_LIB_POLY1305_KUNIT_TEST
tristate "KUnit tests for Poly1305" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/crypto/tests/Makefile b/lib/crypto/tests/Makefile
index 5109a0651925..3dde0b40b2c5 100644
--- a/lib/crypto/tests/Makefile
+++ b/lib/crypto/tests/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_CRYPTO_LIB_BLAKE2B_KUNIT_TEST) += blake2b_kunit.o
obj-$(CONFIG_CRYPTO_LIB_BLAKE2S_KUNIT_TEST) += blake2s_kunit.o
obj-$(CONFIG_CRYPTO_LIB_CURVE25519_KUNIT_TEST) += curve25519_kunit.o
obj-$(CONFIG_CRYPTO_LIB_MD5_KUNIT_TEST) += md5_kunit.o
+obj-$(CONFIG_CRYPTO_LIB_MLDSA_KUNIT_TEST) += mldsa_kunit.o
obj-$(CONFIG_CRYPTO_LIB_POLY1305_KUNIT_TEST) += poly1305_kunit.o
obj-$(CONFIG_CRYPTO_LIB_POLYVAL_KUNIT_TEST) += polyval_kunit.o
obj-$(CONFIG_CRYPTO_LIB_SHA1_KUNIT_TEST) += sha1_kunit.o
diff --git a/lib/crypto/tests/mldsa_kunit.c b/lib/crypto/tests/mldsa_kunit.c
new file mode 100644
index 000000000000..836555bd0096
--- /dev/null
+++ b/lib/crypto/tests/mldsa_kunit.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <crypto/mldsa.h>
+#include <kunit/test.h>
+
+struct mldsa_testvector {
+ u16 pk_len;
+ u16 msg_len;
+ u16 sig_len;
+ const char *what;
+ const char *algo;
+ const u8 *pk;
+ const u8 *sig;
+ const u8 *msg;
+};
+
+/*
+ * Use rejection test vector which will cover all rejection code paths
+ * as generated with the mldsa_edge_case_tester.
+ */
+static const struct mldsa_testvector mldsa_44_testvectors[] = {
+#include "mldsa_pure_rejection_vectors_44.h"
+};
+
+static void do_mldsa_and_assert_success(struct kunit *test, enum mldsa_alg alg,
+ const struct mldsa_testvector *tv)
+{
+ int err = mldsa_verify(alg, tv->sig, tv->sig_len, tv->msg, tv->msg_len,
+ tv->pk, tv->pk_len);
+ KUNIT_ASSERT_EQ(test, err, 0);
+}
+
+static void test_mldsa(struct kunit *test, enum mldsa_alg alg,
+ const struct mldsa_testvector *tvs, size_t num_tvs)
+{
+ for (size_t i = 0; i < num_tvs; i++)
+ do_mldsa_and_assert_success(test, alg, &tvs[i]);
+}
+
+static void test_mldsa44(struct kunit *test)
+{
+ test_mldsa(test, MLDSA44, mldsa_44_testvectors,
+ ARRAY_SIZE(mldsa_44_testvectors));
+}
+
+static void benchmark_mldsa(struct kunit *test, enum mldsa_alg alg,
+ const struct mldsa_testvector *tv)
+{
+ const int warmup_niter = 200;
+ const int benchmark_niter = 200;
+ u64 t0, t1;
+
+ if (!IS_ENABLED(CONFIG_CRYPTO_LIB_BENCHMARK))
+ kunit_skip(test, "not enabled");
+
+ /* Warm-up */
+ for (int i = 0; i < warmup_niter; i++)
+ do_mldsa_and_assert_success(test, alg, tv);
+
+ t0 = ktime_get_ns();
+ for (int i = 0; i < benchmark_niter; i++)
+ do_mldsa_and_assert_success(test, alg, tv);
+ t1 = ktime_get_ns();
+ kunit_info(test, "%llu ops/s",
+ div64_u64((u64)benchmark_niter * NSEC_PER_SEC,
+ t1 - t0 ?: 1));
+}
+
+static void benchmark_mldsa44(struct kunit *test)
+{
+ benchmark_mldsa(test, MLDSA44, &mldsa_44_testvectors[0]);
+}
+
+static struct kunit_case mldsa_kunit_cases[] = {
+ KUNIT_CASE(test_mldsa44),
+ KUNIT_CASE(benchmark_mldsa44),
+ {},
+};
+
+static struct kunit_suite mldsa_kunit_suite = {
+ .name = "mldsa",
+ .test_cases = mldsa_kunit_cases,
+};
+kunit_test_suite(mldsa_kunit_suite);
+
+MODULE_AUTHOR("David Howells <dhowells@redhat.com>");
+MODULE_DESCRIPTION("ML-DSA tests");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/crypto/tests/mldsa_pure_rejection_vectors_44.h b/lib/crypto/tests/mldsa_pure_rejection_vectors_44.h
new file mode 100644
index 000000000000..124dc97c22e5
--- /dev/null
+++ b/lib/crypto/tests/mldsa_pure_rejection_vectors_44.h
@@ -0,0 +1,489 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+ {
+ .what = "pure rejection vector",
+ .algo = "ml-dsa44",
+ .pk_len = MLDSA44_PUBLIC_KEY_SIZE,
+ .pk = (const u8[MLDSA44_PUBLIC_KEY_SIZE]){
+ 0x8f, 0x61, 0x67, 0xa9, 0x7c, 0x61, 0xc2, 0xf2,
+ 0x87, 0xe2, 0x28, 0xf8, 0x44, 0x80, 0x6f, 0xb0,
+ 0x10, 0xc1, 0x14, 0xf6, 0x88, 0x42, 0x76, 0xbe,
+ 0x05, 0xd2, 0x56, 0xa0, 0xb7, 0x46, 0xcf, 0xc5,
+ 0x76, 0x0a, 0x52, 0xfe, 0xa3, 0x3c, 0x05, 0x6e,
+ 0xd5, 0xd3, 0xbd, 0x80, 0x03, 0x29, 0x25, 0x96,
+ 0xdf, 0xa9, 0x5b, 0x12, 0x42, 0x89, 0x6e, 0x03,
+ 0x2c, 0x42, 0x64, 0xee, 0xc7, 0xf7, 0x55, 0xfe,
+ 0xfd, 0x15, 0x74, 0xee, 0x2d, 0xb3, 0xbf, 0xf2,
+ 0x24, 0xe7, 0x35, 0x45, 0x77, 0x67, 0x44, 0xd2,
+ 0x0c, 0x43, 0xfc, 0x7b, 0x47, 0x56, 0xad, 0xc5,
+ 0xe7, 0x37, 0x49, 0x21, 0x12, 0x57, 0x7f, 0xca,
+ 0x7f, 0x5d, 0xac, 0x62, 0x5b, 0x8d, 0xbf, 0xdb,
+ 0x64, 0xac, 0x12, 0x1d, 0x7f, 0x7a, 0x2f, 0xa0,
+ 0x2c, 0xfe, 0x95, 0x4b, 0x78, 0xdb, 0xf0, 0x98,
+ 0x97, 0x70, 0x62, 0xfc, 0x64, 0x4a, 0x1b, 0xbc,
+ 0x30, 0x1b, 0x51, 0x13, 0x47, 0x6e, 0x83, 0xd2,
+ 0xa2, 0xcf, 0x39, 0xba, 0xd3, 0x00, 0x62, 0x7c,
+ 0x5b, 0xe6, 0x14, 0x55, 0xd5, 0xfc, 0xbf, 0x15,
+ 0x65, 0xd7, 0x1f, 0xbb, 0xf6, 0x2b, 0x46, 0x38,
+ 0x1e, 0xf3, 0x8f, 0x0d, 0x57, 0x8a, 0x41, 0xfb,
+ 0x47, 0x19, 0xe1, 0x79, 0xca, 0x98, 0x1a, 0x73,
+ 0x4d, 0x8a, 0xc0, 0xa1, 0xa7, 0x4a, 0x28, 0x4a,
+ 0x92, 0x6c, 0x77, 0x4f, 0x18, 0xe1, 0xce, 0x11,
+ 0x14, 0xd5, 0xf6, 0xc0, 0xa8, 0x1e, 0x26, 0x25,
+ 0xe4, 0x30, 0xb2, 0x6f, 0x89, 0x6e, 0xc6, 0x44,
+ 0x1a, 0xd3, 0xca, 0xe1, 0x05, 0x0d, 0x61, 0x80,
+ 0xd7, 0xbc, 0x56, 0x0a, 0x57, 0x9f, 0x16, 0x40,
+ 0x84, 0x1e, 0xc0, 0x5e, 0xcd, 0xb6, 0xc8, 0x5d,
+ 0x87, 0xd6, 0xee, 0xcb, 0x21, 0x2f, 0x22, 0x9a,
+ 0x7e, 0xea, 0x2e, 0xf5, 0x87, 0xf0, 0x44, 0x10,
+ 0x3d, 0x42, 0x6d, 0x3f, 0x4f, 0xa0, 0x21, 0x8e,
+ 0x40, 0x75, 0x3b, 0xaa, 0xd4, 0xd9, 0x37, 0x0a,
+ 0x35, 0x5b, 0xba, 0xbd, 0x11, 0x17, 0x7b, 0x7e,
+ 0xfc, 0xb1, 0x1c, 0x5c, 0x71, 0xce, 0xde, 0xa5,
+ 0xec, 0xd6, 0x8b, 0x50, 0x64, 0x02, 0xca, 0x84,
+ 0x26, 0xfe, 0x03, 0x9e, 0xaf, 0x13, 0x05, 0x1c,
+ 0x85, 0x32, 0x92, 0x85, 0x84, 0x83, 0xb6, 0x76,
+ 0x27, 0xa7, 0xd8, 0x0c, 0xa4, 0xb5, 0x65, 0x7a,
+ 0xbf, 0x2e, 0x64, 0xce, 0x82, 0x0d, 0x27, 0xd3,
+ 0x2c, 0x9d, 0xb5, 0xa5, 0x51, 0xe3, 0xab, 0xbd,
+ 0xe2, 0xe0, 0x64, 0x71, 0x0f, 0x99, 0x09, 0x6a,
+ 0x40, 0x9d, 0x8c, 0x6d, 0x63, 0x70, 0x42, 0xe2,
+ 0xa8, 0x5c, 0x71, 0xd2, 0x88, 0x10, 0xbc, 0x3b,
+ 0x9d, 0xc8, 0x3a, 0x8a, 0xf3, 0x81, 0x0a, 0x0e,
+ 0xd8, 0xf6, 0x13, 0x90, 0xc2, 0xda, 0x4c, 0x4f,
+ 0x6f, 0x1f, 0x61, 0x0d, 0x46, 0xac, 0x5c, 0x4a,
+ 0xcc, 0x23, 0xfb, 0xf2, 0xf8, 0x9f, 0x48, 0x1f,
+ 0xaf, 0x24, 0xb4, 0xc7, 0xcb, 0x6f, 0x80, 0x74,
+ 0xa3, 0xdb, 0xa8, 0x61, 0x12, 0x60, 0x08, 0x56,
+ 0x85, 0x47, 0x05, 0x32, 0x93, 0x43, 0x9b, 0xa6,
+ 0xf6, 0x56, 0x3b, 0xab, 0x93, 0x28, 0x19, 0xda,
+ 0xad, 0xb5, 0xaa, 0x2e, 0x83, 0x3d, 0x37, 0x0e,
+ 0x83, 0xf2, 0xfe, 0xdd, 0xe8, 0xd9, 0x53, 0x36,
+ 0x3b, 0x5d, 0x8e, 0x24, 0xa9, 0x3a, 0x8f, 0x85,
+ 0x4b, 0x50, 0xf7, 0x61, 0x0f, 0x20, 0x92, 0x09,
+ 0x1f, 0xe4, 0x24, 0x98, 0x9f, 0xa4, 0x52, 0x12,
+ 0x2a, 0xae, 0x6f, 0xd6, 0x0d, 0xb5, 0x22, 0x72,
+ 0x83, 0xfb, 0x4f, 0xef, 0xa6, 0x55, 0x9a, 0x14,
+ 0xab, 0x82, 0x89, 0x1f, 0xf2, 0x0b, 0x14, 0x76,
+ 0xb7, 0xf7, 0x14, 0xdd, 0xd6, 0xc1, 0xe8, 0xb2,
+ 0x99, 0x23, 0x28, 0xe8, 0xa4, 0x69, 0x18, 0xf6,
+ 0x3e, 0xb1, 0xff, 0xde, 0xf2, 0x2c, 0x7c, 0x73,
+ 0x93, 0x32, 0x52, 0x06, 0xeb, 0x59, 0xb2, 0x8f,
+ 0x2f, 0x1d, 0x6a, 0x85, 0x74, 0xd3, 0xe5, 0xa1,
+ 0x95, 0xe4, 0x96, 0x1e, 0x75, 0x16, 0xe4, 0x5c,
+ 0x40, 0xf0, 0x20, 0xb1, 0x10, 0xe7, 0x2a, 0x70,
+ 0x41, 0xac, 0x49, 0x40, 0x55, 0xef, 0xd1, 0x58,
+ 0x24, 0x6a, 0xa6, 0x20, 0xdc, 0x23, 0xdc, 0x66,
+ 0x45, 0x4f, 0x6a, 0x52, 0x3c, 0x61, 0xc8, 0xfb,
+ 0x28, 0x1e, 0x8c, 0x3f, 0xfc, 0xc7, 0x73, 0x2f,
+ 0xf1, 0xe2, 0x31, 0xee, 0xa0, 0x5f, 0x12, 0x3f,
+ 0x94, 0xe5, 0x26, 0xc0, 0x62, 0xcc, 0x67, 0x8f,
+ 0x5d, 0xfd, 0x3d, 0x8f, 0x16, 0xae, 0x4e, 0x04,
+ 0x54, 0x5b, 0x02, 0x66, 0x00, 0x70, 0xe5, 0xcc,
+ 0xb9, 0x51, 0x8c, 0x1a, 0x5d, 0xf0, 0xfe, 0x7d,
+ 0x1b, 0x2f, 0x0a, 0x48, 0x94, 0xda, 0x4b, 0xb3,
+ 0x05, 0x75, 0x3b, 0x25, 0xda, 0xf2, 0x12, 0x47,
+ 0xe2, 0xd7, 0xbb, 0xe7, 0x05, 0xa2, 0x4e, 0xaf,
+ 0x2f, 0x29, 0x6c, 0xbd, 0x80, 0x82, 0xdf, 0xbb,
+ 0x4b, 0x9d, 0x29, 0x9b, 0xef, 0xff, 0x17, 0x2a,
+ 0xa7, 0x2f, 0x4a, 0x2e, 0xbf, 0x29, 0x38, 0xcb,
+ 0x94, 0x9c, 0x10, 0x87, 0x69, 0x57, 0x7d, 0xd7,
+ 0xcf, 0xc1, 0x57, 0x0f, 0xc2, 0x9c, 0x3b, 0x1e,
+ 0xbc, 0x55, 0x08, 0xb1, 0x50, 0xb0, 0x28, 0x79,
+ 0x1d, 0xd9, 0x2f, 0xa5, 0x12, 0xcc, 0xa7, 0x5b,
+ 0xcb, 0x67, 0x57, 0x13, 0x4a, 0xb3, 0xd5, 0x34,
+ 0xea, 0xd9, 0x6b, 0x15, 0x17, 0x1f, 0xe7, 0x52,
+ 0x2a, 0x11, 0xdf, 0xab, 0x3a, 0x8d, 0x1b, 0xaa,
+ 0xfa, 0x54, 0xf0, 0x20, 0x94, 0x40, 0x2d, 0x3e,
+ 0xf2, 0xa2, 0x8b, 0xbb, 0x86, 0xce, 0x29, 0x87,
+ 0xb1, 0xfa, 0x27, 0xf2, 0x5a, 0x79, 0xc2, 0xf8,
+ 0xe9, 0xf2, 0x7c, 0x5a, 0xcd, 0x45, 0x8e, 0x8b,
+ 0xba, 0xad, 0x13, 0x52, 0x79, 0xd5, 0x0f, 0x59,
+ 0xfb, 0x07, 0x15, 0x1f, 0xb3, 0xe6, 0xc8, 0x66,
+ 0xf7, 0x37, 0x19, 0xd8, 0x33, 0xdd, 0xc1, 0xb3,
+ 0x96, 0x4b, 0x28, 0x43, 0xf8, 0x5e, 0xc7, 0xe8,
+ 0x81, 0x6e, 0xd0, 0xb2, 0x5a, 0x3d, 0xf1, 0x68,
+ 0x8f, 0xf0, 0xf5, 0x1a, 0x6d, 0xc3, 0xaa, 0x5b,
+ 0x72, 0x27, 0xbd, 0xd6, 0x7c, 0x9b, 0xbf, 0x89,
+ 0x6a, 0x09, 0xf0, 0x48, 0xf0, 0x8c, 0x27, 0x69,
+ 0x28, 0xf3, 0x5f, 0x53, 0xe3, 0x4d, 0x60, 0x4a,
+ 0xb8, 0xc5, 0xf9, 0x85, 0x07, 0x3e, 0xfb, 0xd7,
+ 0x21, 0x69, 0xd5, 0xec, 0x18, 0x68, 0xb6, 0x55,
+ 0x15, 0xac, 0x2e, 0x0f, 0x5c, 0x2e, 0x9e, 0x12,
+ 0x10, 0x5e, 0xc6, 0xb3, 0xcd, 0xe6, 0x3a, 0x2f,
+ 0x43, 0xf6, 0x75, 0x31, 0x3c, 0x90, 0x34, 0x1c,
+ 0x3d, 0x45, 0xaa, 0x8e, 0x08, 0xcf, 0x58, 0x67,
+ 0x34, 0xd4, 0x24, 0xb8, 0x20, 0x69, 0xfe, 0xf0,
+ 0x33, 0xe9, 0x3e, 0xbd, 0xe5, 0x18, 0x9f, 0x66,
+ 0xc9, 0x0d, 0x6f, 0x47, 0x99, 0xf7, 0x0c, 0xdd,
+ 0xad, 0x8c, 0x6b, 0x80, 0xed, 0x19, 0x61, 0x8a,
+ 0xba, 0x62, 0x2f, 0xcc, 0x0e, 0x47, 0xe0, 0xc6,
+ 0x1f, 0x2f, 0x44, 0x40, 0x17, 0xb6, 0x89, 0xc6,
+ 0xf5, 0xc2, 0x97, 0x89, 0x38, 0x6c, 0x8e, 0x2c,
+ 0x46, 0x0b, 0x6e, 0x01, 0x47, 0xd3, 0x2f, 0x58,
+ 0xf8, 0xc7, 0x3b, 0x49, 0xb2, 0x35, 0x9f, 0x67,
+ 0xc9, 0x6c, 0xde, 0x30, 0x3b, 0x50, 0x19, 0x65,
+ 0xbb, 0x73, 0xa1, 0x47, 0x01, 0x6a, 0x3d, 0xe3,
+ 0x85, 0x4d, 0xd0, 0x72, 0x0d, 0xbc, 0x81, 0x52,
+ 0xe8, 0x1a, 0x8b, 0x85, 0x4c, 0x6f, 0x0e, 0xfc,
+ 0x59, 0x84, 0x7a, 0xf2, 0x28, 0x3e, 0x02, 0xcb,
+ 0xe2, 0x4a, 0xbf, 0xac, 0x22, 0x34, 0x86, 0xe4,
+ 0x7d, 0x6b, 0xa3, 0x52, 0xac, 0xff, 0xe5, 0xbe,
+ 0x0c, 0x8d, 0xf5, 0xd8, 0xfd, 0x5a, 0x3d, 0xad,
+ 0x0d, 0xc0, 0x02, 0xd0, 0x58, 0x8e, 0x7c, 0x50,
+ 0x7c, 0x09, 0xc0, 0xdb, 0xd7, 0xdf, 0xe0, 0xb2,
+ 0x6f, 0xb2, 0x79, 0x0d, 0xc1, 0xa0, 0xb1, 0x7e,
+ 0xe3, 0x0d, 0xfc, 0x93, 0x43, 0x8c, 0x86, 0x68,
+ 0xa6, 0x51, 0x93, 0x42, 0xb7, 0xcd, 0x13, 0x56,
+ 0x6e, 0xdc, 0x63, 0x51, 0x23, 0xcf, 0x29, 0xc5,
+ 0x5a, 0x66, 0x43, 0x80, 0xfe, 0x28, 0x15, 0x71,
+ 0x52, 0x87, 0xe2, 0x18, 0x10, 0xad, 0x94, 0x47,
+ 0x2f, 0xe3, 0x5d, 0x7a, 0x02, 0x6d, 0x31, 0x58,
+ 0xae, 0x2f, 0x96, 0x53, 0x1b, 0x6c, 0x0e, 0x25,
+ 0xea, 0x41, 0xd3, 0x29, 0x27, 0x22, 0x24, 0x8d,
+ 0x1d, 0x8e, 0xd1, 0x33, 0x76, 0x67, 0x7d, 0x9d,
+ 0xd9, 0xa7, 0x3e, 0x61, 0xd0, 0xad, 0x93, 0xb9,
+ 0xdf, 0x87, 0x3a, 0x7b, 0x89, 0xc7, 0x1d, 0x91,
+ 0xec, 0x43, 0xa4, 0xdc, 0x02, 0x88, 0x2e, 0xaa,
+ 0xb2, 0x58, 0xa5, 0xd3, 0x68, 0x9c, 0x9f, 0x60,
+ 0x12, 0xc8, 0x7e, 0x7d, 0x40, 0x80, 0xfd, 0xb4,
+ 0xbf, 0x56, 0xf4, 0x01, 0x39, 0x3d, 0xa0, 0x34,
+ 0x48, 0x79, 0x75, 0xe2, 0x0f, 0x60, 0x09, 0x42,
+ 0x11, 0x6f, 0xa5, 0x31, 0x46, 0xb7, 0x03, 0xc8,
+ 0x61, 0x53, 0x39, 0x1e, 0xf4, 0x99, 0x36, 0x7d,
+ 0xc0, 0x87, 0xda, 0x63, 0x71, 0x9b, 0x29, 0x7b,
+ 0x4e, 0x6f, 0x09, 0xa2, 0x2c, 0xa5, 0xc2, 0xb9,
+ 0xe7, 0xe0, 0x56, 0x8b, 0x1d, 0xbb, 0xcc, 0x34,
+ 0x8c, 0xbc, 0xb6, 0x0a, 0xc9, 0xfa, 0x4a, 0x31,
+ 0x63, 0x0d, 0x30, 0xff, 0x59, 0x3d, 0x8c, 0x4d,
+ 0x74, 0x28, 0xf4, 0xe9, 0x97, 0x43, 0x05, 0x3a,
+ 0x33, 0x51, 0x51, 0xe4, 0x0e, 0x33, 0xae, 0x2c,
+ 0xda, 0x28, 0x83, 0x93, 0x4e, 0xfe, 0x37, 0x1d,
+ 0x6c, 0x25, 0x1e, 0x24, 0xbc, 0x3a, 0x5c, 0x68,
+ 0xac, 0x54, 0x3a, 0x47, 0x74, 0x35, 0xff, 0x37,
+ 0x80, 0x12, 0x30, 0xd7, 0x31, 0x2a, 0x49, 0x51,
+ 0x2d, 0x4f, 0xd2, 0x9c, 0xca, 0x55, 0x87, 0xd0,
+ 0x41, 0x86, 0xc7, 0xf2, 0xda, 0xf8, 0x4b, 0x08,
+ 0x23, 0xb3, 0x00, 0xb7, 0xb6, 0x4f, 0x2e, 0xaf,
+ 0xb8, 0x8e, 0xb1, 0x44, 0xe1, 0xed, 0x67, 0xf8,
+ 0x80, 0xa7, 0x04, 0xa0, 0x66, 0xe6, 0xb5, 0x69,
+ 0xca, 0x95, 0x71, 0xc8, 0x0d, 0x3d, 0xf6, 0x77,
+ 0xfd, 0x2c, 0x95, 0xed, 0xe5, 0x22, 0x43, 0xd9,
+ },
+ .msg_len = 64,
+ .msg = (const u8[64]){
+ 0x6d, 0xb3, 0x8e, 0x80, 0xaf, 0x5f, 0x19, 0xd9,
+ 0xb0, 0xcf, 0xad, 0x58, 0xc7, 0x27, 0xae, 0x12,
+ 0x4e, 0x7d, 0xa3, 0x1a, 0xe3, 0x85, 0xc6, 0xaa,
+ 0xf6, 0xa1, 0x9a, 0xb1, 0xe9, 0xe0, 0xfe, 0x89,
+ 0x1e, 0xc5, 0x6f, 0x10, 0x18, 0x24, 0xab, 0xa8,
+ 0x6d, 0x03, 0xd0, 0x3d, 0xc3, 0xff, 0x67, 0xe7,
+ 0x3a, 0x95, 0x94, 0xc8, 0x49, 0x51, 0x8f, 0xa0,
+ 0x65, 0xcb, 0x20, 0x29, 0x2a, 0x6d, 0xf7, 0xf2,
+ },
+ .sig_len = MLDSA44_SIGNATURE_SIZE,
+ .sig = (const u8[MLDSA44_SIGNATURE_SIZE]){
+ 0x5e, 0x05, 0x37, 0xe2, 0xc1, 0x20, 0xce, 0x7b,
+ 0x8a, 0xdf, 0xf2, 0x22, 0x61, 0x17, 0x58, 0xaa,
+ 0x3c, 0xe4, 0x82, 0x9c, 0x0e, 0xb6, 0x1f, 0xb4,
+ 0x98, 0x0f, 0xba, 0x8e, 0x51, 0x15, 0x67, 0x76,
+ 0x0b, 0x98, 0x63, 0xda, 0x17, 0xd3, 0xbb, 0xbe,
+ 0x16, 0x29, 0x71, 0xab, 0xba, 0x99, 0xed, 0x3f,
+ 0xd4, 0xc2, 0x16, 0x71, 0xb6, 0x21, 0x87, 0x48,
+ 0xaa, 0xb5, 0x39, 0x5e, 0xfb, 0x5d, 0x68, 0x3b,
+ 0xd3, 0x60, 0xf4, 0x5b, 0x85, 0x2a, 0x5b, 0xb5,
+ 0xce, 0x6e, 0xf3, 0x39, 0xc3, 0xbe, 0x96, 0xa7,
+ 0x61, 0xc9, 0xbf, 0xdf, 0x33, 0x1d, 0xec, 0xb9,
+ 0x2b, 0x7a, 0x05, 0xce, 0x1e, 0xd9, 0x46, 0x70,
+ 0xca, 0x54, 0xbf, 0xdc, 0x46, 0x9e, 0x2f, 0x29,
+ 0x18, 0x57, 0x96, 0x84, 0xac, 0xe9, 0xd7, 0x74,
+ 0xeb, 0x8e, 0x6b, 0xec, 0x46, 0x9a, 0x2a, 0xfa,
+ 0xde, 0x80, 0x09, 0x53, 0xd9, 0xeb, 0x9d, 0xf7,
+ 0xaa, 0xe2, 0xe5, 0xdc, 0xc3, 0xd9, 0x70, 0xe5,
+ 0x8b, 0xa8, 0xba, 0x2b, 0x41, 0x72, 0x92, 0x25,
+ 0xaf, 0xd9, 0xb4, 0x5a, 0x53, 0xb7, 0xcc, 0x1d,
+ 0x69, 0xf1, 0x53, 0x5b, 0x52, 0x38, 0xbc, 0x47,
+ 0x24, 0x8c, 0x1d, 0x28, 0x5d, 0x5c, 0x1c, 0xc9,
+ 0x9d, 0xea, 0x1c, 0xb1, 0xb3, 0x49, 0x68, 0xd5,
+ 0xad, 0xdc, 0x47, 0x58, 0x6d, 0x38, 0x33, 0xe7,
+ 0x9b, 0xaa, 0x89, 0xb1, 0x96, 0x0b, 0xcb, 0xc4,
+ 0x24, 0x73, 0xf2, 0xe7, 0xb6, 0xca, 0x74, 0x55,
+ 0x1b, 0xb5, 0xb7, 0x9e, 0x2e, 0xe3, 0x3a, 0x32,
+ 0x5d, 0x1d, 0x6e, 0x15, 0xe6, 0xb8, 0xfb, 0xce,
+ 0x57, 0x81, 0x15, 0xb5, 0xcf, 0x67, 0x2b, 0x55,
+ 0x4c, 0x85, 0x6f, 0x28, 0xa6, 0xbb, 0xb4, 0x28,
+ 0x76, 0x91, 0xa4, 0x29, 0xa1, 0x50, 0x7c, 0xed,
+ 0x9a, 0xfc, 0xe4, 0xbc, 0xd7, 0x28, 0x62, 0x28,
+ 0x61, 0x4d, 0x8d, 0x8c, 0x5a, 0x5e, 0x4d, 0x1d,
+ 0x5e, 0x73, 0xcc, 0x0b, 0x9d, 0x56, 0x73, 0xc7,
+ 0xf2, 0x26, 0xf7, 0x7e, 0x61, 0xa4, 0x86, 0xf5,
+ 0x1c, 0xd1, 0x00, 0xd0, 0x31, 0xc5, 0x03, 0x17,
+ 0x1c, 0xec, 0x04, 0xe5, 0xc7, 0x13, 0xb6, 0x81,
+ 0x78, 0x3d, 0x27, 0x87, 0x36, 0xf3, 0x2a, 0x59,
+ 0x96, 0xeb, 0x44, 0xfd, 0xb9, 0x95, 0xb7, 0x76,
+ 0xb1, 0x08, 0xc4, 0x98, 0xb1, 0x08, 0x36, 0x2a,
+ 0x63, 0x72, 0x4f, 0xef, 0x47, 0xfc, 0x84, 0x09,
+ 0x18, 0x60, 0xb7, 0x8a, 0xff, 0xae, 0x32, 0x3c,
+ 0x79, 0xdf, 0xd6, 0x24, 0xbe, 0x9c, 0x38, 0x68,
+ 0x92, 0xde, 0x81, 0x80, 0x22, 0x06, 0xf2, 0xe4,
+ 0xde, 0x75, 0x4e, 0xd6, 0x36, 0x93, 0x44, 0xd1,
+ 0xa4, 0x2e, 0x2e, 0x05, 0x87, 0xbd, 0xf7, 0xc5,
+ 0xc8, 0x1c, 0x7b, 0x00, 0xe8, 0x11, 0x7f, 0xc2,
+ 0x39, 0x4b, 0x7b, 0x97, 0x11, 0x92, 0x6c, 0xff,
+ 0x89, 0x7f, 0x26, 0x89, 0x4f, 0x38, 0xfd, 0xdd,
+ 0x08, 0xa7, 0xce, 0x6f, 0xe8, 0x57, 0x9b, 0x46,
+ 0xe5, 0xdb, 0x72, 0x03, 0x1e, 0x7d, 0xb0, 0x77,
+ 0xb9, 0xcc, 0xdb, 0x6c, 0xa7, 0xd8, 0x30, 0x34,
+ 0xad, 0xa7, 0xe4, 0x63, 0xf0, 0x19, 0x0e, 0x5d,
+ 0x3b, 0xe0, 0xff, 0x40, 0x1c, 0xa5, 0xb3, 0xb9,
+ 0x87, 0x6e, 0x2c, 0xf3, 0x5f, 0xcd, 0x54, 0x2a,
+ 0xc0, 0x6e, 0x2b, 0xd9, 0x2d, 0xcc, 0xd5, 0x68,
+ 0x95, 0x4a, 0x4a, 0x84, 0x60, 0x54, 0xee, 0xa0,
+ 0x21, 0x9e, 0x8d, 0x20, 0xcb, 0xe8, 0xc5, 0x5a,
+ 0xba, 0xe2, 0xaa, 0x6e, 0x1c, 0xb1, 0xdf, 0x18,
+ 0x9f, 0x94, 0xc7, 0x77, 0x5a, 0x2c, 0x0e, 0x05,
+ 0xaa, 0x2a, 0x54, 0x58, 0x6c, 0xb3, 0x2e, 0x2f,
+ 0xa4, 0x6e, 0x98, 0xbb, 0x6f, 0x41, 0x6d, 0xbd,
+ 0x71, 0x95, 0xe4, 0xbc, 0x13, 0x37, 0x99, 0x0d,
+ 0xac, 0x27, 0x69, 0xb9, 0x0b, 0x14, 0x5f, 0x6e,
+ 0xd2, 0x2b, 0xe2, 0x0c, 0xc6, 0xbc, 0x10, 0x11,
+ 0x47, 0xb7, 0x37, 0x2c, 0x0e, 0x88, 0xcd, 0xbb,
+ 0xf7, 0x28, 0xd6, 0x4a, 0x9d, 0xff, 0x3c, 0x2f,
+ 0x7d, 0x2b, 0xe8, 0xe8, 0x9e, 0xae, 0x7b, 0xe6,
+ 0x2a, 0xb3, 0x4e, 0x20, 0xcc, 0xf1, 0x81, 0x8e,
+ 0xed, 0x6d, 0xe2, 0x99, 0xf5, 0xb5, 0x1a, 0x30,
+ 0x95, 0x52, 0x34, 0xf5, 0x3f, 0xc3, 0x31, 0xd6,
+ 0xbe, 0xa2, 0xc8, 0xdc, 0xe4, 0x1c, 0xf6, 0x0f,
+ 0x4d, 0x0b, 0x89, 0x8e, 0x66, 0x93, 0x88, 0xb8,
+ 0xad, 0xbc, 0xdc, 0x96, 0x01, 0x9f, 0x16, 0x70,
+ 0xf5, 0x4f, 0xa4, 0x0e, 0x0f, 0xc3, 0xf6, 0x9c,
+ 0xe1, 0xa1, 0xe3, 0xec, 0x9d, 0x09, 0xcd, 0x90,
+ 0x52, 0x26, 0x09, 0xd9, 0x9c, 0xde, 0xbd, 0xdf,
+ 0xbb, 0xf4, 0x50, 0xd1, 0x89, 0x68, 0xf6, 0x86,
+ 0x53, 0x33, 0x5a, 0xf6, 0x54, 0xb5, 0x7c, 0xe1,
+ 0xd7, 0x8d, 0xb8, 0x58, 0xf5, 0xda, 0x14, 0xc8,
+ 0x35, 0x1c, 0xcf, 0x44, 0x62, 0xbc, 0xd7, 0xe3,
+ 0xd8, 0x32, 0xcf, 0x16, 0xf5, 0x2f, 0x55, 0x23,
+ 0xc0, 0x1b, 0xc4, 0xe3, 0x28, 0xc8, 0xc8, 0x97,
+ 0x70, 0x8b, 0x06, 0x98, 0xfb, 0xf6, 0x33, 0x6b,
+ 0x86, 0x1c, 0xdb, 0x2a, 0x3c, 0x08, 0x08, 0x57,
+ 0xd3, 0x4a, 0xf8, 0x22, 0x26, 0x78, 0x65, 0x5f,
+ 0xa6, 0xf8, 0x9f, 0x22, 0x76, 0x62, 0xb0, 0x08,
+ 0x68, 0x70, 0xea, 0x72, 0x57, 0x6b, 0xe7, 0xf7,
+ 0xc6, 0x12, 0x9a, 0x49, 0x50, 0xa9, 0xa5, 0x6c,
+ 0xe7, 0xda, 0xb4, 0xbf, 0xb6, 0xbf, 0x4f, 0xdf,
+ 0x9e, 0x9b, 0xb4, 0xb3, 0x8d, 0x1a, 0x12, 0x16,
+ 0x68, 0xd2, 0x63, 0xae, 0x92, 0x77, 0x1f, 0x03,
+ 0xa5, 0xed, 0x58, 0x3b, 0xe9, 0x0b, 0xfe, 0xfc,
+ 0xae, 0x53, 0x0b, 0x5f, 0x13, 0xf2, 0xd2, 0xe2,
+ 0x0b, 0xec, 0x75, 0x85, 0x68, 0x0c, 0x57, 0xde,
+ 0x1b, 0x6d, 0x78, 0x0b, 0x19, 0x66, 0xa8, 0xf5,
+ 0x45, 0x72, 0x2b, 0x01, 0x06, 0xf6, 0xd1, 0x47,
+ 0x21, 0x24, 0x07, 0xf7, 0x71, 0x03, 0xbc, 0xb0,
+ 0x7c, 0x5b, 0x5c, 0x24, 0xff, 0x74, 0x47, 0x62,
+ 0x81, 0xc3, 0x0b, 0x31, 0x76, 0x90, 0x5b, 0xef,
+ 0x95, 0xa8, 0xa7, 0x02, 0xa1, 0xbf, 0xe1, 0xf4,
+ 0x16, 0x06, 0x8a, 0x97, 0x39, 0x35, 0xcf, 0xf3,
+ 0xa7, 0x4a, 0x43, 0xba, 0x05, 0x95, 0x7d, 0x73,
+ 0x76, 0x7a, 0x53, 0xef, 0xf8, 0x4e, 0xcb, 0x04,
+ 0x70, 0x4c, 0xee, 0xff, 0x82, 0xbd, 0xcd, 0xc1,
+ 0xbe, 0x3d, 0x83, 0x71, 0x03, 0xf0, 0xc0, 0x2b,
+ 0x98, 0xf9, 0x60, 0x54, 0x02, 0x7d, 0xa6, 0x41,
+ 0xcc, 0xa3, 0xd7, 0x8d, 0xfd, 0xce, 0x28, 0xae,
+ 0x0f, 0x48, 0x17, 0x2a, 0xaf, 0xe9, 0xb9, 0x4a,
+ 0x8a, 0x22, 0xd2, 0x4d, 0xd3, 0x1b, 0xa3, 0x39,
+ 0x88, 0x8a, 0x8f, 0x5b, 0x44, 0x97, 0xb9, 0x04,
+ 0x1b, 0x58, 0x67, 0x74, 0x2f, 0x07, 0x7a, 0x52,
+ 0xa9, 0x9d, 0xa4, 0x41, 0x28, 0xf2, 0x35, 0xca,
+ 0x68, 0x4e, 0x4a, 0x3a, 0x66, 0xb9, 0x88, 0x2e,
+ 0x65, 0x1d, 0x47, 0x04, 0xed, 0xdb, 0xe1, 0x40,
+ 0x12, 0x06, 0x13, 0x62, 0x28, 0x3d, 0x0b, 0x35,
+ 0x06, 0xc6, 0x2b, 0xb3, 0x71, 0x3c, 0xfa, 0x77,
+ 0xec, 0x47, 0x93, 0x78, 0x36, 0x25, 0x19, 0xd7,
+ 0x70, 0x30, 0x8a, 0x4c, 0x94, 0xdc, 0x3e, 0xeb,
+ 0x61, 0x25, 0xbc, 0xa0, 0x27, 0xd9, 0x17, 0xa5,
+ 0x19, 0x4f, 0xf4, 0x93, 0x32, 0x56, 0x9a, 0x0b,
+ 0x77, 0xb4, 0x55, 0x1b, 0x8f, 0x9e, 0x69, 0x5b,
+ 0xe2, 0x6d, 0x70, 0x15, 0x79, 0x5c, 0xf6, 0xb6,
+ 0x04, 0xa2, 0x01, 0x37, 0x74, 0x20, 0xb8, 0x62,
+ 0xf6, 0x37, 0x3c, 0xab, 0xca, 0x71, 0xa5, 0x8a,
+ 0x56, 0x5d, 0x6a, 0x4a, 0x61, 0x2e, 0xb8, 0x62,
+ 0x7d, 0x47, 0x34, 0x7d, 0xcd, 0x4d, 0x70, 0x23,
+ 0xf5, 0xaa, 0xd1, 0xa5, 0xf0, 0x4c, 0x38, 0xc3,
+ 0x98, 0x79, 0x4c, 0x0b, 0x6b, 0xcc, 0xe7, 0xd7,
+ 0x09, 0xae, 0x23, 0x9b, 0x2f, 0xde, 0x70, 0xc6,
+ 0xad, 0x0f, 0x66, 0xb5, 0x78, 0x6b, 0x0b, 0xb0,
+ 0x2e, 0x94, 0xf2, 0xa8, 0x8b, 0x74, 0xf0, 0x03,
+ 0x47, 0xd8, 0xec, 0xe8, 0x1f, 0xa3, 0x7b, 0x38,
+ 0x9e, 0x0e, 0xc0, 0x47, 0xd2, 0x0f, 0x8e, 0x7f,
+ 0xb1, 0x83, 0xd3, 0x86, 0x79, 0x3c, 0xa1, 0xae,
+ 0xc4, 0xaf, 0xae, 0x9d, 0x83, 0xc0, 0xd1, 0x2b,
+ 0x2b, 0xda, 0x50, 0x8c, 0xea, 0x41, 0x97, 0x9b,
+ 0x0f, 0x15, 0xc2, 0xe2, 0x8f, 0x39, 0x0b, 0x92,
+ 0xdd, 0xde, 0x52, 0x62, 0x74, 0xdc, 0xda, 0x11,
+ 0x87, 0x4d, 0xa9, 0x4a, 0xc5, 0x2f, 0xae, 0xaf,
+ 0xc1, 0xc3, 0x05, 0xfa, 0x38, 0xcc, 0x5c, 0xb1,
+ 0x9f, 0xe0, 0x82, 0x90, 0xb3, 0xd5, 0xdc, 0xf4,
+ 0x55, 0xdb, 0xea, 0x94, 0x06, 0x7c, 0x2c, 0x82,
+ 0x78, 0xeb, 0xa5, 0x01, 0xf0, 0x3d, 0x4b, 0x87,
+ 0xdd, 0xd5, 0x91, 0x4f, 0xf3, 0xa7, 0xdf, 0xa1,
+ 0xd8, 0x31, 0xde, 0x05, 0x99, 0x67, 0x3d, 0xa4,
+ 0x6b, 0x19, 0xa3, 0xe8, 0x55, 0xb7, 0xf5, 0xc3,
+ 0x63, 0x5e, 0xd4, 0x38, 0xf9, 0x24, 0x64, 0x7d,
+ 0x17, 0xc1, 0x07, 0xbe, 0x39, 0x54, 0x1b, 0x44,
+ 0xe5, 0xc6, 0x3c, 0x02, 0xb1, 0x6f, 0xff, 0x8c,
+ 0xcb, 0x79, 0xe2, 0xec, 0x4d, 0x01, 0xfa, 0x7f,
+ 0x88, 0x1d, 0xc3, 0x4c, 0x6a, 0xfb, 0x0b, 0xc6,
+ 0x57, 0xc3, 0xd8, 0x24, 0x47, 0x41, 0xbd, 0x27,
+ 0xc4, 0xd4, 0x49, 0xfb, 0x52, 0xe6, 0x77, 0x5f,
+ 0x0a, 0xdf, 0xea, 0xd5, 0xd3, 0x22, 0xc3, 0x53,
+ 0x16, 0xf3, 0x1b, 0x7b, 0x09, 0xd7, 0x10, 0x0e,
+ 0x23, 0xae, 0x16, 0x8a, 0x93, 0xcb, 0xc9, 0xb7,
+ 0xb8, 0xff, 0xd2, 0x50, 0x1f, 0x25, 0xa7, 0x71,
+ 0x8f, 0x3f, 0xc0, 0xe1, 0x37, 0x10, 0x0b, 0x43,
+ 0x6e, 0x2b, 0x16, 0x59, 0x8f, 0x77, 0x77, 0x6b,
+ 0x77, 0xce, 0x76, 0x6b, 0x37, 0x81, 0xaf, 0x83,
+ 0x42, 0x92, 0x93, 0xe5, 0x39, 0xca, 0xd2, 0x20,
+ 0x2e, 0xcf, 0x24, 0x26, 0x4c, 0x51, 0x1c, 0x58,
+ 0xc5, 0x8d, 0x05, 0x11, 0xdf, 0xae, 0x51, 0x38,
+ 0xde, 0xab, 0x4e, 0x04, 0xc8, 0x24, 0x24, 0x0d,
+ 0xd5, 0x9c, 0x5b, 0x2b, 0xe6, 0x0d, 0x83, 0x95,
+ 0xcd, 0x1c, 0x89, 0xa1, 0xaf, 0x67, 0x47, 0xfb,
+ 0x08, 0x02, 0xf8, 0x8b, 0x63, 0x05, 0x73, 0x20,
+ 0x64, 0xd7, 0x52, 0x15, 0xa4, 0x5d, 0x63, 0x73,
+ 0x73, 0x12, 0x0b, 0xdd, 0xfe, 0x9f, 0xb7, 0xe8,
+ 0xa8, 0x94, 0x3a, 0x86, 0xff, 0xcf, 0x7d, 0x24,
+ 0xbd, 0xb9, 0xea, 0x68, 0x23, 0xf4, 0x07, 0xc3,
+ 0xfe, 0x63, 0xd0, 0xab, 0x65, 0x8a, 0xf0, 0x6d,
+ 0x81, 0x8c, 0xc8, 0x0e, 0xc6, 0x6b, 0xdd, 0x2e,
+ 0x65, 0x9b, 0x17, 0xcf, 0x82, 0x69, 0x46, 0xba,
+ 0x62, 0x5d, 0x31, 0x33, 0x60, 0x18, 0x94, 0xa5,
+ 0x77, 0x24, 0xc6, 0x45, 0xe5, 0xb3, 0xd5, 0x12,
+ 0x10, 0xc9, 0x22, 0x98, 0xf9, 0xca, 0x20, 0x89,
+ 0x79, 0x04, 0x08, 0xf5, 0x1c, 0xf8, 0x50, 0x8c,
+ 0x25, 0xaa, 0x90, 0x90, 0x44, 0xbc, 0xfb, 0x5d,
+ 0x3f, 0xf8, 0x38, 0x64, 0xca, 0x8d, 0xff, 0x17,
+ 0xce, 0x70, 0x51, 0x90, 0x75, 0x6b, 0x7d, 0x64,
+ 0x43, 0x56, 0xcd, 0xf8, 0x85, 0x93, 0x65, 0x09,
+ 0x81, 0x30, 0x76, 0x79, 0xcc, 0xdf, 0x9c, 0x6d,
+ 0xff, 0x89, 0x38, 0x60, 0xbf, 0x07, 0xcb, 0x2f,
+ 0xc9, 0x87, 0xd7, 0xac, 0x74, 0x19, 0x57, 0x90,
+ 0x5e, 0x69, 0x61, 0xf6, 0xca, 0xea, 0x45, 0x6b,
+ 0xe2, 0xfe, 0x2c, 0xff, 0x1b, 0x23, 0x15, 0x52,
+ 0xdd, 0x57, 0xfe, 0x1d, 0x10, 0xea, 0x0f, 0xce,
+ 0x98, 0xe7, 0x47, 0x27, 0xec, 0x36, 0xe5, 0x68,
+ 0x17, 0xcf, 0xdc, 0xb9, 0xef, 0x6a, 0xbc, 0xec,
+ 0x78, 0x08, 0x64, 0x06, 0xe1, 0x1c, 0xc6, 0x87,
+ 0xd6, 0x0a, 0xb1, 0x81, 0xc6, 0xb6, 0xf8, 0x8b,
+ 0xe3, 0x19, 0x8c, 0xce, 0x46, 0x40, 0xc5, 0xc2,
+ 0xae, 0x50, 0x26, 0x4a, 0x90, 0x91, 0x8d, 0xfe,
+ 0x6b, 0x7d, 0x0a, 0x54, 0x4a, 0x4b, 0x48, 0x74,
+ 0x4a, 0x37, 0x21, 0x7f, 0xdd, 0x87, 0xa3, 0x1e,
+ 0xac, 0xcd, 0xf5, 0x9e, 0x75, 0xa2, 0x52, 0x63,
+ 0x76, 0xca, 0x9e, 0x02, 0xeb, 0xe6, 0xa6, 0x73,
+ 0xad, 0xea, 0xe8, 0x3e, 0x6f, 0x44, 0xed, 0xe8,
+ 0x01, 0x29, 0x19, 0x6a, 0x20, 0x35, 0xa7, 0xf0,
+ 0xf1, 0xaf, 0xc0, 0x3b, 0xb1, 0xd5, 0xe4, 0xfb,
+ 0xf7, 0xd7, 0x2f, 0x33, 0x6c, 0x73, 0xfd, 0xe5,
+ 0x5c, 0x63, 0xf6, 0x1c, 0x06, 0x13, 0xaf, 0xc1,
+ 0x80, 0x55, 0x07, 0xae, 0x8c, 0x13, 0x74, 0xf6,
+ 0xe0, 0x54, 0x15, 0xd8, 0xe0, 0xa5, 0x03, 0xcf,
+ 0x22, 0xbe, 0x18, 0xef, 0x26, 0xad, 0x9c, 0x9d,
+ 0x51, 0xb1, 0x3b, 0x37, 0x03, 0xbf, 0xf0, 0xc5,
+ 0xcb, 0x6c, 0x5d, 0x30, 0xa9, 0x5a, 0x10, 0x90,
+ 0xfa, 0xb4, 0xd4, 0x0e, 0x6b, 0x4a, 0x0a, 0x6c,
+ 0x9d, 0x2e, 0x69, 0xe8, 0xec, 0x69, 0xe2, 0x50,
+ 0xab, 0x2f, 0xdc, 0xff, 0xaf, 0xac, 0x65, 0xe7,
+ 0xf0, 0xc1, 0x6f, 0x7c, 0x2d, 0xa1, 0xeb, 0x97,
+ 0x90, 0x7c, 0x1e, 0xa8, 0x53, 0x1b, 0x87, 0xc5,
+ 0xa9, 0xa1, 0xcf, 0x86, 0x7e, 0x11, 0xf8, 0xd6,
+ 0x14, 0xda, 0x19, 0x81, 0x19, 0xb4, 0x45, 0x1c,
+ 0x7c, 0xb9, 0x96, 0xa2, 0xac, 0x79, 0x24, 0x94,
+ 0x7c, 0xb2, 0x1e, 0x83, 0xea, 0xc4, 0xb9, 0xd2,
+ 0x0d, 0x4c, 0x55, 0x3d, 0x15, 0x7b, 0x65, 0xd8,
+ 0xff, 0x03, 0x5d, 0xed, 0x3c, 0x94, 0x76, 0x19,
+ 0x40, 0x3b, 0xcc, 0x45, 0xbe, 0x91, 0x19, 0x8c,
+ 0x75, 0xe5, 0xd2, 0xbe, 0x67, 0x40, 0xb1, 0x67,
+ 0x8e, 0x2a, 0x34, 0xd5, 0x99, 0xee, 0xd9, 0x4f,
+ 0x89, 0x7c, 0xf0, 0xd6, 0x93, 0x59, 0x4b, 0x3a,
+ 0x8d, 0xe6, 0xbd, 0xde, 0xce, 0xef, 0x8b, 0x3c,
+ 0xe3, 0xf7, 0x06, 0x33, 0x27, 0x8d, 0xd9, 0x22,
+ 0x1a, 0x65, 0x40, 0xfc, 0x69, 0x1b, 0x7d, 0xf0,
+ 0xed, 0xe4, 0xe0, 0x7f, 0x6d, 0x23, 0xed, 0x11,
+ 0xd1, 0x07, 0xb0, 0x2f, 0x8a, 0xbf, 0x51, 0x37,
+ 0x22, 0x04, 0xed, 0x93, 0xea, 0x1d, 0x0b, 0x30,
+ 0x15, 0x89, 0x22, 0x7a, 0x45, 0x56, 0x99, 0xc6,
+ 0xac, 0xd6, 0xce, 0x61, 0xea, 0xb2, 0x59, 0xe8,
+ 0xb5, 0xfc, 0x87, 0xa7, 0xfe, 0x09, 0xa2, 0x0d,
+ 0x5e, 0xbe, 0xb9, 0xd4, 0x9a, 0x1b, 0x60, 0xda,
+ 0xb9, 0x32, 0xf1, 0x30, 0x3e, 0xb2, 0x45, 0x6d,
+ 0x55, 0x0c, 0x2c, 0x4b, 0x9a, 0xc0, 0xbb, 0x8e,
+ 0xac, 0x9c, 0x95, 0x5f, 0x08, 0x88, 0xa0, 0x53,
+ 0x05, 0x75, 0x8d, 0x9e, 0x9d, 0x3f, 0x0f, 0xdd,
+ 0x50, 0x0d, 0xf8, 0x11, 0xbd, 0xf9, 0xfb, 0x22,
+ 0x5c, 0x7b, 0x9e, 0x7c, 0x8e, 0x2f, 0x0e, 0xdb,
+ 0xb8, 0x1d, 0x0c, 0x5e, 0x82, 0xf3, 0x8e, 0xec,
+ 0x32, 0x1c, 0x59, 0x73, 0xa5, 0xf3, 0x5b, 0x47,
+ 0x00, 0x64, 0x89, 0x68, 0x3b, 0xaf, 0xe8, 0xe2,
+ 0x9b, 0xa6, 0xac, 0x2c, 0xf9, 0x2b, 0x92, 0xf3,
+ 0xf0, 0x5b, 0xcc, 0x75, 0x22, 0xd5, 0xf4, 0x2b,
+ 0x06, 0x96, 0xc8, 0x50, 0xee, 0xac, 0x62, 0x16,
+ 0x45, 0x9e, 0xbc, 0xcc, 0x8f, 0x5a, 0x66, 0xc6,
+ 0x30, 0x7c, 0xe0, 0x22, 0xcc, 0xb9, 0xda, 0x0b,
+ 0x0a, 0xbd, 0x2a, 0x2e, 0x46, 0x7d, 0xb6, 0x86,
+ 0x70, 0xa3, 0x16, 0x49, 0x85, 0x28, 0x7b, 0xe9,
+ 0x00, 0x6b, 0xfa, 0x06, 0xb0, 0xeb, 0xbd, 0x67,
+ 0x28, 0x6f, 0x27, 0xd7, 0x9c, 0x7f, 0xda, 0xec,
+ 0xf4, 0x7e, 0x55, 0xe1, 0x0c, 0x29, 0x61, 0x7a,
+ 0xf5, 0xb6, 0xb8, 0xa5, 0xef, 0x36, 0x6a, 0xad,
+ 0x59, 0x22, 0xbd, 0x3d, 0xad, 0x86, 0xe7, 0x4c,
+ 0x69, 0x26, 0x0f, 0xbf, 0x67, 0xad, 0x65, 0x32,
+ 0xbd, 0x21, 0xd6, 0x59, 0x6b, 0xe3, 0xda, 0xc1,
+ 0x6f, 0x82, 0x41, 0x2c, 0xaa, 0xe4, 0x8c, 0xfc,
+ 0x7c, 0x61, 0x28, 0x51, 0x52, 0x3d, 0xf1, 0x84,
+ 0xb5, 0x0b, 0xfd, 0x1f, 0x2a, 0x06, 0x2e, 0x30,
+ 0xed, 0x63, 0x43, 0xc9, 0x83, 0x97, 0xb1, 0xd4,
+ 0x80, 0x6f, 0x2c, 0x50, 0xec, 0x20, 0x95, 0x42,
+ 0xa0, 0x34, 0x94, 0x1a, 0xa9, 0x5e, 0x5b, 0x59,
+ 0xe3, 0x39, 0xac, 0xbd, 0x2f, 0x77, 0x36, 0x59,
+ 0x9c, 0xc3, 0x3c, 0x66, 0x87, 0xf5, 0x81, 0x4b,
+ 0xb0, 0x10, 0x4a, 0xe6, 0x46, 0xe7, 0xce, 0x93,
+ 0x7b, 0x24, 0x6b, 0x2e, 0xc1, 0xe5, 0xaf, 0x4b,
+ 0x71, 0x22, 0xad, 0x88, 0xda, 0x55, 0xcb, 0xe0,
+ 0x73, 0xd1, 0x65, 0x7d, 0xa5, 0x7f, 0x36, 0xbc,
+ 0x42, 0xc2, 0x78, 0x9f, 0x88, 0xe8, 0xdb, 0xff,
+ 0x8a, 0x5a, 0x80, 0x34, 0x3a, 0x23, 0x4c, 0x8a,
+ 0x81, 0xff, 0xbd, 0xb7, 0x88, 0xd0, 0x73, 0x07,
+ 0x8a, 0x4e, 0xa7, 0x4a, 0x61, 0x0f, 0x1f, 0x1c,
+ 0xe7, 0x34, 0x37, 0x1c, 0x53, 0x90, 0x3b, 0xa4,
+ 0x32, 0x6c, 0x6d, 0xe8, 0x00, 0xde, 0xe0, 0x0c,
+ 0x5e, 0x06, 0xef, 0xb8, 0x48, 0x2e, 0xb3, 0xda,
+ 0xac, 0x92, 0x4d, 0x0d, 0x95, 0x75, 0x44, 0x01,
+ 0x6f, 0x97, 0xc3, 0x29, 0x76, 0x33, 0x36, 0x9a,
+ 0xae, 0xfb, 0x1b, 0x43, 0xe5, 0xb1, 0x54, 0x3a,
+ 0x9c, 0x76, 0x7f, 0x76, 0x83, 0xc9, 0x9c, 0xd6,
+ 0x56, 0x59, 0x83, 0xa9, 0xde, 0xd7, 0xb0, 0xf3,
+ 0x34, 0x11, 0x31, 0x06, 0x8e, 0xe9, 0xd4, 0x79,
+ 0xd5, 0x3d, 0x31, 0x6b, 0x59, 0xe9, 0x54, 0x69,
+ 0x12, 0xfd, 0x44, 0x59, 0x4e, 0x1b, 0x3b, 0xb4,
+ 0x12, 0xe9, 0xfb, 0xb0, 0xb4, 0x84, 0xb9, 0x7d,
+ 0xea, 0x4f, 0xd1, 0x5f, 0xd0, 0x3e, 0xce, 0xef,
+ 0x5c, 0xf7, 0xea, 0x55, 0xa0, 0x8f, 0xa8, 0xa7,
+ 0x98, 0xe7, 0xa1, 0x6b, 0x3f, 0xba, 0x5a, 0x32,
+ 0x4b, 0xfa, 0x31, 0xb6, 0x63, 0x86, 0x19, 0x00,
+ 0xa2, 0x6d, 0x7d, 0x15, 0x56, 0x05, 0x68, 0xa3,
+ 0xe0, 0xf3, 0xd4, 0x82, 0xcf, 0xeb, 0xd4, 0x1c,
+ 0xd0, 0xb6, 0x14, 0x5e, 0x9e, 0x6b, 0xed, 0x7a,
+ 0x02, 0x1a, 0xcd, 0x09, 0xdc, 0x26, 0x98, 0x50,
+ 0x11, 0x34, 0x39, 0x50, 0x5a, 0x70, 0x79, 0x85,
+ 0xca, 0xd2, 0xf2, 0x0c, 0x0d, 0x12, 0x1f, 0x2e,
+ 0x41, 0x46, 0x51, 0x72, 0x75, 0x78, 0x8c, 0xa4,
+ 0xaf, 0xba, 0xca, 0xd3, 0xdf, 0xea, 0xf8, 0x09,
+ 0x0b, 0x36, 0x45, 0x4f, 0x77, 0x83, 0xae, 0xbc,
+ 0xc5, 0xce, 0xe1, 0xf6, 0x1d, 0x1e, 0x38, 0x56,
+ 0x9c, 0x9f, 0xb1, 0xbd, 0xda, 0xe7, 0xf0, 0xf4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0b, 0x1f, 0x2c, 0x38,
+ },
+ },
^ permalink raw reply related
* [PATCH v10 5/8] crypto: Add ML-DSA crypto_sig support
From: David Howells @ 2025-11-20 10:44 UTC (permalink / raw)
To: Herbert Xu
Cc: David Howells, Eric Biggers, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Jason A . Donenfeld, Ard Biesheuvel,
Stephan Mueller, Lukas Wunner, Ignat Korchagin, linux-crypto,
keyrings, linux-modules, linux-kernel
In-Reply-To: <20251120104439.2620205-1-dhowells@redhat.com>
---
crypto/Kconfig | 10 ++++++++++
crypto/Makefile | 2 ++
2 files changed, 12 insertions(+)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index bf8b8a60a0c0..45e376af02dc 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.
+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
menu "Block ciphers"
diff --git a/crypto/Makefile b/crypto/Makefile
index 093c56a45d3f..b181f8a54099 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
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox