Linux userland API discussions
 help / color / mirror / Atom feed
From: Saravanakrishnan Krishnamoorthy <skrishnamoorthy@rambus.com>
To: Albert Ou <aou@eecs.berkeley.edu>,
	Alex Ousherovitch <aousherovitch@rambus.com>,
	Conor Dooley <conor+dt@kernel.org>,
	"David S. Miller" <davem@davemloft.net>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	Jonathan Corbet <corbet@lwn.net>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Paul Walmsley <pjw@kernel.org>, Rob Herring <robh@kernel.org>,
	Saravanakrishnan Krishnamoorthy <skrishnamoorthy@rambus.com>,
	Shuah Khan <shuah@kernel.org>
Cc: Alexandre Ghiti <alex@ghiti.fr>,
	devicetree@vger.kernel.org,
	Joel Wittenauer <Joel.Wittenauer@cryptography.com>,
	linux-api@vger.kernel.org, linux-crypto@vger.kernel.org,
	linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-riscv@lists.infradead.org,
	Shuah Khan <skhan@linuxfoundation.org>,
	sipsupport@rambus.com, Thi Nguyen <thin@rambus.com>
Subject: [PATCH 16/19] crypto: cmh - add SLH-DSA/LMS/XMSS (HCQ)
Date: Thu, 25 Jun 2026 10:33:24 -0700	[thread overview]
Message-ID: <20260625173328.1140487-17-skrishnamoorthy@rambus.com> (raw)
In-Reply-To: <20260625173328.1140487-1-skrishnamoorthy@rambus.com>

From: Alex Ousherovitch <aousherovitch@rambus.com>

Register SLH-DSA, LMS, LMS-HSS, XMSS, and XMSS-MT algorithms
using the CMH HCQ core (core ID 0x08).  SLH-DSA is registered as a
sig algorithm with sign and verify support.  LMS, LMS-HSS, XMSS,
and XMSS-MT are registered as verify-only sig algorithms: their
stateful signing semantics (one-time-key private state the signer
must track) are not modeled by the kernel crypto API, so only
verification is exposed.

Co-developed-by: Saravanakrishnan Krishnamoorthy <skrishnamoorthy@rambus.com>
Signed-off-by: Saravanakrishnan Krishnamoorthy <skrishnamoorthy@rambus.com>
Signed-off-by: Alex Ousherovitch <aousherovitch@rambus.com>
Reviewed-by: Joel Wittenauer <Joel.Wittenauer@cryptography.com>
Reviewed-by: Thi Nguyen <thin@rambus.com>
---
 drivers/crypto/cmh/Makefile         |   6 +-
 drivers/crypto/cmh/cmh_hcq.c        | 313 +++++++++++++++++++++++
 drivers/crypto/cmh/cmh_main.c       |  24 ++
 drivers/crypto/cmh/cmh_pqc_lms.c    | 230 +++++++++++++++++
 drivers/crypto/cmh/cmh_pqc_slhdsa.c | 377 ++++++++++++++++++++++++++++
 drivers/crypto/cmh/cmh_pqc_xmss.c   | 230 +++++++++++++++++
 6 files changed, 1179 insertions(+), 1 deletion(-)
 create mode 100644 drivers/crypto/cmh/cmh_hcq.c
 create mode 100644 drivers/crypto/cmh/cmh_pqc_lms.c
 create mode 100644 drivers/crypto/cmh/cmh_pqc_slhdsa.c
 create mode 100644 drivers/crypto/cmh/cmh_pqc_xmss.c

diff --git a/drivers/crypto/cmh/Makefile b/drivers/crypto/cmh/Makefile
index 3425eb65d653..c3332804a9d7 100644
--- a/drivers/crypto/cmh/Makefile
+++ b/drivers/crypto/cmh/Makefile
@@ -36,7 +36,11 @@ cmh-y := \
        cmh_pke_ecdh.o \
        cmh_qse.o \
        cmh_pqc_mldsa.o \
-       cmh_pqc_sizes.o
+       cmh_pqc_sizes.o \
+       cmh_hcq.o \
+       cmh_pqc_slhdsa.o \
+       cmh_pqc_lms.o \
+       cmh_pqc_xmss.o

 # Management ioctl device (/dev/cmh_mgmt): key lifecycle, PKE, PQC ioctls.
 cmh-$(CONFIG_CRYPTO_DEV_CMH_MGMT) += \
