From a90c450d7e9b741affbca89be8f2d520103570f9 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 19 Aug 2025 14:42:42 +0200 Subject: [PATCH 1/5] linux: set errno when nvme_generate_tls_key_identity() fails If nvme_generate_tls_key_identity() fails we should set errno to return a more detailed error to the caller. Signed-off-by: Hannes Reinecke --- src/nvme/linux.c | 13 ++++++++++--- src/nvme/linux.h | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/nvme/linux.c b/src/nvme/linux.c index cdbb91254..4c8d3140f 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -1156,22 +1156,29 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, int ret = -1; identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); - if (identity_len < 0) + if (identity_len < 0) { + errno = EINVAL; return NULL; + } identity = malloc(identity_len); - if (!identity) + if (!identity) { + errno = ENOMEM; return NULL; + } psk = malloc(key_len); - if (!psk) + if (!psk) { + errno = ENOMEM; goto out_free_identity; + } memset(psk, 0, key_len); ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, configured_key, psk, key_len); out_free_identity: if (ret < 0) { + errno = -ret; free(identity); identity = NULL; } diff --git a/src/nvme/linux.h b/src/nvme/linux.h index 330fa9fd3..279309a4e 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -413,7 +413,8 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, * generate the corresponding TLs identity. * * Return: The string containing the TLS identity. It is the responsibility - * of the caller to free the returned string. + * of the caller to free the returned string. On error NULL is returned with + * errno set. */ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, int version, int hmac, From 2c692a5a28c00c098b746967533176f211a574ca Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 19 Aug 2025 13:16:29 +0200 Subject: [PATCH 2/5] test/psk: add testcase for TLS identity derivation Add a testcase for TLS identity derivation to catch errors from the HKDF-Expand-Label algorithm changes. Signed-off-by: Hannes Reinecke --- test/psk.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 14 deletions(-) diff --git a/test/psk.c b/test/psk.c index 02cb6d813..24a94a6e5 100644 --- a/test/psk.c +++ b/test/psk.c @@ -15,7 +15,7 @@ static int test_rc; -struct test_data { +struct test_data_psk { const unsigned char configured_psk[48]; size_t psk_length; unsigned char version; @@ -23,7 +23,7 @@ struct test_data { const char *exported_psk; }; -static struct test_data test_data[] = { +static struct test_data_psk test_data_psk[] = { { { 0x55, 0x12, 0xDB, 0xB6, 0x73, 0x7D, 0x01, 0x06, 0xF6, 0x59, 0x75, 0xB7, @@ -60,6 +60,45 @@ static struct test_data test_data[] = { "NVMeTLSkey-1:02:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwP/w0S89ELi3W2LxIcLXVsDn8kXZQ==:" }, }; +struct test_data_identity { + const unsigned char configured_psk[48]; + size_t psk_length; + unsigned char version; + unsigned char hmac; + const char *hostnqn; + const char *subsysnqn; + const char *identity; +}; + +static struct test_data_identity test_data_identity[] = { + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 32, 1, NVME_HMAC_ALG_SHA2_256, + "nqn.psk-test-host", "nqn.psk-test-subsys", + "NVMe1R01 nqn.psk-test-host nqn.psk-test-subsys 66GuqV08TsAGII39teWUfwQwizjv06Jy8jOcX3NAAzM=" }, + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 48, 1, NVME_HMAC_ALG_SHA2_384, + "nqn.psk-test-host", "nqn.psk-test-subsys", + "NVMe1R02 nqn.psk-test-host nqn.psk-test-subsys RsKmYJ3nAn1ApjjMloJFbAkLPivONDAX/xW327YBUsn2eGShXSjCZvBaOxscLqmz" }, +}; + static void check_str(const char *exp, const char *res) { if (!strcmp(res, exp)) @@ -70,7 +109,7 @@ static void check_str(const char *exp, const char *res) test_rc = 1; } -static void export_test(struct test_data *test) +static void export_test(struct test_data_psk *test) { char *psk; @@ -92,7 +131,7 @@ static void export_test(struct test_data *test) free(psk); } -static void import_test(struct test_data *test) +static void import_test(struct test_data_psk *test) { unsigned char *psk; int psk_length; @@ -133,7 +172,7 @@ static void import_test(struct test_data *test) free(psk); } -static void export_versioned_test(struct test_data *test) +static void export_versioned_test(struct test_data_psk *test) { char *psk; @@ -158,7 +197,7 @@ static void export_versioned_test(struct test_data *test) free(psk); } -static void import_versioned_test(struct test_data *test) +static void import_versioned_test(struct test_data_psk *test) { unsigned char *psk; unsigned char version; @@ -207,19 +246,49 @@ static void import_versioned_test(struct test_data *test) free(psk); } +static void identity_test(struct test_data_identity *test) +{ + char *id; + + if (test->version != 1 || + !(test->hmac == NVME_HMAC_ALG_SHA2_256 || + test->hmac == NVME_HMAC_ALG_SHA2_384)) + return; + + printf("test nvme_generate_tls_key_identity host %s subsys %s hmac %d %s\n", + test->hostnqn, test->subsysnqn, test->hmac, test->identity); + + id = nvme_generate_tls_key_identity(test->hostnqn, test->subsysnqn, + test->version, test->hmac, + (unsigned char *)test->configured_psk, + test->psk_length); + if (!id) { + if (errno == ENOTSUP) + return; + test_rc = 1; + printf("ERROR: nvme_generate_tls_key_identity() failed with %d\n", errno); + return; + } + check_str(test->identity, id); + free(id); +} + int main(void) { - for (int i = 0; i < ARRAY_SIZE(test_data); i++) - export_test(&test_data[i]); + for (int i = 0; i < ARRAY_SIZE(test_data_psk); i++) + export_test(&test_data_psk[i]); + + for (int i = 0; i < ARRAY_SIZE(test_data_psk); i++) + import_test(&test_data_psk[i]); - for (int i = 0; i < ARRAY_SIZE(test_data); i++) - import_test(&test_data[i]); + for (int i = 0; i < ARRAY_SIZE(test_data_psk); i++) + export_versioned_test(&test_data_psk[i]); - for (int i = 0; i < ARRAY_SIZE(test_data); i++) - export_versioned_test(&test_data[i]); + for (int i = 0; i < ARRAY_SIZE(test_data_psk); i++) + import_versioned_test(&test_data_psk[i]); - for (int i = 0; i < ARRAY_SIZE(test_data); i++) - import_versioned_test(&test_data[i]); + for (int i = 0; i < ARRAY_SIZE(test_data_identity); i++) + identity_test(&test_data_identity[i]); return test_rc ? EXIT_FAILURE : EXIT_SUCCESS; } From 7821981837ad28499d5b787a70c5fc25e10cf6d6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 19 Aug 2025 12:02:30 +0200 Subject: [PATCH 3/5] linux: rename __nvme_insert_tls_key() to __nvme_import_tls_key() The function __nvme_insert_tls_key() is only called from __nvme_import_keys_from_config(), so rename it to __nvme_import_tls_key() to avoid a name clash with __nvme_insert_tls_key_versioned(). Signed-off-by: Hannes Reinecke --- src/nvme/linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvme/linux.c b/src/nvme/linux.c index 4c8d3140f..f7c304cb1 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -1426,7 +1426,7 @@ long nvme_revoke_tls_key(const char *keyring, const char *key_type, return keyctl_revoke(key); } -static long __nvme_insert_tls_key(long keyring_id, +static long __nvme_import_tls_key(long keyring_id, const char *hostnqn, const char *subsysnqn, const char *identity, const char *key) { @@ -1505,7 +1505,7 @@ int __nvme_import_keys_from_config(nvme_host_t h, nvme_ctrl_t c, id = nvme_lookup_key("psk", identity); if (!id) - id = __nvme_insert_tls_key(kr_id, hostnqn, + id = __nvme_import_tls_key(kr_id, hostnqn, subsysnqn, identity, key); if (id <= 0) { From 0f6ecdb709a7e11b5c8e9087bf86c28c8dad8a72 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 19 Aug 2025 12:07:14 +0200 Subject: [PATCH 4/5] linux: rename __nvme_insert_tls_key_versioned() to __nvme_insert_tls_key() Rename to shorten the function name for better readability. Signed-off-by: Hannes Reinecke --- src/nvme/linux.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/nvme/linux.c b/src/nvme/linux.c index f7c304cb1..8b2671d86 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -1344,10 +1344,10 @@ int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb, return ret; } -static long __nvme_insert_tls_key_versioned(key_serial_t keyring_id, const char *key_type, - const char *hostnqn, const char *subsysnqn, - int version, int hmac, - unsigned char *configured_key, int key_len) +static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) { _cleanup_free_ unsigned char *psk = NULL; _cleanup_free_ char *identity = NULL; @@ -1401,10 +1401,10 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, ret = nvme_set_keyring(keyring_id); if (ret < 0) return 0; - return __nvme_insert_tls_key_versioned(keyring_id, key_type, - hostnqn, subsysnqn, - version, hmac, - configured_key, key_len); + return __nvme_insert_tls_key(keyring_id, key_type, + hostnqn, subsysnqn, + version, hmac, + configured_key, key_len); } long nvme_revoke_tls_key(const char *keyring, const char *key_type, @@ -1447,10 +1447,10 @@ static long __nvme_import_tls_key(long keyring_id, * configured key. Derive a new key and load the newly * created key into the keystore. */ - return __nvme_insert_tls_key_versioned(keyring_id, "psk", - hostnqn, subsysnqn, - version, hmac, - key_data, key_len); + return __nvme_insert_tls_key(keyring_id, "psk", + hostnqn, subsysnqn, + version, hmac, + key_data, key_len); } return nvme_update_key(keyring_id, "psk", identity, From 27043adaffbfdf2f132e90f8a979980fc1bf77d7 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Sun, 20 Jul 2025 19:17:18 -0700 Subject: [PATCH 5/5] libnvme: TLS PSK derivation fixes 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. To not break existing implementations keep the original algorithms with as compat versions and update the 'psk' testcase to cover both versions. Signed-off-by: Chris Leech Signed-off-by: Hannes Reinecke --- src/libnvme.map | 2 + src/nvme/linux.c | 276 +++++++++++++++++++++++++++++++++++++++++++++-- src/nvme/linux.h | 50 +++++++++ test/psk.c | 60 +++++++++++ 4 files changed, 380 insertions(+), 8 deletions(-) diff --git a/src/libnvme.map b/src/libnvme.map index bf2d5a378..af722218a 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -3,6 +3,8 @@ LIBNVME_UNRELEASED { global: nvme_set_etdas; nvme_clear_etdas; + nvme_insert_tls_key_compat; + nvme_generate_tls_key_identity_compat; }; LIBNVME_1_14 { diff --git a/src/nvme/linux.c b/src/nvme/linux.c index 8b2671d86..7d06858a1 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -600,6 +600,17 @@ static int derive_retained_key(int hmac, const char *hostnqn, return -1; } +static int derive_retained_key_compat(int hmac, const char *hostnqn, + unsigned char *generated, + unsigned char *retained, + size_t key_len) +{ + nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; +} + static int derive_psk_digest(const char *hostnqn, const char *subsysnqn, int version, int cipher, unsigned char *retained, size_t key_len, @@ -620,6 +631,16 @@ static int derive_tls_key(int version, int cipher, const char *context, errno = ENOTSUP; return -1; } + +static int derive_tls_key_compat(int version, int cipher, const char *context, + unsigned char *retained, + unsigned char *psk, size_t key_len) +{ + nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; +} #else /* CONFIG_OPENSSL */ static unsigned char default_hmac(size_t key_len) { @@ -665,6 +686,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() * @@ -697,6 +758,67 @@ static int derive_retained_key(int hmac, const char *hostnqn, unsigned char *configured, unsigned char *retained, size_t key_len) +{ + _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; + uint16_t length = htons(key_len & 0xFFFF); + const EVP_MD *md; + size_t hmac_len; + + if (hmac == NVME_HMAC_ALG_NONE) { + memcpy(retained, configured, key_len); + return key_len; + } + + md = select_hmac(hmac, &hmac_len); + if (!md || !hmac_len) { + errno = EINVAL; + return -1; + } + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx) { + errno = ENOMEM; + return -1; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) { + errno = ENOMEM; + return -1; + } + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, configured, key_len) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)&length, 2) <= 0) { + errno = ENOKEY; + return -1; + } + if (hkdf_info_printf(ctx, "tls13 HostNQN") <= 0) { + errno = ENOKEY; + return -1; + } + if (hkdf_info_printf(ctx, "%s", hostnqn) <= 0) { + errno = ENOKEY; + return -1; + } + + if (EVP_PKEY_derive(ctx, retained, &key_len) <= 0) { + errno = ENOKEY; + return -1; + } + + return key_len; +} + +static int derive_retained_key_compat(int hmac, const char *hostnqn, + unsigned char *configured, + unsigned char *retained, + size_t key_len) { _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; uint16_t length = key_len & 0xFFFF; @@ -783,9 +905,78 @@ 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 = htons(key_len & 0xFFFF); + const EVP_MD *md; + size_t hmac_len; + + md = select_hmac(cipher, &hmac_len); + if (!md || !hmac_len) { + errno = EINVAL; + return -1; + } + + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!ctx) { + errno = ENOMEM; + return -1; + } + + if (EVP_PKEY_derive_init(ctx) <= 0) { + errno = ENOMEM; + return -1; + } + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)&length, 2) <= 0) { + errno = ENOKEY; + return -1; + } + if (hkdf_info_printf(ctx, "tls13 nvme-tls-psk") <= 0) { + errno = ENOKEY; + return -1; + } + switch (version) { + case 0: + if (hkdf_info_printf(ctx, "%s", context) <= 0) { + errno = ENOKEY; + return -1; + } + break; + case 1: + if (hkdf_info_printf(ctx, "%02d %s", cipher, context) <= 0) { + errno = ENOKEY; + return -1; + } + break; + default: + errno = ENOKEY; + return -1; + } + + if (EVP_PKEY_derive(ctx, psk, &key_len) <= 0) { + errno = ENOKEY; + return -1; + } + + return key_len; +} + +static int derive_tls_key_compat(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; @@ -1074,7 +1265,7 @@ static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, char *identity, int version, int hmac, unsigned char *configured, - unsigned char *psk, int key_len) + unsigned char *psk, int key_len, bool compat) { _cleanup_free_ unsigned char *retained = NULL; _cleanup_free_ char *digest = NULL; @@ -1092,7 +1283,12 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, errno = ENOMEM; return -1; } - ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len); + if (compat) + ret = derive_retained_key_compat(hmac, hostnqn, configured, + retained, key_len); + else + ret = derive_retained_key(hmac, hostnqn, configured, + retained, key_len); if (ret < 0) return ret; @@ -1120,6 +1316,9 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, digest, identity); if (ret < 0) return ret; + if (compat) + return derive_tls_key_compat(version, cipher, context, retained, + psk, key_len); return derive_tls_key(version, cipher, context, retained, psk, key_len); } @@ -1175,10 +1374,47 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, memset(psk, 0, key_len); ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, - configured_key, psk, key_len); + configured_key, psk, key_len, false); +out_free_identity: + if (ret < 0) { + free(identity); + identity = NULL; + } + return identity; +} + +char *nvme_generate_tls_key_identity_compat(const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + _cleanup_free_ unsigned char *psk = NULL; + char *identity; + ssize_t identity_len; + int ret = -1; + + identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); + if (identity_len < 0) { + errno = EINVAL; + return NULL; + } + + identity = malloc(identity_len); + if (!identity) { + errno = ENOMEM; + return NULL; + } + + psk = malloc(key_len); + if (!psk) { + errno = ENOMEM; + goto out_free_identity; + } + + memset(psk, 0, key_len); + ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, + configured_key, psk, key_len, true); out_free_identity: if (ret < 0) { - errno = -ret; free(identity); identity = NULL; } @@ -1347,7 +1583,8 @@ int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb, static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type, const char *hostnqn, const char *subsysnqn, int version, int hmac, - unsigned char *configured_key, int key_len) + unsigned char *configured_key, int key_len, + bool compat) { _cleanup_free_ unsigned char *psk = NULL; _cleanup_free_ char *identity = NULL; @@ -1373,7 +1610,7 @@ static long __nvme_insert_tls_key(key_serial_t keyring_id, const char *key_type, } memset(psk, 0, key_len); ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, - configured_key, psk, key_len); + configured_key, psk, key_len, compat); if (ret != key_len) { errno = ENOKEY; return 0; @@ -1404,7 +1641,30 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, return __nvme_insert_tls_key(keyring_id, key_type, hostnqn, subsysnqn, version, hmac, - configured_key, key_len); + configured_key, key_len, false); +} + +long nvme_insert_tls_key_compat(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + key_serial_t keyring_id; + int ret; + + keyring_id = nvme_lookup_keyring(keyring); + if (keyring_id == 0) { + errno = ENOKEY; + return 0; + } + + ret = nvme_set_keyring(keyring_id); + if (ret < 0) + return 0; + return __nvme_insert_tls_key(keyring_id, key_type, + hostnqn, subsysnqn, + version, hmac, + configured_key, key_len, true); } long nvme_revoke_tls_key(const char *keyring, const char *key_type, @@ -1450,7 +1710,7 @@ static long __nvme_import_tls_key(long keyring_id, return __nvme_insert_tls_key(keyring_id, "psk", hostnqn, subsysnqn, version, hmac, - key_data, key_len); + key_data, key_len, false); } return nvme_update_key(keyring_id, "psk", identity, diff --git a/src/nvme/linux.h b/src/nvme/linux.h index 279309a4e..3b438720d 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -400,6 +400,32 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, int version, int hmac, unsigned char *configured_key, int key_len); +/** + * nvme_insert_tls_key_compat() - Derive and insert TLS key + * @keyring: Keyring to use + * @key_type: Type of the resulting key + * @hostnqn: Host NVMe Qualified Name + * @subsysnqn: Subsystem NVMe Qualified Name + * @version: Key version to use + * @hmac: HMAC algorithm + * @configured_key: Configured key data to derive the key from + * @key_len: Length of @configured_key + * + * Derives a 'retained' TLS key as specified in NVMe TCP 1.0a (if + * @version s set to '0') or NVMe TP8028 (if @version is set to '1) and + * stores it as type @key_type in the keyring specified by @keyring. + * This version differs from @nvme_insert_tls_key_versioned() in that it + * uses the original implementation for HKDF Expand-Label which does not + * prefix the 'info' and 'label' strings with the length. + * + * Return: The key serial number if the key could be inserted into + * the keyring or 0 with errno otherwise. + */ +long nvme_insert_tls_key_compat(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len); + /** * nvme_generate_tls_key_identity() - Generate the TLS key identity * @hostnqn: Host NVMe Qualified Name @@ -420,6 +446,30 @@ char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, int version, int hmac, unsigned char *configured_key, int key_len); +/** + * nvme_generate_tls_key_identity_compat() - Generate the TLS key identity + * @hostnqn: Host NVMe Qualified Name + * @subsysnqn: Subsystem NVMe Qualified Name + * @version: Key version to use + * @hmac: HMAC algorithm + * @configured_key: Configured key data to derive the key from + * @key_len: Length of @configured_key + * + * Derives a 'retained' TLS key as specified in NVMe TCP and + * generate the corresponding TLs identity. This version differs + * from @nvme_generate_tls_key_identity() in that it uses the original + * implementation for HKDF-Expand-Label which does not prefix the 'info' + * and 'label' string with the length. + * + * Return: The string containing the TLS identity. It is the responsibility + * of the caller to free the returned string. + */ +char *nvme_generate_tls_key_identity_compat(const char *hostnqn, + const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, + int key_len); + /** * nvme_revoke_tls_key() - Revoke TLS key from keyring * @keyring: Keyring to use diff --git a/test/psk.c b/test/psk.c index 24a94a6e5..c9112e9ee 100644 --- a/test/psk.c +++ b/test/psk.c @@ -71,6 +71,35 @@ struct test_data_identity { }; static struct test_data_identity test_data_identity[] = { + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 32, 1, NVME_HMAC_ALG_SHA2_256, + "nqn.psk-test-host", "nqn.psk-test-subsys", + "NVMe1R01 nqn.psk-test-host nqn.psk-test-subsys iSbjiStwJ/1TrTvDlt2fjFmzvsytOJelidNnA+X5lEU=" }, + { { 0x55, 0x12, 0xDB, 0xB6, + 0x73, 0x7D, 0x01, 0x06, + 0xF6, 0x59, 0x75, 0xB7, + 0x73, 0xDF, 0xB0, 0x11, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03, + 0xFF, 0xC3, 0x44, 0xBC, + 0xF4, 0x42, 0xE2, 0xDD, + 0x6D, 0x8B, 0xC4, 0x87, + 0x0B, 0x5D, 0x5B, 0x03}, + 48, 1, NVME_HMAC_ALG_SHA2_384, + "nqn.psk-test-host", "nqn.psk-test-subsys", + "NVMe1R02 nqn.psk-test-host nqn.psk-test-subsys QhW2+Rp6RzHlNtCslyRxMnwJ11tKKhz8JCAQpQ+XUD8f9td1VeH5h53yz2wKJG1a" }, +}; + +static struct test_data_identity test_data_identity_compat[] = { { { 0x55, 0x12, 0xDB, 0xB6, 0x73, 0x7D, 0x01, 0x06, 0xF6, 0x59, 0x75, 0xB7, @@ -273,6 +302,34 @@ static void identity_test(struct test_data_identity *test) free(id); } +static void identity_test_compat(struct test_data_identity *test) +{ + char *id; + + if (test->version != 1 || + !(test->hmac == NVME_HMAC_ALG_SHA2_256 || + test->hmac == NVME_HMAC_ALG_SHA2_384)) + return; + + printf("test nvme_generate_tls_key_identity_compat host %s subsys %s hmac %d %s\n", + test->hostnqn, test->subsysnqn, test->hmac, test->identity); + + id = nvme_generate_tls_key_identity_compat(test->hostnqn, + test->subsysnqn, + test->version, test->hmac, + (unsigned char *)test->configured_psk, + test->psk_length); + if (!id) { + if (errno == ENOTSUP) + return; + test_rc = 1; + printf("ERROR: nvme_generate_tls_key_identity_compat() failed with %d\n", errno); + return; + } + check_str(test->identity, id); + free(id); +} + int main(void) { for (int i = 0; i < ARRAY_SIZE(test_data_psk); i++) @@ -290,5 +347,8 @@ int main(void) for (int i = 0; i < ARRAY_SIZE(test_data_identity); i++) identity_test(&test_data_identity[i]); + for (int i = 0; i < ARRAY_SIZE(test_data_identity_compat); i++) + identity_test_compat(&test_data_identity_compat[i]); + return test_rc ? EXIT_FAILURE : EXIT_SUCCESS; }