All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH nvme-cli 0/2] Support commands for NVMe In-band authentication
@ 2021-09-13 10:24 Hannes Reinecke
  2021-09-13 10:24 ` [PATCH 1/2] Add 'gen-dhchap-key' command Hannes Reinecke
  2021-09-13 10:24 ` [PATCH 2/2] Add 'check-dhchap-key' function Hannes Reinecke
  0 siblings, 2 replies; 7+ messages in thread
From: Hannes Reinecke @ 2021-09-13 10:24 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Sagi Grimberg, Keith Busch, linux-nvme, Hannes Reinecke

Hi all,

this patchset adds two new functions 'gen-dhchap-key' and 'check-dhchap-key'
to help generating and validating host keys for NVMe In-band authentication.

As usual, comments and reviews are welcome.

Hannes Reinecke (2):
  Add 'gen-dhchap-key' command
  Add 'check-dhchap-key' function

 Documentation/nvme-check-dhchap-key.txt |  31 ++++
 Documentation/nvme-gen-dhchap-key.txt   |  52 ++++++
 Makefile                                |  17 +-
 nvme-builtin.h                          |   2 +
 nvme.c                                  | 224 ++++++++++++++++++++++++
 util/argconfig.h                        |   1 +
 util/base64.c                           | 100 +++++++++++
 util/base64.h                           |   7 +
 8 files changed, 433 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/nvme-check-dhchap-key.txt
 create mode 100644 Documentation/nvme-gen-dhchap-key.txt
 create mode 100644 util/base64.c
 create mode 100644 util/base64.h

-- 
2.26.2


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/2] Add 'gen-dhchap-key' command
  2021-09-13 10:24 [PATCH nvme-cli 0/2] Support commands for NVMe In-band authentication Hannes Reinecke
