]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
Add 'check-dhchap-key' function
authorHannes Reinecke <hare@suse.de>
Mon, 21 Dec 2020 08:27:34 +0000 (09:27 +0100)
committerHannes Reinecke <hare@suse.de>
Thu, 18 Nov 2021 14:53:56 +0000 (15:53 +0100)
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 [new file with mode: 0644]
nvme-builtin.h
nvme.c
util/base64.c
util/base64.h

diff --git a/Documentation/nvme-check-dhchap-key.txt b/Documentation/nvme-check-dhchap-key.txt
new file mode 100644 (file)
index 0000000..75008c7
--- /dev/null
@@ -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
index e8df46a55011ed4c43ef93e164799723207e2b43..94ff09d2eb52e0423e6a56151144cf825e21c37d 100644 (file)
@@ -88,6 +88,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 8820cd27a48c05c5f50f2be6aaa77395416c980a..6ebfa928636c4dd43cc74d2af2dd7b93f8d1fa6e 100644 (file)
--- a/nvme.c
+++ b/nvme.c
@@ -6812,6 +6812,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.";
index d9b8f01982eea4786640602efe74f2da244cc9c5..e386c11e771851dbcd5d8fb004d4ad815945104e 100644 (file)
@@ -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;
+}
index 374f5e63c1b7d870f706b39562e969aeb01dcdf5..609a8777d4144218d83356fe1c79ddc0034cde79 100644 (file)
@@ -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 */