summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/platform/cznic/Kconfig5
-rw-r--r--drivers/platform/cznic/Makefile2
-rw-r--r--drivers/platform/cznic/turris-signing-key.c192
-rw-r--r--include/linux/turris-signing-key.h33
5 files changed, 233 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 6182a8a0e1c7..6a90c12c22ad 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2441,6 +2441,7 @@ F: include/dt-bindings/bus/moxtet.h
F: include/linux/armada-37xx-rwtm-mailbox.h
F: include/linux/moxtet.h
F: include/linux/turris-omnia-mcu-interface.h
+F: include/linux/turris-signing-key.h
ARM/FARADAY FA526 PORT
M: Hans Ulli Kroll <ulli.kroll@googlemail.com>
diff --git a/drivers/platform/cznic/Kconfig b/drivers/platform/cznic/Kconfig
index 49c383eb6785..2b4c91ede6d8 100644
--- a/drivers/platform/cznic/Kconfig
+++ b/drivers/platform/cznic/Kconfig
@@ -77,4 +77,9 @@ config TURRIS_OMNIA_MCU_TRNG
endif # TURRIS_OMNIA_MCU
+config TURRIS_SIGNING_KEY
+ tristate
+ depends on KEYS
+ depends on ASYMMETRIC_KEY_TYPE
+
endif # CZNIC_PLATFORMS
diff --git a/drivers/platform/cznic/Makefile b/drivers/platform/cznic/Makefile
index ce6d997f34d6..2b8606a9486b 100644
--- a/drivers/platform/cznic/Makefile
+++ b/drivers/platform/cznic/Makefile
@@ -6,3 +6,5 @@ turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_GPIO) += turris-omnia-mcu-gpio.o
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP) += turris-omnia-mcu-sys-off-wakeup.o
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_TRNG) += turris-omnia-mcu-trng.o
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_WATCHDOG) += turris-omnia-mcu-watchdog.o
+
+obj-$(CONFIG_TURRIS_SIGNING_KEY) += turris-signing-key.o
diff --git a/drivers/platform/cznic/turris-signing-key.c b/drivers/platform/cznic/turris-signing-key.c
new file mode 100644
index 000000000000..3b12e5245fb7
--- /dev/null
+++ b/drivers/platform/cznic/turris-signing-key.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Some of CZ.NIC's Turris devices support signing messages with a per-device unique asymmetric
+ * cryptographic key that was burned into the device at manufacture.
+ *
+ * This helper module exposes this message signing ability via the keyctl() syscall. Upon load, it
+ * creates the `.turris-signing-keys` keyring. A device-specific driver then has to create a signing
+ * key by calling devm_turris_signing_key_create().
+ *
+ * 2025 by Marek BehĂșn <kabel@kernel.org>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/key-type.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <linux/turris-signing-key.h>
+
+static int turris_signing_key_instantiate(struct key *, struct key_preparsed_payload *)
+{
+ return 0;
+}
+
+static void turris_signing_key_describe(const struct key *key, struct seq_file *m)
+{
+ const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
+
+ if (!subtype)
+ return;
+
+ seq_printf(m, "%s: %*phN", key->description, subtype->public_key_size,
+ subtype->get_public_key(key));
+}
+
+static long turris_signing_key_read(const struct key *key, char *buffer, size_t buflen)
+{
+ const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
+
+ if (!subtype)
+ return -EIO;
+
+ if (buffer) {
+ if (buflen > subtype->public_key_size)
+ buflen = subtype->public_key_size;
+
+ memcpy(buffer, subtype->get_public_key(key), subtype->public_key_size);
+ }
+
+ return subtype->public_key_size;
+}
+
+static bool turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype *subtype,
+ const struct kernel_pkey_params *params)
+{
+ if (params->encoding && strcmp(params->encoding, "raw"))
+ return false;
+
+ if (params->hash_algo && strcmp(params->hash_algo, subtype->hash_algo))
+ return false;
+
+ return true;
+}
+
+static int turris_signing_key_asym_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
+
+ if (!subtype)
+ return -EIO;
+
+ if (!turris_signing_key_asym_valid_params(subtype, params))
+ return -EINVAL;
+
+ info->supported_ops = KEYCTL_SUPPORTS_SIGN;
+ info->key_size = subtype->key_size;
+ info->max_data_size = subtype->data_size;
+ info->max_sig_size = subtype->sig_size;
+ info->max_enc_size = 0;
+ info->max_dec_size = 0;
+
+ return 0;
+}
+
+static int turris_signing_key_asym_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
+ int err;
+
+ if (!subtype)
+ return -EIO;
+
+ if (!turris_signing_key_asym_valid_params(subtype, params))
+ return -EINVAL;
+
+ if (params->op != kernel_pkey_sign)
+ return -EOPNOTSUPP;
+
+ if (params->in_len != subtype->data_size || params->out_len != subtype->sig_size)
+ return -EINVAL;
+
+ err = subtype->sign(params->key, in, out);
+ if (err)
+ return err;
+
+ return subtype->sig_size;
+}
+
+static struct key_type turris_signing_key_type = {
+ .name = "turris-signing-key",
+ .instantiate = turris_signing_key_instantiate,
+ .describe = turris_signing_key_describe,
+ .read = turris_signing_key_read,
+ .asym_query = turris_signing_key_asym_query,
+ .asym_eds_op = turris_signing_key_asym_eds_op,
+};
+
+static struct key *turris_signing_keyring;
+
+static void turris_signing_key_release(void *key)
+{
+ key_unlink(turris_signing_keyring, key);
+ key_put(key);
+}
+
+int
+devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype,
+ const char *desc)
+{
+ struct key *key;
+ key_ref_t kref;
+
+ kref = key_create(make_key_ref(turris_signing_keyring, true),
+ turris_signing_key_type.name, desc, NULL, 0,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_SEARCH,
+ KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA);
+ if (IS_ERR(kref))
+ return PTR_ERR(kref);
+
+ key = key_ref_to_ptr(kref);
+ key->payload.data[1] = dev;
+ rcu_assign_keypointer(key, subtype);
+
+ return devm_add_action_or_reset(dev, turris_signing_key_release, key);
+}
+EXPORT_SYMBOL_GPL(devm_turris_signing_key_create);
+
+static int turris_signing_key_init(void)
+{
+ int err;
+
+ err = register_key_type(&turris_signing_key_type);
+ if (err)
+ return err;
+
+ turris_signing_keyring = keyring_alloc(".turris-signing-keys",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW |
+ KEY_USR_READ | KEY_USR_SEARCH,
+ KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP |
+ KEY_ALLOC_NOT_IN_QUOTA,
+ NULL, NULL);
+ if (IS_ERR(turris_signing_keyring)) {
+ pr_err("Cannot allocate Turris keyring\n");
+
+ unregister_key_type(&turris_signing_key_type);
+
+ return PTR_ERR(turris_signing_keyring);
+ }
+
+ return 0;
+}
+module_init(turris_signing_key_init);
+
+static void turris_signing_key_exit(void)
+{
+ key_put(turris_signing_keyring);
+ unregister_key_type(&turris_signing_key_type);
+}
+module_exit(turris_signing_key_exit);
+
+MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
+MODULE_DESCRIPTION("CZ.NIC's Turris signing key helper");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/turris-signing-key.h b/include/linux/turris-signing-key.h
new file mode 100644
index 000000000000..032ca8cbf636
--- /dev/null
+++ b/include/linux/turris-signing-key.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * 2025 by Marek BehĂșn <kabel@kernel.org>
+ */
+
+#ifndef __TURRIS_SIGNING_KEY_H
+#define __TURRIS_SIGNING_KEY_H
+
+#include <linux/key.h>
+#include <linux/types.h>
+
+struct device;
+
+struct turris_signing_key_subtype {
+ u16 key_size;
+ u8 data_size;
+ u8 sig_size;
+ u8 public_key_size;
+ const char *hash_algo;
+ const void *(*get_public_key)(const struct key *key);
+ int (*sign)(const struct key *key, const void *msg, void *signature);
+};
+
+static inline struct device *turris_signing_key_get_dev(const struct key *key)
+{
+ return key->payload.data[1];
+}
+
+int
+devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype,
+ const char *desc);
+
+#endif /* __TURRIS_SIGNING_KEY_H */