Skip to content

Commit 0b28b70

Browse files
elkabloarndb
authored andcommitted
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 <[email protected]> Signed-off-by: Arnd Bergmann <[email protected]>
1 parent ee7f8ed commit 0b28b70

File tree

5 files changed

+233
-0
lines changed

5 files changed

+233
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2441,6 +2441,7 @@ F: include/dt-bindings/bus/moxtet.h
24412441
F: include/linux/armada-37xx-rwtm-mailbox.h
24422442
F: include/linux/moxtet.h
24432443
F: include/linux/turris-omnia-mcu-interface.h
2444+
F: include/linux/turris-signing-key.h
24442445

24452446
ARM/FARADAY FA526 PORT
24462447
M: Hans Ulli Kroll <[email protected]>

drivers/platform/cznic/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,9 @@ config TURRIS_OMNIA_MCU_TRNG
7777

7878
endif # TURRIS_OMNIA_MCU
7979

80+
config TURRIS_SIGNING_KEY
81+
tristate
82+
depends on KEYS
83+
depends on ASYMMETRIC_KEY_TYPE
84+
8085
endif # CZNIC_PLATFORMS

drivers/platform/cznic/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_GPIO) += turris-omnia-mcu-gpio.o
66
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP) += turris-omnia-mcu-sys-off-wakeup.o
77
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_TRNG) += turris-omnia-mcu-trng.o
88
turris-omnia-mcu-$(CONFIG_TURRIS_OMNIA_MCU_WATCHDOG) += turris-omnia-mcu-watchdog.o
9+
10+
obj-$(CONFIG_TURRIS_SIGNING_KEY) += turris-signing-key.o
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Some of CZ.NIC's Turris devices support signing messages with a per-device unique asymmetric
4+
* cryptographic key that was burned into the device at manufacture.
5+
*
6+
* This helper module exposes this message signing ability via the keyctl() syscall. Upon load, it
7+
* creates the `.turris-signing-keys` keyring. A device-specific driver then has to create a signing
8+
* key by calling devm_turris_signing_key_create().
9+
*
10+
* 2025 by Marek Behún <[email protected]>
11+
*/
12+
13+
#include <linux/device.h>
14+
#include <linux/err.h>
15+
#include <linux/key-type.h>
16+
#include <linux/key.h>
17+
#include <linux/keyctl.h>
18+
#include <linux/module.h>
19+
#include <linux/seq_file.h>
20+
#include <linux/string.h>
21+
#include <linux/types.h>
22+
23+
#include <linux/turris-signing-key.h>
24+
25+
static int turris_signing_key_instantiate(struct key *, struct key_preparsed_payload *)
26+
{
27+
return 0;
28+
}
29+
30+
static void turris_signing_key_describe(const struct key *key, struct seq_file *m)
31+
{
32+
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
33+
34+
if (!subtype)
35+
return;
36+
37+
seq_printf(m, "%s: %*phN", key->description, subtype->public_key_size,
38+
subtype->get_public_key(key));
39+
}
40+
41+
static long turris_signing_key_read(const struct key *key, char *buffer, size_t buflen)
42+
{
43+
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
44+
45+
if (!subtype)
46+
return -EIO;
47+
48+
if (buffer) {
49+
if (buflen > subtype->public_key_size)
50+
buflen = subtype->public_key_size;
51+
52+
memcpy(buffer, subtype->get_public_key(key), subtype->public_key_size);
53+
}
54+
55+
return subtype->public_key_size;
56+
}
57+
58+
static bool turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype *subtype,
59+
const struct kernel_pkey_params *params)
60+
{
61+
if (params->encoding && strcmp(params->encoding, "raw"))
62+
return false;
63+
64+
if (params->hash_algo && strcmp(params->hash_algo, subtype->hash_algo))
65+
return false;
66+
67+
return true;
68+
}
69+
70+
static int turris_signing_key_asym_query(const struct kernel_pkey_params *params,
71+
struct kernel_pkey_query *info)
72+
{
73+
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
74+
75+
if (!subtype)
76+
return -EIO;
77+
78+
if (!turris_signing_key_asym_valid_params(subtype, params))
79+
return -EINVAL;
80+
81+
info->supported_ops = KEYCTL_SUPPORTS_SIGN;
82+
info->key_size = subtype->key_size;
83+
info->max_data_size = subtype->data_size;
84+
info->max_sig_size = subtype->sig_size;
85+
info->max_enc_size = 0;
86+
info->max_dec_size = 0;
87+
88+
return 0;
89+
}
90+
91+
static int turris_signing_key_asym_eds_op(struct kernel_pkey_params *params,
92+
const void *in, void *out)
93+
{
94+
const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
95+
int err;
96+
97+
if (!subtype)
98+
return -EIO;
99+
100+
if (!turris_signing_key_asym_valid_params(subtype, params))
101+
return -EINVAL;
102+
103+
if (params->op != kernel_pkey_sign)
104+
return -EOPNOTSUPP;
105+
106+
if (params->in_len != subtype->data_size || params->out_len != subtype->sig_size)
107+
return -EINVAL;
108+
109+
err = subtype->sign(params->key, in, out);
110+
if (err)
111+
return err;
112+
113+
return subtype->sig_size;
114+
}
115+
116+
static struct key_type turris_signing_key_type = {
117+
.name = "turris-signing-key",
118+
.instantiate = turris_signing_key_instantiate,
119+
.describe = turris_signing_key_describe,
120+
.read = turris_signing_key_read,
121+
.asym_query = turris_signing_key_asym_query,
122+
.asym_eds_op = turris_signing_key_asym_eds_op,
123+
};
124+
125+
static struct key *turris_signing_keyring;
126+
127+
static void turris_signing_key_release(void *key)
128+
{
129+
key_unlink(turris_signing_keyring, key);
130+
key_put(key);
131+
}
132+
133+
int
134+
devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype,
135+
const char *desc)
136+
{
137+
struct key *key;
138+
key_ref_t kref;
139+
140+
kref = key_create(make_key_ref(turris_signing_keyring, true),
141+
turris_signing_key_type.name, desc, NULL, 0,
142+
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ |
143+
KEY_USR_SEARCH,
144+
KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA);
145+
if (IS_ERR(kref))
146+
return PTR_ERR(kref);
147+
148+
key = key_ref_to_ptr(kref);
149+
key->payload.data[1] = dev;
150+
rcu_assign_keypointer(key, subtype);
151+
152+
return devm_add_action_or_reset(dev, turris_signing_key_release, key);
153+
}
154+
EXPORT_SYMBOL_GPL(devm_turris_signing_key_create);
155+
156+
static int turris_signing_key_init(void)
157+
{
158+
int err;
159+
160+
err = register_key_type(&turris_signing_key_type);
161+
if (err)
162+
return err;
163+
164+
turris_signing_keyring = keyring_alloc(".turris-signing-keys",
165+
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
166+
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW |
167+
KEY_USR_READ | KEY_USR_SEARCH,
168+
KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP |
169+
KEY_ALLOC_NOT_IN_QUOTA,
170+
NULL, NULL);
171+
if (IS_ERR(turris_signing_keyring)) {
172+
pr_err("Cannot allocate Turris keyring\n");
173+
174+
unregister_key_type(&turris_signing_key_type);
175+
176+
return PTR_ERR(turris_signing_keyring);
177+
}
178+
179+
return 0;
180+
}
181+
module_init(turris_signing_key_init);
182+
183+
static void turris_signing_key_exit(void)
184+
{
185+
key_put(turris_signing_keyring);
186+
unregister_key_type(&turris_signing_key_type);
187+
}
188+
module_exit(turris_signing_key_exit);
189+
190+
MODULE_AUTHOR("Marek Behun <[email protected]>");
191+
MODULE_DESCRIPTION("CZ.NIC's Turris signing key helper");
192+
MODULE_LICENSE("GPL");

include/linux/turris-signing-key.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* 2025 by Marek Behún <[email protected]>
4+
*/
5+
6+
#ifndef __TURRIS_SIGNING_KEY_H
7+
#define __TURRIS_SIGNING_KEY_H
8+
9+
#include <linux/key.h>
10+
#include <linux/types.h>
11+
12+
struct device;
13+
14+
struct turris_signing_key_subtype {
15+
u16 key_size;
16+
u8 data_size;
17+
u8 sig_size;
18+
u8 public_key_size;
19+
const char *hash_algo;
20+
const void *(*get_public_key)(const struct key *key);
21+
int (*sign)(const struct key *key, const void *msg, void *signature);
22+
};
23+
24+
static inline struct device *turris_signing_key_get_dev(const struct key *key)
25+
{
26+
return key->payload.data[1];
27+
}
28+
29+
int
30+
devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype,
31+
const char *desc);
32+
33+
#endif /* __TURRIS_SIGNING_KEY_H */

0 commit comments

Comments
 (0)