162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Crypto operations using stored keys 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2016, Intel Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/slab.h> 862306a36Sopenharmony_ci#include <linux/uaccess.h> 962306a36Sopenharmony_ci#include <linux/scatterlist.h> 1062306a36Sopenharmony_ci#include <linux/crypto.h> 1162306a36Sopenharmony_ci#include <crypto/hash.h> 1262306a36Sopenharmony_ci#include <crypto/kpp.h> 1362306a36Sopenharmony_ci#include <crypto/dh.h> 1462306a36Sopenharmony_ci#include <crypto/kdf_sp800108.h> 1562306a36Sopenharmony_ci#include <keys/user-type.h> 1662306a36Sopenharmony_ci#include "internal.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic ssize_t dh_data_from_key(key_serial_t keyid, const void **data) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci struct key *key; 2162306a36Sopenharmony_ci key_ref_t key_ref; 2262306a36Sopenharmony_ci long status; 2362306a36Sopenharmony_ci ssize_t ret; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ); 2662306a36Sopenharmony_ci if (IS_ERR(key_ref)) { 2762306a36Sopenharmony_ci ret = -ENOKEY; 2862306a36Sopenharmony_ci goto error; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci key = key_ref_to_ptr(key_ref); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 3462306a36Sopenharmony_ci if (key->type == &key_type_user) { 3562306a36Sopenharmony_ci down_read(&key->sem); 3662306a36Sopenharmony_ci status = key_validate(key); 3762306a36Sopenharmony_ci if (status == 0) { 3862306a36Sopenharmony_ci const struct user_key_payload *payload; 3962306a36Sopenharmony_ci uint8_t *duplicate; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci payload = user_key_payload_locked(key); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci duplicate = kmemdup(payload->data, payload->datalen, 4462306a36Sopenharmony_ci GFP_KERNEL); 4562306a36Sopenharmony_ci if (duplicate) { 4662306a36Sopenharmony_ci *data = duplicate; 4762306a36Sopenharmony_ci ret = payload->datalen; 4862306a36Sopenharmony_ci } else { 4962306a36Sopenharmony_ci ret = -ENOMEM; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci up_read(&key->sem); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci key_put(key); 5662306a36Sopenharmony_cierror: 5762306a36Sopenharmony_ci return ret; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void dh_free_data(struct dh *dh) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci kfree_sensitive(dh->key); 6362306a36Sopenharmony_ci kfree_sensitive(dh->p); 6462306a36Sopenharmony_ci kfree_sensitive(dh->g); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int kdf_alloc(struct crypto_shash **hash, char *hashname) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct crypto_shash *tfm; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* allocate synchronous hash */ 7262306a36Sopenharmony_ci tfm = crypto_alloc_shash(hashname, 0, 0); 7362306a36Sopenharmony_ci if (IS_ERR(tfm)) { 7462306a36Sopenharmony_ci pr_info("could not allocate digest TFM handle %s\n", hashname); 7562306a36Sopenharmony_ci return PTR_ERR(tfm); 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (crypto_shash_digestsize(tfm) == 0) { 7962306a36Sopenharmony_ci crypto_free_shash(tfm); 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci *hash = tfm; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void kdf_dealloc(struct crypto_shash *hash) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci if (hash) 9162306a36Sopenharmony_ci crypto_free_shash(hash); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int keyctl_dh_compute_kdf(struct crypto_shash *hash, 9562306a36Sopenharmony_ci char __user *buffer, size_t buflen, 9662306a36Sopenharmony_ci uint8_t *kbuf, size_t kbuflen) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct kvec kbuf_iov = { .iov_base = kbuf, .iov_len = kbuflen }; 9962306a36Sopenharmony_ci uint8_t *outbuf = NULL; 10062306a36Sopenharmony_ci int ret; 10162306a36Sopenharmony_ci size_t outbuf_len = roundup(buflen, crypto_shash_digestsize(hash)); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci outbuf = kmalloc(outbuf_len, GFP_KERNEL); 10462306a36Sopenharmony_ci if (!outbuf) { 10562306a36Sopenharmony_ci ret = -ENOMEM; 10662306a36Sopenharmony_ci goto err; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = crypto_kdf108_ctr_generate(hash, &kbuf_iov, 1, outbuf, outbuf_len); 11062306a36Sopenharmony_ci if (ret) 11162306a36Sopenharmony_ci goto err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ret = buflen; 11462306a36Sopenharmony_ci if (copy_to_user(buffer, outbuf, buflen) != 0) 11562306a36Sopenharmony_ci ret = -EFAULT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cierr: 11862306a36Sopenharmony_ci kfree_sensitive(outbuf); 11962306a36Sopenharmony_ci return ret; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cilong __keyctl_dh_compute(struct keyctl_dh_params __user *params, 12362306a36Sopenharmony_ci char __user *buffer, size_t buflen, 12462306a36Sopenharmony_ci struct keyctl_kdf_params *kdfcopy) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci long ret; 12762306a36Sopenharmony_ci ssize_t dlen; 12862306a36Sopenharmony_ci int secretlen; 12962306a36Sopenharmony_ci int outlen; 13062306a36Sopenharmony_ci struct keyctl_dh_params pcopy; 13162306a36Sopenharmony_ci struct dh dh_inputs; 13262306a36Sopenharmony_ci struct scatterlist outsg; 13362306a36Sopenharmony_ci DECLARE_CRYPTO_WAIT(compl); 13462306a36Sopenharmony_ci struct crypto_kpp *tfm; 13562306a36Sopenharmony_ci struct kpp_request *req; 13662306a36Sopenharmony_ci uint8_t *secret; 13762306a36Sopenharmony_ci uint8_t *outbuf; 13862306a36Sopenharmony_ci struct crypto_shash *hash = NULL; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!params || (!buffer && buflen)) { 14162306a36Sopenharmony_ci ret = -EINVAL; 14262306a36Sopenharmony_ci goto out1; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { 14562306a36Sopenharmony_ci ret = -EFAULT; 14662306a36Sopenharmony_ci goto out1; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (kdfcopy) { 15062306a36Sopenharmony_ci char *hashname; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) { 15362306a36Sopenharmony_ci ret = -EINVAL; 15462306a36Sopenharmony_ci goto out1; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || 15862306a36Sopenharmony_ci kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { 15962306a36Sopenharmony_ci ret = -EMSGSIZE; 16062306a36Sopenharmony_ci goto out1; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* get KDF name string */ 16462306a36Sopenharmony_ci hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); 16562306a36Sopenharmony_ci if (IS_ERR(hashname)) { 16662306a36Sopenharmony_ci ret = PTR_ERR(hashname); 16762306a36Sopenharmony_ci goto out1; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* allocate KDF from the kernel crypto API */ 17162306a36Sopenharmony_ci ret = kdf_alloc(&hash, hashname); 17262306a36Sopenharmony_ci kfree(hashname); 17362306a36Sopenharmony_ci if (ret) 17462306a36Sopenharmony_ci goto out1; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci memset(&dh_inputs, 0, sizeof(dh_inputs)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p); 18062306a36Sopenharmony_ci if (dlen < 0) { 18162306a36Sopenharmony_ci ret = dlen; 18262306a36Sopenharmony_ci goto out1; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci dh_inputs.p_size = dlen; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dlen = dh_data_from_key(pcopy.base, &dh_inputs.g); 18762306a36Sopenharmony_ci if (dlen < 0) { 18862306a36Sopenharmony_ci ret = dlen; 18962306a36Sopenharmony_ci goto out2; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci dh_inputs.g_size = dlen; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci dlen = dh_data_from_key(pcopy.private, &dh_inputs.key); 19462306a36Sopenharmony_ci if (dlen < 0) { 19562306a36Sopenharmony_ci ret = dlen; 19662306a36Sopenharmony_ci goto out2; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci dh_inputs.key_size = dlen; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci secretlen = crypto_dh_key_len(&dh_inputs); 20162306a36Sopenharmony_ci secret = kmalloc(secretlen, GFP_KERNEL); 20262306a36Sopenharmony_ci if (!secret) { 20362306a36Sopenharmony_ci ret = -ENOMEM; 20462306a36Sopenharmony_ci goto out2; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs); 20762306a36Sopenharmony_ci if (ret) 20862306a36Sopenharmony_ci goto out3; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci tfm = crypto_alloc_kpp("dh", 0, 0); 21162306a36Sopenharmony_ci if (IS_ERR(tfm)) { 21262306a36Sopenharmony_ci ret = PTR_ERR(tfm); 21362306a36Sopenharmony_ci goto out3; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ret = crypto_kpp_set_secret(tfm, secret, secretlen); 21762306a36Sopenharmony_ci if (ret) 21862306a36Sopenharmony_ci goto out4; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci outlen = crypto_kpp_maxsize(tfm); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!kdfcopy) { 22362306a36Sopenharmony_ci /* 22462306a36Sopenharmony_ci * When not using a KDF, buflen 0 is used to read the 22562306a36Sopenharmony_ci * required buffer length 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci if (buflen == 0) { 22862306a36Sopenharmony_ci ret = outlen; 22962306a36Sopenharmony_ci goto out4; 23062306a36Sopenharmony_ci } else if (outlen > buflen) { 23162306a36Sopenharmony_ci ret = -EOVERFLOW; 23262306a36Sopenharmony_ci goto out4; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen, 23762306a36Sopenharmony_ci GFP_KERNEL); 23862306a36Sopenharmony_ci if (!outbuf) { 23962306a36Sopenharmony_ci ret = -ENOMEM; 24062306a36Sopenharmony_ci goto out4; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci sg_init_one(&outsg, outbuf, outlen); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci req = kpp_request_alloc(tfm, GFP_KERNEL); 24662306a36Sopenharmony_ci if (!req) { 24762306a36Sopenharmony_ci ret = -ENOMEM; 24862306a36Sopenharmony_ci goto out5; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci kpp_request_set_input(req, NULL, 0); 25262306a36Sopenharmony_ci kpp_request_set_output(req, &outsg, outlen); 25362306a36Sopenharmony_ci kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | 25462306a36Sopenharmony_ci CRYPTO_TFM_REQ_MAY_SLEEP, 25562306a36Sopenharmony_ci crypto_req_done, &compl); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * For DH, generate_public_key and generate_shared_secret are 25962306a36Sopenharmony_ci * the same calculation 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci ret = crypto_kpp_generate_public_key(req); 26262306a36Sopenharmony_ci ret = crypto_wait_req(ret, &compl); 26362306a36Sopenharmony_ci if (ret) 26462306a36Sopenharmony_ci goto out6; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (kdfcopy) { 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * Concatenate SP800-56A otherinfo past DH shared secret -- the 26962306a36Sopenharmony_ci * input to the KDF is (DH shared secret || otherinfo) 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo, 27262306a36Sopenharmony_ci kdfcopy->otherinfolen) != 0) { 27362306a36Sopenharmony_ci ret = -EFAULT; 27462306a36Sopenharmony_ci goto out6; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = keyctl_dh_compute_kdf(hash, buffer, buflen, outbuf, 27862306a36Sopenharmony_ci req->dst_len + kdfcopy->otherinfolen); 27962306a36Sopenharmony_ci } else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) { 28062306a36Sopenharmony_ci ret = req->dst_len; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci ret = -EFAULT; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciout6: 28662306a36Sopenharmony_ci kpp_request_free(req); 28762306a36Sopenharmony_ciout5: 28862306a36Sopenharmony_ci kfree_sensitive(outbuf); 28962306a36Sopenharmony_ciout4: 29062306a36Sopenharmony_ci crypto_free_kpp(tfm); 29162306a36Sopenharmony_ciout3: 29262306a36Sopenharmony_ci kfree_sensitive(secret); 29362306a36Sopenharmony_ciout2: 29462306a36Sopenharmony_ci dh_free_data(&dh_inputs); 29562306a36Sopenharmony_ciout1: 29662306a36Sopenharmony_ci kdf_dealloc(hash); 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cilong keyctl_dh_compute(struct keyctl_dh_params __user *params, 30162306a36Sopenharmony_ci char __user *buffer, size_t buflen, 30262306a36Sopenharmony_ci struct keyctl_kdf_params __user *kdf) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct keyctl_kdf_params kdfcopy; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (!kdf) 30762306a36Sopenharmony_ci return __keyctl_dh_compute(params, buffer, buflen, NULL); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0) 31062306a36Sopenharmony_ci return -EFAULT; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); 31362306a36Sopenharmony_ci} 314