]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
libnvme: support NVMe TLS identities version 1
authorHannes Reinecke <hare@suse.de>
Thu, 16 Nov 2023 06:41:37 +0000 (07:41 +0100)
committerHannes Reinecke <hare@suse.de>
Fri, 17 Nov 2023 07:47:44 +0000 (08:47 +0100)
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 <hare@suse.de>
src/libnvme.map
src/nvme/linux.c
src/nvme/linux.h

index 7130894009b7d40ac6fecd07c64f2e7401a78ad3..29efc5d6ef139472576090a328dd1b9d0896a53b 100644 (file)
@@ -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 {
index ec63e52db821b94efb3bf9aaa191aea67dd6b46e..1e485ddbc48ad32828fd06cf2e2839e66654c5dc 100644 (file)
@@ -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);
+}
index be9181999dbb1c2e38c5d9aac525d07aa9e9285f..c593c9dea37c6559504a66eb82cb8f7202c6b3a4 100644 (file)
@@ -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 */