|
15 | 15 | #include <linux/nvme.h> |
16 | 16 | #include <linux/nvme-auth.h> |
17 | 17 |
|
| 18 | +#define HKDF_MAX_HASHLEN 64 |
| 19 | + |
18 | 20 | static u32 nvme_dhchap_seqnum; |
19 | 21 | static DEFINE_MUTEX(nvme_dhchap_mutex); |
20 | 22 |
|
@@ -692,5 +694,119 @@ int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len, |
692 | 694 | } |
693 | 695 | EXPORT_SYMBOL_GPL(nvme_auth_generate_digest); |
694 | 696 |
|
| 697 | +/** |
| 698 | + * nvme_auth_derive_tls_psk - Derive TLS PSK |
| 699 | + * @hmac_id: Hash function identifier |
| 700 | + * @psk: generated input PSK |
| 701 | + * @psk_len: size of @psk |
| 702 | + * @psk_digest: TLS PSK digest |
| 703 | + * @ret_psk: Pointer to the resulting TLS PSK |
| 704 | + * |
| 705 | + * Derive a TLS PSK as specified in TP8018 Section 3.6.1.3: |
| 706 | + * TLS PSK and PSK identity Derivation |
| 707 | + * |
| 708 | + * The TLS PSK shall be derived as follows from an input PSK |
| 709 | + * (i.e., either a retained PSK or a generated PSK) and a PSK |
| 710 | + * identity using the HKDF-Extract and HKDF-Expand-Label operations |
| 711 | + * (refer to RFC 5869 and RFC 8446) where the hash function is the |
| 712 | + * one specified by the hash specifier of the PSK identity: |
| 713 | + * 1. PRK = HKDF-Extract(0, Input PSK); and |
| 714 | + * 2. TLS PSK = HKDF-Expand-Label(PRK, "nvme-tls-psk", PskIdentityContext, L), |
| 715 | + * where PskIdentityContext is the hash identifier indicated in |
| 716 | + * the PSK identity concatenated to a space character and to the |
| 717 | + * Base64 PSK digest (i.e., "<hash> <PSK digest>") and L is the |
| 718 | + * output size in bytes of the hash function (i.e., 32 for SHA-256 |
| 719 | + * and 48 for SHA-384). |
| 720 | + * |
| 721 | + * Returns 0 on success with a valid psk pointer in @ret_psk or a negative |
| 722 | + * error number otherwise. |
| 723 | + */ |
| 724 | +int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len, |
| 725 | + u8 *psk_digest, u8 **ret_psk) |
| 726 | +{ |
| 727 | + struct crypto_shash *hmac_tfm; |
| 728 | + const char *hmac_name; |
| 729 | + const char *psk_prefix = "tls13 nvme-tls-psk"; |
| 730 | + static const char default_salt[HKDF_MAX_HASHLEN]; |
| 731 | + size_t info_len, prk_len; |
| 732 | + char *info; |
| 733 | + unsigned char *prk, *tls_key; |
| 734 | + int ret; |
| 735 | + |
| 736 | + hmac_name = nvme_auth_hmac_name(hmac_id); |
| 737 | + if (!hmac_name) { |
| 738 | + pr_warn("%s: invalid hash algorithm %d\n", |
| 739 | + __func__, hmac_id); |
| 740 | + return -EINVAL; |
| 741 | + } |
| 742 | + if (hmac_id == NVME_AUTH_HASH_SHA512) { |
| 743 | + pr_warn("%s: unsupported hash algorithm %s\n", |
| 744 | + __func__, hmac_name); |
| 745 | + return -EINVAL; |
| 746 | + } |
| 747 | + |
| 748 | + hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0); |
| 749 | + if (IS_ERR(hmac_tfm)) |
| 750 | + return PTR_ERR(hmac_tfm); |
| 751 | + |
| 752 | + prk_len = crypto_shash_digestsize(hmac_tfm); |
| 753 | + prk = kzalloc(prk_len, GFP_KERNEL); |
| 754 | + if (!prk) { |
| 755 | + ret = -ENOMEM; |
| 756 | + goto out_free_shash; |
| 757 | + } |
| 758 | + |
| 759 | + if (WARN_ON(prk_len > HKDF_MAX_HASHLEN)) { |
| 760 | + ret = -EINVAL; |
| 761 | + goto out_free_prk; |
| 762 | + } |
| 763 | + ret = hkdf_extract(hmac_tfm, psk, psk_len, |
| 764 | + default_salt, prk_len, prk); |
| 765 | + if (ret) |
| 766 | + goto out_free_prk; |
| 767 | + |
| 768 | + ret = crypto_shash_setkey(hmac_tfm, prk, prk_len); |
| 769 | + if (ret) |
| 770 | + goto out_free_prk; |
| 771 | + |
| 772 | + /* |
| 773 | + * 2 addtional bytes for the length field from HDKF-Expand-Label, |
| 774 | + * 2 addtional bytes for the HMAC ID, and one byte for the space |
| 775 | + * separator. |
| 776 | + */ |
| 777 | + info_len = strlen(psk_digest) + strlen(psk_prefix) + 5; |
| 778 | + info = kzalloc(info_len + 1, GFP_KERNEL); |
| 779 | + if (!info) { |
| 780 | + ret = -ENOMEM; |
| 781 | + goto out_free_prk; |
| 782 | + } |
| 783 | + |
| 784 | + put_unaligned_be16(psk_len, info); |
| 785 | + memcpy(info + 2, psk_prefix, strlen(psk_prefix)); |
| 786 | + sprintf(info + 2 + strlen(psk_prefix), "%02d %s", hmac_id, psk_digest); |
| 787 | + |
| 788 | + tls_key = kzalloc(psk_len, GFP_KERNEL); |
| 789 | + if (!tls_key) { |
| 790 | + ret = -ENOMEM; |
| 791 | + goto out_free_info; |
| 792 | + } |
| 793 | + ret = hkdf_expand(hmac_tfm, info, info_len, tls_key, psk_len); |
| 794 | + if (ret) { |
| 795 | + kfree(tls_key); |
| 796 | + goto out_free_info; |
| 797 | + } |
| 798 | + *ret_psk = tls_key; |
| 799 | + |
| 800 | +out_free_info: |
| 801 | + kfree(info); |
| 802 | +out_free_prk: |
| 803 | + kfree(prk); |
| 804 | +out_free_shash: |
| 805 | + crypto_free_shash(hmac_tfm); |
| 806 | + |
| 807 | + return ret; |
| 808 | +} |
| 809 | +EXPORT_SYMBOL_GPL(nvme_auth_derive_tls_psk); |
| 810 | + |
695 | 811 | MODULE_DESCRIPTION("NVMe Authentication framework"); |
696 | 812 | MODULE_LICENSE("GPL v2"); |
0 commit comments