diff --git a/drivers/crypto/cmh/cmh_hcq.c b/drivers/crypto/cmh/cmh_hcq.c
new file mode 100644
index 000000000000..8fc3a5cb0f9f
--- /dev/null
+++ b/drivers/crypto/cmh/cmh_hcq.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Cryptography Research, Inc. (CRI).
+ * CMH LKM -- HCQ Core VCQ Builders
+ *
+ * VCQ builder functions for SLH-DSA, LMS, and XMSS commands.
+ * Each function populates a single vcq_cmd slot.  Callers assemble
+ * complete VCQs with header + command(s) + flush, then submit via
+ * cmh_tm_submit_sync().
+ */
+
+#include <linux/string.h>
+
+#include "cmh_sys.h"
+
+/* -- HCQ flush -- */
+
+/**
+ * vcq_add_hcq_flush() - Build an HCQ flush VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ */
+void vcq_add_hcq_flush(struct vcq_cmd *slot, u32 core_id)
+{
+       vcq_add_flush(slot, core_id);
+}
+
+/* -- SLH-DSA -- */
+
+/**
+ * vcq_add_hcq_slhdsa_keygen() - Build an SLH-DSA key generation VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @param_set: SLH-DSA parameter set identifier
+ * @seed_len: Length of seed buffer in bytes
+ * @pk_len: Length of public key buffer in bytes
+ * @sk_len: Length of secret key buffer in bytes
+ * @seed: DMA address of seed input buffer
+ * @pk: DMA address of public key output buffer
+ * @sk: DMA address of secret key output buffer
+ */
+void vcq_add_hcq_slhdsa_keygen(struct vcq_cmd *slot, u32 core_id, u32 param_set,
+                              u32 seed_len, u32 pk_len, u32 sk_len,
+                              u64 seed, u64 pk, u64 sk)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_SLHDSA_KEYGEN);
+       slot->hwc.hcq.cmd_slhdsa_keygen.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_keygen.seed_len = seed_len;
+       slot->hwc.hcq.cmd_slhdsa_keygen.pk_len = pk_len;
+       slot->hwc.hcq.cmd_slhdsa_keygen.sk_len = sk_len;
+       slot->hwc.hcq.cmd_slhdsa_keygen.seed = seed;
+       slot->hwc.hcq.cmd_slhdsa_keygen.pk = pk;
+       slot->hwc.hcq.cmd_slhdsa_keygen.sk = sk;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_sign() - Build an SLH-DSA signing VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @param_set: SLH-DSA parameter set identifier
+ * @msg_len: Length of message buffer in bytes
+ * @ctx_len: Length of context string in bytes
+ * @add_random: DMA address of additional randomness buffer
+ * @msg: DMA address of message buffer
+ * @ctx: DMA address of context string buffer
+ * @sk: DMA address of secret key buffer
+ * @sig: DMA address of signature output buffer
+ */
+void vcq_add_hcq_slhdsa_sign(struct vcq_cmd *slot, u32 core_id, u32 param_set,
+                            u32 msg_len, u32 ctx_len,
+                            u64 add_random, u64 msg, u64 ctx,
+                            u64 sk, u64 sig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_SLHDSA_SIGN);
+       slot->hwc.hcq.cmd_slhdsa_sign.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_sign.message_len = msg_len;
+       slot->hwc.hcq.cmd_slhdsa_sign.add_random = add_random;
+       slot->hwc.hcq.cmd_slhdsa_sign.message = msg;
+       slot->hwc.hcq.cmd_slhdsa_sign.context = ctx;
+       slot->hwc.hcq.cmd_slhdsa_sign.sk = sk;
+       slot->hwc.hcq.cmd_slhdsa_sign.sig = sig;
+       slot->hwc.hcq.cmd_slhdsa_sign.context_len = ctx_len;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_sign_internal() - Build an SLH-DSA internal signing VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @param_set: SLH-DSA parameter set identifier
+ * @msg_len: Length of message buffer in bytes
+ * @add_random: DMA address of additional randomness buffer
+ * @msg: DMA address of message buffer
+ * @sk: DMA address of secret key buffer
+ * @sig: DMA address of signature output buffer
+ */
+void vcq_add_hcq_slhdsa_sign_internal(struct vcq_cmd *slot, u32 core_id, u32 param_set,
+                                     u32 msg_len, u64 add_random,
+                                     u64 msg, u64 sk, u64 sig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_SLHDSA_SIGN_INTERNAL);
+       slot->hwc.hcq.cmd_slhdsa_sign_internal.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_sign_internal.message_len = msg_len;
+       slot->hwc.hcq.cmd_slhdsa_sign_internal.add_random = add_random;
+       slot->hwc.hcq.cmd_slhdsa_sign_internal.message = msg;
+       slot->hwc.hcq.cmd_slhdsa_sign_internal.sk = sk;
+       slot->hwc.hcq.cmd_slhdsa_sign_internal.sig = sig;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_verify() - Build an SLH-DSA verification VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @param_set: SLH-DSA parameter set identifier
+ * @msg_len: Length of message buffer in bytes
+ * @ctx_len: Length of context string in bytes
+ * @msg: DMA address of message buffer
+ * @ctx: DMA address of context string buffer
+ * @pk: DMA address of public key buffer
+ * @sig: DMA address of signature buffer to verify
+ */
+void vcq_add_hcq_slhdsa_verify(struct vcq_cmd *slot, u32 core_id, u32 param_set,
+                              u32 msg_len, u32 ctx_len,
+                              u64 msg, u64 ctx, u64 pk, u64 sig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_SLHDSA_VERIFY);
+       slot->hwc.hcq.cmd_slhdsa_verify.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_verify.message_len = msg_len;
+       slot->hwc.hcq.cmd_slhdsa_verify.message = msg;
+       slot->hwc.hcq.cmd_slhdsa_verify.context = ctx;
+       slot->hwc.hcq.cmd_slhdsa_verify.pk = pk;
+       slot->hwc.hcq.cmd_slhdsa_verify.sig = sig;
+       slot->hwc.hcq.cmd_slhdsa_verify.context_len = ctx_len;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_sign_prehash() - Build an SLH-DSA prehash signing VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @cmd: VCQ command ID (sign-prehash variant)
+ * @param_set: SLH-DSA parameter set identifier
+ * @prehash_algo: Prehash algorithm identifier
+ * @msg_len: Length of message buffer in bytes
+ * @ctx_len: Length of context string in bytes
+ * @add_random: DMA address of additional randomness buffer
+ * @msg: DMA address of message buffer
+ * @ctx: DMA address of context string buffer
+ * @sk: DMA address of secret key buffer
+ * @sig: DMA address of signature output buffer
+ */
+void vcq_add_hcq_slhdsa_sign_prehash(struct vcq_cmd *slot, u32 core_id,
+                                    u32 cmd, u32 param_set, u32 prehash_algo,
+                                    u32 msg_len, u32 ctx_len,
+                                    u64 add_random, u64 msg, u64 ctx,
+                                    u64 sk, u64 sig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, cmd);
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.prehash_algo = prehash_algo;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.message_len = msg_len;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.context_len = ctx_len;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.add_random = add_random;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.message = msg;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.context = ctx;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.sk = sk;
+       slot->hwc.hcq.cmd_slhdsa_sign_prehash.sig = sig;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_verify_prehash() - Build an SLH-DSA prehash verify VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @cmd: VCQ command ID (verify-prehash variant)
+ * @param_set: SLH-DSA parameter set identifier
+ * @prehash_algo: Prehash algorithm identifier
+ * @msg_len: Length of message buffer in bytes
+ * @ctx_len: Length of context string in bytes
+ * @msg: DMA address of message buffer
+ * @ctx: DMA address of context string buffer
+ * @pk: DMA address of public key buffer
+ * @sig: DMA address of signature buffer to verify
+ */
+void vcq_add_hcq_slhdsa_verify_prehash(struct vcq_cmd *slot, u32 core_id,
+                                      u32 cmd, u32 param_set, u32 prehash_algo,
+                                      u32 msg_len, u32 ctx_len,
+                                      u64 msg, u64 ctx, u64 pk, u64 sig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, cmd);
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.prehash_algo = prehash_algo;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.message_len = msg_len;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.context_len = ctx_len;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.message = msg;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.context = ctx;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.pk = pk;
+       slot->hwc.hcq.cmd_slhdsa_verify_prehash.sig = sig;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_verify_internal() - Build an SLH-DSA internal verify VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @param_set: SLH-DSA parameter set identifier
+ * @msg_len: Length of message buffer in bytes
+ * @msg: DMA address of message buffer
+ * @pk: DMA address of public key buffer
+ * @sig: DMA address of signature buffer to verify
+ */
+void vcq_add_hcq_slhdsa_verify_internal(struct vcq_cmd *slot, u32 core_id, u32 param_set,
+                                       u32 msg_len, u64 msg, u64 pk, u64 sig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1,
+                             HCQ_CMD_SLHDSA_VERIFY_INTERNAL);
+       slot->hwc.hcq.cmd_slhdsa_verify_internal.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_verify_internal.message_len = msg_len;
+       slot->hwc.hcq.cmd_slhdsa_verify_internal.message = msg;
+       slot->hwc.hcq.cmd_slhdsa_verify_internal.pk = pk;
+       slot->hwc.hcq.cmd_slhdsa_verify_internal.sig = sig;
+}
+
+/**
+ * vcq_add_hcq_slhdsa_pubgen() - Build an SLH-DSA public key generation VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @param_set: SLH-DSA parameter set identifier
+ * @sk_len: Length of secret key buffer in bytes
+ * @sk: DMA address of secret key input buffer
+ * @pk: DMA address of public key output buffer
+ */
+void vcq_add_hcq_slhdsa_pubgen(struct vcq_cmd *slot, u32 core_id, u32 param_set,
+                              u32 sk_len, u64 sk, u64 pk)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_SLHDSA_PUBGEN);
+       slot->hwc.hcq.cmd_slhdsa_pubgen.parameter_set = param_set;
+       slot->hwc.hcq.cmd_slhdsa_pubgen.sk_len = sk_len;
+       slot->hwc.hcq.cmd_slhdsa_pubgen.sk = sk;
+       slot->hwc.hcq.cmd_slhdsa_pubgen.pk = pk;
+}
+
+/* -- LMS -- */
+
+/**
+ * vcq_add_hcq_lms_verify() - Build an LMS/HSS signature verify VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @lms_hss: LMS/HSS mode flag (0 = LMS, 1 = HSS)
+ * @pk_len: Length of public key buffer in bytes
+ * @sig_len: Length of signature buffer in bytes
+ * @dig_len: Length of digest buffer in bytes
+ * @pk: DMA address of public key buffer
+ * @sig: DMA address of signature buffer
+ * @dig: DMA address of digest buffer
+ */
+void vcq_add_hcq_lms_verify(struct vcq_cmd *slot, u32 core_id, u32 lms_hss,
+                           u32 pk_len, u32 sig_len, u32 dig_len,
+                           u64 pk, u64 sig, u64 dig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_LMS_VERIFY);
+       slot->hwc.hcq.cmd_lms_verify.lms_hss = lms_hss;
+       slot->hwc.hcq.cmd_lms_verify.pk_len = pk_len;
+       slot->hwc.hcq.cmd_lms_verify.sig_len = sig_len;
+       slot->hwc.hcq.cmd_lms_verify.dig_len = dig_len;
+       slot->hwc.hcq.cmd_lms_verify.pk = pk;
+       slot->hwc.hcq.cmd_lms_verify.sig = sig;
+       slot->hwc.hcq.cmd_lms_verify.dig = dig;
+}
+
+/* -- XMSS -- */
+
+/**
+ * vcq_add_hcq_xmss_verify() - Build an XMSS/XMSS^MT signature verify VCQ command
+ * @slot: VCQ command slot to populate
+ * @core_id: Hardware core ID for dispatch
+ * @xmss_mt: XMSS/XMSS^MT mode flag (0 = XMSS, 1 = XMSS^MT)
+ * @pk_len: Length of public key buffer in bytes
+ * @sig_len: Length of signature buffer in bytes
+ * @dig_len: Length of digest buffer in bytes
+ * @pk: DMA address of public key buffer
+ * @sig: DMA address of signature buffer
+ * @dig: DMA address of digest buffer
+ */
+void vcq_add_hcq_xmss_verify(struct vcq_cmd *slot, u32 core_id, u32 xmss_mt,
+                            u32 pk_len, u32 sig_len, u32 dig_len,
+                            u64 pk, u64 sig, u64 dig)
+{
+       memset(slot, 0, sizeof(*slot));
+       slot->magic = VCQ_CMD_MAGIC;
+       slot->id = VCQ_CMD_ID(core_id, 0, 1, HCQ_CMD_XMSS_VERIFY);
+       slot->hwc.hcq.cmd_xmss_verify.xmss_mt = xmss_mt;
+       slot->hwc.hcq.cmd_xmss_verify.pk_len = pk_len;
+       slot->hwc.hcq.cmd_xmss_verify.sig_len = sig_len;
+       slot->hwc.hcq.cmd_xmss_verify.dig_len = dig_len;
+       slot->hwc.hcq.cmd_xmss_verify.pk = pk;
+       slot->hwc.hcq.cmd_xmss_verify.sig = sig;
+       slot->hwc.hcq.cmd_xmss_verify.dig = dig;
+}
diff --git a/drivers/crypto/cmh/cmh_main.c b/drivers/crypto/cmh/cmh_main.c
index df38f43dc179..03c127083507 100644
--- a/drivers/crypto/cmh/cmh_main.c
+++ b/drivers/crypto/cmh/cmh_main.c
@@ -297,6 +297,21 @@ static int cmh_probe(struct platform_device *pdev)
        if (ret)
                goto err_pqc_mldsa_register;

