From: Hannes Reinecke <hare@suse.de>
To: Sagi Grimberg <sagi@grimberg.me>
Cc: Keith Busch <keith.busch@wdc.com>, Christoph Hellwig <hch@lst.de>,
linux-nvme@lists.infradead.org, Hannes Reinecke <hare@suse.de>
Subject: [PATCH nvme-cli 2/3] Add 'check-dhchap-key' function
Date: Fri, 12 Nov 2021 14:11:10 +0100 [thread overview]
Message-ID: <20211112131111.97599-3-hare@suse.de> (raw)
In-Reply-To: <20211112131111.97599-1-hare@suse.de>
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 | 98 +++++++++++++++++++++++++
util/base64.c | 43 +++++++++++
util/base64.h | 1 +
5 files changed, 174 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 4ad91da..4f8ba8c 100644
--- a/nvme.c
+++ b/nvme.c
@@ -6093,6 +6093,104 @@ 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;
+ }
+ switch (hmac) {
+ case 0:
+ break;
+ case 1:
+ if (strlen(cfg.key) != 59) {
+ fprintf(stderr, "Invalid key length for SHA(256)\n");
+ return -EINVAL;
+ }
+ break;
+ case 2:
+ if (strlen(cfg.key) != 83) {
+ fprintf(stderr, "Invalid key length for SHA(384)\n");
+ return -EINVAL;
+ }
+ break;
+ case 3:
+ if (strlen(cfg.key) != 103) {
+ fprintf(stderr, "Invalid key length for SHA(512)\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ fprintf(stderr, "Invalid HMAC identifier %d\n", hmac);
+ return -EINVAL;
+ break;
+ }
+
+ err = base64_decode(cfg.key + 10, strlen(cfg.key) - 11,
+ decoded_key);
+ if (err < 0) {
+ fprintf(stderr, "Base64 decoding failed, error %d\n",
+ err);
+ return err;
+ }
+ decoded_len = err;
+ 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 d9b8f01..e386c11 100644
--- a/util/base64.c
+++ b/util/base64.c
@@ -20,6 +20,8 @@
*/
#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
static const char base64_table[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -60,3 +62,44 @@ 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);
+ bits += 6;
+ if (bits >= 8)
+ bits -= 8;
+ continue;
+ }
+ if (p == NULL || src[i] == 0)
+ return -EINVAL;
+ ac = (ac << 6) | (p - base64_table);
+ bits += 6;
+ if (bits >= 8) {
+ bits -= 8;
+ *bp++ = (unsigned char)(ac >> bits);
+ }
+ }
+ if (ac && ((1 << bits) - 1))
+ return -EAGAIN;
+
+ 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.31.1
next prev parent reply other threads:[~2021-11-12 13:16 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-11-12 13:11 [PATCHv5 nvme-cli 0/3] nvme-cli authentication support Hannes Reinecke
2021-11-12 13:11 ` [PATCH nvme-cli 1/3] Add 'gen-dhchap-key' command Hannes Reinecke
2021-11-12 13:11 ` Hannes Reinecke [this message]
2021-11-12 13:11 ` [PATCH nvme-cli 3/3] nvme-connect: Add 'dhchap-secret' and 'dhchap-ctrl-secret' arguments Hannes Reinecke
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211112131111.97599-3-hare@suse.de \
--to=hare@suse.de \
--cc=hch@lst.de \
--cc=keith.busch@wdc.com \
--cc=linux-nvme@lists.infradead.org \
--cc=sagi@grimberg.me \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.