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