+       /* Register PQC SLH-DSA */
+       ret = cmh_pqc_slhdsa_register();
+       if (ret)
+               goto err_pqc_slhdsa_register;
+
+       /* Register PQC LMS */
+       ret = cmh_pqc_lms_register();
+       if (ret)
+               goto err_pqc_lms_register;
+
+       /* Register PQC XMSS */
+       ret = cmh_pqc_xmss_register();
+       if (ret)
+               goto err_pqc_xmss_register;
+
        /* Register key management device (/dev/cmh_mgmt) */
        ret = cmh_mgmt_register();
        if (ret)
@@ -309,6 +324,12 @@ static int cmh_probe(struct platform_device *pdev)
        return 0;

 err_mgmt_register:
+       cmh_pqc_xmss_unregister();
+err_pqc_xmss_register:
+       cmh_pqc_lms_unregister();
+err_pqc_lms_register:
+       cmh_pqc_slhdsa_unregister();
+err_pqc_slhdsa_register:
        cmh_pqc_mldsa_unregister();
 err_pqc_mldsa_register:
        cmh_pke_ecdh_unregister();
@@ -373,6 +394,9 @@ static void cmh_remove(struct platform_device *pdev)
        cfg = &dev->config;

        cmh_mgmt_unregister();
+       cmh_pqc_xmss_unregister();
+       cmh_pqc_lms_unregister();
+       cmh_pqc_slhdsa_unregister();
        cmh_pqc_mldsa_unregister();
        cmh_pke_ecdh_unregister();
        cmh_pke_ecdsa_unregister();
