1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
// 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 *key,
struct key_preparsed_payload *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");
|