diff mbox series

[4/9] nvme: add nvme_auth_derive_tls_psk()

Message ID 20240722142122.128258-5-hare@kernel.org
State Superseded
Headers show
Series nvme: implement secure concatenation | expand

Commit Message

Hannes Reinecke July 22, 2024, 2:21 p.m. UTC
Add a function to derive the TLS PSK as specified TP8018.

Signed-off-by: Hannes Reinecke <hare@kernel.org>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
---
 drivers/nvme/common/auth.c | 90 ++++++++++++++++++++++++++++++++++++++
 include/linux/nvme-auth.h  |  2 +
 2 files changed, 92 insertions(+)

Comments

Eric Biggers July 23, 2024, 1:47 a.m. UTC | #1
On Mon, Jul 22, 2024 at 04:21:17PM +0200, Hannes Reinecke wrote:
> +/*
> + * Derive a TLS PSK as specified in TP8018 Section 3.6.1.3:
> + *   TLS PSK and PSK identity Derivation
> + *
> + * The TLS PSK shall be derived as follows from an input PSK
> + * (i.e., either a retained PSK or a generated PSK) and a PSK
> + * identity using the HKDF-Extract and HKDF-Expand-Label operations
> + * (refer to RFC 5869 and RFC 8446) where the hash function is the
> + * one specified by the hash specifier of the PSK identity:
> + * 1. PRK = HKDF-Extract(0, Input PSK); and
> + * 2. TLS PSK = HKDF-Expand-Label(PRK, "nvme-tls-psk", PskIdentityContext, L),
> + * where PskIdentityContext is the hash identifier indicated in
> + * the PSK identity concatenated to a space character and to the
> + * Base64 PSK digest (i.e., "<hash> <PSK digest>") and L is the
> + * output size in bytes of the hash function (i.e., 32 for SHA-256
> + * and 48 for SHA-384).
> + */
> +int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len,
> +		u8 *psk_digest, u8 **ret_psk)
> +{
> +	struct crypto_shash *hmac_tfm;
> +	const char *hmac_name;
> +	const char *psk_prefix = "tls13 nvme-tls-psk";
> +	size_t info_len, prk_len;
> +	char *info;
> +	unsigned char *prk, *tls_key;
> +	int ret;
> +
> +	hmac_name = nvme_auth_hmac_name(hmac_id);
> +	if (!hmac_name) {
> +		pr_warn("%s: invalid hash algoritm %d\n",
> +			__func__, hmac_id);
> +		return -EINVAL;
> +	}
> +	if (hmac_id == NVME_AUTH_HASH_SHA512) {
> +		pr_warn("%s: unsupported hash algorithm %s\n",
> +			__func__, hmac_name);
> +		return -EINVAL;
> +	}
> +
> +	hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0);
> +	if (IS_ERR(hmac_tfm))
> +		return PTR_ERR(hmac_tfm);
> +
> +	prk_len = crypto_shash_digestsize(hmac_tfm);
> +	prk = kzalloc(prk_len, GFP_KERNEL);
> +	if (!prk) {
> +		ret = -ENOMEM;
> +		goto out_free_shash;
> +	}
> +
> +	ret = hkdf_extract(hmac_tfm, psk, psk_len, prk);
> +	if (ret)
> +		goto out_free_prk;
> +
> +	ret = crypto_shash_setkey(hmac_tfm, prk, prk_len);
> +	if (ret)
> +		goto out_free_prk;
> +
> +	info_len = strlen(psk_digest) + strlen(psk_prefix) + 1;
> +	info = kzalloc(info_len, GFP_KERNEL);
> +	if (!info)
> +		goto out_free_prk;
> +
> +	memcpy(info, psk_prefix, strlen(psk_prefix));
> +	memcpy(info + strlen(psk_prefix), psk_digest, strlen(psk_digest));

The code doesn't match the description given in the function comment (which
looks like it was quoted from a specification).

The code does HKDF-Expand with info="tls13 nvme-tls-psk<PSK digest>".

The description does HKDF-Expand-Label with Label="nvme-tls-psk",
Context="<hash identifier> <PSK digest>", Length=digest_size.

