From: Hannes Reinecke Date: Thu, 16 Nov 2023 06:41:37 +0000 (+0100) Subject: libnvme: support NVMe TLS identities version 1 X-Git-Tag: v1.7~22^2~1 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=dc6fc7b07f2890fab11d2115152a8d08bf223298;p=users%2Fsagi%2Flibnvme.git libnvme: support NVMe TLS identities version 1 With NVMe TP8018 a new version '1' for generating NVMe TLS identities was specified; identities generated for this version require a PSK hash to be attached to the version '0' identifier. This patch implements a new function 'nvme_insert_tls_keys_versioned()' to support this functionality and makes the original function 'nvme_insert_tls_keys()' a wrapper for the new function. Signed-off-by: Hannes Reinecke --- diff --git a/src/libnvme.map b/src/libnvme.map index 71308940..29efc5d6 100644 --- a/src/libnvme.map +++ b/src/libnvme.map @@ -3,6 +3,7 @@ LIBNVME_1_7 { global: nvme_init_copy_range_f2; nvme_init_copy_range_f3; + nvme_insert_tls_key_versioned; }; LIBNVME_1_6 { diff --git a/src/nvme/linux.c b/src/nvme/linux.c index ec63e52d..1e485ddb 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -39,6 +39,7 @@ #include "tree.h" #include "log.h" #include "private.h" +#include "base64.h" static int __nvme_open(const char *name) { @@ -554,11 +555,17 @@ static int derive_retained_key(int hmac, const char *hostnqn, } static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, - int hmac, char *identity, + int version, int hmac, char *identity, unsigned char *retained, size_t key_len) { + if (version != 0) { + nvme_msg(NULL, LOG_ERR, "NVMe TLS 2.0 is not supported; " + "recompile with OpenSSL support.\n"); + errno = NOTSUP; + return -1; + } sprintf(identity, "NVMe0R%02d %s %s", - version, hmac, hostnqn, subsysnqn); + hmac, hostnqn, subsysnqn); return strlen(identity); } @@ -780,12 +787,90 @@ out: } static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, - int hmac, char *identity, + int version, int hmac, char *identity, unsigned char *retained, size_t key_len) { - sprintf(identity, "NVMe0R%02d %s %s", - hmac, hostnqn, subsysnqn); - return strlen(identity); + static const char hmac_seed[] = "NVMe-over-Fabrics"; + size_t hmac_len; + const EVP_MD *md = select_hmac(hmac, &hmac_len); + HMAC_CTX *hmac_ctx; + unsigned char *psk_ctx; + char *enc_ctx; + size_t len = -1; + + if (version == 0) { + sprintf(identity, "NVMe%01dR%02d %s %s", + version, hmac, hostnqn, subsysnqn); + return strlen(identity); + } + if (version > 1) { + errno = EINVAL; + return -1; + } + + hmac_ctx = HMAC_CTX_new(); + if (!hmac_ctx) { + errno = ENOMEM; + return -1; + } + if (!md) { + errno = EINVAL; + goto out_free_hmac; + } + + psk_ctx = malloc(key_len); + if (!psk_ctx) { + errno = ENOMEM; + goto out_free_hmac; + } + if (!HMAC_Init_ex(hmac_ctx, retained, key_len, md, NULL)) { + errno = ENOMEM; + goto out_free_psk; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + goto out_free_psk; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + goto out_free_psk; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)subsysnqn, + strlen(subsysnqn))) { + errno = ENOKEY; + goto out_free_psk; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + goto out_free_psk; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + goto out_free_psk; + } + if (!HMAC_Final(hmac_ctx, psk_ctx, (unsigned int *)&key_len)) { + errno = ENOKEY; + goto out_free_psk; + } + enc_ctx = malloc(key_len * 2); + memset(enc_ctx, 0, key_len * 2); + len = base64_encode(psk_ctx, key_len, enc_ctx); + if (len < 0) { + errno = ENOKEY; + goto out_free_enc; + } + sprintf(identity, "NVMe%01dR%02d %s %s %s", + version, hmac, hostnqn, subsysnqn, enc_ctx); + len = strlen(identity); +out_free_enc: + free(enc_ctx); +out_free_psk: + free(psk_ctx); +out_free_hmac: + HMAC_CTX_free(hmac_ctx); + return len; } #endif /* !CONFIG_OPENSSL_1 */ @@ -883,18 +968,133 @@ out: } static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, - int hmac, char *identity, + int version, int hmac, char *identity, unsigned char *retained, size_t key_len) { - sprintf(identity, "NVMe0R%02d %s %s", - version, hmac, hostnqn, subsysnqn); - return strlen(identity); + static const char hmac_seed[] = "NVMe-over-Fabrics"; + size_t hmac_len; + OSSL_PARAM params[2], *p = params; + OSSL_LIB_CTX *lib_ctx; + EVP_MAC_CTX *mac_ctx = NULL; + EVP_MAC *mac = NULL; + char *progq = NULL; + char *digest = NULL; + unsigned char *psk_ctx; + char *enc_ctx; + size_t len = -1; + + if (version == 0) { + sprintf(identity, "NVMe%01dR%02d %s %s", + version, hmac, hostnqn, subsysnqn); + return strlen(identity); + } + if (version > 1) { + errno = EINVAL; + return -1; + } + + lib_ctx = OSSL_LIB_CTX_new(); + if (!lib_ctx) { + errno = ENOMEM; + return -1; + } + mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq); + if (!mac) { + errno = ENOMEM; + goto out_free_ossl; + } + + mac_ctx = EVP_MAC_CTX_new(mac); + if (!mac_ctx) { + errno = ENOMEM; + goto out_free_mac; + } + switch (hmac) { + case NVME_HMAC_ALG_SHA2_256: + digest = OSSL_DIGEST_NAME_SHA2_256; + break; + case NVME_HMAC_ALG_SHA2_384: + digest = OSSL_DIGEST_NAME_SHA2_384; + break; + default: + errno = EINVAL; + break; + } + if (!digest) + goto out_free_ctx; + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + digest, 0); + *p = OSSL_PARAM_construct_end(); + + psk_ctx = malloc(key_len); + if (!psk_ctx) { + errno = ENOMEM; + goto out_free_ctx; + } + + if (!EVP_MAC_init(mac_ctx, retained, key_len, params)) { + errno = ENOKEY; + goto out_free_psk; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + goto out_free_psk; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + goto out_free_psk; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)subsysnqn, + strlen(subsysnqn))) { + errno = ENOKEY; + goto out_free_psk; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + goto out_free_psk; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + goto out_free_psk; + } + if (!EVP_MAC_final(mac_ctx, psk_ctx, &hmac_len, key_len)) { + errno = ENOKEY; + goto out_free_psk; + } + if (hmac_len > key_len) { + errno = EMSGSIZE; + goto out_free_psk; + } + enc_ctx = malloc(hmac_len * 2); + memset(enc_ctx, 0, hmac_len * 2); + len = base64_encode(psk_ctx, hmac_len, enc_ctx); + if (len < 0) { + errno = ENOKEY; + goto out_free_enc; + } + sprintf(identity, "NVMe%01dR%02d %s %s %s", + version, hmac, hostnqn, subsysnqn, enc_ctx); + len = strlen(identity); +out_free_enc: + free(enc_ctx); +out_free_psk: + free(psk_ctx); +out_free_ctx: + EVP_MAC_CTX_free(mac_ctx); +out_free_mac: + EVP_MAC_free(mac); +out_free_ossl: + OSSL_LIB_CTX_free(lib_ctx); + + return len; } #endif /* !CONFIG_OPENSSL_3 */ #ifdef CONFIG_KEYUTILS static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, - char *identity, + char *identity, int version, int hmac, unsigned char *configured, unsigned char *psk, int key_len) { @@ -914,7 +1114,7 @@ static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len); if (ret < 0) goto out; - ret = gen_tls_identity(hostnqn, subsysnqn, hmac, + ret = gen_tls_identity(hostnqn, subsysnqn, version, hmac, identity, retained, key_len); if (ret < 0) goto out; @@ -924,6 +1124,23 @@ out: return ret; } +static size_t nvme_identity_len(int hmac, int version, const char *hostnqn, + const char *subsysnqn) +{ + size_t len; + + len = strlen(hostnqn) + strlen(subsysnqn) + 12; + if (version == 1) { + len += 66; + if (hmac == NVME_HMAC_ALG_SHA2_384) + len += 32; + } else if (version > 1) { + errno = EINVAL; + return -1; + } + return len; +} + long nvme_lookup_keyring(const char *keyring) { key_serial_t keyring_id; @@ -963,9 +1180,10 @@ int nvme_set_keyring(long key_id) return 0; } -long nvme_insert_tls_key(const char *keyring, const char *key_type, - const char *hostnqn, const char *subsysnqn, int hmac, - unsigned char *configured_key, int key_len) +long nvme_insert_tls_key_versioned(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, key = 0; char *identity; @@ -977,7 +1195,10 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, if (keyring_id == 0) return -1; - identity_len = strlen(hostnqn) + strlen(subsysnqn) + 12; + identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); + if (identity_len < 0) + return -1; + identity = malloc(identity_len); if (!identity) { errno = ENOMEM; @@ -990,7 +1211,7 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, goto out_free_identity; } memset(psk, 0, key_len); - ret = derive_nvme_keys(hostnqn, subsysnqn, identity, hmac, + ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, configured_key, psk, key_len); if (ret != key_len) goto out_free_psk; @@ -1045,9 +1266,10 @@ int nvme_set_keyring(long key_id) return -1; } -long nvme_insert_tls_key(const char *keyring, const char *key_type, - const char *hostnqn, const char *subsysnqn, int hmac, - unsigned char *configured_key, int key_len) +long nvme_insert_tls_key_versioned(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_msg(NULL, LOG_ERR, "key operations not supported; " "recompile with keyutils support.\n"); @@ -1055,3 +1277,12 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, return -1; } #endif + +long nvme_insert_tls_key(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, int hmac, + unsigned char *configured_key, int key_len) +{ + return nvme_insert_tls_key_versioned(keyring, key_type, + hostnqn, subsysnqn, 0, hmac, + configured_key, key_len); +} diff --git a/src/nvme/linux.h b/src/nvme/linux.h index be918199..c593c9de 100644 --- a/src/nvme/linux.h +++ b/src/nvme/linux.h @@ -293,4 +293,27 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, const char *hostnqn, const char *subsysnqn, int hmac, unsigned char *configured_key, int key_len); +/** + * nvme_insert_tls_key_versioned() - 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. + * + * Return: The key serial number if the key could be inserted into + * the keyring or 0 with errno otherwise. + */ +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len); + #endif /* _LIBNVME_LINUX_H */