18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Crypto operations using stored keys
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2016, Intel Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
98c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
108c2ecf20Sopenharmony_ci#include <linux/crypto.h>
118c2ecf20Sopenharmony_ci#include <crypto/hash.h>
128c2ecf20Sopenharmony_ci#include <crypto/kpp.h>
138c2ecf20Sopenharmony_ci#include <crypto/dh.h>
148c2ecf20Sopenharmony_ci#include <keys/user-type.h>
158c2ecf20Sopenharmony_ci#include "internal.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic ssize_t dh_data_from_key(key_serial_t keyid, void **data)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct key *key;
208c2ecf20Sopenharmony_ci	key_ref_t key_ref;
218c2ecf20Sopenharmony_ci	long status;
228c2ecf20Sopenharmony_ci	ssize_t ret;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ);
258c2ecf20Sopenharmony_ci	if (IS_ERR(key_ref)) {
268c2ecf20Sopenharmony_ci		ret = -ENOKEY;
278c2ecf20Sopenharmony_ci		goto error;
288c2ecf20Sopenharmony_ci	}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	ret = -EOPNOTSUPP;
338c2ecf20Sopenharmony_ci	if (key->type == &key_type_user) {
348c2ecf20Sopenharmony_ci		down_read(&key->sem);
358c2ecf20Sopenharmony_ci		status = key_validate(key);
368c2ecf20Sopenharmony_ci		if (status == 0) {
378c2ecf20Sopenharmony_ci			const struct user_key_payload *payload;
388c2ecf20Sopenharmony_ci			uint8_t *duplicate;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci			payload = user_key_payload_locked(key);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci			duplicate = kmemdup(payload->data, payload->datalen,
438c2ecf20Sopenharmony_ci					    GFP_KERNEL);
448c2ecf20Sopenharmony_ci			if (duplicate) {
458c2ecf20Sopenharmony_ci				*data = duplicate;
468c2ecf20Sopenharmony_ci				ret = payload->datalen;
478c2ecf20Sopenharmony_ci			} else {
488c2ecf20Sopenharmony_ci				ret = -ENOMEM;
498c2ecf20Sopenharmony_ci			}
508c2ecf20Sopenharmony_ci		}
518c2ecf20Sopenharmony_ci		up_read(&key->sem);
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	key_put(key);
558c2ecf20Sopenharmony_cierror:
568c2ecf20Sopenharmony_ci	return ret;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic void dh_free_data(struct dh *dh)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	kfree_sensitive(dh->key);
628c2ecf20Sopenharmony_ci	kfree_sensitive(dh->p);
638c2ecf20Sopenharmony_ci	kfree_sensitive(dh->g);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistruct dh_completion {
678c2ecf20Sopenharmony_ci	struct completion completion;
688c2ecf20Sopenharmony_ci	int err;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void dh_crypto_done(struct crypto_async_request *req, int err)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct dh_completion *compl = req->data;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (err == -EINPROGRESS)
768c2ecf20Sopenharmony_ci		return;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	compl->err = err;
798c2ecf20Sopenharmony_ci	complete(&compl->completion);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistruct kdf_sdesc {
838c2ecf20Sopenharmony_ci	struct shash_desc shash;
848c2ecf20Sopenharmony_ci	char ctx[];
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct crypto_shash *tfm;
908c2ecf20Sopenharmony_ci	struct kdf_sdesc *sdesc;
918c2ecf20Sopenharmony_ci	int size;
928c2ecf20Sopenharmony_ci	int err;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* allocate synchronous hash */
958c2ecf20Sopenharmony_ci	tfm = crypto_alloc_shash(hashname, 0, 0);
968c2ecf20Sopenharmony_ci	if (IS_ERR(tfm)) {
978c2ecf20Sopenharmony_ci		pr_info("could not allocate digest TFM handle %s\n", hashname);
988c2ecf20Sopenharmony_ci		return PTR_ERR(tfm);
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	err = -EINVAL;
1028c2ecf20Sopenharmony_ci	if (crypto_shash_digestsize(tfm) == 0)
1038c2ecf20Sopenharmony_ci		goto out_free_tfm;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	err = -ENOMEM;
1068c2ecf20Sopenharmony_ci	size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
1078c2ecf20Sopenharmony_ci	sdesc = kmalloc(size, GFP_KERNEL);
1088c2ecf20Sopenharmony_ci	if (!sdesc)
1098c2ecf20Sopenharmony_ci		goto out_free_tfm;
1108c2ecf20Sopenharmony_ci	sdesc->shash.tfm = tfm;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	*sdesc_ret = sdesc;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciout_free_tfm:
1178c2ecf20Sopenharmony_ci	crypto_free_shash(tfm);
1188c2ecf20Sopenharmony_ci	return err;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void kdf_dealloc(struct kdf_sdesc *sdesc)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	if (!sdesc)
1248c2ecf20Sopenharmony_ci		return;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (sdesc->shash.tfm)
1278c2ecf20Sopenharmony_ci		crypto_free_shash(sdesc->shash.tfm);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	kfree_sensitive(sdesc);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/*
1338c2ecf20Sopenharmony_ci * Implementation of the KDF in counter mode according to SP800-108 section 5.1
1348c2ecf20Sopenharmony_ci * as well as SP800-56A section 5.8.1 (Single-step KDF).
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * SP800-56A:
1378c2ecf20Sopenharmony_ci * The src pointer is defined as Z || other info where Z is the shared secret
1388c2ecf20Sopenharmony_ci * from DH and other info is an arbitrary string (see SP800-56A section
1398c2ecf20Sopenharmony_ci * 5.8.1.2).
1408c2ecf20Sopenharmony_ci *
1418c2ecf20Sopenharmony_ci * 'dlen' must be a multiple of the digest size.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen,
1448c2ecf20Sopenharmony_ci		   u8 *dst, unsigned int dlen, unsigned int zlen)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct shash_desc *desc = &sdesc->shash;
1478c2ecf20Sopenharmony_ci	unsigned int h = crypto_shash_digestsize(desc->tfm);
1488c2ecf20Sopenharmony_ci	int err = 0;
1498c2ecf20Sopenharmony_ci	u8 *dst_orig = dst;
1508c2ecf20Sopenharmony_ci	__be32 counter = cpu_to_be32(1);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	while (dlen) {
1538c2ecf20Sopenharmony_ci		err = crypto_shash_init(desc);
1548c2ecf20Sopenharmony_ci		if (err)
1558c2ecf20Sopenharmony_ci			goto err;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32));
1588c2ecf20Sopenharmony_ci		if (err)
1598c2ecf20Sopenharmony_ci			goto err;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		if (zlen && h) {
1628c2ecf20Sopenharmony_ci			u8 tmpbuffer[32];
1638c2ecf20Sopenharmony_ci			size_t chunk = min_t(size_t, zlen, sizeof(tmpbuffer));
1648c2ecf20Sopenharmony_ci			memset(tmpbuffer, 0, chunk);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci			do {
1678c2ecf20Sopenharmony_ci				err = crypto_shash_update(desc, tmpbuffer,
1688c2ecf20Sopenharmony_ci							  chunk);
1698c2ecf20Sopenharmony_ci				if (err)
1708c2ecf20Sopenharmony_ci					goto err;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci				zlen -= chunk;
1738c2ecf20Sopenharmony_ci				chunk = min_t(size_t, zlen, sizeof(tmpbuffer));
1748c2ecf20Sopenharmony_ci			} while (zlen);
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		if (src && slen) {
1788c2ecf20Sopenharmony_ci			err = crypto_shash_update(desc, src, slen);
1798c2ecf20Sopenharmony_ci			if (err)
1808c2ecf20Sopenharmony_ci				goto err;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		err = crypto_shash_final(desc, dst);
1848c2ecf20Sopenharmony_ci		if (err)
1858c2ecf20Sopenharmony_ci			goto err;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		dlen -= h;
1888c2ecf20Sopenharmony_ci		dst += h;
1898c2ecf20Sopenharmony_ci		counter = cpu_to_be32(be32_to_cpu(counter) + 1);
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cierr:
1958c2ecf20Sopenharmony_ci	memzero_explicit(dst_orig, dlen);
1968c2ecf20Sopenharmony_ci	return err;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc,
2008c2ecf20Sopenharmony_ci				 char __user *buffer, size_t buflen,
2018c2ecf20Sopenharmony_ci				 uint8_t *kbuf, size_t kbuflen, size_t lzero)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	uint8_t *outbuf = NULL;
2048c2ecf20Sopenharmony_ci	int ret;
2058c2ecf20Sopenharmony_ci	size_t outbuf_len = roundup(buflen,
2068c2ecf20Sopenharmony_ci				    crypto_shash_digestsize(sdesc->shash.tfm));
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	outbuf = kmalloc(outbuf_len, GFP_KERNEL);
2098c2ecf20Sopenharmony_ci	if (!outbuf) {
2108c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2118c2ecf20Sopenharmony_ci		goto err;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, outbuf_len, lzero);
2158c2ecf20Sopenharmony_ci	if (ret)
2168c2ecf20Sopenharmony_ci		goto err;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	ret = buflen;
2198c2ecf20Sopenharmony_ci	if (copy_to_user(buffer, outbuf, buflen) != 0)
2208c2ecf20Sopenharmony_ci		ret = -EFAULT;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cierr:
2238c2ecf20Sopenharmony_ci	kfree_sensitive(outbuf);
2248c2ecf20Sopenharmony_ci	return ret;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cilong __keyctl_dh_compute(struct keyctl_dh_params __user *params,
2288c2ecf20Sopenharmony_ci			 char __user *buffer, size_t buflen,
2298c2ecf20Sopenharmony_ci			 struct keyctl_kdf_params *kdfcopy)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	long ret;
2328c2ecf20Sopenharmony_ci	ssize_t dlen;
2338c2ecf20Sopenharmony_ci	int secretlen;
2348c2ecf20Sopenharmony_ci	int outlen;
2358c2ecf20Sopenharmony_ci	struct keyctl_dh_params pcopy;
2368c2ecf20Sopenharmony_ci	struct dh dh_inputs;
2378c2ecf20Sopenharmony_ci	struct scatterlist outsg;
2388c2ecf20Sopenharmony_ci	struct dh_completion compl;
2398c2ecf20Sopenharmony_ci	struct crypto_kpp *tfm;
2408c2ecf20Sopenharmony_ci	struct kpp_request *req;
2418c2ecf20Sopenharmony_ci	uint8_t *secret;
2428c2ecf20Sopenharmony_ci	uint8_t *outbuf;
2438c2ecf20Sopenharmony_ci	struct kdf_sdesc *sdesc = NULL;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!params || (!buffer && buflen)) {
2468c2ecf20Sopenharmony_ci		ret = -EINVAL;
2478c2ecf20Sopenharmony_ci		goto out1;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
2508c2ecf20Sopenharmony_ci		ret = -EFAULT;
2518c2ecf20Sopenharmony_ci		goto out1;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (kdfcopy) {
2558c2ecf20Sopenharmony_ci		char *hashname;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) {
2588c2ecf20Sopenharmony_ci			ret = -EINVAL;
2598c2ecf20Sopenharmony_ci			goto out1;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN ||
2638c2ecf20Sopenharmony_ci		    kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) {
2648c2ecf20Sopenharmony_ci			ret = -EMSGSIZE;
2658c2ecf20Sopenharmony_ci			goto out1;
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci		/* get KDF name string */
2698c2ecf20Sopenharmony_ci		hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
2708c2ecf20Sopenharmony_ci		if (IS_ERR(hashname)) {
2718c2ecf20Sopenharmony_ci			ret = PTR_ERR(hashname);
2728c2ecf20Sopenharmony_ci			goto out1;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		/* allocate KDF from the kernel crypto API */
2768c2ecf20Sopenharmony_ci		ret = kdf_alloc(&sdesc, hashname);
2778c2ecf20Sopenharmony_ci		kfree(hashname);
2788c2ecf20Sopenharmony_ci		if (ret)
2798c2ecf20Sopenharmony_ci			goto out1;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	memset(&dh_inputs, 0, sizeof(dh_inputs));
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p);
2858c2ecf20Sopenharmony_ci	if (dlen < 0) {
2868c2ecf20Sopenharmony_ci		ret = dlen;
2878c2ecf20Sopenharmony_ci		goto out1;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci	dh_inputs.p_size = dlen;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	dlen = dh_data_from_key(pcopy.base, &dh_inputs.g);
2928c2ecf20Sopenharmony_ci	if (dlen < 0) {
2938c2ecf20Sopenharmony_ci		ret = dlen;
2948c2ecf20Sopenharmony_ci		goto out2;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	dh_inputs.g_size = dlen;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	dlen = dh_data_from_key(pcopy.private, &dh_inputs.key);
2998c2ecf20Sopenharmony_ci	if (dlen < 0) {
3008c2ecf20Sopenharmony_ci		ret = dlen;
3018c2ecf20Sopenharmony_ci		goto out2;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci	dh_inputs.key_size = dlen;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	secretlen = crypto_dh_key_len(&dh_inputs);
3068c2ecf20Sopenharmony_ci	secret = kmalloc(secretlen, GFP_KERNEL);
3078c2ecf20Sopenharmony_ci	if (!secret) {
3088c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3098c2ecf20Sopenharmony_ci		goto out2;
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs);
3128c2ecf20Sopenharmony_ci	if (ret)
3138c2ecf20Sopenharmony_ci		goto out3;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	tfm = crypto_alloc_kpp("dh", 0, 0);
3168c2ecf20Sopenharmony_ci	if (IS_ERR(tfm)) {
3178c2ecf20Sopenharmony_ci		ret = PTR_ERR(tfm);
3188c2ecf20Sopenharmony_ci		goto out3;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ret = crypto_kpp_set_secret(tfm, secret, secretlen);
3228c2ecf20Sopenharmony_ci	if (ret)
3238c2ecf20Sopenharmony_ci		goto out4;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	outlen = crypto_kpp_maxsize(tfm);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (!kdfcopy) {
3288c2ecf20Sopenharmony_ci		/*
3298c2ecf20Sopenharmony_ci		 * When not using a KDF, buflen 0 is used to read the
3308c2ecf20Sopenharmony_ci		 * required buffer length
3318c2ecf20Sopenharmony_ci		 */
3328c2ecf20Sopenharmony_ci		if (buflen == 0) {
3338c2ecf20Sopenharmony_ci			ret = outlen;
3348c2ecf20Sopenharmony_ci			goto out4;
3358c2ecf20Sopenharmony_ci		} else if (outlen > buflen) {
3368c2ecf20Sopenharmony_ci			ret = -EOVERFLOW;
3378c2ecf20Sopenharmony_ci			goto out4;
3388c2ecf20Sopenharmony_ci		}
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen,
3428c2ecf20Sopenharmony_ci			 GFP_KERNEL);
3438c2ecf20Sopenharmony_ci	if (!outbuf) {
3448c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3458c2ecf20Sopenharmony_ci		goto out4;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	sg_init_one(&outsg, outbuf, outlen);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	req = kpp_request_alloc(tfm, GFP_KERNEL);
3518c2ecf20Sopenharmony_ci	if (!req) {
3528c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3538c2ecf20Sopenharmony_ci		goto out5;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	kpp_request_set_input(req, NULL, 0);
3578c2ecf20Sopenharmony_ci	kpp_request_set_output(req, &outsg, outlen);
3588c2ecf20Sopenharmony_ci	init_completion(&compl.completion);
3598c2ecf20Sopenharmony_ci	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
3608c2ecf20Sopenharmony_ci				 CRYPTO_TFM_REQ_MAY_SLEEP,
3618c2ecf20Sopenharmony_ci				 dh_crypto_done, &compl);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/*
3648c2ecf20Sopenharmony_ci	 * For DH, generate_public_key and generate_shared_secret are
3658c2ecf20Sopenharmony_ci	 * the same calculation
3668c2ecf20Sopenharmony_ci	 */
3678c2ecf20Sopenharmony_ci	ret = crypto_kpp_generate_public_key(req);
3688c2ecf20Sopenharmony_ci	if (ret == -EINPROGRESS) {
3698c2ecf20Sopenharmony_ci		wait_for_completion(&compl.completion);
3708c2ecf20Sopenharmony_ci		ret = compl.err;
3718c2ecf20Sopenharmony_ci		if (ret)
3728c2ecf20Sopenharmony_ci			goto out6;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (kdfcopy) {
3768c2ecf20Sopenharmony_ci		/*
3778c2ecf20Sopenharmony_ci		 * Concatenate SP800-56A otherinfo past DH shared secret -- the
3788c2ecf20Sopenharmony_ci		 * input to the KDF is (DH shared secret || otherinfo)
3798c2ecf20Sopenharmony_ci		 */
3808c2ecf20Sopenharmony_ci		if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo,
3818c2ecf20Sopenharmony_ci				   kdfcopy->otherinfolen) != 0) {
3828c2ecf20Sopenharmony_ci			ret = -EFAULT;
3838c2ecf20Sopenharmony_ci			goto out6;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, outbuf,
3878c2ecf20Sopenharmony_ci					    req->dst_len + kdfcopy->otherinfolen,
3888c2ecf20Sopenharmony_ci					    outlen - req->dst_len);
3898c2ecf20Sopenharmony_ci	} else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) {
3908c2ecf20Sopenharmony_ci		ret = req->dst_len;
3918c2ecf20Sopenharmony_ci	} else {
3928c2ecf20Sopenharmony_ci		ret = -EFAULT;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ciout6:
3968c2ecf20Sopenharmony_ci	kpp_request_free(req);
3978c2ecf20Sopenharmony_ciout5:
3988c2ecf20Sopenharmony_ci	kfree_sensitive(outbuf);
3998c2ecf20Sopenharmony_ciout4:
4008c2ecf20Sopenharmony_ci	crypto_free_kpp(tfm);
4018c2ecf20Sopenharmony_ciout3:
4028c2ecf20Sopenharmony_ci	kfree_sensitive(secret);
4038c2ecf20Sopenharmony_ciout2:
4048c2ecf20Sopenharmony_ci	dh_free_data(&dh_inputs);
4058c2ecf20Sopenharmony_ciout1:
4068c2ecf20Sopenharmony_ci	kdf_dealloc(sdesc);
4078c2ecf20Sopenharmony_ci	return ret;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cilong keyctl_dh_compute(struct keyctl_dh_params __user *params,
4118c2ecf20Sopenharmony_ci		       char __user *buffer, size_t buflen,
4128c2ecf20Sopenharmony_ci		       struct keyctl_kdf_params __user *kdf)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct keyctl_kdf_params kdfcopy;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (!kdf)
4178c2ecf20Sopenharmony_ci		return __keyctl_dh_compute(params, buffer, buflen, NULL);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0)
4208c2ecf20Sopenharmony_ci		return -EFAULT;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
4238c2ecf20Sopenharmony_ci}
424