1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Crypto operations using stored keys |
3 | * |
4 | * Copyright (c) 2016, Intel Corporation |
5 | */ |
6 | |
7 | #include <linux/slab.h> |
8 | #include <linux/uaccess.h> |
9 | #include <linux/scatterlist.h> |
10 | #include <linux/crypto.h> |
11 | #include <crypto/hash.h> |
12 | #include <crypto/kpp.h> |
13 | #include <crypto/dh.h> |
14 | #include <crypto/kdf_sp800108.h> |
15 | #include <keys/user-type.h> |
16 | #include "internal.h" |
17 | |
18 | static ssize_t dh_data_from_key(key_serial_t keyid, const void **data) |
19 | { |
20 | struct key *key; |
21 | key_ref_t key_ref; |
22 | long status; |
23 | ssize_t ret; |
24 | |
25 | key_ref = lookup_user_key(id: keyid, flags: 0, need_perm: KEY_NEED_READ); |
26 | if (IS_ERR(ptr: key_ref)) { |
27 | ret = -ENOKEY; |
28 | goto error; |
29 | } |
30 | |
31 | key = key_ref_to_ptr(key_ref); |
32 | |
33 | ret = -EOPNOTSUPP; |
34 | if (key->type == &key_type_user) { |
35 | down_read(sem: &key->sem); |
36 | status = key_validate(key); |
37 | if (status == 0) { |
38 | const struct user_key_payload *payload; |
39 | uint8_t *duplicate; |
40 | |
41 | payload = user_key_payload_locked(key); |
42 | |
43 | duplicate = kmemdup(p: payload->data, size: payload->datalen, |
44 | GFP_KERNEL); |
45 | if (duplicate) { |
46 | *data = duplicate; |
47 | ret = payload->datalen; |
48 | } else { |
49 | ret = -ENOMEM; |
50 | } |
51 | } |
52 | up_read(sem: &key->sem); |
53 | } |
54 | |
55 | key_put(key); |
56 | error: |
57 | return ret; |
58 | } |
59 | |
60 | static void dh_free_data(struct dh *dh) |
61 | { |
62 | kfree_sensitive(objp: dh->key); |
63 | kfree_sensitive(objp: dh->p); |
64 | kfree_sensitive(objp: dh->g); |
65 | } |
66 | |
67 | static int kdf_alloc(struct crypto_shash **hash, char *hashname) |
68 | { |
69 | struct crypto_shash *tfm; |
70 | |
71 | /* allocate synchronous hash */ |
72 | tfm = crypto_alloc_shash(alg_name: hashname, type: 0, mask: 0); |
73 | if (IS_ERR(ptr: tfm)) { |
74 | pr_info("could not allocate digest TFM handle %s\n" , hashname); |
75 | return PTR_ERR(ptr: tfm); |
76 | } |
77 | |
78 | if (crypto_shash_digestsize(tfm) == 0) { |
79 | crypto_free_shash(tfm); |
80 | return -EINVAL; |
81 | } |
82 | |
83 | *hash = tfm; |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static void kdf_dealloc(struct crypto_shash *hash) |
89 | { |
90 | if (hash) |
91 | crypto_free_shash(tfm: hash); |
92 | } |
93 | |
94 | static int keyctl_dh_compute_kdf(struct crypto_shash *hash, |
95 | char __user *buffer, size_t buflen, |
96 | uint8_t *kbuf, size_t kbuflen) |
97 | { |
98 | struct kvec kbuf_iov = { .iov_base = kbuf, .iov_len = kbuflen }; |
99 | uint8_t *outbuf = NULL; |
100 | int ret; |
101 | size_t outbuf_len = roundup(buflen, crypto_shash_digestsize(hash)); |
102 | |
103 | outbuf = kmalloc(size: outbuf_len, GFP_KERNEL); |
104 | if (!outbuf) { |
105 | ret = -ENOMEM; |
106 | goto err; |
107 | } |
108 | |
109 | ret = crypto_kdf108_ctr_generate(kmd: hash, info: &kbuf_iov, info_nvec: 1, dst: outbuf, dlen: outbuf_len); |
110 | if (ret) |
111 | goto err; |
112 | |
113 | ret = buflen; |
114 | if (copy_to_user(to: buffer, from: outbuf, n: buflen) != 0) |
115 | ret = -EFAULT; |
116 | |
117 | err: |
118 | kfree_sensitive(objp: outbuf); |
119 | return ret; |
120 | } |
121 | |
122 | long __keyctl_dh_compute(struct keyctl_dh_params __user *params, |
123 | char __user *buffer, size_t buflen, |
124 | struct keyctl_kdf_params *kdfcopy) |
125 | { |
126 | long ret; |
127 | ssize_t dlen; |
128 | int secretlen; |
129 | int outlen; |
130 | struct keyctl_dh_params pcopy; |
131 | struct dh dh_inputs; |
132 | struct scatterlist outsg; |
133 | DECLARE_CRYPTO_WAIT(compl); |
134 | struct crypto_kpp *tfm; |
135 | struct kpp_request *req; |
136 | uint8_t *secret; |
137 | uint8_t *outbuf; |
138 | struct crypto_shash *hash = NULL; |
139 | |
140 | if (!params || (!buffer && buflen)) { |
141 | ret = -EINVAL; |
142 | goto out1; |
143 | } |
144 | if (copy_from_user(to: &pcopy, from: params, n: sizeof(pcopy)) != 0) { |
145 | ret = -EFAULT; |
146 | goto out1; |
147 | } |
148 | |
149 | if (kdfcopy) { |
150 | char *hashname; |
151 | |
152 | if (memchr_inv(p: kdfcopy->__spare, c: 0, size: sizeof(kdfcopy->__spare))) { |
153 | ret = -EINVAL; |
154 | goto out1; |
155 | } |
156 | |
157 | if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || |
158 | kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { |
159 | ret = -EMSGSIZE; |
160 | goto out1; |
161 | } |
162 | |
163 | /* get KDF name string */ |
164 | hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); |
165 | if (IS_ERR(ptr: hashname)) { |
166 | ret = PTR_ERR(ptr: hashname); |
167 | goto out1; |
168 | } |
169 | |
170 | /* allocate KDF from the kernel crypto API */ |
171 | ret = kdf_alloc(hash: &hash, hashname); |
172 | kfree(objp: hashname); |
173 | if (ret) |
174 | goto out1; |
175 | } |
176 | |
177 | memset(&dh_inputs, 0, sizeof(dh_inputs)); |
178 | |
179 | dlen = dh_data_from_key(keyid: pcopy.prime, data: &dh_inputs.p); |
180 | if (dlen < 0) { |
181 | ret = dlen; |
182 | goto out1; |
183 | } |
184 | dh_inputs.p_size = dlen; |
185 | |
186 | dlen = dh_data_from_key(keyid: pcopy.base, data: &dh_inputs.g); |
187 | if (dlen < 0) { |
188 | ret = dlen; |
189 | goto out2; |
190 | } |
191 | dh_inputs.g_size = dlen; |
192 | |
193 | dlen = dh_data_from_key(keyid: pcopy.private, data: &dh_inputs.key); |
194 | if (dlen < 0) { |
195 | ret = dlen; |
196 | goto out2; |
197 | } |
198 | dh_inputs.key_size = dlen; |
199 | |
200 | secretlen = crypto_dh_key_len(params: &dh_inputs); |
201 | secret = kmalloc(size: secretlen, GFP_KERNEL); |
202 | if (!secret) { |
203 | ret = -ENOMEM; |
204 | goto out2; |
205 | } |
206 | ret = crypto_dh_encode_key(buf: secret, len: secretlen, params: &dh_inputs); |
207 | if (ret) |
208 | goto out3; |
209 | |
210 | tfm = crypto_alloc_kpp(alg_name: "dh" , type: 0, mask: 0); |
211 | if (IS_ERR(ptr: tfm)) { |
212 | ret = PTR_ERR(ptr: tfm); |
213 | goto out3; |
214 | } |
215 | |
216 | ret = crypto_kpp_set_secret(tfm, buffer: secret, len: secretlen); |
217 | if (ret) |
218 | goto out4; |
219 | |
220 | outlen = crypto_kpp_maxsize(tfm); |
221 | |
222 | if (!kdfcopy) { |
223 | /* |
224 | * When not using a KDF, buflen 0 is used to read the |
225 | * required buffer length |
226 | */ |
227 | if (buflen == 0) { |
228 | ret = outlen; |
229 | goto out4; |
230 | } else if (outlen > buflen) { |
231 | ret = -EOVERFLOW; |
232 | goto out4; |
233 | } |
234 | } |
235 | |
236 | outbuf = kzalloc(size: kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen, |
237 | GFP_KERNEL); |
238 | if (!outbuf) { |
239 | ret = -ENOMEM; |
240 | goto out4; |
241 | } |
242 | |
243 | sg_init_one(&outsg, outbuf, outlen); |
244 | |
245 | req = kpp_request_alloc(tfm, GFP_KERNEL); |
246 | if (!req) { |
247 | ret = -ENOMEM; |
248 | goto out5; |
249 | } |
250 | |
251 | kpp_request_set_input(req, NULL, input_len: 0); |
252 | kpp_request_set_output(req, output: &outsg, output_len: outlen); |
253 | kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | |
254 | CRYPTO_TFM_REQ_MAY_SLEEP, |
255 | cmpl: crypto_req_done, data: &compl); |
256 | |
257 | /* |
258 | * For DH, generate_public_key and generate_shared_secret are |
259 | * the same calculation |
260 | */ |
261 | ret = crypto_kpp_generate_public_key(req); |
262 | ret = crypto_wait_req(err: ret, wait: &compl); |
263 | if (ret) |
264 | goto out6; |
265 | |
266 | if (kdfcopy) { |
267 | /* |
268 | * Concatenate SP800-56A otherinfo past DH shared secret -- the |
269 | * input to the KDF is (DH shared secret || otherinfo) |
270 | */ |
271 | if (copy_from_user(to: outbuf + req->dst_len, from: kdfcopy->otherinfo, |
272 | n: kdfcopy->otherinfolen) != 0) { |
273 | ret = -EFAULT; |
274 | goto out6; |
275 | } |
276 | |
277 | ret = keyctl_dh_compute_kdf(hash, buffer, buflen, kbuf: outbuf, |
278 | kbuflen: req->dst_len + kdfcopy->otherinfolen); |
279 | } else if (copy_to_user(to: buffer, from: outbuf, n: req->dst_len) == 0) { |
280 | ret = req->dst_len; |
281 | } else { |
282 | ret = -EFAULT; |
283 | } |
284 | |
285 | out6: |
286 | kpp_request_free(req); |
287 | out5: |
288 | kfree_sensitive(objp: outbuf); |
289 | out4: |
290 | crypto_free_kpp(tfm); |
291 | out3: |
292 | kfree_sensitive(objp: secret); |
293 | out2: |
294 | dh_free_data(dh: &dh_inputs); |
295 | out1: |
296 | kdf_dealloc(hash); |
297 | return ret; |
298 | } |
299 | |
300 | long keyctl_dh_compute(struct keyctl_dh_params __user *params, |
301 | char __user *buffer, size_t buflen, |
302 | struct keyctl_kdf_params __user *kdf) |
303 | { |
304 | struct keyctl_kdf_params kdfcopy; |
305 | |
306 | if (!kdf) |
307 | return __keyctl_dh_compute(params, buffer, buflen, NULL); |
308 | |
309 | if (copy_from_user(to: &kdfcopy, from: kdf, n: sizeof(kdfcopy)) != 0) |
310 | return -EFAULT; |
311 | |
312 | return __keyctl_dh_compute(params, buffer, buflen, kdfcopy: &kdfcopy); |
313 | } |
314 | |