@ 2021-09-13 10:24 ` Hannes Reinecke
  2021-09-13 19:26   ` Sagi Grimberg
  2021-09-13 10:24 ` [PATCH 2/2] Add 'check-dhchap-key' function Hannes Reinecke
  1 sibling, 1 reply; 7+ messages in thread
From: Hannes Reinecke @ 2021-09-13 10:24 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Sagi Grimberg, Keith Busch, linux-nvme, Hannes Reinecke

Add command 'gen-dhchap-key' to generate an DH-HMAC-CHAP host key
for NVMe in-band authentication.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 Documentation/nvme-gen-dhchap-key.txt |  52 +++++++++
 Makefile                              |  17 ++-
 nvme-builtin.h                        |   1 +
 nvme.c                                | 155 ++++++++++++++++++++++++++
 util/argconfig.h                      |   1 +
 util/base64.c                         |  62 +++++++++++
 util/base64.h                         |   6 +
 7 files changed, 293 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/nvme-gen-dhchap-key.txt
 create mode 100644 util/base64.c
 create mode 100644 util/base64.h

diff --git a/Documentation/nvme-gen-dhchap-key.txt b/Documentation/nvme-gen-dhchap-key.txt
new file mode 100644
index 0000000..eecde76
--- /dev/null
+++ b/Documentation/nvme-gen-dhchap-key.txt
@@ -0,0 +1,52 @@
+nvme-gen-dhchap-key(1)
+===================
+
+NAME
+----
+nvme-gen-dhchap-key - Generate a host DH-HMAC-CHAP key
+
+SYNOPSIS
+--------
+[verse]
+'nvme gen-dhchap-key' [--hmac=<hmac-id> | -h <hmac-id>]
+		      [--secret=<secret> | -s <secret> ]
+		      [--key-length=<len> | -l <len> ]
+		      [--nqn=<host-nqn> | -n <host-nqn> ]
+
+DESCRIPTION
+-----------
+Generate a base64-encoded DH-HMAC-CHAP host key in the form:
+DHHC-1:00:ia6zGodOr4SEG0Zzaw398rpY0wqipUWj4jWjUh4HWUz6aQ2n:
+and prints it to stdout.
+
+OPTIONS
+-------
+-h <hmac-id>::
+--hmac=<hmac-id>::
+	Select a HMAC algorithm to use. Possible values are:
+	0 - No HMAC algorithm
+	1 - SHA-256
+	2 - SHA-384
+	3 - SHA-512
+
+-s <secret>::
+--secret=<secret>::
+	Secret value (in hexadecimal) to be used for the key. If none are
+	provided a random value is used.
+
+-l <len>::
+--key-length=<len>::
+	Length of the resulting key. Possible values are 32, 48, or 64.
+
+-n <hostnqn>::
+--nqn=<hostnqn>::
+	Host-NQN to be used for the transformation. This parameter is only
+	valid if a non-zero HMAC function has been specified.
+
+EXAMPLES
+--------
+No Examples
+
+NVME
+----
+Part of the nvme-user suite
diff --git a/Makefile b/Makefile
index 5fbdfd0..984b380 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,8 @@ LIBUUID = $(shell $(LD) -o /dev/null -luuid >/dev/null 2>&1; echo $$?)
 LIBHUGETLBFS = $(shell $(LD) -o /dev/null -lhugetlbfs >/dev/null 2>&1; echo $$?)
 HAVE_SYSTEMD = $(shell pkg-config --exists libsystemd  --atleast-version=242; echo $$?)
 LIBJSONC = $(shell $(LD) -o /dev/null -ljson-c >/dev/null 2>&1; echo $$?)
+LIBZ = $(shell $(LD) -o /dev/null -lz >/dev/null 2>&1; echo $$?)
+LIBOPENSSL = $(shell $(LD) -o /dev/null -lssl >/dev/null 2>&1; echo $$?)
 NVME = nvme
 INSTALL ?= install
 DESTDIR =
@@ -33,6 +35,18 @@ ifeq ($(LIBHUGETLBFS),0)
 	override LIB_DEPENDS += hugetlbfs
 endif
 
+ifeq ($(LIBZ),0)
+	override LDFLAGS += -lz
+	override CFLAGS += -DLIBZ
+	override LIB_DEPENDS += zlib
+endif
+
+ifeq ($(LIBOPENSSL),0)
+	override LDFLAGS += -lssl -lcrypto
+	override CFLAGS += -DOPENSSL
+	override LIB_DEPENDS += openssl
+endif
+
 INC=-Iutil
 
 ifeq ($(HAVE_SYSTEMD),0)
@@ -70,7 +84,8 @@ OBJS := nvme-print.o nvme-rpmb.o \
 	fabrics.o nvme-models.o plugin.o
 
 UTIL_OBJS := util/argconfig.o util/suffix.o util/parser.o \
-	util/cleanup.o
+	util/cleanup.o util/base64.o
+
 ifneq ($(LIBJSONC), 0)
 override UTIL_OBJS += util/json.o
 endif
diff --git a/nvme-builtin.h b/nvme-builtin.h
index a413d00..a256f05 100644
--- a/nvme-builtin.h
+++ b/nvme-builtin.h
@@ -81,6 +81,7 @@ COMMAND_LIST(
 	ENTRY("disconnect-all", "Disconnect from all connected NVMeoF subsystems", disconnect_all_cmd)
 	ENTRY("gen-hostnqn", "Generate NVMeoF host NQN", gen_hostnqn_cmd)
 	ENTRY("show-hostnqn", "Show NVMeoF host NQN", show_hostnqn_cmd)
+	ENTRY("gen-dhchap-key", "Generate NVMeoF DH-HMAC-CHAP host key", gen_dhchap_key)
 	ENTRY("dir-receive", "Submit a Directive Receive command, return results", dir_receive)
 	ENTRY("dir-send", "Submit a Directive Send command, return results", dir_send)
 	ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller ", virtual_mgmt)
diff --git a/nvme.c b/nvme.c
index 79605f5..5791357 100644
--- a/nvme.c
+++ b/nvme.c
@@ -36,22 +36,31 @@
 #include <math.h>
 #include <dirent.h>
 #include <libgen.h>
+#include <zlib.h>
 
 #ifdef LIBHUGETLBFS
 #include <hugetlbfs.h>
 #endif
 
+#ifdef OPENSSL
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#endif
+
 #include <linux/fs.h>
 
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/random.h>
 
 #include "common.h"
 #include "nvme.h"
 #include "libnvme.h"
 #include "nvme-print.h"
 #include "plugin.h"
+#include "base64.h"
 
 #include "argconfig.h"
 #include "fabrics.h"
@@ -5750,6 +5759,152 @@ static int show_hostnqn_cmd(int argc, char **argv, struct command *command, stru
 	return 0;
 }
 
+static int gen_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin)
+{
+	const char *desc = "Generate a DH-HMAC-CHAP host key usable "\
+		"for NVMe In-Band Authentication.";
+	const char *secret = "Optional secret (in hexadecimal characters) "\
+		"to be used to initialize the host key.";
+	const char *key_len = "Length of the resulting key "\
+		"(32, 48, or 64 bytes).";
+	const char *hmac = "HMAC function to use for key transformation "\
+		"(0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512).";
+	const char *nqn = "Host NQN to use for key transformation.";
+
+	char *hostnqn;
+	char *raw_secret;
+	unsigned char key[68];
+	char encoded_key[128];
+	unsigned long crc = crc32(0L, NULL, 0);
+	int err = 0;
+#ifdef OPENSSL
+	const EVP_MD *md = NULL;
+#else
+	const char *md = NULL;
+#endif
+	struct config {
+		char *secret;
+		unsigned int key_len;
+		char *nqn;
+		unsigned int hmac;
+	};
+
+	struct config cfg = {
+		.secret = NULL,
+		.key_len = 32,
+		.nqn = NULL,
+		.hmac = 0,
+	};
+
+	OPT_ARGS(opts) = {
+		OPT_STR("secret", 's', &cfg.secret, secret),
+		OPT_UINT("key-length", 'l', &cfg.key_len, key_len),
+		OPT_STR("nqn", 'n', &cfg.nqn, nqn),
+		OPT_UINT("hmac", 'h', &cfg.hmac, hmac),
+		OPT_END()
+	};
+
+	err = argconfig_parse(argc, argv, desc, opts);
+	if (err)
+		return err;
+
+	if (cfg.key_len != 32 && cfg.key_len != 48 && cfg.key_len != 64) {
+		fprintf(stderr, "Invalid key length %u\n", cfg.key_len);
+		return -EINVAL;
+	}
+	if (cfg.hmac > 3) {
+		fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac);
+		return -EINVAL;
+	}
+	if (cfg.hmac > 0) {
+#ifdef OPENSSL
+		if (!cfg.nqn) {
+			hostnqn = nvmf_hostnqn_from_file();
+			if (!hostnqn) {
+				fprintf(stderr, "Could not read host NQN\n");
+				return -ENOENT;
+			}
+		} else {
+			hostnqn = cfg.nqn;
+		}
+		switch (cfg.hmac) {
+		case 1:
+			md = EVP_sha256();
+			break;
+		case 2:
+			md = EVP_sha384();
+			break;
+		case 3:
+			md = EVP_sha512();
+			break;
+		default:
+			break;
+		}
+#else
+		fprintf(stderr, "HMAC transformation not supported; "\
+			"recompile with OpenSSL support.\n");
+		return -EINVAL;
+#endif
+	}
+	raw_secret = malloc(cfg.key_len);
+	if (!raw_secret)
+		return -ENOMEM;
+	if (!cfg.secret) {
+		if (getrandom(raw_secret, cfg.key_len, GRND_NONBLOCK) < 0)
+			return errno;
+	} else {
+		int secret_len = 0, i;
+		unsigned int c;
+
+		for (i = 0; i < strlen(cfg.secret); i+=2) {
+			if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
+				fprintf(stderr, "Invalid secret '%s'\n",
+					cfg.secret);
+				return -EINVAL;
+			}
+			raw_secret[secret_len++] = (unsigned char)c;
+		}
+		if (secret_len != cfg.key_len) {
+			fprintf(stderr, "Invalid key length (%d bytes)\n",
+				secret_len);
+			return -EINVAL;
+		}
+	}
+
+	if (md) {
+#ifdef OPENSSL
+		HMAC_CTX *hmac_ctx = HMAC_CTX_new();
+		const char hmac_seed[] = "NVMe-over-Fabrics";
+		unsigned int key_len;
+
+		ENGINE_load_builtin_engines();
+		ENGINE_register_all_complete();
+
+		HMAC_Init_ex(hmac_ctx, raw_secret, cfg.key_len,md, NULL);
+		HMAC_Update(hmac_ctx, (unsigned char *)hostnqn,
+			    strlen(hostnqn));
+		HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed,
+			    strlen(hmac_seed));
+		HMAC_Final(hmac_ctx, key, &key_len);
+		HMAC_CTX_free(hmac_ctx);
+#endif
+	} else {
+		memcpy(key, raw_secret, cfg.key_len);
+	}
+
+	crc = crc32(crc, key, cfg.key_len);
+	key[cfg.key_len++] = crc & 0xff;
+	key[cfg.key_len++] = (crc >> 8) & 0xff;
+	key[cfg.key_len++] = (crc >> 16) & 0xff;
+	key[cfg.key_len++] = (crc >> 24) & 0xff;
+
+	memset(encoded_key, 0, sizeof(encoded_key));
+	base64_encode(key, cfg.key_len, encoded_key);
+
+	printf("DHHC-1:%02x:%s:\n", cfg.hmac, encoded_key);
+	return 0;
+}
+
 static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
 {
 	const char *desc = "Send Get Log Page request to Discovery Controller.";
diff --git a/util/argconfig.h b/util/argconfig.h
index 1a5c693..3b6da4c 100644
--- a/util/argconfig.h
+++ b/util/argconfig.h
@@ -100,6 +100,7 @@ enum argconfig_types {
 #define OPT_FMT(l, s, v, d)  OPT_STRING(l, s, "FMT", v, d)
 #define OPT_FILE(l, s, v, d) OPT_STRING(l, s, "FILE", v, d)
 #define OPT_LIST(l, s, v, d) OPT_STRING(l, s, "LIST", v, d)
+#define OPT_STR(l, s, v, d) OPT_STRING(l, s, "STRING", v, d)
 
 struct argconfig_commandline_options {
 	const char *option;
diff --git a/util/base64.c b/util/base64.c
new file mode 100644
index 0000000..328c8ea
--- /dev/null
+++ b/util/base64.c
@@ -0,0 +1,62 @@
+/*
+ * base64.c - RFC4648-compliant base64 encoding
+ *
+ * Copyright (c) 2020 Hannes Reinecke, SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA  02110-1301, USA.
+ */
+
+#include <stdlib.h>
+
+static const char base64_table[65] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode() - base64-encode some bytes
+ * @src: the bytes to encode
+ * @srclen: number of bytes to encode
+ * @dst: (output) the base64-encoded string.  Not NUL-terminated.
+ *
+ * Encodes the input string using characters from the set [A-Za-z0-9+,].
+ * The encoded string is roughly 4/3 times the size of the input string.
+ *
+ * Return: length of the encoded string
+ */
+int base64_encode(const unsigned char *src, int srclen, char *dst)
+{
+	int i, bits = 0;
+	u_int32_t ac = 0;
+	char *cp = dst;
+
+	for (i = 0; i < srclen; i++) {
+		ac = (ac << 8) | src[i];
+		bits += 8;
+		do {
+			bits -= 6;
+			*cp++ = base64_table[(ac >> bits) & 0x3f];
+		} while (bits >= 6);
+	}
+	if (bits) {
+		bits -= 6;
+		*cp++ = base64_table[(ac >> bits) & 0x3f];
+	}
+	while (bits < 0) {
+		*cp++ = '=';
+		bits += 2;
+	}
+
+	return cp - dst;
+}
diff --git a/util/base64.h b/util/base64.h
new file mode 100644
index 0000000..374f5e6
--- /dev/null
+++ b/util/base64.h
@@ -0,0 +1,6 @@
+#ifndef _BASE64_H
+#define _BASE64_H
+
+int base64_encode(const unsigned char *src, int len, char *dst);
+
+#endif /* _BASE64_H */
-- 
2.26.2


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/2] Add 'check-dhchap-key' function
  2021-09-13 10:24 [PATCH nvme-cli 0/2] Support commands for NVMe In-band authentication Hannes Reinecke
  2021-09-13 10:24 ` [PATCH 1/2] Add 'gen-dhchap-key' command Hannes Reinecke
