xref: /linux/security/keys/dh.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ddbb4114SMat Martineau /* Crypto operations using stored keys
3ddbb4114SMat Martineau  *
4ddbb4114SMat Martineau  * Copyright (c) 2016, Intel Corporation
5ddbb4114SMat Martineau  */
6ddbb4114SMat Martineau 
7ddbb4114SMat Martineau #include <linux/slab.h>
8ddbb4114SMat Martineau #include <linux/uaccess.h>
97cbe0932SMat Martineau #include <linux/scatterlist.h>
10f1c316a3SStephan Mueller #include <linux/crypto.h>
11f1c316a3SStephan Mueller #include <crypto/hash.h>
127cbe0932SMat Martineau #include <crypto/kpp.h>
137cbe0932SMat Martineau #include <crypto/dh.h>
14d3b04a43SStephan Müller #include <crypto/kdf_sp800108.h>
15ddbb4114SMat Martineau #include <keys/user-type.h>
16ddbb4114SMat Martineau #include "internal.h"
17ddbb4114SMat Martineau 
dh_data_from_key(key_serial_t keyid,const void ** data)18215bebc8SNicolai Stange static ssize_t dh_data_from_key(key_serial_t keyid, const void **data)
19ddbb4114SMat Martineau {
20ddbb4114SMat Martineau 	struct key *key;
21ddbb4114SMat Martineau 	key_ref_t key_ref;
22ddbb4114SMat Martineau 	long status;
23ddbb4114SMat Martineau 	ssize_t ret;
24ddbb4114SMat Martineau 
25ddbb4114SMat Martineau 	key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
26ddbb4114SMat Martineau 	if (IS_ERR(key_ref)) {
27ddbb4114SMat Martineau 		ret = -ENOKEY;
28ddbb4114SMat Martineau 		goto error;
29ddbb4114SMat Martineau 	}
30ddbb4114SMat Martineau 
31ddbb4114SMat Martineau 	key = key_ref_to_ptr(key_ref);
32ddbb4114SMat Martineau 
33ddbb4114SMat Martineau 	ret = -EOPNOTSUPP;
34ddbb4114SMat Martineau 	if (key->type == &key_type_user) {
35ddbb4114SMat Martineau 		down_read(&key->sem);
36ddbb4114SMat Martineau 		status = key_validate(key);
37ddbb4114SMat Martineau 		if (status == 0) {
38ddbb4114SMat Martineau 			const struct user_key_payload *payload;
397cbe0932SMat Martineau 			uint8_t *duplicate;
40ddbb4114SMat Martineau 
410837e49aSDavid Howells 			payload = user_key_payload_locked(key);
42ddbb4114SMat Martineau 
437cbe0932SMat Martineau 			duplicate = kmemdup(payload->data, payload->datalen,
447cbe0932SMat Martineau 					    GFP_KERNEL);
457cbe0932SMat Martineau 			if (duplicate) {
467cbe0932SMat Martineau 				*data = duplicate;
47ddbb4114SMat Martineau 				ret = payload->datalen;
48ddbb4114SMat Martineau 			} else {
497cbe0932SMat Martineau 				ret = -ENOMEM;
50ddbb4114SMat Martineau 			}
51ddbb4114SMat Martineau 		}
52ddbb4114SMat Martineau 		up_read(&key->sem);
53ddbb4114SMat Martineau 	}
54ddbb4114SMat Martineau 
55ddbb4114SMat Martineau 	key_put(key);
56ddbb4114SMat Martineau error:
57ddbb4114SMat Martineau 	return ret;
58ddbb4114SMat Martineau }
59ddbb4114SMat Martineau 
dh_free_data(struct dh * dh)607cbe0932SMat Martineau static void dh_free_data(struct dh *dh)
617cbe0932SMat Martineau {
62453431a5SWaiman Long 	kfree_sensitive(dh->key);
63453431a5SWaiman Long 	kfree_sensitive(dh->p);
64453431a5SWaiman Long 	kfree_sensitive(dh->g);
657cbe0932SMat Martineau }
667cbe0932SMat Martineau 
kdf_alloc(struct crypto_shash ** hash,char * hashname)67d3b04a43SStephan Müller static int kdf_alloc(struct crypto_shash **hash, char *hashname)
68f1c316a3SStephan Mueller {
69f1c316a3SStephan Mueller 	struct crypto_shash *tfm;
70f1c316a3SStephan Mueller 
71f1c316a3SStephan Mueller 	/* allocate synchronous hash */
72f1c316a3SStephan Mueller 	tfm = crypto_alloc_shash(hashname, 0, 0);
73f1c316a3SStephan Mueller 	if (IS_ERR(tfm)) {
74f1c316a3SStephan Mueller 		pr_info("could not allocate digest TFM handle %s\n", hashname);
75f1c316a3SStephan Mueller 		return PTR_ERR(tfm);
76f1c316a3SStephan Mueller 	}
77f1c316a3SStephan Mueller 
78d3b04a43SStephan Müller 	if (crypto_shash_digestsize(tfm) == 0) {
79bbe24045SEric Biggers 		crypto_free_shash(tfm);
80d3b04a43SStephan Müller 		return -EINVAL;
81f1c316a3SStephan Mueller 	}
82f1c316a3SStephan Mueller 
83d3b04a43SStephan Müller 	*hash = tfm;
84f1c316a3SStephan Mueller 
85f1c316a3SStephan Mueller 	return 0;
86f1c316a3SStephan Mueller }
87f1c316a3SStephan Mueller 
kdf_dealloc(struct crypto_shash * hash)88d3b04a43SStephan Müller static void kdf_dealloc(struct crypto_shash *hash)
89d3b04a43SStephan Müller {
90d3b04a43SStephan Müller 	if (hash)
91d3b04a43SStephan Müller 		crypto_free_shash(hash);
92d3b04a43SStephan Müller }
93d3b04a43SStephan Müller 
keyctl_dh_compute_kdf(struct crypto_shash * hash,char __user * buffer,size_t buflen,uint8_t * kbuf,size_t kbuflen)94d3b04a43SStephan Müller static int keyctl_dh_compute_kdf(struct crypto_shash *hash,
954693fc73SStephan Mueller 				 char __user *buffer, size_t buflen,
96d7921344SStephan Müller 				 uint8_t *kbuf, size_t kbuflen)
97f1c316a3SStephan Mueller {
98d3b04a43SStephan Müller 	struct kvec kbuf_iov = { .iov_base = kbuf, .iov_len = kbuflen };
99f1c316a3SStephan Mueller 	uint8_t *outbuf = NULL;
100f1c316a3SStephan Mueller 	int ret;
101d3b04a43SStephan Müller 	size_t outbuf_len = roundup(buflen, crypto_shash_digestsize(hash));
102f1c316a3SStephan Mueller 
103383203efSTycho Andersen 	outbuf = kmalloc(outbuf_len, GFP_KERNEL);
104f1c316a3SStephan Mueller 	if (!outbuf) {
105f1c316a3SStephan Mueller 		ret = -ENOMEM;
106f1c316a3SStephan Mueller 		goto err;
107f1c316a3SStephan Mueller 	}
108f1c316a3SStephan Mueller 
109d3b04a43SStephan Müller 	ret = crypto_kdf108_ctr_generate(hash, &kbuf_iov, 1, outbuf, outbuf_len);
110f1c316a3SStephan Mueller 	if (ret)
111f1c316a3SStephan Mueller 		goto err;
112f1c316a3SStephan Mueller 
113f1c316a3SStephan Mueller 	ret = buflen;
114f1c316a3SStephan Mueller 	if (copy_to_user(buffer, outbuf, buflen) != 0)
115f1c316a3SStephan Mueller 		ret = -EFAULT;
116f1c316a3SStephan Mueller 
117f1c316a3SStephan Mueller err:
118453431a5SWaiman Long 	kfree_sensitive(outbuf);
119f1c316a3SStephan Mueller 	return ret;
120f1c316a3SStephan Mueller }
121f1c316a3SStephan Mueller 
__keyctl_dh_compute(struct keyctl_dh_params __user * params,char __user * buffer,size_t buflen,struct keyctl_kdf_params * kdfcopy)122f1c316a3SStephan Mueller long __keyctl_dh_compute(struct keyctl_dh_params __user *params,
123f1c316a3SStephan Mueller 			 char __user *buffer, size_t buflen,
124f1c316a3SStephan Mueller 			 struct keyctl_kdf_params *kdfcopy)
125ddbb4114SMat Martineau {
126ddbb4114SMat Martineau 	long ret;
1277cbe0932SMat Martineau 	ssize_t dlen;
1287cbe0932SMat Martineau 	int secretlen;
1297cbe0932SMat Martineau 	int outlen;
130ddbb4114SMat Martineau 	struct keyctl_dh_params pcopy;
1317cbe0932SMat Martineau 	struct dh dh_inputs;
1327cbe0932SMat Martineau 	struct scatterlist outsg;
133*5419f2b2SHerbert Xu 	DECLARE_CRYPTO_WAIT(compl);
1347cbe0932SMat Martineau 	struct crypto_kpp *tfm;
1357cbe0932SMat Martineau 	struct kpp_request *req;
1367cbe0932SMat Martineau 	uint8_t *secret;
1377cbe0932SMat Martineau 	uint8_t *outbuf;
138d3b04a43SStephan Müller 	struct crypto_shash *hash = NULL;
139ddbb4114SMat Martineau 
140ddbb4114SMat Martineau 	if (!params || (!buffer && buflen)) {
141ddbb4114SMat Martineau 		ret = -EINVAL;
1427cbe0932SMat Martineau 		goto out1;
143ddbb4114SMat Martineau 	}
144ddbb4114SMat Martineau 	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
145ddbb4114SMat Martineau 		ret = -EFAULT;
1467cbe0932SMat Martineau 		goto out1;
147ddbb4114SMat Martineau 	}
148ddbb4114SMat Martineau 
149f1c316a3SStephan Mueller 	if (kdfcopy) {
150f1c316a3SStephan Mueller 		char *hashname;
151f1c316a3SStephan Mueller 
1524f9dabfaSEric Biggers 		if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) {
1534f9dabfaSEric Biggers 			ret = -EINVAL;
1544f9dabfaSEric Biggers 			goto out1;
1554f9dabfaSEric Biggers 		}
1564f9dabfaSEric Biggers 
157f1c316a3SStephan Mueller 		if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN ||
158f1c316a3SStephan Mueller 		    kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) {
159f1c316a3SStephan Mueller 			ret = -EMSGSIZE;
1607cbe0932SMat Martineau 			goto out1;
1614693fc73SStephan Mueller 		}
1624693fc73SStephan Mueller 
163f1c316a3SStephan Mueller 		/* get KDF name string */
164f1c316a3SStephan Mueller 		hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
165f1c316a3SStephan Mueller 		if (IS_ERR(hashname)) {
166f1c316a3SStephan Mueller 			ret = PTR_ERR(hashname);
1677cbe0932SMat Martineau 			goto out1;
168f1c316a3SStephan Mueller 		}
169f1c316a3SStephan Mueller 
170f1c316a3SStephan Mueller 		/* allocate KDF from the kernel crypto API */
171d3b04a43SStephan Müller 		ret = kdf_alloc(&hash, hashname);
172f1c316a3SStephan Mueller 		kfree(hashname);
173f1c316a3SStephan Mueller 		if (ret)
1747cbe0932SMat Martineau 			goto out1;
175f1c316a3SStephan Mueller 	}
176f1c316a3SStephan Mueller 
1777cbe0932SMat Martineau 	memset(&dh_inputs, 0, sizeof(dh_inputs));
1787cbe0932SMat Martineau 
1797cbe0932SMat Martineau 	dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p);
1807cbe0932SMat Martineau 	if (dlen < 0) {
1817cbe0932SMat Martineau 		ret = dlen;
1827cbe0932SMat Martineau 		goto out1;
1837cbe0932SMat Martineau 	}
1847cbe0932SMat Martineau 	dh_inputs.p_size = dlen;
1857cbe0932SMat Martineau 
1867cbe0932SMat Martineau 	dlen = dh_data_from_key(pcopy.base, &dh_inputs.g);
1877cbe0932SMat Martineau 	if (dlen < 0) {
1887cbe0932SMat Martineau 		ret = dlen;
1897cbe0932SMat Martineau 		goto out2;
1907cbe0932SMat Martineau 	}
1917cbe0932SMat Martineau 	dh_inputs.g_size = dlen;
1927cbe0932SMat Martineau 
1938c0f9f5bSLubomir Rintel 	dlen = dh_data_from_key(pcopy.private, &dh_inputs.key);
1947cbe0932SMat Martineau 	if (dlen < 0) {
1957cbe0932SMat Martineau 		ret = dlen;
1967cbe0932SMat Martineau 		goto out2;
1977cbe0932SMat Martineau 	}
1987cbe0932SMat Martineau 	dh_inputs.key_size = dlen;
1997cbe0932SMat Martineau 
2007cbe0932SMat Martineau 	secretlen = crypto_dh_key_len(&dh_inputs);
2017cbe0932SMat Martineau 	secret = kmalloc(secretlen, GFP_KERNEL);
2027cbe0932SMat Martineau 	if (!secret) {
2037cbe0932SMat Martineau 		ret = -ENOMEM;
2047cbe0932SMat Martineau 		goto out2;
2057cbe0932SMat Martineau 	}
2067cbe0932SMat Martineau 	ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs);
2077cbe0932SMat Martineau 	if (ret)
2087cbe0932SMat Martineau 		goto out3;
2097cbe0932SMat Martineau 
21085d7311fSEric Biggers 	tfm = crypto_alloc_kpp("dh", 0, 0);
2117cbe0932SMat Martineau 	if (IS_ERR(tfm)) {
2127cbe0932SMat Martineau 		ret = PTR_ERR(tfm);
2137cbe0932SMat Martineau 		goto out3;
2147cbe0932SMat Martineau 	}
2157cbe0932SMat Martineau 
2167cbe0932SMat Martineau 	ret = crypto_kpp_set_secret(tfm, secret, secretlen);
2177cbe0932SMat Martineau 	if (ret)
2187cbe0932SMat Martineau 		goto out4;
2197cbe0932SMat Martineau 
2207cbe0932SMat Martineau 	outlen = crypto_kpp_maxsize(tfm);
2217cbe0932SMat Martineau 
2227cbe0932SMat Martineau 	if (!kdfcopy) {
2237cbe0932SMat Martineau 		/*
2247cbe0932SMat Martineau 		 * When not using a KDF, buflen 0 is used to read the
2257cbe0932SMat Martineau 		 * required buffer length
2267cbe0932SMat Martineau 		 */
2277cbe0932SMat Martineau 		if (buflen == 0) {
2287cbe0932SMat Martineau 			ret = outlen;
2297cbe0932SMat Martineau 			goto out4;
2307cbe0932SMat Martineau 		} else if (outlen > buflen) {
2317cbe0932SMat Martineau 			ret = -EOVERFLOW;
2327cbe0932SMat Martineau 			goto out4;
2337cbe0932SMat Martineau 		}
2347cbe0932SMat Martineau 	}
2357cbe0932SMat Martineau 
2367cbe0932SMat Martineau 	outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen,
2377cbe0932SMat Martineau 			 GFP_KERNEL);
2387cbe0932SMat Martineau 	if (!outbuf) {
2397cbe0932SMat Martineau 		ret = -ENOMEM;
2407cbe0932SMat Martineau 		goto out4;
2417cbe0932SMat Martineau 	}
2427cbe0932SMat Martineau 
2437cbe0932SMat Martineau 	sg_init_one(&outsg, outbuf, outlen);
2447cbe0932SMat Martineau 
2457cbe0932SMat Martineau 	req = kpp_request_alloc(tfm, GFP_KERNEL);
2467cbe0932SMat Martineau 	if (!req) {
2477cbe0932SMat Martineau 		ret = -ENOMEM;
2487cbe0932SMat Martineau 		goto out5;
2497cbe0932SMat Martineau 	}
2507cbe0932SMat Martineau 
2517cbe0932SMat Martineau 	kpp_request_set_input(req, NULL, 0);
2527cbe0932SMat Martineau 	kpp_request_set_output(req, &outsg, outlen);
2537cbe0932SMat Martineau 	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
2547cbe0932SMat Martineau 				 CRYPTO_TFM_REQ_MAY_SLEEP,
255*5419f2b2SHerbert Xu 				 crypto_req_done, &compl);
2567cbe0932SMat Martineau 
257f1c316a3SStephan Mueller 	/*
2587cbe0932SMat Martineau 	 * For DH, generate_public_key and generate_shared_secret are
2597cbe0932SMat Martineau 	 * the same calculation
260f1c316a3SStephan Mueller 	 */
2617cbe0932SMat Martineau 	ret = crypto_kpp_generate_public_key(req);
262*5419f2b2SHerbert Xu 	ret = crypto_wait_req(ret, &compl);
2637cbe0932SMat Martineau 	if (ret)
2647cbe0932SMat Martineau 		goto out6;
265ddbb4114SMat Martineau 
2667cbe0932SMat Martineau 	if (kdfcopy) {
267f1c316a3SStephan Mueller 		/*
268f1c316a3SStephan Mueller 		 * Concatenate SP800-56A otherinfo past DH shared secret -- the
269f1c316a3SStephan Mueller 		 * input to the KDF is (DH shared secret || otherinfo)
270f1c316a3SStephan Mueller 		 */
2717cbe0932SMat Martineau 		if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo,
272f1c316a3SStephan Mueller 				   kdfcopy->otherinfolen) != 0) {
273f1c316a3SStephan Mueller 			ret = -EFAULT;
2747cbe0932SMat Martineau 			goto out6;
275f1c316a3SStephan Mueller 		}
276f1c316a3SStephan Mueller 
277d3b04a43SStephan Müller 		ret = keyctl_dh_compute_kdf(hash, buffer, buflen, outbuf,
278d7921344SStephan Müller 					    req->dst_len + kdfcopy->otherinfolen);
2797cbe0932SMat Martineau 	} else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) {
2807cbe0932SMat Martineau 		ret = req->dst_len;
281f1c316a3SStephan Mueller 	} else {
282ddbb4114SMat Martineau 		ret = -EFAULT;
283f1c316a3SStephan Mueller 	}
284ddbb4114SMat Martineau 
2857cbe0932SMat Martineau out6:
2867cbe0932SMat Martineau 	kpp_request_free(req);
2877cbe0932SMat Martineau out5:
288453431a5SWaiman Long 	kfree_sensitive(outbuf);
2897cbe0932SMat Martineau out4:
2907cbe0932SMat Martineau 	crypto_free_kpp(tfm);
2917cbe0932SMat Martineau out3:
292453431a5SWaiman Long 	kfree_sensitive(secret);
2937cbe0932SMat Martineau out2:
2947cbe0932SMat Martineau 	dh_free_data(&dh_inputs);
2957cbe0932SMat Martineau out1:
296d3b04a43SStephan Müller 	kdf_dealloc(hash);
297ddbb4114SMat Martineau 	return ret;
298ddbb4114SMat Martineau }
299f1c316a3SStephan Mueller 
keyctl_dh_compute(struct keyctl_dh_params __user * params,char __user * buffer,size_t buflen,struct keyctl_kdf_params __user * kdf)300f1c316a3SStephan Mueller long keyctl_dh_compute(struct keyctl_dh_params __user *params,
301f1c316a3SStephan Mueller 		       char __user *buffer, size_t buflen,
302f1c316a3SStephan Mueller 		       struct keyctl_kdf_params __user *kdf)
303f1c316a3SStephan Mueller {
304f1c316a3SStephan Mueller 	struct keyctl_kdf_params kdfcopy;
305f1c316a3SStephan Mueller 
306f1c316a3SStephan Mueller 	if (!kdf)
307f1c316a3SStephan Mueller 		return __keyctl_dh_compute(params, buffer, buflen, NULL);
308f1c316a3SStephan Mueller 
309f1c316a3SStephan Mueller 	if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0)
310f1c316a3SStephan Mueller 		return -EFAULT;
311f1c316a3SStephan Mueller 
312f1c316a3SStephan Mueller 	return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
313f1c316a3SStephan Mueller }
314