diff --git a/drivers/crypto/cmh/cmh_pqc_lms.c b/drivers/crypto/cmh/cmh_pqc_lms.c
new file mode 100644
index 000000000000..13b2e26aa7bd
--- /dev/null
+++ b/drivers/crypto/cmh/cmh_pqc_lms.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Cryptography Research, Inc. (CRI).
+ * CMH LKM -- LMS/HSS Signature Driver (verify-only, sig_alg, synchronous)
+ *
+ * Registers "lms" and "lms-hss" sig algorithms with verify-only
+ * callbacks.  Sign is not supported (stateful signature -- key
+ * management must happen externally).
+ *
+ * Verify: src = raw signature, digest = message bytes
+ * Public key: raw pk bytes (variable length, set via set_pub_key)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <crypto/sig.h>
+#include <crypto/internal/sig.h>
+
+#include "cmh_sys.h"
+#include "cmh_hcq_abi.h"
+#include "cmh_txn.h"
+#include "cmh_dma.h"
+#include "cmh_pqc.h"
+
+#define LMS_VCQ_CMDS   3       /* header + cmd + flush */
+
+struct cmh_lms_tfm_ctx {
+       u8 *pub_key;
+       u32 pub_key_len;
+       u32 lms_hss;            /* 0 = LMS, 1 = LMS-HSS */
+};
+
+static inline struct cmh_lms_tfm_ctx *cmh_lms_ctx(struct crypto_sig *tfm)
+{
+       return crypto_sig_ctx(tfm);
+}
+
+/*
+ * LMS/HSS verify (synchronous sig_alg)
+ *
+ * @src:    raw signature
+ * @slen:   signature length
+ * @digest: message bytes
+ * @dlen:   message length
+ *
+ * Returns 0 on successful verification, negative errno on failure.
+ */
+static int cmh_lms_verify(struct crypto_sig *tfm,
+                         const void *src, unsigned int slen,
+                         const void *digest, unsigned int dlen)
+{
+       struct cmh_lms_tfm_ctx *ctx = cmh_lms_ctx(tfm);
+       struct core_dispatch d = cmh_core_select_instance(CMH_CORE_HCQ);
+       struct vcq_cmd vcq[LMS_VCQ_CMDS];
+       u8 *sig_buf = NULL, *m_buf = NULL, *pk_buf = NULL;
+       dma_addr_t sig_dma = DMA_MAPPING_ERROR;
+       dma_addr_t m_dma = DMA_MAPPING_ERROR;
+       dma_addr_t pk_dma = DMA_MAPPING_ERROR;
+       int ret;
+
+       if (!ctx->pub_key)
+               return -EINVAL;
+       if (!slen || slen > LMS_MAX_SIG_LEN)
+               return -EINVAL;
+       if (!dlen || dlen > LMS_MAX_MSG_LEN)
+               return -EINVAL;
+
+       sig_buf = kmemdup(src, slen, GFP_KERNEL);
+       m_buf = kmemdup(digest, dlen, GFP_KERNEL);
+       pk_buf = kmemdup(ctx->pub_key, ctx->pub_key_len, GFP_KERNEL);
+       if (!sig_buf || !m_buf || !pk_buf) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       sig_dma = cmh_dma_map_single(sig_buf, slen, DMA_TO_DEVICE);
+       m_dma = cmh_dma_map_single(m_buf, dlen, DMA_TO_DEVICE);
+       pk_dma = cmh_dma_map_single(pk_buf, ctx->pub_key_len, DMA_TO_DEVICE);
+
+       if (cmh_dma_map_error(sig_dma) || cmh_dma_map_error(m_dma) ||
+           cmh_dma_map_error(pk_dma)) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       vcq_set_header(&vcq[0], LMS_VCQ_CMDS);
+       vcq_add_hcq_lms_verify(&vcq[1], d.core_id, ctx->lms_hss,
+                              ctx->pub_key_len, slen, dlen,
+                              pk_dma, sig_dma, m_dma);
+       vcq_add_hcq_flush(&vcq[2], d.core_id);
+
+       /* LMS verify traverses Merkle hash chains -- inherently slow */
+       ret = cmh_tm_submit_sync_tmo(vcq, LMS_VCQ_CMDS, 1, d.mbx_idx,
+                                    cmh_tm_slow_op_timeout_jiffies());
+
+out_unmap:
+       if (!cmh_dma_map_error(pk_dma))
+               cmh_dma_unmap_single(pk_dma, ctx->pub_key_len, DMA_TO_DEVICE);
+       if (!cmh_dma_map_error(m_dma))
+               cmh_dma_unmap_single(m_dma, dlen, DMA_TO_DEVICE);
+       if (!cmh_dma_map_error(sig_dma))
+               cmh_dma_unmap_single(sig_dma, slen, DMA_TO_DEVICE);
+
+out_free:
+       kfree(pk_buf);
+       kfree(m_buf);
+       kfree(sig_buf);
+       return ret;
+}
+
+static int cmh_lms_set_pub_key(struct crypto_sig *tfm,
+                              const void *key, unsigned int keylen)
+{
+       struct cmh_lms_tfm_ctx *ctx = cmh_lms_ctx(tfm);
+
+       if (!keylen || keylen > LMS_MAX_PK_LEN)
+               return -EINVAL;
+
+       kfree(ctx->pub_key);
+       ctx->pub_key = NULL;
+       ctx->pub_key_len = 0;
+
+       ctx->pub_key = kmemdup(key, keylen, GFP_KERNEL);
+       if (!ctx->pub_key)
+               return -ENOMEM;
+
+       ctx->pub_key_len = keylen;
+       return 0;
+}
+
+static unsigned int cmh_lms_key_size(struct crypto_sig *tfm)
+{
+       struct cmh_lms_tfm_ctx *ctx = cmh_lms_ctx(tfm);
+
+       return ctx->pub_key_len * 8;
+}
+
+static int cmh_lms_init(struct crypto_sig *tfm)
+{
+       struct cmh_lms_tfm_ctx *ctx = cmh_lms_ctx(tfm);
+
+       memset(ctx, 0, sizeof(*ctx));
+       return 0;
+}
+
+static int cmh_lms_hss_init(struct crypto_sig *tfm)
+{
+       struct cmh_lms_tfm_ctx *ctx = cmh_lms_ctx(tfm);
+
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->lms_hss = 1;
+       return 0;
+}
+
+static void cmh_lms_exit(struct crypto_sig *tfm)
+{
+       struct cmh_lms_tfm_ctx *ctx = cmh_lms_ctx(tfm);
+
+       kfree(ctx->pub_key);
+       ctx->pub_key = NULL;
+}
+
+static struct sig_alg cmh_lms_algs[] = {
+       {
+               .verify         = cmh_lms_verify,
+               .set_pub_key    = cmh_lms_set_pub_key,
+               .key_size       = cmh_lms_key_size,
+               .init           = cmh_lms_init,
+               .exit           = cmh_lms_exit,
+               .base = {
+                       .cra_name         = "lms",
+                       .cra_driver_name  = "cri-cmh-lms",
+                       .cra_priority     = 300,
+                       .cra_module       = THIS_MODULE,
+                       .cra_ctxsize      = sizeof(struct cmh_lms_tfm_ctx),
+               },
+       },
+       {
+               .verify         = cmh_lms_verify,
+               .set_pub_key    = cmh_lms_set_pub_key,
+               .key_size       = cmh_lms_key_size,
+               .init           = cmh_lms_hss_init,
+               .exit           = cmh_lms_exit,
+               .base = {
+                       .cra_name         = "lms-hss",
+                       .cra_driver_name  = "cri-cmh-lms-hss",
+                       .cra_priority     = 300,
+                       .cra_module       = THIS_MODULE,
+                       .cra_ctxsize      = sizeof(struct cmh_lms_tfm_ctx),
+               },
+       },
+};
+
+/**
+ * cmh_pqc_lms_register() - Register LMS/LMS-HSS sig algorithms with the crypto framework
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int cmh_pqc_lms_register(void)
+{
+       int ret, i;
+
+       for (i = 0; i < ARRAY_SIZE(cmh_lms_algs); i++) {
+               ret = crypto_register_sig(&cmh_lms_algs[i]);
+               if (ret) {
+                       dev_err(cmh_dev(), "cmh: failed to register %s (%d)\n",
+                               cmh_lms_algs[i].base.cra_name, ret);
+                       goto err_unregister;
+               }
+       }
+
+       return 0;
+
+err_unregister:
+       while (i--)
+               crypto_unregister_sig(&cmh_lms_algs[i]);
+       return ret;
+}
+
+/**
+ * cmh_pqc_lms_unregister() - Unregister LMS/LMS-HSS sig algorithms from the crypto framework
+ */
+void cmh_pqc_lms_unregister(void)
+{
+       int i = ARRAY_SIZE(cmh_lms_algs);
+
+       while (i--)
+               crypto_unregister_sig(&cmh_lms_algs[i]);
+}
diff --git a/drivers/crypto/cmh/cmh_pqc_slhdsa.c b/drivers/crypto/cmh/cmh_pqc_slhdsa.c
new file mode 100644
index 000000000000..9cc8cdb442b2
--- /dev/null
+++ b/drivers/crypto/cmh/cmh_pqc_slhdsa.c
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Cryptography Research, Inc. (CRI).
+ * CMH LKM -- SLH-DSA Signature Driver (sig_alg, synchronous)
+ *
+ * Registers SLH-DSA sig algorithms for all 12 parameter sets
+ * (SHAKE/SHA2 x 128/192/256 x s/f) with sign and verify callbacks.
+ *
+ * Key format:
+ *   Public key  = raw pk bytes (2*n bytes)
+ *   Private key = raw sk bytes (4*n)
+ *
+ * Sign: src = message, dst = raw signature
+ * Verify: src = raw signature, digest = message bytes
+ *
+ * Private keys are raw (written to SYS_REF_TEMP per-operation).
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+#include <crypto/sig.h>
+#include <crypto/internal/sig.h>
+
+#include "cmh_sys.h"
+#include "cmh_qse_abi.h"
+#include "cmh_hcq_abi.h"
+#include "cmh_txn.h"
+#include "cmh_dma.h"
+#include "cmh_key.h"
+#include "cmh_pqc.h"
+
+struct cmh_slhdsa_tfm_ctx {
+       struct cmh_key_ctx key;         /* private key (raw sk bytes) */
+       u8 *pub_key;
+       u32 pub_key_len;
+       u32 param_set;                  /* HCQ_SLHDSA_SHAKE_128S .. SHA2_256F */
+};
+
+static inline struct cmh_slhdsa_tfm_ctx *cmh_slhdsa_ctx(struct crypto_sig *tfm)
+{
+       return crypto_sig_ctx(tfm);
+}
+
+/*
+ * SLH-DSA sign (synchronous sig_alg)
+ *
+ * @src:  message bytes
+ * @slen: message length
+ * @dst:  signature output buffer
+ * @dlen: output buffer length
+ *
+ * Returns signature length on success, negative errno on failure.
+ * Uses raw private keys written to SYS_REF_TEMP per-operation.
+ */
+static int cmh_slhdsa_sign(struct crypto_sig *tfm,
+                          const void *src, unsigned int slen,
+                          void *dst, unsigned int dlen)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+       u32 sig_sz = slhdsa_get_sig_size(ctx->param_set);
+       u32 sk_sz = slhdsa_sk_size(ctx->param_set);
+       struct vcq_cmd vcq[HCQ_VCQ_CMDS_MAX]; /* raw: hdr+write+sign+flush */
+       u32 vcq_count;
+       u8 *m_buf = NULL, *sig_buf = NULL, *sk_buf = NULL;
+       dma_addr_t m_dma = DMA_MAPPING_ERROR;
+       dma_addr_t sig_dma = DMA_MAPPING_ERROR;
+       dma_addr_t sk_dma = DMA_MAPPING_ERROR;
+       int ret, idx;
+
+       if (ctx->key.mode == CMH_KEY_NONE)
+               return -EINVAL;
+       if (dlen < sig_sz)
+               return -EINVAL;
+       if (!slen || slen > SLHDSA_MAX_MSG_LEN)
+               return -EINVAL;
+
+       m_buf = kmemdup(src, slen, GFP_KERNEL);
+       sig_buf = kzalloc(sig_sz, GFP_KERNEL);
+       if (!m_buf || !sig_buf) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       m_dma = cmh_dma_map_single(m_buf, slen, DMA_TO_DEVICE);
+       sig_dma = cmh_dma_map_single(sig_buf, sig_sz, DMA_FROM_DEVICE);
+       if (cmh_dma_map_error(m_dma) || cmh_dma_map_error(sig_dma)) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       sk_dma = DMA_MAPPING_ERROR;
+       idx = 0;
+
+       struct core_dispatch d;
+
+       d = cmh_core_select_instance(CMH_CORE_HCQ);
+
+       if (ctx->key.raw.len != sk_sz) {
+               ret = -EINVAL;
+               goto out_unmap;
+       }
+       sk_buf = kmemdup(ctx->key.raw.data, ctx->key.raw.len,
+                        GFP_KERNEL);
+       if (!sk_buf) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+       sk_dma = cmh_dma_map_single(sk_buf, sk_sz, DMA_TO_DEVICE);
+       if (cmh_dma_map_error(sk_dma)) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       vcq_count = HCQ_VCQ_CMDS_MIN + 1;
+       vcq_set_header(&vcq[idx++], vcq_count);
+       vcq_add_sys_write(&vcq[idx++], SYS_REF_TEMP, sk_dma,
+                         SYS_REF_NONE, sk_sz,
+                         ctx->key.raw.sys_type);
+       vcq_add_hcq_slhdsa_sign_internal(&vcq[idx++], d.core_id,
+                                        ctx->param_set,
+                                        slen, 0,
+                                        m_dma, SYS_REF_TEMP,
+                                        sig_dma);
+       vcq_add_hcq_flush(&vcq[idx++], d.core_id);
+
+       ret = cmh_tm_submit_sync_tmo(vcq, vcq_count, 1, d.mbx_idx,
+                                    cmh_tm_slow_op_timeout_jiffies());
+
+       if (!ret) {
+               /* Sync bounce buffer so CPU sees the DMA-written signature */
+               cmh_dma_sync_for_cpu(sig_dma, sig_sz, DMA_FROM_DEVICE);
+               memcpy(dst, sig_buf, sig_sz);
+               ret = sig_sz;
+       }
+
+out_unmap:
+       if (sk_buf) {
+               if (!cmh_dma_map_error(sk_dma))
+                       cmh_dma_unmap_single(sk_dma, sk_sz, DMA_TO_DEVICE);
+               kfree_sensitive(sk_buf);
+       }
+       if (!cmh_dma_map_error(sig_dma))
+               cmh_dma_unmap_single(sig_dma, sig_sz, DMA_FROM_DEVICE);
+       if (!cmh_dma_map_error(m_dma))
+               cmh_dma_unmap_single(m_dma, slen, DMA_TO_DEVICE);
+
+out_free:
+       kfree(sig_buf);
+       kfree(m_buf);
+       return ret;
+}
+
+/*
+ * SLH-DSA verify (synchronous sig_alg)
+ *
+ * @src:    raw signature
+ * @slen:   signature length
+ * @digest: message bytes
+ * @dlen:   message length
+ *
+ * Returns 0 on successful verification, negative errno on failure.
+ */
+static int cmh_slhdsa_verify(struct crypto_sig *tfm,
+                            const void *src, unsigned int slen,
+                            const void *digest, unsigned int dlen)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+       u32 sig_sz = slhdsa_get_sig_size(ctx->param_set);
+       u32 pk_sz = slhdsa_pk_size(ctx->param_set);
+       struct core_dispatch d = cmh_core_select_instance(CMH_CORE_HCQ);
+       struct vcq_cmd vcq[HCQ_VCQ_CMDS_MIN];
+       u8 *sig_buf = NULL, *m_buf = NULL, *pk_buf = NULL;
+       dma_addr_t sig_dma = DMA_MAPPING_ERROR;
+       dma_addr_t m_dma = DMA_MAPPING_ERROR;
+       dma_addr_t pk_dma = DMA_MAPPING_ERROR;
+       int ret;
+
+       if (!ctx->pub_key)
+               return -EINVAL;
+       if (slen != sig_sz)
+               return -EINVAL;
+       if (!dlen || dlen > SLHDSA_MAX_MSG_LEN)
+               return -EINVAL;
+
+       sig_buf = kmemdup(src, slen, GFP_KERNEL);
+       m_buf = kmemdup(digest, dlen, GFP_KERNEL);
+       pk_buf = kmemdup(ctx->pub_key, pk_sz, GFP_KERNEL);
+       if (!sig_buf || !m_buf || !pk_buf) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       sig_dma = cmh_dma_map_single(sig_buf, sig_sz, DMA_TO_DEVICE);
+       m_dma = cmh_dma_map_single(m_buf, dlen, DMA_TO_DEVICE);
+       pk_dma = cmh_dma_map_single(pk_buf, pk_sz, DMA_TO_DEVICE);
+
+       if (cmh_dma_map_error(sig_dma) || cmh_dma_map_error(m_dma) ||
+           cmh_dma_map_error(pk_dma)) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       vcq_set_header(&vcq[0], HCQ_VCQ_CMDS_MIN);
+       vcq_add_hcq_slhdsa_verify_internal(&vcq[1], d.core_id, ctx->param_set,
+                                          dlen, m_dma, pk_dma, sig_dma);
+       vcq_add_hcq_flush(&vcq[2], d.core_id);
+
+       /* SLH-DSA verify recomputes hyper-tree hashes -- inherently slow */
+       ret = cmh_tm_submit_sync_tmo(vcq, HCQ_VCQ_CMDS_MIN, 1, d.mbx_idx,
+                                    cmh_tm_slow_op_timeout_jiffies());
+
+out_unmap:
+       if (!cmh_dma_map_error(pk_dma))
+               cmh_dma_unmap_single(pk_dma, pk_sz, DMA_TO_DEVICE);
+       if (!cmh_dma_map_error(m_dma))
+               cmh_dma_unmap_single(m_dma, dlen, DMA_TO_DEVICE);
+       if (!cmh_dma_map_error(sig_dma))
+               cmh_dma_unmap_single(sig_dma, sig_sz, DMA_TO_DEVICE);
+
+out_free:
+       kfree(pk_buf);
+       kfree(m_buf);
+       kfree(sig_buf);
+       return ret;
+}
+
+static int cmh_slhdsa_set_pub_key(struct crypto_sig *tfm,
+                                 const void *key, unsigned int keylen)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+       u32 expected = slhdsa_pk_size(ctx->param_set);
+
+       if (keylen != expected)
+               return -EINVAL;
+
+       kfree(ctx->pub_key);
+       ctx->pub_key = NULL;
+       ctx->pub_key_len = 0;
+
+       ctx->pub_key = kmemdup(key, keylen, GFP_KERNEL);
+       if (!ctx->pub_key)
+               return -ENOMEM;
+
+       ctx->pub_key_len = keylen;
+       return 0;
+}
+
+static int cmh_slhdsa_set_priv_key(struct crypto_sig *tfm,
+                                  const void *key, unsigned int keylen)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+
+       /* Raw sk (4*n bytes) */
+       if (keylen != slhdsa_sk_size(ctx->param_set))
+               return -EINVAL;
+
+       return cmh_key_setkey_raw(&ctx->key, key, keylen, CORE_ID_HCQ);
+}
+
+static unsigned int cmh_slhdsa_key_size(struct crypto_sig *tfm)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+
+       /* crypto_sig_keysize() returns bits, not bytes */
+       return slhdsa_pk_size(ctx->param_set) * 8;
+}
+
+static unsigned int cmh_slhdsa_max_size(struct crypto_sig *tfm)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+
+       return slhdsa_get_sig_size(ctx->param_set);
+}
+
+static void cmh_slhdsa_exit(struct crypto_sig *tfm)
+{
+       struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);
+
+       cmh_key_destroy(&ctx->key);
+       kfree(ctx->pub_key);
+       ctx->pub_key = NULL;
+}
+
+/* Generate init functions for all 12 parameter sets */
+#define SLHDSA_INIT(ps_val)                                            \
+       static int cmh_slhdsa_init_##ps_val(struct crypto_sig *tfm)     \
+       {                                                               \
+               struct cmh_slhdsa_tfm_ctx *ctx = cmh_slhdsa_ctx(tfm);   \
+               memset(ctx, 0, sizeof(*ctx));                           \
+               ctx->param_set = ps_val;                                \
+               return 0;                                               \
+       }
+
+SLHDSA_INIT(HCQ_SLHDSA_SHAKE_128S)
+SLHDSA_INIT(HCQ_SLHDSA_SHAKE_128F)
+SLHDSA_INIT(HCQ_SLHDSA_SHAKE_192S)
+SLHDSA_INIT(HCQ_SLHDSA_SHAKE_192F)
+SLHDSA_INIT(HCQ_SLHDSA_SHAKE_256S)
+SLHDSA_INIT(HCQ_SLHDSA_SHAKE_256F)
+SLHDSA_INIT(HCQ_SLHDSA_SHA2_128S)
+SLHDSA_INIT(HCQ_SLHDSA_SHA2_128F)
+SLHDSA_INIT(HCQ_SLHDSA_SHA2_192S)
+SLHDSA_INIT(HCQ_SLHDSA_SHA2_192F)
+SLHDSA_INIT(HCQ_SLHDSA_SHA2_256S)
+SLHDSA_INIT(HCQ_SLHDSA_SHA2_256F)
+
+#define SLHDSA_ALG(name, drv, ps_val) {                                        \
+               .sign           = cmh_slhdsa_sign,                      \
+               .verify         = cmh_slhdsa_verify,                    \
+               .set_pub_key    = cmh_slhdsa_set_pub_key,               \
+               .set_priv_key   = cmh_slhdsa_set_priv_key,              \
+               .key_size       = cmh_slhdsa_key_size,                  \
+               .max_size       = cmh_slhdsa_max_size,                  \
+               .init           = cmh_slhdsa_init_##ps_val,             \
+               .exit           = cmh_slhdsa_exit,                      \
+               .base = {                                               \
+                       .cra_name         = name,                       \
+                       .cra_driver_name  = drv,                        \
+                       .cra_priority     = 300,                        \
+                       .cra_module       = THIS_MODULE,                \
+                       .cra_ctxsize      = sizeof(struct cmh_slhdsa_tfm_ctx), \
+               },                                                      \
+       }
+
+static struct sig_alg cmh_slhdsa_algs[] = {
+       SLHDSA_ALG("slh-dsa-shake-128s", "cri-cmh-slh-dsa-shake-128s", HCQ_SLHDSA_SHAKE_128S),
+       SLHDSA_ALG("slh-dsa-shake-128f", "cri-cmh-slh-dsa-shake-128f", HCQ_SLHDSA_SHAKE_128F),
+       SLHDSA_ALG("slh-dsa-shake-192s", "cri-cmh-slh-dsa-shake-192s", HCQ_SLHDSA_SHAKE_192S),
+       SLHDSA_ALG("slh-dsa-shake-192f", "cri-cmh-slh-dsa-shake-192f", HCQ_SLHDSA_SHAKE_192F),
+       SLHDSA_ALG("slh-dsa-shake-256s", "cri-cmh-slh-dsa-shake-256s", HCQ_SLHDSA_SHAKE_256S),
+       SLHDSA_ALG("slh-dsa-shake-256f", "cri-cmh-slh-dsa-shake-256f", HCQ_SLHDSA_SHAKE_256F),
+       SLHDSA_ALG("slh-dsa-sha2-128s",  "cri-cmh-slh-dsa-sha2-128s",  HCQ_SLHDSA_SHA2_128S),
+       SLHDSA_ALG("slh-dsa-sha2-128f",  "cri-cmh-slh-dsa-sha2-128f",  HCQ_SLHDSA_SHA2_128F),
+       SLHDSA_ALG("slh-dsa-sha2-192s",  "cri-cmh-slh-dsa-sha2-192s",  HCQ_SLHDSA_SHA2_192S),
+       SLHDSA_ALG("slh-dsa-sha2-192f",  "cri-cmh-slh-dsa-sha2-192f",  HCQ_SLHDSA_SHA2_192F),
+       SLHDSA_ALG("slh-dsa-sha2-256s",  "cri-cmh-slh-dsa-sha2-256s",  HCQ_SLHDSA_SHA2_256S),
+       SLHDSA_ALG("slh-dsa-sha2-256f",  "cri-cmh-slh-dsa-sha2-256f",  HCQ_SLHDSA_SHA2_256F),
+};
+
+/**
+ * cmh_pqc_slhdsa_register() - Register SLH-DSA akcipher algorithms with the crypto framework
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int cmh_pqc_slhdsa_register(void)
+{
+       int ret, i;
+
+       for (i = 0; i < ARRAY_SIZE(cmh_slhdsa_algs); i++) {
+               ret = crypto_register_sig(&cmh_slhdsa_algs[i]);
+               if (ret) {
+                       dev_err(cmh_dev(), "cmh: failed to register %s (%d)\n",
+                               cmh_slhdsa_algs[i].base.cra_name, ret);
+                       goto err_unregister;
+               }
+       }
+
+       return 0;
+
+err_unregister:
+       while (i--)
+               crypto_unregister_sig(&cmh_slhdsa_algs[i]);
+       return ret;
+}
+
+/**
+ * cmh_pqc_slhdsa_unregister() - Unregister SLH-DSA akcipher algorithms from the crypto framework
+ */
+void cmh_pqc_slhdsa_unregister(void)
+{
+       int i = ARRAY_SIZE(cmh_slhdsa_algs);
+
+       while (i--)
+               crypto_unregister_sig(&cmh_slhdsa_algs[i]);
+}
diff --git a/drivers/crypto/cmh/cmh_pqc_xmss.c b/drivers/crypto/cmh/cmh_pqc_xmss.c
new file mode 100644
index 000000000000..433ffcd6463d
--- /dev/null
+++ b/drivers/crypto/cmh/cmh_pqc_xmss.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Cryptography Research, Inc. (CRI).
+ * CMH LKM -- XMSS/XMSS-MT Signature Driver (verify-only, sig_alg, synchronous)
+ *
+ * Registers "xmss" and "xmss-mt" sig algorithms with verify-only
+ * callbacks.  Sign is not supported (stateful signature -- key
+ * management must happen externally).
+ *
+ * Verify: src = raw signature, digest = message bytes
+ * Public key: raw pk bytes (variable length, set via set_pub_key)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <crypto/sig.h>
+#include <crypto/internal/sig.h>
+
+#include "cmh_sys.h"
+#include "cmh_hcq_abi.h"
+#include "cmh_txn.h"
+#include "cmh_dma.h"
+#include "cmh_pqc.h"
+
+#define XMSS_VCQ_CMDS  3       /* header + cmd + flush */
+
+struct cmh_xmss_tfm_ctx {
+       u8 *pub_key;
+       u32 pub_key_len;
+       u32 xmss_mt;            /* 0 = XMSS, 1 = XMSS-MT */
+};
+
+static inline struct cmh_xmss_tfm_ctx *cmh_xmss_ctx(struct crypto_sig *tfm)
+{
+       return crypto_sig_ctx(tfm);
+}
+
+/*
+ * XMSS/XMSS-MT verify (synchronous sig_alg)
+ *
+ * @src:    raw signature
+ * @slen:   signature length
+ * @digest: message bytes
+ * @dlen:   message length
+ *
+ * Returns 0 on successful verification, negative errno on failure.
+ */
+static int cmh_xmss_verify(struct crypto_sig *tfm,
+                          const void *src, unsigned int slen,
+                          const void *digest, unsigned int dlen)
+{
+       struct cmh_xmss_tfm_ctx *ctx = cmh_xmss_ctx(tfm);
+       struct core_dispatch d = cmh_core_select_instance(CMH_CORE_HCQ);
+       struct vcq_cmd vcq[XMSS_VCQ_CMDS];
+       u8 *sig_buf = NULL, *m_buf = NULL, *pk_buf = NULL;
+       dma_addr_t sig_dma = DMA_MAPPING_ERROR;
+       dma_addr_t m_dma = DMA_MAPPING_ERROR;
+       dma_addr_t pk_dma = DMA_MAPPING_ERROR;
+       int ret;
+
+       if (!ctx->pub_key)
+               return -EINVAL;
+       if (!slen || slen > XMSS_MAX_SIG_LEN)
+               return -EINVAL;
+       if (!dlen || dlen > XMSS_MAX_MSG_LEN)
+               return -EINVAL;
+
+       sig_buf = kmemdup(src, slen, GFP_KERNEL);
+       m_buf = kmemdup(digest, dlen, GFP_KERNEL);
+       pk_buf = kmemdup(ctx->pub_key, ctx->pub_key_len, GFP_KERNEL);
+       if (!sig_buf || !m_buf || !pk_buf) {
+               ret = -ENOMEM;
+               goto out_free;
+       }
+
+       sig_dma = cmh_dma_map_single(sig_buf, slen, DMA_TO_DEVICE);
+       m_dma = cmh_dma_map_single(m_buf, dlen, DMA_TO_DEVICE);
+       pk_dma = cmh_dma_map_single(pk_buf, ctx->pub_key_len, DMA_TO_DEVICE);
+
+       if (cmh_dma_map_error(sig_dma) || cmh_dma_map_error(m_dma) ||
+           cmh_dma_map_error(pk_dma)) {
+               ret = -ENOMEM;
+               goto out_unmap;
+       }
+
+       vcq_set_header(&vcq[0], XMSS_VCQ_CMDS);
+       vcq_add_hcq_xmss_verify(&vcq[1], d.core_id, ctx->xmss_mt,
+                               ctx->pub_key_len, slen, dlen,
+                               pk_dma, sig_dma, m_dma);
+       vcq_add_hcq_flush(&vcq[2], d.core_id);
+
+       /* XMSS verify traverses Merkle hash chains -- inherently slow */
+       ret = cmh_tm_submit_sync_tmo(vcq, XMSS_VCQ_CMDS, 1, d.mbx_idx,
+                                    cmh_tm_slow_op_timeout_jiffies());
+
+out_unmap:
+       if (!cmh_dma_map_error(pk_dma))
+               cmh_dma_unmap_single(pk_dma, ctx->pub_key_len, DMA_TO_DEVICE);
+       if (!cmh_dma_map_error(m_dma))
+               cmh_dma_unmap_single(m_dma, dlen, DMA_TO_DEVICE);
+       if (!cmh_dma_map_error(sig_dma))
+               cmh_dma_unmap_single(sig_dma, slen, DMA_TO_DEVICE);
+
+out_free:
+       kfree(pk_buf);
+       kfree(m_buf);
+       kfree(sig_buf);
+       return ret;
+}
+
+static int cmh_xmss_set_pub_key(struct crypto_sig *tfm,
+                               const void *key, unsigned int keylen)
+{
+       struct cmh_xmss_tfm_ctx *ctx = cmh_xmss_ctx(tfm);
+
+       if (!keylen || keylen > XMSS_MAX_PK_LEN)
+               return -EINVAL;
+
+       kfree(ctx->pub_key);
+       ctx->pub_key = NULL;
+       ctx->pub_key_len = 0;
+
+       ctx->pub_key = kmemdup(key, keylen, GFP_KERNEL);
+       if (!ctx->pub_key)
+               return -ENOMEM;
+
+       ctx->pub_key_len = keylen;
+       return 0;
+}
+
+static unsigned int cmh_xmss_key_size(struct crypto_sig *tfm)
+{
+       struct cmh_xmss_tfm_ctx *ctx = cmh_xmss_ctx(tfm);
+
+       return ctx->pub_key_len * 8;
+}
+
+static int cmh_xmss_init(struct crypto_sig *tfm)
+{
+       struct cmh_xmss_tfm_ctx *ctx = cmh_xmss_ctx(tfm);
+
+       memset(ctx, 0, sizeof(*ctx));
+       return 0;
+}
+
+static int cmh_xmss_mt_init(struct crypto_sig *tfm)
+{
+       struct cmh_xmss_tfm_ctx *ctx = cmh_xmss_ctx(tfm);
+
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->xmss_mt = 1;
+       return 0;
+}
+
+static void cmh_xmss_exit(struct crypto_sig *tfm)
+{
+       struct cmh_xmss_tfm_ctx *ctx = cmh_xmss_ctx(tfm);
+
+       kfree(ctx->pub_key);
+       ctx->pub_key = NULL;
+}
+
+static struct sig_alg cmh_xmss_algs[] = {
+       {
+               .verify         = cmh_xmss_verify,
+               .set_pub_key    = cmh_xmss_set_pub_key,
+               .key_size       = cmh_xmss_key_size,
+               .init           = cmh_xmss_init,
+               .exit           = cmh_xmss_exit,
+               .base = {
+                       .cra_name         = "xmss",
+                       .cra_driver_name  = "cri-cmh-xmss",
+                       .cra_priority     = 300,
+                       .cra_module       = THIS_MODULE,
+                       .cra_ctxsize      = sizeof(struct cmh_xmss_tfm_ctx),
+               },
+       },
+       {
+               .verify         = cmh_xmss_verify,
+               .set_pub_key    = cmh_xmss_set_pub_key,
+               .key_size       = cmh_xmss_key_size,
+               .init           = cmh_xmss_mt_init,
+               .exit           = cmh_xmss_exit,
+               .base = {
+                       .cra_name         = "xmss-mt",
+                       .cra_driver_name  = "cri-cmh-xmss-mt",
+                       .cra_priority     = 300,
+                       .cra_module       = THIS_MODULE,
+                       .cra_ctxsize      = sizeof(struct cmh_xmss_tfm_ctx),
+               },
+       },
+};
+
+/**
+ * cmh_pqc_xmss_register() - Register XMSS/XMSS-MT sig algorithms with the crypto framework
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int cmh_pqc_xmss_register(void)
+{
+       int ret, i;
+
+       for (i = 0; i < ARRAY_SIZE(cmh_xmss_algs); i++) {
+               ret = crypto_register_sig(&cmh_xmss_algs[i]);
+               if (ret) {
+                       dev_err(cmh_dev(), "cmh: failed to register %s (%d)\n",
+                               cmh_xmss_algs[i].base.cra_name, ret);
+                       goto err_unregister;
+               }
+       }
+
+       return 0;
+
+err_unregister:
+       while (i--)
+               crypto_unregister_sig(&cmh_xmss_algs[i]);
+       return ret;
+}
+
+/**
+ * cmh_pqc_xmss_unregister() - Unregister XMSS/XMSS-MT sig algorithms from the crypto framework
+ */
+void cmh_pqc_xmss_unregister(void)
+{
+       int i = ARRAY_SIZE(cmh_xmss_algs);
+
+       while (i--)
+               crypto_unregister_sig(&cmh_xmss_algs[i]);
+}
--
2.43.7


