162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NVMe over Fabrics DH-HMAC-CHAP authentication. 462306a36Sopenharmony_ci * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <crypto/hash.h> 1362306a36Sopenharmony_ci#include <linux/crc32.h> 1462306a36Sopenharmony_ci#include <linux/base64.h> 1562306a36Sopenharmony_ci#include <linux/ctype.h> 1662306a36Sopenharmony_ci#include <linux/random.h> 1762306a36Sopenharmony_ci#include <linux/nvme-auth.h> 1862306a36Sopenharmony_ci#include <asm/unaligned.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "nvmet.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ciint nvmet_auth_set_key(struct nvmet_host *host, const char *secret, 2362306a36Sopenharmony_ci bool set_ctrl) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned char key_hash; 2662306a36Sopenharmony_ci char *dhchap_secret; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1) 2962306a36Sopenharmony_ci return -EINVAL; 3062306a36Sopenharmony_ci if (key_hash > 3) { 3162306a36Sopenharmony_ci pr_warn("Invalid DH-HMAC-CHAP hash id %d\n", 3262306a36Sopenharmony_ci key_hash); 3362306a36Sopenharmony_ci return -EINVAL; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci if (key_hash > 0) { 3662306a36Sopenharmony_ci /* Validate selected hash algorithm */ 3762306a36Sopenharmony_ci const char *hmac = nvme_auth_hmac_name(key_hash); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (!crypto_has_shash(hmac, 0, 0)) { 4062306a36Sopenharmony_ci pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac); 4162306a36Sopenharmony_ci return -ENOTSUPP; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci dhchap_secret = kstrdup(secret, GFP_KERNEL); 4562306a36Sopenharmony_ci if (!dhchap_secret) 4662306a36Sopenharmony_ci return -ENOMEM; 4762306a36Sopenharmony_ci if (set_ctrl) { 4862306a36Sopenharmony_ci kfree(host->dhchap_ctrl_secret); 4962306a36Sopenharmony_ci host->dhchap_ctrl_secret = strim(dhchap_secret); 5062306a36Sopenharmony_ci host->dhchap_ctrl_key_hash = key_hash; 5162306a36Sopenharmony_ci } else { 5262306a36Sopenharmony_ci kfree(host->dhchap_secret); 5362306a36Sopenharmony_ci host->dhchap_secret = strim(dhchap_secret); 5462306a36Sopenharmony_ci host->dhchap_key_hash = key_hash; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciint nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci const char *dhgroup_kpp; 6262306a36Sopenharmony_ci int ret = 0; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci pr_debug("%s: ctrl %d selecting dhgroup %d\n", 6562306a36Sopenharmony_ci __func__, ctrl->cntlid, dhgroup_id); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (ctrl->dh_tfm) { 6862306a36Sopenharmony_ci if (ctrl->dh_gid == dhgroup_id) { 6962306a36Sopenharmony_ci pr_debug("%s: ctrl %d reuse existing DH group %d\n", 7062306a36Sopenharmony_ci __func__, ctrl->cntlid, dhgroup_id); 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci crypto_free_kpp(ctrl->dh_tfm); 7462306a36Sopenharmony_ci ctrl->dh_tfm = NULL; 7562306a36Sopenharmony_ci ctrl->dh_gid = 0; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (dhgroup_id == NVME_AUTH_DHGROUP_NULL) 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id); 8262306a36Sopenharmony_ci if (!dhgroup_kpp) { 8362306a36Sopenharmony_ci pr_debug("%s: ctrl %d invalid DH group %d\n", 8462306a36Sopenharmony_ci __func__, ctrl->cntlid, dhgroup_id); 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0); 8862306a36Sopenharmony_ci if (IS_ERR(ctrl->dh_tfm)) { 8962306a36Sopenharmony_ci pr_debug("%s: ctrl %d failed to setup DH group %d, err %ld\n", 9062306a36Sopenharmony_ci __func__, ctrl->cntlid, dhgroup_id, 9162306a36Sopenharmony_ci PTR_ERR(ctrl->dh_tfm)); 9262306a36Sopenharmony_ci ret = PTR_ERR(ctrl->dh_tfm); 9362306a36Sopenharmony_ci ctrl->dh_tfm = NULL; 9462306a36Sopenharmony_ci ctrl->dh_gid = 0; 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci ctrl->dh_gid = dhgroup_id; 9762306a36Sopenharmony_ci pr_debug("%s: ctrl %d setup DH group %d\n", 9862306a36Sopenharmony_ci __func__, ctrl->cntlid, ctrl->dh_gid); 9962306a36Sopenharmony_ci ret = nvme_auth_gen_privkey(ctrl->dh_tfm, ctrl->dh_gid); 10062306a36Sopenharmony_ci if (ret < 0) { 10162306a36Sopenharmony_ci pr_debug("%s: ctrl %d failed to generate private key, err %d\n", 10262306a36Sopenharmony_ci __func__, ctrl->cntlid, ret); 10362306a36Sopenharmony_ci kfree_sensitive(ctrl->dh_key); 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm); 10762306a36Sopenharmony_ci kfree_sensitive(ctrl->dh_key); 10862306a36Sopenharmony_ci ctrl->dh_key = kzalloc(ctrl->dh_keysize, GFP_KERNEL); 10962306a36Sopenharmony_ci if (!ctrl->dh_key) { 11062306a36Sopenharmony_ci pr_warn("ctrl %d failed to allocate public key\n", 11162306a36Sopenharmony_ci ctrl->cntlid); 11262306a36Sopenharmony_ci return -ENOMEM; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci ret = nvme_auth_gen_pubkey(ctrl->dh_tfm, ctrl->dh_key, 11562306a36Sopenharmony_ci ctrl->dh_keysize); 11662306a36Sopenharmony_ci if (ret < 0) { 11762306a36Sopenharmony_ci pr_warn("ctrl %d failed to generate public key\n", 11862306a36Sopenharmony_ci ctrl->cntlid); 11962306a36Sopenharmony_ci kfree(ctrl->dh_key); 12062306a36Sopenharmony_ci ctrl->dh_key = NULL; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint nvmet_setup_auth(struct nvmet_ctrl *ctrl) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int ret = 0; 13062306a36Sopenharmony_ci struct nvmet_host_link *p; 13162306a36Sopenharmony_ci struct nvmet_host *host = NULL; 13262306a36Sopenharmony_ci const char *hash_name; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci down_read(&nvmet_config_sem); 13562306a36Sopenharmony_ci if (nvmet_is_disc_subsys(ctrl->subsys)) 13662306a36Sopenharmony_ci goto out_unlock; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (ctrl->subsys->allow_any_host) 13962306a36Sopenharmony_ci goto out_unlock; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci list_for_each_entry(p, &ctrl->subsys->hosts, entry) { 14262306a36Sopenharmony_ci pr_debug("check %s\n", nvmet_host_name(p->host)); 14362306a36Sopenharmony_ci if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn)) 14462306a36Sopenharmony_ci continue; 14562306a36Sopenharmony_ci host = p->host; 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci if (!host) { 14962306a36Sopenharmony_ci pr_debug("host %s not found\n", ctrl->hostnqn); 15062306a36Sopenharmony_ci ret = -EPERM; 15162306a36Sopenharmony_ci goto out_unlock; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id); 15562306a36Sopenharmony_ci if (ret < 0) 15662306a36Sopenharmony_ci pr_warn("Failed to setup DH group"); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!host->dhchap_secret) { 15962306a36Sopenharmony_ci pr_debug("No authentication provided\n"); 16062306a36Sopenharmony_ci goto out_unlock; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (host->dhchap_hash_id == ctrl->shash_id) { 16462306a36Sopenharmony_ci pr_debug("Re-use existing hash ID %d\n", 16562306a36Sopenharmony_ci ctrl->shash_id); 16662306a36Sopenharmony_ci } else { 16762306a36Sopenharmony_ci hash_name = nvme_auth_hmac_name(host->dhchap_hash_id); 16862306a36Sopenharmony_ci if (!hash_name) { 16962306a36Sopenharmony_ci pr_warn("Hash ID %d invalid\n", host->dhchap_hash_id); 17062306a36Sopenharmony_ci ret = -EINVAL; 17162306a36Sopenharmony_ci goto out_unlock; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci ctrl->shash_id = host->dhchap_hash_id; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Skip the 'DHHC-1:XX:' prefix */ 17762306a36Sopenharmony_ci nvme_auth_free_key(ctrl->host_key); 17862306a36Sopenharmony_ci ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10, 17962306a36Sopenharmony_ci host->dhchap_key_hash); 18062306a36Sopenharmony_ci if (IS_ERR(ctrl->host_key)) { 18162306a36Sopenharmony_ci ret = PTR_ERR(ctrl->host_key); 18262306a36Sopenharmony_ci ctrl->host_key = NULL; 18362306a36Sopenharmony_ci goto out_free_hash; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci pr_debug("%s: using hash %s key %*ph\n", __func__, 18662306a36Sopenharmony_ci ctrl->host_key->hash > 0 ? 18762306a36Sopenharmony_ci nvme_auth_hmac_name(ctrl->host_key->hash) : "none", 18862306a36Sopenharmony_ci (int)ctrl->host_key->len, ctrl->host_key->key); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci nvme_auth_free_key(ctrl->ctrl_key); 19162306a36Sopenharmony_ci if (!host->dhchap_ctrl_secret) { 19262306a36Sopenharmony_ci ctrl->ctrl_key = NULL; 19362306a36Sopenharmony_ci goto out_unlock; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10, 19762306a36Sopenharmony_ci host->dhchap_ctrl_key_hash); 19862306a36Sopenharmony_ci if (IS_ERR(ctrl->ctrl_key)) { 19962306a36Sopenharmony_ci ret = PTR_ERR(ctrl->ctrl_key); 20062306a36Sopenharmony_ci ctrl->ctrl_key = NULL; 20162306a36Sopenharmony_ci goto out_free_hash; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci pr_debug("%s: using ctrl hash %s key %*ph\n", __func__, 20462306a36Sopenharmony_ci ctrl->ctrl_key->hash > 0 ? 20562306a36Sopenharmony_ci nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none", 20662306a36Sopenharmony_ci (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciout_free_hash: 20962306a36Sopenharmony_ci if (ret) { 21062306a36Sopenharmony_ci if (ctrl->host_key) { 21162306a36Sopenharmony_ci nvme_auth_free_key(ctrl->host_key); 21262306a36Sopenharmony_ci ctrl->host_key = NULL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci ctrl->shash_id = 0; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ciout_unlock: 21762306a36Sopenharmony_ci up_read(&nvmet_config_sem); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_civoid nvmet_auth_sq_free(struct nvmet_sq *sq) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci cancel_delayed_work(&sq->auth_expired_work); 22562306a36Sopenharmony_ci kfree(sq->dhchap_c1); 22662306a36Sopenharmony_ci sq->dhchap_c1 = NULL; 22762306a36Sopenharmony_ci kfree(sq->dhchap_c2); 22862306a36Sopenharmony_ci sq->dhchap_c2 = NULL; 22962306a36Sopenharmony_ci kfree(sq->dhchap_skey); 23062306a36Sopenharmony_ci sq->dhchap_skey = NULL; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_civoid nvmet_destroy_auth(struct nvmet_ctrl *ctrl) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci ctrl->shash_id = 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (ctrl->dh_tfm) { 23862306a36Sopenharmony_ci crypto_free_kpp(ctrl->dh_tfm); 23962306a36Sopenharmony_ci ctrl->dh_tfm = NULL; 24062306a36Sopenharmony_ci ctrl->dh_gid = 0; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci kfree_sensitive(ctrl->dh_key); 24362306a36Sopenharmony_ci ctrl->dh_key = NULL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (ctrl->host_key) { 24662306a36Sopenharmony_ci nvme_auth_free_key(ctrl->host_key); 24762306a36Sopenharmony_ci ctrl->host_key = NULL; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci if (ctrl->ctrl_key) { 25062306a36Sopenharmony_ci nvme_auth_free_key(ctrl->ctrl_key); 25162306a36Sopenharmony_ci ctrl->ctrl_key = NULL; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cibool nvmet_check_auth_status(struct nvmet_req *req) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci if (req->sq->ctrl->host_key && 25862306a36Sopenharmony_ci !req->sq->authenticated) 25962306a36Sopenharmony_ci return false; 26062306a36Sopenharmony_ci return true; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciint nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, 26462306a36Sopenharmony_ci unsigned int shash_len) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct crypto_shash *shash_tfm; 26762306a36Sopenharmony_ci struct shash_desc *shash; 26862306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 26962306a36Sopenharmony_ci const char *hash_name; 27062306a36Sopenharmony_ci u8 *challenge = req->sq->dhchap_c1, *host_response; 27162306a36Sopenharmony_ci u8 buf[4]; 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci hash_name = nvme_auth_hmac_name(ctrl->shash_id); 27562306a36Sopenharmony_ci if (!hash_name) { 27662306a36Sopenharmony_ci pr_warn("Hash ID %d invalid\n", ctrl->shash_id); 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci shash_tfm = crypto_alloc_shash(hash_name, 0, 0); 28162306a36Sopenharmony_ci if (IS_ERR(shash_tfm)) { 28262306a36Sopenharmony_ci pr_err("failed to allocate shash %s\n", hash_name); 28362306a36Sopenharmony_ci return PTR_ERR(shash_tfm); 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (shash_len != crypto_shash_digestsize(shash_tfm)) { 28762306a36Sopenharmony_ci pr_debug("%s: hash len mismatch (len %d digest %d)\n", 28862306a36Sopenharmony_ci __func__, shash_len, 28962306a36Sopenharmony_ci crypto_shash_digestsize(shash_tfm)); 29062306a36Sopenharmony_ci ret = -EINVAL; 29162306a36Sopenharmony_ci goto out_free_tfm; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci host_response = nvme_auth_transform_key(ctrl->host_key, ctrl->hostnqn); 29562306a36Sopenharmony_ci if (IS_ERR(host_response)) { 29662306a36Sopenharmony_ci ret = PTR_ERR(host_response); 29762306a36Sopenharmony_ci goto out_free_tfm; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = crypto_shash_setkey(shash_tfm, host_response, 30162306a36Sopenharmony_ci ctrl->host_key->len); 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci goto out_free_response; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { 30662306a36Sopenharmony_ci challenge = kmalloc(shash_len, GFP_KERNEL); 30762306a36Sopenharmony_ci if (!challenge) { 30862306a36Sopenharmony_ci ret = -ENOMEM; 30962306a36Sopenharmony_ci goto out_free_response; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci ret = nvme_auth_augmented_challenge(ctrl->shash_id, 31262306a36Sopenharmony_ci req->sq->dhchap_skey, 31362306a36Sopenharmony_ci req->sq->dhchap_skey_len, 31462306a36Sopenharmony_ci req->sq->dhchap_c1, 31562306a36Sopenharmony_ci challenge, shash_len); 31662306a36Sopenharmony_ci if (ret) 31762306a36Sopenharmony_ci goto out_free_response; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci pr_debug("ctrl %d qid %d host response seq %u transaction %d\n", 32162306a36Sopenharmony_ci ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, 32262306a36Sopenharmony_ci req->sq->dhchap_tid); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), 32562306a36Sopenharmony_ci GFP_KERNEL); 32662306a36Sopenharmony_ci if (!shash) { 32762306a36Sopenharmony_ci ret = -ENOMEM; 32862306a36Sopenharmony_ci goto out_free_response; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci shash->tfm = shash_tfm; 33162306a36Sopenharmony_ci ret = crypto_shash_init(shash); 33262306a36Sopenharmony_ci if (ret) 33362306a36Sopenharmony_ci goto out; 33462306a36Sopenharmony_ci ret = crypto_shash_update(shash, challenge, shash_len); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci goto out; 33762306a36Sopenharmony_ci put_unaligned_le32(req->sq->dhchap_s1, buf); 33862306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 4); 33962306a36Sopenharmony_ci if (ret) 34062306a36Sopenharmony_ci goto out; 34162306a36Sopenharmony_ci put_unaligned_le16(req->sq->dhchap_tid, buf); 34262306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 2); 34362306a36Sopenharmony_ci if (ret) 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci memset(buf, 0, 4); 34662306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 1); 34762306a36Sopenharmony_ci if (ret) 34862306a36Sopenharmony_ci goto out; 34962306a36Sopenharmony_ci ret = crypto_shash_update(shash, "HostHost", 8); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci goto out; 35262306a36Sopenharmony_ci ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); 35362306a36Sopenharmony_ci if (ret) 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 1); 35662306a36Sopenharmony_ci if (ret) 35762306a36Sopenharmony_ci goto out; 35862306a36Sopenharmony_ci ret = crypto_shash_update(shash, ctrl->subsysnqn, 35962306a36Sopenharmony_ci strlen(ctrl->subsysnqn)); 36062306a36Sopenharmony_ci if (ret) 36162306a36Sopenharmony_ci goto out; 36262306a36Sopenharmony_ci ret = crypto_shash_final(shash, response); 36362306a36Sopenharmony_ciout: 36462306a36Sopenharmony_ci if (challenge != req->sq->dhchap_c1) 36562306a36Sopenharmony_ci kfree(challenge); 36662306a36Sopenharmony_ci kfree(shash); 36762306a36Sopenharmony_ciout_free_response: 36862306a36Sopenharmony_ci kfree_sensitive(host_response); 36962306a36Sopenharmony_ciout_free_tfm: 37062306a36Sopenharmony_ci crypto_free_shash(shash_tfm); 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciint nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, 37562306a36Sopenharmony_ci unsigned int shash_len) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct crypto_shash *shash_tfm; 37862306a36Sopenharmony_ci struct shash_desc *shash; 37962306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 38062306a36Sopenharmony_ci const char *hash_name; 38162306a36Sopenharmony_ci u8 *challenge = req->sq->dhchap_c2, *ctrl_response; 38262306a36Sopenharmony_ci u8 buf[4]; 38362306a36Sopenharmony_ci int ret; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci hash_name = nvme_auth_hmac_name(ctrl->shash_id); 38662306a36Sopenharmony_ci if (!hash_name) { 38762306a36Sopenharmony_ci pr_warn("Hash ID %d invalid\n", ctrl->shash_id); 38862306a36Sopenharmony_ci return -EINVAL; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci shash_tfm = crypto_alloc_shash(hash_name, 0, 0); 39262306a36Sopenharmony_ci if (IS_ERR(shash_tfm)) { 39362306a36Sopenharmony_ci pr_err("failed to allocate shash %s\n", hash_name); 39462306a36Sopenharmony_ci return PTR_ERR(shash_tfm); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (shash_len != crypto_shash_digestsize(shash_tfm)) { 39862306a36Sopenharmony_ci pr_debug("%s: hash len mismatch (len %d digest %d)\n", 39962306a36Sopenharmony_ci __func__, shash_len, 40062306a36Sopenharmony_ci crypto_shash_digestsize(shash_tfm)); 40162306a36Sopenharmony_ci ret = -EINVAL; 40262306a36Sopenharmony_ci goto out_free_tfm; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci ctrl_response = nvme_auth_transform_key(ctrl->ctrl_key, 40662306a36Sopenharmony_ci ctrl->subsysnqn); 40762306a36Sopenharmony_ci if (IS_ERR(ctrl_response)) { 40862306a36Sopenharmony_ci ret = PTR_ERR(ctrl_response); 40962306a36Sopenharmony_ci goto out_free_tfm; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci ret = crypto_shash_setkey(shash_tfm, ctrl_response, 41362306a36Sopenharmony_ci ctrl->ctrl_key->len); 41462306a36Sopenharmony_ci if (ret) 41562306a36Sopenharmony_ci goto out_free_response; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { 41862306a36Sopenharmony_ci challenge = kmalloc(shash_len, GFP_KERNEL); 41962306a36Sopenharmony_ci if (!challenge) { 42062306a36Sopenharmony_ci ret = -ENOMEM; 42162306a36Sopenharmony_ci goto out_free_response; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci ret = nvme_auth_augmented_challenge(ctrl->shash_id, 42462306a36Sopenharmony_ci req->sq->dhchap_skey, 42562306a36Sopenharmony_ci req->sq->dhchap_skey_len, 42662306a36Sopenharmony_ci req->sq->dhchap_c2, 42762306a36Sopenharmony_ci challenge, shash_len); 42862306a36Sopenharmony_ci if (ret) 42962306a36Sopenharmony_ci goto out_free_response; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), 43362306a36Sopenharmony_ci GFP_KERNEL); 43462306a36Sopenharmony_ci if (!shash) { 43562306a36Sopenharmony_ci ret = -ENOMEM; 43662306a36Sopenharmony_ci goto out_free_response; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci shash->tfm = shash_tfm; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci ret = crypto_shash_init(shash); 44162306a36Sopenharmony_ci if (ret) 44262306a36Sopenharmony_ci goto out; 44362306a36Sopenharmony_ci ret = crypto_shash_update(shash, challenge, shash_len); 44462306a36Sopenharmony_ci if (ret) 44562306a36Sopenharmony_ci goto out; 44662306a36Sopenharmony_ci put_unaligned_le32(req->sq->dhchap_s2, buf); 44762306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 4); 44862306a36Sopenharmony_ci if (ret) 44962306a36Sopenharmony_ci goto out; 45062306a36Sopenharmony_ci put_unaligned_le16(req->sq->dhchap_tid, buf); 45162306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 2); 45262306a36Sopenharmony_ci if (ret) 45362306a36Sopenharmony_ci goto out; 45462306a36Sopenharmony_ci memset(buf, 0, 4); 45562306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 1); 45662306a36Sopenharmony_ci if (ret) 45762306a36Sopenharmony_ci goto out; 45862306a36Sopenharmony_ci ret = crypto_shash_update(shash, "Controller", 10); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci goto out; 46162306a36Sopenharmony_ci ret = crypto_shash_update(shash, ctrl->subsysnqn, 46262306a36Sopenharmony_ci strlen(ctrl->subsysnqn)); 46362306a36Sopenharmony_ci if (ret) 46462306a36Sopenharmony_ci goto out; 46562306a36Sopenharmony_ci ret = crypto_shash_update(shash, buf, 1); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci goto out; 46862306a36Sopenharmony_ci ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); 46962306a36Sopenharmony_ci if (ret) 47062306a36Sopenharmony_ci goto out; 47162306a36Sopenharmony_ci ret = crypto_shash_final(shash, response); 47262306a36Sopenharmony_ciout: 47362306a36Sopenharmony_ci if (challenge != req->sq->dhchap_c2) 47462306a36Sopenharmony_ci kfree(challenge); 47562306a36Sopenharmony_ci kfree(shash); 47662306a36Sopenharmony_ciout_free_response: 47762306a36Sopenharmony_ci kfree_sensitive(ctrl_response); 47862306a36Sopenharmony_ciout_free_tfm: 47962306a36Sopenharmony_ci crypto_free_shash(shash_tfm); 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciint nvmet_auth_ctrl_exponential(struct nvmet_req *req, 48462306a36Sopenharmony_ci u8 *buf, int buf_size) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 48762306a36Sopenharmony_ci int ret = 0; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (!ctrl->dh_key) { 49062306a36Sopenharmony_ci pr_warn("ctrl %d no DH public key!\n", ctrl->cntlid); 49162306a36Sopenharmony_ci return -ENOKEY; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci if (buf_size != ctrl->dh_keysize) { 49462306a36Sopenharmony_ci pr_warn("ctrl %d DH public key size mismatch, need %zu is %d\n", 49562306a36Sopenharmony_ci ctrl->cntlid, ctrl->dh_keysize, buf_size); 49662306a36Sopenharmony_ci ret = -EINVAL; 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci memcpy(buf, ctrl->dh_key, buf_size); 49962306a36Sopenharmony_ci pr_debug("%s: ctrl %d public key %*ph\n", __func__, 50062306a36Sopenharmony_ci ctrl->cntlid, (int)buf_size, buf); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return ret; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciint nvmet_auth_ctrl_sesskey(struct nvmet_req *req, 50762306a36Sopenharmony_ci u8 *pkey, int pkey_size) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct nvmet_ctrl *ctrl = req->sq->ctrl; 51062306a36Sopenharmony_ci int ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci req->sq->dhchap_skey_len = ctrl->dh_keysize; 51362306a36Sopenharmony_ci req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL); 51462306a36Sopenharmony_ci if (!req->sq->dhchap_skey) 51562306a36Sopenharmony_ci return -ENOMEM; 51662306a36Sopenharmony_ci ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm, 51762306a36Sopenharmony_ci pkey, pkey_size, 51862306a36Sopenharmony_ci req->sq->dhchap_skey, 51962306a36Sopenharmony_ci req->sq->dhchap_skey_len); 52062306a36Sopenharmony_ci if (ret) 52162306a36Sopenharmony_ci pr_debug("failed to compute shared secret, err %d\n", ret); 52262306a36Sopenharmony_ci else 52362306a36Sopenharmony_ci pr_debug("%s: shared secret %*ph\n", __func__, 52462306a36Sopenharmony_ci (int)req->sq->dhchap_skey_len, 52562306a36Sopenharmony_ci req->sq->dhchap_skey); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return ret; 52862306a36Sopenharmony_ci} 529