Not only does the code not actually use <hash identifier>, but it doesn't follow
the definition of HKDF-Expand-Label from RFC8446
(https://datatracker.ietf.org/doc/html/rfc8446#section-7.1) in that it's missing
all the length fields.  So the info string used by the actual code ends up being
quite different from the one specified.

- Eric
Hannes Reinecke July 23, 2024, 6:26 a.m. UTC | #2
On 7/23/24 03:47, Eric Biggers wrote:
> On Mon, Jul 22, 2024 at 04:21:17PM +0200, Hannes Reinecke wrote:
>> +/*
>> + * Derive a TLS PSK as specified in TP8018 Section 3.6.1.3:
>> + *   TLS PSK and PSK identity Derivation
>> + *
>> + * The TLS PSK shall be derived as follows from an input PSK
>> + * (i.e., either a retained PSK or a generated PSK) and a PSK
>> + * identity using the HKDF-Extract and HKDF-Expand-Label operations
>> + * (refer to RFC 5869 and RFC 8446) where the hash function is the
>> + * one specified by the hash specifier of the PSK identity:
>> + * 1. PRK = HKDF-Extract(0, Input PSK); and
>> + * 2. TLS PSK = HKDF-Expand-Label(PRK, "nvme-tls-psk", PskIdentityContext, L),
>> + * where PskIdentityContext is the hash identifier indicated in
>> + * the PSK identity concatenated to a space character and to the
>> + * Base64 PSK digest (i.e., "<hash> <PSK digest>") and L is the
>> + * output size in bytes of the hash function (i.e., 32 for SHA-256
>> + * and 48 for SHA-384).
>> + */
>> +int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len,
>> +		u8 *psk_digest, u8 **ret_psk)
>> +{
>> +	struct crypto_shash *hmac_tfm;
>> +	const char *hmac_name;
>> +	const char *psk_prefix = "tls13 nvme-tls-psk";
>> +	size_t info_len, prk_len;
>> +	char *info;
>> +	unsigned char *prk, *tls_key;
>> +	int ret;
>> +
>> +	hmac_name = nvme_auth_hmac_name(hmac_id);
>> +	if (!hmac_name) {
>> +		pr_warn("%s: invalid hash algoritm %d\n",
>> +			__func__, hmac_id);
>> +		return -EINVAL;
>> +	}
>> +	if (hmac_id == NVME_AUTH_HASH_SHA512) {
>> +		pr_warn("%s: unsupported hash algorithm %s\n",
>> +			__func__, hmac_name);
>> +		return -EINVAL;
>> +	}
>> +
>> +	hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0);
>> +	if (IS_ERR(hmac_tfm))
>> +		return PTR_ERR(hmac_tfm);
>> +
>> +	prk_len = crypto_shash_digestsize(hmac_tfm);
>> +	prk = kzalloc(prk_len, GFP_KERNEL);
>> +	if (!prk) {
>> +		ret = -ENOMEM;
>> +		goto out_free_shash;
>> +	}
>> +
>> +	ret = hkdf_extract(hmac_tfm, psk, psk_len, prk);
>> +	if (ret)
>> +		goto out_free_prk;
>> +
>> +	ret = crypto_shash_setkey(hmac_tfm, prk, prk_len);
>> +	if (ret)
>> +		goto out_free_prk;
>> +
>> +	info_len = strlen(psk_digest) + strlen(psk_prefix) + 1;
>> +	info = kzalloc(info_len, GFP_KERNEL);
>> +	if (!info)
>> +		goto out_free_prk;
>> +
>> +	memcpy(info, psk_prefix, strlen(psk_prefix));
>> +	memcpy(info + strlen(psk_prefix), psk_digest, strlen(psk_digest));
> 
> The code doesn't match the description given in the function comment (which
> looks like it was quoted from a specification).
> 
> The code does HKDF-Expand with info="tls13 nvme-tls-psk<PSK digest>".
> 
> The description does HKDF-Expand-Label with Label="nvme-tls-psk",
> Context="<hash identifier> <PSK digest>", Length=digest_size.
> 
> Not only does the code not actually use <hash identifier>, but it doesn't follow
> the definition of HKDF-Expand-Label from RFC8446
> (https://datatracker.ietf.org/doc/html/rfc8446#section-7.1) in that it's missing
> all the length fields.  So the info string used by the actual code ends up being
> quite different from the one specified.
> 
Aw. Guess you are right. Will be fixing it up.

Cheers,

Hannes
diff mbox series

Patch

diff --git a/drivers/nvme/common/auth.c b/drivers/nvme/common/auth.c
index 7e40f205d3e4..0b000a562c0f 100644
--- a/drivers/nvme/common/auth.c
+++ b/drivers/nvme/common/auth.c
@@ -684,5 +684,95 @@  int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len,
 }
 EXPORT_SYMBOL_GPL(nvme_auth_generate_digest);
 