@ 2021-09-13 10:24 ` Hannes Reinecke
  1 sibling, 0 replies; 7+ messages in thread
From: Hannes Reinecke @ 2021-09-13 10:24 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Sagi Grimberg, Keith Busch, linux-nvme, Hannes Reinecke

Add a function to validate a given DH-HMAC-CHAP key in transport
encoding for NVMe in-band authentication.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 Documentation/nvme-check-dhchap-key.txt | 31 +++++++++++
 nvme-builtin.h                          |  1 +
 nvme.c                                  | 69 +++++++++++++++++++++++++
 util/base64.c                           | 38 ++++++++++++++
 util/base64.h                           |  1 +
 5 files changed, 140 insertions(+)
 create mode 100644 Documentation/nvme-check-dhchap-key.txt

diff --git a/Documentation/nvme-check-dhchap-key.txt b/Documentation/nvme-check-dhchap-key.txt
new file mode 100644
index 0000000..75008c7
--- /dev/null
+++ b/Documentation/nvme-check-dhchap-key.txt
@@ -0,0 +1,31 @@
+nvme-check-dhchap-key(1)
+===================
+
+NAME
+----
+nvme-check-dhchap-key - Check a generated host DH-HMAC-CHAP key
+
+SYNOPSIS
+--------
+[verse]
+'nvme check-dhchap-key' [--key=<key> ]
+
+DESCRIPTION
+-----------
+Checks if the key is a valid DH-HMAC-CHAP host key of the form:
+DHHC-1:00:ia6zGodOr4SEG0Zzaw398rpY0wqipUWj4jWjUh4HWUz6aQ2n:
+and prints it to stdout.
+
+OPTIONS
+-------
+-k <key>::
+--key=<key>::
+	Key to be checked.
+
+EXAMPLES
+--------
+No Examples
+
+NVME
+----
+Part of the nvme-user suite
diff --git a/nvme-builtin.h b/nvme-builtin.h
index a256f05..c2740bb 100644
--- a/nvme-builtin.h
+++ b/nvme-builtin.h
@@ -82,6 +82,7 @@ COMMAND_LIST(
 	ENTRY("gen-hostnqn", "Generate NVMeoF host NQN", gen_hostnqn_cmd)
 	ENTRY("show-hostnqn", "Show NVMeoF host NQN", show_hostnqn_cmd)
 	ENTRY("gen-dhchap-key", "Generate NVMeoF DH-HMAC-CHAP host key", gen_dhchap_key)
+	ENTRY("check-dhchap-key", "Validate NVMeoF DH-HMAC-CHAP host key", check_dhchap_key)
 	ENTRY("dir-receive", "Submit a Directive Receive command, return results", dir_receive)
 	ENTRY("dir-send", "Submit a Directive Send command, return results", dir_send)
 	ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller ", virtual_mgmt)
diff --git a/nvme.c b/nvme.c
index 5791357..d69a653 100644
--- a/nvme.c
+++ b/nvme.c
@@ -5905,6 +5905,75 @@ static int gen_dhchap_key(int argc, char **argv, struct command *command, struct
 	return 0;
 }
 
+static int check_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin)
+{
+	const char *desc = "Check a DH-HMAC-CHAP host key for usability "\
+		"for NVMe In-Band Authentication.";
+	const char *key = "DH-HMAC-CHAP key (in hexadecimal characters) "\
+		"to be validated.";
+
+	unsigned char decoded_key[128];
+	unsigned int decoded_len;
+	u_int32_t crc = crc32(0L, NULL, 0);
+	u_int32_t key_crc;
+	int err = 0, hmac;
+	struct config {
+		char *key;
+	};
+
+	struct config cfg = {
+		.key = NULL,
+	};
+
+	OPT_ARGS(opts) = {
+		OPT_STR("key", 'k', &cfg.key, key),
+		OPT_END()
+	};
+
+	err = argconfig_parse(argc, argv, desc, opts);
+	if (err)
+		return err;
+
+	if (!cfg.key) {
+		fprintf(stderr, "Key not specified\n");
+		return -EINVAL;
+	}
+
+	if (sscanf(cfg.key, "DHHC-1:%02x:*s", &hmac) != 1) {
+		fprintf(stderr, "Invalid key header '%s'\n", cfg.key);
+		return -EINVAL;
+	}
+	if (hmac > 3) {
+		fprintf(stderr, "Invalid HMAC identifier %d\n", hmac);
+		return -EINVAL;
+	}
+	decoded_len = base64_decode(cfg.key + 10, strlen(cfg.key) - 11,
+				    decoded_key);
+	if (decoded_len < 32) {
+		fprintf(stderr, "Base64 decoding failed (%s, size %u)\n",
+			cfg.key + 10, decoded_len);
+		return -EINVAL;
+	}
+	decoded_len -= 4;
+	if (decoded_len != 32 && decoded_len != 48 && decoded_len != 64) {
+		fprintf(stderr, "Invalid key length %d\n", decoded_len);
+		return -EINVAL;
+	}
+	crc = crc32(crc, decoded_key, decoded_len);
+	key_crc = ((u_int32_t)decoded_key[decoded_len]) |
+		((u_int32_t)decoded_key[decoded_len + 1] << 8) |
+		((u_int32_t)decoded_key[decoded_len + 2] << 16) |
+		((u_int32_t)decoded_key[decoded_len + 3] << 24);
+	if (key_crc != crc) {
+		fprintf(stderr, "CRC mismatch (key %08x, crc %08x)\n",
+			key_crc, crc);
+		return -EINVAL;
+	}
+	printf("Key is valid (HMAC %d, length %d, CRC %08x)\n",
+	       hmac, decoded_len, crc);
+	return 0;
+}
+
 static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
 {
 	const char *desc = "Send Get Log Page request to Discovery Controller.";
diff --git a/util/base64.c b/util/base64.c
index 328c8ea..2cc7ff2 100644
--- a/util/base64.c
+++ b/util/base64.c
@@ -20,6 +20,7 @@
  */
 
 #include <stdlib.h>
+#include <string.h>
 
 static const char base64_table[65] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -60,3 +61,40 @@ int base64_encode(const unsigned char *src, int srclen, char *dst)
 
 	return cp - dst;
 }
+
+/**
+ * base64_decode() - base64-decode some bytes
+ * @src: the base64-encoded string to decode
+ * @len: number of bytes to decode
+ * @dst: (output) the decoded bytes.
+ *
+ * Decodes the base64-encoded bytes @src according to RFC 4648.
+ *
+ * Return: number of decoded bytes
+ */
+int base64_decode(const char *src, int srclen, unsigned char *dst)
+{
+	u_int32_t ac = 0;
+	int i, bits = 0;
+	unsigned char *bp = dst;
+
+        for (i = 0; i < srclen; i++) {
+                const char *p = strchr(base64_table, src[i]);
+
+                if (src[i] == '=') {
+                        ac = (ac << 6);
+                        continue;
+                }
+                if (p == NULL || src[i] == 0)
+                        return -1;
+                ac = (ac << 6) | (p - base64_table);
+                bits += 6;
+                if (bits >= 8) {
+                        bits -= 8;
+                        *bp++ = (unsigned char)(ac >> bits);
+                }
+	}
+	if (ac && ((1 << bits) - 1))
+		return -1;
+	return bp - dst;
+}
diff --git a/util/base64.h b/util/base64.h
index 374f5e6..609a877 100644
--- a/util/base64.h
+++ b/util/base64.h
@@ -2,5 +2,6 @@
 #define _BASE64_H
 
 int base64_encode(const unsigned char *src, int len, char *dst);
+int base64_decode(const char *src, int len, unsigned char *dst);
 
 #endif /* _BASE64_H */
-- 
2.26.2


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] Add 'gen-dhchap-key' command
  2021-09-13 10:24 ` [PATCH 1/2] Add 'gen-dhchap-key' command Hannes Reinecke
