162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020 Hannes Reinecke, SUSE Linux 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/crc32.h> 862306a36Sopenharmony_ci#include <linux/base64.h> 962306a36Sopenharmony_ci#include <linux/prandom.h> 1062306a36Sopenharmony_ci#include <linux/scatterlist.h> 1162306a36Sopenharmony_ci#include <asm/unaligned.h> 1262306a36Sopenharmony_ci#include <crypto/hash.h> 1362306a36Sopenharmony_ci#include <crypto/dh.h> 1462306a36Sopenharmony_ci#include <linux/nvme.h> 1562306a36Sopenharmony_ci#include <linux/nvme-auth.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic u32 nvme_dhchap_seqnum; 1862306a36Sopenharmony_cistatic DEFINE_MUTEX(nvme_dhchap_mutex); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciu32 nvme_auth_get_seqnum(void) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci u32 seqnum; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci mutex_lock(&nvme_dhchap_mutex); 2562306a36Sopenharmony_ci if (!nvme_dhchap_seqnum) 2662306a36Sopenharmony_ci nvme_dhchap_seqnum = get_random_u32(); 2762306a36Sopenharmony_ci else { 2862306a36Sopenharmony_ci nvme_dhchap_seqnum++; 2962306a36Sopenharmony_ci if (!nvme_dhchap_seqnum) 3062306a36Sopenharmony_ci nvme_dhchap_seqnum++; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci seqnum = nvme_dhchap_seqnum; 3362306a36Sopenharmony_ci mutex_unlock(&nvme_dhchap_mutex); 3462306a36Sopenharmony_ci return seqnum; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_get_seqnum); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic struct nvme_auth_dhgroup_map { 3962306a36Sopenharmony_ci const char name[16]; 4062306a36Sopenharmony_ci const char kpp[16]; 4162306a36Sopenharmony_ci} dhgroup_map[] = { 4262306a36Sopenharmony_ci [NVME_AUTH_DHGROUP_NULL] = { 4362306a36Sopenharmony_ci .name = "null", .kpp = "null" }, 4462306a36Sopenharmony_ci [NVME_AUTH_DHGROUP_2048] = { 4562306a36Sopenharmony_ci .name = "ffdhe2048", .kpp = "ffdhe2048(dh)" }, 4662306a36Sopenharmony_ci [NVME_AUTH_DHGROUP_3072] = { 4762306a36Sopenharmony_ci .name = "ffdhe3072", .kpp = "ffdhe3072(dh)" }, 4862306a36Sopenharmony_ci [NVME_AUTH_DHGROUP_4096] = { 4962306a36Sopenharmony_ci .name = "ffdhe4096", .kpp = "ffdhe4096(dh)" }, 5062306a36Sopenharmony_ci [NVME_AUTH_DHGROUP_6144] = { 5162306a36Sopenharmony_ci .name = "ffdhe6144", .kpp = "ffdhe6144(dh)" }, 5262306a36Sopenharmony_ci [NVME_AUTH_DHGROUP_8192] = { 5362306a36Sopenharmony_ci .name = "ffdhe8192", .kpp = "ffdhe8192(dh)" }, 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciconst char *nvme_auth_dhgroup_name(u8 dhgroup_id) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci if (dhgroup_id >= ARRAY_SIZE(dhgroup_map)) 5962306a36Sopenharmony_ci return NULL; 6062306a36Sopenharmony_ci return dhgroup_map[dhgroup_id].name; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciconst char *nvme_auth_dhgroup_kpp(u8 dhgroup_id) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (dhgroup_id >= ARRAY_SIZE(dhgroup_map)) 6762306a36Sopenharmony_ci return NULL; 6862306a36Sopenharmony_ci return dhgroup_map[dhgroup_id].kpp; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciu8 nvme_auth_dhgroup_id(const char *dhgroup_name) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int i; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (!dhgroup_name || !strlen(dhgroup_name)) 7762306a36Sopenharmony_ci return NVME_AUTH_DHGROUP_INVALID; 7862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) { 7962306a36Sopenharmony_ci if (!strlen(dhgroup_map[i].name)) 8062306a36Sopenharmony_ci continue; 8162306a36Sopenharmony_ci if (!strncmp(dhgroup_map[i].name, dhgroup_name, 8262306a36Sopenharmony_ci strlen(dhgroup_map[i].name))) 8362306a36Sopenharmony_ci return i; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci return NVME_AUTH_DHGROUP_INVALID; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct nvme_dhchap_hash_map { 9062306a36Sopenharmony_ci int len; 9162306a36Sopenharmony_ci const char hmac[15]; 9262306a36Sopenharmony_ci const char digest[8]; 9362306a36Sopenharmony_ci} hash_map[] = { 9462306a36Sopenharmony_ci [NVME_AUTH_HASH_SHA256] = { 9562306a36Sopenharmony_ci .len = 32, 9662306a36Sopenharmony_ci .hmac = "hmac(sha256)", 9762306a36Sopenharmony_ci .digest = "sha256", 9862306a36Sopenharmony_ci }, 9962306a36Sopenharmony_ci [NVME_AUTH_HASH_SHA384] = { 10062306a36Sopenharmony_ci .len = 48, 10162306a36Sopenharmony_ci .hmac = "hmac(sha384)", 10262306a36Sopenharmony_ci .digest = "sha384", 10362306a36Sopenharmony_ci }, 10462306a36Sopenharmony_ci [NVME_AUTH_HASH_SHA512] = { 10562306a36Sopenharmony_ci .len = 64, 10662306a36Sopenharmony_ci .hmac = "hmac(sha512)", 10762306a36Sopenharmony_ci .digest = "sha512", 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciconst char *nvme_auth_hmac_name(u8 hmac_id) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci if (hmac_id >= ARRAY_SIZE(hash_map)) 11462306a36Sopenharmony_ci return NULL; 11562306a36Sopenharmony_ci return hash_map[hmac_id].hmac; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_hmac_name); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ciconst char *nvme_auth_digest_name(u8 hmac_id) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci if (hmac_id >= ARRAY_SIZE(hash_map)) 12262306a36Sopenharmony_ci return NULL; 12362306a36Sopenharmony_ci return hash_map[hmac_id].digest; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_digest_name); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciu8 nvme_auth_hmac_id(const char *hmac_name) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int i; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!hmac_name || !strlen(hmac_name)) 13262306a36Sopenharmony_ci return NVME_AUTH_HASH_INVALID; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hash_map); i++) { 13562306a36Sopenharmony_ci if (!strlen(hash_map[i].hmac)) 13662306a36Sopenharmony_ci continue; 13762306a36Sopenharmony_ci if (!strncmp(hash_map[i].hmac, hmac_name, 13862306a36Sopenharmony_ci strlen(hash_map[i].hmac))) 13962306a36Sopenharmony_ci return i; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return NVME_AUTH_HASH_INVALID; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_hmac_id); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cisize_t nvme_auth_hmac_hash_len(u8 hmac_id) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (hmac_id >= ARRAY_SIZE(hash_map)) 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci return hash_map[hmac_id].len; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_hmac_hash_len); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistruct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, 15462306a36Sopenharmony_ci u8 key_hash) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct nvme_dhchap_key *key; 15762306a36Sopenharmony_ci unsigned char *p; 15862306a36Sopenharmony_ci u32 crc; 15962306a36Sopenharmony_ci int ret, key_len; 16062306a36Sopenharmony_ci size_t allocated_len = strlen(secret); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Secret might be affixed with a ':' */ 16362306a36Sopenharmony_ci p = strrchr(secret, ':'); 16462306a36Sopenharmony_ci if (p) 16562306a36Sopenharmony_ci allocated_len = p - secret; 16662306a36Sopenharmony_ci key = kzalloc(sizeof(*key), GFP_KERNEL); 16762306a36Sopenharmony_ci if (!key) 16862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 16962306a36Sopenharmony_ci key->key = kzalloc(allocated_len, GFP_KERNEL); 17062306a36Sopenharmony_ci if (!key->key) { 17162306a36Sopenharmony_ci ret = -ENOMEM; 17262306a36Sopenharmony_ci goto out_free_key; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci key_len = base64_decode(secret, allocated_len, key->key); 17662306a36Sopenharmony_ci if (key_len < 0) { 17762306a36Sopenharmony_ci pr_debug("base64 key decoding error %d\n", 17862306a36Sopenharmony_ci key_len); 17962306a36Sopenharmony_ci ret = key_len; 18062306a36Sopenharmony_ci goto out_free_secret; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (key_len != 36 && key_len != 52 && 18462306a36Sopenharmony_ci key_len != 68) { 18562306a36Sopenharmony_ci pr_err("Invalid key len %d\n", key_len); 18662306a36Sopenharmony_ci ret = -EINVAL; 18762306a36Sopenharmony_ci goto out_free_secret; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (key_hash > 0 && 19162306a36Sopenharmony_ci (key_len - 4) != nvme_auth_hmac_hash_len(key_hash)) { 19262306a36Sopenharmony_ci pr_err("Mismatched key len %d for %s\n", key_len, 19362306a36Sopenharmony_ci nvme_auth_hmac_name(key_hash)); 19462306a36Sopenharmony_ci ret = -EINVAL; 19562306a36Sopenharmony_ci goto out_free_secret; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* The last four bytes is the CRC in little-endian format */ 19962306a36Sopenharmony_ci key_len -= 4; 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * The linux implementation doesn't do pre- and post-increments, 20262306a36Sopenharmony_ci * so we have to do it manually. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci crc = ~crc32(~0, key->key, key_len); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (get_unaligned_le32(key->key + key_len) != crc) { 20762306a36Sopenharmony_ci pr_err("key crc mismatch (key %08x, crc %08x)\n", 20862306a36Sopenharmony_ci get_unaligned_le32(key->key + key_len), crc); 20962306a36Sopenharmony_ci ret = -EKEYREJECTED; 21062306a36Sopenharmony_ci goto out_free_secret; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci key->len = key_len; 21362306a36Sopenharmony_ci key->hash = key_hash; 21462306a36Sopenharmony_ci return key; 21562306a36Sopenharmony_ciout_free_secret: 21662306a36Sopenharmony_ci kfree_sensitive(key->key); 21762306a36Sopenharmony_ciout_free_key: 21862306a36Sopenharmony_ci kfree(key); 21962306a36Sopenharmony_ci return ERR_PTR(ret); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_extract_key); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_civoid nvme_auth_free_key(struct nvme_dhchap_key *key) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci if (!key) 22662306a36Sopenharmony_ci return; 22762306a36Sopenharmony_ci kfree_sensitive(key->key); 22862306a36Sopenharmony_ci kfree(key); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_free_key); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciu8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci const char *hmac_name; 23562306a36Sopenharmony_ci struct crypto_shash *key_tfm; 23662306a36Sopenharmony_ci struct shash_desc *shash; 23762306a36Sopenharmony_ci u8 *transformed_key; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (!key || !key->key) { 24162306a36Sopenharmony_ci pr_warn("No key specified\n"); 24262306a36Sopenharmony_ci return ERR_PTR(-ENOKEY); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci if (key->hash == 0) { 24562306a36Sopenharmony_ci transformed_key = kmemdup(key->key, key->len, GFP_KERNEL); 24662306a36Sopenharmony_ci return transformed_key ? transformed_key : ERR_PTR(-ENOMEM); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci hmac_name = nvme_auth_hmac_name(key->hash); 24962306a36Sopenharmony_ci if (!hmac_name) { 25062306a36Sopenharmony_ci pr_warn("Invalid key hash id %d\n", key->hash); 25162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci key_tfm = crypto_alloc_shash(hmac_name, 0, 0); 25562306a36Sopenharmony_ci if (IS_ERR(key_tfm)) 25662306a36Sopenharmony_ci return (u8 *)key_tfm; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci shash = kmalloc(sizeof(struct shash_desc) + 25962306a36Sopenharmony_ci crypto_shash_descsize(key_tfm), 26062306a36Sopenharmony_ci GFP_KERNEL); 26162306a36Sopenharmony_ci if (!shash) { 26262306a36Sopenharmony_ci ret = -ENOMEM; 26362306a36Sopenharmony_ci goto out_free_key; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL); 26762306a36Sopenharmony_ci if (!transformed_key) { 26862306a36Sopenharmony_ci ret = -ENOMEM; 26962306a36Sopenharmony_ci goto out_free_shash; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci shash->tfm = key_tfm; 27362306a36Sopenharmony_ci ret = crypto_shash_setkey(key_tfm, key->key, key->len); 27462306a36Sopenharmony_ci if (ret < 0) 27562306a36Sopenharmony_ci goto out_free_transformed_key; 27662306a36Sopenharmony_ci ret = crypto_shash_init(shash); 27762306a36Sopenharmony_ci if (ret < 0) 27862306a36Sopenharmony_ci goto out_free_transformed_key; 27962306a36Sopenharmony_ci ret = crypto_shash_update(shash, nqn, strlen(nqn)); 28062306a36Sopenharmony_ci if (ret < 0) 28162306a36Sopenharmony_ci goto out_free_transformed_key; 28262306a36Sopenharmony_ci ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17); 28362306a36Sopenharmony_ci if (ret < 0) 28462306a36Sopenharmony_ci goto out_free_transformed_key; 28562306a36Sopenharmony_ci ret = crypto_shash_final(shash, transformed_key); 28662306a36Sopenharmony_ci if (ret < 0) 28762306a36Sopenharmony_ci goto out_free_transformed_key; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci kfree(shash); 29062306a36Sopenharmony_ci crypto_free_shash(key_tfm); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return transformed_key; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciout_free_transformed_key: 29562306a36Sopenharmony_ci kfree_sensitive(transformed_key); 29662306a36Sopenharmony_ciout_free_shash: 29762306a36Sopenharmony_ci kfree(shash); 29862306a36Sopenharmony_ciout_free_key: 29962306a36Sopenharmony_ci crypto_free_shash(key_tfm); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return ERR_PTR(ret); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_transform_key); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci const char *digest_name; 30862306a36Sopenharmony_ci struct crypto_shash *tfm; 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci digest_name = nvme_auth_digest_name(hmac_id); 31262306a36Sopenharmony_ci if (!digest_name) { 31362306a36Sopenharmony_ci pr_debug("%s: failed to get digest for %d\n", __func__, 31462306a36Sopenharmony_ci hmac_id); 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci tfm = crypto_alloc_shash(digest_name, 0, 0); 31862306a36Sopenharmony_ci if (IS_ERR(tfm)) 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey); 32262306a36Sopenharmony_ci if (ret < 0) 32362306a36Sopenharmony_ci pr_debug("%s: Failed to hash digest len %zu\n", __func__, 32462306a36Sopenharmony_ci skey_len); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci crypto_free_shash(tfm); 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciint nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, 33162306a36Sopenharmony_ci u8 *challenge, u8 *aug, size_t hlen) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct crypto_shash *tfm; 33462306a36Sopenharmony_ci struct shash_desc *desc; 33562306a36Sopenharmony_ci u8 *hashed_key; 33662306a36Sopenharmony_ci const char *hmac_name; 33762306a36Sopenharmony_ci int ret; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci hashed_key = kmalloc(hlen, GFP_KERNEL); 34062306a36Sopenharmony_ci if (!hashed_key) 34162306a36Sopenharmony_ci return -ENOMEM; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ret = nvme_auth_hash_skey(hmac_id, skey, 34462306a36Sopenharmony_ci skey_len, hashed_key); 34562306a36Sopenharmony_ci if (ret < 0) 34662306a36Sopenharmony_ci goto out_free_key; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci hmac_name = nvme_auth_hmac_name(hmac_id); 34962306a36Sopenharmony_ci if (!hmac_name) { 35062306a36Sopenharmony_ci pr_warn("%s: invalid hash algorithm %d\n", 35162306a36Sopenharmony_ci __func__, hmac_id); 35262306a36Sopenharmony_ci ret = -EINVAL; 35362306a36Sopenharmony_ci goto out_free_key; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci tfm = crypto_alloc_shash(hmac_name, 0, 0); 35762306a36Sopenharmony_ci if (IS_ERR(tfm)) { 35862306a36Sopenharmony_ci ret = PTR_ERR(tfm); 35962306a36Sopenharmony_ci goto out_free_key; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm), 36362306a36Sopenharmony_ci GFP_KERNEL); 36462306a36Sopenharmony_ci if (!desc) { 36562306a36Sopenharmony_ci ret = -ENOMEM; 36662306a36Sopenharmony_ci goto out_free_hash; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci desc->tfm = tfm; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ret = crypto_shash_setkey(tfm, hashed_key, hlen); 37162306a36Sopenharmony_ci if (ret) 37262306a36Sopenharmony_ci goto out_free_desc; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci ret = crypto_shash_init(desc); 37562306a36Sopenharmony_ci if (ret) 37662306a36Sopenharmony_ci goto out_free_desc; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = crypto_shash_update(desc, challenge, hlen); 37962306a36Sopenharmony_ci if (ret) 38062306a36Sopenharmony_ci goto out_free_desc; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = crypto_shash_final(desc, aug); 38362306a36Sopenharmony_ciout_free_desc: 38462306a36Sopenharmony_ci kfree_sensitive(desc); 38562306a36Sopenharmony_ciout_free_hash: 38662306a36Sopenharmony_ci crypto_free_shash(tfm); 38762306a36Sopenharmony_ciout_free_key: 38862306a36Sopenharmony_ci kfree_sensitive(hashed_key); 38962306a36Sopenharmony_ci return ret; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ciint nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int ret; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = crypto_kpp_set_secret(dh_tfm, NULL, 0); 39862306a36Sopenharmony_ci if (ret) 39962306a36Sopenharmony_ci pr_debug("failed to set private key, error %d\n", ret); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return ret; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_gen_privkey); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciint nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, 40662306a36Sopenharmony_ci u8 *host_key, size_t host_key_len) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct kpp_request *req; 40962306a36Sopenharmony_ci struct crypto_wait wait; 41062306a36Sopenharmony_ci struct scatterlist dst; 41162306a36Sopenharmony_ci int ret; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci req = kpp_request_alloc(dh_tfm, GFP_KERNEL); 41462306a36Sopenharmony_ci if (!req) 41562306a36Sopenharmony_ci return -ENOMEM; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci crypto_init_wait(&wait); 41862306a36Sopenharmony_ci kpp_request_set_input(req, NULL, 0); 41962306a36Sopenharmony_ci sg_init_one(&dst, host_key, host_key_len); 42062306a36Sopenharmony_ci kpp_request_set_output(req, &dst, host_key_len); 42162306a36Sopenharmony_ci kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 42262306a36Sopenharmony_ci crypto_req_done, &wait); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); 42562306a36Sopenharmony_ci kpp_request_free(req); 42662306a36Sopenharmony_ci return ret; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ciint nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, 43162306a36Sopenharmony_ci u8 *ctrl_key, size_t ctrl_key_len, 43262306a36Sopenharmony_ci u8 *sess_key, size_t sess_key_len) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct kpp_request *req; 43562306a36Sopenharmony_ci struct crypto_wait wait; 43662306a36Sopenharmony_ci struct scatterlist src, dst; 43762306a36Sopenharmony_ci int ret; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci req = kpp_request_alloc(dh_tfm, GFP_KERNEL); 44062306a36Sopenharmony_ci if (!req) 44162306a36Sopenharmony_ci return -ENOMEM; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci crypto_init_wait(&wait); 44462306a36Sopenharmony_ci sg_init_one(&src, ctrl_key, ctrl_key_len); 44562306a36Sopenharmony_ci kpp_request_set_input(req, &src, ctrl_key_len); 44662306a36Sopenharmony_ci sg_init_one(&dst, sess_key, sess_key_len); 44762306a36Sopenharmony_ci kpp_request_set_output(req, &dst, sess_key_len); 44862306a36Sopenharmony_ci kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 44962306a36Sopenharmony_ci crypto_req_done, &wait); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci kpp_request_free(req); 45462306a36Sopenharmony_ci return ret; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ciint nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct nvme_dhchap_key *key; 46162306a36Sopenharmony_ci u8 key_hash; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (!secret) { 46462306a36Sopenharmony_ci *ret_key = NULL; 46562306a36Sopenharmony_ci return 0; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1) 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Pass in the secret without the 'DHHC-1:XX:' prefix */ 47262306a36Sopenharmony_ci key = nvme_auth_extract_key(secret + 10, key_hash); 47362306a36Sopenharmony_ci if (IS_ERR(key)) { 47462306a36Sopenharmony_ci *ret_key = NULL; 47562306a36Sopenharmony_ci return PTR_ERR(key); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci *ret_key = key; 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvme_auth_generate_key); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 484