| /* Crypto operations using stored keys | 
 |  * | 
 |  * Copyright (c) 2016, Intel Corporation | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU General Public License | 
 |  * as published by the Free Software Foundation; either version | 
 |  * 2 of the License, or (at your option) any later version. | 
 |  */ | 
 |  | 
 | #include <linux/slab.h> | 
 | #include <linux/uaccess.h> | 
 | #include <linux/scatterlist.h> | 
 | #include <linux/crypto.h> | 
 | #include <crypto/hash.h> | 
 | #include <crypto/kpp.h> | 
 | #include <crypto/dh.h> | 
 | #include <keys/user-type.h> | 
 | #include "internal.h" | 
 |  | 
 | static ssize_t dh_data_from_key(key_serial_t keyid, void **data) | 
 | { | 
 | 	struct key *key; | 
 | 	key_ref_t key_ref; | 
 | 	long status; | 
 | 	ssize_t ret; | 
 |  | 
 | 	key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ); | 
 | 	if (IS_ERR(key_ref)) { | 
 | 		ret = -ENOKEY; | 
 | 		goto error; | 
 | 	} | 
 |  | 
 | 	key = key_ref_to_ptr(key_ref); | 
 |  | 
 | 	ret = -EOPNOTSUPP; | 
 | 	if (key->type == &key_type_user) { | 
 | 		down_read(&key->sem); | 
 | 		status = key_validate(key); | 
 | 		if (status == 0) { | 
 | 			const struct user_key_payload *payload; | 
 | 			uint8_t *duplicate; | 
 |  | 
 | 			payload = user_key_payload_locked(key); | 
 |  | 
 | 			duplicate = kmemdup(payload->data, payload->datalen, | 
 | 					    GFP_KERNEL); | 
 | 			if (duplicate) { | 
 | 				*data = duplicate; | 
 | 				ret = payload->datalen; | 
 | 			} else { | 
 | 				ret = -ENOMEM; | 
 | 			} | 
 | 		} | 
 | 		up_read(&key->sem); | 
 | 	} | 
 |  | 
 | 	key_put(key); | 
 | error: | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void dh_free_data(struct dh *dh) | 
 | { | 
 | 	kzfree(dh->key); | 
 | 	kzfree(dh->p); | 
 | 	kzfree(dh->g); | 
 | } | 
 |  | 
 | struct dh_completion { | 
 | 	struct completion completion; | 
 | 	int err; | 
 | }; | 
 |  | 
 | static void dh_crypto_done(struct crypto_async_request *req, int err) | 
 | { | 
 | 	struct dh_completion *compl = req->data; | 
 |  | 
 | 	if (err == -EINPROGRESS) | 
 | 		return; | 
 |  | 
 | 	compl->err = err; | 
 | 	complete(&compl->completion); | 
 | } | 
 |  | 
 | struct kdf_sdesc { | 
 | 	struct shash_desc shash; | 
 | 	char ctx[]; | 
 | }; | 
 |  | 
 | static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname) | 
 | { | 
 | 	struct crypto_shash *tfm; | 
 | 	struct kdf_sdesc *sdesc; | 
 | 	int size; | 
 | 	int err; | 
 |  | 
 | 	/* allocate synchronous hash */ | 
 | 	tfm = crypto_alloc_shash(hashname, 0, 0); | 
 | 	if (IS_ERR(tfm)) { | 
 | 		pr_info("could not allocate digest TFM handle %s\n", hashname); | 
 | 		return PTR_ERR(tfm); | 
 | 	} | 
 |  | 
 | 	err = -EINVAL; | 
 | 	if (crypto_shash_digestsize(tfm) == 0) | 
 | 		goto out_free_tfm; | 
 |  | 
 | 	err = -ENOMEM; | 
 | 	size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); | 
 | 	sdesc = kmalloc(size, GFP_KERNEL); | 
 | 	if (!sdesc) | 
 | 		goto out_free_tfm; | 
 | 	sdesc->shash.tfm = tfm; | 
 | 	sdesc->shash.flags = 0x0; | 
 |  | 
 | 	*sdesc_ret = sdesc; | 
 |  | 
 | 	return 0; | 
 |  | 
 | out_free_tfm: | 
 | 	crypto_free_shash(tfm); | 
 | 	return err; | 
 | } | 
 |  | 
 | static void kdf_dealloc(struct kdf_sdesc *sdesc) | 
 | { | 
 | 	if (!sdesc) | 
 | 		return; | 
 |  | 
 | 	if (sdesc->shash.tfm) | 
 | 		crypto_free_shash(sdesc->shash.tfm); | 
 |  | 
 | 	kzfree(sdesc); | 
 | } | 
 |  | 
 | /* | 
 |  * Implementation of the KDF in counter mode according to SP800-108 section 5.1 | 
 |  * as well as SP800-56A section 5.8.1 (Single-step KDF). | 
 |  * | 
 |  * SP800-56A: | 
 |  * The src pointer is defined as Z || other info where Z is the shared secret | 
 |  * from DH and other info is an arbitrary string (see SP800-56A section | 
 |  * 5.8.1.2). | 
 |  */ | 
 | static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, | 
 | 		   u8 *dst, unsigned int dlen, unsigned int zlen) | 
 | { | 
 | 	struct shash_desc *desc = &sdesc->shash; | 
 | 	unsigned int h = crypto_shash_digestsize(desc->tfm); | 
 | 	int err = 0; | 
 | 	u8 *dst_orig = dst; | 
 | 	__be32 counter = cpu_to_be32(1); | 
 |  | 
 | 	while (dlen) { | 
 | 		err = crypto_shash_init(desc); | 
 | 		if (err) | 
 | 			goto err; | 
 |  | 
 | 		err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32)); | 
 | 		if (err) | 
 | 			goto err; | 
 |  | 
 | 		if (zlen && h) { | 
 | 			u8 tmpbuffer[h]; | 
 | 			size_t chunk = min_t(size_t, zlen, h); | 
 | 			memset(tmpbuffer, 0, chunk); | 
 |  | 
 | 			do { | 
 | 				err = crypto_shash_update(desc, tmpbuffer, | 
 | 							  chunk); | 
 | 				if (err) | 
 | 					goto err; | 
 |  | 
 | 				zlen -= chunk; | 
 | 				chunk = min_t(size_t, zlen, h); | 
 | 			} while (zlen); | 
 | 		} | 
 |  | 
 | 		if (src && slen) { | 
 | 			err = crypto_shash_update(desc, src, slen); | 
 | 			if (err) | 
 | 				goto err; | 
 | 		} | 
 |  | 
 | 		if (dlen < h) { | 
 | 			u8 tmpbuffer[h]; | 
 |  | 
 | 			err = crypto_shash_final(desc, tmpbuffer); | 
 | 			if (err) | 
 | 				goto err; | 
 | 			memcpy(dst, tmpbuffer, dlen); | 
 | 			memzero_explicit(tmpbuffer, h); | 
 | 			return 0; | 
 | 		} else { | 
 | 			err = crypto_shash_final(desc, dst); | 
 | 			if (err) | 
 | 				goto err; | 
 |  | 
 | 			dlen -= h; | 
 | 			dst += h; | 
 | 			counter = cpu_to_be32(be32_to_cpu(counter) + 1); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | err: | 
 | 	memzero_explicit(dst_orig, dlen); | 
 | 	return err; | 
 | } | 
 |  | 
 | static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, | 
 | 				 char __user *buffer, size_t buflen, | 
 | 				 uint8_t *kbuf, size_t kbuflen, size_t lzero) | 
 | { | 
 | 	uint8_t *outbuf = NULL; | 
 | 	int ret; | 
 |  | 
 | 	outbuf = kmalloc(buflen, GFP_KERNEL); | 
 | 	if (!outbuf) { | 
 | 		ret = -ENOMEM; | 
 | 		goto err; | 
 | 	} | 
 |  | 
 | 	ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen, lzero); | 
 | 	if (ret) | 
 | 		goto err; | 
 |  | 
 | 	ret = buflen; | 
 | 	if (copy_to_user(buffer, outbuf, buflen) != 0) | 
 | 		ret = -EFAULT; | 
 |  | 
 | err: | 
 | 	kzfree(outbuf); | 
 | 	return ret; | 
 | } | 
 |  | 
 | long __keyctl_dh_compute(struct keyctl_dh_params __user *params, | 
 | 			 char __user *buffer, size_t buflen, | 
 | 			 struct keyctl_kdf_params *kdfcopy) | 
 | { | 
 | 	long ret; | 
 | 	ssize_t dlen; | 
 | 	int secretlen; | 
 | 	int outlen; | 
 | 	struct keyctl_dh_params pcopy; | 
 | 	struct dh dh_inputs; | 
 | 	struct scatterlist outsg; | 
 | 	struct dh_completion compl; | 
 | 	struct crypto_kpp *tfm; | 
 | 	struct kpp_request *req; | 
 | 	uint8_t *secret; | 
 | 	uint8_t *outbuf; | 
 | 	struct kdf_sdesc *sdesc = NULL; | 
 |  | 
 | 	if (!params || (!buffer && buflen)) { | 
 | 		ret = -EINVAL; | 
 | 		goto out1; | 
 | 	} | 
 | 	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { | 
 | 		ret = -EFAULT; | 
 | 		goto out1; | 
 | 	} | 
 |  | 
 | 	if (kdfcopy) { | 
 | 		char *hashname; | 
 |  | 
 | 		if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) { | 
 | 			ret = -EINVAL; | 
 | 			goto out1; | 
 | 		} | 
 |  | 
 | 		if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || | 
 | 		    kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { | 
 | 			ret = -EMSGSIZE; | 
 | 			goto out1; | 
 | 		} | 
 |  | 
 | 		/* get KDF name string */ | 
 | 		hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); | 
 | 		if (IS_ERR(hashname)) { | 
 | 			ret = PTR_ERR(hashname); | 
 | 			goto out1; | 
 | 		} | 
 |  | 
 | 		/* allocate KDF from the kernel crypto API */ | 
 | 		ret = kdf_alloc(&sdesc, hashname); | 
 | 		kfree(hashname); | 
 | 		if (ret) | 
 | 			goto out1; | 
 | 	} | 
 |  | 
 | 	memset(&dh_inputs, 0, sizeof(dh_inputs)); | 
 |  | 
 | 	dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p); | 
 | 	if (dlen < 0) { | 
 | 		ret = dlen; | 
 | 		goto out1; | 
 | 	} | 
 | 	dh_inputs.p_size = dlen; | 
 |  | 
 | 	dlen = dh_data_from_key(pcopy.base, &dh_inputs.g); | 
 | 	if (dlen < 0) { | 
 | 		ret = dlen; | 
 | 		goto out2; | 
 | 	} | 
 | 	dh_inputs.g_size = dlen; | 
 |  | 
 | 	dlen = dh_data_from_key(pcopy.private, &dh_inputs.key); | 
 | 	if (dlen < 0) { | 
 | 		ret = dlen; | 
 | 		goto out2; | 
 | 	} | 
 | 	dh_inputs.key_size = dlen; | 
 |  | 
 | 	secretlen = crypto_dh_key_len(&dh_inputs); | 
 | 	secret = kmalloc(secretlen, GFP_KERNEL); | 
 | 	if (!secret) { | 
 | 		ret = -ENOMEM; | 
 | 		goto out2; | 
 | 	} | 
 | 	ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs); | 
 | 	if (ret) | 
 | 		goto out3; | 
 |  | 
 | 	tfm = crypto_alloc_kpp("dh", CRYPTO_ALG_TYPE_KPP, 0); | 
 | 	if (IS_ERR(tfm)) { | 
 | 		ret = PTR_ERR(tfm); | 
 | 		goto out3; | 
 | 	} | 
 |  | 
 | 	ret = crypto_kpp_set_secret(tfm, secret, secretlen); | 
 | 	if (ret) | 
 | 		goto out4; | 
 |  | 
 | 	outlen = crypto_kpp_maxsize(tfm); | 
 |  | 
 | 	if (!kdfcopy) { | 
 | 		/* | 
 | 		 * When not using a KDF, buflen 0 is used to read the | 
 | 		 * required buffer length | 
 | 		 */ | 
 | 		if (buflen == 0) { | 
 | 			ret = outlen; | 
 | 			goto out4; | 
 | 		} else if (outlen > buflen) { | 
 | 			ret = -EOVERFLOW; | 
 | 			goto out4; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen, | 
 | 			 GFP_KERNEL); | 
 | 	if (!outbuf) { | 
 | 		ret = -ENOMEM; | 
 | 		goto out4; | 
 | 	} | 
 |  | 
 | 	sg_init_one(&outsg, outbuf, outlen); | 
 |  | 
 | 	req = kpp_request_alloc(tfm, GFP_KERNEL); | 
 | 	if (!req) { | 
 | 		ret = -ENOMEM; | 
 | 		goto out5; | 
 | 	} | 
 |  | 
 | 	kpp_request_set_input(req, NULL, 0); | 
 | 	kpp_request_set_output(req, &outsg, outlen); | 
 | 	init_completion(&compl.completion); | 
 | 	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | | 
 | 				 CRYPTO_TFM_REQ_MAY_SLEEP, | 
 | 				 dh_crypto_done, &compl); | 
 |  | 
 | 	/* | 
 | 	 * For DH, generate_public_key and generate_shared_secret are | 
 | 	 * the same calculation | 
 | 	 */ | 
 | 	ret = crypto_kpp_generate_public_key(req); | 
 | 	if (ret == -EINPROGRESS) { | 
 | 		wait_for_completion(&compl.completion); | 
 | 		ret = compl.err; | 
 | 		if (ret) | 
 | 			goto out6; | 
 | 	} | 
 |  | 
 | 	if (kdfcopy) { | 
 | 		/* | 
 | 		 * Concatenate SP800-56A otherinfo past DH shared secret -- the | 
 | 		 * input to the KDF is (DH shared secret || otherinfo) | 
 | 		 */ | 
 | 		if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo, | 
 | 				   kdfcopy->otherinfolen) != 0) { | 
 | 			ret = -EFAULT; | 
 | 			goto out6; | 
 | 		} | 
 |  | 
 | 		ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, outbuf, | 
 | 					    req->dst_len + kdfcopy->otherinfolen, | 
 | 					    outlen - req->dst_len); | 
 | 	} else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) { | 
 | 		ret = req->dst_len; | 
 | 	} else { | 
 | 		ret = -EFAULT; | 
 | 	} | 
 |  | 
 | out6: | 
 | 	kpp_request_free(req); | 
 | out5: | 
 | 	kzfree(outbuf); | 
 | out4: | 
 | 	crypto_free_kpp(tfm); | 
 | out3: | 
 | 	kzfree(secret); | 
 | out2: | 
 | 	dh_free_data(&dh_inputs); | 
 | out1: | 
 | 	kdf_dealloc(sdesc); | 
 | 	return ret; | 
 | } | 
 |  | 
 | long keyctl_dh_compute(struct keyctl_dh_params __user *params, | 
 | 		       char __user *buffer, size_t buflen, | 
 | 		       struct keyctl_kdf_params __user *kdf) | 
 | { | 
 | 	struct keyctl_kdf_params kdfcopy; | 
 |  | 
 | 	if (!kdf) | 
 | 		return __keyctl_dh_compute(params, buffer, buflen, NULL); | 
 |  | 
 | 	if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0) | 
 | 		return -EFAULT; | 
 |  | 
 | 	return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); | 
 | } |