linux-crypto.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: Herbert Xu <herbert@gondor.apana.org.au>
Cc: David Howells <dhowells@redhat.com>,
	Eric Biggers <ebiggers@kernel.org>,
	Luis Chamberlain <mcgrof@kernel.org>,
	Petr Pavlu <petr.pavlu@suse.com>,
	Daniel Gomez <da.gomez@kernel.org>,
	Sami Tolvanen <samitolvanen@google.com>,
	"Jason A . Donenfeld" <Jason@zx2c4.com>,
	Ard Biesheuvel <ardb@kernel.org>,
	Stephan Mueller <smueller@chronox.de>,
	Lukas Wunner <lukas@wunner.de>,
	Ignat Korchagin <ignat@cloudflare.com>,
	linux-crypto@vger.kernel.org, keyrings@vger.kernel.org,
	linux-modules@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v9 3/9] mldsa: Add a simpler API
Date: Mon, 17 Nov 2025 14:55:52 +0000	[thread overview]
Message-ID: <20251117145606.2155773-4-dhowells@redhat.com> (raw)
In-Reply-To: <20251117145606.2155773-1-dhowells@redhat.com>

Add a simpler ML-DSA API to hide all the details of packing/unpacking keys
and signatures.

Signed-off-by: David Howells <dhowells@redhat.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@redhat.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@redhat.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);


  parent reply	other threads:[~2025-11-17 14:56 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-17 14:55 [PATCH v9 0/9] lib/crypto: Add ML-DSA signing David Howells
2025-11-17 14:55 ` [PATCH v9 1/9] crypto: Add support for shake256 through crypto_shash David Howells
2025-11-17 16:50   ` Eric Biggers
2025-11-17 14:55 ` [PATCH v9 2/9] crypto: Add ML-DSA/Dilithium verify support David Howells
2025-11-17 17:10   ` Eric Biggers
2025-11-25 10:10     ` Ignat Korchagin
2025-11-25 20:24       ` Eric Biggers
2025-11-25 20:51         ` Ignat Korchagin
2025-11-17 19:52   ` David Howells
2025-11-17 20:12     ` Eric Biggers
2025-11-19  3:59       ` Eric Biggers
2025-11-19 14:20       ` David Howells
2025-11-17 20:19     ` James Bottomley
2025-11-18  8:39     ` David Howells
2025-11-18 12:59       ` James Bottomley
2025-11-17 20:05   ` David Howells
2025-11-21  1:37   ` Eric Biggers
2025-11-25  4:10     ` Eric Biggers
2025-11-25  8:32       ` Stephan Müller
2025-11-17 14:55 ` David Howells [this message]
2025-11-17 14:55 ` [PATCH v9 4/9] crypto: Add ML-DSA-44 pure rejection test vectors as a kunit test David Howells
2025-11-17 14:55 ` [PATCH v9 5/9] crypto: Add ML-DSA-65 " David Howells
2025-11-17 14:55 ` [PATCH v9 6/9] crypto: Add ML-DSA-87 " David Howells
2025-11-17 14:55 ` [PATCH v9 7/9] pkcs7: Allow the signing algo to calculate the digest itself David Howells
2025-11-17 14:55 ` [PATCH v9 8/9] pkcs7, x509: Add ML-DSA support David Howells
2025-11-17 14:55 ` [PATCH v9 9/9] modsign: Enable ML-DSA module signing David Howells
2025-11-17 15:22 ` Pick up keys-pqc branch for linux-next? David Howells
2025-11-17 17:11   ` Eric Biggers
2025-11-20  9:53     ` Stephen Rothwell
2025-11-21  2:48       ` Eric Biggers
2025-11-17 16:01 ` Where to add FIPS tests David Howells
2025-11-17 16:54   ` Eric Biggers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251117145606.2155773-4-dhowells@redhat.com \
    --to=dhowells@redhat.com \
    --cc=Jason@zx2c4.com \
    --cc=ardb@kernel.org \
    --cc=da.gomez@kernel.org \
    --cc=ebiggers@kernel.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=ignat@cloudflare.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=lukas@wunner.de \
    --cc=mcgrof@kernel.org \
    --cc=petr.pavlu@suse.com \
    --cc=samitolvanen@google.com \
    --cc=smueller@chronox.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).