[PATCH 1/1] libnvme: TLS PSK derivation fixes

Hannes Reinecke hare at suse.de
Sun Jul 20 23:36:01 PDT 2025


On 7/21/25 04:17, Chris Leech wrote:
> There are issues with the Retained and TLS PSK derivations due to the
> implementation not adhering to the RFC 8446 definition of the
> HKDF-Expand-Label function.
> 
>    1) The 16-bit HkdfLabel.length value must be converted to network byte
>       order.
> 
>    2) The variable length HkdfLabel.label and HkdfLabel.context vectors
>       must be prefixed with a length byte.
> 
> Signed-off-by: Chris Leech <cleech at redhat.com>
> ---
>   src/nvme/linux.c | 86 ++++++++++++++++++++++++++++++++----------------
>   1 file changed, 57 insertions(+), 29 deletions(-)
> 
> diff --git a/src/nvme/linux.c b/src/nvme/linux.c
> index ae4aa526..674b20b5 100644
> --- a/src/nvme/linux.c
> +++ b/src/nvme/linux.c
> @@ -618,6 +618,46 @@ static DEFINE_CLEANUP_FUNC(
>   	cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free)
>   #define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx)
>   
> +/*
> + * hkdf_info_printf()
> + *
> + * Helper function to append variable length label and context to an HkdfLabel
> + *
> + * RFC 8446 (TLS 1.3) Section 7.1 defines the HKDF-Expand-Label function as a
> + * specialization of the HKDF-Expand function (RFC 5869), where the info
> + * parameter is structured as an HkdfLabel.
> + *
> + * An HkdfLabel structure includes two variable length vectors (label and
> + * context) which must be preceded by their content length as per RFC 8446
> + * Section 3.4 (and not NUL terminated as per Section 7.1). Additionally,
> + * HkdfLabel.label must begin with "tls13 "
> + *
> + * Returns the number of bytes appended to the HKDF info buffer, or -1 on an
> + * error.
> + */
> +__attribute__((format(printf, 2, 3)))
> +static int hkdf_info_printf(EVP_PKEY_CTX *ctx, char *fmt, ...)
> +{
> +	_cleanup_free_ char *str;
> +	uint8_t len;
> +	int ret;
> +
> +	va_list myargs;
> +	va_start(myargs, fmt);
> +	ret = vasprintf(&str, fmt, myargs);
> +	va_end(myargs);
> +	if (ret < 0)
> +		return ret;
> +	if (ret > 255)
> +		return -1;
> +	len = ret;
> +	if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)&len, 1) <= 0)
> +		return -1;
> +	if (EVP_PKEY_CTX_add1_hkdf_info(ctx, (unsigned char *)str, len) <= 0)
> +		return -1;
> +	return (ret + 1);
> +}
> +
>   /*
>    * derive_retained_key()
>    *
> @@ -652,7 +692,7 @@ static int derive_retained_key(int hmac, const char *hostnqn,
>   			       size_t key_len)
>   {
>   	_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
> -	uint16_t length = key_len & 0xFFFF;
> +	uint16_t length = htons(key_len & 0xFFFF);
>   	const EVP_MD *md;
>   	size_t hmac_len;
>   
> @@ -690,18 +730,11 @@ static int derive_retained_key(int hmac, const char *hostnqn,
>   		errno = ENOKEY;
>   		return -1;
>   	}
> -	if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -			(const unsigned char *)"tls13 ", 6) <= 0) {
> -		errno = ENOKEY;
> -		return -1;
> -	}
> -	if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -			(const unsigned char *)"HostNQN", 7) <= 0) {
> +	if (hkdf_info_printf(ctx, "tls13 HostNQN") <= 0) {
>   		errno = ENOKEY;
>   		return -1;
>   	}
> -	if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -			(const unsigned char *)hostnqn, strlen(hostnqn)) <= 0) {
> +	if (hkdf_info_printf(ctx, "%s", hostnqn) <= 0) {
>   		errno = ENOKEY;
>   		return -1;
>   	}
> @@ -736,12 +769,13 @@ static int derive_retained_key(int hmac, const char *hostnqn,
>    *
>    * and the value '0' is invalid here.
>    */
> +
>   static int derive_tls_key(int version, unsigned char cipher,
>   			  const char *context, unsigned char *retained,
>   			  unsigned char *psk, size_t key_len)
>   {
>   	_cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL;
> -	uint16_t length = key_len & 0xFFFF;
> +	uint16_t length = htons(key_len & 0xFFFF);
>   	const EVP_MD *md;
>   	size_t hmac_len;
>   
> @@ -774,30 +808,24 @@ static int derive_tls_key(int version, unsigned char cipher,
>   		errno = ENOKEY;
>   		return -1;
>   	}
> -	if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -			(const unsigned char *)"tls13 ", 6) <= 0) {
> -		errno = ENOKEY;
> -		return -1;
> -	}
> -	if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -			(const unsigned char *)"nvme-tls-psk", 12) <= 0) {
> +	if (hkdf_info_printf(ctx, "tls13 nvme-tls-psk") <= 0) {
>   		errno = ENOKEY;
>   		return -1;
>   	}
> -	if (version == 1) {
> -		char hash_str[5];
> -
> -		sprintf(hash_str, "%02d ", cipher);
> -		if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -				(const unsigned char *)hash_str,
> -				strlen(hash_str)) <= 0) {
> +	switch (version) {
> +	case 0:
> +		if (hkdf_info_printf(ctx, "%s", context) <= 0) {
>   			errno = ENOKEY;
>   			return -1;
>   		}
> -	}
> -	if (EVP_PKEY_CTX_add1_hkdf_info(ctx,
> -			(const unsigned char *)context,
> -			strlen(context)) <= 0) {
> +		break;
> +	case 1:
> +		if (hkdf_info_printf(ctx, "%02d %s", cipher, context) <= 0) {
> +			errno = ENOKEY;
> +			return -1;
> +		}
> +		break;
> +	default:
>   		errno = ENOKEY;
>   		return -1;
>   	}

Hmm. I _thought_ we had it all fixed...
Reviewed-by: Hannes Reinecke <hare at suse.de>

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare at suse.de                                +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



More information about the Linux-nvme mailing list