@ 2021-09-13 19:26   ` Sagi Grimberg
  2021-09-14  6:18     ` Hannes Reinecke
  0 siblings, 1 reply; 7+ messages in thread
From: Sagi Grimberg @ 2021-09-13 19:26 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig; +Cc: Keith Busch, linux-nvme



On 9/13/21 1:24 PM, Hannes Reinecke wrote:
> Add command 'gen-dhchap-key' to generate an DH-HMAC-CHAP host key
> for NVMe in-band authentication.
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> ---
>   Documentation/nvme-gen-dhchap-key.txt |  52 +++++++++
>   Makefile                              |  17 ++-
>   nvme-builtin.h                        |   1 +
>   nvme.c                                | 155 ++++++++++++++++++++++++++
>   util/argconfig.h                      |   1 +
>   util/base64.c                         |  62 +++++++++++
>   util/base64.h                         |   6 +
>   7 files changed, 293 insertions(+), 1 deletion(-)
>   create mode 100644 Documentation/nvme-gen-dhchap-key.txt
>   create mode 100644 util/base64.c
>   create mode 100644 util/base64.h
> 
> diff --git a/Documentation/nvme-gen-dhchap-key.txt b/Documentation/nvme-gen-dhchap-key.txt
> new file mode 100644
> index 0000000..eecde76
> --- /dev/null
> +++ b/Documentation/nvme-gen-dhchap-key.txt
> @@ -0,0 +1,52 @@
> +nvme-gen-dhchap-key(1)
> +===================
> +
> +NAME
> +----
> +nvme-gen-dhchap-key - Generate a host DH-HMAC-CHAP key
> +
> +SYNOPSIS
> +--------
> +[verse]
> +'nvme gen-dhchap-key' [--hmac=<hmac-id> | -h <hmac-id>]
> +		      [--secret=<secret> | -s <secret> ]
> +		      [--key-length=<len> | -l <len> ]
> +		      [--nqn=<host-nqn> | -n <host-nqn> ]

Maybe it is a good idea to have some sane defaults create
/etc/nvme/hostkey when installing nvme-cli? Or will it
take it unconditionally?

