LCOV - code coverage report
Current view: top level - lib/nvme - nvme_auth.c (source / functions) Hit Total Coverage
Test: ut_cov_unit.info Lines: 0 757 0.0 %
Date: 2024-12-01 05:22:07 Functions: 0 34 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: BSD-3-Clause
       2             :  * Copyright (c) 2024 Intel Corporation.  All rights reserved.
       3             :  */
       4             : 
       5             : #include "spdk/base64.h"
       6             : #include "spdk/crc32.h"
       7             : #include "spdk/endian.h"
       8             : #include "spdk/log.h"
       9             : #include "spdk/string.h"
      10             : #include "spdk/util.h"
      11             : #include "spdk_internal/nvme.h"
      12             : #include "nvme_internal.h"
      13             : 
      14             : #ifdef SPDK_CONFIG_HAVE_EVP_MAC
      15             : #include <openssl/dh.h>
      16             : #include <openssl/evp.h>
      17             : #include <openssl/param_build.h>
      18             : #include <openssl/rand.h>
      19             : #endif
      20             : 
      21             : struct nvme_auth_digest {
      22             :         uint8_t         id;
      23             :         const char      *name;
      24             :         uint8_t         len;
      25             : };
      26             : 
      27             : struct nvme_auth_dhgroup {
      28             :         uint8_t         id;
      29             :         const char      *name;
      30             : };
      31             : 
      32             : #define NVME_AUTH_DATA_SIZE                     4096
      33             : #define NVME_AUTH_DH_KEY_MAX_SIZE               1024
      34             : #define NVME_AUTH_CHAP_KEY_MAX_SIZE             256
      35             : 
      36             : #define AUTH_DEBUGLOG(q, fmt, ...) \
      37             :         SPDK_DEBUGLOG(nvme_auth, "[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, \
      38             :                       (q)->ctrlr->opts.hostnqn, (q)->id, ## __VA_ARGS__)
      39             : #define AUTH_ERRLOG(q, fmt, ...) \
      40             :         SPDK_ERRLOG("[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, (q)->ctrlr->opts.hostnqn, \
      41             :                     (q)->id, ## __VA_ARGS__)
      42             : #define AUTH_LOGDUMP(msg, buf, len) \
      43             :         SPDK_LOGDUMP(nvme_auth, msg, buf, len)
      44             : 
      45             : static const struct nvme_auth_digest g_digests[] = {
      46             :         { SPDK_NVMF_DHCHAP_HASH_SHA256, "sha256", 32 },
      47             :         { SPDK_NVMF_DHCHAP_HASH_SHA384, "sha384", 48 },
      48             :         { SPDK_NVMF_DHCHAP_HASH_SHA512, "sha512", 64 },
      49             : };
      50             : 
      51             : static const struct nvme_auth_dhgroup g_dhgroups[] = {
      52             :         { SPDK_NVMF_DHCHAP_DHGROUP_NULL, "null" },
      53             :         { SPDK_NVMF_DHCHAP_DHGROUP_2048, "ffdhe2048" },
      54             :         { SPDK_NVMF_DHCHAP_DHGROUP_3072, "ffdhe3072" },
      55             :         { SPDK_NVMF_DHCHAP_DHGROUP_4096, "ffdhe4096" },
      56             :         { SPDK_NVMF_DHCHAP_DHGROUP_6144, "ffdhe6144" },
      57             :         { SPDK_NVMF_DHCHAP_DHGROUP_8192, "ffdhe8192" },
      58             : };
      59             : 
      60             : static const struct nvme_auth_digest *
      61           0 : nvme_auth_get_digest(int id)
      62             : {
      63             :         size_t i;
      64             : 
      65           0 :         for (i = 0; i < SPDK_COUNTOF(g_digests); ++i) {
      66           0 :                 if (g_digests[i].id == id) {
      67           0 :                         return &g_digests[i];
      68             :                 }
      69             :         }
      70             : 
      71           0 :         return NULL;
      72             : }
      73             : 
      74             : int
      75           0 : spdk_nvme_dhchap_get_digest_id(const char *digest)
      76             : {
      77             :         size_t i;
      78             : 
      79           0 :         for (i = 0; i < SPDK_COUNTOF(g_digests); ++i) {
      80           0 :                 if (strcmp(g_digests[i].name, digest) == 0) {
      81           0 :                         return g_digests[i].id;
      82             :                 }
      83             :         }
      84             : 
      85           0 :         return -EINVAL;
      86             : }
      87             : 
      88             : const char *
      89           0 : spdk_nvme_dhchap_get_digest_name(int id)
      90             : {
      91           0 :         const struct nvme_auth_digest *digest = nvme_auth_get_digest(id);
      92             : 
      93           0 :         return digest != NULL ? digest->name : NULL;
      94             : }
      95             : 
      96             : int
      97           0 : spdk_nvme_dhchap_get_dhgroup_id(const char *dhgroup)
      98             : {
      99             :         size_t i;
     100             : 
     101           0 :         for (i = 0; i < SPDK_COUNTOF(g_dhgroups); ++i) {
     102           0 :                 if (strcmp(g_dhgroups[i].name, dhgroup) == 0) {
     103           0 :                         return g_dhgroups[i].id;
     104             :                 }
     105             :         }
     106             : 
     107           0 :         return -EINVAL;
     108             : }
     109             : 
     110             : const char *
     111           0 : spdk_nvme_dhchap_get_dhgroup_name(int id)
     112             : {
     113             :         size_t i;
     114             : 
     115           0 :         for (i = 0; i < SPDK_COUNTOF(g_dhgroups); ++i) {
     116           0 :                 if (g_dhgroups[i].id == id) {
     117           0 :                         return g_dhgroups[i].name;
     118             :                 }
     119             :         }
     120             : 
     121           0 :         return NULL;
     122             : }
     123             : 
     124             : uint8_t
     125           0 : spdk_nvme_dhchap_get_digest_length(int id)
     126             : {
     127           0 :         const struct nvme_auth_digest *digest = nvme_auth_get_digest(id);
     128             : 
     129           0 :         return digest != NULL ? digest->len : 0;
     130             : }
     131             : 
     132             : #ifdef SPDK_CONFIG_HAVE_EVP_MAC
     133             : static bool
     134           0 : nvme_auth_digest_allowed(struct spdk_nvme_qpair *qpair, uint8_t digest)
     135             : {
     136           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     137             : 
     138           0 :         return ctrlr->opts.dhchap_digests & SPDK_BIT(digest);
     139             : }
     140             : 
     141             : static bool
     142           0 : nvme_auth_dhgroup_allowed(struct spdk_nvme_qpair *qpair, uint8_t dhgroup)
     143             : {
     144           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     145             : 
     146           0 :         return ctrlr->opts.dhchap_dhgroups & SPDK_BIT(dhgroup);
     147             : }
     148             : 
     149             : static void
     150           0 : nvme_auth_set_state(struct spdk_nvme_qpair *qpair, enum nvme_qpair_auth_state state)
     151             : {
     152             :         static const char *state_names[] __attribute__((unused)) = {
     153             :                 [NVME_QPAIR_AUTH_STATE_NEGOTIATE] = "negotiate",
     154             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE] = "await-negotiate",
     155             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE] = "await-challenge",
     156             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_REPLY] = "await-reply",
     157             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1] = "await-success1",
     158             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2] = "await-success2",
     159             :                 [NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2] = "await-failure2",
     160             :                 [NVME_QPAIR_AUTH_STATE_DONE] = "done",
     161             :         };
     162             : 
     163           0 :         AUTH_DEBUGLOG(qpair, "auth state: %s\n", state_names[state]);
     164           0 :         qpair->auth.state = state;
     165           0 : }
     166             : 
     167             : static void
     168           0 : nvme_auth_set_failure(struct spdk_nvme_qpair *qpair, int status, bool failure2)
     169             : {
     170           0 :         if (qpair->auth.status == 0) {
     171           0 :                 qpair->auth.status = status;
     172             :         }
     173             : 
     174           0 :         nvme_auth_set_state(qpair, failure2 ?
     175             :                             NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2 :
     176             :                             NVME_QPAIR_AUTH_STATE_DONE);
     177           0 : }
     178             : 
     179             : static void
     180           0 : nvme_auth_print_cpl(struct spdk_nvme_qpair *qpair, const char *msg)
     181             : {
     182           0 :         struct nvme_completion_poll_status *status = qpair->poll_status;
     183             : 
     184           0 :         AUTH_ERRLOG(qpair, "%s failed: sc=%d, sct=%d (timed out: %s)\n", msg, status->cpl.status.sc,
     185             :                     status->cpl.status.sct, status->timed_out ? "true" : "false");
     186           0 : }
     187             : 
     188             : static uint32_t
     189           0 : nvme_auth_get_seqnum(struct spdk_nvme_qpair *qpair)
     190             : {
     191           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     192             :         uint32_t seqnum;
     193             :         int rc;
     194             : 
     195           0 :         nvme_ctrlr_lock(ctrlr);
     196           0 :         if (ctrlr->auth_seqnum == 0) {
     197           0 :                 rc = RAND_bytes((void *)&ctrlr->auth_seqnum, sizeof(ctrlr->auth_seqnum));
     198           0 :                 if (rc != 1) {
     199           0 :                         nvme_ctrlr_unlock(ctrlr);
     200           0 :                         return 0;
     201             :                 }
     202             :         }
     203           0 :         if (++ctrlr->auth_seqnum == 0) {
     204           0 :                 ctrlr->auth_seqnum = 1;
     205             :         }
     206           0 :         seqnum = ctrlr->auth_seqnum;
     207           0 :         nvme_ctrlr_unlock(ctrlr);
     208             : 
     209           0 :         return seqnum;
     210             : }
     211             : 
     212             : static int
     213           0 : nvme_auth_transform_key(struct spdk_key *key, int hash, const char *nqn,
     214             :                         const void *keyin, size_t keylen, void *out, size_t outlen)
     215             : {
     216           0 :         EVP_MAC *hmac = NULL;
     217           0 :         EVP_MAC_CTX *ctx = NULL;
     218           0 :         OSSL_PARAM params[2];
     219             :         int rc;
     220             : 
     221           0 :         switch (hash) {
     222           0 :         case SPDK_NVMF_DHCHAP_HASH_NONE:
     223           0 :                 if (keylen > outlen) {
     224           0 :                         SPDK_ERRLOG("Key buffer too small: %zu < %zu (key=%s)\n", outlen, keylen,
     225             :                                     spdk_key_get_name(key));
     226           0 :                         return -ENOBUFS;
     227             :                 }
     228           0 :                 memcpy(out, keyin, keylen);
     229           0 :                 return keylen;
     230           0 :         case SPDK_NVMF_DHCHAP_HASH_SHA256:
     231             :         case SPDK_NVMF_DHCHAP_HASH_SHA384:
     232             :         case SPDK_NVMF_DHCHAP_HASH_SHA512:
     233           0 :                 break;
     234           0 :         default:
     235           0 :                 SPDK_ERRLOG("Unsupported key hash: 0x%x (key=%s)\n", hash, spdk_key_get_name(key));
     236           0 :                 return -EINVAL;
     237             :         }
     238             : 
     239           0 :         hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
     240           0 :         if (hmac == NULL) {
     241           0 :                 return -EIO;
     242             :         }
     243           0 :         ctx = EVP_MAC_CTX_new(hmac);
     244           0 :         if (ctx == NULL) {
     245           0 :                 rc = -EIO;
     246           0 :                 goto out;
     247             :         }
     248           0 :         params[0] = OSSL_PARAM_construct_utf8_string("digest",
     249           0 :                         (char *)spdk_nvme_dhchap_get_digest_name(hash), 0);
     250           0 :         params[1] = OSSL_PARAM_construct_end();
     251             : 
     252           0 :         if (EVP_MAC_init(ctx, keyin, keylen, params) != 1) {
     253           0 :                 rc = -EIO;
     254           0 :                 goto out;
     255             :         }
     256           0 :         if (EVP_MAC_update(ctx, nqn, strlen(nqn)) != 1) {
     257           0 :                 rc = -EIO;
     258           0 :                 goto out;
     259             :         }
     260           0 :         if (EVP_MAC_update(ctx, "NVMe-over-Fabrics", strlen("NVMe-over-Fabrics")) != 1) {
     261           0 :                 rc = -EIO;
     262           0 :                 goto out;
     263             :         }
     264           0 :         if (EVP_MAC_final(ctx, out, &outlen, outlen) != 1) {
     265           0 :                 rc = -EIO;
     266           0 :                 goto out;
     267             :         }
     268           0 :         rc = (int)outlen;
     269           0 : out:
     270           0 :         EVP_MAC_CTX_free(ctx);
     271           0 :         EVP_MAC_free(hmac);
     272             : 
     273           0 :         return rc;
     274             : }
     275             : 
     276             : static int
     277           0 : nvme_auth_get_key(struct spdk_key *key, const char *nqn, void *buf, size_t buflen)
     278             : {
     279           0 :         char keystr[NVME_AUTH_CHAP_KEY_MAX_SIZE + 1] = {};
     280           0 :         char keyb64[NVME_AUTH_CHAP_KEY_MAX_SIZE] = {};
     281             :         char *tmp, *secret;
     282           0 :         int rc, hash;
     283           0 :         size_t keylen;
     284             : 
     285           0 :         rc = spdk_key_get_key(key, keystr, NVME_AUTH_CHAP_KEY_MAX_SIZE);
     286           0 :         if (rc < 0) {
     287           0 :                 SPDK_ERRLOG("Failed to load key=%s: %s\n", spdk_key_get_name(key),
     288             :                             spdk_strerror(-rc));
     289           0 :                 goto out;
     290             :         }
     291             : 
     292           0 :         rc = sscanf(keystr, "DHHC-1:%02x:", &hash);
     293           0 :         if (rc != 1) {
     294           0 :                 SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
     295           0 :                 rc = -EINVAL;
     296           0 :                 goto out;
     297             : 
     298             :         }
     299             :         /* Start at the first character after second ":" and remove the trailing ":" */
     300           0 :         secret = &keystr[10];
     301           0 :         tmp = strstr(secret, ":");
     302           0 :         if (!tmp) {
     303           0 :                 SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
     304           0 :                 rc = -EINVAL;
     305           0 :                 goto out;
     306             :         }
     307             : 
     308           0 :         *tmp = '\0';
     309           0 :         keylen = sizeof(keyb64);
     310           0 :         rc = spdk_base64_decode(keyb64, &keylen, secret);
     311           0 :         if (rc != 0) {
     312           0 :                 SPDK_ERRLOG("Invalid key format (key=%s)\n", spdk_key_get_name(key));
     313           0 :                 rc = -EINVAL;
     314           0 :                 goto out;
     315             :         }
     316             :         /* Only 32B, 48B, and 64B keys are supported (+ 4B, as they're followed by a crc32) */
     317           0 :         if (keylen != 36 && keylen != 52 && keylen != 68) {
     318           0 :                 SPDK_ERRLOG("Invalid key size=%zu (key=%s)\n", keylen, spdk_key_get_name(key));
     319           0 :                 rc = -EINVAL;
     320           0 :                 goto out;
     321             :         }
     322             : 
     323           0 :         keylen -= 4;
     324           0 :         if (~spdk_crc32_ieee_update(keyb64, keylen, ~0) != from_le32(&keyb64[keylen])) {
     325           0 :                 SPDK_ERRLOG("Invalid key checksum (key=%s)\n", spdk_key_get_name(key));
     326           0 :                 rc = -EINVAL;
     327           0 :                 goto out;
     328             :         }
     329             : 
     330           0 :         rc = nvme_auth_transform_key(key, hash, nqn, keyb64, keylen, buf, buflen);
     331           0 : out:
     332           0 :         spdk_memset_s(keystr, sizeof(keystr), 0, sizeof(keystr));
     333           0 :         spdk_memset_s(keyb64, sizeof(keyb64), 0, sizeof(keyb64));
     334             : 
     335           0 :         return rc;
     336             : }
     337             : 
     338             : static int
     339           0 : nvme_auth_augment_challenge(const void *cval, size_t clen, const void *key, size_t keylen,
     340             :                             void *caval, size_t *calen, enum spdk_nvmf_dhchap_hash hash)
     341             : {
     342           0 :         EVP_MAC *hmac = NULL;
     343           0 :         EVP_MAC_CTX *ctx = NULL;
     344           0 :         EVP_MD *md = NULL;
     345           0 :         OSSL_PARAM params[2];
     346           0 :         uint8_t keydgst[NVME_AUTH_DIGEST_MAX_SIZE];
     347           0 :         unsigned int dgstlen = sizeof(keydgst);
     348           0 :         int rc = 0;
     349             : 
     350             :         /* If there's no key, there's nothing to augment, cval == caval */
     351           0 :         if (key == NULL) {
     352           0 :                 assert(clen <= *calen);
     353           0 :                 memcpy(caval, cval, clen);
     354           0 :                 *calen = clen;
     355           0 :                 return 0;
     356             :         }
     357             : 
     358           0 :         md = EVP_MD_fetch(NULL, spdk_nvme_dhchap_get_digest_name(hash), NULL);
     359           0 :         if (!md) {
     360           0 :                 SPDK_ERRLOG("Failed to fetch digest function: %d\n", hash);
     361           0 :                 return -EINVAL;
     362             :         }
     363           0 :         if (EVP_Digest(key, keylen, keydgst, &dgstlen, md, NULL) != 1) {
     364           0 :                 rc = -EIO;
     365           0 :                 goto out;
     366             :         }
     367             : 
     368           0 :         hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
     369           0 :         if (hmac == NULL) {
     370           0 :                 rc = -EIO;
     371           0 :                 goto out;
     372             :         }
     373           0 :         ctx = EVP_MAC_CTX_new(hmac);
     374           0 :         if (ctx == NULL) {
     375           0 :                 rc = -EIO;
     376           0 :                 goto out;
     377             :         }
     378           0 :         params[0] = OSSL_PARAM_construct_utf8_string("digest",
     379           0 :                         (char *)spdk_nvme_dhchap_get_digest_name(hash), 0);
     380           0 :         params[1] = OSSL_PARAM_construct_end();
     381             : 
     382           0 :         if (EVP_MAC_init(ctx, keydgst, dgstlen, params) != 1) {
     383           0 :                 rc = -EIO;
     384           0 :                 goto out;
     385             :         }
     386           0 :         if (EVP_MAC_update(ctx, cval, clen) != 1) {
     387           0 :                 rc = -EIO;
     388           0 :                 goto out;
     389             :         }
     390           0 :         if (EVP_MAC_final(ctx, caval, calen, *calen) != 1) {
     391           0 :                 rc = -EIO;
     392           0 :                 goto out;
     393             :         }
     394           0 : out:
     395           0 :         EVP_MD_free(md);
     396           0 :         EVP_MAC_CTX_free(ctx);
     397           0 :         EVP_MAC_free(hmac);
     398             : 
     399           0 :         return rc;
     400             : }
     401             : 
     402             : int
     403           0 : spdk_nvme_dhchap_calculate(struct spdk_key *key, enum spdk_nvmf_dhchap_hash hash,
     404             :                            const char *type, uint32_t seq, uint16_t tid, uint8_t scc,
     405             :                            const char *nqn1, const char *nqn2, const void *dhkey, size_t dhlen,
     406             :                            const void *cval, void *rval)
     407             : {
     408             :         EVP_MAC *hmac;
     409             :         EVP_MAC_CTX *ctx;
     410           0 :         OSSL_PARAM params[2];
     411           0 :         uint8_t keybuf[NVME_AUTH_CHAP_KEY_MAX_SIZE], term = 0;
     412           0 :         uint8_t caval[NVME_AUTH_DATA_SIZE];
     413           0 :         size_t hlen, calen = sizeof(caval);
     414             :         int rc, keylen;
     415             : 
     416           0 :         hlen = spdk_nvme_dhchap_get_digest_length(hash);
     417           0 :         rc = nvme_auth_augment_challenge(cval, hlen, dhkey, dhlen, caval, &calen, hash);
     418           0 :         if (rc != 0) {
     419           0 :                 return rc;
     420             :         }
     421             : 
     422           0 :         hmac = EVP_MAC_fetch(NULL, "hmac", NULL);
     423           0 :         if (hmac == NULL) {
     424           0 :                 return -EIO;
     425             :         }
     426             : 
     427           0 :         ctx = EVP_MAC_CTX_new(hmac);
     428           0 :         if (ctx == NULL) {
     429           0 :                 rc = -EIO;
     430           0 :                 goto out;
     431             :         }
     432             : 
     433           0 :         keylen = nvme_auth_get_key(key, nqn1, keybuf, sizeof(keybuf));
     434           0 :         if (keylen < 0) {
     435           0 :                 rc = keylen;
     436           0 :                 goto out;
     437             :         }
     438             : 
     439           0 :         params[0] = OSSL_PARAM_construct_utf8_string("digest",
     440           0 :                         (char *)spdk_nvme_dhchap_get_digest_name(hash), 0);
     441           0 :         params[1] = OSSL_PARAM_construct_end();
     442             : 
     443           0 :         rc = -EIO;
     444           0 :         if (EVP_MAC_init(ctx, keybuf, (size_t)keylen, params) != 1) {
     445           0 :                 goto out;
     446             :         }
     447           0 :         if (EVP_MAC_update(ctx, caval, calen) != 1) {
     448           0 :                 goto out;
     449             :         }
     450           0 :         if (EVP_MAC_update(ctx, (void *)&seq, sizeof(seq)) != 1) {
     451           0 :                 goto out;
     452             :         }
     453           0 :         if (EVP_MAC_update(ctx, (void *)&tid, sizeof(tid)) != 1) {
     454           0 :                 goto out;
     455             :         }
     456           0 :         if (EVP_MAC_update(ctx, (void *)&scc, sizeof(scc)) != 1) {
     457           0 :                 goto out;
     458             :         }
     459           0 :         if (EVP_MAC_update(ctx, (void *)type, strlen(type)) != 1) {
     460           0 :                 goto out;
     461             :         }
     462           0 :         if (EVP_MAC_update(ctx, (void *)nqn1, strlen(nqn1)) != 1) {
     463           0 :                 goto out;
     464             :         }
     465           0 :         if (EVP_MAC_update(ctx, (void *)&term, sizeof(term)) != 1) {
     466           0 :                 goto out;
     467             :         }
     468           0 :         if (EVP_MAC_update(ctx, (void *)nqn2, strlen(nqn2)) != 1) {
     469           0 :                 goto out;
     470             :         }
     471           0 :         if (EVP_MAC_final(ctx, rval, &hlen, hlen) != 1) {
     472           0 :                 goto out;
     473             :         }
     474           0 :         rc = 0;
     475           0 : out:
     476           0 :         spdk_memset_s(keybuf, sizeof(keybuf), 0, sizeof(keybuf));
     477           0 :         EVP_MAC_CTX_free(ctx);
     478           0 :         EVP_MAC_free(hmac);
     479             : 
     480           0 :         return rc;
     481             : }
     482             : 
     483             : struct spdk_nvme_dhchap_dhkey *
     484           0 : spdk_nvme_dhchap_generate_dhkey(enum spdk_nvmf_dhchap_dhgroup dhgroup)
     485             : {
     486           0 :         EVP_PKEY_CTX *ctx = NULL;
     487           0 :         EVP_PKEY *key = NULL;
     488           0 :         OSSL_PARAM params[2];
     489             : 
     490           0 :         ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL);
     491           0 :         if (ctx == NULL) {
     492           0 :                 goto error;
     493             :         }
     494           0 :         if (EVP_PKEY_keygen_init(ctx) != 1) {
     495           0 :                 goto error;
     496             :         }
     497             : 
     498           0 :         params[0] = OSSL_PARAM_construct_utf8_string("group",
     499           0 :                         (char *)spdk_nvme_dhchap_get_dhgroup_name(dhgroup), 0);
     500           0 :         params[1] = OSSL_PARAM_construct_end();
     501           0 :         if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
     502           0 :                 SPDK_ERRLOG("Failed to set dhkey's dhgroup: %s\n",
     503             :                             spdk_nvme_dhchap_get_dhgroup_name(dhgroup));
     504           0 :                 goto error;
     505             :         }
     506           0 :         if (EVP_PKEY_generate(ctx, &key) != 1) {
     507           0 :                 goto error;
     508             :         }
     509           0 : error:
     510           0 :         EVP_PKEY_CTX_free(ctx);
     511           0 :         return (void *)key;
     512             : }
     513             : 
     514             : void
     515           0 : spdk_nvme_dhchap_dhkey_free(struct spdk_nvme_dhchap_dhkey **key)
     516             : {
     517           0 :         if (key == NULL) {
     518           0 :                 return;
     519             :         }
     520             : 
     521           0 :         EVP_PKEY_free(*(EVP_PKEY **)key);
     522           0 :         *key = NULL;
     523             : }
     524             : 
     525             : int
     526           0 : spdk_nvme_dhchap_dhkey_get_pubkey(struct spdk_nvme_dhchap_dhkey *dhkey, void *pub, size_t *len)
     527             : {
     528           0 :         EVP_PKEY *key = (EVP_PKEY *)dhkey;
     529           0 :         BIGNUM *bn = NULL;
     530             :         int rc;
     531           0 :         const size_t num_bytes = (size_t)spdk_divide_round_up(EVP_PKEY_get_bits(key), 8);
     532             : 
     533           0 :         if (num_bytes == 0) {
     534           0 :                 SPDK_ERRLOG("Failed to get key size\n");
     535           0 :                 return -EIO;
     536             :         }
     537             : 
     538           0 :         if (num_bytes > *len) {
     539           0 :                 SPDK_ERRLOG("Insufficient key buffer size=%zu (needed=%zu)",
     540             :                             *len, num_bytes);
     541           0 :                 return -EINVAL;
     542             :         }
     543           0 :         *len = num_bytes;
     544             : 
     545           0 :         if (EVP_PKEY_get_bn_param(key, "pub", &bn) != 1) {
     546           0 :                 rc = -EIO;
     547           0 :                 goto error;
     548             :         }
     549             : 
     550           0 :         rc = BN_bn2binpad(bn, pub, *len);
     551           0 :         if (rc <= 0) {
     552           0 :                 rc = -EIO;
     553           0 :                 goto error;
     554             :         }
     555           0 :         rc = 0;
     556           0 : error:
     557           0 :         BN_free(bn);
     558           0 :         return rc;
     559             : }
     560             : 
     561             : static EVP_PKEY *
     562           0 : nvme_auth_get_peerkey(const void *peerkey, size_t len, const char *dhgroup)
     563             : {
     564           0 :         EVP_PKEY_CTX *ctx = NULL;
     565           0 :         EVP_PKEY *result = NULL, *key = NULL;
     566           0 :         OSSL_PARAM_BLD *bld = NULL;
     567           0 :         OSSL_PARAM *params = NULL;
     568           0 :         BIGNUM *bn = NULL;
     569             : 
     570           0 :         ctx = EVP_PKEY_CTX_new_from_name(NULL, "DHX", NULL);
     571           0 :         if (ctx == NULL) {
     572           0 :                 goto error;
     573             :         }
     574           0 :         if (EVP_PKEY_fromdata_init(ctx) != 1) {
     575           0 :                 goto error;
     576             :         }
     577             : 
     578           0 :         bn = BN_bin2bn(peerkey, len, NULL);
     579           0 :         if (bn == NULL) {
     580           0 :                 goto error;
     581             :         }
     582             : 
     583           0 :         bld = OSSL_PARAM_BLD_new();
     584           0 :         if (bld == NULL) {
     585           0 :                 goto error;
     586             :         }
     587           0 :         if (OSSL_PARAM_BLD_push_BN(bld, "pub", bn) != 1) {
     588           0 :                 goto error;
     589             :         }
     590           0 :         if (OSSL_PARAM_BLD_push_utf8_string(bld, "group", dhgroup, 0) != 1) {
     591           0 :                 goto error;
     592             :         }
     593             : 
     594           0 :         params = OSSL_PARAM_BLD_to_param(bld);
     595           0 :         if (params == NULL) {
     596           0 :                 goto error;
     597             :         }
     598           0 :         if (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) != 1) {
     599           0 :                 SPDK_ERRLOG("Failed to create dhkey peer key\n");
     600           0 :                 goto error;
     601             :         }
     602             : 
     603           0 :         result = EVP_PKEY_dup(key);
     604           0 : error:
     605           0 :         EVP_PKEY_free(key);
     606           0 :         EVP_PKEY_CTX_free(ctx);
     607           0 :         OSSL_PARAM_BLD_free(bld);
     608           0 :         OSSL_PARAM_free(params);
     609           0 :         BN_free(bn);
     610             : 
     611           0 :         return result;
     612             : }
     613             : 
     614             : int
     615           0 : spdk_nvme_dhchap_dhkey_derive_secret(struct spdk_nvme_dhchap_dhkey *dhkey,
     616             :                                      const void *peer, size_t peerlen, void *secret, size_t *seclen)
     617             : {
     618           0 :         EVP_PKEY *key = (EVP_PKEY *)dhkey;
     619           0 :         EVP_PKEY_CTX *ctx = NULL;
     620           0 :         EVP_PKEY *peerkey = NULL;
     621           0 :         char dhgroup[64] = {};
     622           0 :         int rc = 0;
     623             : 
     624           0 :         if (EVP_PKEY_get_utf8_string_param(key, "group", dhgroup,
     625             :                                            sizeof(dhgroup), NULL) != 1) {
     626           0 :                 return -EIO;
     627             :         }
     628           0 :         peerkey = nvme_auth_get_peerkey(peer, peerlen, dhgroup);
     629           0 :         if (peerkey == NULL) {
     630           0 :                 return -EINVAL;
     631             :         }
     632           0 :         ctx = EVP_PKEY_CTX_new(key, NULL);
     633           0 :         if (ctx == NULL) {
     634           0 :                 rc = -ENOMEM;
     635           0 :                 goto out;
     636             :         }
     637           0 :         if (EVP_PKEY_derive_init(ctx) != 1) {
     638           0 :                 rc = -EIO;
     639           0 :                 goto out;
     640             :         }
     641           0 :         if (EVP_PKEY_CTX_set_dh_pad(ctx, 1) <= 0) {
     642           0 :                 rc = -EIO;
     643           0 :                 goto out;
     644             :         }
     645           0 :         if (EVP_PKEY_derive_set_peer(ctx, peerkey) != 1) {
     646           0 :                 SPDK_ERRLOG("Failed to set dhsecret's peer key\n");
     647           0 :                 rc = -EINVAL;
     648           0 :                 goto out;
     649             :         }
     650           0 :         if (EVP_PKEY_derive(ctx, secret, seclen) != 1) {
     651           0 :                 SPDK_ERRLOG("Failed to derive dhsecret\n");
     652           0 :                 rc = -ENOBUFS;
     653           0 :                 goto out;
     654             :         }
     655           0 : out:
     656           0 :         EVP_PKEY_free(peerkey);
     657           0 :         EVP_PKEY_CTX_free(ctx);
     658             : 
     659           0 :         return rc;
     660             : }
     661             : 
     662             : static int
     663           0 : nvme_auth_submit_request(struct spdk_nvme_qpair *qpair,
     664             :                          enum spdk_nvmf_fabric_cmd_types type, uint32_t len)
     665             : {
     666           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     667           0 :         struct nvme_request *req = qpair->reserved_req;
     668           0 :         struct nvme_completion_poll_status *status = qpair->poll_status;
     669           0 :         struct spdk_nvmf_fabric_auth_recv_cmd rcmd = {};
     670           0 :         struct spdk_nvmf_fabric_auth_send_cmd scmd = {};
     671             : 
     672           0 :         assert(len <= NVME_AUTH_DATA_SIZE);
     673           0 :         memset(&status->cpl, 0, sizeof(status->cpl));
     674           0 :         status->timeout_tsc = ctrlr->opts.admin_timeout_ms * spdk_get_ticks_hz() / 1000 +
     675           0 :                               spdk_get_ticks();
     676           0 :         status->done = false;
     677           0 :         NVME_INIT_REQUEST(req, nvme_completion_poll_cb, status,
     678             :                           NVME_PAYLOAD_CONTIG(status->dma_data, NULL), len, 0);
     679           0 :         switch (type) {
     680           0 :         case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND:
     681           0 :                 scmd.opcode = SPDK_NVME_OPC_FABRIC;
     682           0 :                 scmd.fctype = type;
     683           0 :                 scmd.spsp0 = 1;
     684           0 :                 scmd.spsp1 = 1;
     685           0 :                 scmd.secp = SPDK_NVMF_AUTH_SECP_NVME;
     686           0 :                 scmd.tl = len;
     687           0 :                 memcpy(&req->cmd, &scmd, sizeof(scmd));
     688           0 :                 break;
     689           0 :         case SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV:
     690           0 :                 rcmd.opcode = SPDK_NVME_OPC_FABRIC;
     691           0 :                 rcmd.fctype = type;
     692           0 :                 rcmd.spsp0 = 1;
     693           0 :                 rcmd.spsp1 = 1;
     694           0 :                 rcmd.secp = SPDK_NVMF_AUTH_SECP_NVME;
     695           0 :                 rcmd.al = len;
     696           0 :                 memcpy(&req->cmd, &rcmd, sizeof(rcmd));
     697           0 :                 break;
     698           0 :         default:
     699           0 :                 assert(0 && "invalid command");
     700             :                 return -EINVAL;
     701             :         }
     702             : 
     703           0 :         return nvme_qpair_submit_request(qpair, req);
     704             : }
     705             : 
     706             : static int
     707           0 : nvme_auth_recv_message(struct spdk_nvme_qpair *qpair)
     708             : {
     709           0 :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     710           0 :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_RECV,
     711             :                                         NVME_AUTH_DATA_SIZE);
     712             : }
     713             : 
     714             : static bool
     715           0 : nvme_auth_send_failure2(struct spdk_nvme_qpair *qpair, enum spdk_nvmf_auth_failure_reason reason)
     716             : {
     717           0 :         struct spdk_nvmf_auth_failure *msg = qpair->poll_status->dma_data;
     718           0 :         struct nvme_auth *auth = &qpair->auth;
     719             : 
     720           0 :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     721           0 :         msg->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
     722           0 :         msg->auth_id = SPDK_NVMF_AUTH_ID_FAILURE2;
     723           0 :         msg->t_id = auth->tid;
     724           0 :         msg->rc = SPDK_NVMF_AUTH_FAILURE;
     725           0 :         msg->rce = reason;
     726             : 
     727           0 :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
     728           0 :                                         sizeof(*msg)) == 0;
     729             : }
     730             : 
     731             : static int
     732           0 : nvme_auth_check_message(struct spdk_nvme_qpair *qpair, enum spdk_nvmf_auth_id auth_id)
     733             : {
     734           0 :         struct spdk_nvmf_auth_failure *msg = qpair->poll_status->dma_data;
     735           0 :         const char *reason = NULL;
     736           0 :         const char *reasons[] = {
     737             :                 [SPDK_NVMF_AUTH_FAILED] = "authentication failed",
     738             :                 [SPDK_NVMF_AUTH_PROTOCOL_UNUSABLE] = "protocol not usable",
     739             :                 [SPDK_NVMF_AUTH_SCC_MISMATCH] = "secure channel concatenation mismatch",
     740             :                 [SPDK_NVMF_AUTH_HASH_UNUSABLE] = "hash not usable",
     741             :                 [SPDK_NVMF_AUTH_DHGROUP_UNUSABLE] = "dhgroup not usable",
     742             :                 [SPDK_NVMF_AUTH_INCORRECT_PAYLOAD] = "incorrect payload",
     743             :                 [SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE] = "incorrect protocol message",
     744             :         };
     745             : 
     746           0 :         switch (msg->auth_type) {
     747           0 :         case SPDK_NVMF_AUTH_TYPE_DHCHAP:
     748           0 :                 if (msg->auth_id == auth_id) {
     749           0 :                         return 0;
     750             :                 }
     751           0 :                 AUTH_ERRLOG(qpair, "received unexpected DH-HMAC-CHAP message id: %u (expected: %u)\n",
     752             :                             msg->auth_id, auth_id);
     753           0 :                 break;
     754           0 :         case SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE:
     755             :                 /* The only common message that we can expect to receive is AUTH_failure1 */
     756           0 :                 if (msg->auth_id != SPDK_NVMF_AUTH_ID_FAILURE1) {
     757           0 :                         AUTH_ERRLOG(qpair, "received unexpected common message id: %u\n",
     758             :                                     msg->auth_id);
     759           0 :                         break;
     760             :                 }
     761           0 :                 if (msg->rc == SPDK_NVMF_AUTH_FAILURE && msg->rce < SPDK_COUNTOF(reasons)) {
     762           0 :                         reason = reasons[msg->rce];
     763             :                 }
     764           0 :                 AUTH_ERRLOG(qpair, "received AUTH_failure1: rc=%d, rce=%d (%s)\n",
     765             :                             msg->rc, msg->rce, reason);
     766           0 :                 nvme_auth_set_failure(qpair, -EACCES, false);
     767           0 :                 return -EACCES;
     768           0 :         default:
     769           0 :                 AUTH_ERRLOG(qpair, "received unknown message type: %u\n", msg->auth_type);
     770           0 :                 break;
     771             :         }
     772             : 
     773           0 :         nvme_auth_set_failure(qpair, -EACCES,
     774           0 :                               nvme_auth_send_failure2(qpair,
     775             :                                               SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE));
     776           0 :         return -EACCES;
     777             : }
     778             : 
     779             : static int
     780           0 : nvme_auth_send_negotiate(struct spdk_nvme_qpair *qpair)
     781             : {
     782           0 :         struct nvme_auth *auth = &qpair->auth;
     783           0 :         struct spdk_nvmf_auth_negotiate *msg = qpair->poll_status->dma_data;
     784           0 :         struct spdk_nvmf_auth_descriptor *desc = msg->descriptors;
     785             :         size_t i;
     786             : 
     787           0 :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     788           0 :         desc->auth_id = SPDK_NVMF_AUTH_TYPE_DHCHAP;
     789             :         assert(SPDK_COUNTOF(g_digests) <= sizeof(desc->hash_id_list));
     790             :         assert(SPDK_COUNTOF(g_dhgroups) <= sizeof(desc->dhg_id_list));
     791             : 
     792           0 :         for (i = 0; i < SPDK_COUNTOF(g_digests); ++i) {
     793           0 :                 if (!nvme_auth_digest_allowed(qpair, g_digests[i].id)) {
     794           0 :                         continue;
     795             :                 }
     796           0 :                 AUTH_DEBUGLOG(qpair, "digest: %u (%s)\n", g_digests[i].id,
     797             :                               spdk_nvme_dhchap_get_digest_name(g_digests[i].id));
     798           0 :                 desc->hash_id_list[desc->halen++] = g_digests[i].id;
     799             :         }
     800           0 :         for (i = 0; i < SPDK_COUNTOF(g_dhgroups); ++i) {
     801           0 :                 if (!nvme_auth_dhgroup_allowed(qpair, g_dhgroups[i].id)) {
     802           0 :                         continue;
     803             :                 }
     804           0 :                 AUTH_DEBUGLOG(qpair, "dhgroup: %u (%s)\n", g_dhgroups[i].id,
     805             :                               spdk_nvme_dhchap_get_dhgroup_name(g_dhgroups[i].id));
     806           0 :                 desc->dhg_id_list[desc->dhlen++] = g_dhgroups[i].id;
     807             :         }
     808             : 
     809           0 :         msg->auth_type = SPDK_NVMF_AUTH_TYPE_COMMON_MESSAGE;
     810           0 :         msg->auth_id = SPDK_NVMF_AUTH_ID_NEGOTIATE;
     811           0 :         msg->t_id = auth->tid;
     812           0 :         msg->sc_c = SPDK_NVMF_AUTH_SCC_DISABLED;
     813           0 :         msg->napd = 1;
     814             : 
     815           0 :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
     816           0 :                                         sizeof(*msg) + msg->napd * sizeof(*desc));
     817             : }
     818             : 
     819             : static int
     820           0 : nvme_auth_check_challenge(struct spdk_nvme_qpair *qpair)
     821             : {
     822           0 :         struct spdk_nvmf_dhchap_challenge *challenge = qpair->poll_status->dma_data;
     823           0 :         struct nvme_auth *auth = &qpair->auth;
     824             :         uint8_t hl;
     825             :         int rc;
     826             : 
     827           0 :         rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_CHALLENGE);
     828           0 :         if (rc != 0) {
     829           0 :                 return rc;
     830             :         }
     831             : 
     832           0 :         if (challenge->t_id != auth->tid) {
     833           0 :                 AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
     834             :                             challenge->t_id, auth->tid);
     835           0 :                 goto error;
     836             :         }
     837             : 
     838           0 :         if (challenge->seqnum == 0) {
     839           0 :                 AUTH_ERRLOG(qpair, "received challenge with seqnum=0\n");
     840           0 :                 goto error;
     841             :         }
     842             : 
     843           0 :         hl = spdk_nvme_dhchap_get_digest_length(challenge->hash_id);
     844           0 :         if (hl == 0) {
     845           0 :                 AUTH_ERRLOG(qpair, "unsupported hash function: 0x%x\n", challenge->hash_id);
     846           0 :                 goto error;
     847             :         }
     848             : 
     849           0 :         if (challenge->hl != hl) {
     850           0 :                 AUTH_ERRLOG(qpair, "unexpected hash length: received=%u, expected=%u\n",
     851             :                             challenge->hl, hl);
     852           0 :                 goto error;
     853             :         }
     854             : 
     855           0 :         switch (challenge->dhg_id) {
     856           0 :         case SPDK_NVMF_DHCHAP_DHGROUP_NULL:
     857           0 :                 if (challenge->dhvlen != 0) {
     858           0 :                         AUTH_ERRLOG(qpair, "unexpected dhvlen=%u for dhgroup 0\n",
     859             :                                     challenge->dhvlen);
     860           0 :                         goto error;
     861             :                 }
     862           0 :                 break;
     863           0 :         case SPDK_NVMF_DHCHAP_DHGROUP_2048:
     864             :         case SPDK_NVMF_DHCHAP_DHGROUP_3072:
     865             :         case SPDK_NVMF_DHCHAP_DHGROUP_4096:
     866             :         case SPDK_NVMF_DHCHAP_DHGROUP_6144:
     867             :         case SPDK_NVMF_DHCHAP_DHGROUP_8192:
     868           0 :                 if (sizeof(*challenge) + hl + challenge->dhvlen > NVME_AUTH_DATA_SIZE ||
     869           0 :                     challenge->dhvlen == 0) {
     870           0 :                         AUTH_ERRLOG(qpair, "invalid dhvlen=%u for dhgroup %u\n",
     871             :                                     challenge->dhvlen, challenge->dhg_id);
     872           0 :                         goto error;
     873             :                 }
     874           0 :                 break;
     875           0 :         default:
     876           0 :                 AUTH_ERRLOG(qpair, "unsupported dhgroup: 0x%x\n", challenge->dhg_id);
     877           0 :                 goto error;
     878             :         }
     879             : 
     880           0 :         if (!nvme_auth_digest_allowed(qpair, challenge->hash_id)) {
     881           0 :                 AUTH_ERRLOG(qpair, "received disallowed digest: %u (%s)\n", challenge->hash_id,
     882             :                             spdk_nvme_dhchap_get_digest_name(challenge->hash_id));
     883           0 :                 goto error;
     884             :         }
     885             : 
     886           0 :         if (!nvme_auth_dhgroup_allowed(qpair, challenge->dhg_id)) {
     887           0 :                 AUTH_ERRLOG(qpair, "received disallowed dhgroup: %u (%s)\n", challenge->dhg_id,
     888             :                             spdk_nvme_dhchap_get_dhgroup_name(challenge->dhg_id));
     889           0 :                 goto error;
     890             :         }
     891             : 
     892           0 :         return 0;
     893           0 : error:
     894           0 :         nvme_auth_set_failure(qpair, -EACCES,
     895           0 :                               nvme_auth_send_failure2(qpair, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD));
     896           0 :         return -EACCES;
     897             : }
     898             : 
     899             : static int
     900           0 : nvme_auth_send_reply(struct spdk_nvme_qpair *qpair)
     901             : {
     902           0 :         struct nvme_completion_poll_status *status = qpair->poll_status;
     903           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
     904           0 :         struct spdk_nvmf_dhchap_challenge *challenge = status->dma_data;
     905           0 :         struct spdk_nvmf_dhchap_reply *reply = status->dma_data;
     906           0 :         struct nvme_auth *auth = &qpair->auth;
     907           0 :         struct spdk_nvme_dhchap_dhkey *dhkey;
     908           0 :         struct spdk_key *key = NULL, *ckey = NULL;
     909           0 :         uint8_t hl, response[NVME_AUTH_DATA_SIZE];
     910           0 :         uint8_t pubkey[NVME_AUTH_DH_KEY_MAX_SIZE];
     911           0 :         uint8_t dhsec[NVME_AUTH_DH_KEY_MAX_SIZE];
     912           0 :         uint8_t ctrlr_challenge[NVME_AUTH_DIGEST_MAX_SIZE] = {};
     913           0 :         size_t dhseclen = 0, publen = 0;
     914           0 :         uint32_t seqnum = 0;
     915             :         int rc;
     916             : 
     917           0 :         auth->hash = challenge->hash_id;
     918           0 :         hl = spdk_nvme_dhchap_get_digest_length(challenge->hash_id);
     919           0 :         if (challenge->dhg_id != SPDK_NVMF_DHCHAP_DHGROUP_NULL) {
     920           0 :                 dhseclen = sizeof(dhsec);
     921           0 :                 publen = sizeof(pubkey);
     922           0 :                 AUTH_LOGDUMP("ctrlr pubkey:", &challenge->cval[hl], challenge->dhvlen);
     923           0 :                 dhkey = spdk_nvme_dhchap_generate_dhkey(
     924           0 :                                 (enum spdk_nvmf_dhchap_dhgroup)challenge->dhg_id);
     925           0 :                 if (dhkey == NULL) {
     926           0 :                         rc = -EINVAL;
     927           0 :                         goto out;
     928             :                 }
     929           0 :                 rc = spdk_nvme_dhchap_dhkey_get_pubkey(dhkey, pubkey, &publen);
     930           0 :                 if (rc != 0) {
     931           0 :                         spdk_nvme_dhchap_dhkey_free(&dhkey);
     932           0 :                         goto out;
     933             :                 }
     934           0 :                 AUTH_LOGDUMP("host pubkey:", pubkey, publen);
     935           0 :                 rc = spdk_nvme_dhchap_dhkey_derive_secret(dhkey,
     936           0 :                                 &challenge->cval[hl], challenge->dhvlen, dhsec, &dhseclen);
     937           0 :                 spdk_nvme_dhchap_dhkey_free(&dhkey);
     938           0 :                 if (rc != 0) {
     939           0 :                         goto out;
     940             :                 }
     941             : 
     942           0 :                 AUTH_LOGDUMP("dh secret:", dhsec, dhseclen);
     943             :         }
     944             : 
     945           0 :         nvme_ctrlr_lock(ctrlr);
     946           0 :         key = ctrlr->opts.dhchap_key ? spdk_key_dup(ctrlr->opts.dhchap_key) : NULL;
     947           0 :         ckey = ctrlr->opts.dhchap_ctrlr_key ? spdk_key_dup(ctrlr->opts.dhchap_ctrlr_key) : NULL;
     948           0 :         nvme_ctrlr_unlock(ctrlr);
     949             : 
     950           0 :         AUTH_DEBUGLOG(qpair, "key=%s, hash=%u, dhgroup=%u, seq=%u, tid=%u, subnqn=%s, hostnqn=%s, "
     951             :                       "len=%u\n", spdk_key_get_name(key), challenge->hash_id, challenge->dhg_id,
     952             :                       challenge->seqnum, auth->tid, ctrlr->trid.subnqn, ctrlr->opts.hostnqn, hl);
     953           0 :         rc = spdk_nvme_dhchap_calculate(key, (enum spdk_nvmf_dhchap_hash)challenge->hash_id,
     954           0 :                                         "HostHost", challenge->seqnum, auth->tid, 0,
     955           0 :                                         ctrlr->opts.hostnqn, ctrlr->trid.subnqn,
     956           0 :                                         dhseclen > 0 ? dhsec : NULL, dhseclen,
     957           0 :                                         challenge->cval, response);
     958           0 :         if (rc != 0) {
     959           0 :                 AUTH_ERRLOG(qpair, "failed to calculate response: %s\n", spdk_strerror(-rc));
     960           0 :                 goto out;
     961             :         }
     962             : 
     963           0 :         if (ckey != NULL) {
     964           0 :                 seqnum = nvme_auth_get_seqnum(qpair);
     965           0 :                 if (seqnum == 0) {
     966           0 :                         rc = -EIO;
     967           0 :                         goto out;
     968             :                 }
     969             : 
     970           0 :                 assert(sizeof(ctrlr_challenge) >= hl);
     971           0 :                 rc = RAND_bytes(ctrlr_challenge, hl);
     972           0 :                 if (rc != 1) {
     973           0 :                         rc = -EIO;
     974           0 :                         goto out;
     975             :                 }
     976             : 
     977           0 :                 rc = spdk_nvme_dhchap_calculate(ckey,
     978           0 :                                                 (enum spdk_nvmf_dhchap_hash)challenge->hash_id,
     979           0 :                                                 "Controller", seqnum, auth->tid, 0,
     980           0 :                                                 ctrlr->trid.subnqn, ctrlr->opts.hostnqn,
     981           0 :                                                 dhseclen > 0 ? dhsec : NULL, dhseclen,
     982           0 :                                                 ctrlr_challenge, auth->challenge);
     983           0 :                 if (rc != 0) {
     984           0 :                         AUTH_ERRLOG(qpair, "failed to calculate controller's response: %s\n",
     985             :                                     spdk_strerror(-rc));
     986           0 :                         goto out;
     987             :                 }
     988             :         }
     989             : 
     990             :         /* Now that the response has been calculated, send the reply */
     991           0 :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
     992           0 :         assert(sizeof(*reply) + 2 * hl + publen <= NVME_AUTH_DATA_SIZE);
     993           0 :         memcpy(reply->rval, response, hl);
     994           0 :         memcpy(&reply->rval[1 * hl], ctrlr_challenge, hl);
     995           0 :         memcpy(&reply->rval[2 * hl], pubkey, publen);
     996             : 
     997           0 :         reply->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
     998           0 :         reply->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_REPLY;
     999           0 :         reply->t_id = auth->tid;
    1000           0 :         reply->hl = hl;
    1001           0 :         reply->cvalid = ckey != NULL;
    1002           0 :         reply->dhvlen = publen;
    1003           0 :         reply->seqnum = seqnum;
    1004             : 
    1005             :         /* The 2 * reply->hl below is because the spec says that both rval[hl] and cval[hl] must
    1006             :          * always be part of the reply message, even cvalid is zero.
    1007             :          */
    1008           0 :         rc = nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
    1009           0 :                                       sizeof(*reply) + 2 * reply->hl + publen);
    1010           0 : out:
    1011           0 :         spdk_keyring_put_key(key);
    1012           0 :         spdk_keyring_put_key(ckey);
    1013             : 
    1014           0 :         return rc;
    1015             : }
    1016             : 
    1017             : static int
    1018           0 : nvme_auth_check_success1(struct spdk_nvme_qpair *qpair)
    1019             : {
    1020           0 :         struct spdk_nvmf_dhchap_success1 *msg = qpair->poll_status->dma_data;
    1021           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1022           0 :         struct nvme_auth *auth = &qpair->auth;
    1023             :         uint8_t hl;
    1024             :         int rc, status;
    1025             : 
    1026           0 :         rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS1);
    1027           0 :         if (rc != 0) {
    1028           0 :                 return rc;
    1029             :         }
    1030             : 
    1031           0 :         if (msg->t_id != auth->tid) {
    1032           0 :                 AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
    1033             :                             msg->t_id, auth->tid);
    1034           0 :                 status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
    1035           0 :                 goto error;
    1036             :         }
    1037             : 
    1038           0 :         if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
    1039           0 :                 if (!msg->rvalid) {
    1040           0 :                         AUTH_ERRLOG(qpair, "received rvalid=0, expected response\n");
    1041           0 :                         status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
    1042           0 :                         goto error;
    1043             :                 }
    1044             : 
    1045           0 :                 hl = spdk_nvme_dhchap_get_digest_length(auth->hash);
    1046           0 :                 if (msg->hl != hl) {
    1047           0 :                         AUTH_ERRLOG(qpair, "received invalid hl=%u, expected=%u\n", msg->hl, hl);
    1048           0 :                         status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
    1049           0 :                         goto error;
    1050             :                 }
    1051             : 
    1052           0 :                 if (memcmp(msg->rval, auth->challenge, hl) != 0) {
    1053           0 :                         AUTH_ERRLOG(qpair, "controller challenge mismatch\n");
    1054           0 :                         AUTH_LOGDUMP("received:", msg->rval, hl);
    1055           0 :                         AUTH_LOGDUMP("expected:", auth->challenge, hl);
    1056           0 :                         status = SPDK_NVMF_AUTH_FAILED;
    1057           0 :                         goto error;
    1058             :                 }
    1059             :         }
    1060             : 
    1061           0 :         return 0;
    1062           0 : error:
    1063           0 :         nvme_auth_set_failure(qpair, -EACCES, nvme_auth_send_failure2(qpair, status));
    1064             : 
    1065           0 :         return -EACCES;
    1066             : }
    1067             : 
    1068             : static int
    1069           0 : nvme_auth_send_success2(struct spdk_nvme_qpair *qpair)
    1070             : {
    1071           0 :         struct spdk_nvmf_dhchap_success2 *msg = qpair->poll_status->dma_data;
    1072           0 :         struct nvme_auth *auth = &qpair->auth;
    1073             : 
    1074           0 :         memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
    1075           0 :         msg->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
    1076           0 :         msg->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS2;
    1077           0 :         msg->t_id = auth->tid;
    1078             : 
    1079           0 :         return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
    1080             :                                         sizeof(*msg));
    1081             : }
    1082             : 
    1083             : int
    1084           0 : nvme_fabric_qpair_authenticate_poll(struct spdk_nvme_qpair *qpair)
    1085             : {
    1086           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1087           0 :         struct nvme_auth *auth = &qpair->auth;
    1088           0 :         struct nvme_completion_poll_status *status = qpair->poll_status;
    1089             :         enum nvme_qpair_auth_state prev_state;
    1090             :         int rc;
    1091             : 
    1092             :         do {
    1093           0 :                 prev_state = auth->state;
    1094             : 
    1095           0 :                 switch (auth->state) {
    1096           0 :                 case NVME_QPAIR_AUTH_STATE_NEGOTIATE:
    1097           0 :                         rc = nvme_auth_send_negotiate(qpair);
    1098           0 :                         if (rc != 0) {
    1099           0 :                                 nvme_auth_set_failure(qpair, rc, false);
    1100           0 :                                 AUTH_ERRLOG(qpair, "failed to send AUTH_negotiate: %s\n",
    1101             :                                             spdk_strerror(-rc));
    1102           0 :                                 break;
    1103             :                         }
    1104           0 :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE);
    1105           0 :                         break;
    1106           0 :                 case NVME_QPAIR_AUTH_STATE_AWAIT_NEGOTIATE:
    1107           0 :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1108           0 :                         if (rc != 0) {
    1109           0 :                                 if (rc != -EAGAIN) {
    1110           0 :                                         nvme_auth_print_cpl(qpair, "AUTH_negotiate");
    1111           0 :                                         nvme_auth_set_failure(qpair, rc, false);
    1112             :                                 }
    1113           0 :                                 break;
    1114             :                         }
    1115             :                         /* Negotiate has been sent, try to receive the challenge */
    1116           0 :                         rc = nvme_auth_recv_message(qpair);
    1117           0 :                         if (rc != 0) {
    1118           0 :                                 nvme_auth_set_failure(qpair, rc, false);
    1119           0 :                                 AUTH_ERRLOG(qpair, "failed to recv DH-HMAC-CHAP_challenge: %s\n",
    1120             :                                             spdk_strerror(-rc));
    1121           0 :                                 break;
    1122             :                         }
    1123           0 :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE);
    1124           0 :                         break;
    1125           0 :                 case NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE:
    1126           0 :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1127           0 :                         if (rc != 0) {
    1128           0 :                                 if (rc != -EAGAIN) {
    1129           0 :                                         nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_challenge");
    1130           0 :                                         nvme_auth_set_failure(qpair, rc, false);
    1131             :                                 }
    1132           0 :                                 break;
    1133             :                         }
    1134           0 :                         rc = nvme_auth_check_challenge(qpair);
    1135           0 :                         if (rc != 0) {
    1136           0 :                                 break;
    1137             :                         }
    1138           0 :                         rc = nvme_auth_send_reply(qpair);
    1139           0 :                         if (rc != 0) {
    1140           0 :                                 nvme_auth_set_failure(qpair, rc, false);
    1141           0 :                                 AUTH_ERRLOG(qpair, "failed to send DH-HMAC-CHAP_reply: %s\n",
    1142             :                                             spdk_strerror(-rc));
    1143           0 :                                 break;
    1144             :                         }
    1145           0 :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_REPLY);
    1146           0 :                         break;
    1147           0 :                 case NVME_QPAIR_AUTH_STATE_AWAIT_REPLY:
    1148           0 :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1149           0 :                         if (rc != 0) {
    1150           0 :                                 if (rc != -EAGAIN) {
    1151           0 :                                         nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_reply");
    1152           0 :                                         nvme_auth_set_failure(qpair, rc, false);
    1153             :                                 }
    1154           0 :                                 break;
    1155             :                         }
    1156             :                         /* Reply has been sent, try to receive response */
    1157           0 :                         rc = nvme_auth_recv_message(qpair);
    1158           0 :                         if (rc != 0) {
    1159           0 :                                 nvme_auth_set_failure(qpair, rc, false);
    1160           0 :                                 AUTH_ERRLOG(qpair, "failed to recv DH-HMAC-CHAP_success1: %s\n",
    1161             :                                             spdk_strerror(-rc));
    1162           0 :                                 break;
    1163             :                         }
    1164           0 :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1);
    1165           0 :                         break;
    1166           0 :                 case NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1:
    1167           0 :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1168           0 :                         if (rc != 0) {
    1169           0 :                                 if (rc != -EAGAIN) {
    1170           0 :                                         nvme_auth_print_cpl(qpair, "DH-HMAC-CHAP_success1");
    1171           0 :                                         nvme_auth_set_failure(qpair, rc, false);
    1172             :                                 }
    1173           0 :                                 break;
    1174             :                         }
    1175           0 :                         rc = nvme_auth_check_success1(qpair);
    1176           0 :                         if (rc != 0) {
    1177           0 :                                 break;
    1178             :                         }
    1179           0 :                         AUTH_DEBUGLOG(qpair, "authentication completed successfully\n");
    1180           0 :                         if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
    1181           0 :                                 rc = nvme_auth_send_success2(qpair);
    1182           0 :                                 if (rc != 0) {
    1183           0 :                                         AUTH_ERRLOG(qpair, "failed to send DH-HMAC-CHAP_success2: "
    1184             :                                                     "%s\n", spdk_strerror(rc));
    1185           0 :                                         nvme_auth_set_failure(qpair, rc, false);
    1186           0 :                                         break;
    1187             :                                 }
    1188           0 :                                 nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2);
    1189           0 :                                 break;
    1190             :                         }
    1191           0 :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
    1192           0 :                         break;
    1193           0 :                 case NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2:
    1194             :                 case NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2:
    1195           0 :                         rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
    1196           0 :                         if (rc == -EAGAIN) {
    1197           0 :                                 break;
    1198             :                         }
    1199           0 :                         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
    1200           0 :                         break;
    1201           0 :                 case NVME_QPAIR_AUTH_STATE_DONE:
    1202           0 :                         if (qpair->poll_status != NULL && !status->timed_out) {
    1203           0 :                                 qpair->poll_status = NULL;
    1204           0 :                                 spdk_free(status->dma_data);
    1205           0 :                                 free(status);
    1206             :                         }
    1207           0 :                         if (auth->cb_fn != NULL) {
    1208           0 :                                 auth->cb_fn(auth->cb_ctx, auth->status);
    1209           0 :                                 auth->cb_fn = NULL;
    1210             :                         }
    1211           0 :                         return auth->status;
    1212           0 :                 default:
    1213           0 :                         assert(0 && "invalid state");
    1214             :                         return -EINVAL;
    1215             :                 }
    1216           0 :         } while (auth->state != prev_state);
    1217             : 
    1218           0 :         return -EAGAIN;
    1219             : }
    1220             : 
    1221             : int
    1222           0 : nvme_fabric_qpair_authenticate_async(struct spdk_nvme_qpair *qpair)
    1223             : {
    1224           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1225             :         struct nvme_completion_poll_status *status;
    1226           0 :         struct nvme_auth *auth = &qpair->auth;
    1227             :         int rc;
    1228             : 
    1229           0 :         if (ctrlr->opts.dhchap_key == NULL) {
    1230           0 :                 AUTH_ERRLOG(qpair, "missing DH-HMAC-CHAP key\n");
    1231           0 :                 return -ENOKEY;
    1232             :         }
    1233             : 
    1234           0 :         if (qpair->auth.flags & NVME_QPAIR_AUTH_FLAG_ASCR) {
    1235           0 :                 AUTH_ERRLOG(qpair, "secure channel concatenation is not supported\n");
    1236           0 :                 return -EINVAL;
    1237             :         }
    1238             : 
    1239           0 :         status = calloc(1, sizeof(*qpair->poll_status));
    1240           0 :         if (!status) {
    1241           0 :                 AUTH_ERRLOG(qpair, "failed to allocate poll status\n");
    1242           0 :                 return -ENOMEM;
    1243             :         }
    1244             : 
    1245           0 :         status->dma_data = spdk_zmalloc(NVME_AUTH_DATA_SIZE, 0, NULL, SPDK_ENV_LCORE_ID_ANY,
    1246             :                                         SPDK_MALLOC_DMA);
    1247           0 :         if (!status->dma_data) {
    1248           0 :                 AUTH_ERRLOG(qpair, "failed to allocate poll status\n");
    1249           0 :                 free(status);
    1250           0 :                 return -ENOMEM;
    1251             :         }
    1252             : 
    1253           0 :         assert(qpair->poll_status == NULL);
    1254           0 :         qpair->poll_status = status;
    1255             : 
    1256           0 :         nvme_ctrlr_lock(ctrlr);
    1257           0 :         auth->tid = ctrlr->auth_tid++;
    1258           0 :         nvme_ctrlr_unlock(ctrlr);
    1259             : 
    1260           0 :         nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_NEGOTIATE);
    1261             : 
    1262             :         /* Do the initial poll to kick-start the state machine */
    1263           0 :         rc = nvme_fabric_qpair_authenticate_poll(qpair);
    1264           0 :         return rc != -EAGAIN ? rc : 0;
    1265             : }
    1266             : 
    1267             : int
    1268           0 : spdk_nvme_qpair_authenticate(struct spdk_nvme_qpair *qpair,
    1269             :                              spdk_nvme_authenticate_cb cb_fn, void *cb_ctx)
    1270             : {
    1271           0 :         struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
    1272             :         int rc;
    1273             : 
    1274           0 :         if (qpair->auth.cb_fn != NULL) {
    1275           0 :                 SPDK_ERRLOG("authentication already in-progress\n");
    1276           0 :                 return -EALREADY;
    1277             :         }
    1278             : 
    1279           0 :         if (ctrlr->opts.dhchap_key == NULL) {
    1280           0 :                 SPDK_ERRLOG("missing DH-HMAC-CHAP key\n");
    1281           0 :                 return -ENOKEY;
    1282             :         }
    1283             : 
    1284           0 :         rc = nvme_transport_qpair_authenticate(qpair);
    1285           0 :         if (rc == 0) {
    1286           0 :                 qpair->auth.cb_fn = cb_fn;
    1287           0 :                 qpair->auth.cb_ctx = cb_ctx;
    1288             :         }
    1289             : 
    1290           0 :         return rc;
    1291             : }
    1292             : #endif /* SPDK_CONFIG_EVP_MAC */
    1293             : 
    1294           0 : SPDK_LOG_REGISTER_COMPONENT(nvme_auth)

Generated by: LCOV version 1.15