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 <kabel@kernel.org>
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
turris_signing_key_instantiate(struct key * key,struct key_preparsed_payload * payload)25 static int turris_signing_key_instantiate(struct key *key,
26 struct key_preparsed_payload *payload)
27 {
28 return 0;
29 }
30
turris_signing_key_describe(const struct key * key,struct seq_file * m)31 static void turris_signing_key_describe(const struct key *key, struct seq_file *m)
32 {
33 const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
34
35 if (!subtype)
36 return;
37
38 seq_printf(m, "%s: %*phN", key->description, subtype->public_key_size,
39 subtype->get_public_key(key));
40 }
41
turris_signing_key_read(const struct key * key,char * buffer,size_t buflen)42 static long turris_signing_key_read(const struct key *key, char *buffer, size_t buflen)
43 {
44 const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key);
45
46 if (!subtype)
47 return -EIO;
48
49 if (buffer) {
50 if (buflen > subtype->public_key_size)
51 buflen = subtype->public_key_size;
52
53 memcpy(buffer, subtype->get_public_key(key), subtype->public_key_size);
54 }
55
56 return subtype->public_key_size;
57 }
58
turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype * subtype,const struct kernel_pkey_params * params)59 static bool turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype *subtype,
60 const struct kernel_pkey_params *params)
61 {
62 if (params->encoding && strcmp(params->encoding, "raw"))
63 return false;
64
65 if (params->hash_algo && strcmp(params->hash_algo, subtype->hash_algo))
66 return false;
67
68 return true;
69 }
70
turris_signing_key_asym_query(const struct kernel_pkey_params * params,struct kernel_pkey_query * info)71 static int turris_signing_key_asym_query(const struct kernel_pkey_params *params,
72 struct kernel_pkey_query *info)
73 {
74 const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
75
76 if (!subtype)
77 return -EIO;
78
79 if (!turris_signing_key_asym_valid_params(subtype, params))
80 return -EINVAL;
81
82 info->supported_ops = KEYCTL_SUPPORTS_SIGN;
83 info->key_size = subtype->key_size;
84 info->max_data_size = subtype->data_size;
85 info->max_sig_size = subtype->sig_size;
86 info->max_enc_size = 0;
87 info->max_dec_size = 0;
88
89 return 0;
90 }
91
turris_signing_key_asym_eds_op(struct kernel_pkey_params * params,const void * in,void * out)92 static int turris_signing_key_asym_eds_op(struct kernel_pkey_params *params,
93 const void *in, void *out)
94 {
95 const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key);
96 int err;
97
98 if (!subtype)
99 return -EIO;
100
101 if (!turris_signing_key_asym_valid_params(subtype, params))
102 return -EINVAL;
103
104 if (params->op != kernel_pkey_sign)
105 return -EOPNOTSUPP;
106
107 if (params->in_len != subtype->data_size || params->out_len != subtype->sig_size)
108 return -EINVAL;
109
110 err = subtype->sign(params->key, in, out);
111 if (err)
112 return err;
113
114 return subtype->sig_size;
115 }
116
117 static struct key_type turris_signing_key_type = {
118 .name = "turris-signing-key",
119 .instantiate = turris_signing_key_instantiate,
120 .describe = turris_signing_key_describe,
121 .read = turris_signing_key_read,
122 .asym_query = turris_signing_key_asym_query,
123 .asym_eds_op = turris_signing_key_asym_eds_op,
124 };
125
126 static struct key *turris_signing_keyring;
127
turris_signing_key_release(void * key)128 static void turris_signing_key_release(void *key)
129 {
130 key_unlink(turris_signing_keyring, key);
131 key_put(key);
132 }
133
134 int
devm_turris_signing_key_create(struct device * dev,const struct turris_signing_key_subtype * subtype,const char * desc)135 devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype,
136 const char *desc)
137 {
138 struct key *key;
139 key_ref_t kref;
140
141 kref = key_create(make_key_ref(turris_signing_keyring, true),
142 turris_signing_key_type.name, desc, NULL, 0,
143 (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ |
144 KEY_USR_SEARCH,
145 KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA);
146 if (IS_ERR(kref))
147 return PTR_ERR(kref);
148
149 key = key_ref_to_ptr(kref);
150 key->payload.data[1] = dev;
151 rcu_assign_keypointer(key, subtype);
152
153 return devm_add_action_or_reset(dev, turris_signing_key_release, key);
154 }
155 EXPORT_SYMBOL_GPL(devm_turris_signing_key_create);
156
turris_signing_key_init(void)157 static int turris_signing_key_init(void)
158 {
159 int err;
160
161 err = register_key_type(&turris_signing_key_type);
162 if (err)
163 return err;
164
165 turris_signing_keyring = keyring_alloc(".turris-signing-keys",
166 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
167 (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW |
168 KEY_USR_READ | KEY_USR_SEARCH,
169 KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP |
170 KEY_ALLOC_NOT_IN_QUOTA,
171 NULL, NULL);
172 if (IS_ERR(turris_signing_keyring)) {
173 pr_err("Cannot allocate Turris keyring\n");
174
175 unregister_key_type(&turris_signing_key_type);
176
177 return PTR_ERR(turris_signing_keyring);
178 }
179
180 return 0;
181 }
182 module_init(turris_signing_key_init);
183
turris_signing_key_exit(void)184 static void turris_signing_key_exit(void)
185 {
186 key_put(turris_signing_keyring);
187 unregister_key_type(&turris_signing_key_type);
188 }
189 module_exit(turris_signing_key_exit);
190
191 MODULE_AUTHOR("Marek Behun <kabel@kernel.org>");
192 MODULE_DESCRIPTION("CZ.NIC's Turris signing key helper");
193 MODULE_LICENSE("GPL");
194