> +
> +DESCRIPTION
> +-----------
> +Generate a base64-encoded DH-HMAC-CHAP host key in the form:
> +DHHC-1:00:ia6zGodOr4SEG0Zzaw398rpY0wqipUWj4jWjUh4HWUz6aQ2n:
> +and prints it to stdout.
> +
> +OPTIONS
> +-------
> +-h <hmac-id>::
> +--hmac=<hmac-id>::
> +	Select a HMAC algorithm to use. Possible values are:
> +	0 - No HMAC algorithm
> +	1 - SHA-256
> +	2 - SHA-384
> +	3 - SHA-512
> +
> +-s <secret>::
> +--secret=<secret>::
> +	Secret value (in hexadecimal) to be used for the key. If none are
> +	provided a random value is used.
> +
> +-l <len>::
> +--key-length=<len>::
> +	Length of the resulting key. Possible values are 32, 48, or 64.
> +
> +-n <hostnqn>::
> +--nqn=<hostnqn>::
> +	Host-NQN to be used for the transformation. This parameter is only
> +	valid if a non-zero HMAC function has been specified.
> +
> +EXAMPLES
> +--------
> +No Examples
> +
> +NVME
> +----
> +Part of the nvme-user suite
> diff --git a/Makefile b/Makefile
> index 5fbdfd0..984b380 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -5,6 +5,8 @@ LIBUUID = $(shell $(LD) -o /dev/null -luuid >/dev/null 2>&1; echo $$?)
>   LIBHUGETLBFS = $(shell $(LD) -o /dev/null -lhugetlbfs >/dev/null 2>&1; echo $$?)
>   HAVE_SYSTEMD = $(shell pkg-config --exists libsystemd  --atleast-version=242; echo $$?)
>   LIBJSONC = $(shell $(LD) -o /dev/null -ljson-c >/dev/null 2>&1; echo $$?)
> +LIBZ = $(shell $(LD) -o /dev/null -lz >/dev/null 2>&1; echo $$?)
> +LIBOPENSSL = $(shell $(LD) -o /dev/null -lssl >/dev/null 2>&1; echo $$?)
>   NVME = nvme
>   INSTALL ?= install
>   DESTDIR =
> @@ -33,6 +35,18 @@ ifeq ($(LIBHUGETLBFS),0)
>   	override LIB_DEPENDS += hugetlbfs
>   endif
>   
> +ifeq ($(LIBZ),0)
> +	override LDFLAGS += -lz
> +	override CFLAGS += -DLIBZ
> +	override LIB_DEPENDS += zlib
> +endif
> +
> +ifeq ($(LIBOPENSSL),0)
> +	override LDFLAGS += -lssl -lcrypto
> +	override CFLAGS += -DOPENSSL
> +	override LIB_DEPENDS += openssl
> +endif
> +
>   INC=-Iutil
>   
>   ifeq ($(HAVE_SYSTEMD),0)
> @@ -70,7 +84,8 @@ OBJS := nvme-print.o nvme-rpmb.o \
>   	fabrics.o nvme-models.o plugin.o
>   
>   UTIL_OBJS := util/argconfig.o util/suffix.o util/parser.o \
> -	util/cleanup.o
> +	util/cleanup.o util/base64.o
> +
>   ifneq ($(LIBJSONC), 0)
>   override UTIL_OBJS += util/json.o
>   endif
> diff --git a/nvme-builtin.h b/nvme-builtin.h
> index a413d00..a256f05 100644
> --- a/nvme-builtin.h
> +++ b/nvme-builtin.h
> @@ -81,6 +81,7 @@ COMMAND_LIST(
>   	ENTRY("disconnect-all", "Disconnect from all connected NVMeoF subsystems", disconnect_all_cmd)
>   	ENTRY("gen-hostnqn", "Generate NVMeoF host NQN", gen_hostnqn_cmd)
>   	ENTRY("show-hostnqn", "Show NVMeoF host NQN", show_hostnqn_cmd)
> +	ENTRY("gen-dhchap-key", "Generate NVMeoF DH-HMAC-CHAP host key", gen_dhchap_key)
>   	ENTRY("dir-receive", "Submit a Directive Receive command, return results", dir_receive)
>   	ENTRY("dir-send", "Submit a Directive Send command, return results", dir_send)
>   	ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller ", virtual_mgmt)
> diff --git a/nvme.c b/nvme.c
> index 79605f5..5791357 100644
> --- a/nvme.c
> +++ b/nvme.c
> @@ -36,22 +36,31 @@
>   #include <math.h>
>   #include <dirent.h>
>   #include <libgen.h>
> +#include <zlib.h>
>   
>   #ifdef LIBHUGETLBFS
>   #include <hugetlbfs.h>
>   #endif
>   
> +#ifdef OPENSSL
> +#include <openssl/engine.h>
> +#include <openssl/evp.h>
> +#include <openssl/hmac.h>
> +#endif
> +
>   #include <linux/fs.h>
>   
>   #include <sys/mman.h>
>   #include <sys/types.h>
>   #include <sys/stat.h>
> +#include <sys/random.h>
>   
>   #include "common.h"
>   #include "nvme.h"
>   #include "libnvme.h"
>   #include "nvme-print.h"
>   #include "plugin.h"
> +#include "base64.h"
>   
>   #include "argconfig.h"
>   #include "fabrics.h"
> @@ -5750,6 +5759,152 @@ static int show_hostnqn_cmd(int argc, char **argv, struct command *command, stru
>   	return 0;
>   }
>   
> +static int gen_dhchap_key(int argc, char **argv, struct command *command, struct plugin *plugin)
> +{
> +	const char *desc = "Generate a DH-HMAC-CHAP host key usable "\
> +		"for NVMe In-Band Authentication.";
> +	const char *secret = "Optional secret (in hexadecimal characters) "\
> +		"to be used to initialize the host key.";
> +	const char *key_len = "Length of the resulting key "\
> +		"(32, 48, or 64 bytes).";
> +	const char *hmac = "HMAC function to use for key transformation "\
> +		"(0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512).";
> +	const char *nqn = "Host NQN to use for key transformation.";
> +
> +	char *hostnqn;
> +	char *raw_secret;
> +	unsigned char key[68];
> +	char encoded_key[128];
> +	unsigned long crc = crc32(0L, NULL, 0);
> +	int err = 0;
> +#ifdef OPENSSL
> +	const EVP_MD *md = NULL;
> +#else
> +	const char *md = NULL;
> +#endif
> +	struct config {
> +		char *secret;
> +		unsigned int key_len;
> +		char *nqn;
> +		unsigned int hmac;
> +	};
> +
> +	struct config cfg = {
> +		.secret = NULL,
> +		.key_len = 32,
> +		.nqn = NULL,
> +		.hmac = 0,
> +	};
> +
> +	OPT_ARGS(opts) = {
> +		OPT_STR("secret", 's', &cfg.secret, secret),
> +		OPT_UINT("key-length", 'l', &cfg.key_len, key_len),
> +		OPT_STR("nqn", 'n', &cfg.nqn, nqn),
> +		OPT_UINT("hmac", 'h', &cfg.hmac, hmac),
> +		OPT_END()
> +	};
> +
> +	err = argconfig_parse(argc, argv, desc, opts);
> +	if (err)
> +		return err;
> +
> +	if (cfg.key_len != 32 && cfg.key_len != 48 && cfg.key_len != 64) {
> +		fprintf(stderr, "Invalid key length %u\n", cfg.key_len);
> +		return -EINVAL;
> +	}
> +	if (cfg.hmac > 3) {
> +		fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac);
> +		return -EINVAL;
> +	}
> +	if (cfg.hmac > 0) {
> +#ifdef OPENSSL
> +		if (!cfg.nqn) {
> +			hostnqn = nvmf_hostnqn_from_file();
> +			if (!hostnqn) {
> +				fprintf(stderr, "Could not read host NQN\n");
> +				return -ENOENT;
> +			}
> +		} else {
> +			hostnqn = cfg.nqn;
> +		}
> +		switch (cfg.hmac) {
> +		case 1:
> +			md = EVP_sha256();
> +			break;
> +		case 2:
> +			md = EVP_sha384();
> +			break;
> +		case 3:
> +			md = EVP_sha512();
> +			break;
> +		default:
> +			break;
> +		}
> +#else
> +		fprintf(stderr, "HMAC transformation not supported; "\
> +			"recompile with OpenSSL support.\n");
> +		return -EINVAL;
> +#endif
> +	}
> +	raw_secret = malloc(cfg.key_len);
> +	if (!raw_secret)
> +		return -ENOMEM;
> +	if (!cfg.secret) {
> +		if (getrandom(raw_secret, cfg.key_len, GRND_NONBLOCK) < 0)
> +			return errno;
> +	} else {
> +		int secret_len = 0, i;
> +		unsigned int c;
> +
> +		for (i = 0; i < strlen(cfg.secret); i+=2) {
> +			if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
> +				fprintf(stderr, "Invalid secret '%s'\n",
> +					cfg.secret);
> +				return -EINVAL;
> +			}
> +			raw_secret[secret_len++] = (unsigned char)c;
> +		}
> +		if (secret_len != cfg.key_len) {
> +			fprintf(stderr, "Invalid key length (%d bytes)\n",
> +				secret_len);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (md) {
> +#ifdef OPENSSL
> +		HMAC_CTX *hmac_ctx = HMAC_CTX_new();
> +		const char hmac_seed[] = "NVMe-over-Fabrics";
> +		unsigned int key_len;
> +
> +		ENGINE_load_builtin_engines();
> +		ENGINE_register_all_complete();
> +
> +		HMAC_Init_ex(hmac_ctx, raw_secret, cfg.key_len,md, NULL);
> +		HMAC_Update(hmac_ctx, (unsigned char *)hostnqn,
> +			    strlen(hostnqn));
> +		HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed,
> +			    strlen(hmac_seed));
> +		HMAC_Final(hmac_ctx, key, &key_len);
> +		HMAC_CTX_free(hmac_ctx);
> +#endif
> +	} else {
> +		memcpy(key, raw_secret, cfg.key_len);
> +	}
> +
> +	crc = crc32(crc, key, cfg.key_len);
> +	key[cfg.key_len++] = crc & 0xff;
> +	key[cfg.key_len++] = (crc >> 8) & 0xff;
> +	key[cfg.key_len++] = (crc >> 16) & 0xff;
> +	key[cfg.key_len++] = (crc >> 24) & 0xff;
> +
> +	memset(encoded_key, 0, sizeof(encoded_key));
> +	base64_encode(key, cfg.key_len, encoded_key);
> +
> +	printf("DHHC-1:%02x:%s:\n", cfg.hmac, encoded_key);
> +	return 0;
> +}
> +
>   static int discover_cmd(int argc, char **argv, struct command *command, struct plugin *plugin)
>   {
>   	const char *desc = "Send Get Log Page request to Discovery Controller.";
> diff --git a/util/argconfig.h b/util/argconfig.h
> index 1a5c693..3b6da4c 100644
> --- a/util/argconfig.h
> +++ b/util/argconfig.h
> @@ -100,6 +100,7 @@ enum argconfig_types {
>   #define OPT_FMT(l, s, v, d)  OPT_STRING(l, s, "FMT", v, d)
>   #define OPT_FILE(l, s, v, d) OPT_STRING(l, s, "FILE", v, d)
>   #define OPT_LIST(l, s, v, d) OPT_STRING(l, s, "LIST", v, d)
> +#define OPT_STR(l, s, v, d) OPT_STRING(l, s, "STRING", v, d)
>   
>   struct argconfig_commandline_options {
>   	const char *option;
> diff --git a/util/base64.c b/util/base64.c
> new file mode 100644
> index 0000000..328c8ea
> --- /dev/null
> +++ b/util/base64.c
> @@ -0,0 +1,62 @@
> +/*
> + * base64.c - RFC4648-compliant base64 encoding
> + *
> + * Copyright (c) 2020 Hannes Reinecke, SUSE
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
> + * MA  02110-1301, USA.
> + */
> +
> +#include <stdlib.h>
> +
> +static const char base64_table[65] =
> +	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
> +
> +/**
> + * base64_encode() - base64-encode some bytes
> + * @src: the bytes to encode
> + * @srclen: number of bytes to encode
> + * @dst: (output) the base64-encoded string.  Not NUL-terminated.
> + *
> + * Encodes the input string using characters from the set [A-Za-z0-9+,].
> + * The encoded string is roughly 4/3 times the size of the input string.
> + *
> + * Return: length of the encoded string
> + */
> +int base64_encode(const unsigned char *src, int srclen, char *dst)
> +{
> +	int i, bits = 0;
> +	u_int32_t ac = 0;
> +	char *cp = dst;
> +
> +	for (i = 0; i < srclen; i++) {
> +		ac = (ac << 8) | src[i];
> +		bits += 8;
> +		do {
> +			bits -= 6;
> +			*cp++ = base64_table[(ac >> bits) & 0x3f];
> +		} while (bits >= 6);
> +	}
> +	if (bits) {
> +		bits -= 6;
> +		*cp++ = base64_table[(ac >> bits) & 0x3f];
> +	}
> +	while (bits < 0) {
> +		*cp++ = '=';
> +		bits += 2;
> +	}
> +
> +	return cp - dst;
> +}
> diff --git a/util/base64.h b/util/base64.h
> new file mode 100644
> index 0000000..374f5e6
> --- /dev/null
> +++ b/util/base64.h
> @@ -0,0 +1,6 @@
> +#ifndef _BASE64_H
> +#define _BASE64_H
> +
> +int base64_encode(const unsigned char *src, int len, char *dst);
> +
> +#endif /* _BASE64_H */
> 


Overall looks good, do we want to allow different host keys
for different NVM subsystems?

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] Add 'gen-dhchap-key' command
  2021-09-13 19:26   ` Sagi Grimberg
@ 2021-09-14  6:18     ` Hannes Reinecke
  2021-09-14  6:49       ` Sagi Grimberg
  0 siblings, 1 reply; 7+ messages in thread
From: Hannes Reinecke @ 2021-09-14  6:18 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig; +Cc: Keith Busch, linux-nvme

On 9/13/21 9:26 PM, Sagi Grimberg wrote:
> 
> 
> On 9/13/21 1:24 PM, Hannes Reinecke wrote:
>> Add command 'gen-dhchap-key' to generate an DH-HMAC-CHAP host key
>> for NVMe in-band authentication.
>>
>> Signed-off-by: Hannes Reinecke <hare@suse.de>
>> ---
>>   Documentation/nvme-gen-dhchap-key.txt |  52 +++++++++
>>   Makefile                              |  17 ++-
>>   nvme-builtin.h                        |   1 +
>>   nvme.c                                | 155 ++++++++++++++++++++++++++
>>   util/argconfig.h                      |   1 +
>>   util/base64.c                         |  62 +++++++++++
>>   util/base64.h                         |   6 +
>>   7 files changed, 293 insertions(+), 1 deletion(-)
>>   create mode 100644 Documentation/nvme-gen-dhchap-key.txt
>>   create mode 100644 util/base64.c
>>   create mode 100644 util/base64.h
>>
>> diff --git a/Documentation/nvme-gen-dhchap-key.txt 
>> b/Documentation/nvme-gen-dhchap-key.txt
>> new file mode 100644
>> index 0000000..eecde76
>> --- /dev/null
>> +++ b/Documentation/nvme-gen-dhchap-key.txt
>> @@ -0,0 +1,52 @@
>> +nvme-gen-dhchap-key(1)
>> +===================
>> +
>> +NAME
>> +----
>> +nvme-gen-dhchap-key - Generate a host DH-HMAC-CHAP key
>> +
>> +SYNOPSIS
>> +--------
>> +[verse]
>> +'nvme gen-dhchap-key' [--hmac=<hmac-id> | -h <hmac-id>]
>> +              [--secret=<secret> | -s <secret> ]
>> +              [--key-length=<len> | -l <len> ]
>> +              [--nqn=<host-nqn> | -n <host-nqn> ]
> 
> Maybe it is a good idea to have some sane defaults create
> /etc/nvme/hostkey when installing nvme-cli? Or will it
> take it unconditionally?
> 
That was the idea; will put some statements into the man-page.
But 'gen-dhchap-key' shouldn't be doing it automatically, but rather 
behave similar to 'gen-hostnqn' (which also only prints out the host NQN 
to stdout, and leaves is to the caller to forward that to 
/etc/nvme/hostnqn).

>> +
>> +DESCRIPTION
>> +-----------
>> +Generate a base64-encoded DH-HMAC-CHAP host key in the form:
>> +DHHC-1:00:ia6zGodOr4SEG0Zzaw398rpY0wqipUWj4jWjUh4HWUz6aQ2n:
>> +and prints it to stdout.
>> +
>> +OPTIONS
>> +-------
>> +-h <hmac-id>::
>> +--hmac=<hmac-id>::
>> +    Select a HMAC algorithm to use. Possible values are:
>> +    0 - No HMAC algorithm
>> +    1 - SHA-256
>> +    2 - SHA-384
>> +    3 - SHA-512
>> +
>> +-s <secret>::
>> +--secret=<secret>::
>> +    Secret value (in hexadecimal) to be used for the key. If none are
>> +    provided a random value is used.
>> +
>> +-l <len>::
>> +--key-length=<len>::
>> +    Length of the resulting key. Possible values are 32, 48, or 64.
>> +
>> +-n <hostnqn>::
>> +--nqn=<hostnqn>::
>> +    Host-NQN to be used for the transformation. This parameter is only
>> +    valid if a non-zero HMAC function has been specified.
>> +
>> +EXAMPLES
>> +--------
>> +No Examples
>> +
>> +NVME
>> +----
>> +Part of the nvme-user suite
>> diff --git a/Makefile b/Makefile
>> index 5fbdfd0..984b380 100644
>> --- a/Makefile
>> +++ b/Makefile
>> @@ -5,6 +5,8 @@ LIBUUID = $(shell $(LD) -o /dev/null -luuid >/dev/null 
>> 2>&1; echo $$?)
>>   LIBHUGETLBFS = $(shell $(LD) -o /dev/null -lhugetlbfs >/dev/null 
>> 2>&1; echo $$?)
>>   HAVE_SYSTEMD = $(shell pkg-config --exists libsystemd  
>> --atleast-version=242; echo $$?)
>>   LIBJSONC = $(shell $(LD) -o /dev/null -ljson-c >/dev/null 2>&1; echo 
>> $$?)
>> +LIBZ = $(shell $(LD) -o /dev/null -lz >/dev/null 2>&1; echo $$?)
>> +LIBOPENSSL = $(shell $(LD) -o /dev/null -lssl >/dev/null 2>&1; echo $$?)
>>   NVME = nvme
>>   INSTALL ?= install
>>   DESTDIR =
>> @@ -33,6 +35,18 @@ ifeq ($(LIBHUGETLBFS),0)
>>       override LIB_DEPENDS += hugetlbfs
>>   endif
>> +ifeq ($(LIBZ),0)
>> +    override LDFLAGS += -lz
>> +    override CFLAGS += -DLIBZ
>> +    override LIB_DEPENDS += zlib
>> +endif
>> +
>> +ifeq ($(LIBOPENSSL),0)
>> +    override LDFLAGS += -lssl -lcrypto
>> +    override CFLAGS += -DOPENSSL
>> +    override LIB_DEPENDS += openssl
>> +endif
>> +
>>   INC=-Iutil
>>   ifeq ($(HAVE_SYSTEMD),0)
>> @@ -70,7 +84,8 @@ OBJS := nvme-print.o nvme-rpmb.o \
>>       fabrics.o nvme-models.o plugin.o
>>   UTIL_OBJS := util/argconfig.o util/suffix.o util/parser.o \
>> -    util/cleanup.o
>> +    util/cleanup.o util/base64.o
>> +
>>   ifneq ($(LIBJSONC), 0)
>>   override UTIL_OBJS += util/json.o
>>   endif
>> diff --git a/nvme-builtin.h b/nvme-builtin.h
>> index a413d00..a256f05 100644
>> --- a/nvme-builtin.h
>> +++ b/nvme-builtin.h
>> @@ -81,6 +81,7 @@ COMMAND_LIST(
>>       ENTRY("disconnect-all", "Disconnect from all connected NVMeoF 
>> subsystems", disconnect_all_cmd)
>>       ENTRY("gen-hostnqn", "Generate NVMeoF host NQN", gen_hostnqn_cmd)
>>       ENTRY("show-hostnqn", "Show NVMeoF host NQN", show_hostnqn_cmd)
>> +    ENTRY("gen-dhchap-key", "Generate NVMeoF DH-HMAC-CHAP host key", 
>> gen_dhchap_key)
>>       ENTRY("dir-receive", "Submit a Directive Receive command, return 
>> results", dir_receive)
>>       ENTRY("dir-send", "Submit a Directive Send command, return 
>> results", dir_send)
>>       ENTRY("virt-mgmt", "Manage Flexible Resources between Primary 
>> and Secondary Controller ", virtual_mgmt)
>> diff --git a/nvme.c b/nvme.c
>> index 79605f5..5791357 100644
>> --- a/nvme.c
>> +++ b/nvme.c
>> @@ -36,22 +36,31 @@
>>   #include <math.h>
>>   #include <dirent.h>
>>   #include <libgen.h>
>> +#include <zlib.h>
>>   #ifdef LIBHUGETLBFS
>>   #include <hugetlbfs.h>
>>   #endif
>> +#ifdef OPENSSL
>> +#include <openssl/engine.h>
>> +#include <openssl/evp.h>
>> +#include <openssl/hmac.h>
>> +#endif
>> +
>>   #include <linux/fs.h>
>>   #include <sys/mman.h>
>>   #include <sys/types.h>
>>   #include <sys/stat.h>
>> +#include <sys/random.h>
>>   #include "common.h"
>>   #include "nvme.h"
>>   #include "libnvme.h"
>>   #include "nvme-print.h"
>>   #include "plugin.h"
>> +#include "base64.h"
>>   #include "argconfig.h"
>>   #include "fabrics.h"
>> @@ -5750,6 +5759,152 @@ static int show_hostnqn_cmd(int argc, char 
>> **argv, struct command *command, stru
>>       return 0;
>>   }
>> +static int gen_dhchap_key(int argc, char **argv, struct command 
>> *command, struct plugin *plugin)
>> +{
>> +    const char *desc = "Generate a DH-HMAC-CHAP host key usable "\
>> +        "for NVMe In-Band Authentication.";
>> +    const char *secret = "Optional secret (in hexadecimal characters) "\
>> +        "to be used to initialize the host key.";
>> +    const char *key_len = "Length of the resulting key "\
>> +        "(32, 48, or 64 bytes).";
>> +    const char *hmac = "HMAC function to use for key transformation "\
>> +        "(0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512).";
>> +    const char *nqn = "Host NQN to use for key transformation.";
>> +
>> +    char *hostnqn;
>> +    char *raw_secret;
>> +    unsigned char key[68];
>> +    char encoded_key[128];
>> +    unsigned long crc = crc32(0L, NULL, 0);
>> +    int err = 0;
>> +#ifdef OPENSSL
>> +    const EVP_MD *md = NULL;
>> +#else
>> +    const char *md = NULL;
>> +#endif
>> +    struct config {
>> +        char *secret;
>> +        unsigned int key_len;
>> +        char *nqn;
>> +        unsigned int hmac;
>> +    };
>> +
>> +    struct config cfg = {
>> +        .secret = NULL,
>> +        .key_len = 32,
>> +        .nqn = NULL,
>> +        .hmac = 0,
>> +    };
>> +
>> +    OPT_ARGS(opts) = {
>> +        OPT_STR("secret", 's', &cfg.secret, secret),
>> +        OPT_UINT("key-length", 'l', &cfg.key_len, key_len),
>> +        OPT_STR("nqn", 'n', &cfg.nqn, nqn),
>> +        OPT_UINT("hmac", 'h', &cfg.hmac, hmac),
>> +        OPT_END()
>> +    };
>> +
>> +    err = argconfig_parse(argc, argv, desc, opts);
>> +    if (err)
>> +        return err;
>> +
>> +    if (cfg.key_len != 32 && cfg.key_len != 48 && cfg.key_len != 64) {
>> +        fprintf(stderr, "Invalid key length %u\n", cfg.key_len);
>> +        return -EINVAL;
>> +    }
>> +    if (cfg.hmac > 3) {
>> +        fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac);
>> +        return -EINVAL;
>> +    }
>> +    if (cfg.hmac > 0) {
>> +#ifdef OPENSSL
>> +        if (!cfg.nqn) {
>> +            hostnqn = nvmf_hostnqn_from_file();
>> +            if (!hostnqn) {
>> +                fprintf(stderr, "Could not read host NQN\n");
>> +                return -ENOENT;
>> +            }
>> +        } else {
>> +            hostnqn = cfg.nqn;
>> +        }
>> +        switch (cfg.hmac) {
>> +        case 1:
>> +            md = EVP_sha256();
>> +            break;
>> +        case 2:
>> +            md = EVP_sha384();
>> +            break;
>> +        case 3:
>> +            md = EVP_sha512();
>> +            break;
>> +        default:
>> +            break;
>> +        }
>> +#else
>> +        fprintf(stderr, "HMAC transformation not supported; "\
>> +            "recompile with OpenSSL support.\n");
>> +        return -EINVAL;
>> +#endif
>> +    }
>> +    raw_secret = malloc(cfg.key_len);
>> +    if (!raw_secret)
>> +        return -ENOMEM;
>> +    if (!cfg.secret) {
>> +        if (getrandom(raw_secret, cfg.key_len, GRND_NONBLOCK) < 0)
>> +            return errno;
>> +    } else {
>> +        int secret_len = 0, i;
>> +        unsigned int c;
>> +
>> +        for (i = 0; i < strlen(cfg.secret); i+=2) {
>> +            if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
>> +                fprintf(stderr, "Invalid secret '%s'\n",
>> +                    cfg.secret);
>> +                return -EINVAL;
>> +            }
>> +            raw_secret[secret_len++] = (unsigned char)c;
>> +        }
>> +        if (secret_len != cfg.key_len) {
>> +            fprintf(stderr, "Invalid key length (%d bytes)\n",
>> +                secret_len);
>> +            return -EINVAL;
>> +        }
>> +    }
>> +
>> +    if (md) {
>> +#ifdef OPENSSL
>> +        HMAC_CTX *hmac_ctx = HMAC_CTX_new();
>> +        const char hmac_seed[] = "NVMe-over-Fabrics";
>> +        unsigned int key_len;
>> +
>> +        ENGINE_load_builtin_engines();
>> +        ENGINE_register_all_complete();
>> +
>> +        HMAC_Init_ex(hmac_ctx, raw_secret, cfg.key_len,md, NULL);
>> +        HMAC_Update(hmac_ctx, (unsigned char *)hostnqn,
>> +                strlen(hostnqn));
>> +        HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed,
>> +                strlen(hmac_seed));
>> +        HMAC_Final(hmac_ctx, key, &key_len);
>> +        HMAC_CTX_free(hmac_ctx);
>> +#endif
>> +    } else {
>> +        memcpy(key, raw_secret, cfg.key_len);
>> +    }
>> +
>> +    crc = crc32(crc, key, cfg.key_len);
>> +    key[cfg.key_len++] = crc & 0xff;
>> +    key[cfg.key_len++] = (crc >> 8) & 0xff;
>> +    key[cfg.key_len++] = (crc >> 16) & 0xff;
>> +    key[cfg.key_len++] = (crc >> 24) & 0xff;
>> +
>> +    memset(encoded_key, 0, sizeof(encoded_key));
>> +    base64_encode(key, cfg.key_len, encoded_key);
>> +
>> +    printf("DHHC-1:%02x:%s:\n", cfg.hmac, encoded_key);
>> +    return 0;
>> +}
>> +
>>   static int discover_cmd(int argc, char **argv, struct command 
>> *command, struct plugin *plugin)
>>   {
>>       const char *desc = "Send Get Log Page request to Discovery 
>> Controller.";
>> diff --git a/util/argconfig.h b/util/argconfig.h
>> index 1a5c693..3b6da4c 100644
>> --- a/util/argconfig.h
>> +++ b/util/argconfig.h
>> @@ -100,6 +100,7 @@ enum argconfig_types {
>>   #define OPT_FMT(l, s, v, d)  OPT_STRING(l, s, "FMT", v, d)
>>   #define OPT_FILE(l, s, v, d) OPT_STRING(l, s, "FILE", v, d)
>>   #define OPT_LIST(l, s, v, d) OPT_STRING(l, s, "LIST", v, d)
>> +#define OPT_STR(l, s, v, d) OPT_STRING(l, s, "STRING", v, d)
>>   struct argconfig_commandline_options {
>>       const char *option;
>> diff --git a/util/base64.c b/util/base64.c
>> new file mode 100644
>> index 0000000..328c8ea
>> --- /dev/null
>> +++ b/util/base64.c
>> @@ -0,0 +1,62 @@
>> +/*
>> + * base64.c - RFC4648-compliant base64 encoding
>> + *
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
>> + * MA  02110-1301, USA.
>> + */
>> +
>> +#include <stdlib.h>
>> +
>> +static const char base64_table[65] =
>> +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
>> +
>> +/**
>> + * base64_encode() - base64-encode some bytes
>> + * @src: the bytes to encode
>> + * @srclen: number of bytes to encode
>> + * @dst: (output) the base64-encoded string.  Not NUL-terminated.
>> + *
>> + * Encodes the input string using characters from the set [A-Za-z0-9+,].
>> + * The encoded string is roughly 4/3 times the size of the input string.
>> + *
>> + * Return: length of the encoded string
>> + */
>> +int base64_encode(const unsigned char *src, int srclen, char *dst)
>> +{
>> +    int i, bits = 0;
>> +    u_int32_t ac = 0;
>> +    char *cp = dst;
>> +
>> +    for (i = 0; i < srclen; i++) {
>> +        ac = (ac << 8) | src[i];
>> +        bits += 8;
>> +        do {
>> +            bits -= 6;
>> +            *cp++ = base64_table[(ac >> bits) & 0x3f];
>> +        } while (bits >= 6);
>> +    }
>> +    if (bits) {
>> +        bits -= 6;
>> +        *cp++ = base64_table[(ac >> bits) & 0x3f];
>> +    }
>> +    while (bits < 0) {
>> +        *cp++ = '=';
>> +        bits += 2;
>> +    }
>> +
>> +    return cp - dst;
>> +}
>> diff --git a/util/base64.h b/util/base64.h
>> new file mode 100644
>> index 0000000..374f5e6
>> --- /dev/null
>> +++ b/util/base64.h
>> @@ -0,0 +1,6 @@
>> +#ifndef _BASE64_H
>> +#define _BASE64_H
>> +
>> +int base64_encode(const unsigned char *src, int len, char *dst);
>> +
>> +#endif /* _BASE64_H */
>>
> 
> 
> Overall looks good, do we want to allow different host keys
> for different NVM subsystems?

Sigh. It's the same issue as we currently have with allowing different 
host NQNs for the same system.

In theory we could, and there is nothing in the current code which 
forbids us to do so. But tooling around it will be getting arbitrarily 
complex; remember, it's not only the different host keys for different 
NVM subsystems, but possibly also the various NVM subsystem keys if 
someone were to enable bi-directional authentication.

So would just stick with the simple case, and leave this as an exercise 
to the reader.

Which reminds me; might be that there's an update required to handle 
keys for bi-directional authentication ... hmm.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                Kernel Storage Architect
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] Add 'gen-dhchap-key' command
  2021-09-14  6:18     ` Hannes Reinecke
@ 2021-09-14  6:49       ` Sagi Grimberg
  2021-09-14  6:59         ` Sagi Grimberg
  0 siblings, 1 reply; 7+ messages in thread
From: Sagi Grimberg @ 2021-09-14  6:49 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig; +Cc: Keith Busch, linux-nvme


>> On 9/13/21 1:24 PM, Hannes Reinecke wrote:
>>> Add command 'gen-dhchap-key' to generate an DH-HMAC-CHAP host key
>>> for NVMe in-band authentication.
>>>
>>> Signed-off-by: Hannes Reinecke <hare@suse.de>
>>> ---
>>>   Documentation/nvme-gen-dhchap-key.txt |  52 +++++++++
>>>   Makefile                              |  17 ++-
>>>   nvme-builtin.h                        |   1 +
>>>   nvme.c                                | 155 ++++++++++++++++++++++++++
>>>   util/argconfig.h                      |   1 +
>>>   util/base64.c                         |  62 +++++++++++
>>>   util/base64.h                         |   6 +
>>>   7 files changed, 293 insertions(+), 1 deletion(-)
>>>   create mode 100644 Documentation/nvme-gen-dhchap-key.txt
>>>   create mode 100644 util/base64.c
>>>   create mode 100644 util/base64.h
>>>
>>> diff --git a/Documentation/nvme-gen-dhchap-key.txt 
>>> b/Documentation/nvme-gen-dhchap-key.txt
>>> new file mode 100644
>>> index 0000000..eecde76
>>> --- /dev/null
>>> +++ b/Documentation/nvme-gen-dhchap-key.txt
>>> @@ -0,0 +1,52 @@
>>> +nvme-gen-dhchap-key(1)
>>> +===================
>>> +
>>> +NAME
>>> +----
>>> +nvme-gen-dhchap-key - Generate a host DH-HMAC-CHAP key
>>> +
>>> +SYNOPSIS
>>> +--------
>>> +[verse]
>>> +'nvme gen-dhchap-key' [--hmac=<hmac-id> | -h <hmac-id>]
>>> +              [--secret=<secret> | -s <secret> ]
>>> +              [--key-length=<len> | -l <len> ]
>>> +              [--nqn=<host-nqn> | -n <host-nqn> ]
>>
>> Maybe it is a good idea to have some sane defaults create
>> /etc/nvme/hostkey when installing nvme-cli? Or will it
>> take it unconditionally?
>>
> That was the idea; will put some statements into the man-page.
> But 'gen-dhchap-key' shouldn't be doing it automatically, but rather 
> behave similar to 'gen-hostnqn' (which also only prints out the host NQN 
> to stdout, and leaves is to the caller to forward that to 
> /etc/nvme/hostnqn).

Yes, but we also install these files when installing nvme-cli,
look at the spec and makefile (install-hostparams target).

However this one is different I think and we don't want to generate it
by default.

>> Overall looks good, do we want to allow different host keys
>> for different NVM subsystems?
> 
> Sigh. It's the same issue as we currently have with allowing different 
> host NQNs for the same system.
> 
> In theory we could, and there is nothing in the current code which 
> forbids us to do so. But tooling around it will be getting arbitrarily 
> complex; remember, it's not only the different host keys for different 
> NVM subsystems, but possibly also the various NVM subsystem keys if 
> someone were to enable bi-directional authentication.

Yee... it won't be long until we'll hear complaints on this one.
I think we need to have some simple format like:
<default>:<def_hostkey>
<subsystem1>:<hostkey1>
<subsystem2>:<hostkey2>
...

And we will need /etc/nvme/subsyskey in the same format:
<default>:<def_subsyskey>
<subsystem1>:<subsyskey1>
<subsystem2>:<subsyskey2>
...

And ideally we also have a way for nvme-cli to populate it in this
format (or some helper script for it).

> So would just stick with the simple case, and leave this as an exercise 
> to the reader.

Don't know if that is good enough...
What do others think?

> Which reminds me; might be that there's an update required to handle 
> keys for bi-directional authentication ... hmm.

Yes.

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] Add 'gen-dhchap-key' command
  2021-09-14  6:49       ` Sagi Grimberg
@ 2021-09-14  6:59         ` Sagi Grimberg
  0 siblings, 0 replies; 7+ messages in thread
From: Sagi Grimberg @ 2021-09-14  6:59 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig; +Cc: Keith Busch, linux-nvme


> Yee... it won't be long until we'll hear complaints on this one.
> I think we need to have some simple format like:
> <default>:<def_hostkey>
> <subsystem1>:<hostkey1>
> <subsystem2>:<hostkey2>
> ...
> 
> And we will need /etc/nvme/subsyskey in the same format:
> <default>:<def_subsyskey>
> <subsystem1>:<subsyskey1>
> <subsystem2>:<subsyskey2>
> ...
> 
> And ideally we also have a way for nvme-cli to populate it in this
> format (or some helper script for it).
> 
>> So would just stick with the simple case, and leave this as an 
>> exercise to the reader.
> 
> Don't know if that is good enough...
> What do others think?

Also, if we expand the format later, we may introduce a pain when
updating nvme-cli...

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-09-14  6:59 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-13 10:24 [PATCH nvme-cli 0/2] Support commands for NVMe In-band authentication Hannes Reinecke
2021-09-13 10:24 ` [PATCH 1/2] Add 'gen-dhchap-key' command Hannes Reinecke
2021-09-13 19:26   ` Sagi Grimberg
2021-09-14  6:18     ` Hannes Reinecke
2021-09-14  6:49       ` Sagi Grimberg
2021-09-14  6:59         ` Sagi Grimberg
2021-09-13 10:24 ` [PATCH 2/2] Add 'check-dhchap-key' function Hannes Reinecke

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.