mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-30 13:03:01 +00:00
platform: cznic: Add keyctl helpers for Turris platform
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>
This commit is contained in:
parent
ee7f8ed729
commit
0b28b7080e
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
192
drivers/platform/cznic/turris-signing-key.c
Normal file
192
drivers/platform/cznic/turris-signing-key.c
Normal file
@ -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");
|
33
include/linux/turris-signing-key.h
Normal file
33
include/linux/turris-signing-key.h
Normal file
@ -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 */
|
Loading…
Reference in New Issue
Block a user