Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
0004  */
0005 
0006 #include <linux/module.h>
0007 #include <linux/crc32.h>
0008 #include <linux/base64.h>
0009 #include <linux/prandom.h>
0010 #include <linux/scatterlist.h>
0011 #include <asm/unaligned.h>
0012 #include <crypto/hash.h>
0013 #include <crypto/dh.h>
0014 #include <linux/nvme.h>
0015 #include <linux/nvme-auth.h>
0016 
0017 static u32 nvme_dhchap_seqnum;
0018 static DEFINE_MUTEX(nvme_dhchap_mutex);
0019 
0020 u32 nvme_auth_get_seqnum(void)
0021 {
0022     u32 seqnum;
0023 
0024     mutex_lock(&nvme_dhchap_mutex);
0025     if (!nvme_dhchap_seqnum)
0026         nvme_dhchap_seqnum = prandom_u32();
0027     else {
0028         nvme_dhchap_seqnum++;
0029         if (!nvme_dhchap_seqnum)
0030             nvme_dhchap_seqnum++;
0031     }
0032     seqnum = nvme_dhchap_seqnum;
0033     mutex_unlock(&nvme_dhchap_mutex);
0034     return seqnum;
0035 }
0036 EXPORT_SYMBOL_GPL(nvme_auth_get_seqnum);
0037 
0038 static struct nvme_auth_dhgroup_map {
0039     const char name[16];
0040     const char kpp[16];
0041 } dhgroup_map[] = {
0042     [NVME_AUTH_DHGROUP_NULL] = {
0043         .name = "null", .kpp = "null" },
0044     [NVME_AUTH_DHGROUP_2048] = {
0045         .name = "ffdhe2048", .kpp = "ffdhe2048(dh)" },
0046     [NVME_AUTH_DHGROUP_3072] = {
0047         .name = "ffdhe3072", .kpp = "ffdhe3072(dh)" },
0048     [NVME_AUTH_DHGROUP_4096] = {
0049         .name = "ffdhe4096", .kpp = "ffdhe4096(dh)" },
0050     [NVME_AUTH_DHGROUP_6144] = {
0051         .name = "ffdhe6144", .kpp = "ffdhe6144(dh)" },
0052     [NVME_AUTH_DHGROUP_8192] = {
0053         .name = "ffdhe8192", .kpp = "ffdhe8192(dh)" },
0054 };
0055 
0056 const char *nvme_auth_dhgroup_name(u8 dhgroup_id)
0057 {
0058     if (dhgroup_id >= ARRAY_SIZE(dhgroup_map))
0059         return NULL;
0060     return dhgroup_map[dhgroup_id].name;
0061 }
0062 EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
0063 
0064 const char *nvme_auth_dhgroup_kpp(u8 dhgroup_id)
0065 {
0066     if (dhgroup_id >= ARRAY_SIZE(dhgroup_map))
0067         return NULL;
0068     return dhgroup_map[dhgroup_id].kpp;
0069 }
0070 EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
0071 
0072 u8 nvme_auth_dhgroup_id(const char *dhgroup_name)
0073 {
0074     int i;
0075 
0076     if (!dhgroup_name || !strlen(dhgroup_name))
0077         return NVME_AUTH_DHGROUP_INVALID;
0078     for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
0079         if (!strlen(dhgroup_map[i].name))
0080             continue;
0081         if (!strncmp(dhgroup_map[i].name, dhgroup_name,
0082                  strlen(dhgroup_map[i].name)))
0083             return i;
0084     }
0085     return NVME_AUTH_DHGROUP_INVALID;
0086 }
0087 EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
0088 
0089 static struct nvme_dhchap_hash_map {
0090     int len;
0091     const char hmac[15];
0092     const char digest[8];
0093 } hash_map[] = {
0094     [NVME_AUTH_HASH_SHA256] = {
0095         .len = 32,
0096         .hmac = "hmac(sha256)",
0097         .digest = "sha256",
0098     },
0099     [NVME_AUTH_HASH_SHA384] = {
0100         .len = 48,
0101         .hmac = "hmac(sha384)",
0102         .digest = "sha384",
0103     },
0104     [NVME_AUTH_HASH_SHA512] = {
0105         .len = 64,
0106         .hmac = "hmac(sha512)",
0107         .digest = "sha512",
0108     },
0109 };
0110 
0111 const char *nvme_auth_hmac_name(u8 hmac_id)
0112 {
0113     if (hmac_id >= ARRAY_SIZE(hash_map))
0114         return NULL;
0115     return hash_map[hmac_id].hmac;
0116 }
0117 EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
0118 
0119 const char *nvme_auth_digest_name(u8 hmac_id)
0120 {
0121     if (hmac_id >= ARRAY_SIZE(hash_map))
0122         return NULL;
0123     return hash_map[hmac_id].digest;
0124 }
0125 EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
0126 
0127 u8 nvme_auth_hmac_id(const char *hmac_name)
0128 {
0129     int i;
0130 
0131     if (!hmac_name || !strlen(hmac_name))
0132         return NVME_AUTH_HASH_INVALID;
0133 
0134     for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
0135         if (!strlen(hash_map[i].hmac))
0136             continue;
0137         if (!strncmp(hash_map[i].hmac, hmac_name,
0138                  strlen(hash_map[i].hmac)))
0139             return i;
0140     }
0141     return NVME_AUTH_HASH_INVALID;
0142 }
0143 EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
0144 
0145 size_t nvme_auth_hmac_hash_len(u8 hmac_id)
0146 {
0147     if (hmac_id >= ARRAY_SIZE(hash_map))
0148         return 0;
0149     return hash_map[hmac_id].len;
0150 }
0151 EXPORT_SYMBOL_GPL(nvme_auth_hmac_hash_len);
0152 
0153 struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret,
0154                           u8 key_hash)
0155 {
0156     struct nvme_dhchap_key *key;
0157     unsigned char *p;
0158     u32 crc;
0159     int ret, key_len;
0160     size_t allocated_len = strlen(secret);
0161 
0162     /* Secret might be affixed with a ':' */
0163     p = strrchr(secret, ':');
0164     if (p)
0165         allocated_len = p - secret;
0166     key = kzalloc(sizeof(*key), GFP_KERNEL);
0167     if (!key)
0168         return ERR_PTR(-ENOMEM);
0169     key->key = kzalloc(allocated_len, GFP_KERNEL);
0170     if (!key->key) {
0171         ret = -ENOMEM;
0172         goto out_free_key;
0173     }
0174 
0175     key_len = base64_decode(secret, allocated_len, key->key);
0176     if (key_len < 0) {
0177         pr_debug("base64 key decoding error %d\n",
0178              key_len);
0179         ret = key_len;
0180         goto out_free_secret;
0181     }
0182 
0183     if (key_len != 36 && key_len != 52 &&
0184         key_len != 68) {
0185         pr_err("Invalid key len %d\n", key_len);
0186         ret = -EINVAL;
0187         goto out_free_secret;
0188     }
0189 
0190     if (key_hash > 0 &&
0191         (key_len - 4) != nvme_auth_hmac_hash_len(key_hash)) {
0192         pr_err("Mismatched key len %d for %s\n", key_len,
0193                nvme_auth_hmac_name(key_hash));
0194         ret = -EINVAL;
0195         goto out_free_secret;
0196     }
0197 
0198     /* The last four bytes is the CRC in little-endian format */
0199     key_len -= 4;
0200     /*
0201      * The linux implementation doesn't do pre- and post-increments,
0202      * so we have to do it manually.
0203      */
0204     crc = ~crc32(~0, key->key, key_len);
0205 
0206     if (get_unaligned_le32(key->key + key_len) != crc) {
0207         pr_err("key crc mismatch (key %08x, crc %08x)\n",
0208                get_unaligned_le32(key->key + key_len), crc);
0209         ret = -EKEYREJECTED;
0210         goto out_free_secret;
0211     }
0212     key->len = key_len;
0213     key->hash = key_hash;
0214     return key;
0215 out_free_secret:
0216     kfree_sensitive(key->key);
0217 out_free_key:
0218     kfree(key);
0219     return ERR_PTR(ret);
0220 }
0221 EXPORT_SYMBOL_GPL(nvme_auth_extract_key);
0222 
0223 void nvme_auth_free_key(struct nvme_dhchap_key *key)
0224 {
0225     if (!key)
0226         return;
0227     kfree_sensitive(key->key);
0228     kfree(key);
0229 }
0230 EXPORT_SYMBOL_GPL(nvme_auth_free_key);
0231 
0232 u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn)
0233 {
0234     const char *hmac_name;
0235     struct crypto_shash *key_tfm;
0236     struct shash_desc *shash;
0237     u8 *transformed_key;
0238     int ret;
0239 
0240     if (!key || !key->key) {
0241         pr_warn("No key specified\n");
0242         return ERR_PTR(-ENOKEY);
0243     }
0244     if (key->hash == 0) {
0245         transformed_key = kmemdup(key->key, key->len, GFP_KERNEL);
0246         return transformed_key ? transformed_key : ERR_PTR(-ENOMEM);
0247     }
0248     hmac_name = nvme_auth_hmac_name(key->hash);
0249     if (!hmac_name) {
0250         pr_warn("Invalid key hash id %d\n", key->hash);
0251         return ERR_PTR(-EINVAL);
0252     }
0253 
0254     key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
0255     if (IS_ERR(key_tfm))
0256         return (u8 *)key_tfm;
0257 
0258     shash = kmalloc(sizeof(struct shash_desc) +
0259             crypto_shash_descsize(key_tfm),
0260             GFP_KERNEL);
0261     if (!shash) {
0262         ret = -ENOMEM;
0263         goto out_free_key;
0264     }
0265 
0266     transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL);
0267     if (!transformed_key) {
0268         ret = -ENOMEM;
0269         goto out_free_shash;
0270     }
0271 
0272     shash->tfm = key_tfm;
0273     ret = crypto_shash_setkey(key_tfm, key->key, key->len);
0274     if (ret < 0)
0275         goto out_free_transformed_key;
0276     ret = crypto_shash_init(shash);
0277     if (ret < 0)
0278         goto out_free_transformed_key;
0279     ret = crypto_shash_update(shash, nqn, strlen(nqn));
0280     if (ret < 0)
0281         goto out_free_transformed_key;
0282     ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
0283     if (ret < 0)
0284         goto out_free_transformed_key;
0285     ret = crypto_shash_final(shash, transformed_key);
0286     if (ret < 0)
0287         goto out_free_transformed_key;
0288 
0289     kfree(shash);
0290     crypto_free_shash(key_tfm);
0291 
0292     return transformed_key;
0293 
0294 out_free_transformed_key:
0295     kfree_sensitive(transformed_key);
0296 out_free_shash:
0297     kfree(shash);
0298 out_free_key:
0299     crypto_free_shash(key_tfm);
0300 
0301     return ERR_PTR(ret);
0302 }
0303 EXPORT_SYMBOL_GPL(nvme_auth_transform_key);
0304 
0305 static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey)
0306 {
0307     const char *digest_name;
0308     struct crypto_shash *tfm;
0309     int ret;
0310 
0311     digest_name = nvme_auth_digest_name(hmac_id);
0312     if (!digest_name) {
0313         pr_debug("%s: failed to get digest for %d\n", __func__,
0314              hmac_id);
0315         return -EINVAL;
0316     }
0317     tfm = crypto_alloc_shash(digest_name, 0, 0);
0318     if (IS_ERR(tfm))
0319         return -ENOMEM;
0320 
0321     ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey);
0322     if (ret < 0)
0323         pr_debug("%s: Failed to hash digest len %zu\n", __func__,
0324              skey_len);
0325 
0326     crypto_free_shash(tfm);
0327     return ret;
0328 }
0329 
0330 int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len,
0331         u8 *challenge, u8 *aug, size_t hlen)
0332 {
0333     struct crypto_shash *tfm;
0334     struct shash_desc *desc;
0335     u8 *hashed_key;
0336     const char *hmac_name;
0337     int ret;
0338 
0339     hashed_key = kmalloc(hlen, GFP_KERNEL);
0340     if (!hashed_key)
0341         return -ENOMEM;
0342 
0343     ret = nvme_auth_hash_skey(hmac_id, skey,
0344                   skey_len, hashed_key);
0345     if (ret < 0)
0346         goto out_free_key;
0347 
0348     hmac_name = nvme_auth_hmac_name(hmac_id);
0349     if (!hmac_name) {
0350         pr_warn("%s: invalid hash algorithm %d\n",
0351             __func__, hmac_id);
0352         ret = -EINVAL;
0353         goto out_free_key;
0354     }
0355 
0356     tfm = crypto_alloc_shash(hmac_name, 0, 0);
0357     if (IS_ERR(tfm)) {
0358         ret = PTR_ERR(tfm);
0359         goto out_free_key;
0360     }
0361 
0362     desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
0363                GFP_KERNEL);
0364     if (!desc) {
0365         ret = -ENOMEM;
0366         goto out_free_hash;
0367     }
0368     desc->tfm = tfm;
0369 
0370     ret = crypto_shash_setkey(tfm, hashed_key, hlen);
0371     if (ret)
0372         goto out_free_desc;
0373 
0374     ret = crypto_shash_init(desc);
0375     if (ret)
0376         goto out_free_desc;
0377 
0378     ret = crypto_shash_update(desc, challenge, hlen);
0379     if (ret)
0380         goto out_free_desc;
0381 
0382     ret = crypto_shash_final(desc, aug);
0383 out_free_desc:
0384     kfree_sensitive(desc);
0385 out_free_hash:
0386     crypto_free_shash(tfm);
0387 out_free_key:
0388     kfree_sensitive(hashed_key);
0389     return ret;
0390 }
0391 EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge);
0392 
0393 int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid)
0394 {
0395     int ret;
0396 
0397     ret = crypto_kpp_set_secret(dh_tfm, NULL, 0);
0398     if (ret)
0399         pr_debug("failed to set private key, error %d\n", ret);
0400 
0401     return ret;
0402 }
0403 EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey);
0404 
0405 int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm,
0406         u8 *host_key, size_t host_key_len)
0407 {
0408     struct kpp_request *req;
0409     struct crypto_wait wait;
0410     struct scatterlist dst;
0411     int ret;
0412 
0413     req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
0414     if (!req)
0415         return -ENOMEM;
0416 
0417     crypto_init_wait(&wait);
0418     kpp_request_set_input(req, NULL, 0);
0419     sg_init_one(&dst, host_key, host_key_len);
0420     kpp_request_set_output(req, &dst, host_key_len);
0421     kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
0422                  crypto_req_done, &wait);
0423 
0424     ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
0425     kpp_request_free(req);
0426     return ret;
0427 }
0428 EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey);
0429 
0430 int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
0431         u8 *ctrl_key, size_t ctrl_key_len,
0432         u8 *sess_key, size_t sess_key_len)
0433 {
0434     struct kpp_request *req;
0435     struct crypto_wait wait;
0436     struct scatterlist src, dst;
0437     int ret;
0438 
0439     req = kpp_request_alloc(dh_tfm, GFP_KERNEL);
0440     if (!req)
0441         return -ENOMEM;
0442 
0443     crypto_init_wait(&wait);
0444     sg_init_one(&src, ctrl_key, ctrl_key_len);
0445     kpp_request_set_input(req, &src, ctrl_key_len);
0446     sg_init_one(&dst, sess_key, sess_key_len);
0447     kpp_request_set_output(req, &dst, sess_key_len);
0448     kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
0449                  crypto_req_done, &wait);
0450 
0451     ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait);
0452 
0453     kpp_request_free(req);
0454     return ret;
0455 }
0456 EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret);
0457 
0458 int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key)
0459 {
0460     struct nvme_dhchap_key *key;
0461     u8 key_hash;
0462 
0463     if (!secret) {
0464         *ret_key = NULL;
0465         return 0;
0466     }
0467 
0468     if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1)
0469         return -EINVAL;
0470 
0471     /* Pass in the secret without the 'DHHC-1:XX:' prefix */
0472     key = nvme_auth_extract_key(secret + 10, key_hash);
0473     if (IS_ERR(key)) {
0474         *ret_key = NULL;
0475         return PTR_ERR(key);
0476     }
0477 
0478     *ret_key = key;
0479     return 0;
0480 }
0481 EXPORT_SYMBOL_GPL(nvme_auth_generate_key);
0482 
0483 MODULE_LICENSE("GPL v2");