Skip to contentSkip to conversation

crypto,nvme: fixup HKDF-Expand-Label implementation

PATCH In review [email protected] 5 participants 7 messages Aug 20, 2025 – Aug 21, 2025

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover crypto,nvme: fixup HKDF-Expand-Label implementation

From: Hannes Reinecke <[email protected]>

As per RFC 8446 (TLS 1.3) the HKDF-Expand-Label function is using vectors for the 'label' and 'context' field, but defines these vectors as a string prefixed with the string length (in binary). The implementation in nvme is missing the length prefix which was causing interoperability issues with spec-conformant implementations.

This patchset adds a function 'hkdf_expand_label()' to correctly implement the HKDF-Expand-Label functionality and modifies the nvme driver to utilize this function instead of the open-coded implementation.

As usual, comments and reviews are welcome.

Chris Leech (1):
crypto: hkdf: add hkdf_expand_label()

Hannes Reinecke (1):
nvme-auth: use hkdf_expand_label()

 crypto/hkdf.c              | 55 ++++++++++++++++++++++++++++++++++++++
 drivers/nvme/common/auth.c | 33 +++++++++--------------
 include/crypto/hkdf.h      |  4 +++
 3 files changed, 72 insertions(+), 20 deletions(-)
Signature · 1 line
2.43.0
#1 lore raw

PATCH 1/2 crypto: hkdf: add hkdf_expand_label()

From: Chris Leech <[email protected]>

Provide an implementation of RFC 8446 (TLS 1.3) HKDF-Expand-Label

---
 crypto/hkdf.c         | 55 +++++++++++++++++++++++++++++++++++++++++++
 include/crypto/hkdf.h |  4 ++++
 2 files changed, 59 insertions(+)
crypto/hkdf.c +55 -0
index 82d1b32ca6ce..465bad6e6c93 100644
@@ -11,6 +11,7 @@
 #include <crypto/sha2.h>
 #include <crypto/hkdf.h>
 #include <linux/module.h>