+/*
+ * Derive a TLS PSK as specified in TP8018 Section 3.6.1.3:
+ *   TLS PSK and PSK identity Derivation
+ *
+ * The TLS PSK shall be derived as follows from an input PSK
+ * (i.e., either a retained PSK or a generated PSK) and a PSK
+ * identity using the HKDF-Extract and HKDF-Expand-Label operations
+ * (refer to RFC 5869 and RFC 8446) where the hash function is the
+ * one specified by the hash specifier of the PSK identity:
+ * 1. PRK = HKDF-Extract(0, Input PSK); and
+ * 2. TLS PSK = HKDF-Expand-Label(PRK, "nvme-tls-psk", PskIdentityContext, L),
+ * where PskIdentityContext is the hash identifier indicated in
+ * the PSK identity concatenated to a space character and to the
+ * Base64 PSK digest (i.e., "<hash> <PSK digest>") and L is the
+ * output size in bytes of the hash function (i.e., 32 for SHA-256
+ * and 48 for SHA-384).
+ */
+int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len,
+		u8 *psk_digest, u8 **ret_psk)
+{
+	struct crypto_shash *hmac_tfm;
+	const char *hmac_name;
+	const char *psk_prefix = "tls13 nvme-tls-psk";
+	size_t info_len, prk_len;
+	char *info;
+	unsigned char *prk, *tls_key;
+	int ret;
+
+	hmac_name = nvme_auth_hmac_name(hmac_id);
+	if (!hmac_name) {
+		pr_warn("%s: invalid hash algoritm %d\n",
+			__func__, hmac_id);
+		return -EINVAL;
+	}
+	if (hmac_id == NVME_AUTH_HASH_SHA512) {
+		pr_warn("%s: unsupported hash algorithm %s\n",
+			__func__, hmac_name);
+		return -EINVAL;
+	}
+
+	hmac_tfm = crypto_alloc_shash(hmac_name, 0, 0);
+	if (IS_ERR(hmac_tfm))
+		return PTR_ERR(hmac_tfm);
+
+	prk_len = crypto_shash_digestsize(hmac_tfm);
+	prk = kzalloc(prk_len, GFP_KERNEL);
+	if (!prk) {
+		ret = -ENOMEM;
+		goto out_free_shash;
+	}
+
+	ret = hkdf_extract(hmac_tfm, psk, psk_len, prk);
+	if (ret)
+		goto out_free_prk;
+
+	ret = crypto_shash_setkey(hmac_tfm, prk, prk_len);
+	if (ret)
+		goto out_free_prk;
+
+	info_len = strlen(psk_digest) + strlen(psk_prefix) + 1;
+	info = kzalloc(info_len, GFP_KERNEL);
+	if (!info)
+		goto out_free_prk;
+
+	memcpy(info, psk_prefix, strlen(psk_prefix));
+	memcpy(info + strlen(psk_prefix), psk_digest, strlen(psk_digest));
+
+	tls_key = kzalloc(psk_len, GFP_KERNEL);
+	if (!tls_key) {
+		ret = -ENOMEM;
+		goto out_free_info;
+	}
+	ret = hkdf_expand(hmac_tfm, info, strlen(info), tls_key, psk_len);
+	if (ret) {
+		kfree(tls_key);
+		goto out_free_info;
+	}
+	*ret_psk = tls_key;
+
+out_free_info:
+	kfree(info);
+out_free_prk:
+	kfree(prk);
+out_free_shash:
+	crypto_free_shash(hmac_tfm);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_derive_tls_psk);
+
 MODULE_DESCRIPTION("NVMe Authentication framework");
 MODULE_LICENSE("GPL v2");
diff --git a/include/linux/nvme-auth.h b/include/linux/nvme-auth.h
index 998f06bf10fd..60e069a6757f 100644
--- a/include/linux/nvme-auth.h
+++ b/include/linux/nvme-auth.h
@@ -45,5 +45,7 @@  int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len,
 			   u8 **ret_psk, size_t *ret_len);
 int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len,
 		char *subsysnqn, char *hostnqn, u8 **ret_digest);
+int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len,
+		u8 *psk_digest, u8 **ret_psk);
 
 #endif /* _NVME_AUTH_H */