summaryrefslogtreecommitdiff
path: root/drivers/platform/cznic/turris-omnia-mcu-keyctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/cznic/turris-omnia-mcu-keyctl.c')
-rw-r--r--drivers/platform/cznic/turris-omnia-mcu-keyctl.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/drivers/platform/cznic/turris-omnia-mcu-keyctl.c b/drivers/platform/cznic/turris-omnia-mcu-keyctl.c
new file mode 100644
index 000000000000..dc40f942f082
--- /dev/null
+++ b/drivers/platform/cznic/turris-omnia-mcu-keyctl.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CZ.NIC's Turris Omnia MCU ECDSA message signing via keyctl
+ *
+ * 2025 by Marek BehĂșn <kabel@kernel.org>
+ */
+
+#include <crypto/sha2.h>
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/key.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <linux/turris-omnia-mcu-interface.h>
+#include <linux/turris-signing-key.h>
+#include "turris-omnia-mcu.h"
+
+static irqreturn_t omnia_msg_signed_irq_handler(int irq, void *dev_id)
+{
+ u8 reply[1 + OMNIA_MCU_CRYPTO_SIGNATURE_LEN];
+ struct omnia_mcu *mcu = dev_id;
+ int err;
+
+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_CRYPTO_COLLECT_SIGNATURE,
+ reply, sizeof(reply));
+ if (!err && reply[0] != OMNIA_MCU_CRYPTO_SIGNATURE_LEN)
+ err = -EIO;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ if (mcu->sign_requested) {
+ mcu->sign_err = err;
+ if (!err)
+ memcpy(mcu->signature, &reply[1],
+ OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
+ mcu->sign_requested = false;
+ complete(&mcu->msg_signed);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int omnia_mcu_sign(const struct key *key, const void *msg,
+ void *signature)
+{
+ struct omnia_mcu *mcu = dev_get_drvdata(turris_signing_key_get_dev(key));
+ u8 cmd[1 + SHA256_DIGEST_SIZE], reply;
+ int err;
+
+ scoped_guard(mutex, &mcu->sign_lock) {
+ if (mcu->sign_requested)
+ return -EBUSY;
+
+ cmd[0] = OMNIA_CMD_CRYPTO_SIGN_MESSAGE;
+ memcpy(&cmd[1], msg, SHA256_DIGEST_SIZE);
+
+ err = omnia_cmd_write_read(mcu->client, cmd, sizeof(cmd),
+ &reply, 1);
+ if (err)
+ return err;
+
+ if (!reply)
+ return -EBUSY;
+
+ mcu->sign_requested = true;
+ }
+
+ if (wait_for_completion_interruptible(&mcu->msg_signed))
+ return -EINTR;
+
+ guard(mutex)(&mcu->sign_lock);
+
+ if (mcu->sign_err)
+ return mcu->sign_err;
+
+ memcpy(signature, mcu->signature, OMNIA_MCU_CRYPTO_SIGNATURE_LEN);
+
+ /* forget the signature, for security */
+ memzero_explicit(mcu->signature, sizeof(mcu->signature));
+
+ return OMNIA_MCU_CRYPTO_SIGNATURE_LEN;
+}
+
+static const void *omnia_mcu_get_public_key(const struct key *key)
+{
+ struct omnia_mcu *mcu = dev_get_drvdata(turris_signing_key_get_dev(key));
+
+ return mcu->board_public_key;
+}
+
+static const struct turris_signing_key_subtype omnia_signing_key_subtype = {
+ .key_size = 256,
+ .data_size = SHA256_DIGEST_SIZE,
+ .sig_size = OMNIA_MCU_CRYPTO_SIGNATURE_LEN,
+ .public_key_size = OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN,
+ .hash_algo = "sha256",
+ .get_public_key = omnia_mcu_get_public_key,
+ .sign = omnia_mcu_sign,
+};
+
+static int omnia_mcu_read_public_key(struct omnia_mcu *mcu)
+{
+ u8 reply[1 + OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN];
+ int err;
+
+ err = omnia_cmd_read(mcu->client, OMNIA_CMD_CRYPTO_GET_PUBLIC_KEY,
+ reply, sizeof(reply));
+ if (err)
+ return err;
+
+ if (reply[0] != OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN)
+ return -EIO;
+
+ memcpy(mcu->board_public_key, &reply[1],
+ OMNIA_MCU_CRYPTO_PUBLIC_KEY_LEN);
+
+ return 0;
+}
+
+int omnia_mcu_register_keyctl(struct omnia_mcu *mcu)
+{
+ struct device *dev = &mcu->client->dev;
+ char desc[48];
+ int err;
+
+ if (!(mcu->features & OMNIA_FEAT_CRYPTO))
+ return 0;
+
+ err = omnia_mcu_read_public_key(mcu);
+ if (err)
+ return dev_err_probe(dev, err,
+ "Cannot read board public key\n");
+
+ err = devm_mutex_init(dev, &mcu->sign_lock);
+ if (err)
+ return err;
+
+ init_completion(&mcu->msg_signed);
+
+ err = omnia_mcu_request_irq(mcu, OMNIA_INT_MESSAGE_SIGNED,
+ omnia_msg_signed_irq_handler,
+ "turris-omnia-mcu-keyctl");
+ if (err)
+ return dev_err_probe(dev, err,
+ "Cannot request MESSAGE_SIGNED IRQ\n");
+
+ sprintf(desc, "Turris Omnia SN %016llX MCU ECDSA key",
+ mcu->board_serial_number);
+
+ err = devm_turris_signing_key_create(dev, &omnia_signing_key_subtype,
+ desc);
+ if (err)
+ return dev_err_probe(dev, err, "Cannot create signing key\n");
+
+ return 0;
+}