cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dh.c (6858B)


      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
     18static 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(keyid, 0, KEY_NEED_READ);
     26	if (IS_ERR(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(&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(payload->data, 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(&key->sem);
     53	}
     54
     55	key_put(key);
     56error:
     57	return ret;
     58}
     59
     60static void dh_free_data(struct dh *dh)
     61{
     62	kfree_sensitive(dh->key);
     63	kfree_sensitive(dh->p);
     64	kfree_sensitive(dh->g);
     65}
     66
     67struct dh_completion {
     68	struct completion completion;
     69	int err;
     70};
     71
     72static void dh_crypto_done(struct crypto_async_request *req, int err)
     73{
     74	struct dh_completion *compl = req->data;
     75
     76	if (err == -EINPROGRESS)
     77		return;
     78
     79	compl->err = err;
     80	complete(&compl->completion);
     81}
     82
     83static int kdf_alloc(struct crypto_shash **hash, char *hashname)
     84{
     85	struct crypto_shash *tfm;
     86
     87	/* allocate synchronous hash */
     88	tfm = crypto_alloc_shash(hashname, 0, 0);
     89	if (IS_ERR(tfm)) {
     90		pr_info("could not allocate digest TFM handle %s\n", hashname);
     91		return PTR_ERR(tfm);
     92	}
     93
     94	if (crypto_shash_digestsize(tfm) == 0) {
     95		crypto_free_shash(tfm);
     96		return -EINVAL;
     97	}
     98
     99	*hash = tfm;
    100
    101	return 0;
    102}
    103
    104static void kdf_dealloc(struct crypto_shash *hash)
    105{
    106	if (hash)
    107		crypto_free_shash(hash);
    108}
    109
    110static int keyctl_dh_compute_kdf(struct crypto_shash *hash,
    111				 char __user *buffer, size_t buflen,
    112				 uint8_t *kbuf, size_t kbuflen)
    113{
    114	struct kvec kbuf_iov = { .iov_base = kbuf, .iov_len = kbuflen };
    115	uint8_t *outbuf = NULL;
    116	int ret;
    117	size_t outbuf_len = roundup(buflen, crypto_shash_digestsize(hash));
    118
    119	outbuf = kmalloc(outbuf_len, GFP_KERNEL);
    120	if (!outbuf) {
    121		ret = -ENOMEM;
    122		goto err;
    123	}
    124
    125	ret = crypto_kdf108_ctr_generate(hash, &kbuf_iov, 1, outbuf, outbuf_len);
    126	if (ret)
    127		goto err;
    128
    129	ret = buflen;
    130	if (copy_to_user(buffer, outbuf, buflen) != 0)
    131		ret = -EFAULT;
    132
    133err:
    134	kfree_sensitive(outbuf);
    135	return ret;
    136}
    137
    138long __keyctl_dh_compute(struct keyctl_dh_params __user *params,
    139			 char __user *buffer, size_t buflen,
    140			 struct keyctl_kdf_params *kdfcopy)
    141{
    142	long ret;
    143	ssize_t dlen;
    144	int secretlen;
    145	int outlen;
    146	struct keyctl_dh_params pcopy;
    147	struct dh dh_inputs;
    148	struct scatterlist outsg;
    149	struct dh_completion compl;
    150	struct crypto_kpp *tfm;
    151	struct kpp_request *req;
    152	uint8_t *secret;
    153	uint8_t *outbuf;
    154	struct crypto_shash *hash = NULL;
    155
    156	if (!params || (!buffer && buflen)) {
    157		ret = -EINVAL;
    158		goto out1;
    159	}
    160	if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) {
    161		ret = -EFAULT;
    162		goto out1;
    163	}
    164
    165	if (kdfcopy) {
    166		char *hashname;
    167
    168		if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) {
    169			ret = -EINVAL;
    170			goto out1;
    171		}
    172
    173		if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN ||
    174		    kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) {
    175			ret = -EMSGSIZE;
    176			goto out1;
    177		}
    178
    179		/* get KDF name string */
    180		hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME);
    181		if (IS_ERR(hashname)) {
    182			ret = PTR_ERR(hashname);
    183			goto out1;
    184		}
    185
    186		/* allocate KDF from the kernel crypto API */
    187		ret = kdf_alloc(&hash, hashname);
    188		kfree(hashname);
    189		if (ret)
    190			goto out1;
    191	}
    192
    193	memset(&dh_inputs, 0, sizeof(dh_inputs));
    194
    195	dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p);
    196	if (dlen < 0) {
    197		ret = dlen;
    198		goto out1;
    199	}
    200	dh_inputs.p_size = dlen;
    201
    202	dlen = dh_data_from_key(pcopy.base, &dh_inputs.g);
    203	if (dlen < 0) {
    204		ret = dlen;
    205		goto out2;
    206	}
    207	dh_inputs.g_size = dlen;
    208
    209	dlen = dh_data_from_key(pcopy.private, &dh_inputs.key);
    210	if (dlen < 0) {
    211		ret = dlen;
    212		goto out2;
    213	}
    214	dh_inputs.key_size = dlen;
    215
    216	secretlen = crypto_dh_key_len(&dh_inputs);
    217	secret = kmalloc(secretlen, GFP_KERNEL);
    218	if (!secret) {
    219		ret = -ENOMEM;
    220		goto out2;
    221	}
    222	ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs);
    223	if (ret)
    224		goto out3;
    225
    226	tfm = crypto_alloc_kpp("dh", 0, 0);
    227	if (IS_ERR(tfm)) {
    228		ret = PTR_ERR(tfm);
    229		goto out3;
    230	}
    231
    232	ret = crypto_kpp_set_secret(tfm, secret, secretlen);
    233	if (ret)
    234		goto out4;
    235
    236	outlen = crypto_kpp_maxsize(tfm);
    237
    238	if (!kdfcopy) {
    239		/*
    240		 * When not using a KDF, buflen 0 is used to read the
    241		 * required buffer length
    242		 */
    243		if (buflen == 0) {
    244			ret = outlen;
    245			goto out4;
    246		} else if (outlen > buflen) {
    247			ret = -EOVERFLOW;
    248			goto out4;
    249		}
    250	}
    251
    252	outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen,
    253			 GFP_KERNEL);
    254	if (!outbuf) {
    255		ret = -ENOMEM;
    256		goto out4;
    257	}
    258
    259	sg_init_one(&outsg, outbuf, outlen);
    260
    261	req = kpp_request_alloc(tfm, GFP_KERNEL);
    262	if (!req) {
    263		ret = -ENOMEM;
    264		goto out5;
    265	}
    266
    267	kpp_request_set_input(req, NULL, 0);
    268	kpp_request_set_output(req, &outsg, outlen);
    269	init_completion(&compl.completion);
    270	kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
    271				 CRYPTO_TFM_REQ_MAY_SLEEP,
    272				 dh_crypto_done, &compl);
    273
    274	/*
    275	 * For DH, generate_public_key and generate_shared_secret are
    276	 * the same calculation
    277	 */
    278	ret = crypto_kpp_generate_public_key(req);
    279	if (ret == -EINPROGRESS) {
    280		wait_for_completion(&compl.completion);
    281		ret = compl.err;
    282		if (ret)
    283			goto out6;
    284	}
    285
    286	if (kdfcopy) {
    287		/*
    288		 * Concatenate SP800-56A otherinfo past DH shared secret -- the
    289		 * input to the KDF is (DH shared secret || otherinfo)
    290		 */
    291		if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo,
    292				   kdfcopy->otherinfolen) != 0) {
    293			ret = -EFAULT;
    294			goto out6;
    295		}
    296
    297		ret = keyctl_dh_compute_kdf(hash, buffer, buflen, outbuf,
    298					    req->dst_len + kdfcopy->otherinfolen);
    299	} else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) {
    300		ret = req->dst_len;
    301	} else {
    302		ret = -EFAULT;
    303	}
    304
    305out6:
    306	kpp_request_free(req);
    307out5:
    308	kfree_sensitive(outbuf);
    309out4:
    310	crypto_free_kpp(tfm);
    311out3:
    312	kfree_sensitive(secret);
    313out2:
    314	dh_free_data(&dh_inputs);
    315out1:
    316	kdf_dealloc(hash);
    317	return ret;
    318}
    319
    320long keyctl_dh_compute(struct keyctl_dh_params __user *params,
    321		       char __user *buffer, size_t buflen,
    322		       struct keyctl_kdf_params __user *kdf)
    323{
    324	struct keyctl_kdf_params kdfcopy;
    325
    326	if (!kdf)
    327		return __keyctl_dh_compute(params, buffer, buflen, NULL);
    328
    329	if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0)
    330		return -EFAULT;
    331
    332	return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy);
    333}