From: Hannes Reinecke Date: Fri, 21 Jan 2022 08:33:16 +0000 (+0100) Subject: nvme: add 'gen-tls-key' and 'check-tls-key' options X-Git-Tag: v2.0-rc1~6^2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=e10138bc98c2190651ccae4852ae6bd4deca0255;p=users%2Fsagi%2Fnvme-cli.git nvme: add 'gen-tls-key' and 'check-tls-key' options Add options to generate and check an NVMe TLS PSK. Signed-off-by: Hannes Reinecke --- diff --git a/Documentation/nvme-check-tls-key.txt b/Documentation/nvme-check-tls-key.txt new file mode 100644 index 00000000..9ef6679b --- /dev/null +++ b/Documentation/nvme-check-tls-key.txt @@ -0,0 +1,31 @@ +nvme-check-tls-key(1) +======================== + +NAME +---- +nvme-check-tls-key - Check a generated NVMe TLS PSK + +SYNOPSIS +-------- +[verse] +'nvme check-tls-key' [--key= ] + +DESCRIPTION +----------- +Checks if the key is a valid NVMe TLS PSK in the PSK interchange format +NVMeTLSkey-1:01:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwNf9LrZ: + + +OPTIONS +------- +-k :: +--key=:: + Key to be checked. + +EXAMPLES +-------- +No Examples + +NVME +---- +Part of the nvme-user suite diff --git a/Documentation/nvme-gen-tls-key.txt b/Documentation/nvme-gen-tls-key.txt new file mode 100644 index 00000000..cfa86147 --- /dev/null +++ b/Documentation/nvme-gen-tls-key.txt @@ -0,0 +1,40 @@ +nvme-gen-tls-key(1) +====================== + +NAME +---- +nvme-gen-tls-key - Generate a NVMe TLS PSK + +SYNOPSIS +-------- +[verse] +'nvme gen-tls-key' [--hmac= | -h ] + [--secret= | -s ] + +DESCRIPTION +----------- +Generate a base64-encoded NVMe TLS pre-shared key (PSK) in +the PSK interchange format +NVMeTLSkey-1:01:VRLbtnN9AQb2WXW3c9+wEf/DRLz0QuLdbYvEhwtdWwNf9LrZ: +and prints it to stdout. + +OPTIONS +------- +-h :: +--hmac=:: + Select a HMAC algorithm to use. Possible values are: + 1 - SHA-256 (default) + 2 - SHA-384 + +-s :: +--secret=:: + Secret value (in hexadecimal) to be used for the key. If none are + provided a random value is used. + +EXAMPLES +-------- +No Examples + +NVME +---- +Part of the nvme-user suite diff --git a/nvme-builtin.h b/nvme-builtin.h index c724ad95..e53a2dfb 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -90,6 +90,8 @@ COMMAND_LIST( 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("gen-tls-key", "Generate NVMeoF TLS PSK", gen_tls_key) + ENTRY("check-tls-key", "Validate NVMeoF TLS PSK", check_tls_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 b3caa523..ce5d2356 100644 --- a/nvme.c +++ b/nvme.c @@ -7242,6 +7242,179 @@ static int check_dhchap_key(int argc, char **argv, struct command *command, stru return 0; } +static int gen_tls_key(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Generate a TLS key in NVMe PSK Interchange format."; + const char *secret = "Optional secret (in hexadecimal characters) "\ + "to be used for the TLS key."; + const char *hmac = "HMAC function to use for the retained key "\ + "(1 = SHA-256, 2 = SHA-384)."; + + unsigned char *raw_secret; + char encoded_key[128]; + int key_len = 32; + unsigned long crc = crc32(0L, NULL, 0); + int err = 0; + + struct config { + char *secret; + unsigned int hmac; + }; + + struct config cfg = { + .secret = NULL, + .hmac = 1, + }; + + OPT_ARGS(opts) = { + OPT_STR("secret", 's', &cfg.secret, secret), + OPT_UINT("hmac", 'm', &cfg.hmac, hmac), + OPT_END() + }; + + err = argconfig_parse(argc, argv, desc, opts); + if (err) + return err; + if (cfg.hmac < 1 || cfg.hmac > 3) { + fprintf(stderr, "Invalid HMAC identifier %u\n", cfg.hmac); + return -EINVAL; + } + + if (cfg.hmac == 2) + key_len = 48; + + raw_secret = malloc(key_len + 4); + if (!raw_secret) + return -ENOMEM; + if (!cfg.secret) { + if (getrandom_bytes(raw_secret, key_len) < 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; + } + if (i >= key_len) { + fprintf(stderr, + "Skipping excess secret bytes\n"); + break; + } + raw_secret[secret_len++] = (unsigned char)c; + } + if (secret_len != key_len) { + fprintf(stderr, "Invalid key length (%d bytes)\n", + secret_len); + return -EINVAL; + } + } + + crc = crc32(crc, raw_secret, key_len); + raw_secret[key_len++] = crc & 0xff; + raw_secret[key_len++] = (crc >> 8) & 0xff; + raw_secret[key_len++] = (crc >> 16) & 0xff; + raw_secret[key_len++] = (crc >> 24) & 0xff; + + memset(encoded_key, 0, sizeof(encoded_key)); + base64_encode(raw_secret, key_len, encoded_key); + + printf("NVMeTLSkey-1:%02x:%s:\n", cfg.hmac, encoded_key); + return 0; +} + +static int check_tls_key(int argc, char **argv, struct command *command, struct plugin *plugin) +{ + const char *desc = "Check a TLS key for NVMe PSK Interchange format.\n"; + const char *key = "TLS key (in PSK Interchange format) "\ + "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, "NVMeTLSkey-1:%02x:*s", &hmac) != 1) { + fprintf(stderr, "Invalid key header '%s'\n", cfg.key); + return -EINVAL; + } + switch (hmac) { + 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; + 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.";