Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

nvme-auth: Diffie-Hellman key exchange support

Implement Diffie-Hellman key exchange using FFDHE groups
for NVMe In-Band Authentication.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Hannes Reinecke and committed by
Jens Axboe
b61775d1 f50fff73

+358 -6
+153
drivers/nvme/common/auth.c
··· 295 295 } 296 296 EXPORT_SYMBOL_GPL(nvme_auth_transform_key); 297 297 298 + static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey) 299 + { 300 + const char *digest_name; 301 + struct crypto_shash *tfm; 302 + int ret; 303 + 304 + digest_name = nvme_auth_digest_name(hmac_id); 305 + if (!digest_name) { 306 + pr_debug("%s: failed to get digest for %d\n", __func__, 307 + hmac_id); 308 + return -EINVAL; 309 + } 310 + tfm = crypto_alloc_shash(digest_name, 0, 0); 311 + if (IS_ERR(tfm)) 312 + return -ENOMEM; 313 + 314 + ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey); 315 + if (ret < 0) 316 + pr_debug("%s: Failed to hash digest len %zu\n", __func__, 317 + skey_len); 318 + 319 + crypto_free_shash(tfm); 320 + return ret; 321 + } 322 + 323 + int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, 324 + u8 *challenge, u8 *aug, size_t hlen) 325 + { 326 + struct crypto_shash *tfm; 327 + struct shash_desc *desc; 328 + u8 *hashed_key; 329 + const char *hmac_name; 330 + int ret; 331 + 332 + hashed_key = kmalloc(hlen, GFP_KERNEL); 333 + if (!hashed_key) 334 + return -ENOMEM; 335 + 336 + ret = nvme_auth_hash_skey(hmac_id, skey, 337 + skey_len, hashed_key); 338 + if (ret < 0) 339 + goto out_free_key; 340 + 341 + hmac_name = nvme_auth_hmac_name(hmac_id); 342 + if (!hmac_name) { 343 + pr_warn("%s: invalid hash algoritm %d\n", 344 + __func__, hmac_id); 345 + ret = -EINVAL; 346 + goto out_free_key; 347 + } 348 + 349 + tfm = crypto_alloc_shash(hmac_name, 0, 0); 350 + if (IS_ERR(tfm)) { 351 + ret = PTR_ERR(tfm); 352 + goto out_free_key; 353 + } 354 + 355 + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm), 356 + GFP_KERNEL); 357 + if (!desc) { 358 + ret = -ENOMEM; 359 + goto out_free_hash; 360 + } 361 + desc->tfm = tfm; 362 + 363 + ret = crypto_shash_setkey(tfm, hashed_key, hlen); 364 + if (ret) 365 + goto out_free_desc; 366 + 367 + ret = crypto_shash_init(desc); 368 + if (ret) 369 + goto out_free_desc; 370 + 371 + ret = crypto_shash_update(desc, challenge, hlen); 372 + if (ret) 373 + goto out_free_desc; 374 + 375 + ret = crypto_shash_final(desc, aug); 376 + out_free_desc: 377 + kfree_sensitive(desc); 378 + out_free_hash: 379 + crypto_free_shash(tfm); 380 + out_free_key: 381 + kfree_sensitive(hashed_key); 382 + return ret; 383 + } 384 + EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge); 385 + 386 + int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid) 387 + { 388 + int ret; 389 + 390 + ret = crypto_kpp_set_secret(dh_tfm, NULL, 0); 391 + if (ret) 392 + pr_debug("failed to set private key, error %d\n", ret); 393 + 394 + return ret; 395 + } 396 + EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey); 397 + 398 + int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, 399 + u8 *host_key, size_t host_key_len) 400 + { 401 + struct kpp_request *req; 402 + struct crypto_wait wait; 403 + struct scatterlist dst; 404 + int ret; 405 + 406 + req = kpp_request_alloc(dh_tfm, GFP_KERNEL); 407 + if (!req) 408 + return -ENOMEM; 409 + 410 + crypto_init_wait(&wait); 411 + kpp_request_set_input(req, NULL, 0); 412 + sg_init_one(&dst, host_key, host_key_len); 413 + kpp_request_set_output(req, &dst, host_key_len); 414 + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 415 + crypto_req_done, &wait); 416 + 417 + ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); 418 + kpp_request_free(req); 419 + return ret; 420 + } 421 + EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey); 422 + 423 + int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, 424 + u8 *ctrl_key, size_t ctrl_key_len, 425 + u8 *sess_key, size_t sess_key_len) 426 + { 427 + struct kpp_request *req; 428 + struct crypto_wait wait; 429 + struct scatterlist src, dst; 430 + int ret; 431 + 432 + req = kpp_request_alloc(dh_tfm, GFP_KERNEL); 433 + if (!req) 434 + return -ENOMEM; 435 + 436 + crypto_init_wait(&wait); 437 + sg_init_one(&src, ctrl_key, ctrl_key_len); 438 + kpp_request_set_input(req, &src, ctrl_key_len); 439 + sg_init_one(&dst, sess_key, sess_key_len); 440 + kpp_request_set_output(req, &dst, sess_key_len); 441 + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, 442 + crypto_req_done, &wait); 443 + 444 + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); 445 + 446 + kpp_request_free(req); 447 + return ret; 448 + } 449 + EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret); 450 + 298 451 int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) 299 452 { 300 453 struct nvme_dhchap_key *key;
+2
drivers/nvme/host/Kconfig
··· 100 100 select CRYPTO_HMAC 101 101 select CRYPTO_SHA256 102 102 select CRYPTO_SHA512 103 + select CRYPTO_DH 104 + select CRYPTO_DH_RFC7919_GROUPS 103 105 help 104 106 This provides support for NVMe over Fabrics In-Band Authentication. 105 107
+195 -6
drivers/nvme/host/auth.c
··· 18 18 struct work_struct auth_work; 19 19 struct nvme_ctrl *ctrl; 20 20 struct crypto_shash *shash_tfm; 21 + struct crypto_kpp *dh_tfm; 21 22 void *buf; 22 23 size_t buf_size; 23 24 int qid; ··· 34 33 u8 c2[64]; 35 34 u8 response[64]; 36 35 u8 *host_response; 36 + u8 *ctrl_key; 37 + int ctrl_key_len; 38 + u8 *host_key; 39 + int host_key_len; 40 + u8 *sess_key; 41 + int sess_key_len; 37 42 }; 38 43 39 44 #define nvme_auth_flags_from_qid(qid) \ ··· 144 137 struct nvmf_auth_dhchap_challenge_data *data = chap->buf; 145 138 u16 dhvlen = le16_to_cpu(data->dhvlen); 146 139 size_t size = sizeof(*data) + data->hl + dhvlen; 140 + const char *gid_name = nvme_auth_dhgroup_name(data->dhgid); 147 141 const char *hmac_name, *kpp_name; 148 142 149 143 if (chap->buf_size < size) { ··· 215 207 "qid %d: invalid DH group id %d\n", 216 208 chap->qid, data->dhgid); 217 209 chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; 210 + /* Leave previous dh_tfm intact */ 218 211 return NVME_SC_AUTH_REQUIRED; 219 212 } 220 213 214 + /* Clear host and controller key to avoid accidental reuse */ 215 + kfree_sensitive(chap->host_key); 216 + chap->host_key = NULL; 217 + chap->host_key_len = 0; 218 + kfree_sensitive(chap->ctrl_key); 219 + chap->ctrl_key = NULL; 220 + chap->ctrl_key_len = 0; 221 + 222 + if (chap->dhgroup_id == data->dhgid && 223 + (data->dhgid == NVME_AUTH_DHGROUP_NULL || chap->dh_tfm)) { 224 + dev_dbg(ctrl->device, 225 + "qid %d: reuse existing DH group %s\n", 226 + chap->qid, gid_name); 227 + goto skip_kpp; 228 + } 229 + 230 + /* Reset dh_tfm if it can't be reused */ 231 + if (chap->dh_tfm) { 232 + crypto_free_kpp(chap->dh_tfm); 233 + chap->dh_tfm = NULL; 234 + } 235 + 221 236 if (data->dhgid != NVME_AUTH_DHGROUP_NULL) { 222 - dev_warn(ctrl->device, 223 - "qid %d: unsupported DH group %s\n", 224 - chap->qid, kpp_name); 225 - chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; 226 - return NVME_SC_AUTH_REQUIRED; 237 + if (dhvlen == 0) { 238 + dev_warn(ctrl->device, 239 + "qid %d: empty DH value\n", 240 + chap->qid); 241 + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; 242 + return NVME_SC_INVALID_FIELD; 243 + } 244 + 245 + chap->dh_tfm = crypto_alloc_kpp(kpp_name, 0, 0); 246 + if (IS_ERR(chap->dh_tfm)) { 247 + int ret = PTR_ERR(chap->dh_tfm); 248 + 249 + dev_warn(ctrl->device, 250 + "qid %d: error %d initializing DH group %s\n", 251 + chap->qid, ret, gid_name); 252 + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; 253 + chap->dh_tfm = NULL; 254 + return NVME_SC_AUTH_REQUIRED; 255 + } 256 + dev_dbg(ctrl->device, "qid %d: selected DH group %s\n", 257 + chap->qid, gid_name); 227 258 } else if (dhvlen != 0) { 228 259 dev_warn(ctrl->device, 229 260 "qid %d: invalid DH value for NULL DH\n", ··· 272 225 } 273 226 chap->dhgroup_id = data->dhgid; 274 227 228 + skip_kpp: 275 229 chap->s1 = le32_to_cpu(data->seqnum); 276 230 memcpy(chap->c1, data->cval, chap->hash_len); 231 + if (dhvlen) { 232 + chap->ctrl_key = kmalloc(dhvlen, GFP_KERNEL); 233 + if (!chap->ctrl_key) { 234 + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; 235 + return NVME_SC_AUTH_REQUIRED; 236 + } 237 + chap->ctrl_key_len = dhvlen; 238 + memcpy(chap->ctrl_key, data->cval + chap->hash_len, 239 + dhvlen); 240 + dev_dbg(ctrl->device, "ctrl public key %*ph\n", 241 + (int)chap->ctrl_key_len, chap->ctrl_key); 242 + } 277 243 278 244 return 0; 279 245 } ··· 299 239 300 240 size += 2 * chap->hash_len; 301 241 242 + if (chap->host_key_len) 243 + size += chap->host_key_len; 244 + 302 245 if (chap->buf_size < size) { 303 246 chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; 304 247 return -EINVAL; ··· 312 249 data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY; 313 250 data->t_id = cpu_to_le16(chap->transaction); 314 251 data->hl = chap->hash_len; 315 - data->dhvlen = 0; 252 + data->dhvlen = cpu_to_le16(chap->host_key_len); 316 253 memcpy(data->rval, chap->response, chap->hash_len); 317 254 if (ctrl->ctrl_key) { 318 255 get_random_bytes(chap->c2, chap->hash_len); ··· 327 264 chap->s2 = 0; 328 265 } 329 266 data->seqnum = cpu_to_le32(chap->s2); 267 + if (chap->host_key_len) { 268 + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n", 269 + __func__, chap->qid, 270 + chap->host_key_len, chap->host_key); 271 + memcpy(data->rval + 2 * chap->hash_len, chap->host_key, 272 + chap->host_key_len); 273 + } 274 + 330 275 return size; 331 276 } 332 277 ··· 452 381 goto out; 453 382 } 454 383 384 + if (chap->dh_tfm) { 385 + challenge = kmalloc(chap->hash_len, GFP_KERNEL); 386 + if (!challenge) { 387 + ret = -ENOMEM; 388 + goto out; 389 + } 390 + ret = nvme_auth_augmented_challenge(chap->hash_id, 391 + chap->sess_key, 392 + chap->sess_key_len, 393 + chap->c1, challenge, 394 + chap->hash_len); 395 + if (ret) 396 + goto out; 397 + } 398 + 455 399 shash->tfm = chap->shash_tfm; 456 400 ret = crypto_shash_init(shash); 457 401 if (ret) ··· 529 443 goto out; 530 444 } 531 445 446 + if (chap->dh_tfm) { 447 + challenge = kmalloc(chap->hash_len, GFP_KERNEL); 448 + if (!challenge) { 449 + ret = -ENOMEM; 450 + goto out; 451 + } 452 + ret = nvme_auth_augmented_challenge(chap->hash_id, 453 + chap->sess_key, 454 + chap->sess_key_len, 455 + chap->c2, challenge, 456 + chap->hash_len); 457 + if (ret) 458 + goto out; 459 + } 532 460 dev_dbg(ctrl->device, "%s: qid %d ctrl response seq %u transaction %d\n", 533 461 __func__, chap->qid, chap->s2, chap->transaction); 534 462 dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n", ··· 592 492 return ret; 593 493 } 594 494 495 + static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl, 496 + struct nvme_dhchap_queue_context *chap) 497 + { 498 + int ret; 499 + 500 + if (chap->host_key && chap->host_key_len) { 501 + dev_dbg(ctrl->device, 502 + "qid %d: reusing host key\n", chap->qid); 503 + goto gen_sesskey; 504 + } 505 + ret = nvme_auth_gen_privkey(chap->dh_tfm, chap->dhgroup_id); 506 + if (ret < 0) { 507 + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; 508 + return ret; 509 + } 510 + 511 + chap->host_key_len = crypto_kpp_maxsize(chap->dh_tfm); 512 + 513 + chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL); 514 + if (!chap->host_key) { 515 + chap->host_key_len = 0; 516 + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; 517 + return -ENOMEM; 518 + } 519 + ret = nvme_auth_gen_pubkey(chap->dh_tfm, 520 + chap->host_key, chap->host_key_len); 521 + if (ret) { 522 + dev_dbg(ctrl->device, 523 + "failed to generate public key, error %d\n", ret); 524 + kfree(chap->host_key); 525 + chap->host_key = NULL; 526 + chap->host_key_len = 0; 527 + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; 528 + return ret; 529 + } 530 + 531 + gen_sesskey: 532 + chap->sess_key_len = chap->host_key_len; 533 + chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL); 534 + if (!chap->sess_key) { 535 + chap->sess_key_len = 0; 536 + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; 537 + return -ENOMEM; 538 + } 539 + 540 + ret = nvme_auth_gen_shared_secret(chap->dh_tfm, 541 + chap->ctrl_key, chap->ctrl_key_len, 542 + chap->sess_key, chap->sess_key_len); 543 + if (ret) { 544 + dev_dbg(ctrl->device, 545 + "failed to generate shared secret, error %d\n", ret); 546 + kfree_sensitive(chap->sess_key); 547 + chap->sess_key = NULL; 548 + chap->sess_key_len = 0; 549 + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; 550 + return ret; 551 + } 552 + dev_dbg(ctrl->device, "shared secret %*ph\n", 553 + (int)chap->sess_key_len, chap->sess_key); 554 + return 0; 555 + } 556 + 595 557 static void __nvme_auth_reset(struct nvme_dhchap_queue_context *chap) 596 558 { 559 + kfree_sensitive(chap->host_response); 560 + chap->host_response = NULL; 561 + kfree_sensitive(chap->host_key); 562 + chap->host_key = NULL; 563 + chap->host_key_len = 0; 564 + kfree_sensitive(chap->ctrl_key); 565 + chap->ctrl_key = NULL; 566 + chap->ctrl_key_len = 0; 567 + kfree_sensitive(chap->sess_key); 568 + chap->sess_key = NULL; 569 + chap->sess_key_len = 0; 597 570 chap->status = 0; 598 571 chap->error = 0; 599 572 chap->s1 = 0; ··· 681 508 __nvme_auth_reset(chap); 682 509 if (chap->shash_tfm) 683 510 crypto_free_shash(chap->shash_tfm); 511 + if (chap->dh_tfm) 512 + crypto_free_kpp(chap->dh_tfm); 513 + kfree_sensitive(chap->ctrl_key); 514 + kfree_sensitive(chap->host_key); 515 + kfree_sensitive(chap->sess_key); 684 516 kfree_sensitive(chap->host_response); 685 517 kfree(chap->buf); 686 518 kfree(chap); ··· 742 564 /* Invalid challenge parameters */ 743 565 chap->error = ret; 744 566 goto fail2; 567 + } 568 + 569 + if (chap->ctrl_key_len) { 570 + dev_dbg(ctrl->device, 571 + "%s: qid %d DH exponential\n", 572 + __func__, chap->qid); 573 + ret = nvme_auth_dhchap_exponential(ctrl, chap); 574 + if (ret) { 575 + chap->error = ret; 576 + goto fail2; 577 + } 745 578 } 746 579 747 580 dev_dbg(ctrl->device, "%s: qid %d host response\n",
+8
include/linux/nvme-auth.h
··· 29 29 void nvme_auth_free_key(struct nvme_dhchap_key *key); 30 30 u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn); 31 31 int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key); 32 + int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, 33 + u8 *challenge, u8 *aug, size_t hlen); 34 + int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid); 35 + int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, 36 + u8 *host_key, size_t host_key_len); 37 + int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, 38 + u8 *ctrl_key, size_t ctrl_key_len, 39 + u8 *sess_key, size_t sess_key_len); 32 40 33 41 #endif /* _NVME_AUTH_H */