** This message and any attachments are for the sole use of the intended recipient(s). It may contain information that is confidential and privileged. If you are not the intended recipient of this message, you are prohibited from printing, copying, forwarding or saving it. Please delete the message and attachments and notify the sender immediately. **

Rambus Inc.<http://www.rambus.com>

  parent reply	other threads:[~2026-06-25 17:34 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-25 17:33 [PATCH 00/19] crypto: cmh - add CRI CryptoManager Hub driver Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 01/19] dt-bindings: crypto: add Rambus CryptoManager Hub Saravanakrishnan Krishnamoorthy
2026-06-26 10:55   ` Krzysztof Kozlowski
2026-06-26 17:15     ` Krishnamoorthy, Saravanakrishnan
2026-06-25 17:33 ` [PATCH 02/19] crypto: cmh - add core platform driver Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 03/19] crypto: cmh - add key provisioning and management Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 04/19] crypto: cmh - add SHA-2/SHA-3/SHAKE ahash Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 05/19] crypto: cmh - add HMAC ahash Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 06/19] crypto: cmh - add CSHAKE/KMAC ahash Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 07/19] crypto: cmh - add SM3 ahash Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 08/19] crypto: cmh - add AES skcipher/aead/cmac Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 09/19] crypto: cmh - add SM4 skcipher/aead/cmac/xcbc Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 10/19] crypto: cmh - add ChaCha20-Poly1305 Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 11/19] crypto: cmh - add DRBG hwrng Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 12/19] crypto: cmh - add RSA akcipher Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 13/19] crypto: cmh - add ECDSA/SM2 sig Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 14/19] crypto: cmh - add ECDH/X25519 kpp Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 15/19] crypto: cmh - add ML-KEM/ML-DSA (QSE) Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` Saravanakrishnan Krishnamoorthy [this message]
2026-06-25 17:33 ` [PATCH 17/19] Documentation: ioctl: add CMH ioctl documentation and register 'J' Saravanakrishnan Krishnamoorthy
2026-06-25 18:29   ` Randy Dunlap
2026-06-25 21:21     ` Krishnamoorthy, Saravanakrishnan
2026-06-25 17:33 ` [PATCH 18/19] selftests: crypto: cmh - add kselftest for management ioctl Saravanakrishnan Krishnamoorthy
2026-06-25 17:33 ` [PATCH 19/19] MAINTAINERS: add Rambus CryptoManager Hub (CMH) Saravanakrishnan Krishnamoorthy
2026-06-26 10:57   ` Krzysztof Kozlowski
2026-06-26 17:22     ` Krishnamoorthy, Saravanakrishnan
2026-06-25 18:05 ` [PATCH 00/19] crypto: cmh - add CRI CryptoManager Hub driver 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=20260625173328.1140487-17-skrishnamoorthy@rambus.com \
    --to=skrishnamoorthy@rambus.com \
    --cc=Joel.Wittenauer@cryptography.com \
    --cc=alex@ghiti.fr \
    --cc=aou@eecs.berkeley.edu \
    --cc=aousherovitch@rambus.com \
    --cc=conor+dt@kernel.org \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=krzk+dt@kernel.org \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=palmer@dabbelt.com \
    --cc=pjw@kernel.org \
    --cc=robh@kernel.org \
    --cc=shuah@kernel.org \
    --cc=sipsupport@rambus.com \
    --cc=skhan@linuxfoundation.org \
    --cc=thin@rambus.com \
    /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