Skip to content

Commit b7cdfb1

Browse files
committed
linux: add nvme_create_raw_secret
Move helper function to create the raw secret from the input string to the library. This allows to use a common function to consistently create the raw secret in nvme-cli and libnvme. For testing purposes it is also helpful to generate a secret based from a pin. Use the pin as seed and create the raw secret with a simple algorithm: function create_key(seed, key_len): output = [] counter = 0 while length(output) < key_len: hash = HASH(seed || encode(counter)) output += hash counter += 1 return output[0:key_len] Signed-off-by: Daniel Wagner <[email protected]>
1 parent b2e613f commit b7cdfb1

4 files changed

Lines changed: 160 additions & 74 deletions

File tree

libnvme/src/libnvme.ld

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ LIBNVME_3 {
66
nvme_close;
77
nvme_create_ctrl;
88
nvme_create_global_ctx;
9+
nvme_create_raw_secret;
910
nvme_ctrl_first_ns;
1011
nvme_ctrl_first_path;
1112
nvme_ctrl_get_config;

libnvme/src/nvme/linux.c

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
#include <sys/ioctl.h>
2121
#include <sys/param.h>
22+
#if HAVE_SYS_RANDOM
23+
#include <sys/random.h>
24+
#endif
2225
#include <sys/stat.h>
2326

2427
#ifndef _GNU_SOURCE
@@ -163,6 +166,14 @@ __public int nvme_gen_dhchap_key(struct nvme_global_ctx *ctx,
163166
return 0;
164167
}
165168

169+
__public int nvme_create_raw_secret(struct nvme_global_ctx *ctx,
170+
const char *secret, size_t key_len, unsigned char **raw_secret)
171+
{
172+
nvme_msg(ctx, LOG_ERR, "NVMe TLS 2.0 is not supported; "
173+
"recompile with OpenSSL support.\n");
174+
return -ENOTSUP;
175+
}
176+
166177
static int derive_retained_key(struct nvme_global_ctx *ctx,
167178
int hmac, const char *hostnqn, unsigned char *generated,
168179
unsigned char *retained, size_t key_len)
@@ -745,7 +756,130 @@ static int derive_psk_digest(struct nvme_global_ctx *ctx,
745756

746757
return strlen(digest);
747758
}
748-
#endif /* !CONFIG_OPENSSL */
759+
760+
static ssize_t getrandom_bytes(void *buf, size_t buflen)
761+
{
762+
ssize_t result;
763+
#if HAVE_SYS_RANDOM
764+
result = getrandom(buf, buflen, GRND_NONBLOCK);
765+
#else
766+
_cleanup_fd_ int fd = -1;
767+
768+
fd = open("/dev/urandom", O_RDONLY);
769+
if (fd < 0)
770+
return -errno;
771+
result = read(fd, buf, buflen);
772+
#endif
773+
if (result < 0)
774+
return -errno;
775+
return result;
776+
}
777+
778+
static ssize_t getswordfish(struct nvme_global_ctx *ctx,
779+
const char *seed, void *buf, size_t buflen)
780+
{
781+
unsigned char hash[EVP_MAX_MD_SIZE];
782+
EVP_MD_CTX *md_ctx;
783+
size_t copied = 0;
784+
785+
md_ctx = EVP_MD_CTX_new();
786+
if (!md_ctx)
787+
return -ENOMEM;
788+
789+
while (copied < buflen) {
790+
unsigned int counter = 0;
791+
unsigned int hash_len;
792+
size_t to_copy;
793+
794+
if (EVP_DigestInit_ex(md_ctx, EVP_sha256(), NULL) != 1)
795+
goto err;
796+
797+
EVP_DigestUpdate(md_ctx, seed, strlen(seed));
798+
EVP_DigestUpdate(md_ctx, &counter, sizeof(counter));
799+
800+
if (EVP_DigestFinal_ex(md_ctx, hash, &hash_len) != 1)
801+
goto err;
802+
803+
to_copy = buflen - copied;
804+
if (to_copy > hash_len)
805+
to_copy = hash_len;
806+
807+
memcpy((unsigned char *)buf + copied, hash, to_copy);
808+
copied += to_copy;
809+
counter++;
810+
}
811+
812+
EVP_MD_CTX_free(md_ctx);
813+
return buflen;
814+
815+
err:
816+
EVP_MD_CTX_free(md_ctx);
817+
return -EIO;
818+
}
819+
820+
__public int nvme_create_raw_secret(struct nvme_global_ctx *ctx,
821+
const char *secret, size_t key_len, unsigned char **raw_secret)
822+
{
823+
_cleanup_free_ unsigned char *buf = NULL;
824+
int secret_len = 0, i, err;
825+
unsigned int c;
826+
827+
if (key_len != 32 && key_len != 48 && key_len != 64) {
828+
nvme_msg(ctx, LOG_ERR, "Invalid key length %ld", key_len);
829+
return -EINVAL;
830+
}
831+
832+
buf = malloc(key_len);
833+
if (!buf)
834+
return -ENOMEM;
835+
836+
if (!secret) {
837+
err = getrandom_bytes(buf, key_len);
838+
if (err < 0)
839+
return err;
840+
841+
goto out;
842+
}
843+
844+
if (strlen(secret) < 4) {
845+
nvme_msg(ctx, LOG_ERR, "Input secret too short\n");
846+
return -EINVAL;
847+
}
848+
849+
if (!strncmp(secret, "pin:", 4)) {
850+
err = getswordfish(ctx, &secret[4], buf, key_len);
851+
if (err < 0)
852+
return err;
853+
854+
goto out;
855+
}
856+
857+
for (i = 0; i < strlen(secret); i += 2) {
858+
if (sscanf(&secret[i], "%02x", &c) != 1) {
859+
nvme_msg(ctx, LOG_ERR,
860+
"Invalid secret '%s'", secret);
861+
return -EINVAL;
862+
}
863+
if (i >= key_len * 2) {
864+
nvme_msg(ctx, LOG_ERR,
865+
"Skipping excess secret bytes\n");
866+
break;
867+
}
868+
buf[secret_len++] = (unsigned char)c;
869+
}
870+
if (secret_len != key_len) {
871+
nvme_msg(ctx, LOG_ERR,
872+
"Invalid key length (%d bytes)\n", secret_len);
873+
return -EINVAL;
874+
}
875+
876+
out:
877+
*raw_secret = buf;
878+
buf = NULL;
879+
return 0;
880+
}
881+
882+
#endif /* CONFIG_OPENSSL */
749883

750884
static int gen_tls_identity(const char *hostnqn, const char *subsysnqn,
751885
int version, int cipher, char *digest,

libnvme/src/nvme/linux.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,24 @@ int nvme_lookup_key(struct nvme_global_ctx *ctx, const char *type,
103103
*/
104104
int nvme_set_keyring(struct nvme_global_ctx *ctx, long keyring_id);
105105

106+
/**
107+
* nvme_create_raw_secret - Generate a raw secret buffer from input data
108+
* @ctx: struct nvme_global_ctx object
109+
* @secret: Input secret data
110+
* @key_len: The length of the raw_secret in bytes
111+
* @raw_secret: Return buffer with the generated raw secret
112+
*
113+
* Transforms the provided @secret into a raw secret buffer suitable for
114+
* use with NVMe key management operations.
115+
*
116+
* The generated raw secret can subsequently be passed to nvme_read_key()
117+
* or nvme_update_key().
118+
*
119+
* Return: 0 on success, or a negative error code on failure.
120+
*/
121+
int nvme_create_raw_secret(struct nvme_global_ctx *ctx,
122+
const char *secret, size_t key_len, unsigned char **raw_secret);
123+
106124
/**
107125
* nvme_read_key() - Read key raw data
108126
* @ctx: struct nvme_global_ctx object

nvme.c

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@
4646
#include <sys/stat.h>
4747
#include <sys/types.h>
4848

49-
#if HAVE_SYS_RANDOM
50-
#include <sys/random.h>
51-
#endif
5249

5350
#include <libnvme.h>
5451

@@ -311,24 +308,6 @@ static OPT_VALS(feature_name) = {
311308
VAL_END()
312309
};
313310

314-
static ssize_t getrandom_bytes(void *buf, size_t buflen)
315-
{
316-
ssize_t result;
317-
#if HAVE_SYS_RANDOM
318-
result = getrandom(buf, buflen, GRND_NONBLOCK);
319-
#else
320-
_cleanup_fd_ int fd = -1;
321-
322-
fd = open("/dev/urandom", O_RDONLY);
323-
if (fd < 0)
324-
return -errno;
325-
result = read(fd, buf, buflen);
326-
#endif
327-
if (result < 0)
328-
return -errno;
329-
return result;
330-
}
331-
332311
static int check_arg_dev(int argc, char **argv)
333312
{
334313
if (optind >= argc) {
@@ -9642,32 +9621,9 @@ static int gen_dhchap_key(int argc, char **argv, struct command *acmd, struct pl
96429621
cfg.key_len = 32;
96439622
}
96449623

9645-
if (cfg.key_len != 32 && cfg.key_len != 48 && cfg.key_len != 64) {
9646-
nvme_show_error("Invalid key length %u", cfg.key_len);
9647-
return -EINVAL;
9648-
}
9649-
raw_secret = malloc(cfg.key_len);
9650-
if (!raw_secret)
9651-
return -ENOMEM;
9652-
if (!cfg.secret) {
9653-
if (getrandom_bytes(raw_secret, cfg.key_len) < 0)
9654-
return -errno;
9655-
} else {
9656-
int secret_len = 0, i;
9657-
unsigned int c;
9658-
9659-
for (i = 0; i < strlen(cfg.secret); i += 2) {
9660-
if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
9661-
nvme_show_error("Invalid secret '%s'", cfg.secret);
9662-
return -EINVAL;
9663-
}
9664-
raw_secret[secret_len++] = (unsigned char)c;
9665-
}
9666-
if (secret_len != cfg.key_len) {
9667-
nvme_show_error("Invalid key length (%d bytes)", secret_len);
9668-
return -EINVAL;
9669-
}
9670-
}
9624+
err = nvme_create_raw_secret(ctx, cfg.secret, cfg.key_len, &raw_secret);
9625+
if (err)
9626+
return err;
96719627

96729628
if (!cfg.nqn) {
96739629
cfg.nqn = hnqn = nvme_read_hostnqn();
@@ -9950,32 +9906,9 @@ static int gen_tls_key(int argc, char **argv, struct command *acmd, struct plugi
99509906
return -ENOMEM;
99519907
}
99529908

9953-
raw_secret = malloc(key_len + 4);
9954-
if (!raw_secret)
9955-
return -ENOMEM;
9956-
if (!cfg.secret) {
9957-
if (getrandom_bytes(raw_secret, key_len) < 0)
9958-
return -errno;
9959-
} else {
9960-
int secret_len = 0, i;
9961-
unsigned int c;
9962-
9963-
for (i = 0; i < strlen(cfg.secret); i += 2) {
9964-
if (sscanf(&cfg.secret[i], "%02x", &c) != 1) {
9965-
nvme_show_error("Invalid secret '%s'", cfg.secret);
9966-
return -EINVAL;
9967-
}
9968-
if (i >= key_len * 2) {
9969-
fprintf(stderr, "Skipping excess secret bytes\n");
9970-
break;
9971-
}
9972-
raw_secret[secret_len++] = (unsigned char)c;
9973-
}
9974-
if (secret_len != key_len) {
9975-
nvme_show_error("Invalid key length (%d bytes)", secret_len);
9976-
return -EINVAL;
9977-
}
9978-
}
9909+
err = nvme_create_raw_secret(ctx, cfg.secret, key_len, &raw_secret);
9910+
if (err)
9911+
return err;
99799912

99809913
err = nvme_export_tls_key(ctx, raw_secret, key_len, &encoded_key);
99819914
if (err) {

0 commit comments

Comments
 (0)