[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251117145606.2155773-4-dhowells@redhat.com>
Date: Mon, 17 Nov 2025 14:55:52 +0000
From: David Howells <dhowells@...hat.com>
To: Herbert Xu <herbert@...dor.apana.org.au>
Cc: David Howells <dhowells@...hat.com>,
Eric Biggers <ebiggers@...nel.org>,
Luis Chamberlain <mcgrof@...nel.org>,
Petr Pavlu <petr.pavlu@...e.com>,
Daniel Gomez <da.gomez@...nel.org>,
Sami Tolvanen <samitolvanen@...gle.com>,
"Jason A . Donenfeld" <Jason@...c4.com>,
Ard Biesheuvel <ardb@...nel.org>,
Stephan Mueller <smueller@...onox.de>,
Lukas Wunner <lukas@...ner.de>,
Ignat Korchagin <ignat@...udflare.com>,
linux-crypto@...r.kernel.org,
keyrings@...r.kernel.org,
linux-modules@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH v9 3/9] mldsa: Add a simpler API
Add a simpler ML-DSA API to hide all the details of packing/unpacking keys
and signatures.
Signed-off-by: David Howells <dhowells@...hat.com>
---
Documentation/crypto/index.rst | 1 +
Documentation/crypto/mldsa.rst | 143 +++++++++++++++++++++++++
include/crypto/mldsa.h | 50 +++++++++
lib/crypto/mldsa/dilithium_44.c | 1 +
lib/crypto/mldsa/dilithium_65.c | 1 +
lib/crypto/mldsa/dilithium_87.c | 1 +
lib/crypto/mldsa/dilithium_type.h | 13 +++
lib/crypto/mldsa/mldsa_api.c | 168 ++++++++++++++++++++++++++++++
8 files changed, 378 insertions(+)
create mode 100644 Documentation/crypto/mldsa.rst
create mode 100644 include/crypto/mldsa.h
create mode 100644 lib/crypto/mldsa/mldsa_api.c
diff --git a/Documentation/crypto/index.rst b/Documentation/crypto/index.rst
index 4ee667c446f9..4498fc92bfc5 100644
--- a/Documentation/crypto/index.rst
+++ b/Documentation/crypto/index.rst
@@ -28,3 +28,4 @@ for cryptographic use cases, as well as programming examples.
device_drivers/index
krb5
sha3
+ mldsa
diff --git a/Documentation/crypto/mldsa.rst b/Documentation/crypto/mldsa.rst
new file mode 100644
index 000000000000..dda5d42bfae9
--- /dev/null
+++ b/Documentation/crypto/mldsa.rst
@@ -0,0 +1,143 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+===========================
+ML-DSA Algorithm Collection
+===========================
+
+.. contents::
+
+ - Overview
+ - Library API
+ - References
+ - API Function Reference
+
+
+Overview
+========
+
+The ML-DSA algorithm, as specified in NIST FIPS-204 [1]_, is a "Post Quantum"
+asymmetric cipher/public key algorithm. It has digestion of the message to be
+signed built in to the algorithm, though options exist to do that separately
+(those aren't supported in the API presented here, however). The algorithm
+used to digest the message in this implementation is SHAKE256, though in theory
+other algorithms can be used too.
+
+This implementation only supports signature verification and does not support
+keypair generation or signing.
+
+Three strengths are provided:
+
+ - ML-DSA 44
+ - ML-DSA 65
+ - ML-DSA 87
+
+This document describes the ML-DSA library API. Each strength has its own API
+and would be built as a separate module if not built in. There is also a
+common module for shared pieces.
+
+The algorithms are also available through the crypto_sig API, though
+`-EOPNOTSUPP` will be returned if any of the API functions involved in signing
+a message are invoked.
+
+
+Library API
+===========
+
+To use this::
+
+ #include <crypto/mldsa.h>
+
+must be included.
+
+[Note for anyone looking for the following functions in the code! These
+functions are created by preprocessor templating and should be looked for
+without the strength in the name.]
+
+To perform single-step verification of a signature, one of the following
+functions can be used::
+
+ int mldsa_44_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len);
+
+ int mldsa_65_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len);
+
+ int mldsa_87_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len);
+
+They take the public key, signature and the complete message as a single
+buffer. They return `-EINVAL` the public key type and/or signature size are
+incorrect. They return `-EBADMSG` if the computed signature doesn't match the
+supplied one.
+
+If, however, the message to be verified is split into multiple fragments, then
+the multi-step API must be used. Firstly, a context must be allocated::
+
+ struct mldsa_44_ctx *mldsa_ctx_alloc(void);
+
+ struct mldsa_65_ctx *mldsa_ctx_alloc(void);
+
+ struct mldsa_87_ctx *mldsa_ctx_alloc(void);
+
+This is type-specific as the size of the allocated state may vary by strength.
+Then data can be added to the internal hash::
+
+ int mldsa_44_verify_update(struct mldsa_44_ctx *ctx,
+ const void *data, size_t data_len);
+
+ int mldsa_65_verify_update(struct mldsa_65_ctx *ctx,
+ const void *data, size_t data_len);
+
+ int mldsa_87_verify_update(struct mldsa_87_ctx *ctx,
+ const void *data, size_t data_len);
+
+And finally the signature verification can be performed::
+
+ int mldsa_44_verify_final(struct mldsa_44_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len);
+
+ int mldsa_65_verify_final(struct mldsa_65_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len);
+
+ int mldsa_87_verify_final(struct mldsa_87_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len);
+
+These return `-EINVAL` if the specified type (if known), the public key type
+and signature type don't all match. It return `-EBADMSG` if the computed
+signature doesn't match the supplied one.
+
+The context can be reset and used again (provided it's for a key of the same
+type)::
+
+ void mldsa_44_ctx_zeroize(struct mldsa_44_ctx *ctx);
+
+ void mldsa_65_ctx_zeroize(struct mldsa_65_ctx *ctx);
+
+ void mldsa_87_ctx_zeroize(struct mldsa_87_ctx *ctx);
+
+And finally, it can be freed::
+
+ void mldsa_44_ctx_free(struct mldsa_44_ctx *ctx);
+
+ void mldsa_65_ctx_free(struct mldsa_65_ctx *ctx);
+
+ void mldsa_87_ctx_free(struct mldsa_87_ctx *ctx);
+
+
+References
+==========
+
+.. [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf
+
+
+API Function Reference
+======================
+
+.. kernel-doc:: include/crypto/mldsa.h
+.. kernel-doc:: lib/crypto/mldsa/mldsa_api.c
diff --git a/include/crypto/mldsa.h b/include/crypto/mldsa.h
new file mode 100644
index 000000000000..f105f23b66b0
--- /dev/null
+++ b/include/crypto/mldsa.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Simple API for ML-DSA.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@...hat.com)
+ *
+ * See also Documentation/crypto/mldsa.rst
+ */
+
+#ifndef _CRYPTO_MLDSA_H
+#define _CRYPTO_MLDSA_H
+
+struct mldsa_44_ctx;
+struct mldsa_65_ctx;
+struct mldsa_87_ctx;
+
+int mldsa_44_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len);
+struct mldsa_44_ctx *mldsa_44_ctx_alloc(void);
+int mldsa_44_verify_update(struct mldsa_44_ctx *ctx, const void *data, size_t data_len);
+int mldsa_44_verify_final(struct mldsa_44_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len);
+void mldsa_44_ctx_zeroize(struct mldsa_44_ctx *ctx);
+void mldsa_44_ctx_free(struct mldsa_44_ctx *ctx);
+
+int mldsa_65_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len);
+struct mldsa_65_ctx *mldsa_65_ctx_alloc(void);
+int mldsa_65_verify_update(struct mldsa_65_ctx *ctx, const void *data, size_t data_len);
+int mldsa_65_verify_final(struct mldsa_65_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len);
+void mldsa_65_ctx_zeroize(struct mldsa_65_ctx *ctx);
+void mldsa_65_ctx_free(struct mldsa_65_ctx *ctx);
+
+int mldsa_87_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len);
+struct mldsa_87_ctx *mldsa_87_ctx_alloc(void);
+int mldsa_87_verify_update(struct mldsa_87_ctx *ctx, const void *data, size_t data_len);
+int mldsa_87_verify_final(struct mldsa_87_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len);
+void mldsa_87_ctx_zeroize(struct mldsa_87_ctx *ctx);
+void mldsa_87_ctx_free(struct mldsa_87_ctx *ctx);
+
+#endif /* _CRYPTO_MLDSA_H */
diff --git a/lib/crypto/mldsa/dilithium_44.c b/lib/crypto/mldsa/dilithium_44.c
index 1aea716016f0..4c70465a5601 100644
--- a/lib/crypto/mldsa/dilithium_44.c
+++ b/lib/crypto/mldsa/dilithium_44.c
@@ -31,3 +31,4 @@
#include "dilithium_poly.c"
#include "dilithium_rounding.c"
#include "dilithium_signature_c.c"
+#include "mldsa_api.c"
diff --git a/lib/crypto/mldsa/dilithium_65.c b/lib/crypto/mldsa/dilithium_65.c
index 08f3a8e71228..7a2ab3f5a179 100644
--- a/lib/crypto/mldsa/dilithium_65.c
+++ b/lib/crypto/mldsa/dilithium_65.c
@@ -31,3 +31,4 @@
#include "dilithium_poly.c"
#include "dilithium_rounding.c"
#include "dilithium_signature_c.c"
+#include "mldsa_api.c"
diff --git a/lib/crypto/mldsa/dilithium_87.c b/lib/crypto/mldsa/dilithium_87.c
index fcc3e0229ed9..548a7d1d365c 100644
--- a/lib/crypto/mldsa/dilithium_87.c
+++ b/lib/crypto/mldsa/dilithium_87.c
@@ -31,3 +31,4 @@
#include "dilithium_poly.c"
#include "dilithium_rounding.c"
#include "dilithium_signature_c.c"
+#include "mldsa_api.c"
diff --git a/lib/crypto/mldsa/dilithium_type.h b/lib/crypto/mldsa/dilithium_type.h
index 84da7b97dd19..27b21b298001 100644
--- a/lib/crypto/mldsa/dilithium_type.h
+++ b/lib/crypto/mldsa/dilithium_type.h
@@ -34,23 +34,29 @@
* compilation different sizes would not be possible.
*/
#ifdef DILITHIUM_TYPE_65
+#define MLDSA_F(name) mldsa_65_##name
#define DILITHIUM_F(name) dilithium_65_##name
#define dilithium_pk dilithium_65_pk
#define dilithium_sig dilithium_65_sig
+#define mldsa_ctx mldsa_65_ctx
#include "dilithium_65.h"
#elif defined DILITHIUM_TYPE_44
+#define MLDSA_F(name) mldsa_44_##name
#define DILITHIUM_F(name) dilithium_44_##name
#define dilithium_pk dilithium_44_pk
#define dilithium_sig dilithium_44_sig
+#define mldsa_ctx mldsa_44_ctx
#include "dilithium_44.h"
#else
+#define MLDSA_F(name) mldsa_87_##name
#define DILITHIUM_F(name) dilithium_87_##name
#define dilithium_pk dilithium_87_pk
#define dilithium_sig dilithium_87_sig
+#define mldsa_ctx mldsa_87_ctx
#include "dilithium_87.h"
@@ -60,6 +66,13 @@
* The following defines simply allow duplicate compilation of the
* respective functions.
*/
+#define mldsa_verify MLDSA_F(verify)
+#define mldsa_verify_update MLDSA_F(verify_update)
+#define mldsa_verify_final MLDSA_F(verify_final)
+#define mldsa_ctx_alloc MLDSA_F(ctx_alloc)
+#define mldsa_ctx_zeroize MLDSA_F(ctx_zeroize)
+#define mldsa_ctx_free MLDSA_F(ctx_free)
+
#define dilithium_pk_load DILITHIUM_F(pk_load)
#define dilithium_sig_load DILITHIUM_F(sig_load)
#define dilithium_pk_ptr DILITHIUM_F(pk_ptr)
diff --git a/lib/crypto/mldsa/mldsa_api.c b/lib/crypto/mldsa/mldsa_api.c
new file mode 100644
index 000000000000..23895df30357
--- /dev/null
+++ b/lib/crypto/mldsa/mldsa_api.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Simple API for ML-DSA.
+ *
+ * Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@...hat.com)
+ *
+ * See also Documentation/crypto/mldsa.rst
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <crypto/mldsa.h>
+#include "dilithium.h"
+
+struct mldsa_ctx {
+ struct dilithium_pk pk;
+ struct dilithium_sig sig;
+ struct dilithium_ctx *dilithium;
+ enum dilithium_type type;
+};
+
+/**
+ * mldsa_verify - All-in-one ML-DSA signature verification
+ * @pk: Pointer to public key
+ * @pk_len: Length of public key
+ * @data: Pointer to signed data
+ * @data_len: Length of signed data
+ * @sig: Pointer to signature
+ * @sig_len: Length of signature
+ *
+ * Perform all the steps needed to verify an ML-DSA signature in one go. Only
+ * one data buffer may be provided. For multifragment messages, the
+ * alloc/update/final interface must be used instead.
+ *
+ * Return: 0 if signature could be verified correctly, -EBADMSG when signature
+ * cannot be verified and < 0 on other errors.
+ */
+int mldsa_verify(const void *pk, size_t pk_len,
+ const uint8_t *data, size_t data_len,
+ const void *sig, size_t sig_len)
+{
+ struct mldsa_ctx *ctx;
+ int ret;
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ret = dilithium_pk_load(&ctx->pk, pk, pk_len);
+ if (ret < 0)
+ goto out;
+ ret = dilithium_sig_load(&ctx->sig, sig, sig_len);
+ if (ret < 0)
+ goto out;
+
+ ret = dilithium_verify(&ctx->sig, data, data_len, &ctx->pk);
+out:
+ kfree_sensitive(ctx);
+ return ret;
+}
+EXPORT_SYMBOL(mldsa_verify);
+
+/**
+ * mldsa_ctx_alloc - Allocate ML-DSA context
+ *
+ * Return: Pointer to the allocated context or error code on failure.
+ *
+ * Context: Process context. May sleep to allocate memory.
+ */
+struct mldsa_ctx *mldsa_ctx_alloc(void)
+{
+ struct mldsa_ctx *ctx;
+
+ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->dilithium = dilithium_ctx_alloc_ahat(ctx->type);
+ if (!ctx->dilithium) {
+ kfree(ctx);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return ctx;
+}
+EXPORT_SYMBOL(mldsa_ctx_alloc);
+
+/**
+ * mldsa_verify_update - Add more data to an already initialized context
+ * @ctx: Pointer to an allocated ML-DSA context
+ * @data: Pointer to signed data
+ * @data_len: Length of signed data
+ *
+ * Add more signed data to the state in an allocated context. This can be use
+ * to provide data that is split into multiple fragments.
+ *
+ * Return: 0 (success) or < 0 on error
+ *
+ * Context: Process context. May sleep to allocate memory.
+ */
+int mldsa_verify_update(struct mldsa_ctx *ctx, const void *data, size_t data_len)
+{
+ return dilithium_verify_update(ctx->dilithium, data, data_len);
+}
+EXPORT_SYMBOL(mldsa_verify_update);
+
+/**
+ * mldsa_verify_final - Perform signature verification
+ * @ctx: Pointer to an allocated and updated ML-DSA context
+ * @pk: Pointer to public key
+ * @pk_len: Length of public key
+ * @sig: Pointer to signature
+ * @sig_len: Length of signature
+ *
+ * Finalise the state in the ML-DSA context and verify the signature. The
+ * caller must have allocated it and updated it with all the pieces of data.
+ * Note that this does not free the context, but does reset it.
+ *
+ * Return: 0 if signature could be verified correctly, -EBADMSG when the
+ * signature cannot be verified and < 0 on other errors.
+ *
+ * Context: Process context. May sleep to allocate memory.
+ */
+int mldsa_verify_final(struct mldsa_ctx *ctx,
+ const void *pk, size_t pk_len,
+ const void *sig, size_t sig_len)
+{
+ int ret;
+
+ ret = dilithium_pk_load(&ctx->pk, pk, pk_len);
+ if (ret < 0)
+ return ret;
+ ret = dilithium_sig_load(&ctx->sig, sig, sig_len);
+ if (ret < 0)
+ return ret;
+
+ ret = dilithium_verify_final(&ctx->sig, ctx->dilithium, &ctx->pk);
+ dilithium_ctx_zero(ctx->dilithium);
+ return ret;
+}
+EXPORT_SYMBOL(mldsa_verify_final);
+
+/**
+ * mldsa_ctx_zeroize - Resets an ML-DSA context
+ * @ctx: Context pointer
+ *
+ * Context: Any context.
+ */
+void mldsa_ctx_zeroize(struct mldsa_ctx *ctx)
+{
+ dilithium_ctx_zero(ctx->dilithium);
+ memset(&ctx->pk, 0, sizeof(ctx->pk));
+ memset(&ctx->sig, 0, sizeof(ctx->sig));
+}
+EXPORT_SYMBOL(mldsa_ctx_zeroize);
+
+/**
+ * mldsa_ctx_free - Clears and frees an ML-DSA context.
+ * @ctx: Context pointer
+ *
+ * Context: Any context.
+ */
+void mldsa_ctx_free(struct mldsa_ctx *ctx)
+{
+ dilithium_ctx_zero_free(ctx->dilithium);
+ kfree(ctx);
+}
+EXPORT_SYMBOL(mldsa_ctx_free);
Powered by blists - more mailing lists