|
11 | 11 | #include <linux/unaligned.h> |
12 | 12 | #include <crypto/hash.h> |
13 | 13 | #include <crypto/dh.h> |
| 14 | +#include <crypto/hkdf.h> |
14 | 15 | #include <linux/nvme.h> |
15 | 16 | #include <linux/nvme-auth.h> |
16 | 17 |
|
@@ -471,5 +472,91 @@ int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) |
471 | 472 | } |
472 | 473 | EXPORT_SYMBOL_GPL(nvme_auth_generate_key); |
473 | 474 |
|
| 475 | +/** |
| 476 | + * nvme_auth_generate_psk - Generate a PSK for TLS |
| 477 | + * @hmac_id: Hash function identifier |
| 478 | + * @skey: Session key |
| 479 | + * @skey_len: Length of @skey |
| 480 | + * @c1: Value of challenge C1 |
| 481 | + * @c2: Value of challenge C2 |
| 482 | + * @hash_len: Hash length of the hash algorithm |
| 483 | + * @ret_psk: Pointer too the resulting generated PSK |
| 484 | + * @ret_len: length of @ret_psk |
| 485 | + * |
| 486 | + * Generate a PSK for TLS as specified in NVMe base specification, section |
| 487 | + * 8.13.5.9: Generated PSK for TLS |
| 488 | + * |
| 489 | + * The generated PSK for TLS shall be computed applying the HMAC function |
| 490 | + * using the hash function H( ) selected by the HashID parameter in the |
| 491 | + * DH-HMAC-CHAP_Challenge message with the session key KS as key to the |
| 492 | + * concatenation of the two challenges C1 and C2 (i.e., generated |
| 493 | + * PSK = HMAC(KS, C1 || C2)). |
| 494 | + * |
| 495 | + * Returns 0 on success with a valid generated PSK pointer in @ret_psk and |
| 496 | + * the length of @ret_psk in @ret_len, or a negative error number otherwise. |
| 497 | + */ |
| 498 | +int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len, |
| 499 | + u8 *c1, u8 *c2, size_t hash_len, u8 **ret_psk, size_t *ret_len) |
| 500 | +{ |
| 501 | + struct crypto_shash *tfm; |
| 502 | + SHASH_DESC_ON_STACK(shash, tfm); |
| 503 | + u8 *psk; |
| 504 | + const char *hmac_name; |
| 505 | + int ret, psk_len; |
| 506 | + |
| 507 | + if (!c1 || !c2) |
| 508 | + return -EINVAL; |
| 509 | + |
| 510 | + hmac_name = nvme_auth_hmac_name(hmac_id); |
| 511 | + if (!hmac_name) { |
| 512 | + pr_warn("%s: invalid hash algorithm %d\n", |
| 513 | + __func__, hmac_id); |
| 514 | + return -EINVAL; |
| 515 | + } |
| 516 | + |
| 517 | + tfm = crypto_alloc_shash(hmac_name, 0, 0); |
| 518 | + if (IS_ERR(tfm)) |
| 519 | + return PTR_ERR(tfm); |
| 520 | + |
| 521 | + psk_len = crypto_shash_digestsize(tfm); |
| 522 | + psk = kzalloc(psk_len, GFP_KERNEL); |
| 523 | + if (!psk) { |
| 524 | + ret = -ENOMEM; |
| 525 | + goto out_free_tfm; |
| 526 | + } |
| 527 | + |
| 528 | + shash->tfm = tfm; |
| 529 | + ret = crypto_shash_setkey(tfm, skey, skey_len); |
| 530 | + if (ret) |
| 531 | + goto out_free_psk; |
| 532 | + |
| 533 | + ret = crypto_shash_init(shash); |
| 534 | + if (ret) |
| 535 | + goto out_free_psk; |
| 536 | + |
| 537 | + ret = crypto_shash_update(shash, c1, hash_len); |
| 538 | + if (ret) |
| 539 | + goto out_free_psk; |
| 540 | + |
| 541 | + ret = crypto_shash_update(shash, c2, hash_len); |
| 542 | + if (ret) |
| 543 | + goto out_free_psk; |
| 544 | + |
| 545 | + ret = crypto_shash_final(shash, psk); |
| 546 | + if (!ret) { |
| 547 | + *ret_psk = psk; |
| 548 | + *ret_len = psk_len; |
| 549 | + } |
| 550 | + |
| 551 | +out_free_psk: |
| 552 | + if (ret) |
| 553 | + kfree_sensitive(psk); |
| 554 | +out_free_tfm: |
| 555 | + crypto_free_shash(tfm); |
| 556 | + |
| 557 | + return ret; |
| 558 | +} |
| 559 | +EXPORT_SYMBOL_GPL(nvme_auth_generate_psk); |
| 560 | + |
474 | 561 | MODULE_DESCRIPTION("NVMe Authentication framework"); |
475 | 562 | MODULE_LICENSE("GPL v2"); |
0 commit comments