]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
platform: cznic: Add keyctl helpers for Turris platform
authorMarek Behún <kabel@kernel.org>
Tue, 4 Feb 2025 13:14:12 +0000 (14:14 +0100)
committerArnd Bergmann <arnd@arndb.de>
Thu, 20 Mar 2025 16:56:56 +0000 (17:56 +0100)
Some Turris devices support signing messages with a per-device unique
asymmetric key that was created on the device at manufacture time.

Add helper module that helps to expose this ability via the keyctl()
syscall.

A device-specific driver can register a signing key by calling
devm_turris_signing_key_create().

Both the `.turris-signing-keys` keyring and the signing key are created
with only the VIEW, READ and SEARCH permissions for userspace - it is
impossible to link / unlink / move them, set their attributes, or unlink
the keyring from userspace.

Signed-off-by: Marek Behún <kabel@kernel.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
MAINTAINERS
drivers/platform/cznic/Kconfig
drivers/platform/cznic/Makefile
drivers/platform/cznic/turris-signing-key.c [new file with mode: 0644]
include/linux/turris-signing-key.h [new file with mode: 0644]

index 6182a8a0e1c71e97dcd945194c8a32ff164ecffd..6a90c12c22ade34f733fad7c509684da2d92c755 100644 (file)
@@ -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>
index 49c383eb67854173babef2ada3d60a2f562cd4bd..2b4c91ede6d853d131e9474cd0330985644f98f6 100644 (file)
@@ -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
index ce6d997f34d66e943d44c1993cce63142e00ffbf..2b8606a9486b983387344a36c5f45a988f2fea0c 100644 (file)
@@ -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 (file)
index 0000000..3b12e52
--- /dev/null
@@ -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 (file)
index 0000000..032ca8c
--- /dev/null
@@ -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 */