+#include <linux/unaligned.h>
 
 /*
  * HKDF consists of two steps:
@@ -129,6 +130,60 @@ int hkdf_expand(struct crypto_shash *hmac_tfm,
 }
 EXPORT_SYMBOL_GPL(hkdf_expand);
 
+/**
+ * hkdf_expand_label - HKDF-Expand-Label (RFC 8846 section 7.1)
+ * @hmac_tfm: hash context keyed with pseudorandom key
+ * @label: ASCII label without "tls13 " prefix
+ * @label_len: length of @label
+ * @context: context bytes
+ * @contextlen: length of @context
+ * @okm: output keying material
+ * @okmlen: length of @okm
+ *
+ * Build the TLS 1.3 HkdfLabel structure and invoke hkdf_expand().
+ *
+ * Returns 0 on success with output keying material stored in @okm,
+ * or a negative errno value otherwise.
+ */
+int hkdf_expand_label(struct crypto_shash *hmac_tfm,
+		const u8 *label, unsigned int labellen,
+		const u8 *context, unsigned int contextlen,
+		u8 *okm, unsigned int okmlen)
+{
+	int err;
+	u8 *info;
+	unsigned int infolen;
+	static const char tls13_prefix[] = "tls13 ";
+	unsigned int prefixlen = sizeof(tls13_prefix) - 1; /* exclude NUL */
+
+	if (WARN_ON(labellen > (255 - prefixlen)))
+		return -EINVAL;
+	if (WARN_ON(contextlen > 255))
+		return -EINVAL;
+
+	infolen = 2 + (1 + prefixlen + labellen) + (1 + contextlen);
+	info = kzalloc(infolen, GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	/* HkdfLabel.Length */
+	put_unaligned_be16(okmlen, info);
+
+	/* HkdfLabel.Label */
+	info[2] = prefixlen + labellen;
+	memcpy(info + 3, tls13_prefix, prefixlen);
+	memcpy(info + 3 + prefixlen, label, labellen);
+
+	/* HkdfLabel.Context */
+	info[3 + prefixlen + labellen] = contextlen;
+	memcpy(info + 4 + prefixlen + labellen, context, contextlen);
+
+	err = hkdf_expand(hmac_tfm, info, infolen, okm, okmlen);
+	kfree_sensitive(info);
+	return err;
+}
+EXPORT_SYMBOL_GPL(hkdf_expand_label);
+
 struct hkdf_testvec {
 	const char *test;
 	const u8 *ikm;
include/crypto/hkdf.h +4 -0
index 6a9678f508f5..5e75d17a58ab 100644
@@ -17,4 +17,8 @@ int hkdf_extract(struct crypto_shash *hmac_tfm, const u8 *ikm,
 int hkdf_expand(struct crypto_shash *hmac_tfm,
 		const u8 *info, unsigned int infolen,
 		u8 *okm, unsigned int okmlen);
+int hkdf_expand_label(struct crypto_shash *hmac_tfm,
+		const u8 *label, unsigned int labellen,
+		const u8 *context, unsigned int contextlen,
+		u8 *okm, unsigned int okmlen);
 #endif
Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH 1/2] crypto: hkdf: add hkdf_expand_label()

Quoted 71 lines · On Wed, Aug 20, 2025 at 11:12:10AM +0200, [email protected] wrote:

From: Chris Leech <[email protected]>

Provide an implementation of RFC 8446 (TLS 1.3) HKDF-Expand-Label

---
 crypto/hkdf.c         | 55 +++++++++++++++++++++++++++++++++++++++++++
 include/crypto/hkdf.h |  4 ++++
 2 files changed, 59 insertions(+)
crypto/hkdf.c +44 -0
index 82d1b32ca6ce..465bad6e6c93 100644
@@ -11,6 +11,7 @@
 #include <crypto/sha2.h>
 #include <crypto/hkdf.h>
 #include <linux/module.h>
+#include <linux/unaligned.h>
 
 /*
  * HKDF consists of two steps:
@@ -129,6 +130,60 @@ int hkdf_expand(struct crypto_shash *hmac_tfm,
 }
 EXPORT_SYMBOL_GPL(hkdf_expand);
 
+/**
+ * hkdf_expand_label - HKDF-Expand-Label (RFC 8846 section 7.1)
+ * @hmac_tfm: hash context keyed with pseudorandom key
+ * @label: ASCII label without "tls13 " prefix
+ * @label_len: length of @label
+ * @context: context bytes
+ * @contextlen: length of @context
+ * @okm: output keying material
+ * @okmlen: length of @okm
+ *
+ * Build the TLS 1.3 HkdfLabel structure and invoke hkdf_expand().
+ *
+ * Returns 0 on success with output keying material stored in @okm,
+ * or a negative errno value otherwise.
+ */
+int hkdf_expand_label(struct crypto_shash *hmac_tfm,
+		const u8 *label, unsigned int labellen,
+		const u8 *context, unsigned int contextlen,
+		u8 *okm, unsigned int okmlen)
+{
+	int err;
+	u8 *info;
+	unsigned int infolen;
+	static const char tls13_prefix[] = "tls13 ";
+	unsigned int prefixlen = sizeof(tls13_prefix) - 1; /* exclude NUL */
+
+	if (WARN_ON(labellen > (255 - prefixlen)))
+		return -EINVAL;
+	if (WARN_ON(contextlen > 255))
+		return -EINVAL;
+
+	infolen = 2 + (1 + prefixlen + labellen) + (1 + contextlen);
+	info = kzalloc(infolen, GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	/* HkdfLabel.Length */
+	put_unaligned_be16(okmlen, info);
+
+	/* HkdfLabel.Label */
+	info[2] = prefixlen + labellen;
+	memcpy(info + 3, tls13_prefix, prefixlen);
+	memcpy(info + 3 + prefixlen, label, labellen);

crypto/hkdf.c int hkdf_expand(struct crypto_shash *hmac_tfm, · line 185

Quoted 10 lines
crypto/hkdf.c +10 -0
+
+	/* HkdfLabel.Context */
+	info[3 + prefixlen + labellen] = contextlen;
+	memcpy(info + 4 + prefixlen + labellen, context, contextlen);
+
+	err = hkdf_expand(hmac_tfm, info, infolen, okm, okmlen);
+	kfree_sensitive(info);
+	return err;
+}
+EXPORT_SYMBOL_GPL(hkdf_expand_label);

Does this belong in crypto/hkdf.c? It seems to be specific to a particular user of HKDF.

- Eric

#3 lore raw

Re: [PATCH 1/2] crypto: hkdf: add hkdf_expand_label()

Quoted 13 lines · Eric Biggers
Quoted 12 lines · On Wed, Aug 20, 2025 at 11:12:10AM +0200, [email protected] wrote:

From: Chris Leech <[email protected]>

Provide an implementation of RFC 8446 (TLS 1.3) HKDF-Expand-Label

---
 crypto/hkdf.c         | 55 +++++++++++++++++++++++++++++++++++++++++++
 include/crypto/hkdf.h |  4 ++++
 2 files changed, 59 insertions(+)
Quoted 4 lines
Quoted 1 line

...

Does this belong in crypto/hkdf.c? It seems to be specific to a particular user of HKDF.

While this is needed for NVMe/TLS, it's a case of the NVMe specifications referencing a function defined in the TLS 1.3 RFC to be used. I though it would be clearest to fix the open-coded implemenation by creating an RFC complient function, which is now no-longer specific to NVMe so I moved it out to crypto/hkdf.c

I don't know that there will be other users, it just seemed to make the most sense there.

- Chris

#4 lore raw

Re: [PATCH 1/2] crypto: hkdf: add hkdf_expand_label()

Quoted 19 lines · Chris Leech
Quoted 13 lines · Eric Biggers
Quoted 12 lines · On Wed, Aug 20, 2025 at 11:12:10AM +0200, [email protected] wrote:

From: Chris Leech <[email protected]>

Provide an implementation of RFC 8446 (TLS 1.3) HKDF-Expand-Label

---
  crypto/hkdf.c         | 55 +++++++++++++++++++++++++++++++++++++++++++
  include/crypto/hkdf.h |  4 ++++
  2 files changed, 59 insertions(+)
Quoted 4 lines
Quoted 1 line

...

Does this belong in crypto/hkdf.c? It seems to be specific to a particular user of HKDF.

Quoted 9 lines

While this is needed for NVMe/TLS, it's a case of the NVMe specifications referencing a function defined in the TLS 1.3 RFC to be used. I though it would be clearest to fix the open-coded implemenation by creating an RFC complient function, which is now no-longer specific to NVMe so I moved it out to crypto/hkdf.c

I don't know that there will be other users, it just seemed to make the most sense there.

But having said that, we can easily move it into the nvme code, and let others move it into crypto if there is a need.
Will be updating the patchset.

Cheers,

Hannes

Signature · 4 lines
Dr. Hannes Reinecke                  Kernel Storage Architect
[email protected]                                +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich
#5 lore raw

Re: [PATCH 1/2] crypto: hkdf: add hkdf_expand_label()

In reply to [email protected]
kernel test robot noticed the following build warnings:

Hi,

kernel test robot noticed the following build warnings:

[auto build test WARNING on herbert-cryptodev-2.6/master]
[also build test WARNING on herbert-crypto-2.6/master linus/master v6.17-rc2 next-20250820] [cannot apply to hch-configfs/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/hare-kernel-org/crypto-hkdf-add-hkdf_expand_label/20250820-172750
base:   https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git master
patch link:    https://lore.kernel.org/r/20250820091211.25368-2-hare%40kernel.org
patch subject: [PATCH 1/2] crypto: hkdf: add hkdf_expand_label()
config: loongarch-randconfig-001-20250821 (https://download.01.org/0day-ci/archive/20250821/[email protected]/config)
compiler: loongarch64-linux-gcc (GCC) 14.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250821/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All warnings (new ones prefixed by >>):

>> Warning: crypto/hkdf.c:151 function parameter 'labellen' not described in 'hkdf_expand_label'
Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#6 lore raw

PATCH 2/2 nvme-auth: use hkdf_expand_label()

In reply to [email protected]

From: Hannes Reinecke <[email protected]>

When generating keying material during an authentication transaction (secure channel concatenation), the HKDF-Expand-Label function is part of the specified key derivation process.

The current open-coded implementation misses the length prefix requirements on the HkdfLabel label and context variable-length vectors (RFC 8446 Section 3.4).

Instead, use the hkdf_expand_label() function.

---
 drivers/nvme/common/auth.c | 33 +++++++++++++--------------------
 1 file changed, 13 insertions(+), 20 deletions(-)
drivers/nvme/common/auth.c +13 -20
index 91e273b89fea..5ea4d6d9a394 100644
@@ -715,10 +715,10 @@ int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len,
 {
 	struct crypto_shash *hmac_tfm;
 	const char *hmac_name;
-	const char *psk_prefix = "tls13 nvme-tls-psk";
+	const char *label = "nvme-tls-psk";
 	static const char default_salt[HKDF_MAX_HASHLEN];
-	size_t info_len, prk_len;
-	char *info;
+	size_t prk_len;
+	const char *ctx;
 	unsigned char *prk, *tls_key;
 	int ret;
 
@@ -758,36 +758,29 @@ int nvme_auth_derive_tls_psk(int hmac_id, u8 *psk, size_t psk_len,
 	if (ret)
 		goto out_free_prk;
 
-	/*
-	 * 2 additional bytes for the length field from HDKF-Expand-Label,
-	 * 2 additional bytes for the HMAC ID, and one byte for the space
-	 * separator.
-	 */
-	info_len = strlen(psk_digest) + strlen(psk_prefix) + 5;
-	info = kzalloc(info_len + 1, GFP_KERNEL);
-	if (!info) {
+	ctx = kasprintf(GFP_KERNEL, "%02d %s", hmac_id, psk_digest);
+	if (!ctx) {
 		ret = -ENOMEM;
 		goto out_free_prk;
 	}
 
-	put_unaligned_be16(psk_len, info);
-	memcpy(info + 2, psk_prefix, strlen(psk_prefix));
-	sprintf(info + 2 + strlen(psk_prefix), "%02d %s", hmac_id, psk_digest);
-
 	tls_key = kzalloc(psk_len, GFP_KERNEL);
 	if (!tls_key) {
 		ret = -ENOMEM;
-		goto out_free_info;
+		goto out_free_ctx;
 	}
-	ret = hkdf_expand(hmac_tfm, info, info_len, tls_key, psk_len);
+	ret = hkdf_expand_label(hmac_tfm,
+				label, strlen(label),
+				ctx, strlen(ctx),
+				tls_key, psk_len);
 	if (ret) {
 		kfree(tls_key);
-		goto out_free_info;
+		goto out_free_ctx;
 	}
 	*ret_psk = tls_key;
 
-out_free_info:
-	kfree(info);
+out_free_ctx:
+	kfree(ctx);
 out_free_prk:
 	kfree(prk);
 out_free_shash:
Signature · 1 line
2.43.0