2021-07-16 11:05:20

by Hannes Reinecke

[permalink] [raw]
Subject: [RFC PATCH 00/11] nvme: In-band authentication support

Hi all,

recent updates to the NVMe spec have added definitions for in-band
authentication, and seeing that it provides some real benefit especially
for NVMe-TCP here's an attempt to implement it.

Tricky bit here is that the specification orients itself on TLS 1.3,
but supports only the FFDHE groups. Which of course the kernel doesn't
support. I've been able to come up with a patch for this, but as this
is my first attempt to fix anything in the crypto area I would invite
people more familiar with these matters to have a look.

Also note that this is just for in-band authentication. Secure concatenation
(ie starting TLS with the negotiated parameters) is not implemented; one would
need to update the kernel TLS implementation for this, which at this time is
beyond scope.

As usual, comments and reviews are welcome.

Hannes Reinecke (11):
crypto: add crypto_has_shash()
crypto: add crypto_has_kpp()
crypto/ffdhe: Finite Field DH Ephemeral Parameters
lib/base64: RFC4648-compliant base64 encoding
nvme: add definitions for NVMe In-Band authentication
nvme: Implement In-Band authentication
nvme-auth: augmented challenge support
nvmet: Parse fabrics commands on all queues
nvmet: Implement basic In-Band Authentication
nvmet-auth: implement support for augmented challenge
nvme: add non-standard ECDH and curve25517 algorithms

crypto/Kconfig | 8 +
crypto/Makefile | 1 +
crypto/ffdhe_helper.c | 877 +++++++++++++++++
crypto/kpp.c | 6 +
crypto/shash.c | 6 +
drivers/nvme/host/Kconfig | 11 +
drivers/nvme/host/Makefile | 1 +
drivers/nvme/host/auth.c | 1188 ++++++++++++++++++++++++
drivers/nvme/host/auth.h | 23 +
drivers/nvme/host/core.c | 77 +-
drivers/nvme/host/fabrics.c | 65 +-
drivers/nvme/host/fabrics.h | 8 +
drivers/nvme/host/nvme.h | 15 +
drivers/nvme/host/trace.c | 32 +
drivers/nvme/target/Kconfig | 10 +
drivers/nvme/target/Makefile | 1 +
drivers/nvme/target/admin-cmd.c | 4 +
drivers/nvme/target/auth.c | 608 ++++++++++++
drivers/nvme/target/configfs.c | 102 +-
drivers/nvme/target/core.c | 10 +
drivers/nvme/target/fabrics-cmd-auth.c | 472 ++++++++++
drivers/nvme/target/fabrics-cmd.c | 30 +-
drivers/nvme/target/nvmet.h | 71 ++
include/crypto/ffdhe.h | 24 +
include/crypto/hash.h | 2 +
include/crypto/kpp.h | 2 +
include/linux/base64.h | 16 +
include/linux/nvme.h | 187 +++-
lib/Makefile | 2 +-
lib/base64.c | 111 +++
30 files changed, 3961 insertions(+), 9 deletions(-)
create mode 100644 crypto/ffdhe_helper.c
create mode 100644 drivers/nvme/host/auth.c
create mode 100644 drivers/nvme/host/auth.h
create mode 100644 drivers/nvme/target/auth.c
create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
create mode 100644 include/crypto/ffdhe.h
create mode 100644 include/linux/base64.h
create mode 100644 lib/base64.c

--
2.29.2


2021-07-16 11:05:20

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding

Add RFC4648-compliant base64 encoding and decoding routines.

Signed-off-by: Hannes Reinecke <[email protected]>
---
include/linux/base64.h | 16 ++++++
lib/Makefile | 2 +-
lib/base64.c | 111 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 128 insertions(+), 1 deletion(-)
create mode 100644 include/linux/base64.h
create mode 100644 lib/base64.c

diff --git a/include/linux/base64.h b/include/linux/base64.h
new file mode 100644
index 000000000000..660d4cb1ef31
--- /dev/null
+++ b/include/linux/base64.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * base64 encoding, lifted from fs/crypto/fname.c.
+ */
+
+#ifndef _LINUX_BASE64_H
+#define _LINUX_BASE64_H
+
+#include <linux/types.h>
+
+#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3)
+
+int base64_encode(const u8 *src, int len, char *dst);
+int base64_decode(const char *src, int len, u8 *dst);
+
+#endif /* _LINUX_BASE64_H */
diff --git a/lib/Makefile b/lib/Makefile
index 6d765d5fb8ac..92a428aa0e5f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -46,7 +46,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \
bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \
list_sort.o uuid.o iov_iter.o clz_ctz.o \
bsearch.o find_bit.o llist.o memweight.o kfifo.o \
- percpu-refcount.o rhashtable.o \
+ percpu-refcount.o rhashtable.o base64.o \
once.o refcount.o usercopy.o errseq.o bucket_locks.o \
generic-radix-tree.o
obj-$(CONFIG_STRING_SELFTEST) += test_string.o
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 000000000000..a8929a3a04fe
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * base64.c - RFC4648-compliant base64 encoding
+ *
+ * Copyright (c) 2020 Hannes Reinecke, SUSE
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/base64.h>
+
+static const char lookup_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode() - base64-encode some bytes
+ * @src: the bytes to encode
+ * @len: 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 u8 *src, int len, char *dst)
+{
+ int i, bits = 0;
+ u32 ac = 0;
+ char *cp = dst;
+
+ for (i = 0; i < len; i++) {
+ ac = (ac << 8) | src[i];
+ bits += 8;
+ if (bits < 24)
+ continue;
+ do {
+ bits -= 6;
+ *cp++ = lookup_table[(ac >> bits) & 0x3f];
+ } while (bits);
+ ac = 0;
+ }
+ if (bits) {
+ int more = 0;
+
+ if (bits < 16)
+ more = 2;
+ ac = (ac << (2 + more));
+ bits += (2 + more);
+ do {
+ bits -= 6;
+ *cp++ = lookup_table[(ac >> bits) & 0x3f];
+ } while (bits);
+ *cp++ = '=';
+ if (more)
+ *cp++ = '=';
+ }
+
+ return cp - dst;
+}
+EXPORT_SYMBOL_GPL(base64_encode);
+
+/**
+ * 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 len, u8 *dst)
+{
+ int i, bits = 0, pad = 0;
+ u32 ac = 0;
+ size_t dst_len = 0;
+
+ for (i = 0; i < len; i++) {
+ int c, p = -1;
+
+ if (src[i] == '=') {
+ pad++;
+ if (i + 1 < len && src[i + 1] == '=')
+ pad++;
+ break;
+ }
+ for (c = 0; c < strlen(lookup_table); c++) {
+ if (src[i] == lookup_table[c]) {
+ p = c;
+ break;
+ }
+ }
+ if (p < 0)
+ break;
+ ac = (ac << 6) | p;
+ bits += 6;
+ if (bits < 24)
+ continue;
+ while (bits) {
+ bits -= 8;
+ dst[dst_len++] = (ac >> bits) & 0xff;
+ }
+ ac = 0;
+ }
+ dst_len -= pad;
+ return dst_len;
+}
+EXPORT_SYMBOL_GPL(base64_decode);
--
2.29.2

2021-07-16 11:05:22

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 08/11] nvmet: Parse fabrics commands on all queues

Fabrics commands might be sent to all queues, not just the admin one.

Signed-off-by: Hannes Reinecke <[email protected]>
---
drivers/nvme/target/core.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index ac7210a3ea1c..163f7dc1a929 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -942,6 +942,8 @@ bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
if (unlikely(!req->sq->ctrl))
/* will return an error for any non-connect command: */
status = nvmet_parse_connect_cmd(req);
+ else if (nvme_is_fabrics(req->cmd))
+ status = nvmet_parse_fabrics_cmd(req);
else if (likely(req->sq->qid != 0))
status = nvmet_parse_io_cmd(req);
else
--
2.29.2

2021-07-16 11:05:24

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 01/11] crypto: add crypto_has_shash()

Add helper function to determine if a given synchronous hash is supported.

Signed-off-by: Hannes Reinecke <[email protected]>
---
crypto/shash.c | 6 ++++++
include/crypto/hash.h | 2 ++
2 files changed, 8 insertions(+)

diff --git a/crypto/shash.c b/crypto/shash.c
index 0a0a50cb694f..4c88e63b3350 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -521,6 +521,12 @@ struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
}
EXPORT_SYMBOL_GPL(crypto_alloc_shash);

+int crypto_has_shash(const char *alg_name, u32 type, u32 mask)
+{
+ return crypto_type_has_alg(alg_name, &crypto_shash_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_has_shash);
+
static int shash_prepare_alg(struct shash_alg *alg)
{
struct crypto_alg *base = &alg->base;
diff --git a/include/crypto/hash.h b/include/crypto/hash.h
index f140e4643949..f5841992dc9b 100644
--- a/include/crypto/hash.h
+++ b/include/crypto/hash.h
@@ -718,6 +718,8 @@ static inline void ahash_request_set_crypt(struct ahash_request *req,
struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,
u32 mask);

+int crypto_has_shash(const char *alg_name, u32 type, u32 mask);
+
static inline struct crypto_tfm *crypto_shash_tfm(struct crypto_shash *tfm)
{
return &tfm->base;
--
2.29.2

2021-07-16 11:05:24

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters

Add helper functions to generaten Finite Field DH Ephemeral Parameters as
specified in RFC 7919.

Signed-off-by: Hannes Reinecke <[email protected]>
---
crypto/Kconfig | 8 +
crypto/Makefile | 1 +
crypto/ffdhe_helper.c | 877 +++++++++++++++++++++++++++++++++++++++++
include/crypto/ffdhe.h | 24 ++
4 files changed, 910 insertions(+)
create mode 100644 crypto/ffdhe_helper.c
create mode 100644 include/crypto/ffdhe.h

diff --git a/crypto/Kconfig b/crypto/Kconfig
index ca3b02dcbbfa..1bea506ba56f 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -231,6 +231,14 @@ config CRYPTO_DH
help
Generic implementation of the Diffie-Hellman algorithm.

+config CRYPTO_FFDHE
+ tristate "Finite Field DH (RFC 7919) ephemeral parameters"
+ select CRYPTO_DH
+ select CRYPTO_KPP
+ select CRYPTO_RNG_DEFAULT
+ help
+ Generic implementation of the Finite Field DH algorithm
+
config CRYPTO_ECC
tristate

diff --git a/crypto/Makefile b/crypto/Makefile
index 10526d4559b8..d3bc79fba23f 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -177,6 +177,7 @@ obj-$(CONFIG_CRYPTO_OFB) += ofb.o
obj-$(CONFIG_CRYPTO_ECC) += ecc.o
obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o
obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o
+obj-$(CONFIG_CRYPTO_FFDHE) += ffdhe_helper.o

ecdh_generic-y += ecdh.o
ecdh_generic-y += ecdh_helper.o
diff --git a/crypto/ffdhe_helper.c b/crypto/ffdhe_helper.c
new file mode 100644
index 000000000000..dc023e30c4e5
--- /dev/null
+++ b/crypto/ffdhe_helper.c
@@ -0,0 +1,877 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Finite Field DH Ephemeral Parameters (RFC 7919)
+ *
+ * Copyright (c) 2021, Hannes Reinecke, SUSE Software Products
+ *
+ */
+
+#include <linux/module.h>
+#include <crypto/internal/kpp.h>
+#include <crypto/kpp.h>
+#include <crypto/dh.h>
+#include <linux/mpi.h>
+
+/*
+ * ffdhe2048 generator (g), modulus (p) and group size (q)
+ */
+const u8 ffdhe2048_g[] = { 0x02 };
+
+const u8 ffdhe2048_p[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
+ 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
+ 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
+ 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
+ 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
+ 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
+ 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
+ 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
+ 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
+ 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
+ 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
+ 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
+ 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
+ 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
+ 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
+ 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
+ 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
+ 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
+ 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
+ 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
+ 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
+ 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
+ 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
+ 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
+ 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
+ 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
+ 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
+ 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
+ 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
+ 0x88,0x6b,0x42,0x38,0x61,0x28,0x5c,0x97,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+const u8 ffdhe2048_q[] = {
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
+ 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
+ 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
+ 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
+ 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
+ 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
+ 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
+ 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
+ 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
+ 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
+ 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
+ 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
+ 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
+ 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
+ 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
+ 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
+ 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
+ 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
+ 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
+ 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
+ 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
+ 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
+ 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
+ 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
+ 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
+ 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
+ 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
+ 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
+ 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
+ 0x44,0x35,0xa1,0x1c,0x30,0x94,0x2e,0x4b,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+/*
+ * ffdhe3072 generator (g), modulus (p) and group size (q)
+ */
+
+const u8 ffdhe3072_g[] = { 0x02 };
+
+const u8 ffdhe3072_p[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
+ 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
+ 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
+ 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
+ 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
+ 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
+ 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
+ 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
+ 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
+ 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
+ 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
+ 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
+ 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
+ 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
+ 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
+ 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
+ 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
+ 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
+ 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
+ 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
+ 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
+ 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
+ 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
+ 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
+ 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
+ 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
+ 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
+ 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
+ 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
+ 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
+ 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
+ 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
+ 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
+ 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
+ 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
+ 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
+ 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
+ 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
+ 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
+ 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
+ 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
+ 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
+ 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
+ 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
+ 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
+ 0x25,0xe4,0x1d,0x2b,0x66,0xc6,0x2e,0x37,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+const u8 ffdhe3072_q[] = {
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
+ 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
+ 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
+ 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
+ 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
+ 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
+ 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
+ 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
+ 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
+ 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
+ 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
+ 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
+ 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
+ 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
+ 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
+ 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
+ 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
+ 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
+ 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
+ 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
+ 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
+ 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
+ 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
+ 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
+ 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
+ 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
+ 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
+ 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
+ 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
+ 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
+ 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
+ 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
+ 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
+ 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
+ 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
+ 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
+ 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
+ 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
+ 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
+ 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
+ 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
+ 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
+ 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
+ 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
+ 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
+ 0x12,0xf2,0x0e,0x95,0xb3,0x63,0x17,0x1b,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+/*
+ * ffdhe4096 generator (g), modulus (p) and group size (q)
+ */
+
+const u8 ffdhe4096_g[] = { 0x02 };
+
+const u8 ffdhe4096_p[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
+ 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
+ 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
+ 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
+ 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
+ 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
+ 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
+ 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
+ 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
+ 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
+ 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
+ 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
+ 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
+ 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
+ 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
+ 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
+ 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
+ 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
+ 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
+ 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
+ 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
+ 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
+ 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
+ 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
+ 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
+ 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
+ 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
+ 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
+ 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
+ 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
+ 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
+ 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
+ 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
+ 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
+ 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
+ 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
+ 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
+ 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
+ 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
+ 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
+ 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
+ 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
+ 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
+ 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
+ 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
+ 0x25,0xe4,0x1d,0x2b,0x66,0x9e,0x1e,0xf1,
+ 0x6e,0x6f,0x52,0xc3,0x16,0x4d,0xf4,0xfb,
+ 0x79,0x30,0xe9,0xe4,0xe5,0x88,0x57,0xb6,
+ 0xac,0x7d,0x5f,0x42,0xd6,0x9f,0x6d,0x18,
+ 0x77,0x63,0xcf,0x1d,0x55,0x03,0x40,0x04,
+ 0x87,0xf5,0x5b,0xa5,0x7e,0x31,0xcc,0x7a,
+ 0x71,0x35,0xc8,0x86,0xef,0xb4,0x31,0x8a,
+ 0xed,0x6a,0x1e,0x01,0x2d,0x9e,0x68,0x32,
+ 0xa9,0x07,0x60,0x0a,0x91,0x81,0x30,0xc4,
+ 0x6d,0xc7,0x78,0xf9,0x71,0xad,0x00,0x38,
+ 0x09,0x29,0x99,0xa3,0x33,0xcb,0x8b,0x7a,
+ 0x1a,0x1d,0xb9,0x3d,0x71,0x40,0x00,0x3c,
+ 0x2a,0x4e,0xce,0xa9,0xf9,0x8d,0x0a,0xcc,
+ 0x0a,0x82,0x91,0xcd,0xce,0xc9,0x7d,0xcf,
+ 0x8e,0xc9,0xb5,0x5a,0x7f,0x88,0xa4,0x6b,
+ 0x4d,0xb5,0xa8,0x51,0xf4,0x41,0x82,0xe1,
+ 0xc6,0x8a,0x00,0x7e,0x5e,0x65,0x5f,0x6a,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+const u8 ffdhe4096_q[] = {
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
+ 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
+ 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
+ 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
+ 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
+ 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
+ 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
+ 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
+ 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
+ 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
+ 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
+ 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
+ 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
+ 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
+ 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
+ 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
+ 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
+ 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
+ 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
+ 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
+ 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
+ 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
+ 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
+ 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
+ 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
+ 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
+ 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
+ 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
+ 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
+ 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
+ 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
+ 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
+ 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
+ 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
+ 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
+ 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
+ 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
+ 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
+ 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
+ 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
+ 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
+ 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
+ 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
+ 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
+ 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
+ 0x12,0xf2,0x0e,0x95,0xb3,0x4f,0x0f,0x78,
+ 0xb7,0x37,0xa9,0x61,0x8b,0x26,0xfa,0x7d,
+ 0xbc,0x98,0x74,0xf2,0x72,0xc4,0x2b,0xdb,
+ 0x56,0x3e,0xaf,0xa1,0x6b,0x4f,0xb6,0x8c,
+ 0x3b,0xb1,0xe7,0x8e,0xaa,0x81,0xa0,0x02,
+ 0x43,0xfa,0xad,0xd2,0xbf,0x18,0xe6,0x3d,
+ 0x38,0x9a,0xe4,0x43,0x77,0xda,0x18,0xc5,
+ 0x76,0xb5,0x0f,0x00,0x96,0xcf,0x34,0x19,
+ 0x54,0x83,0xb0,0x05,0x48,0xc0,0x98,0x62,
+ 0x36,0xe3,0xbc,0x7c,0xb8,0xd6,0x80,0x1c,
+ 0x04,0x94,0xcc,0xd1,0x99,0xe5,0xc5,0xbd,
+ 0x0d,0x0e,0xdc,0x9e,0xb8,0xa0,0x00,0x1e,
+ 0x15,0x27,0x67,0x54,0xfc,0xc6,0x85,0x66,
+ 0x05,0x41,0x48,0xe6,0xe7,0x64,0xbe,0xe7,
+ 0xc7,0x64,0xda,0xad,0x3f,0xc4,0x52,0x35,
+ 0xa6,0xda,0xd4,0x28,0xfa,0x20,0xc1,0x70,
+ 0xe3,0x45,0x00,0x3f,0x2f,0x32,0xaf,0xb5,
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+/*
+ * ffdhe6144 generator (g), modulus (p) and group size (q)
+ */
+
+const u8 ffdhe6144_g[] = { 0x02 };
+
+const u8 ffdhe6144_p[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
+ 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
+ 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
+ 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
+ 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
+ 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
+ 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
+ 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
+ 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
+ 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
+ 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
+ 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
+ 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
+ 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
+ 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
+ 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
+ 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
+ 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
+ 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
+ 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
+ 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
+ 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
+ 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
+ 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
+ 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
+ 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
+ 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
+ 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
+ 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
+ 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
+ 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
+ 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
+ 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
+ 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
+ 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
+ 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
+ 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
+ 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
+ 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
+ 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
+ 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
+ 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
+ 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
+ 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
+ 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
+ 0x25,0xe4,0x1d,0x2b,0x66,0x9e,0x1e,0xf1,
+ 0x6e,0x6f,0x52,0xc3,0x16,0x4d,0xf4,0xfb,
+ 0x79,0x30,0xe9,0xe4,0xe5,0x88,0x57,0xb6,
+ 0xac,0x7d,0x5f,0x42,0xd6,0x9f,0x6d,0x18,
+ 0x77,0x63,0xcf,0x1d,0x55,0x03,0x40,0x04,
+ 0x87,0xf5,0x5b,0xa5,0x7e,0x31,0xcc,0x7a,
+ 0x71,0x35,0xc8,0x86,0xef,0xb4,0x31,0x8a,
+ 0xed,0x6a,0x1e,0x01,0x2d,0x9e,0x68,0x32,
+ 0xa9,0x07,0x60,0x0a,0x91,0x81,0x30,0xc4,
+ 0x6d,0xc7,0x78,0xf9,0x71,0xad,0x00,0x38,
+ 0x09,0x29,0x99,0xa3,0x33,0xcb,0x8b,0x7a,
+ 0x1a,0x1d,0xb9,0x3d,0x71,0x40,0x00,0x3c,
+ 0x2a,0x4e,0xce,0xa9,0xf9,0x8d,0x0a,0xcc,
+ 0x0a,0x82,0x91,0xcd,0xce,0xc9,0x7d,0xcf,
+ 0x8e,0xc9,0xb5,0x5a,0x7f,0x88,0xa4,0x6b,
+ 0x4d,0xb5,0xa8,0x51,0xf4,0x41,0x82,0xe1,
+ 0xc6,0x8a,0x00,0x7e,0x5e,0x0d,0xd9,0x02,
+ 0x0b,0xfd,0x64,0xb6,0x45,0x03,0x6c,0x7a,
+ 0x4e,0x67,0x7d,0x2c,0x38,0x53,0x2a,0x3a,
+ 0x23,0xba,0x44,0x42,0xca,0xf5,0x3e,0xa6,
+ 0x3b,0xb4,0x54,0x32,0x9b,0x76,0x24,0xc8,
+ 0x91,0x7b,0xdd,0x64,0xb1,0xc0,0xfd,0x4c,
+ 0xb3,0x8e,0x8c,0x33,0x4c,0x70,0x1c,0x3a,
+ 0xcd,0xad,0x06,0x57,0xfc,0xcf,0xec,0x71,
+ 0x9b,0x1f,0x5c,0x3e,0x4e,0x46,0x04,0x1f,
+ 0x38,0x81,0x47,0xfb,0x4c,0xfd,0xb4,0x77,
+ 0xa5,0x24,0x71,0xf7,0xa9,0xa9,0x69,0x10,
+ 0xb8,0x55,0x32,0x2e,0xdb,0x63,0x40,0xd8,
+ 0xa0,0x0e,0xf0,0x92,0x35,0x05,0x11,0xe3,
+ 0x0a,0xbe,0xc1,0xff,0xf9,0xe3,0xa2,0x6e,
+ 0x7f,0xb2,0x9f,0x8c,0x18,0x30,0x23,0xc3,
+ 0x58,0x7e,0x38,0xda,0x00,0x77,0xd9,0xb4,
+ 0x76,0x3e,0x4e,0x4b,0x94,0xb2,0xbb,0xc1,
+ 0x94,0xc6,0x65,0x1e,0x77,0xca,0xf9,0x92,
+ 0xee,0xaa,0xc0,0x23,0x2a,0x28,0x1b,0xf6,
+ 0xb3,0xa7,0x39,0xc1,0x22,0x61,0x16,0x82,
+ 0x0a,0xe8,0xdb,0x58,0x47,0xa6,0x7c,0xbe,
+ 0xf9,0xc9,0x09,0x1b,0x46,0x2d,0x53,0x8c,
+ 0xd7,0x2b,0x03,0x74,0x6a,0xe7,0x7f,0x5e,
+ 0x62,0x29,0x2c,0x31,0x15,0x62,0xa8,0x46,
+ 0x50,0x5d,0xc8,0x2d,0xb8,0x54,0x33,0x8a,
+ 0xe4,0x9f,0x52,0x35,0xc9,0x5b,0x91,0x17,
+ 0x8c,0xcf,0x2d,0xd5,0xca,0xce,0xf4,0x03,
+ 0xec,0x9d,0x18,0x10,0xc6,0x27,0x2b,0x04,
+ 0x5b,0x3b,0x71,0xf9,0xdc,0x6b,0x80,0xd6,
+ 0x3f,0xdd,0x4a,0x8e,0x9a,0xdb,0x1e,0x69,
+ 0x62,0xa6,0x95,0x26,0xd4,0x31,0x61,0xc1,
+ 0xa4,0x1d,0x57,0x0d,0x79,0x38,0xda,0xd4,
+ 0xa4,0x0e,0x32,0x9c,0xd0,0xe4,0x0e,0x65,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+const u8 ffdhe6144_q[] = {
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
+ 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
+ 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
+ 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
+ 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
+ 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
+ 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
+ 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
+ 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
+ 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
+ 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
+ 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
+ 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
+ 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
+ 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
+ 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
+ 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
+ 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
+ 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
+ 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
+ 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
+ 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
+ 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
+ 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
+ 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
+ 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
+ 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
+ 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
+ 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
+ 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
+ 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
+ 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
+ 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
+ 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
+ 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
+ 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
+ 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
+ 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
+ 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
+ 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
+ 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
+ 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
+ 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
+ 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
+ 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
+ 0x12,0xf2,0x0e,0x95,0xb3,0x4f,0x0f,0x78,
+ 0xb7,0x37,0xa9,0x61,0x8b,0x26,0xfa,0x7d,
+ 0xbc,0x98,0x74,0xf2,0x72,0xc4,0x2b,0xdb,
+ 0x56,0x3e,0xaf,0xa1,0x6b,0x4f,0xb6,0x8c,
+ 0x3b,0xb1,0xe7,0x8e,0xaa,0x81,0xa0,0x02,
+ 0x43,0xfa,0xad,0xd2,0xbf,0x18,0xe6,0x3d,
+ 0x38,0x9a,0xe4,0x43,0x77,0xda,0x18,0xc5,
+ 0x76,0xb5,0x0f,0x00,0x96,0xcf,0x34,0x19,
+ 0x54,0x83,0xb0,0x05,0x48,0xc0,0x98,0x62,
+ 0x36,0xe3,0xbc,0x7c,0xb8,0xd6,0x80,0x1c,
+ 0x04,0x94,0xcc,0xd1,0x99,0xe5,0xc5,0xbd,
+ 0x0d,0x0e,0xdc,0x9e,0xb8,0xa0,0x00,0x1e,
+ 0x15,0x27,0x67,0x54,0xfc,0xc6,0x85,0x66,
+ 0x05,0x41,0x48,0xe6,0xe7,0x64,0xbe,0xe7,
+ 0xc7,0x64,0xda,0xad,0x3f,0xc4,0x52,0x35,
+ 0xa6,0xda,0xd4,0x28,0xfa,0x20,0xc1,0x70,
+ 0xe3,0x45,0x00,0x3f,0x2f,0x06,0xec,0x81,
+ 0x05,0xfe,0xb2,0x5b,0x22,0x81,0xb6,0x3d,
+ 0x27,0x33,0xbe,0x96,0x1c,0x29,0x95,0x1d,
+ 0x11,0xdd,0x22,0x21,0x65,0x7a,0x9f,0x53,
+ 0x1d,0xda,0x2a,0x19,0x4d,0xbb,0x12,0x64,
+ 0x48,0xbd,0xee,0xb2,0x58,0xe0,0x7e,0xa6,
+ 0x59,0xc7,0x46,0x19,0xa6,0x38,0x0e,0x1d,
+ 0x66,0xd6,0x83,0x2b,0xfe,0x67,0xf6,0x38,
+ 0xcd,0x8f,0xae,0x1f,0x27,0x23,0x02,0x0f,
+ 0x9c,0x40,0xa3,0xfd,0xa6,0x7e,0xda,0x3b,
+ 0xd2,0x92,0x38,0xfb,0xd4,0xd4,0xb4,0x88,
+ 0x5c,0x2a,0x99,0x17,0x6d,0xb1,0xa0,0x6c,
+ 0x50,0x07,0x78,0x49,0x1a,0x82,0x88,0xf1,
+ 0x85,0x5f,0x60,0xff,0xfc,0xf1,0xd1,0x37,
+ 0x3f,0xd9,0x4f,0xc6,0x0c,0x18,0x11,0xe1,
+ 0xac,0x3f,0x1c,0x6d,0x00,0x3b,0xec,0xda,
+ 0x3b,0x1f,0x27,0x25,0xca,0x59,0x5d,0xe0,
+ 0xca,0x63,0x32,0x8f,0x3b,0xe5,0x7c,0xc9,
+ 0x77,0x55,0x60,0x11,0x95,0x14,0x0d,0xfb,
+ 0x59,0xd3,0x9c,0xe0,0x91,0x30,0x8b,0x41,
+ 0x05,0x74,0x6d,0xac,0x23,0xd3,0x3e,0x5f,
+ 0x7c,0xe4,0x84,0x8d,0xa3,0x16,0xa9,0xc6,
+ 0x6b,0x95,0x81,0xba,0x35,0x73,0xbf,0xaf,
+ 0x31,0x14,0x96,0x18,0x8a,0xb1,0x54,0x23,
+ 0x28,0x2e,0xe4,0x16,0xdc,0x2a,0x19,0xc5,
+ 0x72,0x4f,0xa9,0x1a,0xe4,0xad,0xc8,0x8b,
+ 0xc6,0x67,0x96,0xea,0xe5,0x67,0x7a,0x01,
+ 0xf6,0x4e,0x8c,0x08,0x63,0x13,0x95,0x82,
+ 0x2d,0x9d,0xb8,0xfc,0xee,0x35,0xc0,0x6b,
+ 0x1f,0xee,0xa5,0x47,0x4d,0x6d,0x8f,0x34,
+ 0xb1,0x53,0x4a,0x93,0x6a,0x18,0xb0,0xe0,
+ 0xd2,0x0e,0xab,0x86,0xbc,0x9c,0x6d,0x6a,
+ 0x52,0x07,0x19,0x4e,0x68,0x72,0x07,0x32,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+/*
+ * ffdhe8192 generator (g), modulus (p) and group size (q)
+ */
+
+const u8 ffdhe8192_g[] = { 0x02 };
+
+const u8 ffdhe8192_p[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
+ 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
+ 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
+ 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
+ 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
+ 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
+ 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
+ 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
+ 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
+ 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
+ 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
+ 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
+ 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
+ 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
+ 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
+ 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
+ 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
+ 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
+ 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
+ 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
+ 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
+ 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
+ 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
+ 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
+ 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
+ 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
+ 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
+ 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
+ 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
+ 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
+ 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
+ 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
+ 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
+ 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
+ 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
+ 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
+ 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
+ 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
+ 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
+ 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
+ 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
+ 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
+ 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
+ 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
+ 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
+ 0x25,0xe4,0x1d,0x2b,0x66,0x9e,0x1e,0xf1,
+ 0x6e,0x6f,0x52,0xc3,0x16,0x4d,0xf4,0xfb,
+ 0x79,0x30,0xe9,0xe4,0xe5,0x88,0x57,0xb6,
+ 0xac,0x7d,0x5f,0x42,0xd6,0x9f,0x6d,0x18,
+ 0x77,0x63,0xcf,0x1d,0x55,0x03,0x40,0x04,
+ 0x87,0xf5,0x5b,0xa5,0x7e,0x31,0xcc,0x7a,
+ 0x71,0x35,0xc8,0x86,0xef,0xb4,0x31,0x8a,
+ 0xed,0x6a,0x1e,0x01,0x2d,0x9e,0x68,0x32,
+ 0xa9,0x07,0x60,0x0a,0x91,0x81,0x30,0xc4,
+ 0x6d,0xc7,0x78,0xf9,0x71,0xad,0x00,0x38,
+ 0x09,0x29,0x99,0xa3,0x33,0xcb,0x8b,0x7a,
+ 0x1a,0x1d,0xb9,0x3d,0x71,0x40,0x00,0x3c,
+ 0x2a,0x4e,0xce,0xa9,0xf9,0x8d,0x0a,0xcc,
+ 0x0a,0x82,0x91,0xcd,0xce,0xc9,0x7d,0xcf,
+ 0x8e,0xc9,0xb5,0x5a,0x7f,0x88,0xa4,0x6b,
+ 0x4d,0xb5,0xa8,0x51,0xf4,0x41,0x82,0xe1,
+ 0xc6,0x8a,0x00,0x7e,0x5e,0x0d,0xd9,0x02,
+ 0x0b,0xfd,0x64,0xb6,0x45,0x03,0x6c,0x7a,
+ 0x4e,0x67,0x7d,0x2c,0x38,0x53,0x2a,0x3a,
+ 0x23,0xba,0x44,0x42,0xca,0xf5,0x3e,0xa6,
+ 0x3b,0xb4,0x54,0x32,0x9b,0x76,0x24,0xc8,
+ 0x91,0x7b,0xdd,0x64,0xb1,0xc0,0xfd,0x4c,
+ 0xb3,0x8e,0x8c,0x33,0x4c,0x70,0x1c,0x3a,
+ 0xcd,0xad,0x06,0x57,0xfc,0xcf,0xec,0x71,
+ 0x9b,0x1f,0x5c,0x3e,0x4e,0x46,0x04,0x1f,
+ 0x38,0x81,0x47,0xfb,0x4c,0xfd,0xb4,0x77,
+ 0xa5,0x24,0x71,0xf7,0xa9,0xa9,0x69,0x10,
+ 0xb8,0x55,0x32,0x2e,0xdb,0x63,0x40,0xd8,
+ 0xa0,0x0e,0xf0,0x92,0x35,0x05,0x11,0xe3,
+ 0x0a,0xbe,0xc1,0xff,0xf9,0xe3,0xa2,0x6e,
+ 0x7f,0xb2,0x9f,0x8c,0x18,0x30,0x23,0xc3,
+ 0x58,0x7e,0x38,0xda,0x00,0x77,0xd9,0xb4,
+ 0x76,0x3e,0x4e,0x4b,0x94,0xb2,0xbb,0xc1,
+ 0x94,0xc6,0x65,0x1e,0x77,0xca,0xf9,0x92,
+ 0xee,0xaa,0xc0,0x23,0x2a,0x28,0x1b,0xf6,
+ 0xb3,0xa7,0x39,0xc1,0x22,0x61,0x16,0x82,
+ 0x0a,0xe8,0xdb,0x58,0x47,0xa6,0x7c,0xbe,
+ 0xf9,0xc9,0x09,0x1b,0x46,0x2d,0x53,0x8c,
+ 0xd7,0x2b,0x03,0x74,0x6a,0xe7,0x7f,0x5e,
+ 0x62,0x29,0x2c,0x31,0x15,0x62,0xa8,0x46,
+ 0x50,0x5d,0xc8,0x2d,0xb8,0x54,0x33,0x8a,
+ 0xe4,0x9f,0x52,0x35,0xc9,0x5b,0x91,0x17,
+ 0x8c,0xcf,0x2d,0xd5,0xca,0xce,0xf4,0x03,
+ 0xec,0x9d,0x18,0x10,0xc6,0x27,0x2b,0x04,
+ 0x5b,0x3b,0x71,0xf9,0xdc,0x6b,0x80,0xd6,
+ 0x3f,0xdd,0x4a,0x8e,0x9a,0xdb,0x1e,0x69,
+ 0x62,0xa6,0x95,0x26,0xd4,0x31,0x61,0xc1,
+ 0xa4,0x1d,0x57,0x0d,0x79,0x38,0xda,0xd4,
+ 0xa4,0x0e,0x32,0x9c,0xcf,0xf4,0x6a,0xaa,
+ 0x36,0xad,0x00,0x4c,0xf6,0x00,0xc8,0x38,
+ 0x1e,0x42,0x5a,0x31,0xd9,0x51,0xae,0x64,
+ 0xfd,0xb2,0x3f,0xce,0xc9,0x50,0x9d,0x43,
+ 0x68,0x7f,0xeb,0x69,0xed,0xd1,0xcc,0x5e,
+ 0x0b,0x8c,0xc3,0xbd,0xf6,0x4b,0x10,0xef,
+ 0x86,0xb6,0x31,0x42,0xa3,0xab,0x88,0x29,
+ 0x55,0x5b,0x2f,0x74,0x7c,0x93,0x26,0x65,
+ 0xcb,0x2c,0x0f,0x1c,0xc0,0x1b,0xd7,0x02,
+ 0x29,0x38,0x88,0x39,0xd2,0xaf,0x05,0xe4,
+ 0x54,0x50,0x4a,0xc7,0x8b,0x75,0x82,0x82,
+ 0x28,0x46,0xc0,0xba,0x35,0xc3,0x5f,0x5c,
+ 0x59,0x16,0x0c,0xc0,0x46,0xfd,0x82,0x51,
+ 0x54,0x1f,0xc6,0x8c,0x9c,0x86,0xb0,0x22,
+ 0xbb,0x70,0x99,0x87,0x6a,0x46,0x0e,0x74,
+ 0x51,0xa8,0xa9,0x31,0x09,0x70,0x3f,0xee,
+ 0x1c,0x21,0x7e,0x6c,0x38,0x26,0xe5,0x2c,
+ 0x51,0xaa,0x69,0x1e,0x0e,0x42,0x3c,0xfc,
+ 0x99,0xe9,0xe3,0x16,0x50,0xc1,0x21,0x7b,
+ 0x62,0x48,0x16,0xcd,0xad,0x9a,0x95,0xf9,
+ 0xd5,0xb8,0x01,0x94,0x88,0xd9,0xc0,0xa0,
+ 0xa1,0xfe,0x30,0x75,0xa5,0x77,0xe2,0x31,
+ 0x83,0xf8,0x1d,0x4a,0x3f,0x2f,0xa4,0x57,
+ 0x1e,0xfc,0x8c,0xe0,0xba,0x8a,0x4f,0xe8,
+ 0xb6,0x85,0x5d,0xfe,0x72,0xb0,0xa6,0x6e,
+ 0xde,0xd2,0xfb,0xab,0xfb,0xe5,0x8a,0x30,
+ 0xfa,0xfa,0xbe,0x1c,0x5d,0x71,0xa8,0x7e,
+ 0x2f,0x74,0x1e,0xf8,0xc1,0xfe,0x86,0xfe,
+ 0xa6,0xbb,0xfd,0xe5,0x30,0x67,0x7f,0x0d,
+ 0x97,0xd1,0x1d,0x49,0xf7,0xa8,0x44,0x3d,
+ 0x08,0x22,0xe5,0x06,0xa9,0xf4,0x61,0x4e,
+ 0x01,0x1e,0x2a,0x94,0x83,0x8f,0xf8,0x8c,
+ 0xd6,0x8c,0x8b,0xb7,0xc5,0xc6,0x42,0x4c,
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+const u8 ffdhe8192_q[] = {
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+ 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
+ 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
+ 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
+ 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
+ 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
+ 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
+ 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
+ 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
+ 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
+ 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
+ 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
+ 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
+ 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
+ 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
+ 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
+ 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
+ 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
+ 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
+ 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
+ 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
+ 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
+ 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
+ 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
+ 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
+ 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
+ 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
+ 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
+ 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
+ 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
+ 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
+ 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
+ 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
+ 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
+ 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
+ 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
+ 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
+ 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
+ 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
+ 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
+ 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
+ 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
+ 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
+ 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
+ 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
+ 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
+ 0x12,0xf2,0x0e,0x95,0xb3,0x4f,0x0f,0x78,
+ 0xb7,0x37,0xa9,0x61,0x8b,0x26,0xfa,0x7d,
+ 0xbc,0x98,0x74,0xf2,0x72,0xc4,0x2b,0xdb,
+ 0x56,0x3e,0xaf,0xa1,0x6b,0x4f,0xb6,0x8c,
+ 0x3b,0xb1,0xe7,0x8e,0xaa,0x81,0xa0,0x02,
+ 0x43,0xfa,0xad,0xd2,0xbf,0x18,0xe6,0x3d,
+ 0x38,0x9a,0xe4,0x43,0x77,0xda,0x18,0xc5,
+ 0x76,0xb5,0x0f,0x00,0x96,0xcf,0x34,0x19,
+ 0x54,0x83,0xb0,0x05,0x48,0xc0,0x98,0x62,
+ 0x36,0xe3,0xbc,0x7c,0xb8,0xd6,0x80,0x1c,
+ 0x04,0x94,0xcc,0xd1,0x99,0xe5,0xc5,0xbd,
+ 0x0d,0x0e,0xdc,0x9e,0xb8,0xa0,0x00,0x1e,
+ 0x15,0x27,0x67,0x54,0xfc,0xc6,0x85,0x66,
+ 0x05,0x41,0x48,0xe6,0xe7,0x64,0xbe,0xe7,
+ 0xc7,0x64,0xda,0xad,0x3f,0xc4,0x52,0x35,
+ 0xa6,0xda,0xd4,0x28,0xfa,0x20,0xc1,0x70,
+ 0xe3,0x45,0x00,0x3f,0x2f,0x06,0xec,0x81,
+ 0x05,0xfe,0xb2,0x5b,0x22,0x81,0xb6,0x3d,
+ 0x27,0x33,0xbe,0x96,0x1c,0x29,0x95,0x1d,
+ 0x11,0xdd,0x22,0x21,0x65,0x7a,0x9f,0x53,
+ 0x1d,0xda,0x2a,0x19,0x4d,0xbb,0x12,0x64,
+ 0x48,0xbd,0xee,0xb2,0x58,0xe0,0x7e,0xa6,
+ 0x59,0xc7,0x46,0x19,0xa6,0x38,0x0e,0x1d,
+ 0x66,0xd6,0x83,0x2b,0xfe,0x67,0xf6,0x38,
+ 0xcd,0x8f,0xae,0x1f,0x27,0x23,0x02,0x0f,
+ 0x9c,0x40,0xa3,0xfd,0xa6,0x7e,0xda,0x3b,
+ 0xd2,0x92,0x38,0xfb,0xd4,0xd4,0xb4,0x88,
+ 0x5c,0x2a,0x99,0x17,0x6d,0xb1,0xa0,0x6c,
+ 0x50,0x07,0x78,0x49,0x1a,0x82,0x88,0xf1,
+ 0x85,0x5f,0x60,0xff,0xfc,0xf1,0xd1,0x37,
+ 0x3f,0xd9,0x4f,0xc6,0x0c,0x18,0x11,0xe1,
+ 0xac,0x3f,0x1c,0x6d,0x00,0x3b,0xec,0xda,
+ 0x3b,0x1f,0x27,0x25,0xca,0x59,0x5d,0xe0,
+ 0xca,0x63,0x32,0x8f,0x3b,0xe5,0x7c,0xc9,
+ 0x77,0x55,0x60,0x11,0x95,0x14,0x0d,0xfb,
+ 0x59,0xd3,0x9c,0xe0,0x91,0x30,0x8b,0x41,
+ 0x05,0x74,0x6d,0xac,0x23,0xd3,0x3e,0x5f,
+ 0x7c,0xe4,0x84,0x8d,0xa3,0x16,0xa9,0xc6,
+ 0x6b,0x95,0x81,0xba,0x35,0x73,0xbf,0xaf,
+ 0x31,0x14,0x96,0x18,0x8a,0xb1,0x54,0x23,
+ 0x28,0x2e,0xe4,0x16,0xdc,0x2a,0x19,0xc5,
+ 0x72,0x4f,0xa9,0x1a,0xe4,0xad,0xc8,0x8b,
+ 0xc6,0x67,0x96,0xea,0xe5,0x67,0x7a,0x01,
+ 0xf6,0x4e,0x8c,0x08,0x63,0x13,0x95,0x82,
+ 0x2d,0x9d,0xb8,0xfc,0xee,0x35,0xc0,0x6b,
+ 0x1f,0xee,0xa5,0x47,0x4d,0x6d,0x8f,0x34,
+ 0xb1,0x53,0x4a,0x93,0x6a,0x18,0xb0,0xe0,
+ 0xd2,0x0e,0xab,0x86,0xbc,0x9c,0x6d,0x6a,
+ 0x52,0x07,0x19,0x4e,0x67,0xfa,0x35,0x55,
+ 0x1b,0x56,0x80,0x26,0x7b,0x00,0x64,0x1c,
+ 0x0f,0x21,0x2d,0x18,0xec,0xa8,0xd7,0x32,
+ 0x7e,0xd9,0x1f,0xe7,0x64,0xa8,0x4e,0xa1,
+ 0xb4,0x3f,0xf5,0xb4,0xf6,0xe8,0xe6,0x2f,
+ 0x05,0xc6,0x61,0xde,0xfb,0x25,0x88,0x77,
+ 0xc3,0x5b,0x18,0xa1,0x51,0xd5,0xc4,0x14,
+ 0xaa,0xad,0x97,0xba,0x3e,0x49,0x93,0x32,
+ 0xe5,0x96,0x07,0x8e,0x60,0x0d,0xeb,0x81,
+ 0x14,0x9c,0x44,0x1c,0xe9,0x57,0x82,0xf2,
+ 0x2a,0x28,0x25,0x63,0xc5,0xba,0xc1,0x41,
+ 0x14,0x23,0x60,0x5d,0x1a,0xe1,0xaf,0xae,
+ 0x2c,0x8b,0x06,0x60,0x23,0x7e,0xc1,0x28,
+ 0xaa,0x0f,0xe3,0x46,0x4e,0x43,0x58,0x11,
+ 0x5d,0xb8,0x4c,0xc3,0xb5,0x23,0x07,0x3a,
+ 0x28,0xd4,0x54,0x98,0x84,0xb8,0x1f,0xf7,
+ 0x0e,0x10,0xbf,0x36,0x1c,0x13,0x72,0x96,
+ 0x28,0xd5,0x34,0x8f,0x07,0x21,0x1e,0x7e,
+ 0x4c,0xf4,0xf1,0x8b,0x28,0x60,0x90,0xbd,
+ 0xb1,0x24,0x0b,0x66,0xd6,0xcd,0x4a,0xfc,
+ 0xea,0xdc,0x00,0xca,0x44,0x6c,0xe0,0x50,
+ 0x50,0xff,0x18,0x3a,0xd2,0xbb,0xf1,0x18,
+ 0xc1,0xfc,0x0e,0xa5,0x1f,0x97,0xd2,0x2b,
+ 0x8f,0x7e,0x46,0x70,0x5d,0x45,0x27,0xf4,
+ 0x5b,0x42,0xae,0xff,0x39,0x58,0x53,0x37,
+ 0x6f,0x69,0x7d,0xd5,0xfd,0xf2,0xc5,0x18,
+ 0x7d,0x7d,0x5f,0x0e,0x2e,0xb8,0xd4,0x3f,
+ 0x17,0xba,0x0f,0x7c,0x60,0xff,0x43,0x7f,
+ 0x53,0x5d,0xfe,0xf2,0x98,0x33,0xbf,0x86,
+ 0xcb,0xe8,0x8e,0xa4,0xfb,0xd4,0x22,0x1e,
+ 0x84,0x11,0x72,0x83,0x54,0xfa,0x30,0xa7,
+ 0x00,0x8f,0x15,0x4a,0x41,0xc7,0xfc,0x46,
+ 0x6b,0x46,0x45,0xdb,0xe2,0xe3,0x21,0x26,
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+};
+
+struct ffdhe_group {
+ int bits;
+ int minsize;
+ const u8 *p;
+ const u8 *q;
+ const u8 *g;
+} ffdhe_group_map[] = {
+ {
+ .bits = 2048,
+ .minsize = 225,
+ .p = ffdhe2048_p,
+ .q = ffdhe2048_q,
+ .g = ffdhe2048_g,
+ },
+ {
+ .bits = 3072,
+ .minsize = 275,
+ .p = ffdhe3072_p,
+ .q = ffdhe3072_q,
+ .g = ffdhe3072_g,
+ },
+ {
+ .bits = 4096,
+ .minsize = 325,
+ .p = ffdhe4096_p,
+ .q = ffdhe4096_q,
+ .g = ffdhe4096_g,
+ },
+ {
+ .bits = 6144,
+ .minsize = 375,
+ .p = ffdhe6144_p,
+ .q = ffdhe6144_q,
+ .g = ffdhe6144_g,
+ },
+ {
+ .bits = 8192,
+ .minsize = 400,
+ .p = ffdhe8192_p,
+ .q = ffdhe8192_q,
+ .g = ffdhe8192_g,
+ },
+};
+
+int crypto_ffdhe_params(struct dh *p, int bits)
+{
+ struct ffdhe_group *grp = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ffdhe_group_map); i++) {
+ if (ffdhe_group_map[i].bits == bits) {
+ grp = &ffdhe_group_map[i];
+ break;
+ }
+ }
+ if (!grp || !p)
+ return -EINVAL;
+
+ p->p_size = grp->bits / 8;
+ p->p = (u8 *)grp->p;
+ p->g_size = 1;
+ p->g = (u8 *)grp->g;
+ p->q_size = grp->bits / 8;
+ p->q = (u8 *)grp->q;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_ffdhe_params);
diff --git a/include/crypto/ffdhe.h b/include/crypto/ffdhe.h
new file mode 100644
index 000000000000..6cb9253ddb34
--- /dev/null
+++ b/include/crypto/ffdhe.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Finite-Field Diffie-Hellman definition according to RFC 7919
+ *
+ * Copyright (c) 2021, SUSE Software Products
+ * Authors: Hannes Reinecke <[email protected]>
+ */
+#ifndef _CRYPTO_FFDHE_
+#define _CRYPTO_FFDHE_
+
+/**
+ * crypto_ffdhe_params() - Generate FFDHE params
+ * @params: DH params
+ * @bits: Bitsize of the FFDHE parameters
+ *
+ * This functions sets the FFDHE parameter for @bits in @params.
+ * Valid bit sizes are 2048, 3072, 4096, 6144, or 8194.
+ *
+ * Returns: 0 on success, errno on failure.
+ */
+
+int crypto_ffdhe_params(struct dh *p, int bits);
+
+#endif /* _CRYPTO_FFDHE_H */
--
2.29.2

2021-07-16 11:06:04

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication

Signed-off-by: Hannes Reinecke <[email protected]>
---
include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 184 insertions(+), 1 deletion(-)

diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index b7c4c4130b65..7b94abacfd08 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -19,6 +19,7 @@
#define NVMF_TRSVCID_SIZE 32
#define NVMF_TRADDR_SIZE 256
#define NVMF_TSAS_SIZE 256
+#define NVMF_AUTH_HASH_LEN 64

#define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"

@@ -1263,6 +1264,8 @@ enum nvmf_capsule_command {
nvme_fabrics_type_property_set = 0x00,
nvme_fabrics_type_connect = 0x01,
nvme_fabrics_type_property_get = 0x04,
+ nvme_fabrics_type_auth_send = 0x05,
+ nvme_fabrics_type_auth_receive = 0x06,
};

#define nvme_fabrics_type_name(type) { type, #type }
@@ -1270,7 +1273,9 @@ enum nvmf_capsule_command {
__print_symbolic(type, \
nvme_fabrics_type_name(nvme_fabrics_type_property_set), \
nvme_fabrics_type_name(nvme_fabrics_type_connect), \
- nvme_fabrics_type_name(nvme_fabrics_type_property_get))
+ nvme_fabrics_type_name(nvme_fabrics_type_property_get), \
+ nvme_fabrics_type_name(nvme_fabrics_type_auth_send), \
+ nvme_fabrics_type_name(nvme_fabrics_type_auth_receive))

/*
* If not fabrics command, fctype will be ignored.
@@ -1393,6 +1398,182 @@ struct nvmf_property_get_command {
__u8 resv4[16];
};

+struct nvmf_auth_send_command {
+ __u8 opcode;
+ __u8 resv1;
+ __u16 command_id;
+ __u8 fctype;
+ __u8 resv2[19];
+ union nvme_data_ptr dptr;
+ __u8 resv3;
+ __u8 spsp0;
+ __u8 spsp1;
+ __u8 secp;
+ __le32 tl;
+ __u8 resv4[12];
+
+};
+
+struct nvmf_auth_receive_command {
+ __u8 opcode;
+ __u8 resv1;
+ __u16 command_id;
+ __u8 fctype;
+ __u8 resv2[19];
+ union nvme_data_ptr dptr;
+ __u8 resv3;
+ __u8 spsp0;
+ __u8 spsp1;
+ __u8 secp;
+ __le32 al;
+ __u8 resv4[12];
+};
+
+/* Value for secp */
+enum {
+ NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER = 0xe9,
+};
+
+/* Defined value for auth_type */
+enum {
+ NVME_AUTH_COMMON_MESSAGES = 0x00,
+ NVME_AUTH_DHCHAP_MESSAGES = 0x01,
+};
+
+/* Defined messages for auth_id */
+enum {
+ NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE = 0x00,
+ NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE = 0x01,
+ NVME_AUTH_DHCHAP_MESSAGE_REPLY = 0x02,
+ NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 = 0x03,
+ NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 = 0x04,
+ NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 = 0xf0,
+ NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 = 0xf1,
+};
+
+struct nvmf_auth_dhchap_protocol_descriptor {
+ __u8 authid;
+ __u8 rsvd;
+ __u8 halen;
+ __u8 dhlen;
+ __u8 idlist[60];
+};
+
+enum {
+ NVME_AUTH_DHCHAP_AUTH_ID = 0x01,
+};
+
+enum {
+ NVME_AUTH_DHCHAP_HASH_SHA256 = 0x01,
+ NVME_AUTH_DHCHAP_HASH_SHA384 = 0x02,
+ NVME_AUTH_DHCHAP_HASH_SHA512 = 0x03,
+};
+
+enum {
+ NVME_AUTH_DHCHAP_DHGROUP_NULL = 0x00,
+ NVME_AUTH_DHCHAP_DHGROUP_2048 = 0x01,
+ NVME_AUTH_DHCHAP_DHGROUP_3072 = 0x02,
+ NVME_AUTH_DHCHAP_DHGROUP_4096 = 0x03,
+ NVME_AUTH_DHCHAP_DHGROUP_6144 = 0x04,
+ NVME_AUTH_DHCHAP_DHGROUP_8192 = 0x05,
+};
+
+union nvmf_auth_protocol {
+ struct nvmf_auth_dhchap_protocol_descriptor dhchap;
+};
+
+struct nvmf_auth_dhchap_negotiate_data {
+ __u8 auth_type;
+ __u8 auth_id;
+ __u8 rsvd[2];
+ __le16 t_id;
+ __u8 sc_c;
+ __u8 napd;
+ union nvmf_auth_protocol auth_protocol[];
+};
+
+struct nvmf_auth_dhchap_challenge_data {
+ __u8 auth_type;
+ __u8 auth_id;
+ __u8 rsvd1[2];
+ __le16 t_id;
+ __u8 hl;
+ __u8 rsvd2;
+ __u8 hashid;
+ __u8 dhgid;
+ __le16 dhvlen;
+ __le32 seqnum;
+ /* 'hl' bytes of challenge value */
+ __u8 cval[];
+ /* followed by 'dhvlen' bytes of DH value */
+};
+
+struct nvmf_auth_dhchap_reply_data {
+ __u8 auth_type;
+ __u8 auth_id;
+ __u8 rsvd1[2];
+ __le16 t_id;
+ __u8 hl;
+ __u8 rsvd2;
+ __u8 cvalid;
+ __u8 rsvd3;
+ __le16 dhvlen;
+ __le32 seqnum;
+ /* 'hl' bytes of response data */
+ __u8 rval[];
+ /* followed by 'hl' bytes of Challenge value */
+ /* followed by 'dhvlen' bytes of DH value */
+};
+
+enum {
+ NVME_AUTH_DHCHAP_RESPONSE_VALID = (1 << 0),
+};
+
+struct nvmf_auth_dhchap_success1_data {
+ __u8 auth_type;
+ __u8 auth_id;
+ __u8 rsvd1[2];
+ __le16 t_id;
+ __u8 hl;
+ __u8 rsvd2;
+ __u8 rvalid;
+ __u8 rsvd3[7];
+ /* 'hl' bytes of response value if 'rvalid' is set */
+ __u8 rval[];
+};
+
+struct nvmf_auth_dhchap_success2_data {
+ __u8 auth_type;
+ __u8 auth_id;
+ __u8 rsvd1[2];
+ __le16 t_id;
+ __u8 rsvd2[10];
+};
+
+struct nvmf_auth_dhchap_failure_data {
+ __u8 auth_type;
+ __u8 auth_id;
+ __u8 rsvd1[2];
+ __le16 t_id;
+ __u8 reason_code;
+ __u8 reason_code_explanation;
+};
+
+enum {
+ NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED = 0x01,
+};
+
+enum {
+ NVME_AUTH_DHCHAP_FAILURE_FAILED = 0x01,
+ NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE = 0x02,
+ NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH = 0x03,
+ NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE = 0x04,
+ NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE = 0x05,
+ NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD = 0x06,
+ NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE = 0x07,
+};
+
+
struct nvme_dbbuf {
__u8 opcode;
__u8 flags;
@@ -1436,6 +1617,8 @@ struct nvme_command {
struct nvmf_connect_command connect;
struct nvmf_property_set_command prop_set;
struct nvmf_property_get_command prop_get;
+ struct nvmf_auth_send_command auth_send;
+ struct nvmf_auth_receive_command auth_receive;
struct nvme_dbbuf dbbuf;
struct nvme_directive_cmd directive;
};
--
2.29.2

2021-07-16 11:06:14

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 07/11] nvme-auth: augmented challenge support

Implement support for augmented challenge using FFDHE groups.

Signed-off-by: Hannes Reinecke <[email protected]>
---
drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
1 file changed, 371 insertions(+), 32 deletions(-)

diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
index 448a3adebea6..754343aced19 100644
--- a/drivers/nvme/host/auth.c
+++ b/drivers/nvme/host/auth.c
@@ -8,6 +8,8 @@
#include <asm/unaligned.h>
#include <crypto/hash.h>
#include <crypto/kpp.h>
+#include <crypto/dh.h>
+#include <crypto/ffdhe.h>
#include "nvme.h"
#include "fabrics.h"
#include "auth.h"
@@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;

struct nvme_dhchap_context {
struct crypto_shash *shash_tfm;
+ struct crypto_shash *digest_tfm;
+ struct crypto_kpp *dh_tfm;
unsigned char *key;
size_t key_len;
int qid;
@@ -25,6 +29,8 @@ struct nvme_dhchap_context {
u8 status;
u8 hash_id;
u8 hash_len;
+ u8 dhgroup_id;
+ u16 dhgroup_size;
u8 c1[64];
u8 c2[64];
u8 response[64];
@@ -36,6 +42,94 @@ struct nvme_dhchap_context {
int sess_key_len;
};

+struct nvme_auth_dhgroup_map {
+ int id;
+ const char name[16];
+ const char kpp[16];
+ int privkey_size;
+ int pubkey_size;
+} dhgroup_map[] = {
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
+ .name = "NULL", .kpp = "NULL",
+ .privkey_size = 0, .pubkey_size = 0 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_2048,
+ .name = "ffdhe2048", .kpp = "dh",
+ .privkey_size = 256, .pubkey_size = 256 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_3072,
+ .name = "ffdhe3072", .kpp = "dh",
+ .privkey_size = 384, .pubkey_size = 384 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_4096,
+ .name = "ffdhe4096", .kpp = "dh",
+ .privkey_size = 512, .pubkey_size = 512 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_6144,
+ .name = "ffdhe6144", .kpp = "dh",
+ .privkey_size = 768, .pubkey_size = 768 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
+ .name = "ffdhe8192", .kpp = "dh",
+ .privkey_size = 1024, .pubkey_size = 1024 },
+};
+
+const char *nvme_auth_dhgroup_name(int dhgroup_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+ if (dhgroup_map[i].id == dhgroup_id)
+ return dhgroup_map[i].name;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
+
+int nvme_auth_dhgroup_pubkey_size(int dhgroup_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+ if (dhgroup_map[i].id == dhgroup_id)
+ return dhgroup_map[i].pubkey_size;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_pubkey_size);
+
+int nvme_auth_dhgroup_privkey_size(int dhgroup_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+ if (dhgroup_map[i].id == dhgroup_id)
+ return dhgroup_map[i].privkey_size;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_privkey_size);
+
+const char *nvme_auth_dhgroup_kpp(int dhgroup_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+ if (dhgroup_map[i].id == dhgroup_id)
+ return dhgroup_map[i].kpp;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
+
+int nvme_auth_dhgroup_id(const char *dhgroup_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+ if (!strncmp(dhgroup_map[i].name, dhgroup_name,
+ strlen(dhgroup_map[i].name)))
+ return dhgroup_map[i].id;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
+
struct nvmet_dhchap_hash_map {
int id;
int hash_len;
@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
data->napd = 1;
data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
data->auth_protocol[0].dhchap.halen = 3;
- data->auth_protocol[0].dhchap.dhlen = 1;
+ data->auth_protocol[0].dhchap.dhlen = 6;
data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
+ data->auth_protocol[0].dhchap.idlist[4] = NVME_AUTH_DHCHAP_DHGROUP_2048;
+ data->auth_protocol[0].dhchap.idlist[5] = NVME_AUTH_DHCHAP_DHGROUP_3072;
+ data->auth_protocol[0].dhchap.idlist[6] = NVME_AUTH_DHCHAP_DHGROUP_4096;
+ data->auth_protocol[0].dhchap.idlist[7] = NVME_AUTH_DHCHAP_DHGROUP_6144;
+ data->auth_protocol[0].dhchap.idlist[8] = NVME_AUTH_DHCHAP_DHGROUP_8192;

return size;
}
@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
return -EPROTO;
}
- switch (data->dhgid) {
- case NVME_AUTH_DHCHAP_DHGROUP_NULL:
- gid_name = "null";
- break;
- default:
- gid_name = NULL;
- break;
- }
+ gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
if (!gid_name) {
dev_warn(ctrl->device,
"qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
return -EPROTO;
}
if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
- chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
- return -EPROTO;
- }
- if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
+ if (data->dhvlen == 0) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: empty DH value\n",
+ chap->qid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ return -EPROTO;
+ }
+ chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
+ if (IS_ERR(chap->dh_tfm)) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
+ chap->qid, gid_name);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ chap->dh_tfm = NULL;
+ return -EPROTO;
+ }
+ chap->dhgroup_id = data->dhgid;
+ } else if (data->dhvlen != 0) {
dev_warn(ctrl->device,
"qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
chap->qid);
@@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
chap->hash_len = data->hl;
chap->s1 = le32_to_cpu(data->seqnum);
memcpy(chap->c1, data->cval, chap->hash_len);
+ if (data->dhvlen) {
+ chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL);
+ if (!chap->ctrl_key)
+ return -ENOMEM;
+ chap->ctrl_key_len = data->dhvlen;
+ memcpy(chap->ctrl_key, data->cval + chap->hash_len,
+ data->dhvlen);
+ dev_dbg(ctrl->device, "ctrl public key %*ph\n",
+ (int)chap->ctrl_key_len, chap->ctrl_key);
+ }

return 0;
}
@@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
memcpy(data->rval + chap->hash_len, chap->c2,
chap->hash_len);
}
- if (chap->host_key_len)
+ if (chap->host_key_len) {
+ dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
+ __func__, chap->qid,
+ chap->host_key_len, chap->host_key);
memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
chap->host_key_len);
-
+ }
return size;
}

@@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
struct nvme_dhchap_context *chap)
{
- char *hash_name;
+ const char *hash_name, *digest_name;
int ret;

- switch (chap->hash_id) {
- case NVME_AUTH_DHCHAP_HASH_SHA256:
- hash_name = "hmac(sha256)";
- break;
- case NVME_AUTH_DHCHAP_HASH_SHA384:
- hash_name = "hmac(sha384)";
- break;
- case NVME_AUTH_DHCHAP_HASH_SHA512:
- hash_name = "hmac(sha512)";
- break;
- default:
- hash_name = NULL;
- break;
- }
+ hash_name = nvme_auth_hmac_name(chap->hash_id);
if (!hash_name) {
chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
return -EPROTO;
@@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
chap->shash_tfm = NULL;
return -EPROTO;
}
+ digest_name = nvme_auth_digest_name(chap->hash_id);
+ if (!digest_name) {
+ crypto_free_shash(chap->shash_tfm);
+ chap->shash_tfm = NULL;
+ return -EPROTO;
+ }
+ chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0);
+ if (IS_ERR(chap->digest_tfm)) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ crypto_free_shash(chap->shash_tfm);
+ chap->shash_tfm = NULL;
+ chap->digest_tfm = NULL;
+ return -EPROTO;
+ }
if (!chap->key) {
dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
chap->qid);
chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ crypto_free_shash(chap->digest_tfm);
crypto_free_shash(chap->shash_tfm);
chap->shash_tfm = NULL;
+ chap->digest_tfm = NULL;
return -EINVAL;
}
ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
if (ret) {
chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ crypto_free_shash(chap->digest_tfm);
crypto_free_shash(chap->shash_tfm);
chap->shash_tfm = NULL;
+ chap->digest_tfm = NULL;
return ret;
}
- dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
- chap->qid, hash_name);
+ dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
+ chap->qid, hash_name);
return 0;
}

+static int nvme_auth_augmented_challenge(struct nvme_dhchap_context *chap,
+ u8 *challenge, u8 *aug)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ u8 *hashed_key;
+ const char *hash_name;
+ int ret;
+
+ hashed_key = kmalloc(chap->hash_len, GFP_KERNEL);
+ if (!hashed_key)
+ return -ENOMEM;
+
+ ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key,
+ chap->sess_key_len, hashed_key);
+ if (ret < 0) {
+ pr_debug("failed to hash session key, err %d\n", ret);
+ kfree(hashed_key);
+ return ret;
+ }
+ hash_name = crypto_shash_alg_name(chap->shash_tfm);
+ if (!hash_name) {
+ pr_debug("Invalid hash algoritm\n");
+ return -EINVAL;
+ }
+ tfm = crypto_alloc_shash(hash_name, 0, 0);
+ if (IS_ERR(tfm)) {
+ ret = PTR_ERR(tfm);
+ goto out_free_key;
+ }
+ desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto out_free_hash;
+ }
+ desc->tfm = tfm;
+
+ ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len);
+ if (ret)
+ goto out_free_desc;
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto out_free_desc;
+ crypto_shash_update(desc, challenge, chap->hash_len);
+ crypto_shash_final(desc, aug);
+
+out_free_desc:
+ kfree_sensitive(desc);
+out_free_hash:
+ crypto_free_shash(tfm);
+out_free_key:
+ kfree(hashed_key);
+ return ret;
+}
+
static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
struct nvme_dhchap_context *chap)
{
@@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,

dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
__func__, chap->qid, chap->s1, chap->transaction);
+ if (chap->dh_tfm) {
+ challenge = kmalloc(chap->hash_len, GFP_KERNEL);
+ if (!challenge) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = nvme_auth_augmented_challenge(chap, chap->c1, challenge);
+ if (ret)
+ goto out;
+ }
shash->tfm = chap->shash_tfm;
ret = crypto_shash_init(shash);
if (ret)
@@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
goto out;
ret = crypto_shash_final(shash, chap->response);
out:
+ if (challenge != chap->c1)
+ kfree(challenge);
return ret;
}

@@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
u8 buf[4], *challenge = chap->c2;
int ret;

+ if (chap->dh_tfm) {
+ challenge = kmalloc(chap->hash_len, GFP_KERNEL);
+ if (!challenge) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = nvme_auth_augmented_challenge(chap, chap->c2,
+ challenge);
+ if (ret)
+ goto out;
+ }
dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
__func__, chap->qid, chap->s2, chap->transaction);
dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
@@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
goto out;
ret = crypto_shash_final(shash, chap->response);
out:
+ if (challenge != chap->c2)
+ kfree(challenge);
return ret;
}

@@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
return 0;
}

+static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap)
+{
+ struct kpp_request *req;
+ struct crypto_wait wait;
+ struct scatterlist src, dst;
+ u8 *pkey;
+ int ret, pkey_len;
+
+ if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
+ chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
+ chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
+ chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
+ chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_8192) {
+ struct dh p = {0};
+ int pubkey_size = nvme_auth_dhgroup_pubkey_size(chap->dhgroup_id);
+
+ ret = crypto_ffdhe_params(&p, pubkey_size << 3);
+ if (ret) {
+ dev_dbg(ctrl->device,
+ "failed to generate ffdhe params, error %d\n",
+ ret);
+ return ret;
+ }
+ p.key = chap->key;
+ p.key_size = chap->key_len;
+
+ pkey_len = crypto_dh_key_len(&p);
+ pkey = kzalloc(pkey_len, GFP_KERNEL);
+
+ get_random_bytes(pkey, pkey_len);
+ ret = crypto_dh_encode_key(pkey, pkey_len, &p);
+ if (ret) {
+ dev_dbg(ctrl->device,
+ "failed to encode pkey, error %d\n", ret);
+ kfree(pkey);
+ return ret;
+ }
+ chap->host_key_len = pubkey_size;
+ chap->sess_key_len = pubkey_size;
+ } else {
+ dev_warn(ctrl->device, "Invalid DH group id %d\n",
+ chap->dhgroup_id);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ return -EINVAL;
+ }
+
+ ret = crypto_kpp_set_secret(chap->dh_tfm, pkey, pkey_len);
+ if (ret) {
+ dev_dbg(ctrl->dev, "failed to set secret, error %d\n", ret);
+ kfree(pkey);
+ return ret;
+ }
+ req = kpp_request_alloc(chap->dh_tfm, GFP_KERNEL);
+ if (!req) {
+ ret = -ENOMEM;
+ goto out_free_exp;
+ }
+
+ chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL);
+ if (!chap->host_key) {
+ ret = -ENOMEM;
+ goto out_free_req;
+ }
+ crypto_init_wait(&wait);
+ kpp_request_set_input(req, NULL, 0);
+ sg_init_one(&dst, chap->host_key, chap->host_key_len);
+ kpp_request_set_output(req, &dst, chap->host_key_len);
+ kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
+ if (ret == -EOVERFLOW) {
+ dev_dbg(ctrl->dev,
+ "public key buffer too small, wants %d is %d\n",
+ crypto_kpp_maxsize(chap->dh_tfm), chap->host_key_len);
+ goto out_free_host;
+ } else if (ret) {
+ dev_dbg(ctrl->dev,
+ "failed to generate public key, error %d\n", ret);
+ goto out_free_host;
+ }
+
+ chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL);
+ if (!chap->sess_key)
+ goto out_free_host;
+
+ crypto_init_wait(&wait);
+ sg_init_one(&src, chap->ctrl_key, chap->ctrl_key_len);
+ kpp_request_set_input(req, &src, chap->ctrl_key_len);
+ sg_init_one(&dst, chap->sess_key, chap->sess_key_len);
+ kpp_request_set_output(req, &dst, chap->sess_key_len);
+ kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait);
+ if (ret) {
+ dev_dbg(ctrl->dev,
+ "failed to generate shared secret, error %d\n", ret);
+ kfree_sensitive(chap->sess_key);
+ chap->sess_key = NULL;
+ chap->sess_key_len = 0;
+ } else
+ dev_dbg(ctrl->dev, "shared secret %*ph\n",
+ (int)chap->sess_key_len, chap->sess_key);
+out_free_host:
+ if (ret) {
+ kfree(chap->host_key);
+ chap->host_key = NULL;
+ chap->host_key_len = 0;
+ }
+out_free_req:
+ kpp_request_free(req);
+out_free_exp:
+ kfree_sensitive(pkey);
+ if (ret)
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ return ret;
+}
+
void nvme_auth_free(struct nvme_dhchap_context *chap)
{
if (chap->shash_tfm)
crypto_free_shash(chap->shash_tfm);
+ if (chap->digest_tfm)
+ crypto_free_shash(chap->digest_tfm);
+ if (chap->dh_tfm)
+ crypto_free_kpp(chap->dh_tfm);
if (chap->key)
kfree(chap->key);
if (chap->ctrl_key)
@@ -732,6 +1061,15 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
if (ret)
goto fail2;

+ if (chap->ctrl_key_len) {
+ dev_dbg(ctrl->device,
+ "%s: qid %d DH-HMAC-DHAP DH exponential\n",
+ __func__, qid);
+ ret = nvme_auth_dhchap_exponential(ctrl, chap);
+ if (ret)
+ goto fail2;
+ }
+
dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
__func__, qid);
ret = nvme_auth_dhchap_host_response(ctrl, chap);
@@ -806,6 +1144,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
ret = -EPROTO;
if (!ret) {
ctrl->dhchap_hash = chap->hash_id;
+ ctrl->dhchap_dhgroup = chap->dhgroup_id;
}
kfree(buf);
nvme_auth_free(chap);
--
2.29.2

2021-07-16 11:06:15

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Implement support for NVMe-oF In-Band authentication. This patch
adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
to the 'host' configfs directory. The 'dhchap_key' needs to be
specified in the format outlined in the base spec.
Augmented challenge support is not implemented, and concatenation
with TLS encryption is not supported.

Signed-off-by: Hannes Reinecke <[email protected]>
---
drivers/nvme/target/Kconfig | 10 +
drivers/nvme/target/Makefile | 1 +
drivers/nvme/target/admin-cmd.c | 4 +
drivers/nvme/target/auth.c | 352 +++++++++++++++++++
drivers/nvme/target/configfs.c | 71 +++-
drivers/nvme/target/core.c | 8 +
drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
drivers/nvme/target/fabrics-cmd.c | 30 +-
drivers/nvme/target/nvmet.h | 71 ++++
9 files changed, 1004 insertions(+), 3 deletions(-)
create mode 100644 drivers/nvme/target/auth.c
create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c

diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
index 4be2ececbc45..d5656ef1559e 100644
--- a/drivers/nvme/target/Kconfig
+++ b/drivers/nvme/target/Kconfig
@@ -85,3 +85,13 @@ config NVME_TARGET_TCP
devices over TCP.

If unsure, say N.
+
+config NVME_TARGET_AUTH
+ bool "NVMe over Fabrics In-band Authentication support"
+ depends on NVME_TARGET
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ help
+ This enables support for NVMe over Fabrics In-band Authentication
+
+ If unsure, say N.
diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
index 9837e580fa7e..c66820102493 100644
--- a/drivers/nvme/target/Makefile
+++ b/drivers/nvme/target/Makefile
@@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \
discovery.o io-cmd-file.o io-cmd-bdev.o
nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
+nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o
nvme-loop-y += loop.o
nvmet-rdma-y += rdma.o
nvmet-fc-y += fc.o
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 0cb98f2bbc8c..320cefc64ee0 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)

if (nvme_is_fabrics(cmd))
return nvmet_parse_fabrics_cmd(req);
+
+ if (unlikely(!nvmet_check_auth_status(req)))
+ return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
+
if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
return nvmet_parse_discovery_cmd(req);

diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
new file mode 100644
index 000000000000..00c7d051dfb1
--- /dev/null
+++ b/drivers/nvme/target/auth.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NVMe over Fabrics DH-HMAC-CHAP authentication.
+ * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
+ * All rights reserved.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <crypto/hash.h>
+#include <crypto/kpp.h>
+#include <crypto/dh.h>
+#include <crypto/ffdhe.h>
+#include <linux/crc32.h>
+#include <linux/base64.h>
+#include <linux/ctype.h>
+#include <linux/random.h>
+#include <asm/unaligned.h>
+
+#include "nvmet.h"
+#include "../host/auth.h"
+
+int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
+{
+ if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
+ return -EINVAL;
+ if (host->dhchap_key_hash > 3) {
+ pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
+ host->dhchap_key_hash);
+ return -EINVAL;
+ }
+ if (host->dhchap_key_hash > 0) {
+ /* Validate selected hash algorithm */
+ const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
+
+ if (!crypto_has_shash(hmac, 0, 0)) {
+ pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
+ host->dhchap_key_hash = -1;
+ return -EAGAIN;
+ }
+ /* Use this hash as default */
+ if (!host->dhchap_hash_id)
+ host->dhchap_hash_id = host->dhchap_key_hash;
+ }
+ host->dhchap_secret = kstrdup(secret, GFP_KERNEL);
+ if (!host->dhchap_secret)
+ return -ENOMEM;
+ /* Default to SHA256 */
+ if (!host->dhchap_hash_id)
+ host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;
+
+ pr_debug("Using hash %s\n",
+ nvme_auth_hmac_name(host->dhchap_hash_id));
+ return 0;
+}
+
+int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
+{
+ int ret = -ENOTSUPP;
+
+ if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
+ return 0;
+
+ return ret;
+}
+
+int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
+{
+ int ret = 0;
+ struct nvmet_host_link *p;
+ struct nvmet_host *host = NULL;
+ const char *hash_name;
+
+ down_read(&nvmet_config_sem);
+ if (ctrl->subsys->type == NVME_NQN_DISC)
+ goto out_unlock;
+
+ list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
+ pr_debug("check %s\n", nvmet_host_name(p->host));
+ if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
+ continue;
+ host = p->host;
+ break;
+ }
+ if (!host) {
+ pr_debug("host %s not found\n", ctrl->hostnqn);
+ ret = -EPERM;
+ goto out_unlock;
+ }
+ if (!host->dhchap_secret) {
+ pr_debug("No authentication provided\n");
+ goto out_unlock;
+ }
+
+ hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
+ if (!hash_name) {
+ pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
+ CRYPTO_ALG_ALLOCATES_MEMORY);
+ if (IS_ERR(ctrl->shash_tfm)) {
+ pr_debug("failed to allocate shash %s\n", hash_name);
+ ret = PTR_ERR(ctrl->shash_tfm);
+ ctrl->shash_tfm = NULL;
+ goto out_unlock;
+ }
+
+ ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
+ &ctrl->dhchap_key_len);
+ if (IS_ERR(ctrl->dhchap_key)) {
+ pr_debug("failed to extract host key, error %d\n", ret);
+ ret = PTR_ERR(ctrl->dhchap_key);
+ ctrl->dhchap_key = NULL;
+ goto out_free_hash;
+ }
+ if (host->dhchap_key_hash) {
+ struct crypto_shash *key_tfm;
+
+ hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
+ key_tfm = crypto_alloc_shash(hash_name, 0, 0);
+ if (IS_ERR(key_tfm)) {
+ ret = PTR_ERR(key_tfm);
+ goto out_free_hash;
+ } else {
+ SHASH_DESC_ON_STACK(shash, key_tfm);
+
+ shash->tfm = key_tfm;
+ ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
+ ctrl->dhchap_key_len);
+ crypto_shash_init(shash);
+ crypto_shash_update(shash, ctrl->subsys->subsysnqn,
+ strlen(ctrl->subsys->subsysnqn));
+ crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
+ crypto_shash_final(shash, ctrl->dhchap_key);
+ crypto_free_shash(key_tfm);
+ }
+ }
+ pr_debug("%s: using key %*ph\n", __func__,
+ (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
+ ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
+ ctrl->dhchap_key_len);
+out_free_hash:
+ if (ret) {
+ if (ctrl->dhchap_key) {
+ kfree(ctrl->dhchap_key);
+ ctrl->dhchap_key = NULL;
+ }
+ crypto_free_shash(ctrl->shash_tfm);
+ ctrl->shash_tfm = NULL;
+ }
+out_unlock:
+ up_read(&nvmet_config_sem);
+
+ return ret;
+}
+
+void nvmet_auth_sq_free(struct nvmet_sq *sq)
+{
+ if (sq->dhchap_c1)
+ kfree(sq->dhchap_c1);
+ if (sq->dhchap_c2)
+ kfree(sq->dhchap_c2);
+ if (sq->dhchap_skey)
+ kfree(sq->dhchap_skey);
+}
+
+void nvmet_reset_auth(struct nvmet_ctrl *ctrl)
+{
+ if (ctrl->shash_tfm) {
+ crypto_free_shash(ctrl->shash_tfm);
+ ctrl->shash_tfm = NULL;
+ }
+ if (ctrl->dh_tfm) {
+ crypto_free_kpp(ctrl->dh_tfm);
+ ctrl->dh_tfm = NULL;
+ }
+ if (ctrl->dhchap_key) {
+ kfree(ctrl->dhchap_key);
+ ctrl->dhchap_key = NULL;
+ }
+}
+
+bool nvmet_check_auth_status(struct nvmet_req *req)
+{
+ if (req->sq->ctrl->shash_tfm &&
+ !req->sq->authenticated)
+ return false;
+ return true;
+}
+
+int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
+ unsigned int shash_len)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
+ u8 *challenge = req->sq->dhchap_c1;
+ u8 buf[4];
+ int ret;
+
+ if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ shash->tfm = ctrl->shash_tfm;
+ ret = crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, challenge, shash_len);
+ if (ret)
+ goto out;
+ put_unaligned_le32(req->sq->dhchap_s1, buf);
+ ret = crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+ put_unaligned_le16(req->sq->dhchap_tid, buf);
+ ret = crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
+ memset(buf, 0, 4);
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, "HostHost", 8);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->subsysnqn,
+ strlen(ctrl->subsysnqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_final(shash, response);
+out:
+ if (challenge != req->sq->dhchap_c1)
+ kfree(challenge);
+ return 0;
+}
+
+int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
+ unsigned int shash_len)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
+ u8 *challenge = req->sq->dhchap_c2;
+ u8 buf[4];
+ int ret;
+
+ pr_debug("%s: ctrl %d hash seq %d transaction %u\n", __func__,
+ ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid);
+ pr_debug("%s: ctrl %d challenge %*ph\n", __func__,
+ ctrl->cntlid, shash_len, req->sq->dhchap_c2);
+ pr_debug("%s: ctrl %d subsysnqn %s\n", __func__,
+ ctrl->cntlid, ctrl->subsysnqn);
+ pr_debug("%s: ctrl %d hostnqn %s\n", __func__,
+ ctrl->cntlid, ctrl->hostnqn);
+
+ if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ shash->tfm = ctrl->shash_tfm;
+ ret = crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, challenge, shash_len);
+ if (ret)
+ goto out;
+ put_unaligned_le32(req->sq->dhchap_s2, buf);
+ ret = crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+ put_unaligned_le16(req->sq->dhchap_tid, buf);
+ ret = crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
+ memset(buf, 0, 4);
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, "Controller", 10);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->subsysnqn,
+ strlen(ctrl->subsysnqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_final(shash, response);
+out:
+ if (challenge != req->sq->dhchap_c2)
+ kfree(challenge);
+ return 0;
+}
+
+int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
+ u8 *pkey, int pkey_size)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct kpp_request *kpp_req;
+ struct crypto_wait wait;
+ struct scatterlist src, dst;
+ int ret;
+
+ req->sq->dhchap_skey_len =
+ nvme_auth_dhgroup_privkey_size(ctrl->dh_gid);
+ req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
+ if (!req->sq->dhchap_skey)
+ return -ENOMEM;
+ kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
+ if (!kpp_req) {
+ kfree(req->sq->dhchap_skey);
+ req->sq->dhchap_skey = NULL;
+ return -ENOMEM;
+ }
+
+ pr_debug("%s: host public key %*ph\n", __func__,
+ (int)pkey_size, pkey);
+ crypto_init_wait(&wait);
+ sg_init_one(&src, pkey, pkey_size);
+ kpp_request_set_input(kpp_req, &src, pkey_size);
+ sg_init_one(&dst, req->sq->dhchap_skey,
+ req->sq->dhchap_skey_len);
+ kpp_request_set_output(kpp_req, &dst, req->sq->dhchap_skey_len);
+ kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ ret = crypto_wait_req(crypto_kpp_compute_shared_secret(kpp_req), &wait);
+ kpp_request_free(kpp_req);
+ if (ret)
+ pr_debug("failed to compute shared secred, err %d\n", ret);
+ else
+ pr_debug("%s: shared secret %*ph\n", __func__,
+ (int)req->sq->dhchap_skey_len,
+ req->sq->dhchap_skey);
+
+ return ret;
+}
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 273555127188..e0760911a761 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -11,8 +11,13 @@
#include <linux/ctype.h>
#include <linux/pci.h>
#include <linux/pci-p2pdma.h>
+#include <crypto/hash.h>
+#include <crypto/kpp.h>

#include "nvmet.h"
+#ifdef CONFIG_NVME_TARGET_AUTH
+#include "../host/auth.h"
+#endif

static const struct config_item_type nvmet_host_type;
static const struct config_item_type nvmet_subsys_type;
@@ -1656,10 +1661,71 @@ static const struct config_item_type nvmet_ports_type = {
static struct config_group nvmet_subsystems_group;
static struct config_group nvmet_ports_group;

-static void nvmet_host_release(struct config_item *item)
+#ifdef CONFIG_NVME_TARGET_AUTH
+static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
+ char *page)
+{
+ u8 *dhchap_secret = to_host(item)->dhchap_secret;
+
+ if (!dhchap_secret)
+ return sprintf(page, "\n");
+ return sprintf(page, "%s\n", dhchap_secret);
+}
+
+static ssize_t nvmet_host_dhchap_key_store(struct config_item *item,
+ const char *page, size_t count)
{
struct nvmet_host *host = to_host(item);
+ int ret;

+ ret = nvmet_auth_set_host_key(host, page);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+CONFIGFS_ATTR(nvmet_host_, dhchap_key);
+
+static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item,
+ char *page)
+{
+ struct nvmet_host *host = to_host(item);
+ const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
+
+ return sprintf(page, "%s\n", hash_name ? hash_name : "none");
+}
+
+static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct nvmet_host *host = to_host(item);
+ int hmac_id;
+
+ hmac_id = nvme_auth_hmac_id(page);
+ if (hmac_id < 0)
+ return -EINVAL;
+ if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0))
+ return -ENOTSUPP;
+ host->dhchap_hash_id = hmac_id;
+ return count;
+}
+
+CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
+
+static struct configfs_attribute *nvmet_host_attrs[] = {
+ &nvmet_host_attr_dhchap_key,
+ &nvmet_host_attr_dhchap_hash,
+ NULL,
+};
+#endif /* CONFIG_NVME_TARGET_AUTH */
+
+static void nvmet_host_release(struct config_item *item)
+{
+ struct nvmet_host *host = to_host(item);
+#ifdef CONFIG_NVME_TARGET_AUTH
+ if (host->dhchap_secret)
+ kfree(host->dhchap_secret);
+#endif
kfree(host);
}

@@ -1669,6 +1735,9 @@ static struct configfs_item_operations nvmet_host_item_ops = {

static const struct config_item_type nvmet_host_type = {
.ct_item_ops = &nvmet_host_item_ops,
+#ifdef CONFIG_NVME_TARGET_AUTH
+ .ct_attrs = nvmet_host_attrs,
+#endif
.ct_owner = THIS_MODULE,
};

diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 163f7dc1a929..b5d7971f566b 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
wait_for_completion(&sq->confirm_done);
wait_for_completion(&sq->free_done);
percpu_ref_exit(&sq->ref);
+ nvmet_auth_sq_free(sq);

if (ctrl) {
/*
@@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req)
req->cmd->common.opcode, req->sq->qid);
return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
}
+
+ if (unlikely(!nvmet_check_auth_status(req))) {
+ pr_warn("qid %d not authenticated\n", req->sq->qid);
+ return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
+ }
return 0;
}

@@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)
flush_work(&ctrl->async_event_work);
cancel_work_sync(&ctrl->fatal_err_work);

+ nvmet_reset_auth(ctrl);
+
ida_simple_remove(&cntlid_ida, ctrl->cntlid);

nvmet_async_events_free(ctrl);
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
new file mode 100644
index 000000000000..962f9f5e9d89
--- /dev/null
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NVMe over Fabrics DH-HMAC-CHAP authentication command handling.
+ * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
+ * All rights reserved.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/blkdev.h>
+#include <linux/random.h>
+#include <crypto/hash.h>
+#include <crypto/kpp.h>
+#include "nvmet.h"
+#include "../host/auth.h"
+
+void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
+{
+ /* Initialize in-band authentication */
+ req->sq->authenticated = false;
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+ req->cqe->result.u32 |= 0x2 << 16;
+}
+
+static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct nvmf_auth_dhchap_negotiate_data *data = d;
+ int i, hash_id, null_dh = -1;
+
+ pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
+ data->auth_protocol[0].dhchap.halen,
+ data->auth_protocol[0].dhchap.dhlen);
+ req->sq->dhchap_tid = le16_to_cpu(data->t_id);
+ if (data->sc_c)
+ return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
+
+ if (data->napd != 1)
+ return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+
+ if (data->auth_protocol[0].dhchap.authid != 0x01)
+ return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+
+ hash_id = nvme_auth_hmac_id(crypto_shash_alg_name(ctrl->shash_tfm));
+ for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
+ pr_debug("%s: ctrl %d qid %d checking hash %d for %d\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ data->auth_protocol[0].dhchap.idlist[i], hash_id);
+ if (hash_id != data->auth_protocol[0].dhchap.idlist[i])
+ continue;
+ req->sq->dhchap_hash_id = hash_id;
+ req->sq->dhchap_hash_len = crypto_shash_digestsize(ctrl->shash_tfm);
+ break;
+ }
+ if (req->sq->dhchap_hash_id == 0) {
+ pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
+ __func__, ctrl->cntlid, req->sq->qid);
+ return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+ }
+
+ for (i = data->auth_protocol[0].dhchap.halen;
+ i < data->auth_protocol[0].dhchap.halen +
+ data->auth_protocol[0].dhchap.dhlen; i++) {
+ int dhgid = data->auth_protocol[0].dhchap.idlist[i];
+
+ if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+ null_dh = dhgid;
+ continue;
+ }
+ if (nvmet_setup_dhgroup(ctrl, dhgid) == 0)
+ break;
+ }
+ if (!ctrl->dh_tfm && null_dh < 0) {
+ pr_debug("%s: ctrl %d qid %d: no DH group selected\n",
+ __func__, ctrl->cntlid, req->sq->qid);
+ return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ }
+ if (ctrl->dh_gid == -1) {
+ ctrl->dh_gid = null_dh;
+ ctrl->dh_tfm = NULL;
+ }
+ pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
+ return 0;
+}
+
+static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct nvmf_auth_dhchap_reply_data *data = d;
+ u8 *response;
+
+ pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ data->hl, data->cvalid, data->dhvlen);
+ if (data->hl != req->sq->dhchap_hash_len)
+ return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+
+ if (data->dhvlen) {
+ return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ }
+
+ response = kmalloc(data->hl, GFP_KERNEL);
+ if (!response)
+ return NVME_AUTH_DHCHAP_FAILURE_FAILED;
+
+ if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
+ pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n",
+ ctrl->cntlid, req->sq->qid);
+ kfree(response);
+ return NVME_AUTH_DHCHAP_FAILURE_FAILED;
+ }
+
+ if (memcmp(data->rval, response, data->hl)) {
+ pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n",
+ ctrl->cntlid, req->sq->qid);
+ kfree(response);
+ return NVME_AUTH_DHCHAP_FAILURE_FAILED;
+ }
+ kfree(response);
+ pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n",
+ ctrl->cntlid, req->sq->qid);
+ if (data->cvalid) {
+ req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL);
+ if (!req->sq->dhchap_c2)
+ return NVME_AUTH_DHCHAP_FAILURE_FAILED;
+ memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl);
+
+ pr_debug("ctrl %d qid %d challenge %*ph\n",
+ ctrl->cntlid, req->sq->qid, data->hl,
+ req->sq->dhchap_c2);
+ req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
+ } else
+ req->sq->dhchap_c2 = NULL;
+
+ return 0;
+}
+
+static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d)
+{
+ struct nvmf_auth_dhchap_failure_data *data = d;
+
+ return data->reason_code_explanation;
+}
+
+void nvmet_execute_auth_send(struct nvmet_req *req)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct nvmf_auth_dhchap_success2_data *data;
+ void *d;
+ u32 tl;
+ u16 status = 0;
+
+ if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_send_command, secp);
+ goto done;
+ }
+ if (req->cmd->auth_send.spsp0 != 0x01) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_send_command, spsp0);
+ goto done;
+ }
+ if (req->cmd->auth_send.spsp1 != 0x01) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_send_command, spsp1);
+ goto done;
+ }
+ tl = le32_to_cpu(req->cmd->auth_send.tl);
+ if (!tl) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_send_command, tl);
+ goto done;
+ }
+ if (!nvmet_check_transfer_len(req, tl)) {
+ pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
+ return;
+ }
+
+ d = kmalloc(tl, GFP_KERNEL);
+ if (!d) {
+ status = NVME_SC_INTERNAL;
+ goto done;
+ }
+
+ status = nvmet_copy_from_sgl(req, 0, d, tl);
+ if (status) {
+ kfree(d);
+ goto done;
+ }
+
+ data = d;
+ pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
+ ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id,
+ req->sq->dhchap_step);
+ if (data->auth_type != NVME_AUTH_COMMON_MESSAGES &&
+ data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) {
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
+ } else if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
+ if (data->auth_id != req->sq->dhchap_step) {
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
+ } else if (data->auth_id != NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
+ } else {
+ /* Validate negotiation parameters */
+ status = nvmet_auth_negotiate(req, d);
+ if (status == 0)
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
+ else {
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = status;
+ status = 0;
+ }
+ }
+ } else if (data->auth_type == NVME_AUTH_DHCHAP_MESSAGES) {
+ if (data->auth_id != req->sq->dhchap_step) {
+ pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ data->auth_id, req->sq->dhchap_step);
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
+ } else if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) {
+ pr_debug("%s: ctrl %d qid %d invalid transaction %d (expected %d)\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ le16_to_cpu(data->t_id),
+ req->sq->dhchap_tid);
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ } else {
+ switch (data->auth_id) {
+ case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
+ status = nvmet_auth_reply(req, d);
+ if (status == 0)
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
+ else {
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = status;
+ status = 0;
+ }
+ break;
+ case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
+ req->sq->authenticated = true;
+ pr_debug("%s: ctrl %d qid %d authenticated\n",
+ __func__, ctrl->cntlid, req->sq->qid);
+ break;
+ case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
+ status = nvmet_auth_failure2(req, d);
+ if (status) {
+ pr_warn("ctrl %d qid %d: DH-HMAC-CHAP negotiation failed (%d)\n",
+ ctrl->cntlid, req->sq->qid,
+ status);
+ req->sq->dhchap_status = status;
+ status = 0;
+ }
+ break;
+ default:
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
+ break;
+ }
+ }
+ } else {
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
+ }
+ kfree(d);
+done:
+ pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__,
+ ctrl->cntlid, req->sq->qid,
+ req->sq->dhchap_status, req->sq->dhchap_step);
+ if (status)
+ pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n",
+ __func__, ctrl->cntlid, req->sq->qid,
+ status, req->error_loc);
+ req->cqe->result.u64 = 0;
+ nvmet_req_complete(req, status);
+ if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
+ req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
+ return;
+ /* Final states, clear up variables */
+ kfree(req->sq->dhchap_c1);
+ kfree(req->sq->dhchap_c2);
+ if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
+ nvmet_ctrl_fatal_error(ctrl);
+}
+
+static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
+{
+ struct nvmf_auth_dhchap_challenge_data *data = d;
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ int ret = 0;
+ int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
+
+ if (al < data_size) {
+ pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
+ al, data_size);
+ return -EINVAL;
+ }
+ memset(data, 0, data_size);
+ req->sq->dhchap_s1 = ctrl->dhchap_seqnum++;
+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
+ data->t_id = cpu_to_le16(req->sq->dhchap_tid);
+ data->hashid = req->sq->dhchap_hash_id;
+ data->hl = req->sq->dhchap_hash_len;
+ data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
+ req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
+ if (!req->sq->dhchap_c1)
+ return -ENOMEM;
+ get_random_bytes(req->sq->dhchap_c1, data->hl);
+ memcpy(data->cval, req->sq->dhchap_c1, data->hl);
+ pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n",
+ __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
+ req->sq->dhchap_tid, data->hl, data->dhvlen);
+ return ret;
+}
+
+static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al)
+{
+ struct nvmf_auth_dhchap_success1_data *data = d;
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+
+ WARN_ON(al < sizeof(*data));
+ memset(data, 0, sizeof(*data));
+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
+ data->t_id = cpu_to_le16(req->sq->dhchap_tid);
+ data->hl = req->sq->dhchap_hash_len;
+ if (req->sq->dhchap_c2) {
+ if (nvmet_auth_ctrl_hash(req, data->rval, data->hl))
+ return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+ data->rvalid = 1;
+ pr_debug("ctrl %d qid %d response %*ph\n",
+ ctrl->cntlid, req->sq->qid, data->hl, data->rval);
+ }
+ return 0;
+}
+
+static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
+{
+ struct nvmf_auth_dhchap_failure_data *data = d;
+
+ WARN_ON(al < sizeof(*data));
+ data->auth_type = NVME_AUTH_COMMON_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ data->t_id = cpu_to_le32(req->sq->dhchap_tid);
+ data->reason_code = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
+ data->reason_code_explanation = req->sq->dhchap_status;
+}
+
+void nvmet_execute_auth_receive(struct nvmet_req *req)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ void *d;
+ u32 al;
+ u16 status = 0;
+
+ if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_receive_command, secp);
+ goto done;
+ }
+ if (req->cmd->auth_receive.spsp0 != 0x01) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_receive_command, spsp0);
+ goto done;
+ }
+ if (req->cmd->auth_receive.spsp1 != 0x01) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_receive_command, spsp1);
+ goto done;
+ }
+ al = le32_to_cpu(req->cmd->auth_receive.al);
+ if (!al) {
+ status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
+ req->error_loc =
+ offsetof(struct nvmf_auth_receive_command, al);
+ goto done;
+ }
+ if (!nvmet_check_transfer_len(req, al)) {
+ pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
+ return;
+ }
+
+ d = kmalloc(al, GFP_KERNEL);
+ if (!d) {
+ status = NVME_SC_INTERNAL;
+ goto done;
+ }
+ pr_debug("%s: ctrl %d qid %d step %x\n", __func__,
+ ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
+ switch (req->sq->dhchap_step) {
+ case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
+ status = nvmet_auth_challenge(req, d, al);
+ if (status < 0) {
+ pr_warn("ctrl %d qid %d: challenge error (%d)\n",
+ ctrl->cntlid, req->sq->qid, status);
+ status = NVME_SC_INTERNAL;
+ break;
+ }
+ if (status) {
+ req->sq->dhchap_status = status;
+ nvmet_auth_failure1(req, d, al);
+ pr_warn("ctrl %d qid %d: challenge status (%x)\n",
+ ctrl->cntlid, req->sq->qid,
+ req->sq->dhchap_status);
+ status = 0;
+ break;
+ }
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
+ break;
+ case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
+ status = nvmet_auth_success1(req, d, al);
+ if (status) {
+ req->sq->dhchap_status = status;
+ nvmet_auth_failure1(req, d, al);
+ pr_warn("ctrl %d qid %d: success1 status (%x)\n",
+ ctrl->cntlid, req->sq->qid,
+ req->sq->dhchap_status);
+ break;
+ }
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
+ break;
+ case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1:
+ nvmet_auth_failure1(req, d, al);
+ pr_warn("ctrl %d qid %d failure1 (%x)\n",
+ ctrl->cntlid, req->sq->qid, req->sq->dhchap_status);
+ break;
+ default:
+ pr_warn("ctrl %d qid %d unhandled step (%d)\n",
+ ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
+ req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
+ req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
+ nvmet_auth_failure1(req, d, al);
+ status = 0;
+ break;
+ }
+
+ status = nvmet_copy_to_sgl(req, 0, d, al);
+ kfree(d);
+done:
+ req->cqe->result.u64 = 0;
+ nvmet_req_complete(req, status);
+ if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
+ kfree(req->sq->dhchap_c1);
+ kfree(req->sq->dhchap_c2);
+ nvmet_ctrl_fatal_error(ctrl);
+ }
+}
diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
index 7d0f3523fdab..53fb853cd8fe 100644
--- a/drivers/nvme/target/fabrics-cmd.c
+++ b/drivers/nvme/target/fabrics-cmd.c
@@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)
case nvme_fabrics_type_property_get:
req->execute = nvmet_execute_prop_get;
break;
+#ifdef CONFIG_NVME_TARGET_AUTH
+ case nvme_fabrics_type_auth_send:
+ req->execute = nvmet_execute_auth_send;
+ break;
+ case nvme_fabrics_type_auth_receive:
+ req->execute = nvmet_execute_auth_receive;
+ break;
+#endif
default:
pr_debug("received unknown capsule type 0x%x\n",
cmd->fabrics.fctype);
@@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
struct nvmf_connect_data *d;
struct nvmet_ctrl *ctrl = NULL;
u16 status = 0;
+ int ret;

if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
return;
@@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)

uuid_copy(&ctrl->hostid, &d->hostid);

+ ret = nvmet_setup_auth(ctrl, req);
+ if (ret < 0) {
+ pr_err("Failed to setup authentication, error %d\n", ret);
+ nvmet_ctrl_put(ctrl);
+ if (ret == -EPERM)
+ status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
+ else
+ status = NVME_SC_INTERNAL;
+ goto out;
+ }
+
status = nvmet_install_queue(ctrl, req);
if (status) {
nvmet_ctrl_put(ctrl);
goto out;
}

- pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
+ pr_info("creating controller %d for subsystem %s for NQN %s%s%s.\n",
ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
- ctrl->pi_support ? " T10-PI is enabled" : "");
+ ctrl->pi_support ? " T10-PI is enabled" : "",
+ nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);

+ if (nvmet_has_auth(ctrl))
+ nvmet_init_auth(ctrl, req);
out:
kfree(d);
complete:
@@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
}

pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
+ req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
+ if (nvmet_has_auth(ctrl))
+ nvmet_init_auth(ctrl, req);

out:
kfree(d);
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 06dd3d537f07..ef8815e137d7 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -108,6 +108,20 @@ struct nvmet_sq {
u16 size;
u32 sqhd;
bool sqhd_disabled;
+#ifdef CONFIG_NVME_TARGET_AUTH
+ bool authenticated;
+ u16 dhchap_tid;
+ u16 dhchap_status;
+ int dhchap_step;
+ u8 dhchap_hash_id;
+ u8 dhchap_hash_len;
+ u8 *dhchap_c1;
+ u8 *dhchap_c2;
+ u32 dhchap_s1;
+ u32 dhchap_s2;
+ u8 *dhchap_skey;
+ int dhchap_skey_len;
+#endif
struct completion free_done;
struct completion confirm_done;
};
@@ -209,6 +223,15 @@ struct nvmet_ctrl {
u64 err_counter;
struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS];
bool pi_support;
+#ifdef CONFIG_NVME_TARGET_AUTH
+ u32 dhchap_seqnum;
+ u8 *dhchap_key;
+ size_t dhchap_key_len;
+ struct crypto_shash *shash_tfm;
+ struct crypto_kpp *dh_tfm;
+ u32 dh_gid;
+ u32 dh_keysize;
+#endif
};

struct nvmet_subsys {
@@ -270,6 +293,10 @@ static inline struct nvmet_subsys *namespaces_to_subsys(

struct nvmet_host {
struct config_group group;
+ u8 *dhchap_secret;
+ u8 dhchap_key_hash;
+ u8 dhchap_hash_id;
+ u8 dhchap_dhgroup_id;
};

static inline struct nvmet_host *to_host(struct config_item *item)
@@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio)
bio_put(bio);
}

+#ifdef CONFIG_NVME_TARGET_AUTH
+void nvmet_execute_auth_send(struct nvmet_req *req);
+void nvmet_execute_auth_receive(struct nvmet_req *req);
+int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret);
+int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
+int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
+void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
+void nvmet_reset_auth(struct nvmet_ctrl *ctrl);
+void nvmet_auth_sq_free(struct nvmet_sq *sq);
+int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id);
+bool nvmet_check_auth_status(struct nvmet_req *req);
+int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
+ unsigned int hash_len);
+int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
+ unsigned int hash_len);
+static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
+{
+ return ctrl->shash_tfm != NULL;
+}
+int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
+ u8 *buf, int buf_size);
+int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
+ u8 *buf, int buf_size);
+#else
+static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl,
+ struct nvmet_req *req)
+{
+ return 0;
+}
+static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl,
+ struct nvmet_req *req) {};
+static inline void nvmet_reset_auth(struct nvmet_ctrl *ctrl) {};
+static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
+static inline bool nvmet_check_auth_status(struct nvmet_req *req)
+{
+ return true;
+}
+static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
+{
+ return false;
+}
+static inline const char *nvmet_dhchap_dhgroup_name(int dhgid) { return NULL; }
+#endif
+
#endif /* _NVMET_H */
--
2.29.2

2021-07-16 11:06:25

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 06/11] nvme: Implement In-Band authentication

Implement NVMe-oF In-Band authentication. This patch adds two new
fabric options 'dhchap_key' to specify the PSK and 'dhchap_authenticate'
to request bi-directional authentication of both the host and the controller.

Signed-off-by: Hannes Reinecke <[email protected]>
---
drivers/nvme/host/Kconfig | 11 +
drivers/nvme/host/Makefile | 1 +
drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++
drivers/nvme/host/auth.h | 23 +
drivers/nvme/host/core.c | 77 +++-
drivers/nvme/host/fabrics.c | 65 ++-
drivers/nvme/host/fabrics.h | 8 +
drivers/nvme/host/nvme.h | 15 +
drivers/nvme/host/trace.c | 32 ++
9 files changed, 1041 insertions(+), 4 deletions(-)
create mode 100644 drivers/nvme/host/auth.c
create mode 100644 drivers/nvme/host/auth.h

diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
index c3f3d77f1aac..853c546305e9 100644
--- a/drivers/nvme/host/Kconfig
+++ b/drivers/nvme/host/Kconfig
@@ -85,3 +85,14 @@ config NVME_TCP
from https://github.com/linux-nvme/nvme-cli.

If unsure, say N.
+
+config NVME_AUTH
+ bool "NVM Express over Fabrics In-Band Authentication"
+ depends on NVME_TCP
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
+ help
+ This provides support for NVMe over Fabrics In-Band Authentication
+ for the NVMe over TCP transport.
+
+ If unsure, say N.
diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
index cbc509784b2e..03748a55a12b 100644
--- a/drivers/nvme/host/Makefile
+++ b/drivers/nvme/host/Makefile
@@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o
nvme-y += pci.o

nvme-fabrics-y += fabrics.o
+nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o

nvme-rdma-y += rdma.o

diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
new file mode 100644
index 000000000000..448a3adebea6
--- /dev/null
+++ b/drivers/nvme/host/auth.c
@@ -0,0 +1,813 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
+ */
+
+#include <linux/crc32.h>
+#include <linux/base64.h>
+#include <asm/unaligned.h>
+#include <crypto/hash.h>
+#include <crypto/kpp.h>
+#include "nvme.h"
+#include "fabrics.h"
+#include "auth.h"
+
+static u32 nvme_dhchap_seqnum;
+
+struct nvme_dhchap_context {
+ struct crypto_shash *shash_tfm;
+ unsigned char *key;
+ size_t key_len;
+ int qid;
+ u32 s1;
+ u32 s2;
+ u16 transaction;
+ u8 status;
+ u8 hash_id;
+ u8 hash_len;
+ u8 c1[64];
+ u8 c2[64];
+ u8 response[64];
+ u8 *ctrl_key;
+ int ctrl_key_len;
+ u8 *host_key;
+ int host_key_len;
+ u8 *sess_key;
+ int sess_key_len;
+};
+
+struct nvmet_dhchap_hash_map {
+ int id;
+ int hash_len;
+ const char hmac[15];
+ const char digest[15];
+} hash_map[] = {
+ {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
+ .hash_len = 32,
+ .hmac = "hmac(sha256)", .digest = "sha256" },
+ {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
+ .hash_len = 48,
+ .hmac = "hmac(sha384)", .digest = "sha384" },
+ {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
+ .hash_len = 64,
+ .hmac = "hmac(sha512)", .digest = "sha512" },
+};
+
+const char *nvme_auth_hmac_name(int hmac_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
+ if (hash_map[i].id == hmac_id)
+ return hash_map[i].hmac;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
+
+const char *nvme_auth_digest_name(int hmac_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
+ if (hash_map[i].id == hmac_id)
+ return hash_map[i].digest;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
+
+int nvme_auth_hmac_len(int hmac_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
+ if (hash_map[i].id == hmac_id)
+ return hash_map[i].hash_len;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_hmac_len);
+
+int nvme_auth_hmac_id(const char *hmac_name)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
+ if (!strncmp(hash_map[i].hmac, hmac_name,
+ strlen(hash_map[i].hmac)))
+ return hash_map[i].id;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
+ size_t *dhchap_key_len)
+{
+ unsigned char *dhchap_key;
+ u32 crc;
+ int key_len;
+ size_t allocated_len;
+
+ allocated_len = strlen(dhchap_secret) - 10;
+ dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
+ if (!dhchap_key)
+ return ERR_PTR(-ENOMEM);
+
+ key_len = base64_decode(dhchap_secret + 10,
+ allocated_len, dhchap_key);
+ if (key_len != 36 && key_len != 52 &&
+ key_len != 68) {
+ pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
+ key_len);
+ kfree(dhchap_key);
+ return ERR_PTR(-EINVAL);
+ }
+ pr_debug("DH-HMAC-CHAP Key: %*ph\n",
+ (int)key_len, dhchap_key);
+
+ /* The last four bytes is the CRC in little-endian format */
+ key_len -= 4;
+ /*
+ * The linux implementation doesn't do pre- and post-increments,
+ * so we have to do it manually.
+ */
+ crc = ~crc32(~0, dhchap_key, key_len);
+
+ if (get_unaligned_le32(dhchap_key + key_len) != crc) {
+ pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n",
+ get_unaligned_le32(dhchap_key + key_len), crc);
+ kfree(dhchap_key);
+ return ERR_PTR(-EKEYREJECTED);
+ }
+ *dhchap_key_len = key_len;
+ return dhchap_key;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
+
+static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
+ void *data, size_t tl)
+{
+ struct nvme_command cmd = {};
+ blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
+ 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
+ struct request_queue *q = qid == NVME_QID_ANY ?
+ ctrl->fabrics_q : ctrl->connect_q;
+ int ret;
+
+ cmd.auth_send.opcode = nvme_fabrics_command;
+ cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
+ cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
+ cmd.auth_send.spsp0 = 0x01;
+ cmd.auth_send.spsp1 = 0x01;
+ cmd.auth_send.tl = tl;
+
+ ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid,
+ 0, flags);
+ if (ret)
+ dev_dbg(ctrl->device,
+ "%s: qid %d error %d\n", __func__, qid, ret);
+ return ret;
+}
+
+static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
+ void *buf, size_t al,
+ u16 transaction, u8 expected_msg )
+{
+ struct nvme_command cmd = {};
+ struct nvmf_auth_dhchap_failure_data *data = buf;
+ blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
+ 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
+ struct request_queue *q = qid == NVME_QID_ANY ?
+ ctrl->fabrics_q : ctrl->connect_q;
+ int ret;
+
+ cmd.auth_receive.opcode = nvme_fabrics_command;
+ cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
+ cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
+ cmd.auth_receive.spsp0 = 0x01;
+ cmd.auth_receive.spsp1 = 0x01;
+ cmd.auth_receive.al = al;
+
+ ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid,
+ 0, flags);
+ if (ret > 0) {
+ dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n",
+ __func__, qid, ret);
+ ret = -EIO;
+ }
+ if (ret < 0) {
+ dev_dbg(ctrl->device, "%s: qid %d error %d\n",
+ __func__, qid, ret);
+ return ret;
+ }
+ dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
+ __func__, qid, data->auth_type, data->auth_id);
+ if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
+ data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
+ return data->reason_code_explanation;
+ }
+ if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
+ data->auth_id != expected_msg) {
+ dev_warn(ctrl->device,
+ "qid %d invalid message %02x/%02x\n",
+ qid, data->auth_type, data->auth_id);
+ return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ }
+ if (le16_to_cpu(data->t_id) != transaction) {
+ dev_warn(ctrl->device,
+ "qid %d invalid transaction ID %d\n",
+ qid, le16_to_cpu(data->t_id));
+ return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ }
+
+ return 0;
+}
+
+static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap,
+ void *buf, size_t buf_size)
+{
+ struct nvmf_auth_dhchap_negotiate_data *data = buf;
+ size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
+
+ if (buf_size < size)
+ return -EINVAL;
+
+ memset((u8 *)buf, 0, size);
+ data->auth_type = NVME_AUTH_COMMON_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
+ data->t_id = cpu_to_le16(chap->transaction);
+ data->sc_c = 0; /* No secure channel concatenation */
+ data->napd = 1;
+ data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
+ data->auth_protocol[0].dhchap.halen = 3;
+ data->auth_protocol[0].dhchap.dhlen = 1;
+ data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
+ data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
+ data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
+ data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
+
+ return size;
+}
+
+static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap,
+ void *buf, size_t buf_size)
+{
+ struct nvmf_auth_dhchap_challenge_data *data = buf;
+ size_t size = sizeof(*data) + data->hl + data->dhvlen;
+ const char *gid_name;
+
+ if (buf_size < size) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ return -ENOMSG;
+ }
+
+ if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 &&
+ data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 &&
+ data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n",
+ chap->qid, data->hashid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+ return -EPROTO;
+ }
+ switch (data->dhgid) {
+ case NVME_AUTH_DHCHAP_DHGROUP_NULL:
+ gid_name = "null";
+ break;
+ default:
+ gid_name = NULL;
+ break;
+ }
+ if (!gid_name) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
+ chap->qid, data->dhgid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ return -EPROTO;
+ }
+ if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ return -EPROTO;
+ }
+ if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
+ chap->qid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+ return -EPROTO;
+ }
+ dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n",
+ __func__, chap->qid, data->hashid);
+ if (nvme_auth_hmac_len(data->hashid) != data->hl) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid hash length\n",
+ chap->qid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+ return -EPROTO;
+ }
+ chap->hash_id = data->hashid;
+ chap->hash_len = data->hl;
+ chap->s1 = le32_to_cpu(data->seqnum);
+ memcpy(chap->c1, data->cval, chap->hash_len);
+
+ return 0;
+}
+
+static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap,
+ void *buf, size_t buf_size)
+{
+ struct nvmf_auth_dhchap_reply_data *data = buf;
+ size_t size = sizeof(*data);
+
+ size += 2 * chap->hash_len;
+ if (ctrl->opts->dhchap_auth) {
+ get_random_bytes(chap->c2, chap->hash_len);
+ chap->s2 = nvme_dhchap_seqnum++;
+ } else
+ memset(chap->c2, 0, chap->hash_len);
+
+ if (chap->host_key_len)
+ size += chap->host_key_len;
+
+ if (buf_size < size)
+ return -EINVAL;
+
+ memset(buf, 0, size);
+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
+ data->t_id = cpu_to_le16(chap->transaction);
+ data->hl = chap->hash_len;
+ data->dhvlen = chap->host_key_len;
+ data->seqnum = cpu_to_le32(chap->s2);
+ memcpy(data->rval, chap->response, chap->hash_len);
+ if (ctrl->opts->dhchap_auth) {
+ dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
+ __func__, chap->qid,
+ chap->hash_len, chap->c2);
+ data->cvalid = 1;
+ memcpy(data->rval + chap->hash_len, chap->c2,
+ chap->hash_len);
+ }
+ if (chap->host_key_len)
+ memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
+ chap->host_key_len);
+
+ return size;
+}
+
+static int nvme_auth_dhchap_success1(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap,
+ void *buf, size_t buf_size)
+{
+ struct nvmf_auth_dhchap_success1_data *data = buf;
+ size_t size = sizeof(*data);
+
+ if (ctrl->opts->dhchap_auth)
+ size += chap->hash_len;
+
+
+ if (buf_size < size) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ return -ENOMSG;
+ }
+
+ if (data->hl != chap->hash_len) {
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: invalid hash length %d\n",
+ chap->qid, data->hl);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
+ return -EPROTO;
+ }
+
+ if (!data->rvalid)
+ return 0;
+
+ /* Validate controller response */
+ if (memcmp(chap->response, data->rval, data->hl)) {
+ dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
+ __func__, chap->qid, chap->hash_len, data->rval);
+ dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
+ __func__, chap->qid, chap->hash_len, chap->response);
+ dev_warn(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: controller authentication failed\n",
+ chap->qid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ return -EPROTO;
+ }
+ dev_info(ctrl->device,
+ "qid %d: DH-HMAC-CHAP: controller authenticated\n",
+ chap->qid);
+ return 0;
+}
+
+static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap,
+ void *buf, size_t buf_size)
+{
+ struct nvmf_auth_dhchap_success2_data *data = buf;
+ size_t size = sizeof(*data);
+
+ memset(buf, 0, size);
+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
+ data->t_id = cpu_to_le16(chap->transaction);
+
+ return size;
+}
+
+static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap,
+ void *buf, size_t buf_size)
+{
+ struct nvmf_auth_dhchap_failure_data *data = buf;
+ size_t size = sizeof(*data);
+
+ memset(buf, 0, size);
+ data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
+ data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
+ data->t_id = cpu_to_le16(chap->transaction);
+ data->reason_code = 1;
+ data->reason_code_explanation = chap->status;
+
+ return size;
+}
+
+int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap)
+{
+ char *hash_name;
+ int ret;
+
+ switch (chap->hash_id) {
+ case NVME_AUTH_DHCHAP_HASH_SHA256:
+ hash_name = "hmac(sha256)";
+ break;
+ case NVME_AUTH_DHCHAP_HASH_SHA384:
+ hash_name = "hmac(sha384)";
+ break;
+ case NVME_AUTH_DHCHAP_HASH_SHA512:
+ hash_name = "hmac(sha512)";
+ break;
+ default:
+ hash_name = NULL;
+ break;
+ }
+ if (!hash_name) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ return -EPROTO;
+ }
+ chap->shash_tfm = crypto_alloc_shash(hash_name, 0,
+ CRYPTO_ALG_ALLOCATES_MEMORY);
+ if (IS_ERR(chap->shash_tfm)) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ chap->shash_tfm = NULL;
+ return -EPROTO;
+ }
+ if (!chap->key) {
+ dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
+ chap->qid);
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ crypto_free_shash(chap->shash_tfm);
+ chap->shash_tfm = NULL;
+ return -EINVAL;
+ }
+ ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
+ if (ret) {
+ chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
+ crypto_free_shash(chap->shash_tfm);
+ chap->shash_tfm = NULL;
+ return ret;
+ }
+ dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
+ chap->qid, hash_name);
+ return 0;
+}
+
+static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap)
+{
+ SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
+ u8 buf[4], *challenge = chap->c1;
+ int ret;
+
+ dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
+ __func__, chap->qid, chap->s1, chap->transaction);
+ shash->tfm = chap->shash_tfm;
+ ret = crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, challenge, chap->hash_len);
+ if (ret)
+ goto out;
+ put_unaligned_le32(chap->s1, buf);
+ ret = crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+ put_unaligned_le16(chap->transaction, buf);
+ ret = crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
+ memset(buf, 0, sizeof(buf));
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, "HostHost", 8);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
+ strlen(ctrl->opts->host->nqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
+ strlen(ctrl->opts->subsysnqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_final(shash, chap->response);
+out:
+ return ret;
+}
+
+static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap)
+{
+ SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
+ u8 buf[4], *challenge = chap->c2;
+ int ret;
+
+ dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
+ __func__, chap->qid, chap->s2, chap->transaction);
+ dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
+ __func__, chap->qid, chap->hash_len, challenge);
+ dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
+ __func__, chap->qid, ctrl->opts->subsysnqn);
+ dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
+ __func__, chap->qid, ctrl->opts->host->nqn);
+ shash->tfm = chap->shash_tfm;
+ ret = crypto_shash_init(shash);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, challenge, chap->hash_len);
+ if (ret)
+ goto out;
+ put_unaligned_le32(chap->s2, buf);
+ ret = crypto_shash_update(shash, buf, 4);
+ if (ret)
+ goto out;
+ put_unaligned_le16(chap->transaction, buf);
+ ret = crypto_shash_update(shash, buf, 2);
+ if (ret)
+ goto out;
+ memset(buf, 0, 4);
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, "Controller", 10);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
+ strlen(ctrl->opts->subsysnqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, buf, 1);
+ if (ret)
+ goto out;
+ ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
+ strlen(ctrl->opts->host->nqn));
+ if (ret)
+ goto out;
+ ret = crypto_shash_final(shash, chap->response);
+out:
+ return ret;
+}
+
+int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
+ struct nvme_dhchap_context *chap)
+{
+ int ret;
+ u8 key_hash;
+ const char *hmac_name;
+ struct crypto_shash *key_tfm;
+
+ if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
+ &key_hash) != 1)
+ return -EINVAL;
+
+ chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
+ &chap->key_len);
+ if (IS_ERR(chap->key)) {
+ ret = PTR_ERR(chap->key);
+ chap->key = NULL;
+ return ret;
+ }
+
+ if (key_hash == 0)
+ return 0;
+
+ hmac_name = nvme_auth_hmac_name(key_hash);
+ if (!hmac_name) {
+ pr_debug("Invalid key hash id %d\n", key_hash);
+ return -EKEYREJECTED;
+ }
+
+ key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
+ if (IS_ERR(key_tfm)) {
+ kfree(chap->key);
+ chap->key = NULL;
+ ret = PTR_ERR(key_tfm);
+ } else {
+ SHASH_DESC_ON_STACK(shash, key_tfm);
+
+ shash->tfm = key_tfm;
+ ret = crypto_shash_setkey(key_tfm, chap->key,
+ chap->key_len);
+ if (ret < 0) {
+ crypto_free_shash(key_tfm);
+ kfree(chap->key);
+ chap->key = NULL;
+ return ret;
+ }
+ crypto_shash_init(shash);
+ crypto_shash_update(shash, ctrl->opts->host->nqn,
+ strlen(ctrl->opts->host->nqn));
+ crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
+ crypto_shash_final(shash, chap->key);
+ crypto_free_shash(key_tfm);
+ }
+ return 0;
+}
+
+void nvme_auth_free(struct nvme_dhchap_context *chap)
+{
+ if (chap->shash_tfm)
+ crypto_free_shash(chap->shash_tfm);
+ if (chap->key)
+ kfree(chap->key);
+ if (chap->ctrl_key)
+ kfree(chap->ctrl_key);
+ if (chap->host_key)
+ kfree(chap->host_key);
+ if (chap->sess_key)
+ kfree(chap->sess_key);
+ kfree(chap);
+}
+
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+ struct nvme_dhchap_context *chap;
+ void *buf;
+ size_t buf_size, tl;
+ int ret = 0;
+
+ chap = kzalloc(sizeof(*chap), GFP_KERNEL);
+ if (!chap)
+ return -ENOMEM;
+ chap->qid = qid;
+ chap->transaction = ctrl->transaction++;
+
+ ret = nvme_auth_generate_key(ctrl, chap);
+ if (ret) {
+ dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n",
+ __func__, ret);
+ nvme_auth_free(chap);
+ return ret;
+ }
+
+ /*
+ * Allocate a large enough buffer for the entire negotiation:
+ * 4k should be enough to ffdhe8192.
+ */
+ buf_size = 4096;
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* DH-HMAC-CHAP Step 1: send negotiate */
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n",
+ __func__, qid);
+ ret = nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size);
+ if (ret < 0)
+ goto out;
+ tl = ret;
+ ret = nvme_auth_send(ctrl, qid, buf, tl);
+ if (ret)
+ goto out;
+
+ memset(buf, 0, buf_size);
+ ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
+ NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
+ if (ret < 0) {
+ dev_dbg(ctrl->device,
+ "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n",
+ __func__, qid);
+ goto out;
+ }
+ if (ret > 0) {
+ chap->status = ret;
+ goto fail1;
+ }
+
+ /* DH-HMAC-CHAP Step 2: receive challenge */
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n",
+ __func__, qid);
+
+ ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size);
+ if (ret) {
+ /* Invalid parameters for negotiate */
+ goto fail2;
+ }
+
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n",
+ __func__, qid);
+ ret = nvme_auth_select_hash(ctrl, chap);
+ if (ret)
+ goto fail2;
+
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
+ __func__, qid);
+ ret = nvme_auth_dhchap_host_response(ctrl, chap);
+ if (ret)
+ goto fail2;
+
+ /* DH-HMAC-CHAP Step 3: send reply */
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n",
+ __func__, qid);
+ ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size);
+ if (ret < 0)
+ goto fail2;
+
+ tl = ret;
+ ret = nvme_auth_send(ctrl, qid, buf, tl);
+ if (ret)
+ goto fail2;
+
+ memset(buf, 0, buf_size);
+ ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
+ NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
+ if (ret < 0) {
+ dev_dbg(ctrl->device,
+ "%s: qid %d DH-HMAC-CHAP failed to receive success1\n",
+ __func__, qid);
+ goto out;
+ }
+ if (ret > 0) {
+ chap->status = ret;
+ goto fail1;
+ }
+
+ if (ctrl->opts->dhchap_auth) {
+ dev_dbg(ctrl->device,
+ "%s: qid %d DH-HMAC-CHAP controller response\n",
+ __func__, qid);
+ ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
+ if (ret)
+ goto fail2;
+ }
+
+ /* DH-HMAC-CHAP Step 4: receive success1 */
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n",
+ __func__, qid);
+ ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size);
+ if (ret < 0) {
+ /* Controller authentication failed */
+ goto fail2;
+ }
+ tl = ret;
+ /* DH-HMAC-CHAP Step 5: send success2 */
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n",
+ __func__, qid);
+ tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size);
+ ret = nvme_auth_send(ctrl, qid, buf, tl);
+ if (!ret)
+ goto out;
+
+fail1:
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status %x\n",
+ __func__, qid, chap->status);
+ goto out;
+
+fail2:
+ dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status %x\n",
+ __func__, qid, chap->status);
+ tl = nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size);
+ ret = nvme_auth_send(ctrl, qid, buf, tl);
+
+out:
+ if (!ret && chap->status)
+ ret = -EPROTO;
+ if (!ret) {
+ ctrl->dhchap_hash = chap->hash_id;
+ }
+ kfree(buf);
+ nvme_auth_free(chap);
+ return ret;
+}
diff --git a/drivers/nvme/host/auth.h b/drivers/nvme/host/auth.h
new file mode 100644
index 000000000000..4950b1cb9470
--- /dev/null
+++ b/drivers/nvme/host/auth.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
+ */
+
+#ifndef _NVME_AUTH_H
+#define _NVME_AUTH_H
+
+const char *nvme_auth_dhgroup_name(int dhgroup_id);
+int nvme_auth_dhgroup_pubkey_size(int dhgroup_id);
+int nvme_auth_dhgroup_privkey_size(int dhgroup_id);
+const char *nvme_auth_dhgroup_kpp(int dhgroup_id);
+int nvme_auth_dhgroup_id(const char *dhgroup_name);
+
+const char *nvme_auth_hmac_name(int hmac_id);
+const char *nvme_auth_digest_name(int hmac_id);
+int nvme_auth_hmac_id(const char *hmac_name);
+int nvme_auth_hmac_len(int hmac_len);
+
+unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
+ size_t *dhchap_key_len);
+
+#endif /* _NVME_AUTH_H */
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 11779be42186..7ce9b666dc09 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
switch (ctrl->state) {
case NVME_CTRL_CONNECTING:
if (blk_rq_is_passthrough(rq) && nvme_is_fabrics(req->cmd) &&
- req->cmd->fabrics.fctype == nvme_fabrics_type_connect)
+ (req->cmd->fabrics.fctype == nvme_fabrics_type_connect ||
+ req->cmd->fabrics.fctype == nvme_fabrics_type_auth_send ||
+ req->cmd->fabrics.fctype == nvme_fabrics_type_auth_receive))
return true;
break;
default:
@@ -3426,6 +3428,66 @@ static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev,
static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store);

+#ifdef CONFIG_NVME_AUTH
+struct nvmet_dhchap_hash_map {
+ int id;
+ const char name[15];
+} hash_map[] = {
+ {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
+ .name = "hmac(sha256)", },
+ {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
+ .name = "hmac(sha384)", },
+ {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
+ .name = "hmac(sha512)", },
+};
+
+static ssize_t dhchap_hash_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
+ if (hash_map[i].id == ctrl->dhchap_hash)
+ return sprintf(buf, "%s\n", hash_map[i].name);
+ }
+ return sprintf(buf, "none\n");
+}
+DEVICE_ATTR_RO(dhchap_hash);
+
+struct nvmet_dhchap_group_map {
+ int id;
+ const char name[15];
+} dhgroup_map[] = {
+ {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
+ .name = "NULL", },
+ {.id = NVME_AUTH_DHCHAP_DHGROUP_2048,
+ .name = "ffdhe2048", },
+ {.id = NVME_AUTH_DHCHAP_DHGROUP_3072,
+ .name = "ffdhe3072", },
+ {.id = NVME_AUTH_DHCHAP_DHGROUP_4096,
+ .name = "ffdhe4096", },
+ {.id = NVME_AUTH_DHCHAP_DHGROUP_6144,
+ .name = "ffdhe6144", },
+ {.id = NVME_AUTH_DHCHAP_DHGROUP_8192,
+ .name = "ffdhe8192", },
+};
+
+static ssize_t dhchap_dhgroup_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+ if (hash_map[i].id == ctrl->dhchap_dhgroup)
+ return sprintf(buf, "%s\n", dhgroup_map[i].name);
+ }
+ return sprintf(buf, "none\n");
+}
+DEVICE_ATTR_RO(dhchap_dhgroup);
+#endif
+
static struct attribute *nvme_dev_attrs[] = {
&dev_attr_reset_controller.attr,
&dev_attr_rescan_controller.attr,
@@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = {
&dev_attr_reconnect_delay.attr,
&dev_attr_fast_io_fail_tmo.attr,
&dev_attr_kato.attr,
+#ifdef CONFIG_NVME_AUTH
+ &dev_attr_dhchap_hash.attr,
+ &dev_attr_dhchap_dhgroup.attr,
+#endif
NULL
};

@@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,
return 0;
if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
return 0;
+#ifdef CONFIG_NVME_AUTH
+ if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts)
+ return 0;
+#endif

return a->mode;
}
@@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)
BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
+ BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8);
+ BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16);
+ BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16);
+ BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16);
+ BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16);
}


diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index a5469fd9d4c3..6404ab9b604b 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
union nvme_result res;
struct nvmf_connect_data *data;
int ret;
+ u32 result;

cmd.connect.opcode = nvme_fabrics_command;
cmd.connect.fctype = nvme_fabrics_type_connect;
@@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
goto out_free_data;
}

- ctrl->cntlid = le16_to_cpu(res.u16);
-
+ result = le32_to_cpu(res.u32);
+ ctrl->cntlid = result & 0xFFFF;
+ if ((result >> 16) & 2) {
+ /* Authentication required */
+ ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
+ if (ret)
+ dev_warn(ctrl->device,
+ "qid 0: authentication failed\n");
+ else
+ dev_info(ctrl->device,
+ "qid 0: authenticated\n");
+ }
out_free_data:
kfree(data);
return ret;
@@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)
struct nvmf_connect_data *data;
union nvme_result res;
int ret;
+ u32 result;

cmd.connect.opcode = nvme_fabrics_command;
cmd.connect.fctype = nvme_fabrics_type_connect;
@@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)
nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32),
&cmd, data);
}
+ result = le32_to_cpu(res.u32);
+ if ((result >> 16) & 2) {
+ /* Authentication required */
+ ret = nvme_auth_negotiate(ctrl, qid);
+ if (ret)
+ dev_warn(ctrl->device,
+ "qid %u: authentication failed\n", qid);
+ else
+ dev_info(ctrl->device,
+ "qid %u: authenticated\n", qid);
+ }
kfree(data);
return ret;
}
@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
{ NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" },
{ NVMF_OPT_TOS, "tos=%d" },
{ NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" },
+ { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" },
+ { NVMF_OPT_DHCHAP_AUTH, "authenticate" },
+ { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" },
{ NVMF_OPT_ERR, NULL }
};

@@ -824,6 +850,35 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
}
opts->tos = token;
break;
+ case NVMF_OPT_DHCHAP_SECRET:
+ p = match_strdup(args);
+ if (!p) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (strncmp(p, "DHHC-1:00:", 10)) {
+ pr_err("Invalid DH-CHAP secret %s\n", p);
+ ret = -EINVAL;
+ goto out;
+ }
+ kfree(opts->dhchap_secret);
+ opts->dhchap_secret = p;
+ break;
+ case NVMF_OPT_DHCHAP_AUTH:
+ opts->dhchap_auth = true;
+ break;
+ case NVMF_OPT_DHCHAP_GROUP:
+ if (match_int(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (token <= 0) {
+ pr_err("Invalid dhchap_group %d\n", token);
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->dhchap_group = token;
+ break;
default:
pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
p);
@@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts)
kfree(opts->subsysnqn);
kfree(opts->host_traddr);
kfree(opts->host_iface);
+ kfree(opts->dhchap_secret);
kfree(opts);
}
EXPORT_SYMBOL_GPL(nvmf_free_options);
@@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options);
NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \
NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
NVMF_OPT_DISABLE_SQFLOW |\
- NVMF_OPT_FAIL_FAST_TMO)
+ NVMF_OPT_CTRL_LOSS_TMO |\
+ NVMF_OPT_FAIL_FAST_TMO |\
+ NVMF_OPT_DHCHAP_SECRET |\
+ NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)

static struct nvme_ctrl *
nvmf_create_ctrl(struct device *dev, const char *buf)
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index a146cb903869..535bc544f0f6 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -67,6 +67,9 @@ enum {
NVMF_OPT_TOS = 1 << 19,
NVMF_OPT_FAIL_FAST_TMO = 1 << 20,
NVMF_OPT_HOST_IFACE = 1 << 21,
+ NVMF_OPT_DHCHAP_SECRET = 1 << 22,
+ NVMF_OPT_DHCHAP_AUTH = 1 << 23,
+ NVMF_OPT_DHCHAP_GROUP = 1 << 24,
};

/**
@@ -96,6 +99,8 @@ enum {
* @max_reconnects: maximum number of allowed reconnect attempts before removing
* the controller, (-1) means reconnect forever, zero means remove
* immediately;
+ * @dhchap_secret: DH-HMAC-CHAP secret
+ * @dhchap_auth: DH-HMAC-CHAP authenticate controller
* @disable_sqflow: disable controller sq flow control
* @hdr_digest: generate/verify header digest (TCP)
* @data_digest: generate/verify data digest (TCP)
@@ -120,6 +125,9 @@ struct nvmf_ctrl_options {
unsigned int kato;
struct nvmf_host *host;
int max_reconnects;
+ char *dhchap_secret;
+ int dhchap_group;
+ bool dhchap_auth;
bool disable_sqflow;
bool hdr_digest;
bool data_digest;
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 18ef8dd03a90..bcd5b8276c26 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -328,6 +328,12 @@ struct nvme_ctrl {
struct work_struct ana_work;
#endif

+#ifdef CONFIG_NVME_AUTH
+ u16 transaction;
+ u8 dhchap_hash;
+ u8 dhchap_dhgroup;
+#endif
+
/* Power saving configuration */
u64 ps_max_latency_us;
bool apst_enabled;
@@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)
return ctrl->sgls & ((1 << 0) | (1 << 1));
}

+#ifdef CONFIG_NVME_AUTH
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
+#else
+static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+ return -EPROTONOSUPPORT;
+}
+#endif
+
u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
u8 opcode);
int nvme_execute_passthru_rq(struct request *rq);
diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c
index 6543015b6121..66f75d8ea925 100644
--- a/drivers/nvme/host/trace.c
+++ b/drivers/nvme/host/trace.c
@@ -271,6 +271,34 @@ static const char *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc)
return ret;
}

+static const char *nvme_trace_fabrics_auth_send(struct trace_seq *p, u8 *spc)
+{
+ const char *ret = trace_seq_buffer_ptr(p);
+ u8 spsp0 = spc[1];
+ u8 spsp1 = spc[2];
+ u8 secp = spc[3];
+ u32 tl = get_unaligned_le32(spc + 4);
+
+ trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u",
+ spsp0, spsp1, secp, tl);
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
+static const char *nvme_trace_fabrics_auth_receive(struct trace_seq *p, u8 *spc)
+{
+ const char *ret = trace_seq_buffer_ptr(p);
+ u8 spsp0 = spc[1];
+ u8 spsp1 = spc[2];
+ u8 secp = spc[3];
+ u32 al = get_unaligned_le32(spc + 4);
+
+ trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, al=%u",
+ spsp0, spsp1, secp, al);
+ trace_seq_putc(p, 0);
+ return ret;
+}
+
static const char *nvme_trace_fabrics_common(struct trace_seq *p, u8 *spc)
{
const char *ret = trace_seq_buffer_ptr(p);
@@ -290,6 +318,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct trace_seq *p,
return nvme_trace_fabrics_connect(p, spc);
case nvme_fabrics_type_property_get:
return nvme_trace_fabrics_property_get(p, spc);
+ case nvme_fabrics_type_auth_send:
+ return nvme_trace_fabrics_auth_send(p, spc);
+ case nvme_fabrics_type_auth_receive:
+ return nvme_trace_fabrics_auth_receive(p, spc);
default:
return nvme_trace_fabrics_common(p, spc);
}
--
2.29.2

2021-07-16 11:06:58

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 10/11] nvmet-auth: implement support for augmented challenge

Implement support for augmented challenge with FFDHE groups.
This patch adds a new configfs attribute 'dhchap_dhgroup' to
select the DH group to use.

Signed-off-by: Hannes Reinecke <[email protected]>
---
drivers/nvme/target/auth.c | 241 ++++++++++++++++++++++++-
drivers/nvme/target/configfs.c | 31 ++++
drivers/nvme/target/fabrics-cmd-auth.c | 14 +-
3 files changed, 281 insertions(+), 5 deletions(-)

diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index 00c7d051dfb1..cc7f12a7c8bf 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -58,11 +58,56 @@ int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)

int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
{
+ struct nvmet_host_link *p;
+ struct nvmet_host *host = NULL;
+ const char *dhgroup_kpp;
int ret = -ENOTSUPP;

if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
return 0;

+ down_read(&nvmet_config_sem);
+ if (ctrl->subsys->type == NVME_NQN_DISC)
+ goto out_unlock;
+
+ list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
+ if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
+ continue;
+ host = p->host;
+ break;
+ }
+ if (!host) {
+ pr_debug("host %s not found\n", ctrl->hostnqn);
+ ret = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (host->dhchap_dhgroup_id != dhgroup_id) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
+ if (!dhgroup_kpp) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+ ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0);
+ if (IS_ERR(ctrl->dh_tfm)) {
+ pr_debug("failed to setup DH group %d, err %ld\n",
+ dhgroup_id, PTR_ERR(ctrl->dh_tfm));
+ ret = PTR_ERR(ctrl->dh_tfm);
+ ctrl->dh_tfm = NULL;
+ } else {
+ ctrl->dh_gid = dhgroup_id;
+ ctrl->dh_keysize = nvme_auth_dhgroup_pubkey_size(dhgroup_id);
+ pr_debug("select DH group %d keysize %d\n",
+ ctrl->dh_gid, ctrl->dh_keysize);
+ ret = 0;
+ }
+
+out_unlock:
+ up_read(&nvmet_config_sem);
+
return ret;
}

@@ -192,6 +237,101 @@ bool nvmet_check_auth_status(struct nvmet_req *req)
return true;
}

+static int nvmet_auth_hash_sesskey(struct nvmet_req *req, u8 *hashed_key)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ const char *hmac_name, *digest_name;
+ struct crypto_shash *tfm;
+ int hmac_id, ret;
+
+ if (!ctrl->shash_tfm) {
+ pr_debug("%s: hash alg not set\n", __func__);
+ return -EINVAL;
+ }
+ hmac_name = crypto_shash_alg_name(ctrl->shash_tfm);
+ hmac_id = nvme_auth_hmac_id(hmac_name);
+ if (hmac_id < 0) {
+ pr_debug("%s: unsupported hmac %s\n", __func__,
+ hmac_name);
+ return -EINVAL;
+ }
+ digest_name = nvme_auth_digest_name(hmac_id);
+ if (!digest_name) {
+ pr_debug("%s: failed to get digest for %s\n", __func__,
+ hmac_name);
+ return -EINVAL;
+ }
+ tfm = crypto_alloc_shash(digest_name, 0, 0);
+ if (IS_ERR(tfm))
+ return -ENOMEM;
+
+ ret = crypto_shash_tfm_digest(tfm, req->sq->dhchap_skey,
+ req->sq->dhchap_skey_len, hashed_key);
+ if (ret < 0)
+ pr_debug("%s: Failed to hash digest len %d\n", __func__,
+ req->sq->dhchap_skey_len);
+
+ crypto_free_shash(tfm);
+ return ret;
+}
+
+static int nvmet_auth_augmented_challenge(struct nvmet_req *req,
+ u8 *challenge, u8 *aug)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ u8 *hashed_key;
+ const char *hash_name;
+ int hash_len = req->sq->dhchap_hash_len;
+ int ret;
+
+ hashed_key = kmalloc(hash_len, GFP_KERNEL);
+ if (!hashed_key)
+ return -ENOMEM;
+
+ ret = nvmet_auth_hash_sesskey(req, hashed_key);
+ if (ret < 0) {
+ pr_debug("failed to hash session key, err %d\n", ret);
+ kfree(hashed_key);
+ return ret;
+ }
+ hash_name = crypto_shash_alg_name(ctrl->shash_tfm);
+ if (!hash_name) {
+ pr_debug("Invalid hash algoritm\n");
+ return -EINVAL;
+ }
+ tfm = crypto_alloc_shash(hash_name, 0, 0);
+ if (IS_ERR(tfm)) {
+ ret = PTR_ERR(tfm);
+ goto out_free_key;
+ }
+ desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!desc) {
+ ret = -ENOMEM;
+ goto out_free_hash;
+ }
+ desc->tfm = tfm;
+
+ ret = crypto_shash_setkey(tfm, hashed_key, hash_len);
+ if (ret)
+ goto out_free_desc;
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto out_free_desc;
+ crypto_shash_update(desc, challenge, hash_len);
+ crypto_shash_final(desc, aug);
+
+out_free_desc:
+ kfree_sensitive(desc);
+out_free_hash:
+ crypto_free_shash(tfm);
+out_free_key:
+ kfree(hashed_key);
+ return ret;
+}
+
int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
unsigned int shash_len)
{
@@ -202,8 +342,15 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
int ret;

if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
- ret = -ENOTSUPP;
- goto out;
+ challenge = kmalloc(shash_len, GFP_KERNEL);
+ if (!challenge) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = nvmet_auth_augmented_challenge(req, req->sq->dhchap_c1,
+ challenge);
+ if (ret)
+ goto out;
}

shash->tfm = ctrl->shash_tfm;
@@ -264,8 +411,15 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
ctrl->cntlid, ctrl->hostnqn);

if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
- ret = -ENOTSUPP;
- goto out;
+ challenge = kmalloc(shash_len, GFP_KERNEL);
+ if (!challenge) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = nvmet_auth_augmented_challenge(req, req->sq->dhchap_c2,
+ challenge);
+ if (ret)
+ goto out;
}

shash->tfm = ctrl->shash_tfm;
@@ -307,6 +461,85 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
return 0;
}

+int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
+ u8 *buf, int buf_size)
+{
+ struct nvmet_ctrl *ctrl = req->sq->ctrl;
+ struct kpp_request *kpp_req;
+ struct crypto_wait wait;
+ char *pkey;
+ struct scatterlist dst;
+ int ret, pkey_len;
+
+ if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
+ ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
+ ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
+ ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
+ ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) {
+ struct dh p = {0};
+ int bits = nvme_auth_dhgroup_pubkey_size(ctrl->dh_gid) << 3;
+
+ ret = crypto_ffdhe_params(&p, bits);
+ if (ret)
+ return ret;
+
+ p.key = ctrl->dhchap_key;
+ p.key_size = ctrl->dhchap_key_len;
+
+ pkey_len = crypto_dh_key_len(&p);
+ pkey = kmalloc(pkey_len, GFP_KERNEL);
+ if (!pkey)
+ return -ENOMEM;
+
+ get_random_bytes(pkey, pkey_len);
+ ret = crypto_dh_encode_key(pkey, pkey_len, &p);
+ if (ret) {
+ pr_debug("failed to encode private key, error %d\n",
+ ret);
+ goto out;
+ }
+ } else {
+ pr_warn("invalid dh group %d\n", ctrl->dh_gid);
+ return -EINVAL;
+ }
+ ret = crypto_kpp_set_secret(ctrl->dh_tfm, pkey, pkey_len);
+ if (ret) {
+ pr_debug("failed to set private key, error %d\n", ret);
+ goto out;
+ }
+
+ kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
+ if (!kpp_req) {
+ pr_debug("cannot allocate kpp request\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ crypto_init_wait(&wait);
+ kpp_request_set_input(kpp_req, NULL, 0);
+ sg_init_one(&dst, buf, buf_size);
+ kpp_request_set_output(kpp_req, &dst, buf_size);
+ kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+
+ ret = crypto_wait_req(crypto_kpp_generate_public_key(kpp_req), &wait);
+ kpp_request_free(kpp_req);
+ if (ret == -EOVERFLOW) {
+ pr_debug("public key buffer too small, need %d is %d\n",
+ crypto_kpp_maxsize(ctrl->dh_tfm), buf_size);
+ ret = -ENOKEY;
+ } else if (ret) {
+ pr_debug("failed to generate public key, err %d\n", ret);
+ ret = -ENOKEY;
+ } else
+ pr_debug("%s: ctrl public key %*ph\n", __func__,
+ (int)buf_size, buf);
+
+out:
+ kfree_sensitive(pkey);
+ return ret;
+}
+
int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
u8 *pkey, int pkey_size)
{
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index e0760911a761..e9b8884a83b0 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -1712,9 +1712,40 @@ static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,

CONFIGFS_ATTR(nvmet_host_, dhchap_hash);

+static ssize_t nvmet_host_dhchap_dhgroup_show(struct config_item *item,
+ char *page)
+{
+ struct nvmet_host *host = to_host(item);
+ const char *dhgroup = nvme_auth_dhgroup_name(host->dhchap_dhgroup_id);
+
+ return sprintf(page, "%s\n", dhgroup ? dhgroup : "none");
+}
+
+static ssize_t nvmet_host_dhchap_dhgroup_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct nvmet_host *host = to_host(item);
+ int dhgroup_id;
+
+ dhgroup_id = nvme_auth_dhgroup_id(page);
+ if (dhgroup_id < 0)
+ return -EINVAL;
+ if (dhgroup_id != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
+ const char *kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
+
+ if (!crypto_has_kpp(kpp, 0, 0))
+ return -EINVAL;
+ }
+ host->dhchap_dhgroup_id = dhgroup_id;
+ return count;
+}
+
+CONFIGFS_ATTR(nvmet_host_, dhchap_dhgroup);
+
static struct configfs_attribute *nvmet_host_attrs[] = {
&nvmet_host_attr_dhchap_key,
&nvmet_host_attr_dhchap_hash,
+ &nvmet_host_attr_dhchap_dhgroup,
NULL,
};
#endif /* CONFIG_NVME_TARGET_AUTH */
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 962f9f5e9d89..478ac351c645 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -98,7 +98,11 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;

if (data->dhvlen) {
- return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ if (!ctrl->dh_tfm)
+ return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
+ if (nvmet_auth_ctrl_sesskey(req, data->rval + 2 * data->hl,
+ data->dhvlen) < 0)
+ return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
}

response = kmalloc(data->hl, GFP_KERNEL);
@@ -299,6 +303,8 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
int ret = 0;
int data_size = sizeof(*d) + req->sq->dhchap_hash_len;

+ if (ctrl->dh_tfm)
+ data_size += ctrl->dh_keysize;
if (al < data_size) {
pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
al, data_size);
@@ -317,6 +323,12 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
return -ENOMEM;
get_random_bytes(req->sq->dhchap_c1, data->hl);
memcpy(data->cval, req->sq->dhchap_c1, data->hl);
+ if (ctrl->dh_tfm) {
+ data->dhgid = ctrl->dh_gid;
+ data->dhvlen = ctrl->dh_keysize;
+ ret = nvmet_auth_ctrl_exponential(req, data->cval + data->hl,
+ data->dhvlen);
+ }
pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n",
__func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
req->sq->dhchap_tid, data->hl, data->dhvlen);
--
2.29.2

2021-07-16 11:06:58

by Hannes Reinecke

[permalink] [raw]
Subject: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms

TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE
groups, and these are already implemented in the kernel.
So add support for these non-standard groups for NVMe in-band
authentication to validate the augmented challenge implementation.

Signed-off-by: Hannes Reinecke <[email protected]>
---
drivers/nvme/host/auth.c | 38 +++++++++++++++++++++++++++++++++++++-
drivers/nvme/target/auth.c | 23 +++++++++++++++++++++++
include/linux/nvme.h | 2 ++
3 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
index 754343aced19..d0dd63b455ef 100644
--- a/drivers/nvme/host/auth.c
+++ b/drivers/nvme/host/auth.c
@@ -10,6 +10,8 @@
#include <crypto/kpp.h>
#include <crypto/dh.h>
#include <crypto/ffdhe.h>
+#include <crypto/ecdh.h>
+#include <crypto/curve25519.h>
#include "nvme.h"
#include "fabrics.h"
#include "auth.h"
@@ -67,6 +69,13 @@ struct nvme_auth_dhgroup_map {
{ .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
.name = "ffdhe8192", .kpp = "dh",
.privkey_size = 1024, .pubkey_size = 1024 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_ECDH,
+ .name = "ecdh", .kpp = "ecdh-nist-p256",
+ .privkey_size = 32, .pubkey_size = 64 },
+ { .id = NVME_AUTH_DHCHAP_DHGROUP_25519,
+ .name = "curve25519", .kpp = "curve25519",
+ .privkey_size = CURVE25519_KEY_SIZE,
+ .pubkey_size = CURVE25519_KEY_SIZE },
};

const char *nvme_auth_dhgroup_name(int dhgroup_id)
@@ -337,7 +346,7 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
data->napd = 1;
data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
data->auth_protocol[0].dhchap.halen = 3;
- data->auth_protocol[0].dhchap.dhlen = 6;
+ data->auth_protocol[0].dhchap.dhlen = 8;
data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
@@ -347,6 +356,8 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
data->auth_protocol[0].dhchap.idlist[6] = NVME_AUTH_DHCHAP_DHGROUP_4096;
data->auth_protocol[0].dhchap.idlist[7] = NVME_AUTH_DHCHAP_DHGROUP_6144;
data->auth_protocol[0].dhchap.idlist[8] = NVME_AUTH_DHCHAP_DHGROUP_8192;
+ data->auth_protocol[0].dhchap.idlist[9] = NVME_AUTH_DHCHAP_DHGROUP_ECDH;
+ data->auth_protocol[0].dhchap.idlist[10] = NVME_AUTH_DHCHAP_DHGROUP_25519;

return size;
}
@@ -889,6 +900,31 @@ static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl,
}
chap->host_key_len = pubkey_size;
chap->sess_key_len = pubkey_size;
+ } else if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_ECDH) {
+ struct ecdh p = {0};
+
+ pkey_len = crypto_ecdh_key_len(&p);
+ pkey = kzalloc(pkey_len, GFP_KERNEL);
+ if (!pkey)
+ return -ENOMEM;
+
+ get_random_bytes(pkey, pkey_len);
+ ret = crypto_ecdh_encode_key(pkey, pkey_len, &p);
+ if (ret) {
+ dev_dbg(ctrl->device,
+ "failed to encode pkey, error %d\n", ret);
+ kfree(pkey);
+ return ret;
+ }
+ chap->host_key_len = 64;
+ chap->sess_key_len = 32;
+ } else if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_25519) {
+ pkey_len = CURVE25519_KEY_SIZE;
+ pkey = kzalloc(pkey_len, GFP_KERNEL);
+ if (!pkey)
+ return -ENOMEM;
+ get_random_bytes(pkey, pkey_len);
+ chap->host_key_len = chap->sess_key_len = CURVE25519_KEY_SIZE;
} else {
dev_warn(ctrl->device, "Invalid DH group id %d\n",
chap->dhgroup_id);
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index cc7f12a7c8bf..7e3b613cb08b 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -13,6 +13,8 @@
#include <crypto/kpp.h>
#include <crypto/dh.h>
#include <crypto/ffdhe.h>
+#include <crypto/ecdh.h>
+#include <crypto/curve25519.h>
#include <linux/crc32.h>
#include <linux/base64.h>
#include <linux/ctype.h>
@@ -498,6 +500,27 @@ int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
ret);
goto out;
}
+ } else if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_ECDH) {
+ struct ecdh p = {0};
+
+ pkey_len = crypto_ecdh_key_len(&p);
+ pkey = kmalloc(pkey_len, GFP_KERNEL);
+ if (!pkey)
+ return -ENOMEM;
+
+ get_random_bytes(pkey, pkey_len);
+ ret = crypto_ecdh_encode_key(pkey, pkey_len, &p);
+ if (ret) {
+ pr_debug("failed to encode private key, error %d\n",
+ ret);
+ goto out;
+ }
+ } else if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_25519) {
+ pkey_len = CURVE25519_KEY_SIZE;
+ pkey = kmalloc(pkey_len, GFP_KERNEL);
+ if (!pkey)
+ return -ENOMEM;
+ get_random_bytes(pkey, pkey_len);
} else {
pr_warn("invalid dh group %d\n", ctrl->dh_gid);
return -EINVAL;
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 7b94abacfd08..75b638adbca1 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -1476,6 +1476,8 @@ enum {
NVME_AUTH_DHCHAP_DHGROUP_4096 = 0x03,
NVME_AUTH_DHCHAP_DHGROUP_6144 = 0x04,
NVME_AUTH_DHCHAP_DHGROUP_8192 = 0x05,
+ NVME_AUTH_DHCHAP_DHGROUP_ECDH = 0x0e,
+ NVME_AUTH_DHCHAP_DHGROUP_25519 = 0x0f,
};

union nvmf_auth_protocol {
--
2.29.2

2021-07-17 06:07:30

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support


> Hi all,

Hey Hannes, nice progress. This is definitely
a step in the right direction.

> recent updates to the NVMe spec have added definitions for in-band
> authentication, and seeing that it provides some real benefit especially
> for NVMe-TCP here's an attempt to implement it.

Please call out the TP 8006 specifically so people can look
into it.

> Tricky bit here is that the specification orients itself on TLS 1.3,
> but supports only the FFDHE groups. Which of course the kernel doesn't
> support. I've been able to come up with a patch for this, but as this
> is my first attempt to fix anything in the crypto area I would invite
> people more familiar with these matters to have a look.

Glad to see this turned out to be very simple!

> Also note that this is just for in-band authentication. Secure concatenation
> (ie starting TLS with the negotiated parameters) is not implemented; one would
> need to update the kernel TLS implementation for this, which at this time is
> beyond scope.

TLS is an additional effort, as discussed, inband auth alone
has merits and we should not lock it down to NVMe/TCP-TLS.

> As usual, comments and reviews are welcome.

Having another look into this now...

2021-07-17 06:12:51

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 01/11] crypto: add crypto_has_shash()

> Add helper function to determine if a given synchronous hash is supported.

Can you add more info in the change log? Who is the consumer
and why is this needed.

2021-07-17 06:15:33

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters

> Add helper functions to generaten Finite Field DH Ephemeral Parameters as
> specified in RFC 7919.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> crypto/Kconfig | 8 +
> crypto/Makefile | 1 +
> crypto/ffdhe_helper.c | 877 +++++++++++++++++++++++++++++++++++++++++
> include/crypto/ffdhe.h | 24 ++
> 4 files changed, 910 insertions(+)
> create mode 100644 crypto/ffdhe_helper.c
> create mode 100644 include/crypto/ffdhe.h
>
> diff --git a/crypto/Kconfig b/crypto/Kconfig
> index ca3b02dcbbfa..1bea506ba56f 100644
> --- a/crypto/Kconfig
> +++ b/crypto/Kconfig
> @@ -231,6 +231,14 @@ config CRYPTO_DH
> help
> Generic implementation of the Diffie-Hellman algorithm.
>
> +config CRYPTO_FFDHE
> + tristate "Finite Field DH (RFC 7919) ephemeral parameters"

I'd stick with "Diffie-Hellman" in the tristate.

> + select CRYPTO_DH
> + select CRYPTO_KPP
> + select CRYPTO_RNG_DEFAULT
> + help
> + Generic implementation of the Finite Field DH algorithm

Diffie-Hellman algorithm
And not sure I'd call it algorithm implementation, but rather a
helper but maybe something like:
Finite Field Diffie-Hellman ephemeral parameters helper implementation

> +
> config CRYPTO_ECC
> tristate
>
> diff --git a/crypto/Makefile b/crypto/Makefile
> index 10526d4559b8..d3bc79fba23f 100644
> --- a/crypto/Makefile
> +++ b/crypto/Makefile
> @@ -177,6 +177,7 @@ obj-$(CONFIG_CRYPTO_OFB) += ofb.o
> obj-$(CONFIG_CRYPTO_ECC) += ecc.o
> obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o
> obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o
> +obj-$(CONFIG_CRYPTO_FFDHE) += ffdhe_helper.o
>
> ecdh_generic-y += ecdh.o
> ecdh_generic-y += ecdh_helper.o
> diff --git a/crypto/ffdhe_helper.c b/crypto/ffdhe_helper.c
> new file mode 100644
> index 000000000000..dc023e30c4e5
> --- /dev/null
> +++ b/crypto/ffdhe_helper.c
> @@ -0,0 +1,877 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Finite Field DH Ephemeral Parameters (RFC 7919)
> + *
> + * Copyright (c) 2021, Hannes Reinecke, SUSE Software Products
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <crypto/internal/kpp.h>
> +#include <crypto/kpp.h>
> +#include <crypto/dh.h>
> +#include <linux/mpi.h>
> +
> +/*
> + * ffdhe2048 generator (g), modulus (p) and group size (q)

Maybe worth to refer exactly the source of these parameters
in the comment body (rfc section/appendix).

> + */
> +const u8 ffdhe2048_g[] = { 0x02 };
> +
> +const u8 ffdhe2048_p[] = {
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
> + 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
> + 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
> + 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
> + 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
> + 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
> + 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
> + 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
> + 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
> + 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
> + 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
> + 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
> + 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
> + 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
> + 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
> + 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
> + 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
> + 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
> + 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
> + 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
> + 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
> + 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
> + 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
> + 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
> + 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
> + 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
> + 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
> + 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
> + 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
> + 0x88,0x6b,0x42,0x38,0x61,0x28,0x5c,0x97,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +const u8 ffdhe2048_q[] = {
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
> + 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
> + 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
> + 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
> + 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
> + 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
> + 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
> + 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
> + 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
> + 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
> + 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
> + 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
> + 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
> + 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
> + 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
> + 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
> + 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
> + 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
> + 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
> + 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
> + 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
> + 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
> + 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
> + 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
> + 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
> + 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
> + 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
> + 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
> + 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
> + 0x44,0x35,0xa1,0x1c,0x30,0x94,0x2e,0x4b,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +/*
> + * ffdhe3072 generator (g), modulus (p) and group size (q)
> + */
> +
> +const u8 ffdhe3072_g[] = { 0x02 };
> +
> +const u8 ffdhe3072_p[] = {
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
> + 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
> + 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
> + 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
> + 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
> + 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
> + 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
> + 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
> + 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
> + 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
> + 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
> + 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
> + 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
> + 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
> + 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
> + 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
> + 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
> + 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
> + 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
> + 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
> + 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
> + 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
> + 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
> + 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
> + 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
> + 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
> + 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
> + 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
> + 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
> + 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
> + 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
> + 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
> + 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
> + 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
> + 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
> + 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
> + 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
> + 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
> + 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
> + 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
> + 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
> + 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
> + 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
> + 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
> + 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
> + 0x25,0xe4,0x1d,0x2b,0x66,0xc6,0x2e,0x37,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +const u8 ffdhe3072_q[] = {
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
> + 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
> + 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
> + 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
> + 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
> + 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
> + 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
> + 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
> + 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
> + 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
> + 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
> + 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
> + 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
> + 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
> + 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
> + 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
> + 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
> + 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
> + 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
> + 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
> + 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
> + 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
> + 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
> + 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
> + 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
> + 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
> + 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
> + 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
> + 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
> + 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
> + 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
> + 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
> + 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
> + 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
> + 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
> + 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
> + 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
> + 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
> + 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
> + 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
> + 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
> + 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
> + 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
> + 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
> + 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
> + 0x12,0xf2,0x0e,0x95,0xb3,0x63,0x17,0x1b,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +/*
> + * ffdhe4096 generator (g), modulus (p) and group size (q)
> + */
> +
> +const u8 ffdhe4096_g[] = { 0x02 };
> +
> +const u8 ffdhe4096_p[] = {
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
> + 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
> + 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
> + 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
> + 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
> + 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
> + 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
> + 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
> + 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
> + 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
> + 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
> + 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
> + 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
> + 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
> + 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
> + 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
> + 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
> + 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
> + 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
> + 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
> + 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
> + 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
> + 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
> + 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
> + 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
> + 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
> + 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
> + 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
> + 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
> + 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
> + 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
> + 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
> + 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
> + 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
> + 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
> + 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
> + 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
> + 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
> + 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
> + 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
> + 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
> + 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
> + 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
> + 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
> + 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
> + 0x25,0xe4,0x1d,0x2b,0x66,0x9e,0x1e,0xf1,
> + 0x6e,0x6f,0x52,0xc3,0x16,0x4d,0xf4,0xfb,
> + 0x79,0x30,0xe9,0xe4,0xe5,0x88,0x57,0xb6,
> + 0xac,0x7d,0x5f,0x42,0xd6,0x9f,0x6d,0x18,
> + 0x77,0x63,0xcf,0x1d,0x55,0x03,0x40,0x04,
> + 0x87,0xf5,0x5b,0xa5,0x7e,0x31,0xcc,0x7a,
> + 0x71,0x35,0xc8,0x86,0xef,0xb4,0x31,0x8a,
> + 0xed,0x6a,0x1e,0x01,0x2d,0x9e,0x68,0x32,
> + 0xa9,0x07,0x60,0x0a,0x91,0x81,0x30,0xc4,
> + 0x6d,0xc7,0x78,0xf9,0x71,0xad,0x00,0x38,
> + 0x09,0x29,0x99,0xa3,0x33,0xcb,0x8b,0x7a,
> + 0x1a,0x1d,0xb9,0x3d,0x71,0x40,0x00,0x3c,
> + 0x2a,0x4e,0xce,0xa9,0xf9,0x8d,0x0a,0xcc,
> + 0x0a,0x82,0x91,0xcd,0xce,0xc9,0x7d,0xcf,
> + 0x8e,0xc9,0xb5,0x5a,0x7f,0x88,0xa4,0x6b,
> + 0x4d,0xb5,0xa8,0x51,0xf4,0x41,0x82,0xe1,
> + 0xc6,0x8a,0x00,0x7e,0x5e,0x65,0x5f,0x6a,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +const u8 ffdhe4096_q[] = {
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
> + 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
> + 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
> + 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
> + 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
> + 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
> + 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
> + 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
> + 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
> + 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
> + 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
> + 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
> + 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
> + 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
> + 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
> + 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
> + 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
> + 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
> + 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
> + 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
> + 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
> + 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
> + 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
> + 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
> + 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
> + 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
> + 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
> + 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
> + 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
> + 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
> + 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
> + 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
> + 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
> + 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
> + 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
> + 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
> + 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
> + 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
> + 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
> + 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
> + 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
> + 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
> + 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
> + 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
> + 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
> + 0x12,0xf2,0x0e,0x95,0xb3,0x4f,0x0f,0x78,
> + 0xb7,0x37,0xa9,0x61,0x8b,0x26,0xfa,0x7d,
> + 0xbc,0x98,0x74,0xf2,0x72,0xc4,0x2b,0xdb,
> + 0x56,0x3e,0xaf,0xa1,0x6b,0x4f,0xb6,0x8c,
> + 0x3b,0xb1,0xe7,0x8e,0xaa,0x81,0xa0,0x02,
> + 0x43,0xfa,0xad,0xd2,0xbf,0x18,0xe6,0x3d,
> + 0x38,0x9a,0xe4,0x43,0x77,0xda,0x18,0xc5,
> + 0x76,0xb5,0x0f,0x00,0x96,0xcf,0x34,0x19,
> + 0x54,0x83,0xb0,0x05,0x48,0xc0,0x98,0x62,
> + 0x36,0xe3,0xbc,0x7c,0xb8,0xd6,0x80,0x1c,
> + 0x04,0x94,0xcc,0xd1,0x99,0xe5,0xc5,0xbd,
> + 0x0d,0x0e,0xdc,0x9e,0xb8,0xa0,0x00,0x1e,
> + 0x15,0x27,0x67,0x54,0xfc,0xc6,0x85,0x66,
> + 0x05,0x41,0x48,0xe6,0xe7,0x64,0xbe,0xe7,
> + 0xc7,0x64,0xda,0xad,0x3f,0xc4,0x52,0x35,
> + 0xa6,0xda,0xd4,0x28,0xfa,0x20,0xc1,0x70,
> + 0xe3,0x45,0x00,0x3f,0x2f,0x32,0xaf,0xb5,
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +/*
> + * ffdhe6144 generator (g), modulus (p) and group size (q)
> + */
> +
> +const u8 ffdhe6144_g[] = { 0x02 };
> +
> +const u8 ffdhe6144_p[] = {
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
> + 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
> + 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
> + 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
> + 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
> + 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
> + 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
> + 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
> + 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
> + 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
> + 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
> + 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
> + 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
> + 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
> + 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
> + 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
> + 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
> + 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
> + 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
> + 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
> + 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
> + 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
> + 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
> + 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
> + 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
> + 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
> + 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
> + 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
> + 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
> + 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
> + 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
> + 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
> + 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
> + 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
> + 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
> + 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
> + 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
> + 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
> + 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
> + 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
> + 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
> + 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
> + 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
> + 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
> + 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
> + 0x25,0xe4,0x1d,0x2b,0x66,0x9e,0x1e,0xf1,
> + 0x6e,0x6f,0x52,0xc3,0x16,0x4d,0xf4,0xfb,
> + 0x79,0x30,0xe9,0xe4,0xe5,0x88,0x57,0xb6,
> + 0xac,0x7d,0x5f,0x42,0xd6,0x9f,0x6d,0x18,
> + 0x77,0x63,0xcf,0x1d,0x55,0x03,0x40,0x04,
> + 0x87,0xf5,0x5b,0xa5,0x7e,0x31,0xcc,0x7a,
> + 0x71,0x35,0xc8,0x86,0xef,0xb4,0x31,0x8a,
> + 0xed,0x6a,0x1e,0x01,0x2d,0x9e,0x68,0x32,
> + 0xa9,0x07,0x60,0x0a,0x91,0x81,0x30,0xc4,
> + 0x6d,0xc7,0x78,0xf9,0x71,0xad,0x00,0x38,
> + 0x09,0x29,0x99,0xa3,0x33,0xcb,0x8b,0x7a,
> + 0x1a,0x1d,0xb9,0x3d,0x71,0x40,0x00,0x3c,
> + 0x2a,0x4e,0xce,0xa9,0xf9,0x8d,0x0a,0xcc,
> + 0x0a,0x82,0x91,0xcd,0xce,0xc9,0x7d,0xcf,
> + 0x8e,0xc9,0xb5,0x5a,0x7f,0x88,0xa4,0x6b,
> + 0x4d,0xb5,0xa8,0x51,0xf4,0x41,0x82,0xe1,
> + 0xc6,0x8a,0x00,0x7e,0x5e,0x0d,0xd9,0x02,
> + 0x0b,0xfd,0x64,0xb6,0x45,0x03,0x6c,0x7a,
> + 0x4e,0x67,0x7d,0x2c,0x38,0x53,0x2a,0x3a,
> + 0x23,0xba,0x44,0x42,0xca,0xf5,0x3e,0xa6,
> + 0x3b,0xb4,0x54,0x32,0x9b,0x76,0x24,0xc8,
> + 0x91,0x7b,0xdd,0x64,0xb1,0xc0,0xfd,0x4c,
> + 0xb3,0x8e,0x8c,0x33,0x4c,0x70,0x1c,0x3a,
> + 0xcd,0xad,0x06,0x57,0xfc,0xcf,0xec,0x71,
> + 0x9b,0x1f,0x5c,0x3e,0x4e,0x46,0x04,0x1f,
> + 0x38,0x81,0x47,0xfb,0x4c,0xfd,0xb4,0x77,
> + 0xa5,0x24,0x71,0xf7,0xa9,0xa9,0x69,0x10,
> + 0xb8,0x55,0x32,0x2e,0xdb,0x63,0x40,0xd8,
> + 0xa0,0x0e,0xf0,0x92,0x35,0x05,0x11,0xe3,
> + 0x0a,0xbe,0xc1,0xff,0xf9,0xe3,0xa2,0x6e,
> + 0x7f,0xb2,0x9f,0x8c,0x18,0x30,0x23,0xc3,
> + 0x58,0x7e,0x38,0xda,0x00,0x77,0xd9,0xb4,
> + 0x76,0x3e,0x4e,0x4b,0x94,0xb2,0xbb,0xc1,
> + 0x94,0xc6,0x65,0x1e,0x77,0xca,0xf9,0x92,
> + 0xee,0xaa,0xc0,0x23,0x2a,0x28,0x1b,0xf6,
> + 0xb3,0xa7,0x39,0xc1,0x22,0x61,0x16,0x82,
> + 0x0a,0xe8,0xdb,0x58,0x47,0xa6,0x7c,0xbe,
> + 0xf9,0xc9,0x09,0x1b,0x46,0x2d,0x53,0x8c,
> + 0xd7,0x2b,0x03,0x74,0x6a,0xe7,0x7f,0x5e,
> + 0x62,0x29,0x2c,0x31,0x15,0x62,0xa8,0x46,
> + 0x50,0x5d,0xc8,0x2d,0xb8,0x54,0x33,0x8a,
> + 0xe4,0x9f,0x52,0x35,0xc9,0x5b,0x91,0x17,
> + 0x8c,0xcf,0x2d,0xd5,0xca,0xce,0xf4,0x03,
> + 0xec,0x9d,0x18,0x10,0xc6,0x27,0x2b,0x04,
> + 0x5b,0x3b,0x71,0xf9,0xdc,0x6b,0x80,0xd6,
> + 0x3f,0xdd,0x4a,0x8e,0x9a,0xdb,0x1e,0x69,
> + 0x62,0xa6,0x95,0x26,0xd4,0x31,0x61,0xc1,
> + 0xa4,0x1d,0x57,0x0d,0x79,0x38,0xda,0xd4,
> + 0xa4,0x0e,0x32,0x9c,0xd0,0xe4,0x0e,0x65,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +const u8 ffdhe6144_q[] = {
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
> + 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
> + 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
> + 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
> + 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
> + 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
> + 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
> + 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
> + 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
> + 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
> + 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
> + 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
> + 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
> + 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
> + 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
> + 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
> + 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
> + 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
> + 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
> + 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
> + 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
> + 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
> + 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
> + 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
> + 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
> + 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
> + 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
> + 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
> + 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
> + 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
> + 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
> + 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
> + 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
> + 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
> + 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
> + 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
> + 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
> + 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
> + 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
> + 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
> + 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
> + 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
> + 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
> + 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
> + 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
> + 0x12,0xf2,0x0e,0x95,0xb3,0x4f,0x0f,0x78,
> + 0xb7,0x37,0xa9,0x61,0x8b,0x26,0xfa,0x7d,
> + 0xbc,0x98,0x74,0xf2,0x72,0xc4,0x2b,0xdb,
> + 0x56,0x3e,0xaf,0xa1,0x6b,0x4f,0xb6,0x8c,
> + 0x3b,0xb1,0xe7,0x8e,0xaa,0x81,0xa0,0x02,
> + 0x43,0xfa,0xad,0xd2,0xbf,0x18,0xe6,0x3d,
> + 0x38,0x9a,0xe4,0x43,0x77,0xda,0x18,0xc5,
> + 0x76,0xb5,0x0f,0x00,0x96,0xcf,0x34,0x19,
> + 0x54,0x83,0xb0,0x05,0x48,0xc0,0x98,0x62,
> + 0x36,0xe3,0xbc,0x7c,0xb8,0xd6,0x80,0x1c,
> + 0x04,0x94,0xcc,0xd1,0x99,0xe5,0xc5,0xbd,
> + 0x0d,0x0e,0xdc,0x9e,0xb8,0xa0,0x00,0x1e,
> + 0x15,0x27,0x67,0x54,0xfc,0xc6,0x85,0x66,
> + 0x05,0x41,0x48,0xe6,0xe7,0x64,0xbe,0xe7,
> + 0xc7,0x64,0xda,0xad,0x3f,0xc4,0x52,0x35,
> + 0xa6,0xda,0xd4,0x28,0xfa,0x20,0xc1,0x70,
> + 0xe3,0x45,0x00,0x3f,0x2f,0x06,0xec,0x81,
> + 0x05,0xfe,0xb2,0x5b,0x22,0x81,0xb6,0x3d,
> + 0x27,0x33,0xbe,0x96,0x1c,0x29,0x95,0x1d,
> + 0x11,0xdd,0x22,0x21,0x65,0x7a,0x9f,0x53,
> + 0x1d,0xda,0x2a,0x19,0x4d,0xbb,0x12,0x64,
> + 0x48,0xbd,0xee,0xb2,0x58,0xe0,0x7e,0xa6,
> + 0x59,0xc7,0x46,0x19,0xa6,0x38,0x0e,0x1d,
> + 0x66,0xd6,0x83,0x2b,0xfe,0x67,0xf6,0x38,
> + 0xcd,0x8f,0xae,0x1f,0x27,0x23,0x02,0x0f,
> + 0x9c,0x40,0xa3,0xfd,0xa6,0x7e,0xda,0x3b,
> + 0xd2,0x92,0x38,0xfb,0xd4,0xd4,0xb4,0x88,
> + 0x5c,0x2a,0x99,0x17,0x6d,0xb1,0xa0,0x6c,
> + 0x50,0x07,0x78,0x49,0x1a,0x82,0x88,0xf1,
> + 0x85,0x5f,0x60,0xff,0xfc,0xf1,0xd1,0x37,
> + 0x3f,0xd9,0x4f,0xc6,0x0c,0x18,0x11,0xe1,
> + 0xac,0x3f,0x1c,0x6d,0x00,0x3b,0xec,0xda,
> + 0x3b,0x1f,0x27,0x25,0xca,0x59,0x5d,0xe0,
> + 0xca,0x63,0x32,0x8f,0x3b,0xe5,0x7c,0xc9,
> + 0x77,0x55,0x60,0x11,0x95,0x14,0x0d,0xfb,
> + 0x59,0xd3,0x9c,0xe0,0x91,0x30,0x8b,0x41,
> + 0x05,0x74,0x6d,0xac,0x23,0xd3,0x3e,0x5f,
> + 0x7c,0xe4,0x84,0x8d,0xa3,0x16,0xa9,0xc6,
> + 0x6b,0x95,0x81,0xba,0x35,0x73,0xbf,0xaf,
> + 0x31,0x14,0x96,0x18,0x8a,0xb1,0x54,0x23,
> + 0x28,0x2e,0xe4,0x16,0xdc,0x2a,0x19,0xc5,
> + 0x72,0x4f,0xa9,0x1a,0xe4,0xad,0xc8,0x8b,
> + 0xc6,0x67,0x96,0xea,0xe5,0x67,0x7a,0x01,
> + 0xf6,0x4e,0x8c,0x08,0x63,0x13,0x95,0x82,
> + 0x2d,0x9d,0xb8,0xfc,0xee,0x35,0xc0,0x6b,
> + 0x1f,0xee,0xa5,0x47,0x4d,0x6d,0x8f,0x34,
> + 0xb1,0x53,0x4a,0x93,0x6a,0x18,0xb0,0xe0,
> + 0xd2,0x0e,0xab,0x86,0xbc,0x9c,0x6d,0x6a,
> + 0x52,0x07,0x19,0x4e,0x68,0x72,0x07,0x32,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +/*
> + * ffdhe8192 generator (g), modulus (p) and group size (q)
> + */
> +
> +const u8 ffdhe8192_g[] = { 0x02 };
> +
> +const u8 ffdhe8192_p[] = {
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xad,0xf8,0x54,0x58,0xa2,0xbb,0x4a,0x9a,
> + 0xaf,0xdc,0x56,0x20,0x27,0x3d,0x3c,0xf1,
> + 0xd8,0xb9,0xc5,0x83,0xce,0x2d,0x36,0x95,
> + 0xa9,0xe1,0x36,0x41,0x14,0x64,0x33,0xfb,
> + 0xcc,0x93,0x9d,0xce,0x24,0x9b,0x3e,0xf9,
> + 0x7d,0x2f,0xe3,0x63,0x63,0x0c,0x75,0xd8,
> + 0xf6,0x81,0xb2,0x02,0xae,0xc4,0x61,0x7a,
> + 0xd3,0xdf,0x1e,0xd5,0xd5,0xfd,0x65,0x61,
> + 0x24,0x33,0xf5,0x1f,0x5f,0x06,0x6e,0xd0,
> + 0x85,0x63,0x65,0x55,0x3d,0xed,0x1a,0xf3,
> + 0xb5,0x57,0x13,0x5e,0x7f,0x57,0xc9,0x35,
> + 0x98,0x4f,0x0c,0x70,0xe0,0xe6,0x8b,0x77,
> + 0xe2,0xa6,0x89,0xda,0xf3,0xef,0xe8,0x72,
> + 0x1d,0xf1,0x58,0xa1,0x36,0xad,0xe7,0x35,
> + 0x30,0xac,0xca,0x4f,0x48,0x3a,0x79,0x7a,
> + 0xbc,0x0a,0xb1,0x82,0xb3,0x24,0xfb,0x61,
> + 0xd1,0x08,0xa9,0x4b,0xb2,0xc8,0xe3,0xfb,
> + 0xb9,0x6a,0xda,0xb7,0x60,0xd7,0xf4,0x68,
> + 0x1d,0x4f,0x42,0xa3,0xde,0x39,0x4d,0xf4,
> + 0xae,0x56,0xed,0xe7,0x63,0x72,0xbb,0x19,
> + 0x0b,0x07,0xa7,0xc8,0xee,0x0a,0x6d,0x70,
> + 0x9e,0x02,0xfc,0xe1,0xcd,0xf7,0xe2,0xec,
> + 0xc0,0x34,0x04,0xcd,0x28,0x34,0x2f,0x61,
> + 0x91,0x72,0xfe,0x9c,0xe9,0x85,0x83,0xff,
> + 0x8e,0x4f,0x12,0x32,0xee,0xf2,0x81,0x83,
> + 0xc3,0xfe,0x3b,0x1b,0x4c,0x6f,0xad,0x73,
> + 0x3b,0xb5,0xfc,0xbc,0x2e,0xc2,0x20,0x05,
> + 0xc5,0x8e,0xf1,0x83,0x7d,0x16,0x83,0xb2,
> + 0xc6,0xf3,0x4a,0x26,0xc1,0xb2,0xef,0xfa,
> + 0x88,0x6b,0x42,0x38,0x61,0x1f,0xcf,0xdc,
> + 0xde,0x35,0x5b,0x3b,0x65,0x19,0x03,0x5b,
> + 0xbc,0x34,0xf4,0xde,0xf9,0x9c,0x02,0x38,
> + 0x61,0xb4,0x6f,0xc9,0xd6,0xe6,0xc9,0x07,
> + 0x7a,0xd9,0x1d,0x26,0x91,0xf7,0xf7,0xee,
> + 0x59,0x8c,0xb0,0xfa,0xc1,0x86,0xd9,0x1c,
> + 0xae,0xfe,0x13,0x09,0x85,0x13,0x92,0x70,
> + 0xb4,0x13,0x0c,0x93,0xbc,0x43,0x79,0x44,
> + 0xf4,0xfd,0x44,0x52,0xe2,0xd7,0x4d,0xd3,
> + 0x64,0xf2,0xe2,0x1e,0x71,0xf5,0x4b,0xff,
> + 0x5c,0xae,0x82,0xab,0x9c,0x9d,0xf6,0x9e,
> + 0xe8,0x6d,0x2b,0xc5,0x22,0x36,0x3a,0x0d,
> + 0xab,0xc5,0x21,0x97,0x9b,0x0d,0xea,0xda,
> + 0x1d,0xbf,0x9a,0x42,0xd5,0xc4,0x48,0x4e,
> + 0x0a,0xbc,0xd0,0x6b,0xfa,0x53,0xdd,0xef,
> + 0x3c,0x1b,0x20,0xee,0x3f,0xd5,0x9d,0x7c,
> + 0x25,0xe4,0x1d,0x2b,0x66,0x9e,0x1e,0xf1,
> + 0x6e,0x6f,0x52,0xc3,0x16,0x4d,0xf4,0xfb,
> + 0x79,0x30,0xe9,0xe4,0xe5,0x88,0x57,0xb6,
> + 0xac,0x7d,0x5f,0x42,0xd6,0x9f,0x6d,0x18,
> + 0x77,0x63,0xcf,0x1d,0x55,0x03,0x40,0x04,
> + 0x87,0xf5,0x5b,0xa5,0x7e,0x31,0xcc,0x7a,
> + 0x71,0x35,0xc8,0x86,0xef,0xb4,0x31,0x8a,
> + 0xed,0x6a,0x1e,0x01,0x2d,0x9e,0x68,0x32,
> + 0xa9,0x07,0x60,0x0a,0x91,0x81,0x30,0xc4,
> + 0x6d,0xc7,0x78,0xf9,0x71,0xad,0x00,0x38,
> + 0x09,0x29,0x99,0xa3,0x33,0xcb,0x8b,0x7a,
> + 0x1a,0x1d,0xb9,0x3d,0x71,0x40,0x00,0x3c,
> + 0x2a,0x4e,0xce,0xa9,0xf9,0x8d,0x0a,0xcc,
> + 0x0a,0x82,0x91,0xcd,0xce,0xc9,0x7d,0xcf,
> + 0x8e,0xc9,0xb5,0x5a,0x7f,0x88,0xa4,0x6b,
> + 0x4d,0xb5,0xa8,0x51,0xf4,0x41,0x82,0xe1,
> + 0xc6,0x8a,0x00,0x7e,0x5e,0x0d,0xd9,0x02,
> + 0x0b,0xfd,0x64,0xb6,0x45,0x03,0x6c,0x7a,
> + 0x4e,0x67,0x7d,0x2c,0x38,0x53,0x2a,0x3a,
> + 0x23,0xba,0x44,0x42,0xca,0xf5,0x3e,0xa6,
> + 0x3b,0xb4,0x54,0x32,0x9b,0x76,0x24,0xc8,
> + 0x91,0x7b,0xdd,0x64,0xb1,0xc0,0xfd,0x4c,
> + 0xb3,0x8e,0x8c,0x33,0x4c,0x70,0x1c,0x3a,
> + 0xcd,0xad,0x06,0x57,0xfc,0xcf,0xec,0x71,
> + 0x9b,0x1f,0x5c,0x3e,0x4e,0x46,0x04,0x1f,
> + 0x38,0x81,0x47,0xfb,0x4c,0xfd,0xb4,0x77,
> + 0xa5,0x24,0x71,0xf7,0xa9,0xa9,0x69,0x10,
> + 0xb8,0x55,0x32,0x2e,0xdb,0x63,0x40,0xd8,
> + 0xa0,0x0e,0xf0,0x92,0x35,0x05,0x11,0xe3,
> + 0x0a,0xbe,0xc1,0xff,0xf9,0xe3,0xa2,0x6e,
> + 0x7f,0xb2,0x9f,0x8c,0x18,0x30,0x23,0xc3,
> + 0x58,0x7e,0x38,0xda,0x00,0x77,0xd9,0xb4,
> + 0x76,0x3e,0x4e,0x4b,0x94,0xb2,0xbb,0xc1,
> + 0x94,0xc6,0x65,0x1e,0x77,0xca,0xf9,0x92,
> + 0xee,0xaa,0xc0,0x23,0x2a,0x28,0x1b,0xf6,
> + 0xb3,0xa7,0x39,0xc1,0x22,0x61,0x16,0x82,
> + 0x0a,0xe8,0xdb,0x58,0x47,0xa6,0x7c,0xbe,
> + 0xf9,0xc9,0x09,0x1b,0x46,0x2d,0x53,0x8c,
> + 0xd7,0x2b,0x03,0x74,0x6a,0xe7,0x7f,0x5e,
> + 0x62,0x29,0x2c,0x31,0x15,0x62,0xa8,0x46,
> + 0x50,0x5d,0xc8,0x2d,0xb8,0x54,0x33,0x8a,
> + 0xe4,0x9f,0x52,0x35,0xc9,0x5b,0x91,0x17,
> + 0x8c,0xcf,0x2d,0xd5,0xca,0xce,0xf4,0x03,
> + 0xec,0x9d,0x18,0x10,0xc6,0x27,0x2b,0x04,
> + 0x5b,0x3b,0x71,0xf9,0xdc,0x6b,0x80,0xd6,
> + 0x3f,0xdd,0x4a,0x8e,0x9a,0xdb,0x1e,0x69,
> + 0x62,0xa6,0x95,0x26,0xd4,0x31,0x61,0xc1,
> + 0xa4,0x1d,0x57,0x0d,0x79,0x38,0xda,0xd4,
> + 0xa4,0x0e,0x32,0x9c,0xcf,0xf4,0x6a,0xaa,
> + 0x36,0xad,0x00,0x4c,0xf6,0x00,0xc8,0x38,
> + 0x1e,0x42,0x5a,0x31,0xd9,0x51,0xae,0x64,
> + 0xfd,0xb2,0x3f,0xce,0xc9,0x50,0x9d,0x43,
> + 0x68,0x7f,0xeb,0x69,0xed,0xd1,0xcc,0x5e,
> + 0x0b,0x8c,0xc3,0xbd,0xf6,0x4b,0x10,0xef,
> + 0x86,0xb6,0x31,0x42,0xa3,0xab,0x88,0x29,
> + 0x55,0x5b,0x2f,0x74,0x7c,0x93,0x26,0x65,
> + 0xcb,0x2c,0x0f,0x1c,0xc0,0x1b,0xd7,0x02,
> + 0x29,0x38,0x88,0x39,0xd2,0xaf,0x05,0xe4,
> + 0x54,0x50,0x4a,0xc7,0x8b,0x75,0x82,0x82,
> + 0x28,0x46,0xc0,0xba,0x35,0xc3,0x5f,0x5c,
> + 0x59,0x16,0x0c,0xc0,0x46,0xfd,0x82,0x51,
> + 0x54,0x1f,0xc6,0x8c,0x9c,0x86,0xb0,0x22,
> + 0xbb,0x70,0x99,0x87,0x6a,0x46,0x0e,0x74,
> + 0x51,0xa8,0xa9,0x31,0x09,0x70,0x3f,0xee,
> + 0x1c,0x21,0x7e,0x6c,0x38,0x26,0xe5,0x2c,
> + 0x51,0xaa,0x69,0x1e,0x0e,0x42,0x3c,0xfc,
> + 0x99,0xe9,0xe3,0x16,0x50,0xc1,0x21,0x7b,
> + 0x62,0x48,0x16,0xcd,0xad,0x9a,0x95,0xf9,
> + 0xd5,0xb8,0x01,0x94,0x88,0xd9,0xc0,0xa0,
> + 0xa1,0xfe,0x30,0x75,0xa5,0x77,0xe2,0x31,
> + 0x83,0xf8,0x1d,0x4a,0x3f,0x2f,0xa4,0x57,
> + 0x1e,0xfc,0x8c,0xe0,0xba,0x8a,0x4f,0xe8,
> + 0xb6,0x85,0x5d,0xfe,0x72,0xb0,0xa6,0x6e,
> + 0xde,0xd2,0xfb,0xab,0xfb,0xe5,0x8a,0x30,
> + 0xfa,0xfa,0xbe,0x1c,0x5d,0x71,0xa8,0x7e,
> + 0x2f,0x74,0x1e,0xf8,0xc1,0xfe,0x86,0xfe,
> + 0xa6,0xbb,0xfd,0xe5,0x30,0x67,0x7f,0x0d,
> + 0x97,0xd1,0x1d,0x49,0xf7,0xa8,0x44,0x3d,
> + 0x08,0x22,0xe5,0x06,0xa9,0xf4,0x61,0x4e,
> + 0x01,0x1e,0x2a,0x94,0x83,0x8f,0xf8,0x8c,
> + 0xd6,0x8c,0x8b,0xb7,0xc5,0xc6,0x42,0x4c,
> + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +const u8 ffdhe8192_q[] = {
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> + 0xd6,0xfc,0x2a,0x2c,0x51,0x5d,0xa5,0x4d,
> + 0x57,0xee,0x2b,0x10,0x13,0x9e,0x9e,0x78,
> + 0xec,0x5c,0xe2,0xc1,0xe7,0x16,0x9b,0x4a,
> + 0xd4,0xf0,0x9b,0x20,0x8a,0x32,0x19,0xfd,
> + 0xe6,0x49,0xce,0xe7,0x12,0x4d,0x9f,0x7c,
> + 0xbe,0x97,0xf1,0xb1,0xb1,0x86,0x3a,0xec,
> + 0x7b,0x40,0xd9,0x01,0x57,0x62,0x30,0xbd,
> + 0x69,0xef,0x8f,0x6a,0xea,0xfe,0xb2,0xb0,
> + 0x92,0x19,0xfa,0x8f,0xaf,0x83,0x37,0x68,
> + 0x42,0xb1,0xb2,0xaa,0x9e,0xf6,0x8d,0x79,
> + 0xda,0xab,0x89,0xaf,0x3f,0xab,0xe4,0x9a,
> + 0xcc,0x27,0x86,0x38,0x70,0x73,0x45,0xbb,
> + 0xf1,0x53,0x44,0xed,0x79,0xf7,0xf4,0x39,
> + 0x0e,0xf8,0xac,0x50,0x9b,0x56,0xf3,0x9a,
> + 0x98,0x56,0x65,0x27,0xa4,0x1d,0x3c,0xbd,
> + 0x5e,0x05,0x58,0xc1,0x59,0x92,0x7d,0xb0,
> + 0xe8,0x84,0x54,0xa5,0xd9,0x64,0x71,0xfd,
> + 0xdc,0xb5,0x6d,0x5b,0xb0,0x6b,0xfa,0x34,
> + 0x0e,0xa7,0xa1,0x51,0xef,0x1c,0xa6,0xfa,
> + 0x57,0x2b,0x76,0xf3,0xb1,0xb9,0x5d,0x8c,
> + 0x85,0x83,0xd3,0xe4,0x77,0x05,0x36,0xb8,
> + 0x4f,0x01,0x7e,0x70,0xe6,0xfb,0xf1,0x76,
> + 0x60,0x1a,0x02,0x66,0x94,0x1a,0x17,0xb0,
> + 0xc8,0xb9,0x7f,0x4e,0x74,0xc2,0xc1,0xff,
> + 0xc7,0x27,0x89,0x19,0x77,0x79,0x40,0xc1,
> + 0xe1,0xff,0x1d,0x8d,0xa6,0x37,0xd6,0xb9,
> + 0x9d,0xda,0xfe,0x5e,0x17,0x61,0x10,0x02,
> + 0xe2,0xc7,0x78,0xc1,0xbe,0x8b,0x41,0xd9,
> + 0x63,0x79,0xa5,0x13,0x60,0xd9,0x77,0xfd,
> + 0x44,0x35,0xa1,0x1c,0x30,0x8f,0xe7,0xee,
> + 0x6f,0x1a,0xad,0x9d,0xb2,0x8c,0x81,0xad,
> + 0xde,0x1a,0x7a,0x6f,0x7c,0xce,0x01,0x1c,
> + 0x30,0xda,0x37,0xe4,0xeb,0x73,0x64,0x83,
> + 0xbd,0x6c,0x8e,0x93,0x48,0xfb,0xfb,0xf7,
> + 0x2c,0xc6,0x58,0x7d,0x60,0xc3,0x6c,0x8e,
> + 0x57,0x7f,0x09,0x84,0xc2,0x89,0xc9,0x38,
> + 0x5a,0x09,0x86,0x49,0xde,0x21,0xbc,0xa2,
> + 0x7a,0x7e,0xa2,0x29,0x71,0x6b,0xa6,0xe9,
> + 0xb2,0x79,0x71,0x0f,0x38,0xfa,0xa5,0xff,
> + 0xae,0x57,0x41,0x55,0xce,0x4e,0xfb,0x4f,
> + 0x74,0x36,0x95,0xe2,0x91,0x1b,0x1d,0x06,
> + 0xd5,0xe2,0x90,0xcb,0xcd,0x86,0xf5,0x6d,
> + 0x0e,0xdf,0xcd,0x21,0x6a,0xe2,0x24,0x27,
> + 0x05,0x5e,0x68,0x35,0xfd,0x29,0xee,0xf7,
> + 0x9e,0x0d,0x90,0x77,0x1f,0xea,0xce,0xbe,
> + 0x12,0xf2,0x0e,0x95,0xb3,0x4f,0x0f,0x78,
> + 0xb7,0x37,0xa9,0x61,0x8b,0x26,0xfa,0x7d,
> + 0xbc,0x98,0x74,0xf2,0x72,0xc4,0x2b,0xdb,
> + 0x56,0x3e,0xaf,0xa1,0x6b,0x4f,0xb6,0x8c,
> + 0x3b,0xb1,0xe7,0x8e,0xaa,0x81,0xa0,0x02,
> + 0x43,0xfa,0xad,0xd2,0xbf,0x18,0xe6,0x3d,
> + 0x38,0x9a,0xe4,0x43,0x77,0xda,0x18,0xc5,
> + 0x76,0xb5,0x0f,0x00,0x96,0xcf,0x34,0x19,
> + 0x54,0x83,0xb0,0x05,0x48,0xc0,0x98,0x62,
> + 0x36,0xe3,0xbc,0x7c,0xb8,0xd6,0x80,0x1c,
> + 0x04,0x94,0xcc,0xd1,0x99,0xe5,0xc5,0xbd,
> + 0x0d,0x0e,0xdc,0x9e,0xb8,0xa0,0x00,0x1e,
> + 0x15,0x27,0x67,0x54,0xfc,0xc6,0x85,0x66,
> + 0x05,0x41,0x48,0xe6,0xe7,0x64,0xbe,0xe7,
> + 0xc7,0x64,0xda,0xad,0x3f,0xc4,0x52,0x35,
> + 0xa6,0xda,0xd4,0x28,0xfa,0x20,0xc1,0x70,
> + 0xe3,0x45,0x00,0x3f,0x2f,0x06,0xec,0x81,
> + 0x05,0xfe,0xb2,0x5b,0x22,0x81,0xb6,0x3d,
> + 0x27,0x33,0xbe,0x96,0x1c,0x29,0x95,0x1d,
> + 0x11,0xdd,0x22,0x21,0x65,0x7a,0x9f,0x53,
> + 0x1d,0xda,0x2a,0x19,0x4d,0xbb,0x12,0x64,
> + 0x48,0xbd,0xee,0xb2,0x58,0xe0,0x7e,0xa6,
> + 0x59,0xc7,0x46,0x19,0xa6,0x38,0x0e,0x1d,
> + 0x66,0xd6,0x83,0x2b,0xfe,0x67,0xf6,0x38,
> + 0xcd,0x8f,0xae,0x1f,0x27,0x23,0x02,0x0f,
> + 0x9c,0x40,0xa3,0xfd,0xa6,0x7e,0xda,0x3b,
> + 0xd2,0x92,0x38,0xfb,0xd4,0xd4,0xb4,0x88,
> + 0x5c,0x2a,0x99,0x17,0x6d,0xb1,0xa0,0x6c,
> + 0x50,0x07,0x78,0x49,0x1a,0x82,0x88,0xf1,
> + 0x85,0x5f,0x60,0xff,0xfc,0xf1,0xd1,0x37,
> + 0x3f,0xd9,0x4f,0xc6,0x0c,0x18,0x11,0xe1,
> + 0xac,0x3f,0x1c,0x6d,0x00,0x3b,0xec,0xda,
> + 0x3b,0x1f,0x27,0x25,0xca,0x59,0x5d,0xe0,
> + 0xca,0x63,0x32,0x8f,0x3b,0xe5,0x7c,0xc9,
> + 0x77,0x55,0x60,0x11,0x95,0x14,0x0d,0xfb,
> + 0x59,0xd3,0x9c,0xe0,0x91,0x30,0x8b,0x41,
> + 0x05,0x74,0x6d,0xac,0x23,0xd3,0x3e,0x5f,
> + 0x7c,0xe4,0x84,0x8d,0xa3,0x16,0xa9,0xc6,
> + 0x6b,0x95,0x81,0xba,0x35,0x73,0xbf,0xaf,
> + 0x31,0x14,0x96,0x18,0x8a,0xb1,0x54,0x23,
> + 0x28,0x2e,0xe4,0x16,0xdc,0x2a,0x19,0xc5,
> + 0x72,0x4f,0xa9,0x1a,0xe4,0xad,0xc8,0x8b,
> + 0xc6,0x67,0x96,0xea,0xe5,0x67,0x7a,0x01,
> + 0xf6,0x4e,0x8c,0x08,0x63,0x13,0x95,0x82,
> + 0x2d,0x9d,0xb8,0xfc,0xee,0x35,0xc0,0x6b,
> + 0x1f,0xee,0xa5,0x47,0x4d,0x6d,0x8f,0x34,
> + 0xb1,0x53,0x4a,0x93,0x6a,0x18,0xb0,0xe0,
> + 0xd2,0x0e,0xab,0x86,0xbc,0x9c,0x6d,0x6a,
> + 0x52,0x07,0x19,0x4e,0x67,0xfa,0x35,0x55,
> + 0x1b,0x56,0x80,0x26,0x7b,0x00,0x64,0x1c,
> + 0x0f,0x21,0x2d,0x18,0xec,0xa8,0xd7,0x32,
> + 0x7e,0xd9,0x1f,0xe7,0x64,0xa8,0x4e,0xa1,
> + 0xb4,0x3f,0xf5,0xb4,0xf6,0xe8,0xe6,0x2f,
> + 0x05,0xc6,0x61,0xde,0xfb,0x25,0x88,0x77,
> + 0xc3,0x5b,0x18,0xa1,0x51,0xd5,0xc4,0x14,
> + 0xaa,0xad,0x97,0xba,0x3e,0x49,0x93,0x32,
> + 0xe5,0x96,0x07,0x8e,0x60,0x0d,0xeb,0x81,
> + 0x14,0x9c,0x44,0x1c,0xe9,0x57,0x82,0xf2,
> + 0x2a,0x28,0x25,0x63,0xc5,0xba,0xc1,0x41,
> + 0x14,0x23,0x60,0x5d,0x1a,0xe1,0xaf,0xae,
> + 0x2c,0x8b,0x06,0x60,0x23,0x7e,0xc1,0x28,
> + 0xaa,0x0f,0xe3,0x46,0x4e,0x43,0x58,0x11,
> + 0x5d,0xb8,0x4c,0xc3,0xb5,0x23,0x07,0x3a,
> + 0x28,0xd4,0x54,0x98,0x84,0xb8,0x1f,0xf7,
> + 0x0e,0x10,0xbf,0x36,0x1c,0x13,0x72,0x96,
> + 0x28,0xd5,0x34,0x8f,0x07,0x21,0x1e,0x7e,
> + 0x4c,0xf4,0xf1,0x8b,0x28,0x60,0x90,0xbd,
> + 0xb1,0x24,0x0b,0x66,0xd6,0xcd,0x4a,0xfc,
> + 0xea,0xdc,0x00,0xca,0x44,0x6c,0xe0,0x50,
> + 0x50,0xff,0x18,0x3a,0xd2,0xbb,0xf1,0x18,
> + 0xc1,0xfc,0x0e,0xa5,0x1f,0x97,0xd2,0x2b,
> + 0x8f,0x7e,0x46,0x70,0x5d,0x45,0x27,0xf4,
> + 0x5b,0x42,0xae,0xff,0x39,0x58,0x53,0x37,
> + 0x6f,0x69,0x7d,0xd5,0xfd,0xf2,0xc5,0x18,
> + 0x7d,0x7d,0x5f,0x0e,0x2e,0xb8,0xd4,0x3f,
> + 0x17,0xba,0x0f,0x7c,0x60,0xff,0x43,0x7f,
> + 0x53,0x5d,0xfe,0xf2,0x98,0x33,0xbf,0x86,
> + 0xcb,0xe8,0x8e,0xa4,0xfb,0xd4,0x22,0x1e,
> + 0x84,0x11,0x72,0x83,0x54,0xfa,0x30,0xa7,
> + 0x00,0x8f,0x15,0x4a,0x41,0xc7,0xfc,0x46,
> + 0x6b,0x46,0x45,0xdb,0xe2,0xe3,0x21,0x26,
> + 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
> +};
> +
> +struct ffdhe_group {
> + int bits;
> + int minsize;
> + const u8 *p;
> + const u8 *q;
> + const u8 *g;
> +} ffdhe_group_map[] = {
> + {
> + .bits = 2048,
> + .minsize = 225,
> + .p = ffdhe2048_p,
> + .q = ffdhe2048_q,
> + .g = ffdhe2048_g,
> + },
> + {
> + .bits = 3072,
> + .minsize = 275,
> + .p = ffdhe3072_p,
> + .q = ffdhe3072_q,
> + .g = ffdhe3072_g,
> + },
> + {
> + .bits = 4096,
> + .minsize = 325,
> + .p = ffdhe4096_p,
> + .q = ffdhe4096_q,
> + .g = ffdhe4096_g,
> + },
> + {
> + .bits = 6144,
> + .minsize = 375,
> + .p = ffdhe6144_p,
> + .q = ffdhe6144_q,
> + .g = ffdhe6144_g,
> + },
> + {
> + .bits = 8192,
> + .minsize = 400,
> + .p = ffdhe8192_p,
> + .q = ffdhe8192_q,
> + .g = ffdhe8192_g,
> + },
> +};
> +
> +int crypto_ffdhe_params(struct dh *p, int bits)
> +{
> + struct ffdhe_group *grp = NULL;
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(ffdhe_group_map); i++) {
> + if (ffdhe_group_map[i].bits == bits) {
> + grp = &ffdhe_group_map[i];
> + break;
> + }
> + }
> + if (!grp || !p)
> + return -EINVAL;
> +
> + p->p_size = grp->bits / 8;
> + p->p = (u8 *)grp->p;
> + p->g_size = 1;
> + p->g = (u8 *)grp->g;
> + p->q_size = grp->bits / 8;
> + p->q = (u8 *)grp->q;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(crypto_ffdhe_params);
> diff --git a/include/crypto/ffdhe.h b/include/crypto/ffdhe.h
> new file mode 100644
> index 000000000000..6cb9253ddb34
> --- /dev/null
> +++ b/include/crypto/ffdhe.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Finite-Field Diffie-Hellman definition according to RFC 7919
> + *
> + * Copyright (c) 2021, SUSE Software Products
> + * Authors: Hannes Reinecke <[email protected]>
> + */
> +#ifndef _CRYPTO_FFDHE_
> +#define _CRYPTO_FFDHE_
> +
> +/**
> + * crypto_ffdhe_params() - Generate FFDHE params
> + * @params: DH params
> + * @bits: Bitsize of the FFDHE parameters
> + *
> + * This functions sets the FFDHE parameter for @bits in @params.
> + * Valid bit sizes are 2048, 3072, 4096, 6144, or 8194.
> + *
> + * Returns: 0 on success, errno on failure.
> + */
> +
> +int crypto_ffdhe_params(struct dh *p, int bits);
> +
> +#endif /* _CRYPTO_FFDHE_H */
>

2021-07-17 06:16:53

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding


> Add RFC4648-compliant base64 encoding and decoding routines.

Looks good to me (although didn't look in the logic itself).
Can you maybe mention where was this taken from?

2021-07-17 06:30:53

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication



On 7/16/21 4:04 AM, Hannes Reinecke wrote:
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 184 insertions(+), 1 deletion(-)
>
> diff --git a/include/linux/nvme.h b/include/linux/nvme.h
> index b7c4c4130b65..7b94abacfd08 100644
> --- a/include/linux/nvme.h
> +++ b/include/linux/nvme.h
> @@ -19,6 +19,7 @@
> #define NVMF_TRSVCID_SIZE 32
> #define NVMF_TRADDR_SIZE 256
> #define NVMF_TSAS_SIZE 256
> +#define NVMF_AUTH_HASH_LEN 64
>
> #define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery"
>
> @@ -1263,6 +1264,8 @@ enum nvmf_capsule_command {
> nvme_fabrics_type_property_set = 0x00,
> nvme_fabrics_type_connect = 0x01,
> nvme_fabrics_type_property_get = 0x04,
> + nvme_fabrics_type_auth_send = 0x05,
> + nvme_fabrics_type_auth_receive = 0x06,
> };
>
> #define nvme_fabrics_type_name(type) { type, #type }
> @@ -1270,7 +1273,9 @@ enum nvmf_capsule_command {
> __print_symbolic(type, \
> nvme_fabrics_type_name(nvme_fabrics_type_property_set), \
> nvme_fabrics_type_name(nvme_fabrics_type_connect), \
> - nvme_fabrics_type_name(nvme_fabrics_type_property_get))
> + nvme_fabrics_type_name(nvme_fabrics_type_property_get), \
> + nvme_fabrics_type_name(nvme_fabrics_type_auth_send), \
> + nvme_fabrics_type_name(nvme_fabrics_type_auth_receive))
>
> /*
> * If not fabrics command, fctype will be ignored.
> @@ -1393,6 +1398,182 @@ struct nvmf_property_get_command {
> __u8 resv4[16];
> };
>
> +struct nvmf_auth_send_command {
> + __u8 opcode;
> + __u8 resv1;
> + __u16 command_id;
> + __u8 fctype;
> + __u8 resv2[19];
> + union nvme_data_ptr dptr;
> + __u8 resv3;
> + __u8 spsp0;
> + __u8 spsp1;
> + __u8 secp;
> + __le32 tl;
> + __u8 resv4[12];

Isn't that 16 bytes?
You should add these to the compile time checkers
in _nvme_check_size.

> +
> +};
> +
> +struct nvmf_auth_receive_command {
> + __u8 opcode;
> + __u8 resv1;
> + __u16 command_id;
> + __u8 fctype;
> + __u8 resv2[19];
> + union nvme_data_ptr dptr;
> + __u8 resv3;
> + __u8 spsp0;
> + __u8 spsp1;
> + __u8 secp;
> + __le32 al;
> + __u8 resv4[12];
> +};
> +
> +/* Value for secp */
> +enum {
> + NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER = 0xe9,
> +};
> +
> +/* Defined value for auth_type */
> +enum {
> + NVME_AUTH_COMMON_MESSAGES = 0x00,
> + NVME_AUTH_DHCHAP_MESSAGES = 0x01,
> +};
> +
> +/* Defined messages for auth_id */
> +enum {
> + NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE = 0x00,
> + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE = 0x01,
> + NVME_AUTH_DHCHAP_MESSAGE_REPLY = 0x02,
> + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 = 0x03,
> + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 = 0x04,
> + NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 = 0xf0,
> + NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 = 0xf1,
> +};
> +
> +struct nvmf_auth_dhchap_protocol_descriptor {
> + __u8 authid;
> + __u8 rsvd;
> + __u8 halen;
> + __u8 dhlen;
> + __u8 idlist[60];
> +};
> +
> +enum {
> + NVME_AUTH_DHCHAP_AUTH_ID = 0x01,
> +};
> +
> +enum {
> + NVME_AUTH_DHCHAP_HASH_SHA256 = 0x01,

Maybe s/HASH/HF/ (stands for hash function, which is
a better description).

> + NVME_AUTH_DHCHAP_HASH_SHA384 = 0x02,
> + NVME_AUTH_DHCHAP_HASH_SHA512 = 0x03,
> +};
> +
> +enum {
> + NVME_AUTH_DHCHAP_DHGROUP_NULL = 0x00,
> + NVME_AUTH_DHCHAP_DHGROUP_2048 = 0x01,
> + NVME_AUTH_DHCHAP_DHGROUP_3072 = 0x02,
> + NVME_AUTH_DHCHAP_DHGROUP_4096 = 0x03,
> + NVME_AUTH_DHCHAP_DHGROUP_6144 = 0x04,
> + NVME_AUTH_DHCHAP_DHGROUP_8192 = 0x05,
> +};
> +
> +union nvmf_auth_protocol {
> + struct nvmf_auth_dhchap_protocol_descriptor dhchap;
> +};
> +
> +struct nvmf_auth_dhchap_negotiate_data {
> + __u8 auth_type;
> + __u8 auth_id;
> + __u8 rsvd[2];
> + __le16 t_id;
> + __u8 sc_c;
> + __u8 napd;
> + union nvmf_auth_protocol auth_protocol[];
> +};
> +
> +struct nvmf_auth_dhchap_challenge_data {
> + __u8 auth_type;
> + __u8 auth_id;
> + __u8 rsvd1[2];
> + __le16 t_id;
> + __u8 hl;
> + __u8 rsvd2;
> + __u8 hashid;
> + __u8 dhgid;
> + __le16 dhvlen;
> + __le32 seqnum;
> + /* 'hl' bytes of challenge value */
> + __u8 cval[];
> + /* followed by 'dhvlen' bytes of DH value */
> +};
> +
> +struct nvmf_auth_dhchap_reply_data {
> + __u8 auth_type;
> + __u8 auth_id;
> + __u8 rsvd1[2];

Maybe __u32 rsvd1? Usually its done this way in the other
headers...

> + __le16 t_id;
> + __u8 hl;
> + __u8 rsvd2;
> + __u8 cvalid;
> + __u8 rsvd3;
> + __le16 dhvlen;
> + __le32 seqnum;
> + /* 'hl' bytes of response data */
> + __u8 rval[];
> + /* followed by 'hl' bytes of Challenge value */
> + /* followed by 'dhvlen' bytes of DH value */
> +};
> +
> +enum {
> + NVME_AUTH_DHCHAP_RESPONSE_VALID = (1 << 0),
> +};
> +
> +struct nvmf_auth_dhchap_success1_data {
> + __u8 auth_type;
> + __u8 auth_id;
> + __u8 rsvd1[2];
> + __le16 t_id;
> + __u8 hl;
> + __u8 rsvd2;
> + __u8 rvalid;
> + __u8 rsvd3[7];
> + /* 'hl' bytes of response value if 'rvalid' is set */
> + __u8 rval[];

It really sucks that we have zero-length pointers in
a wire-format struct... but anyways, it is what it is...

> +};
> +
> +struct nvmf_auth_dhchap_success2_data {
> + __u8 auth_type;
> + __u8 auth_id;
> + __u8 rsvd1[2];
> + __le16 t_id;
> + __u8 rsvd2[10];
> +};
> +
> +struct nvmf_auth_dhchap_failure_data {
> + __u8 auth_type;
> + __u8 auth_id;
> + __u8 rsvd1[2];
> + __le16 t_id;
> + __u8 reason_code;
> + __u8 reason_code_explanation;

I'd maybe do those shorter;
rescode
rescode_exp

> +};
> +
> +enum {
> + NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED = 0x01,
> +};
> +
> +enum {
> + NVME_AUTH_DHCHAP_FAILURE_FAILED = 0x01,
> + NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE = 0x02,
> + NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH = 0x03,
> + NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE = 0x04,
> + NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE = 0x05,
> + NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD = 0x06,
> + NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE = 0x07,

I think the language in the spec is "incorrect", why not
stick with that instead of "invalid"?

> +};
> +
> +
> struct nvme_dbbuf {
> __u8 opcode;
> __u8 flags;
> @@ -1436,6 +1617,8 @@ struct nvme_command {
> struct nvmf_connect_command connect;
> struct nvmf_property_set_command prop_set;
> struct nvmf_property_get_command prop_get;
> + struct nvmf_auth_send_command auth_send;
> + struct nvmf_auth_receive_command auth_receive;
> struct nvme_dbbuf dbbuf;
> struct nvme_directive_cmd directive;
> };
>

2021-07-17 07:23:59

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

> Implement NVMe-oF In-Band authentication. This patch adds two new
> fabric options 'dhchap_key' to specify the PSK

pre-shared-key.

Also, we need a sysfs knob to rotate the key that will trigger
re-authentication or even a simple controller(s-plural) reset, so this
should go beyond just the connection string.

P.S. can you add also the nvme-cli code in the next go?

> and 'dhchap_authenticate'
> to request bi-directional authentication of both the host and the controller.

bidirectional? not uni-directional?

>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/host/Kconfig | 11 +
> drivers/nvme/host/Makefile | 1 +
> drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++
> drivers/nvme/host/auth.h | 23 +
> drivers/nvme/host/core.c | 77 +++-
> drivers/nvme/host/fabrics.c | 65 ++-
> drivers/nvme/host/fabrics.h | 8 +
> drivers/nvme/host/nvme.h | 15 +
> drivers/nvme/host/trace.c | 32 ++
> 9 files changed, 1041 insertions(+), 4 deletions(-)
> create mode 100644 drivers/nvme/host/auth.c
> create mode 100644 drivers/nvme/host/auth.h
>
> diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
> index c3f3d77f1aac..853c546305e9 100644
> --- a/drivers/nvme/host/Kconfig
> +++ b/drivers/nvme/host/Kconfig
> @@ -85,3 +85,14 @@ config NVME_TCP
> from https://github.com/linux-nvme/nvme-cli.
>
> If unsure, say N.
> +
> +config NVME_AUTH
> + bool "NVM Express over Fabrics In-Band Authentication"
> + depends on NVME_TCP
> + select CRYPTO_SHA256
> + select CRYPTO_SHA512
> + help
> + This provides support for NVMe over Fabrics In-Band Authentication
> + for the NVMe over TCP transport.

In this form, nothing is specific to nvme-tcp here afaict.

> +
> + If unsure, say N.
> diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
> index cbc509784b2e..03748a55a12b 100644
> --- a/drivers/nvme/host/Makefile
> +++ b/drivers/nvme/host/Makefile
> @@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o
> nvme-y += pci.o
>
> nvme-fabrics-y += fabrics.o
> +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o
>
> nvme-rdma-y += rdma.o
>
> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
> new file mode 100644
> index 000000000000..448a3adebea6
> --- /dev/null
> +++ b/drivers/nvme/host/auth.c
> @@ -0,0 +1,813 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
> + */
> +
> +#include <linux/crc32.h>
> +#include <linux/base64.h>
> +#include <asm/unaligned.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include "nvme.h"
> +#include "fabrics.h"
> +#include "auth.h"
> +
> +static u32 nvme_dhchap_seqnum;
> +
> +struct nvme_dhchap_context {

Maybe nvme_dhchap_queue_context ?

I'm thinking that we should perhaps split
it to host-wide, subsys-wide and queue specific
auth contexts?

Let's see...

> + struct crypto_shash *shash_tfm;
> + unsigned char *key;
> + size_t key_len;
> + int qid;
> + u32 s1;
> + u32 s2;
> + u16 transaction;
> + u8 status;
> + u8 hash_id;
> + u8 hash_len;
> + u8 c1[64];
> + u8 c2[64];
> + u8 response[64];
> + u8 *ctrl_key;
> + int ctrl_key_len;
> + u8 *host_key;
> + int host_key_len;
> + u8 *sess_key;
> + int sess_key_len;
> +};
> +
> +struct nvmet_dhchap_hash_map {

nvmet?

> + int id;
> + int hash_len;
> + const char hmac[15];
> + const char digest[15];
> +} hash_map[] = {
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
> + .hash_len = 32,
> + .hmac = "hmac(sha256)", .digest = "sha256" },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
> + .hash_len = 48,
> + .hmac = "hmac(sha384)", .digest = "sha384" },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
> + .hash_len = 64,
> + .hmac = "hmac(sha512)", .digest = "sha512" },
> +};
> +
> +const char *nvme_auth_hmac_name(int hmac_id)

Should these arrays be static?

> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == hmac_id)
> + return hash_map[i].hmac;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
> +
> +const char *nvme_auth_digest_name(int hmac_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == hmac_id)
> + return hash_map[i].digest;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
> +
> +int nvme_auth_hmac_len(int hmac_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == hmac_id)
> + return hash_map[i].hash_len;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_len);
> +
> +int nvme_auth_hmac_id(const char *hmac_name)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (!strncmp(hash_map[i].hmac, hmac_name,
> + strlen(hash_map[i].hmac)))
> + return hash_map[i].id;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
> +
> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
> + size_t *dhchap_key_len)
> +{
> + unsigned char *dhchap_key;
> + u32 crc;
> + int key_len;
> + size_t allocated_len;
> +
> + allocated_len = strlen(dhchap_secret) - 10;

the 10 feels like a magic here, should at least note this is the
"DHHC-1:..." prefix.

> + dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
> + if (!dhchap_key)
> + return ERR_PTR(-ENOMEM);
> +
> + key_len = base64_decode(dhchap_secret + 10,
> + allocated_len, dhchap_key);
> + if (key_len != 36 && key_len != 52 &&
> + key_len != 68) {
> + pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
> + key_len);
> + kfree(dhchap_key);
> + return ERR_PTR(-EINVAL);
> + }
> + pr_debug("DH-HMAC-CHAP Key: %*ph\n",
> + (int)key_len, dhchap_key);

One can argue if even printing this is problematic..

> +
> + /* The last four bytes is the CRC in little-endian format */
> + key_len -= 4;
> + /*
> + * The linux implementation doesn't do pre- and post-increments,
> + * so we have to do it manually.
> + */
> + crc = ~crc32(~0, dhchap_key, key_len);
> +
> + if (get_unaligned_le32(dhchap_key + key_len) != crc) {
> + pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n",
> + get_unaligned_le32(dhchap_key + key_len), crc);
> + kfree(dhchap_key);
> + return ERR_PTR(-EKEYREJECTED);
> + }
> + *dhchap_key_len = key_len;
> + return dhchap_key;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
> +
> +static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
> + void *data, size_t tl)
> +{
> + struct nvme_command cmd = {};
> + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
> + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
> + struct request_queue *q = qid == NVME_QID_ANY ?
> + ctrl->fabrics_q : ctrl->connect_q;
> + int ret;
> +
> + cmd.auth_send.opcode = nvme_fabrics_command;
> + cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
> + cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
> + cmd.auth_send.spsp0 = 0x01;
> + cmd.auth_send.spsp1 = 0x01;
> + cmd.auth_send.tl = tl;
> +
> + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid,
> + 0, flags);
> + if (ret)
> + dev_dbg(ctrl->device,
> + "%s: qid %d error %d\n", __func__, qid, ret);

Maybe a little more informative print rather than __func__ ?

> + return ret;
> +}
> +
> +static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
> + void *buf, size_t al,
> + u16 transaction, u8 expected_msg )
> +{
> + struct nvme_command cmd = {};
> + struct nvmf_auth_dhchap_failure_data *data = buf;
> + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
> + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
> + struct request_queue *q = qid == NVME_QID_ANY ?
> + ctrl->fabrics_q : ctrl->connect_q;
> + int ret;
> +
> + cmd.auth_receive.opcode = nvme_fabrics_command;
> + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
> + cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
> + cmd.auth_receive.spsp0 = 0x01;
> + cmd.auth_receive.spsp1 = 0x01;
> + cmd.auth_receive.al = al;
> +
> + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid,
> + 0, flags);
> + if (ret > 0) {
> + dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n",
> + __func__, qid, ret);
> + ret = -EIO;
> + }
> + if (ret < 0) {
> + dev_dbg(ctrl->device, "%s: qid %d error %d\n",
> + __func__, qid, ret);
> + return ret;
> + }
> + dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
> + __func__, qid, data->auth_type, data->auth_id);
> + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
> + data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
> + return data->reason_code_explanation;
> + }
> + if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
> + data->auth_id != expected_msg) {
> + dev_warn(ctrl->device,
> + "qid %d invalid message %02x/%02x\n",
> + qid, data->auth_type, data->auth_id);
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + }
> + if (le16_to_cpu(data->t_id) != transaction) {
> + dev_warn(ctrl->device,
> + "qid %d invalid transaction ID %d\n",
> + qid, le16_to_cpu(data->t_id));
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + }
> +
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)

Maybe nvme_auth_set_dhchap_negotiate_data ?

> +{
> + struct nvmf_auth_dhchap_negotiate_data *data = buf;
> + size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
> +
> + if (buf_size < size)
> + return -EINVAL;
> +
> + memset((u8 *)buf, 0, size);
> + data->auth_type = NVME_AUTH_COMMON_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
> + data->t_id = cpu_to_le16(chap->transaction);
> + data->sc_c = 0; /* No secure channel concatenation */
> + data->napd = 1;
> + data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
> + data->auth_protocol[0].dhchap.halen = 3;
> + data->auth_protocol[0].dhchap.dhlen = 1;
> + data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
> + data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
> + data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
> + data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
You should comment that this routine expects buf to have enough
room for both negotiate and auth_proto structures.

> +
> + return size;
> +}
> +
> +static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)

Maybe nvme_auth_process_dhchap_challange ?

> +{
> + struct nvmf_auth_dhchap_challenge_data *data = buf;
> + size_t size = sizeof(*data) + data->hl + data->dhvlen;
> + const char *gid_name;
> +
> + if (buf_size < size) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -ENOMSG;
> + }
> +
> + if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 &&
> + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 &&
> + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n",
> + chap->qid, data->hashid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + return -EPROTO;
> + }
> + switch (data->dhgid) {
> + case NVME_AUTH_DHCHAP_DHGROUP_NULL:
> + gid_name = "null";
> + break;
> + default:
> + gid_name = NULL;
> + break;
> + }
> + if (!gid_name) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
> + chap->qid, data->dhgid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }

Maybe some spaces between condition blocks?

> + if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n",
> + __func__, chap->qid, data->hashid);
> + if (nvme_auth_hmac_len(data->hashid) != data->hl) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid hash length\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + return -EPROTO;
> + }
> + chap->hash_id = data->hashid;
> + chap->hash_len = data->hl;
> + chap->s1 = le32_to_cpu(data->seqnum);
> + memcpy(chap->c1, data->cval, chap->hash_len);
> +
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)

nvme_auth_set_dhchap_reply

> +{
> + struct nvmf_auth_dhchap_reply_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + size += 2 * chap->hash_len;
> + if (ctrl->opts->dhchap_auth) {

The ctrl opts is not clear to me. what is dhchap_auth
mean?

Also shouldn't these params be lifted to the subsys?

> + get_random_bytes(chap->c2, chap->hash_len);
> + chap->s2 = nvme_dhchap_seqnum++;
> + } else
> + memset(chap->c2, 0, chap->hash_len);
> +
> + if (chap->host_key_len)
> + size += chap->host_key_len;
> +
> + if (buf_size < size)
> + return -EINVAL;
> +
> + memset(buf, 0, size);
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
> + data->t_id = cpu_to_le16(chap->transaction);
> + data->hl = chap->hash_len;
> + data->dhvlen = chap->host_key_len;
> + data->seqnum = cpu_to_le32(chap->s2);
> + memcpy(data->rval, chap->response, chap->hash_len);
> + if (ctrl->opts->dhchap_auth) {
> + dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
> + __func__, chap->qid,
> + chap->hash_len, chap->c2);
> + data->cvalid = 1;
> + memcpy(data->rval + chap->hash_len, chap->c2,
> + chap->hash_len);
> + }
> + if (chap->host_key_len)
> + memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
> + chap->host_key_len);
> +
> + return size;
> +}
> +
> +static int nvme_auth_dhchap_success1(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)

nvme_auth_process_dhchap_success1

> +{
> + struct nvmf_auth_dhchap_success1_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + if (ctrl->opts->dhchap_auth)
> + size += chap->hash_len;
> +
> +
> + if (buf_size < size) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -ENOMSG;
> + }
> +
> + if (data->hl != chap->hash_len) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid hash length %d\n",
> + chap->qid, data->hl);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + return -EPROTO;
> + }
> +
> + if (!data->rvalid)
> + return 0;
> +
> + /* Validate controller response */
> + if (memcmp(chap->response, data->rval, data->hl)) {
> + dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
> + __func__, chap->qid, chap->hash_len, data->rval);
> + dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
> + __func__, chap->qid, chap->hash_len, chap->response);
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: controller authentication failed\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -EPROTO;
> + }
> + dev_info(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: controller authenticated\n",
> + chap->qid);
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)

same

> +{
> + struct nvmf_auth_dhchap_success2_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + memset(buf, 0, size);
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
> + data->t_id = cpu_to_le16(chap->transaction);
> +
> + return size;
> +}
> +
> +static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)

same

> +{
> + struct nvmf_auth_dhchap_failure_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + memset(buf, 0, size);
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
> + data->t_id = cpu_to_le16(chap->transaction);
> + data->reason_code = 1;
> + data->reason_code_explanation = chap->status;
> +
> + return size;
> +}
> +
> +int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)

Maybe _select_hf (hash function)? not a must, just sticks
to the spec language.

> +{
> + char *hash_name;
> + int ret;
> +
> + switch (chap->hash_id) {
> + case NVME_AUTH_DHCHAP_HASH_SHA256:
> + hash_name = "hmac(sha256)";
> + break;
> + case NVME_AUTH_DHCHAP_HASH_SHA384:
> + hash_name = "hmac(sha384)";
> + break;
> + case NVME_AUTH_DHCHAP_HASH_SHA512:
> + hash_name = "hmac(sha512)";
> + break;
> + default:
> + hash_name = NULL;
> + break;
> + }
> + if (!hash_name) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + return -EPROTO;
> + }
> + chap->shash_tfm = crypto_alloc_shash(hash_name, 0,
> + CRYPTO_ALG_ALLOCATES_MEMORY);
> + if (IS_ERR(chap->shash_tfm)) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + chap->shash_tfm = NULL;
> + return -EPROTO;
> + }
> + if (!chap->key) {
> + dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->shash_tfm);

Wouldn't it better to check this before allocating the tfm?

> + chap->shash_tfm = NULL;
> + return -EINVAL;
> + }
> + ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
> + if (ret) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + return ret;
> + }
> + dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> + chap->qid, hash_name);
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
> + u8 buf[4], *challenge = chap->c1;
> + int ret;
> +
> + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
> + __func__, chap->qid, chap->s1, chap->transaction);
> + shash->tfm = chap->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, chap->hash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(chap->s1, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(chap->transaction, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, sizeof(buf));
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "HostHost", 8);

HostHost ? Can you refer me to the specific section
that talks about this?

Would be good to have a comment on the format fed to the
shash.

> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
> + strlen(ctrl->opts->host->nqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
> + strlen(ctrl->opts->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, chap->response);
> +out:
> + return ret;
> +}
> +
> +static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
> + u8 buf[4], *challenge = chap->c2;
> + int ret;
> +
> + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
> + __func__, chap->qid, chap->s2, chap->transaction);
> + dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
> + __func__, chap->qid, chap->hash_len, challenge);
> + dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
> + __func__, chap->qid, ctrl->opts->subsysnqn);
> + dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
> + __func__, chap->qid, ctrl->opts->host->nqn);
> + shash->tfm = chap->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, chap->hash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(chap->s2, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(chap->transaction, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, 4);
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "Controller", 10);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
> + strlen(ctrl->opts->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
> + strlen(ctrl->opts->host->nqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, chap->response);
> +out:
> + return ret;
> +}
> +
> +int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + int ret;
> + u8 key_hash;
> + const char *hmac_name;
> + struct crypto_shash *key_tfm;
> +
> + if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
> + &key_hash) != 1)
> + return -EINVAL;

I'd expect that the user will pass in a secret key (as binary)
the the driver will build the spec compliant formatted string no?

Am I not reading this correctly?

> +
> + chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
> + &chap->key_len);
> + if (IS_ERR(chap->key)) {
> + ret = PTR_ERR(chap->key);
> + chap->key = NULL;
> + return ret;
> + }
> +
> + if (key_hash == 0)
> + return 0;
> +
> + hmac_name = nvme_auth_hmac_name(key_hash);
> + if (!hmac_name) {
> + pr_debug("Invalid key hash id %d\n", key_hash);
> + return -EKEYREJECTED;
> + }

Why does the user influence the hmac used? isn't that is driven
by the susbsystem?

I don't think that the user should choose in this level.

> +
> + key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
> + if (IS_ERR(key_tfm)) {
> + kfree(chap->key);
> + chap->key = NULL;
> + ret = PTR_ERR(key_tfm);

You set ret and later return 0? I think that the success
path in the else clause is hard to read and error prone...

> + } else {
> + SHASH_DESC_ON_STACK(shash, key_tfm);
> +
> + shash->tfm = key_tfm;
> + ret = crypto_shash_setkey(key_tfm, chap->key,
> + chap->key_len);
> + if (ret < 0) {
> + crypto_free_shash(key_tfm);
> + kfree(chap->key);
> + chap->key = NULL;
> + return ret;
> + }
> + crypto_shash_init(shash);
> + crypto_shash_update(shash, ctrl->opts->host->nqn,
> + strlen(ctrl->opts->host->nqn));
> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
> + crypto_shash_final(shash, chap->key);
> + crypto_free_shash(key_tfm);

Shouldn't these be done when preparing the dh-hmac-chap reply?

> + }
> + return 0;
> +}
> +
> +void nvme_auth_free(struct nvme_dhchap_context *chap)
> +{
> + if (chap->shash_tfm)
> + crypto_free_shash(chap->shash_tfm);
> + if (chap->key)
> + kfree(chap->key);
> + if (chap->ctrl_key)
> + kfree(chap->ctrl_key);
> + if (chap->host_key)
> + kfree(chap->host_key);
> + if (chap->sess_key)
> + kfree(chap->sess_key);

No need to check null for kfree...

> + kfree(chap);
> +}
> +
> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
> +{
> + struct nvme_dhchap_context *chap;
> + void *buf;
> + size_t buf_size, tl;
> + int ret = 0;
> +
> + chap = kzalloc(sizeof(*chap), GFP_KERNEL);
> + if (!chap)
> + return -ENOMEM;
> + chap->qid = qid;
> + chap->transaction = ctrl->transaction++;
> +
> + ret = nvme_auth_generate_key(ctrl, chap);
> + if (ret) {
> + dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n",
> + __func__, ret);
> + nvme_auth_free(chap);
> + return ret;
> + }
> +
> + /*
> + * Allocate a large enough buffer for the entire negotiation:
> + * 4k should be enough to ffdhe8192.
> + */
> + buf_size = 4096;
> + buf = kzalloc(buf_size, GFP_KERNEL);
> + if (!buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + /* DH-HMAC-CHAP Step 1: send negotiate */

I'd consider breaking these into sub-routines.

> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size);
> + if (ret < 0)
> + goto out;
> + tl = ret;
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> + if (ret)
> + goto out;
> +
> + memset(buf, 0, buf_size);
> + ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
> + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
> + if (ret < 0) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n",
> + __func__, qid);
> + goto out;
> + }
> + if (ret > 0) {
> + chap->status = ret;
> + goto fail1;
> + }
> +
> + /* DH-HMAC-CHAP Step 2: receive challenge */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n",
> + __func__, qid);
> +
> + ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size);
> + if (ret) {
> + /* Invalid parameters for negotiate */
> + goto fail2;
> + }
> +
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n",
> + __func__, qid);
> + ret = nvme_auth_select_hash(ctrl, chap);
> + if (ret)
> + goto fail2;
> +
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_host_response(ctrl, chap);
> + if (ret)
> + goto fail2;
> +
> + /* DH-HMAC-CHAP Step 3: send reply */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size);
> + if (ret < 0)
> + goto fail2;
> +
> + tl = ret;
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> + if (ret)
> + goto fail2;
> +
> + memset(buf, 0, buf_size);
> + ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
> + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
> + if (ret < 0) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-CHAP failed to receive success1\n",
> + __func__, qid);
> + goto out;
> + }
> + if (ret > 0) {
> + chap->status = ret;
> + goto fail1;
> + }
> +
> + if (ctrl->opts->dhchap_auth) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-CHAP controller response\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
> + if (ret)
> + goto fail2;
> + }
> +
> + /* DH-HMAC-CHAP Step 4: receive success1 */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size);
> + if (ret < 0) {
> + /* Controller authentication failed */
> + goto fail2;
> + }
> + tl = ret;
> + /* DH-HMAC-CHAP Step 5: send success2 */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n",
> + __func__, qid);
> + tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size);
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> + if (!ret)
> + goto out;
> +
> +fail1:
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status %x\n",
> + __func__, qid, chap->status);
> + goto out;
> +
> +fail2:
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status %x\n",
> + __func__, qid, chap->status);
> + tl = nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size);
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> +
> +out:
> + if (!ret && chap->status)
> + ret = -EPROTO;
> + if (!ret) {
> + ctrl->dhchap_hash = chap->hash_id;
> + }
> + kfree(buf);
> + nvme_auth_free(chap);
> + return ret;
> +}
> diff --git a/drivers/nvme/host/auth.h b/drivers/nvme/host/auth.h
> new file mode 100644
> index 000000000000..4950b1cb9470
> --- /dev/null
> +++ b/drivers/nvme/host/auth.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
> + */
> +
> +#ifndef _NVME_AUTH_H
> +#define _NVME_AUTH_H
> +
> +const char *nvme_auth_dhgroup_name(int dhgroup_id);
> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id);
> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id);
> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id);
> +int nvme_auth_dhgroup_id(const char *dhgroup_name);
> +
> +const char *nvme_auth_hmac_name(int hmac_id);
> +const char *nvme_auth_digest_name(int hmac_id);
> +int nvme_auth_hmac_id(const char *hmac_name);
> +int nvme_auth_hmac_len(int hmac_len);
> +
> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
> + size_t *dhchap_key_len);
> +
> +#endif /* _NVME_AUTH_H */
> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
> index 11779be42186..7ce9b666dc09 100644
> --- a/drivers/nvme/host/core.c
> +++ b/drivers/nvme/host/core.c
> @@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq,
> switch (ctrl->state) {
> case NVME_CTRL_CONNECTING:
> if (blk_rq_is_passthrough(rq) && nvme_is_fabrics(req->cmd) &&
> - req->cmd->fabrics.fctype == nvme_fabrics_type_connect)
> + (req->cmd->fabrics.fctype == nvme_fabrics_type_connect ||
> + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_send ||
> + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_receive))
> return true;
> break;
> default:
> @@ -3426,6 +3428,66 @@ static ssize_t nvme_ctrl_fast_io_fail_tmo_store(struct device *dev,
> static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
> nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store);
>
> +#ifdef CONFIG_NVME_AUTH
> +struct nvmet_dhchap_hash_map {
> + int id;
> + const char name[15];
> +} hash_map[] = {
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
> + .name = "hmac(sha256)", },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
> + .name = "hmac(sha384)", },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
> + .name = "hmac(sha512)", },
> +};
> +
> +static ssize_t dhchap_hash_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == ctrl->dhchap_hash)
> + return sprintf(buf, "%s\n", hash_map[i].name);
> + }
> + return sprintf(buf, "none\n");
> +}
> +DEVICE_ATTR_RO(dhchap_hash);
> +
> +struct nvmet_dhchap_group_map {
> + int id;
> + const char name[15];
> +} dhgroup_map[] = {
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
> + .name = "NULL", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_2048,
> + .name = "ffdhe2048", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_3072,
> + .name = "ffdhe3072", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_4096,
> + .name = "ffdhe4096", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_6144,
> + .name = "ffdhe6144", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_8192,
> + .name = "ffdhe8192", },
> +};
> +
> +static ssize_t dhchap_dhgroup_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (hash_map[i].id == ctrl->dhchap_dhgroup)
> + return sprintf(buf, "%s\n", dhgroup_map[i].name);
> + }
> + return sprintf(buf, "none\n");
> +}
> +DEVICE_ATTR_RO(dhchap_dhgroup);
> +#endif
> +
> static struct attribute *nvme_dev_attrs[] = {
> &dev_attr_reset_controller.attr,
> &dev_attr_rescan_controller.attr,
> @@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = {
> &dev_attr_reconnect_delay.attr,
> &dev_attr_fast_io_fail_tmo.attr,
> &dev_attr_kato.attr,
> +#ifdef CONFIG_NVME_AUTH
> + &dev_attr_dhchap_hash.attr,
> + &dev_attr_dhchap_dhgroup.attr,
> +#endif
> NULL
> };
>
> @@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj,
> return 0;
> if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
> return 0;
> +#ifdef CONFIG_NVME_AUTH
> + if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts)
> + return 0;
> +#endif
>
> return a->mode;
> }
> @@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)
> BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
> BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
> BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16);
> }
>
>
> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
> index a5469fd9d4c3..6404ab9b604b 100644
> --- a/drivers/nvme/host/fabrics.c
> +++ b/drivers/nvme/host/fabrics.c
> @@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
> union nvme_result res;
> struct nvmf_connect_data *data;
> int ret;
> + u32 result;
>
> cmd.connect.opcode = nvme_fabrics_command;
> cmd.connect.fctype = nvme_fabrics_type_connect;
> @@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
> goto out_free_data;
> }
>
> - ctrl->cntlid = le16_to_cpu(res.u16);
> -
> + result = le32_to_cpu(res.u32);
> + ctrl->cntlid = result & 0xFFFF;
> + if ((result >> 16) & 2) {
> + /* Authentication required */
> + ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
> + if (ret)
> + dev_warn(ctrl->device,
> + "qid 0: authentication failed\n");
> + else
> + dev_info(ctrl->device,
> + "qid 0: authenticated\n");

info is too chatty.

> + }
> out_free_data:
> kfree(data);
> return ret;
> @@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)
> struct nvmf_connect_data *data;
> union nvme_result res;
> int ret;
> + u32 result;
>
> cmd.connect.opcode = nvme_fabrics_command;
> cmd.connect.fctype = nvme_fabrics_type_connect;
> @@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid)
> nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32),
> &cmd, data);
> }
> + result = le32_to_cpu(res.u32);
> + if ((result >> 16) & 2) {
> + /* Authentication required */
> + ret = nvme_auth_negotiate(ctrl, qid);
> + if (ret)
> + dev_warn(ctrl->device,
> + "qid %u: authentication failed\n", qid);
> + else
> + dev_info(ctrl->device,
> + "qid %u: authenticated\n", qid);
> + }
> kfree(data);
> return ret;
> }
> @@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
> { NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" },
> { NVMF_OPT_TOS, "tos=%d" },
> { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" },
> + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" },
> + { NVMF_OPT_DHCHAP_AUTH, "authenticate" },
> + { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" },

Isn't the group driven by the subsystem? also why is there a
"authenticate" boolean? what is it good for?

> { NVMF_OPT_ERR, NULL }
> };
>
> @@ -824,6 +850,35 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
> }
> opts->tos = token;
> break;
> + case NVMF_OPT_DHCHAP_SECRET:
> + p = match_strdup(args);
> + if (!p) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + if (strncmp(p, "DHHC-1:00:", 10)) {
> + pr_err("Invalid DH-CHAP secret %s\n", p);
> + ret = -EINVAL;
> + goto out;
> + }
> + kfree(opts->dhchap_secret);
> + opts->dhchap_secret = p;
> + break;
> + case NVMF_OPT_DHCHAP_AUTH:
> + opts->dhchap_auth = true;
> + break;
> + case NVMF_OPT_DHCHAP_GROUP:
> + if (match_int(args, &token)) {
> + ret = -EINVAL;
> + goto out;
> + }
> + if (token <= 0) {
> + pr_err("Invalid dhchap_group %d\n", token);
> + ret = -EINVAL;
> + goto out;
> + }
> + opts->dhchap_group = token;
> + break;
> default:
> pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n",
> p);
> @@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts)
> kfree(opts->subsysnqn);
> kfree(opts->host_traddr);
> kfree(opts->host_iface);
> + kfree(opts->dhchap_secret);
> kfree(opts);
> }
> EXPORT_SYMBOL_GPL(nvmf_free_options);
> @@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options);
> NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \
> NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
> NVMF_OPT_DISABLE_SQFLOW |\
> - NVMF_OPT_FAIL_FAST_TMO)
> + NVMF_OPT_CTRL_LOSS_TMO |\
> + NVMF_OPT_FAIL_FAST_TMO |\
> + NVMF_OPT_DHCHAP_SECRET |\
> + NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)
>
> static struct nvme_ctrl *
> nvmf_create_ctrl(struct device *dev, const char *buf)
> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
> index a146cb903869..535bc544f0f6 100644
> --- a/drivers/nvme/host/fabrics.h
> +++ b/drivers/nvme/host/fabrics.h
> @@ -67,6 +67,9 @@ enum {
> NVMF_OPT_TOS = 1 << 19,
> NVMF_OPT_FAIL_FAST_TMO = 1 << 20,
> NVMF_OPT_HOST_IFACE = 1 << 21,
> + NVMF_OPT_DHCHAP_SECRET = 1 << 22,
> + NVMF_OPT_DHCHAP_AUTH = 1 << 23,
> + NVMF_OPT_DHCHAP_GROUP = 1 << 24,
> };
>
> /**
> @@ -96,6 +99,8 @@ enum {
> * @max_reconnects: maximum number of allowed reconnect attempts before removing
> * the controller, (-1) means reconnect forever, zero means remove
> * immediately;
> + * @dhchap_secret: DH-HMAC-CHAP secret
> + * @dhchap_auth: DH-HMAC-CHAP authenticate controller
> * @disable_sqflow: disable controller sq flow control
> * @hdr_digest: generate/verify header digest (TCP)
> * @data_digest: generate/verify data digest (TCP)
> @@ -120,6 +125,9 @@ struct nvmf_ctrl_options {
> unsigned int kato;
> struct nvmf_host *host;
> int max_reconnects;
> + char *dhchap_secret;
> + int dhchap_group;
> + bool dhchap_auth;
> bool disable_sqflow;
> bool hdr_digest;
> bool data_digest;
> diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
> index 18ef8dd03a90..bcd5b8276c26 100644
> --- a/drivers/nvme/host/nvme.h
> +++ b/drivers/nvme/host/nvme.h
> @@ -328,6 +328,12 @@ struct nvme_ctrl {
> struct work_struct ana_work;
> #endif
>
> +#ifdef CONFIG_NVME_AUTH
> + u16 transaction;
> + u8 dhchap_hash;
> + u8 dhchap_dhgroup;

Do multiple controllers in the same subsystem have different
params? no, so I think these should be lifted to subsys.

> +#endif
> +
> /* Power saving configuration */
> u64 ps_max_latency_us;
> bool apst_enabled;
> @@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)
> return ctrl->sgls & ((1 << 0) | (1 << 1));
> }
>
> +#ifdef CONFIG_NVME_AUTH
> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
> +#else
> +static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
> +{
> + return -EPROTONOSUPPORT;
> +}
> +#endif
> +
> u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
> u8 opcode);
> int nvme_execute_passthru_rq(struct request *rq);
> diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c
> index 6543015b6121..66f75d8ea925 100644
> --- a/drivers/nvme/host/trace.c
> +++ b/drivers/nvme/host/trace.c

I'd split out the tracing logic.

> @@ -271,6 +271,34 @@ static const char *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc)
> return ret;
> }
>
> +static const char *nvme_trace_fabrics_auth_send(struct trace_seq *p, u8 *spc)
> +{
> + const char *ret = trace_seq_buffer_ptr(p);
> + u8 spsp0 = spc[1];
> + u8 spsp1 = spc[2];
> + u8 secp = spc[3];
> + u32 tl = get_unaligned_le32(spc + 4);
> +
> + trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u",
> + spsp0, spsp1, secp, tl);
> + trace_seq_putc(p, 0);
> + return ret;
> +}
> +
> +static const char *nvme_trace_fabrics_auth_receive(struct trace_seq *p, u8 *spc)
> +{
> + const char *ret = trace_seq_buffer_ptr(p);
> + u8 spsp0 = spc[1];
> + u8 spsp1 = spc[2];
> + u8 secp = spc[3];
> + u32 al = get_unaligned_le32(spc + 4);
> +
> + trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, al=%u",
> + spsp0, spsp1, secp, al);
> + trace_seq_putc(p, 0);
> + return ret;
> +}
> +
> static const char *nvme_trace_fabrics_common(struct trace_seq *p, u8 *spc)
> {
> const char *ret = trace_seq_buffer_ptr(p);
> @@ -290,6 +318,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct trace_seq *p,
> return nvme_trace_fabrics_connect(p, spc);
> case nvme_fabrics_type_property_get:
> return nvme_trace_fabrics_property_get(p, spc);
> + case nvme_fabrics_type_auth_send:
> + return nvme_trace_fabrics_auth_send(p, spc);
> + case nvme_fabrics_type_auth_receive:
> + return nvme_trace_fabrics_auth_receive(p, spc);
> default:
> return nvme_trace_fabrics_common(p, spc);
> }
>

2021-07-17 13:58:15

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters

On 7/17/21 8:14 AM, Sagi Grimberg wrote:
>> Add helper functions to generaten Finite Field DH Ephemeral Parameters as
>> specified in RFC 7919.
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>>   crypto/Kconfig         |   8 +
>>   crypto/Makefile        |   1 +
>>   crypto/ffdhe_helper.c  | 877 +++++++++++++++++++++++++++++++++++++++++
>>   include/crypto/ffdhe.h |  24 ++
>>   4 files changed, 910 insertions(+)
>>   create mode 100644 crypto/ffdhe_helper.c
>>   create mode 100644 include/crypto/ffdhe.h
>>
>> diff --git a/crypto/Kconfig b/crypto/Kconfig
>> index ca3b02dcbbfa..1bea506ba56f 100644
>> --- a/crypto/Kconfig
>> +++ b/crypto/Kconfig
>> @@ -231,6 +231,14 @@ config CRYPTO_DH
>>       help
>>         Generic implementation of the Diffie-Hellman algorithm.
>> +config CRYPTO_FFDHE
>> +    tristate "Finite Field DH (RFC 7919) ephemeral parameters"
>
> I'd stick with "Diffie-Hellman" in the tristate.
>

Ok.

>> +    select CRYPTO_DH
>> +    select CRYPTO_KPP
>> +    select CRYPTO_RNG_DEFAULT
>> +    help
>> +      Generic implementation of the Finite Field DH algorithm
>
> Diffie-Hellman algorithm
> And not sure I'd call it algorithm implementation, but rather a
> helper but maybe something like:
> Finite Field Diffie-Hellman ephemeral parameters helper implementation
>

Wasn't sure how to call it myself; as stated I'm not a security expert.

>> +
>>   config CRYPTO_ECC
>>       tristate
>> diff --git a/crypto/Makefile b/crypto/Makefile
>> index 10526d4559b8..d3bc79fba23f 100644
>> --- a/crypto/Makefile
>> +++ b/crypto/Makefile
>> @@ -177,6 +177,7 @@ obj-$(CONFIG_CRYPTO_OFB) += ofb.o
>>   obj-$(CONFIG_CRYPTO_ECC) += ecc.o
>>   obj-$(CONFIG_CRYPTO_ESSIV) += essiv.o
>>   obj-$(CONFIG_CRYPTO_CURVE25519) += curve25519-generic.o
>> +obj-$(CONFIG_CRYPTO_FFDHE) += ffdhe_helper.o
>>   ecdh_generic-y += ecdh.o
>>   ecdh_generic-y += ecdh_helper.o
>> diff --git a/crypto/ffdhe_helper.c b/crypto/ffdhe_helper.c
>> new file mode 100644
>> index 000000000000..dc023e30c4e5
>> --- /dev/null
>> +++ b/crypto/ffdhe_helper.c
>> @@ -0,0 +1,877 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Finite Field DH Ephemeral Parameters (RFC 7919)
>> + *
>> + * Copyright (c) 2021, Hannes Reinecke, SUSE Software Products
>> + *
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <crypto/internal/kpp.h>
>> +#include <crypto/kpp.h>
>> +#include <crypto/dh.h>
>> +#include <linux/mpi.h>
>> +
>> +/*
>> + * ffdhe2048 generator (g), modulus (p) and group size (q)
>
> Maybe worth to refer exactly the source of these parameters
> in the comment body (rfc section/appendix).
>

Sure. These actually are copies from RFC 7919, so will be adding a
reference to it.

Cheers,

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

2021-07-17 14:00:36

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding

On 7/17/21 8:16 AM, Sagi Grimberg wrote:
>
>> Add RFC4648-compliant base64 encoding and decoding routines.
>
> Looks good to me (although didn't look in the logic itself).
> Can you maybe mention where was this taken from?

Umm ... yeah, I guess I can; I _think_ I've copied it from base64
routines in fs/crypto/fname.c, but I'll check.

Cheers,

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

2021-07-17 14:04:52

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication

On 7/17/21 8:30 AM, Sagi Grimberg wrote:
>
>
> On 7/16/21 4:04 AM, Hannes Reinecke wrote:
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>>   include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 184 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/linux/nvme.h b/include/linux/nvme.h
>> index b7c4c4130b65..7b94abacfd08 100644
>> --- a/include/linux/nvme.h
>> +++ b/include/linux/nvme.h
>> @@ -19,6 +19,7 @@
>>   #define NVMF_TRSVCID_SIZE    32
>>   #define NVMF_TRADDR_SIZE    256
>>   #define NVMF_TSAS_SIZE        256
>> +#define NVMF_AUTH_HASH_LEN    64
>>   #define NVME_DISC_SUBSYS_NAME    "nqn.2014-08.org.nvmexpress.discovery"
>> @@ -1263,6 +1264,8 @@ enum nvmf_capsule_command {
>>       nvme_fabrics_type_property_set    = 0x00,
>>       nvme_fabrics_type_connect    = 0x01,
>>       nvme_fabrics_type_property_get    = 0x04,
>> +    nvme_fabrics_type_auth_send    = 0x05,
>> +    nvme_fabrics_type_auth_receive    = 0x06,
>>   };
>>   #define nvme_fabrics_type_name(type)   { type, #type }
>> @@ -1270,7 +1273,9 @@ enum nvmf_capsule_command {
>>       __print_symbolic(type,                        \
>>           nvme_fabrics_type_name(nvme_fabrics_type_property_set),    \
>>           nvme_fabrics_type_name(nvme_fabrics_type_connect),    \
>> -        nvme_fabrics_type_name(nvme_fabrics_type_property_get))
>> +        nvme_fabrics_type_name(nvme_fabrics_type_property_get), \
>> +        nvme_fabrics_type_name(nvme_fabrics_type_auth_send),    \
>> +        nvme_fabrics_type_name(nvme_fabrics_type_auth_receive))
>>   /*
>>    * If not fabrics command, fctype will be ignored.
>> @@ -1393,6 +1398,182 @@ struct nvmf_property_get_command {
>>       __u8        resv4[16];
>>   };
>> +struct nvmf_auth_send_command {
>> +    __u8        opcode;
>> +    __u8        resv1;
>> +    __u16        command_id;
>> +    __u8        fctype;
>> +    __u8        resv2[19];
>> +    union nvme_data_ptr dptr;
>> +    __u8        resv3;
>> +    __u8        spsp0;
>> +    __u8        spsp1;
>> +    __u8        secp;
>> +    __le32        tl;
>> +    __u8        resv4[12];
>
> Isn't that 16 bytes?
> You should add these to the compile time checkers
> in _nvme_check_size.
>

If you say so ... I'll cross-check.

>> +
>> +};
>> +
>> +struct nvmf_auth_receive_command {
>> +    __u8        opcode;
>> +    __u8        resv1;
>> +    __u16        command_id;
>> +    __u8        fctype;
>> +    __u8        resv2[19];
>> +    union nvme_data_ptr dptr;
>> +    __u8        resv3;
>> +    __u8        spsp0;
>> +    __u8        spsp1;
>> +    __u8        secp;
>> +    __le32        al;
>> +    __u8        resv4[12];
>> +};
>> +
>> +/* Value for secp */
>> +enum {
>> +    NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER    = 0xe9,
>> +};
>> +
>> +/* Defined value for auth_type */
>> +enum {
>> +    NVME_AUTH_COMMON_MESSAGES    = 0x00,
>> +    NVME_AUTH_DHCHAP_MESSAGES    = 0x01,
>> +};
>> +
>> +/* Defined messages for auth_id */
>> +enum {
>> +    NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE    = 0x00,
>> +    NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE    = 0x01,
>> +    NVME_AUTH_DHCHAP_MESSAGE_REPLY        = 0x02,
>> +    NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1    = 0x03,
>> +    NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2    = 0x04,
>> +    NVME_AUTH_DHCHAP_MESSAGE_FAILURE2    = 0xf0,
>> +    NVME_AUTH_DHCHAP_MESSAGE_FAILURE1    = 0xf1,
>> +};
>> +
>> +struct nvmf_auth_dhchap_protocol_descriptor {
>> +    __u8        authid;
>> +    __u8        rsvd;
>> +    __u8        halen;
>> +    __u8        dhlen;
>> +    __u8        idlist[60];
>> +};
>> +
>> +enum {
>> +    NVME_AUTH_DHCHAP_AUTH_ID    = 0x01,
>> +};
>> +
>> +enum {
>> +    NVME_AUTH_DHCHAP_HASH_SHA256    = 0x01,
>
> Maybe s/HASH/HF/ (stands for hash function, which is
> a better description).
>

Or HMAC, as this is what it's used for...

>> +    NVME_AUTH_DHCHAP_HASH_SHA384    = 0x02,
>> +    NVME_AUTH_DHCHAP_HASH_SHA512    = 0x03,
>> +};
>> +
>> +enum {
>> +    NVME_AUTH_DHCHAP_DHGROUP_NULL    = 0x00,
>> +    NVME_AUTH_DHCHAP_DHGROUP_2048    = 0x01,
>> +    NVME_AUTH_DHCHAP_DHGROUP_3072    = 0x02,
>> +    NVME_AUTH_DHCHAP_DHGROUP_4096    = 0x03,
>> +    NVME_AUTH_DHCHAP_DHGROUP_6144    = 0x04,
>> +    NVME_AUTH_DHCHAP_DHGROUP_8192    = 0x05,
>> +};
>> +
>> +union nvmf_auth_protocol {
>> +    struct nvmf_auth_dhchap_protocol_descriptor dhchap;
>> +};
>> +
>> +struct nvmf_auth_dhchap_negotiate_data {
>> +    __u8        auth_type;
>> +    __u8        auth_id;
>> +    __u8        rsvd[2];
>> +    __le16        t_id;
>> +    __u8        sc_c;
>> +    __u8        napd;
>> +    union nvmf_auth_protocol auth_protocol[];
>> +};
>> +
>> +struct nvmf_auth_dhchap_challenge_data {
>> +    __u8        auth_type;
>> +    __u8        auth_id;
>> +    __u8        rsvd1[2];
>> +    __le16        t_id;
>> +    __u8        hl;
>> +    __u8        rsvd2;
>> +    __u8        hashid;
>> +    __u8        dhgid;
>> +    __le16        dhvlen;
>> +    __le32        seqnum;
>> +    /* 'hl' bytes of challenge value */
>> +    __u8        cval[];
>> +    /* followed by 'dhvlen' bytes of DH value */
>> +};
>> +
>> +struct nvmf_auth_dhchap_reply_data {
>> +    __u8        auth_type;
>> +    __u8        auth_id;
>> +    __u8        rsvd1[2];
>
> Maybe __u32 rsvd1? Usually its done this way in the other
> headers...
>

Ah. Right, will fix.

>> +    __le16        t_id;
>> +    __u8        hl;
>> +    __u8        rsvd2;
>> +    __u8        cvalid;
>> +    __u8        rsvd3;
>> +    __le16        dhvlen;
>> +    __le32        seqnum;
>> +    /* 'hl' bytes of response data */
>> +    __u8        rval[];
>> +    /* followed by 'hl' bytes of Challenge value */
>> +    /* followed by 'dhvlen' bytes of DH value */
>> +};
>> +
>> +enum {
>> +    NVME_AUTH_DHCHAP_RESPONSE_VALID    = (1 << 0),
>> +};
>> +
>> +struct nvmf_auth_dhchap_success1_data {
>> +    __u8        auth_type;
>> +    __u8        auth_id;
>> +    __u8        rsvd1[2];
>> +    __le16        t_id;
>> +    __u8        hl;
>> +    __u8        rsvd2;
>> +    __u8        rvalid;
>> +    __u8        rsvd3[7];
>> +    /* 'hl' bytes of response value if 'rvalid' is set */
>> +    __u8        rval[];
>
> It really sucks that we have zero-length pointers in
> a wire-format struct... but anyways, it is what it is...
>

Yeah, and that's not the worst of it; the 'reply' structure has _three_
zero-length pointers.
Makes me wonder if I should drop them completely...

>> +};
>> +
>> +struct nvmf_auth_dhchap_success2_data {
>> +    __u8        auth_type;
>> +    __u8        auth_id;
>> +    __u8        rsvd1[2];
>> +    __le16        t_id;
>> +    __u8        rsvd2[10];
>> +};
>> +
>> +struct nvmf_auth_dhchap_failure_data {
>> +    __u8        auth_type;
>> +    __u8        auth_id;
>> +    __u8        rsvd1[2];
>> +    __le16        t_id;
>> +    __u8        reason_code;
>> +    __u8        reason_code_explanation;
>
> I'd maybe do those shorter;
> rescode
> rescode_exp
>

Ok.

>> +};
>> +
>> +enum {
>> +    NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED    = 0x01,
>> +};
>> +
>> +enum {
>> +    NVME_AUTH_DHCHAP_FAILURE_FAILED            = 0x01,
>> +    NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE        = 0x02,
>> +    NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH    = 0x03,
>> +    NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE        = 0x04,
>> +    NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE    = 0x05,
>> +    NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD    = 0x06,
>> +    NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE    = 0x07,
>
> I think the language in the spec is "incorrect", why not
> stick with that instead of "invalid"?
>

Ah. Things got changed so much during development of the spec that I
seem to have lost track. Will change it.

>> +};
>> +
>> +
>>   struct nvme_dbbuf {
>>       __u8            opcode;
>>       __u8            flags;
>> @@ -1436,6 +1617,8 @@ struct nvme_command {
>>           struct nvmf_connect_command connect;
>>           struct nvmf_property_set_command prop_set;
>>           struct nvmf_property_get_command prop_get;
>> +        struct nvmf_auth_send_command auth_send;
>> +        struct nvmf_auth_receive_command auth_receive;
>>           struct nvme_dbbuf dbbuf;
>>           struct nvme_directive_cmd directive;
>>       };
>>

Cheers,

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

2021-07-17 14:13:15

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding

On Sat, Jul 17, 2021 at 04:00:20PM +0200, Hannes Reinecke wrote:
> On 7/17/21 8:16 AM, Sagi Grimberg wrote:
> >
> > > Add RFC4648-compliant base64 encoding and decoding routines.
> >
> > Looks good to me (although didn't look in the logic itself).
> > Can you maybe mention where was this taken from?
>
> Umm ... yeah, I guess I can; I _think_ I've copied it from base64 routines
> in fs/crypto/fname.c, but I'll check.
>

Note that it wasn't simply a copy, as you changed the variant of base64 that is
implemented. So please make sure that you are very clear about which variant of
base64 it is, and update all the comments accordingly.

- Eric

2021-07-17 14:21:27

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding

On Fri, Jul 16, 2021 at 01:04:21PM +0200, Hannes Reinecke wrote:
> +/**
> + * 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 len, u8 *dst)
> +{
> + int i, bits = 0, pad = 0;
> + u32 ac = 0;
> + size_t dst_len = 0;
> +
> + for (i = 0; i < len; i++) {
> + int c, p = -1;
> +
> + if (src[i] == '=') {
> + pad++;
> + if (i + 1 < len && src[i + 1] == '=')
> + pad++;
> + break;
> + }
> + for (c = 0; c < strlen(lookup_table); c++) {
> + if (src[i] == lookup_table[c]) {
> + p = c;
> + break;
> + }
> + }
> + if (p < 0)
> + break;
> + ac = (ac << 6) | p;
> + bits += 6;
> + if (bits < 24)
> + continue;
> + while (bits) {
> + bits -= 8;
> + dst[dst_len++] = (ac >> bits) & 0xff;
> + }
> + ac = 0;
> + }
> + dst_len -= pad;
> + return dst_len;
> +}
> +EXPORT_SYMBOL_GPL(base64_decode);

This should return an error if the input isn't valid base64.

- Eric

2021-07-17 15:04:49

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters

Am Freitag, 16. Juli 2021, 13:04:20 CEST schrieb Hannes Reinecke:

Hi Hannes,

> +#include <linux/module.h>
> +#include <crypto/internal/kpp.h>
> +#include <crypto/kpp.h>
> +#include <crypto/dh.h>
> +#include <linux/mpi.h>
> +
> +/*
> + * ffdhe2048 generator (g), modulus (p) and group size (q)
> + */
> +const u8 ffdhe2048_g[] = { 0x02 };

What about using static const here (and for all the following groups)?

Ciao
Stephan


2021-07-17 16:50:29

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 07/11] nvme-auth: augmented challenge support

Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:

Hi Hannes,

> Implement support for augmented challenge using FFDHE groups.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
> 1 file changed, 371 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
> index 448a3adebea6..754343aced19 100644
> --- a/drivers/nvme/host/auth.c
> +++ b/drivers/nvme/host/auth.c
> @@ -8,6 +8,8 @@
> #include <asm/unaligned.h>
> #include <crypto/hash.h>
> #include <crypto/kpp.h>
> +#include <crypto/dh.h>
> +#include <crypto/ffdhe.h>
> #include "nvme.h"
> #include "fabrics.h"
> #include "auth.h"
> @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;
>
> struct nvme_dhchap_context {
> struct crypto_shash *shash_tfm;
> + struct crypto_shash *digest_tfm;
> + struct crypto_kpp *dh_tfm;
> unsigned char *key;
> size_t key_len;
> int qid;
> @@ -25,6 +29,8 @@ struct nvme_dhchap_context {
> u8 status;
> u8 hash_id;
> u8 hash_len;
> + u8 dhgroup_id;
> + u16 dhgroup_size;
> u8 c1[64];
> u8 c2[64];
> u8 response[64];
> @@ -36,6 +42,94 @@ struct nvme_dhchap_context {
> int sess_key_len;
> };
>
> +struct nvme_auth_dhgroup_map {
> + int id;
> + const char name[16];
> + const char kpp[16];
> + int privkey_size;
> + int pubkey_size;
> +} dhgroup_map[] = {
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
> + .name = "NULL", .kpp = "NULL",
> + .privkey_size = 0, .pubkey_size = 0 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_2048,
> + .name = "ffdhe2048", .kpp = "dh",
> + .privkey_size = 256, .pubkey_size = 256 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_3072,
> + .name = "ffdhe3072", .kpp = "dh",
> + .privkey_size = 384, .pubkey_size = 384 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_4096,
> + .name = "ffdhe4096", .kpp = "dh",
> + .privkey_size = 512, .pubkey_size = 512 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_6144,
> + .name = "ffdhe6144", .kpp = "dh",
> + .privkey_size = 768, .pubkey_size = 768 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
> + .name = "ffdhe8192", .kpp = "dh",
> + .privkey_size = 1024, .pubkey_size = 1024 },
> +};
> +
> +const char *nvme_auth_dhgroup_name(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].name;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
> +
> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].pubkey_size;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_pubkey_size);
> +
> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].privkey_size;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_privkey_size);
> +
> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].kpp;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
> +
> +int nvme_auth_dhgroup_id(const char *dhgroup_name)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (!strncmp(dhgroup_map[i].name, dhgroup_name,
> + strlen(dhgroup_map[i].name)))
> + return dhgroup_map[i].id;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
> +
> struct nvmet_dhchap_hash_map {
> int id;
> int hash_len;
> @@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl
> *ctrl, data->napd = 1;
> data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
> data->auth_protocol[0].dhchap.halen = 3;
> - data->auth_protocol[0].dhchap.dhlen = 1;
> + data->auth_protocol[0].dhchap.dhlen = 6;
> data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
> data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
> data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
> data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
> + data->auth_protocol[0].dhchap.idlist[4] = NVME_AUTH_DHCHAP_DHGROUP_2048;
> + data->auth_protocol[0].dhchap.idlist[5] = NVME_AUTH_DHCHAP_DHGROUP_3072;
> + data->auth_protocol[0].dhchap.idlist[6] = NVME_AUTH_DHCHAP_DHGROUP_4096;
> + data->auth_protocol[0].dhchap.idlist[7] = NVME_AUTH_DHCHAP_DHGROUP_6144;
> + data->auth_protocol[0].dhchap.idlist[8] = NVME_AUTH_DHCHAP_DHGROUP_8192;
>
> return size;
> }
> @@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl
> *ctrl, chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> return -EPROTO;
> }
> - switch (data->dhgid) {
> - case NVME_AUTH_DHCHAP_DHGROUP_NULL:
> - gid_name = "null";
> - break;
> - default:
> - gid_name = NULL;
> - break;
> - }
> + gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
> if (!gid_name) {
> dev_warn(ctrl->device,
> "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
> @@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl
> *ctrl, return -EPROTO;
> }
> if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> - chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> - return -EPROTO;
> - }
> - if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
> + if (data->dhvlen == 0) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: empty DH value\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
> + if (IS_ERR(chap->dh_tfm)) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
> + chap->qid, gid_name);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + chap->dh_tfm = NULL;
> + return -EPROTO;
> + }
> + chap->dhgroup_id = data->dhgid;
> + } else if (data->dhvlen != 0) {
> dev_warn(ctrl->device,
> "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
> chap->qid);
> @@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl
> *ctrl, chap->hash_len = data->hl;
> chap->s1 = le32_to_cpu(data->seqnum);
> memcpy(chap->c1, data->cval, chap->hash_len);
> + if (data->dhvlen) {
> + chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL);
> + if (!chap->ctrl_key)
> + return -ENOMEM;
> + chap->ctrl_key_len = data->dhvlen;
> + memcpy(chap->ctrl_key, data->cval + chap->hash_len,
> + data->dhvlen);
> + dev_dbg(ctrl->device, "ctrl public key %*ph\n",
> + (int)chap->ctrl_key_len, chap->ctrl_key);
> + }
>
> return 0;
> }
> @@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl
> *ctrl, memcpy(data->rval + chap->hash_len, chap->c2,
> chap->hash_len);
> }
> - if (chap->host_key_len)
> + if (chap->host_key_len) {
> + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
> + __func__, chap->qid,
> + chap->host_key_len, chap->host_key);
> memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
> chap->host_key_len);
> -
> + }
> return size;
> }
>
> @@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl
> *ctrl, int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> struct nvme_dhchap_context *chap)
> {
> - char *hash_name;
> + const char *hash_name, *digest_name;
> int ret;
>
> - switch (chap->hash_id) {
> - case NVME_AUTH_DHCHAP_HASH_SHA256:
> - hash_name = "hmac(sha256)";
> - break;
> - case NVME_AUTH_DHCHAP_HASH_SHA384:
> - hash_name = "hmac(sha384)";
> - break;
> - case NVME_AUTH_DHCHAP_HASH_SHA512:
> - hash_name = "hmac(sha512)";
> - break;
> - default:
> - hash_name = NULL;
> - break;
> - }
> + hash_name = nvme_auth_hmac_name(chap->hash_id);
> if (!hash_name) {
> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> return -EPROTO;
> @@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> chap->shash_tfm = NULL;
> return -EPROTO;
> }
> + digest_name = nvme_auth_digest_name(chap->hash_id);
> + if (!digest_name) {
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + return -EPROTO;
> + }
> + chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0);
> + if (IS_ERR(chap->digest_tfm)) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + chap->digest_tfm = NULL;
> + return -EPROTO;
> + }
> if (!chap->key) {
> dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
> chap->qid);
> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->digest_tfm);
> crypto_free_shash(chap->shash_tfm);
> chap->shash_tfm = NULL;
> + chap->digest_tfm = NULL;
> return -EINVAL;
> }
> ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
> if (ret) {
> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->digest_tfm);
> crypto_free_shash(chap->shash_tfm);
> chap->shash_tfm = NULL;
> + chap->digest_tfm = NULL;
> return ret;
> }
> - dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> - chap->qid, hash_name);
> + dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> + chap->qid, hash_name);
> return 0;
> }
>
> +static int nvme_auth_augmented_challenge(struct nvme_dhchap_context *chap,
> + u8 *challenge, u8 *aug)
> +{
> + struct crypto_shash *tfm;
> + struct shash_desc *desc;
> + u8 *hashed_key;
> + const char *hash_name;
> + int ret;
> +
> + hashed_key = kmalloc(chap->hash_len, GFP_KERNEL);
> + if (!hashed_key)
> + return -ENOMEM;
> +
> + ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key,
> + chap->sess_key_len, hashed_key);
> + if (ret < 0) {
> + pr_debug("failed to hash session key, err %d\n", ret);
> + kfree(hashed_key);
> + return ret;
> + }
> + hash_name = crypto_shash_alg_name(chap->shash_tfm);
> + if (!hash_name) {
> + pr_debug("Invalid hash algoritm\n");
> + return -EINVAL;
> + }
> + tfm = crypto_alloc_shash(hash_name, 0, 0);
> + if (IS_ERR(tfm)) {
> + ret = PTR_ERR(tfm);
> + goto out_free_key;
> + }
> + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
> + GFP_KERNEL);
> + if (!desc) {
> + ret = -ENOMEM;
> + goto out_free_hash;
> + }
> + desc->tfm = tfm;
> +
> + ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len);
> + if (ret)
> + goto out_free_desc;
> + ret = crypto_shash_init(desc);
> + if (ret)
> + goto out_free_desc;
> + crypto_shash_update(desc, challenge, chap->hash_len);
> + crypto_shash_final(desc, aug);
> +
> +out_free_desc:
> + kfree_sensitive(desc);
> +out_free_hash:
> + crypto_free_shash(tfm);
> +out_free_key:
> + kfree(hashed_key);
> + return ret;
> +}
> +
> static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
> struct nvme_dhchap_context *chap)
> {
> @@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct
> nvme_ctrl *ctrl,
>
> dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
> __func__, chap->qid, chap->s1, chap->transaction);
> + if (chap->dh_tfm) {
> + challenge = kmalloc(chap->hash_len, GFP_KERNEL);

Again, alignment?

> + if (!challenge) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + ret = nvme_auth_augmented_challenge(chap, chap->c1, challenge);
> + if (ret)
> + goto out;
> + }
> shash->tfm = chap->shash_tfm;
> ret = crypto_shash_init(shash);
> if (ret)
> @@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct
> nvme_ctrl *ctrl, goto out;
> ret = crypto_shash_final(shash, chap->response);
> out:
> + if (challenge != chap->c1)
> + kfree(challenge);
> return ret;
> }
>
> @@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(struct
> nvme_ctrl *ctrl, u8 buf[4], *challenge = chap->c2;
> int ret;
>
> + if (chap->dh_tfm) {
> + challenge = kmalloc(chap->hash_len, GFP_KERNEL);

dto.

> + if (!challenge) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + ret = nvme_auth_augmented_challenge(chap, chap->c2,
> + challenge);
> + if (ret)
> + goto out;
> + }
> dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
> __func__, chap->qid, chap->s2, chap->transaction);
> dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
> @@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct
> nvme_ctrl *ctrl, goto out;
> ret = crypto_shash_final(shash, chap->response);
> out:
> + if (challenge != chap->c2)
> + kfree(challenge);
> return ret;
> }
>
> @@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
> return 0;
> }
>
> +static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + struct kpp_request *req;
> + struct crypto_wait wait;
> + struct scatterlist src, dst;
> + u8 *pkey;
> + int ret, pkey_len;
> +
> + if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_8192) {
> + struct dh p = {0};
> + int pubkey_size = nvme_auth_dhgroup_pubkey_size(chap->dhgroup_id);
> +
> + ret = crypto_ffdhe_params(&p, pubkey_size << 3);
> + if (ret) {
> + dev_dbg(ctrl->device,
> + "failed to generate ffdhe params, error %d\n",
> + ret);
> + return ret;
> + }
> + p.key = chap->key;
> + p.key_size = chap->key_len;
> +
> + pkey_len = crypto_dh_key_len(&p);
> + pkey = kzalloc(pkey_len, GFP_KERNEL);
> +
> + get_random_bytes(pkey, pkey_len);
> + ret = crypto_dh_encode_key(pkey, pkey_len, &p);
> + if (ret) {
> + dev_dbg(ctrl->device,
> + "failed to encode pkey, error %d\n", ret);
> + kfree(pkey);
> + return ret;
> + }
> + chap->host_key_len = pubkey_size;
> + chap->sess_key_len = pubkey_size;
> + } else {
> + dev_warn(ctrl->device, "Invalid DH group id %d\n",
> + chap->dhgroup_id);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -EINVAL;
> + }
> +
> + ret = crypto_kpp_set_secret(chap->dh_tfm, pkey, pkey_len);
> + if (ret) {
> + dev_dbg(ctrl->dev, "failed to set secret, error %d\n", ret);
> + kfree(pkey);
> + return ret;
> + }
> + req = kpp_request_alloc(chap->dh_tfm, GFP_KERNEL);
> + if (!req) {
> + ret = -ENOMEM;
> + goto out_free_exp;
> + }
> +
> + chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL);
> + if (!chap->host_key) {
> + ret = -ENOMEM;
> + goto out_free_req;
> + }
> + crypto_init_wait(&wait);
> + kpp_request_set_input(req, NULL, 0);
> + sg_init_one(&dst, chap->host_key, chap->host_key_len);
> + kpp_request_set_output(req, &dst, chap->host_key_len);
> + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
> + if (ret == -EOVERFLOW) {
> + dev_dbg(ctrl->dev,
> + "public key buffer too small, wants %d is %d\n",
> + crypto_kpp_maxsize(chap->dh_tfm), chap->host_key_len);
> + goto out_free_host;
> + } else if (ret) {
> + dev_dbg(ctrl->dev,
> + "failed to generate public key, error %d\n", ret);
> + goto out_free_host;
> + }
> +
> + chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL);
> + if (!chap->sess_key)
> + goto out_free_host;
> +
> + crypto_init_wait(&wait);
> + sg_init_one(&src, chap->ctrl_key, chap->ctrl_key_len);
> + kpp_request_set_input(req, &src, chap->ctrl_key_len);
> + sg_init_one(&dst, chap->sess_key, chap->sess_key_len);
> + kpp_request_set_output(req, &dst, chap->sess_key_len);
> + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait);
> + if (ret) {
> + dev_dbg(ctrl->dev,
> + "failed to generate shared secret, error %d\n", ret);
> + kfree_sensitive(chap->sess_key);
> + chap->sess_key = NULL;
> + chap->sess_key_len = 0;
> + } else
> + dev_dbg(ctrl->dev, "shared secret %*ph\n",
> + (int)chap->sess_key_len, chap->sess_key);
> +out_free_host:
> + if (ret) {
> + kfree(chap->host_key);
> + chap->host_key = NULL;
> + chap->host_key_len = 0;
> + }
> +out_free_req:
> + kpp_request_free(req);
> +out_free_exp:
> + kfree_sensitive(pkey);
> + if (ret)
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return ret;
> +}
> +
> void nvme_auth_free(struct nvme_dhchap_context *chap)
> {
> if (chap->shash_tfm)
> crypto_free_shash(chap->shash_tfm);
> + if (chap->digest_tfm)
> + crypto_free_shash(chap->digest_tfm);
> + if (chap->dh_tfm)
> + crypto_free_kpp(chap->dh_tfm);
> if (chap->key)
> kfree(chap->key);
> if (chap->ctrl_key)
> @@ -732,6 +1061,15 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int
> qid) if (ret)
> goto fail2;
>
> + if (chap->ctrl_key_len) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-DHAP DH exponential\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_exponential(ctrl, chap);
> + if (ret)
> + goto fail2;
> + }
> +
> dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
> __func__, qid);
> ret = nvme_auth_dhchap_host_response(ctrl, chap);
> @@ -806,6 +1144,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int
> qid) ret = -EPROTO;
> if (!ret) {
> ctrl->dhchap_hash = chap->hash_id;
> + ctrl->dhchap_dhgroup = chap->dhgroup_id;
> }
> kfree(buf);
> nvme_auth_free(chap);


Ciao
Stephan


2021-07-17 16:50:29

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

Am Freitag, 16. Juli 2021, 13:04:23 CEST schrieb Hannes Reinecke:

Hi Hannes,

> Implement NVMe-oF In-Band authentication. This patch adds two new
> fabric options 'dhchap_key' to specify the PSK and 'dhchap_authenticate'
> to request bi-directional authentication of both the host and the
> controller.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/host/Kconfig | 11 +
> drivers/nvme/host/Makefile | 1 +
> drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++
> drivers/nvme/host/auth.h | 23 +
> drivers/nvme/host/core.c | 77 +++-
> drivers/nvme/host/fabrics.c | 65 ++-
> drivers/nvme/host/fabrics.h | 8 +
> drivers/nvme/host/nvme.h | 15 +
> drivers/nvme/host/trace.c | 32 ++
> 9 files changed, 1041 insertions(+), 4 deletions(-)
> create mode 100644 drivers/nvme/host/auth.c
> create mode 100644 drivers/nvme/host/auth.h
>
> diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
> index c3f3d77f1aac..853c546305e9 100644
> --- a/drivers/nvme/host/Kconfig
> +++ b/drivers/nvme/host/Kconfig
> @@ -85,3 +85,14 @@ config NVME_TCP
> from https://github.com/linux-nvme/nvme-cli.
>
> If unsure, say N.
> +
> +config NVME_AUTH
> + bool "NVM Express over Fabrics In-Band Authentication"
> + depends on NVME_TCP
> + select CRYPTO_SHA256
> + select CRYPTO_SHA512

What about adding CRYPTO_HMAC here?

> + help
> + This provides support for NVMe over Fabrics In-Band Authentication
> + for the NVMe over TCP transport.
> +
> + If unsure, say N.
> diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
> index cbc509784b2e..03748a55a12b 100644
> --- a/drivers/nvme/host/Makefile
> +++ b/drivers/nvme/host/Makefile
> @@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o
> nvme-y += pci.o
>
> nvme-fabrics-y += fabrics.o
> +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o
>
> nvme-rdma-y += rdma.o
>
> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
> new file mode 100644
> index 000000000000..448a3adebea6
> --- /dev/null
> +++ b/drivers/nvme/host/auth.c
> @@ -0,0 +1,813 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
> + */
> +
> +#include <linux/crc32.h>
> +#include <linux/base64.h>
> +#include <asm/unaligned.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include "nvme.h"
> +#include "fabrics.h"
> +#include "auth.h"
> +
> +static u32 nvme_dhchap_seqnum;
> +
> +struct nvme_dhchap_context {
> + struct crypto_shash *shash_tfm;
> + unsigned char *key;
> + size_t key_len;
> + int qid;
> + u32 s1;
> + u32 s2;
> + u16 transaction;
> + u8 status;
> + u8 hash_id;
> + u8 hash_len;
> + u8 c1[64];
> + u8 c2[64];
> + u8 response[64];
> + u8 *ctrl_key;
> + int ctrl_key_len;
> + u8 *host_key;
> + int host_key_len;
> + u8 *sess_key;
> + int sess_key_len;
> +};
> +
> +struct nvmet_dhchap_hash_map {
> + int id;
> + int hash_len;
> + const char hmac[15];
> + const char digest[15];
> +} hash_map[] = {
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
> + .hash_len = 32,
> + .hmac = "hmac(sha256)", .digest = "sha256" },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
> + .hash_len = 48,
> + .hmac = "hmac(sha384)", .digest = "sha384" },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
> + .hash_len = 64,
> + .hmac = "hmac(sha512)", .digest = "sha512" },
> +};
> +
> +const char *nvme_auth_hmac_name(int hmac_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == hmac_id)
> + return hash_map[i].hmac;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
> +
> +const char *nvme_auth_digest_name(int hmac_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == hmac_id)
> + return hash_map[i].digest;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
> +
> +int nvme_auth_hmac_len(int hmac_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == hmac_id)
> + return hash_map[i].hash_len;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_len);
> +
> +int nvme_auth_hmac_id(const char *hmac_name)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (!strncmp(hash_map[i].hmac, hmac_name,
> + strlen(hash_map[i].hmac)))
> + return hash_map[i].id;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
> +
> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
> + size_t *dhchap_key_len)
> +{
> + unsigned char *dhchap_key;
> + u32 crc;
> + int key_len;
> + size_t allocated_len;
> +
> + allocated_len = strlen(dhchap_secret) - 10;

Are you sure that the string is always at least 10 bytes long? If so, can you
please add a comment to it?

Also, is it guaranteed that we have an ASCII string? Note, a secret sounds to
be like a binary string which may contain \0 as an appropriate value.

> + dhchap_key = kzalloc(allocated_len, GFP_KERNEL);

What about aligning it to CRYPTO_MINALIGN_ATTR to save a memcpy in
shash_final?

> + if (!dhchap_key)
> + return ERR_PTR(-ENOMEM);
> +
> + key_len = base64_decode(dhchap_secret + 10,
> + allocated_len, dhchap_key);
> + if (key_len != 36 && key_len != 52 &&
> + key_len != 68) {
> + pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
> + key_len);
> + kfree(dhchap_key);
> + return ERR_PTR(-EINVAL);
> + }
> + pr_debug("DH-HMAC-CHAP Key: %*ph\n",
> + (int)key_len, dhchap_key);
> +
> + /* The last four bytes is the CRC in little-endian format */
> + key_len -= 4;
> + /*
> + * The linux implementation doesn't do pre- and post-increments,
> + * so we have to do it manually.
> + */
> + crc = ~crc32(~0, dhchap_key, key_len);
> +
> + if (get_unaligned_le32(dhchap_key + key_len) != crc) {
> + pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n",
> + get_unaligned_le32(dhchap_key + key_len), crc);
> + kfree(dhchap_key);
> + return ERR_PTR(-EKEYREJECTED);
> + }
> + *dhchap_key_len = key_len;
> + return dhchap_key;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
> +
> +static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
> + void *data, size_t tl)
> +{
> + struct nvme_command cmd = {};
> + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
> + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
> + struct request_queue *q = qid == NVME_QID_ANY ?
> + ctrl->fabrics_q : ctrl->connect_q;
> + int ret;
> +
> + cmd.auth_send.opcode = nvme_fabrics_command;
> + cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
> + cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
> + cmd.auth_send.spsp0 = 0x01;
> + cmd.auth_send.spsp1 = 0x01;
> + cmd.auth_send.tl = tl;
> +
> + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid,
> + 0, flags);
> + if (ret)
> + dev_dbg(ctrl->device,
> + "%s: qid %d error %d\n", __func__, qid, ret);
> + return ret;
> +}
> +
> +static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
> + void *buf, size_t al,
> + u16 transaction, u8 expected_msg )
> +{
> + struct nvme_command cmd = {};
> + struct nvmf_auth_dhchap_failure_data *data = buf;
> + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
> + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
> + struct request_queue *q = qid == NVME_QID_ANY ?
> + ctrl->fabrics_q : ctrl->connect_q;
> + int ret;
> +
> + cmd.auth_receive.opcode = nvme_fabrics_command;
> + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
> + cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
> + cmd.auth_receive.spsp0 = 0x01;
> + cmd.auth_receive.spsp1 = 0x01;
> + cmd.auth_receive.al = al;
> +
> + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid,
> + 0, flags);
> + if (ret > 0) {
> + dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n",
> + __func__, qid, ret);
> + ret = -EIO;
> + }
> + if (ret < 0) {
> + dev_dbg(ctrl->device, "%s: qid %d error %d\n",
> + __func__, qid, ret);
> + return ret;
> + }
> + dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
> + __func__, qid, data->auth_type, data->auth_id);
> + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
> + data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
> + return data->reason_code_explanation;
> + }
> + if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
> + data->auth_id != expected_msg) {
> + dev_warn(ctrl->device,
> + "qid %d invalid message %02x/%02x\n",
> + qid, data->auth_type, data->auth_id);
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + }
> + if (le16_to_cpu(data->t_id) != transaction) {
> + dev_warn(ctrl->device,
> + "qid %d invalid transaction ID %d\n",
> + qid, le16_to_cpu(data->t_id));
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + }
> +
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)
> +{
> + struct nvmf_auth_dhchap_negotiate_data *data = buf;
> + size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
> +
> + if (buf_size < size)
> + return -EINVAL;
> +
> + memset((u8 *)buf, 0, size);
> + data->auth_type = NVME_AUTH_COMMON_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
> + data->t_id = cpu_to_le16(chap->transaction);
> + data->sc_c = 0; /* No secure channel concatenation */
> + data->napd = 1;
> + data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
> + data->auth_protocol[0].dhchap.halen = 3;
> + data->auth_protocol[0].dhchap.dhlen = 1;
> + data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
> + data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
> + data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
> + data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
> +
> + return size;
> +}
> +
> +static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)
> +{
> + struct nvmf_auth_dhchap_challenge_data *data = buf;
> + size_t size = sizeof(*data) + data->hl + data->dhvlen;
> + const char *gid_name;
> +
> + if (buf_size < size) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -ENOMSG;
> + }
> +
> + if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 &&
> + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 &&
> + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n",
> + chap->qid, data->hashid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + return -EPROTO;
> + }
> + switch (data->dhgid) {
> + case NVME_AUTH_DHCHAP_DHGROUP_NULL:
> + gid_name = "null";
> + break;
> + default:
> + gid_name = NULL;
> + break;
> + }
> + if (!gid_name) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
> + chap->qid, data->dhgid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n",
> + __func__, chap->qid, data->hashid);
> + if (nvme_auth_hmac_len(data->hashid) != data->hl) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid hash length\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + return -EPROTO;
> + }
> + chap->hash_id = data->hashid;
> + chap->hash_len = data->hl;
> + chap->s1 = le32_to_cpu(data->seqnum);
> + memcpy(chap->c1, data->cval, chap->hash_len);
> +
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)
> +{
> + struct nvmf_auth_dhchap_reply_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + size += 2 * chap->hash_len;
> + if (ctrl->opts->dhchap_auth) {
> + get_random_bytes(chap->c2, chap->hash_len);

Why are you using CRYPTO_RNG_DEFAULT when you are using get_random_bytes here?

> + chap->s2 = nvme_dhchap_seqnum++;
> + } else
> + memset(chap->c2, 0, chap->hash_len);
> +
> + if (chap->host_key_len)
> + size += chap->host_key_len;
> +
> + if (buf_size < size)
> + return -EINVAL;
> +
> + memset(buf, 0, size);
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
> + data->t_id = cpu_to_le16(chap->transaction);
> + data->hl = chap->hash_len;
> + data->dhvlen = chap->host_key_len;
> + data->seqnum = cpu_to_le32(chap->s2);
> + memcpy(data->rval, chap->response, chap->hash_len);
> + if (ctrl->opts->dhchap_auth) {
> + dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
> + __func__, chap->qid,
> + chap->hash_len, chap->c2);
> + data->cvalid = 1;
> + memcpy(data->rval + chap->hash_len, chap->c2,
> + chap->hash_len);
> + }
> + if (chap->host_key_len)
> + memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
> + chap->host_key_len);
> +
> + return size;
> +}
> +
> +static int nvme_auth_dhchap_success1(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)
> +{
> + struct nvmf_auth_dhchap_success1_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + if (ctrl->opts->dhchap_auth)
> + size += chap->hash_len;
> +
> +
> + if (buf_size < size) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -ENOMSG;
> + }
> +
> + if (data->hl != chap->hash_len) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: invalid hash length %d\n",
> + chap->qid, data->hl);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + return -EPROTO;
> + }
> +
> + if (!data->rvalid)
> + return 0;
> +
> + /* Validate controller response */
> + if (memcmp(chap->response, data->rval, data->hl)) {
> + dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
> + __func__, chap->qid, chap->hash_len, data->rval);
> + dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
> + __func__, chap->qid, chap->hash_len, chap->response);
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: controller authentication failed\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -EPROTO;
> + }
> + dev_info(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: controller authenticated\n",
> + chap->qid);
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)
> +{
> + struct nvmf_auth_dhchap_success2_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + memset(buf, 0, size);
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
> + data->t_id = cpu_to_le16(chap->transaction);
> +
> + return size;
> +}
> +
> +static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap,
> + void *buf, size_t buf_size)
> +{
> + struct nvmf_auth_dhchap_failure_data *data = buf;
> + size_t size = sizeof(*data);
> +
> + memset(buf, 0, size);
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
> + data->t_id = cpu_to_le16(chap->transaction);
> + data->reason_code = 1;
> + data->reason_code_explanation = chap->status;
> +
> + return size;
> +}
> +
> +int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + char *hash_name;
> + int ret;
> +
> + switch (chap->hash_id) {
> + case NVME_AUTH_DHCHAP_HASH_SHA256:
> + hash_name = "hmac(sha256)";
> + break;
> + case NVME_AUTH_DHCHAP_HASH_SHA384:
> + hash_name = "hmac(sha384)";
> + break;
> + case NVME_AUTH_DHCHAP_HASH_SHA512:
> + hash_name = "hmac(sha512)";
> + break;
> + default:
> + hash_name = NULL;
> + break;
> + }
> + if (!hash_name) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + return -EPROTO;
> + }
> + chap->shash_tfm = crypto_alloc_shash(hash_name, 0,
> + CRYPTO_ALG_ALLOCATES_MEMORY);
> + if (IS_ERR(chap->shash_tfm)) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + chap->shash_tfm = NULL;
> + return -EPROTO;
> + }
> + if (!chap->key) {
> + dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + return -EINVAL;
> + }
> + ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
> + if (ret) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + return ret;
> + }
> + dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> + chap->qid, hash_name);
> + return 0;
> +}
> +
> +static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
> + u8 buf[4], *challenge = chap->c1;
> + int ret;
> +
> + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
> + __func__, chap->qid, chap->s1, chap->transaction);
> + shash->tfm = chap->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, chap->hash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(chap->s1, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(chap->transaction, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, sizeof(buf));
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "HostHost", 8);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
> + strlen(ctrl->opts->host->nqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
> + strlen(ctrl->opts->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, chap->response);
> +out:
> + return ret;
> +}
> +
> +static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
> + u8 buf[4], *challenge = chap->c2;
> + int ret;
> +
> + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
%d\n",
> + __func__, chap->qid, chap->s2, chap->transaction);
> + dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
> + __func__, chap->qid, chap->hash_len, challenge);
> + dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
> + __func__, chap->qid, ctrl->opts->subsysnqn);
> + dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
> + __func__, chap->qid, ctrl->opts->host->nqn);
> + shash->tfm = chap->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, chap->hash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(chap->s2, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(chap->transaction, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, 4);
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "Controller", 10);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
> + strlen(ctrl->opts->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
> + strlen(ctrl->opts->host->nqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, chap->response);
> +out:
> + return ret;
> +}
> +
> +int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + int ret;
> + u8 key_hash;
> + const char *hmac_name;
> + struct crypto_shash *key_tfm;
> +
> + if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
> + &key_hash) != 1)
> + return -EINVAL;
> +
> + chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
> + &chap->key_len);
> + if (IS_ERR(chap->key)) {
> + ret = PTR_ERR(chap->key);
> + chap->key = NULL;
> + return ret;
> + }
> +
> + if (key_hash == 0)
> + return 0;
> +
> + hmac_name = nvme_auth_hmac_name(key_hash);
> + if (!hmac_name) {
> + pr_debug("Invalid key hash id %d\n", key_hash);
> + return -EKEYREJECTED;
> + }
> +
> + key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
> + if (IS_ERR(key_tfm)) {
> + kfree(chap->key);
> + chap->key = NULL;
> + ret = PTR_ERR(key_tfm);
> + } else {
> + SHASH_DESC_ON_STACK(shash, key_tfm);
> +
> + shash->tfm = key_tfm;
> + ret = crypto_shash_setkey(key_tfm, chap->key,
> + chap->key_len);
> + if (ret < 0) {
> + crypto_free_shash(key_tfm);
> + kfree(chap->key);
> + chap->key = NULL;
> + return ret;
> + }
> + crypto_shash_init(shash);
> + crypto_shash_update(shash, ctrl->opts->host->nqn,
> + strlen(ctrl->opts->host->nqn));
> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
> + crypto_shash_final(shash, chap->key);
> + crypto_free_shash(key_tfm);
> + }
> + return 0;
> +}
> +
> +void nvme_auth_free(struct nvme_dhchap_context *chap)
> +{
> + if (chap->shash_tfm)
> + crypto_free_shash(chap->shash_tfm);
> + if (chap->key)
> + kfree(chap->key);
> + if (chap->ctrl_key)
> + kfree(chap->ctrl_key);
> + if (chap->host_key)
> + kfree(chap->host_key);
> + if (chap->sess_key)
> + kfree(chap->sess_key);
> + kfree(chap);

kfree_sensitive in all cases as all buffers have sensitive data?

> +}
> +
> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
> +{
> + struct nvme_dhchap_context *chap;
> + void *buf;
> + size_t buf_size, tl;
> + int ret = 0;
> +
> + chap = kzalloc(sizeof(*chap), GFP_KERNEL);

Suggestion: make sure that chap->response is aligned to CRYPTO_MINALIGN_ATTR -
then you would save a memcpy in crypto_shash_final

> + if (!chap)
> + return -ENOMEM;
> + chap->qid = qid;
> + chap->transaction = ctrl->transaction++;
> +
> + ret = nvme_auth_generate_key(ctrl, chap);
> + if (ret) {
> + dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n",
> + __func__, ret);
> + nvme_auth_free(chap);
> + return ret;
> + }
> +
> + /*
> + * Allocate a large enough buffer for the entire negotiation:
> + * 4k should be enough to ffdhe8192.
> + */
> + buf_size = 4096;
> + buf = kzalloc(buf_size, GFP_KERNEL);
> + if (!buf) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + /* DH-HMAC-CHAP Step 1: send negotiate */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size);
> + if (ret < 0)
> + goto out;
> + tl = ret;
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> + if (ret)
> + goto out;
> +
> + memset(buf, 0, buf_size);
> + ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
> + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
> + if (ret < 0) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n",
> + __func__, qid);
> + goto out;
> + }
> + if (ret > 0) {
> + chap->status = ret;
> + goto fail1;
> + }
> +
> + /* DH-HMAC-CHAP Step 2: receive challenge */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n",
> + __func__, qid);
> +
> + ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size);
> + if (ret) {
> + /* Invalid parameters for negotiate */
> + goto fail2;
> + }
> +
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n",
> + __func__, qid);
> + ret = nvme_auth_select_hash(ctrl, chap);
> + if (ret)
> + goto fail2;
> +
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_host_response(ctrl, chap);
> + if (ret)
> + goto fail2;
> +
> + /* DH-HMAC-CHAP Step 3: send reply */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size);
> + if (ret < 0)
> + goto fail2;
> +
> + tl = ret;
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> + if (ret)
> + goto fail2;
> +
> + memset(buf, 0, buf_size);
> + ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
> + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
> + if (ret < 0) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-CHAP failed to receive success1\n",
> + __func__, qid);
> + goto out;
> + }
> + if (ret > 0) {
> + chap->status = ret;
> + goto fail1;
> + }
> +
> + if (ctrl->opts->dhchap_auth) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-CHAP controller response\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
> + if (ret)
> + goto fail2;
> + }
> +
> + /* DH-HMAC-CHAP Step 4: receive success1 */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size);
> + if (ret < 0) {
> + /* Controller authentication failed */
> + goto fail2;
> + }
> + tl = ret;
> + /* DH-HMAC-CHAP Step 5: send success2 */
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n",
> + __func__, qid);
> + tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size);
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> + if (!ret)
> + goto out;
> +
> +fail1:
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status %x\n",
> + __func__, qid, chap->status);
> + goto out;
> +
> +fail2:
> + dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status %x\n",
> + __func__, qid, chap->status);
> + tl = nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size);
> + ret = nvme_auth_send(ctrl, qid, buf, tl);
> +
> +out:
> + if (!ret && chap->status)
> + ret = -EPROTO;
> + if (!ret) {
> + ctrl->dhchap_hash = chap->hash_id;
> + }
> + kfree(buf);
> + nvme_auth_free(chap);
> + return ret;
> +}
> diff --git a/drivers/nvme/host/auth.h b/drivers/nvme/host/auth.h
> new file mode 100644
> index 000000000000..4950b1cb9470
> --- /dev/null
> +++ b/drivers/nvme/host/auth.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
> + */
> +
> +#ifndef _NVME_AUTH_H
> +#define _NVME_AUTH_H
> +
> +const char *nvme_auth_dhgroup_name(int dhgroup_id);
> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id);
> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id);
> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id);
> +int nvme_auth_dhgroup_id(const char *dhgroup_name);
> +
> +const char *nvme_auth_hmac_name(int hmac_id);
> +const char *nvme_auth_digest_name(int hmac_id);
> +int nvme_auth_hmac_id(const char *hmac_name);
> +int nvme_auth_hmac_len(int hmac_len);
> +
> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
> + size_t *dhchap_key_len);
> +
> +#endif /* _NVME_AUTH_H */
> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
> index 11779be42186..7ce9b666dc09 100644
> --- a/drivers/nvme/host/core.c
> +++ b/drivers/nvme/host/core.c
> @@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct
> request *rq, switch (ctrl->state) {
> case NVME_CTRL_CONNECTING:
> if (blk_rq_is_passthrough(rq) && nvme_is_fabrics(req->cmd)
&&
> - req->cmd->fabrics.fctype == nvme_fabrics_type_connect)
> + (req->cmd->fabrics.fctype == nvme_fabrics_type_connect
||
> + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_send
||
> + req->cmd->fabrics.fctype ==
nvme_fabrics_type_auth_receive))
> return true;
> break;
> default:
> @@ -3426,6 +3428,66 @@ static ssize_t
> nvme_ctrl_fast_io_fail_tmo_store(struct device *dev, static
> DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
> nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store);
>
> +#ifdef CONFIG_NVME_AUTH
> +struct nvmet_dhchap_hash_map {
> + int id;
> + const char name[15];
> +} hash_map[] = {
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
> + .name = "hmac(sha256)", },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
> + .name = "hmac(sha384)", },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
> + .name = "hmac(sha512)", },
> +};
> +
> +static ssize_t dhchap_hash_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
> + if (hash_map[i].id == ctrl->dhchap_hash)
> + return sprintf(buf, "%s\n", hash_map[i].name);
> + }
> + return sprintf(buf, "none\n");
> +}
> +DEVICE_ATTR_RO(dhchap_hash);
> +
> +struct nvmet_dhchap_group_map {
> + int id;
> + const char name[15];
> +} dhgroup_map[] = {
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
> + .name = "NULL", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_2048,
> + .name = "ffdhe2048", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_3072,
> + .name = "ffdhe3072", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_4096,
> + .name = "ffdhe4096", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_6144,
> + .name = "ffdhe6144", },
> + {.id = NVME_AUTH_DHCHAP_DHGROUP_8192,
> + .name = "ffdhe8192", },
> +};
> +
> +static ssize_t dhchap_dhgroup_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (hash_map[i].id == ctrl->dhchap_dhgroup)
> + return sprintf(buf, "%s\n", dhgroup_map[i].name);
> + }
> + return sprintf(buf, "none\n");
> +}
> +DEVICE_ATTR_RO(dhchap_dhgroup);
> +#endif
> +
> static struct attribute *nvme_dev_attrs[] = {
> &dev_attr_reset_controller.attr,
> &dev_attr_rescan_controller.attr,
> @@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = {
> &dev_attr_reconnect_delay.attr,
> &dev_attr_fast_io_fail_tmo.attr,
> &dev_attr_kato.attr,
> +#ifdef CONFIG_NVME_AUTH
> + &dev_attr_dhchap_hash.attr,
> + &dev_attr_dhchap_dhgroup.attr,
> +#endif
> NULL
> };
>
> @@ -3470,6 +3536,10 @@ static umode_t nvme_dev_attrs_are_visible(struct
> kobject *kobj, return 0;
> if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
> return 0;
> +#ifdef CONFIG_NVME_AUTH
> + if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts)
> + return 0;
> +#endif
>
> return a->mode;
> }
> @@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)
> BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
> BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
> BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16);
> + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16);
> }
>
>
> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
> index a5469fd9d4c3..6404ab9b604b 100644
> --- a/drivers/nvme/host/fabrics.c
> +++ b/drivers/nvme/host/fabrics.c
> @@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
> union nvme_result res;
> struct nvmf_connect_data *data;
> int ret;
> + u32 result;
>
> cmd.connect.opcode = nvme_fabrics_command;
> cmd.connect.fctype = nvme_fabrics_type_connect;
> @@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
> goto out_free_data;
> }
>
> - ctrl->cntlid = le16_to_cpu(res.u16);
> -
> + result = le32_to_cpu(res.u32);
> + ctrl->cntlid = result & 0xFFFF;
> + if ((result >> 16) & 2) {
> + /* Authentication required */
> + ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
> + if (ret)
> + dev_warn(ctrl->device,
> + "qid 0: authentication failed\n");
> + else
> + dev_info(ctrl->device,
> + "qid 0: authenticated\n");
> + }
> out_free_data:
> kfree(data);
> return ret;
> @@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16
> qid) struct nvmf_connect_data *data;
> union nvme_result res;
> int ret;
> + u32 result;
>
> cmd.connect.opcode = nvme_fabrics_command;
> cmd.connect.fctype = nvme_fabrics_type_connect;
> @@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16
> qid) nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32),
> &cmd, data);
> }
> + result = le32_to_cpu(res.u32);
> + if ((result >> 16) & 2) {
> + /* Authentication required */
> + ret = nvme_auth_negotiate(ctrl, qid);
> + if (ret)
> + dev_warn(ctrl->device,
> + "qid %u: authentication failed\n", qid);
> + else
> + dev_info(ctrl->device,
> + "qid %u: authenticated\n", qid);
> + }
> kfree(data);
> return ret;
> }
> @@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
> { NVMF_OPT_NR_POLL_QUEUES, "nr_poll_queues=%d" },
> { NVMF_OPT_TOS, "tos=%d" },
> { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" },
> + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" },
> + { NVMF_OPT_DHCHAP_AUTH, "authenticate" },
> + { NVMF_OPT_DHCHAP_GROUP, "dhchap_group=%s" },
> { NVMF_OPT_ERR, NULL }
> };
>
> @@ -824,6 +850,35 @@ static int nvmf_parse_options(struct nvmf_ctrl_options
> *opts, }
> opts->tos = token;
> break;
> + case NVMF_OPT_DHCHAP_SECRET:
> + p = match_strdup(args);
> + if (!p) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + if (strncmp(p, "DHHC-1:00:", 10)) {
> + pr_err("Invalid DH-CHAP secret %s\n", p);
> + ret = -EINVAL;
> + goto out;
> + }
> + kfree(opts->dhchap_secret);
> + opts->dhchap_secret = p;
> + break;
> + case NVMF_OPT_DHCHAP_AUTH:
> + opts->dhchap_auth = true;
> + break;
> + case NVMF_OPT_DHCHAP_GROUP:
> + if (match_int(args, &token)) {
> + ret = -EINVAL;
> + goto out;
> + }
> + if (token <= 0) {
> + pr_err("Invalid dhchap_group %d\n", token);
> + ret = -EINVAL;
> + goto out;
> + }
> + opts->dhchap_group = token;
> + break;
> default:
> pr_warn("unknown parameter or missing value '%s' in ctrl
creation
> request\n", p);
> @@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts)
> kfree(opts->subsysnqn);
> kfree(opts->host_traddr);
> kfree(opts->host_iface);
> + kfree(opts->dhchap_secret);
> kfree(opts);
> }
> EXPORT_SYMBOL_GPL(nvmf_free_options);
> @@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options);
> NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \
> NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
> NVMF_OPT_DISABLE_SQFLOW |\
> - NVMF_OPT_FAIL_FAST_TMO)
> + NVMF_OPT_CTRL_LOSS_TMO |\
> + NVMF_OPT_FAIL_FAST_TMO |\
> + NVMF_OPT_DHCHAP_SECRET |\
> + NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)
>
> static struct nvme_ctrl *
> nvmf_create_ctrl(struct device *dev, const char *buf)
> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
> index a146cb903869..535bc544f0f6 100644
> --- a/drivers/nvme/host/fabrics.h
> +++ b/drivers/nvme/host/fabrics.h
> @@ -67,6 +67,9 @@ enum {
> NVMF_OPT_TOS = 1 << 19,
> NVMF_OPT_FAIL_FAST_TMO = 1 << 20,
> NVMF_OPT_HOST_IFACE = 1 << 21,
> + NVMF_OPT_DHCHAP_SECRET = 1 << 22,
> + NVMF_OPT_DHCHAP_AUTH = 1 << 23,
> + NVMF_OPT_DHCHAP_GROUP = 1 << 24,
> };
>
> /**
> @@ -96,6 +99,8 @@ enum {
> * @max_reconnects: maximum number of allowed reconnect attempts before
> removing * the controller, (-1) means reconnect forever, zero
> means remove * immediately;
> + * @dhchap_secret: DH-HMAC-CHAP secret
> + * @dhchap_auth: DH-HMAC-CHAP authenticate controller
> * @disable_sqflow: disable controller sq flow control
> * @hdr_digest: generate/verify header digest (TCP)
> * @data_digest: generate/verify data digest (TCP)
> @@ -120,6 +125,9 @@ struct nvmf_ctrl_options {
> unsigned int kato;
> struct nvmf_host *host;
> int max_reconnects;
> + char *dhchap_secret;
> + int dhchap_group;
> + bool dhchap_auth;
> bool disable_sqflow;
> bool hdr_digest;
> bool data_digest;
> diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
> index 18ef8dd03a90..bcd5b8276c26 100644
> --- a/drivers/nvme/host/nvme.h
> +++ b/drivers/nvme/host/nvme.h
> @@ -328,6 +328,12 @@ struct nvme_ctrl {
> struct work_struct ana_work;
> #endif
>
> +#ifdef CONFIG_NVME_AUTH
> + u16 transaction;
> + u8 dhchap_hash;
> + u8 dhchap_dhgroup;
> +#endif
> +
> /* Power saving configuration */
> u64 ps_max_latency_us;
> bool apst_enabled;
> @@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct
> nvme_ctrl *ctrl) return ctrl->sgls & ((1 << 0) | (1 << 1));
> }
>
> +#ifdef CONFIG_NVME_AUTH
> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
> +#else
> +static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
> +{
> + return -EPROTONOSUPPORT;
> +}
> +#endif
> +
> u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
> u8 opcode);
> int nvme_execute_passthru_rq(struct request *rq);
> diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c
> index 6543015b6121..66f75d8ea925 100644
> --- a/drivers/nvme/host/trace.c
> +++ b/drivers/nvme/host/trace.c
> @@ -271,6 +271,34 @@ static const char
> *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc) return ret;
> }
>
> +static const char *nvme_trace_fabrics_auth_send(struct trace_seq *p, u8
> *spc) +{
> + const char *ret = trace_seq_buffer_ptr(p);
> + u8 spsp0 = spc[1];
> + u8 spsp1 = spc[2];
> + u8 secp = spc[3];
> + u32 tl = get_unaligned_le32(spc + 4);
> +
> + trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u",
> + spsp0, spsp1, secp, tl);
> + trace_seq_putc(p, 0);
> + return ret;
> +}
> +
> +static const char *nvme_trace_fabrics_auth_receive(struct trace_seq *p, u8
> *spc) +{
> + const char *ret = trace_seq_buffer_ptr(p);
> + u8 spsp0 = spc[1];
> + u8 spsp1 = spc[2];
> + u8 secp = spc[3];
> + u32 al = get_unaligned_le32(spc + 4);
> +
> + trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, al=%u",
> + spsp0, spsp1, secp, al);
> + trace_seq_putc(p, 0);
> + return ret;
> +}
> +
> static const char *nvme_trace_fabrics_common(struct trace_seq *p, u8 *spc)
> {
> const char *ret = trace_seq_buffer_ptr(p);
> @@ -290,6 +318,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct
> trace_seq *p, return nvme_trace_fabrics_connect(p, spc);
> case nvme_fabrics_type_property_get:
> return nvme_trace_fabrics_property_get(p, spc);
> + case nvme_fabrics_type_auth_send:
> + return nvme_trace_fabrics_auth_send(p, spc);
> + case nvme_fabrics_type_auth_receive:
> + return nvme_trace_fabrics_auth_receive(p, spc);
> default:
> return nvme_trace_fabrics_common(p, spc);
> }


Ciao
Stephan


2021-07-17 16:50:57

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms

Am Freitag, 16. Juli 2021, 13:04:28 CEST schrieb Hannes Reinecke:

Hi Hannes,

> TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE

curve25519?

> groups, and these are already implemented in the kernel.
> So add support for these non-standard groups for NVMe in-band
> authentication to validate the augmented challenge implementation.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/host/auth.c | 38 +++++++++++++++++++++++++++++++++++++-
> drivers/nvme/target/auth.c | 23 +++++++++++++++++++++++
> include/linux/nvme.h | 2 ++
> 3 files changed, 62 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
> index 754343aced19..d0dd63b455ef 100644
> --- a/drivers/nvme/host/auth.c
> +++ b/drivers/nvme/host/auth.c
> @@ -10,6 +10,8 @@
> #include <crypto/kpp.h>
> #include <crypto/dh.h>
> #include <crypto/ffdhe.h>
> +#include <crypto/ecdh.h>
> +#include <crypto/curve25519.h>
> #include "nvme.h"
> #include "fabrics.h"
> #include "auth.h"
> @@ -67,6 +69,13 @@ struct nvme_auth_dhgroup_map {
> { .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
> .name = "ffdhe8192", .kpp = "dh",
> .privkey_size = 1024, .pubkey_size = 1024 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_ECDH,
> + .name = "ecdh", .kpp = "ecdh-nist-p256",
> + .privkey_size = 32, .pubkey_size = 64 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_25519,
> + .name = "curve25519", .kpp = "curve25519",
> + .privkey_size = CURVE25519_KEY_SIZE,
> + .pubkey_size = CURVE25519_KEY_SIZE },
> };
>
> const char *nvme_auth_dhgroup_name(int dhgroup_id)
> @@ -337,7 +346,7 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl
> *ctrl, data->napd = 1;
> data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
> data->auth_protocol[0].dhchap.halen = 3;
> - data->auth_protocol[0].dhchap.dhlen = 6;
> + data->auth_protocol[0].dhchap.dhlen = 8;
> data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
> data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
> data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
> @@ -347,6 +356,8 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl
> *ctrl, data->auth_protocol[0].dhchap.idlist[6] =
> NVME_AUTH_DHCHAP_DHGROUP_4096; data->auth_protocol[0].dhchap.idlist[7] =
> NVME_AUTH_DHCHAP_DHGROUP_6144; data->auth_protocol[0].dhchap.idlist[8] =
> NVME_AUTH_DHCHAP_DHGROUP_8192; + data->auth_protocol[0].dhchap.idlist[9]
=
> NVME_AUTH_DHCHAP_DHGROUP_ECDH; + data->auth_protocol[0].dhchap.idlist[10]
=
> NVME_AUTH_DHCHAP_DHGROUP_25519;
>
> return size;
> }
> @@ -889,6 +900,31 @@ static int nvme_auth_dhchap_exponential(struct
> nvme_ctrl *ctrl, }
> chap->host_key_len = pubkey_size;
> chap->sess_key_len = pubkey_size;
> + } else if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_ECDH) {
> + struct ecdh p = {0};
> +
> + pkey_len = crypto_ecdh_key_len(&p);
> + pkey = kzalloc(pkey_len, GFP_KERNEL);
> + if (!pkey)
> + return -ENOMEM;
> +
> + get_random_bytes(pkey, pkey_len);
> + ret = crypto_ecdh_encode_key(pkey, pkey_len, &p);
> + if (ret) {
> + dev_dbg(ctrl->device,
> + "failed to encode pkey, error %d\n", ret);
> + kfree(pkey);
> + return ret;
> + }
> + chap->host_key_len = 64;
> + chap->sess_key_len = 32;
> + } else if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_25519) {
> + pkey_len = CURVE25519_KEY_SIZE;
> + pkey = kzalloc(pkey_len, GFP_KERNEL);
> + if (!pkey)
> + return -ENOMEM;
> + get_random_bytes(pkey, pkey_len);
> + chap->host_key_len = chap->sess_key_len = CURVE25519_KEY_SIZE;
> } else {
> dev_warn(ctrl->device, "Invalid DH group id %d\n",
> chap->dhgroup_id);
> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
> index cc7f12a7c8bf..7e3b613cb08b 100644
> --- a/drivers/nvme/target/auth.c
> +++ b/drivers/nvme/target/auth.c
> @@ -13,6 +13,8 @@
> #include <crypto/kpp.h>
> #include <crypto/dh.h>
> #include <crypto/ffdhe.h>
> +#include <crypto/ecdh.h>
> +#include <crypto/curve25519.h>
> #include <linux/crc32.h>
> #include <linux/base64.h>
> #include <linux/ctype.h>
> @@ -498,6 +500,27 @@ int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
> ret);
> goto out;
> }
> + } else if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_ECDH) {
> + struct ecdh p = {0};
> +
> + pkey_len = crypto_ecdh_key_len(&p);
> + pkey = kmalloc(pkey_len, GFP_KERNEL);
> + if (!pkey)
> + return -ENOMEM;
> +
> + get_random_bytes(pkey, pkey_len);
> + ret = crypto_ecdh_encode_key(pkey, pkey_len, &p);
> + if (ret) {
> + pr_debug("failed to encode private key, error %d\n",
> + ret);
> + goto out;
> + }
> + } else if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_25519) {
> + pkey_len = CURVE25519_KEY_SIZE;
> + pkey = kmalloc(pkey_len, GFP_KERNEL);
> + if (!pkey)
> + return -ENOMEM;
> + get_random_bytes(pkey, pkey_len);
> } else {
> pr_warn("invalid dh group %d\n", ctrl->dh_gid);
> return -EINVAL;
> diff --git a/include/linux/nvme.h b/include/linux/nvme.h
> index 7b94abacfd08..75b638adbca1 100644
> --- a/include/linux/nvme.h
> +++ b/include/linux/nvme.h
> @@ -1476,6 +1476,8 @@ enum {
> NVME_AUTH_DHCHAP_DHGROUP_4096 = 0x03,
> NVME_AUTH_DHCHAP_DHGROUP_6144 = 0x04,
> NVME_AUTH_DHCHAP_DHGROUP_8192 = 0x05,
> + NVME_AUTH_DHCHAP_DHGROUP_ECDH = 0x0e,
> + NVME_AUTH_DHCHAP_DHGROUP_25519 = 0x0f,
> };
>
> union nvmf_auth_protocol {


Ciao
Stephan


2021-07-17 16:51:05

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:

Hi Hannes,

> Implement support for NVMe-oF In-Band authentication. This patch
> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
> to the 'host' configfs directory. The 'dhchap_key' needs to be
> specified in the format outlined in the base spec.
> Augmented challenge support is not implemented, and concatenation
> with TLS encryption is not supported.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/target/Kconfig | 10 +
> drivers/nvme/target/Makefile | 1 +
> drivers/nvme/target/admin-cmd.c | 4 +
> drivers/nvme/target/auth.c | 352 +++++++++++++++++++
> drivers/nvme/target/configfs.c | 71 +++-
> drivers/nvme/target/core.c | 8 +
> drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
> drivers/nvme/target/fabrics-cmd.c | 30 +-
> drivers/nvme/target/nvmet.h | 71 ++++
> 9 files changed, 1004 insertions(+), 3 deletions(-)
> create mode 100644 drivers/nvme/target/auth.c
> create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
>
> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
> index 4be2ececbc45..d5656ef1559e 100644
> --- a/drivers/nvme/target/Kconfig
> +++ b/drivers/nvme/target/Kconfig
> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
> devices over TCP.
>
> If unsure, say N.
> +
> +config NVME_TARGET_AUTH
> + bool "NVMe over Fabrics In-band Authentication support"
> + depends on NVME_TARGET
> + select CRYPTO_SHA256
> + select CRYPTO_SHA512
> + help
> + This enables support for NVMe over Fabrics In-band Authentication
> +
> + If unsure, say N.
> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
> index 9837e580fa7e..c66820102493 100644
> --- a/drivers/nvme/target/Makefile
> +++ b/drivers/nvme/target/Makefile
> @@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o
fabrics-cmd.o \
> discovery.o io-cmd-file.o io-cmd-bdev.o
> nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
> nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
> +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o
> nvme-loop-y += loop.o
> nvmet-rdma-y += rdma.o
> nvmet-fc-y += fc.o
> diff --git a/drivers/nvme/target/admin-cmd.c
> b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644
> --- a/drivers/nvme/target/admin-cmd.c
> +++ b/drivers/nvme/target/admin-cmd.c
> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
>
> if (nvme_is_fabrics(cmd))
> return nvmet_parse_fabrics_cmd(req);
> +
> + if (unlikely(!nvmet_check_auth_status(req)))
> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
> +
> if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
> return nvmet_parse_discovery_cmd(req);
>
> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
> new file mode 100644
> index 000000000000..00c7d051dfb1
> --- /dev/null
> +++ b/drivers/nvme/target/auth.c
> @@ -0,0 +1,352 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
> + * All rights reserved.
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include <crypto/dh.h>
> +#include <crypto/ffdhe.h>
> +#include <linux/crc32.h>
> +#include <linux/base64.h>
> +#include <linux/ctype.h>
> +#include <linux/random.h>
> +#include <asm/unaligned.h>
> +
> +#include "nvmet.h"
> +#include "../host/auth.h"
> +
> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
> +{
> + if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
> + return -EINVAL;
> + if (host->dhchap_key_hash > 3) {
> + pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
> + host->dhchap_key_hash);
> + return -EINVAL;
> + }
> + if (host->dhchap_key_hash > 0) {
> + /* Validate selected hash algorithm */
> + const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
> +
> + if (!crypto_has_shash(hmac, 0, 0)) {
> + pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
> + host->dhchap_key_hash = -1;
> + return -EAGAIN;
> + }
> + /* Use this hash as default */
> + if (!host->dhchap_hash_id)
> + host->dhchap_hash_id = host->dhchap_key_hash;
> + }
> + host->dhchap_secret = kstrdup(secret, GFP_KERNEL);

Just like before - are you sure that the secret is an ASCII string and no
binary blob?

> + if (!host->dhchap_secret)
> + return -ENOMEM;
> + /* Default to SHA256 */
> + if (!host->dhchap_hash_id)
> + host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;
> +
> + pr_debug("Using hash %s\n",
> + nvme_auth_hmac_name(host->dhchap_hash_id));
> + return 0;
> +}
> +
> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
> +{
> + int ret = -ENOTSUPP;
> +
> + if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
> + return 0;
> +
> + return ret;
> +}
> +
> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
> +{
> + int ret = 0;
> + struct nvmet_host_link *p;
> + struct nvmet_host *host = NULL;
> + const char *hash_name;
> +
> + down_read(&nvmet_config_sem);
> + if (ctrl->subsys->type == NVME_NQN_DISC)
> + goto out_unlock;
> +
> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
> + pr_debug("check %s\n", nvmet_host_name(p->host));
> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
> + continue;
> + host = p->host;
> + break;
> + }
> + if (!host) {
> + pr_debug("host %s not found\n", ctrl->hostnqn);
> + ret = -EPERM;
> + goto out_unlock;
> + }
> + if (!host->dhchap_secret) {
> + pr_debug("No authentication provided\n");
> + goto out_unlock;
> + }
> +
> + hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
> + if (!hash_name) {
> + pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);
> + ret = -EINVAL;
> + goto out_unlock;
> + }
> + ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
> + CRYPTO_ALG_ALLOCATES_MEMORY);
> + if (IS_ERR(ctrl->shash_tfm)) {
> + pr_debug("failed to allocate shash %s\n", hash_name);
> + ret = PTR_ERR(ctrl->shash_tfm);
> + ctrl->shash_tfm = NULL;
> + goto out_unlock;
> + }
> +
> + ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
> + &ctrl->dhchap_key_len);
> + if (IS_ERR(ctrl->dhchap_key)) {
> + pr_debug("failed to extract host key, error %d\n", ret);
> + ret = PTR_ERR(ctrl->dhchap_key);
> + ctrl->dhchap_key = NULL;
> + goto out_free_hash;
> + }
> + if (host->dhchap_key_hash) {
> + struct crypto_shash *key_tfm;
> +
> + hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
> + key_tfm = crypto_alloc_shash(hash_name, 0, 0);
> + if (IS_ERR(key_tfm)) {
> + ret = PTR_ERR(key_tfm);
> + goto out_free_hash;
> + } else {
> + SHASH_DESC_ON_STACK(shash, key_tfm);
> +
> + shash->tfm = key_tfm;
> + ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
> + ctrl->dhchap_key_len);
> + crypto_shash_init(shash);
> + crypto_shash_update(shash, ctrl->subsys->subsysnqn,
> + strlen(ctrl->subsys->subsysnqn));
> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
> + crypto_shash_final(shash, ctrl->dhchap_key);
> + crypto_free_shash(key_tfm);
> + }
> + }
> + pr_debug("%s: using key %*ph\n", __func__,
> + (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
> + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,

Is it truly necessary to keep the key around in ctrl->dhchap_key? It looks to
me that this buffer is only used here and thus could be turned into a local
variable. Keys flying around in memory is not a good idea. :-)

> + ctrl->dhchap_key_len);
> +out_free_hash:
> + if (ret) {
> + if (ctrl->dhchap_key) {
> + kfree(ctrl->dhchap_key);

kfree_sensitive?

> + ctrl->dhchap_key = NULL;
> + }
> + crypto_free_shash(ctrl->shash_tfm);
> + ctrl->shash_tfm = NULL;
> + }
> +out_unlock:
> + up_read(&nvmet_config_sem);
> +
> + return ret;
> +}
> +
> +void nvmet_auth_sq_free(struct nvmet_sq *sq)
> +{
> + if (sq->dhchap_c1)
> + kfree(sq->dhchap_c1);
> + if (sq->dhchap_c2)
> + kfree(sq->dhchap_c2);
> + if (sq->dhchap_skey)
> + kfree(sq->dhchap_skey);

kfree_sensitive?

> +}
> +
> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl)
> +{
> + if (ctrl->shash_tfm) {
> + crypto_free_shash(ctrl->shash_tfm);
> + ctrl->shash_tfm = NULL;
> + }
> + if (ctrl->dh_tfm) {
> + crypto_free_kpp(ctrl->dh_tfm);
> + ctrl->dh_tfm = NULL;
> + }
> + if (ctrl->dhchap_key) {
> + kfree(ctrl->dhchap_key);

kfree_sensitive?

> + ctrl->dhchap_key = NULL;
> + }
> +}
> +
> +bool nvmet_check_auth_status(struct nvmet_req *req)
> +{
> + if (req->sq->ctrl->shash_tfm &&
> + !req->sq->authenticated)
> + return false;
> + return true;
> +}
> +
> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
> + unsigned int shash_len)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
> + u8 *challenge = req->sq->dhchap_c1;
> + u8 buf[4];
> + int ret;
> +
> + if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + ret = -ENOTSUPP;
> + goto out;
> + }
> +
> + shash->tfm = ctrl->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, shash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(req->sq->dhchap_s1, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(req->sq->dhchap_tid, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, 4);
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "HostHost", 8);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->subsysnqn,
> + strlen(ctrl->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, response);
> +out:
> + if (challenge != req->sq->dhchap_c1)
> + kfree(challenge);
> + return 0;
> +}
> +
> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
> + unsigned int shash_len)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
> + u8 *challenge = req->sq->dhchap_c2;
> + u8 buf[4];
> + int ret;
> +
> + pr_debug("%s: ctrl %d hash seq %d transaction %u\n", __func__,
> + ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid);
> + pr_debug("%s: ctrl %d challenge %*ph\n", __func__,
> + ctrl->cntlid, shash_len, req->sq->dhchap_c2);
> + pr_debug("%s: ctrl %d subsysnqn %s\n", __func__,
> + ctrl->cntlid, ctrl->subsysnqn);
> + pr_debug("%s: ctrl %d hostnqn %s\n", __func__,
> + ctrl->cntlid, ctrl->hostnqn);
> +
> + if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + ret = -ENOTSUPP;
> + goto out;
> + }
> +
> + shash->tfm = ctrl->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, shash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(req->sq->dhchap_s2, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(req->sq->dhchap_tid, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, 4);
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "Controller", 10);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->subsysnqn,
> + strlen(ctrl->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, response);
> +out:
> + if (challenge != req->sq->dhchap_c2)
> + kfree(challenge);
> + return 0;
> +}
> +
> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
> + u8 *pkey, int pkey_size)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct kpp_request *kpp_req;
> + struct crypto_wait wait;
> + struct scatterlist src, dst;
> + int ret;
> +
> + req->sq->dhchap_skey_len =
> + nvme_auth_dhgroup_privkey_size(ctrl->dh_gid);
> + req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
> + if (!req->sq->dhchap_skey)
> + return -ENOMEM;
> + kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
> + if (!kpp_req) {
> + kfree(req->sq->dhchap_skey);
> + req->sq->dhchap_skey = NULL;
> + return -ENOMEM;
> + }
> +
> + pr_debug("%s: host public key %*ph\n", __func__,
> + (int)pkey_size, pkey);
> + crypto_init_wait(&wait);
> + sg_init_one(&src, pkey, pkey_size);
> + kpp_request_set_input(kpp_req, &src, pkey_size);
> + sg_init_one(&dst, req->sq->dhchap_skey,
> + req->sq->dhchap_skey_len);
> + kpp_request_set_output(kpp_req, &dst, req->sq->dhchap_skey_len);
> + kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(kpp_req), &wait);
> + kpp_request_free(kpp_req);
> + if (ret)
> + pr_debug("failed to compute shared secred, err %d\n", ret);
> + else
> + pr_debug("%s: shared secret %*ph\n", __func__,
> + (int)req->sq->dhchap_skey_len,
> + req->sq->dhchap_skey);
> +
> + return ret;
> +}
> diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
> index 273555127188..e0760911a761 100644
> --- a/drivers/nvme/target/configfs.c
> +++ b/drivers/nvme/target/configfs.c
> @@ -11,8 +11,13 @@
> #include <linux/ctype.h>
> #include <linux/pci.h>
> #include <linux/pci-p2pdma.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
>
> #include "nvmet.h"
> +#ifdef CONFIG_NVME_TARGET_AUTH
> +#include "../host/auth.h"
> +#endif
>
> static const struct config_item_type nvmet_host_type;
> static const struct config_item_type nvmet_subsys_type;
> @@ -1656,10 +1661,71 @@ static const struct config_item_type
> nvmet_ports_type = { static struct config_group nvmet_subsystems_group;
> static struct config_group nvmet_ports_group;
>
> -static void nvmet_host_release(struct config_item *item)
> +#ifdef CONFIG_NVME_TARGET_AUTH
> +static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
> + char *page)
> +{
> + u8 *dhchap_secret = to_host(item)->dhchap_secret;
> +
> + if (!dhchap_secret)
> + return sprintf(page, "\n");
> + return sprintf(page, "%s\n", dhchap_secret);
> +}
> +
> +static ssize_t nvmet_host_dhchap_key_store(struct config_item *item,
> + const char *page, size_t count)
> {
> struct nvmet_host *host = to_host(item);
> + int ret;
>
> + ret = nvmet_auth_set_host_key(host, page);
> + if (ret < 0)
> + return ret;
> + return count;
> +}
> +
> +CONFIGFS_ATTR(nvmet_host_, dhchap_key);
> +
> +static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item,
> + char *page)
> +{
> + struct nvmet_host *host = to_host(item);
> + const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
> +
> + return sprintf(page, "%s\n", hash_name ? hash_name : "none");
> +}
> +
> +static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,
> + const char *page, size_t count)
> +{
> + struct nvmet_host *host = to_host(item);
> + int hmac_id;
> +
> + hmac_id = nvme_auth_hmac_id(page);
> + if (hmac_id < 0)
> + return -EINVAL;
> + if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0))
> + return -ENOTSUPP;
> + host->dhchap_hash_id = hmac_id;
> + return count;
> +}
> +
> +CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
> +
> +static struct configfs_attribute *nvmet_host_attrs[] = {
> + &nvmet_host_attr_dhchap_key,
> + &nvmet_host_attr_dhchap_hash,
> + NULL,
> +};
> +#endif /* CONFIG_NVME_TARGET_AUTH */
> +
> +static void nvmet_host_release(struct config_item *item)
> +{
> + struct nvmet_host *host = to_host(item);
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + if (host->dhchap_secret)
> + kfree(host->dhchap_secret);
> +#endif
> kfree(host);
> }
>
> @@ -1669,6 +1735,9 @@ static struct configfs_item_operations
> nvmet_host_item_ops = {
>
> static const struct config_item_type nvmet_host_type = {
> .ct_item_ops = &nvmet_host_item_ops,
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + .ct_attrs = nvmet_host_attrs,
> +#endif
> .ct_owner = THIS_MODULE,
> };
>
> diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
> index 163f7dc1a929..b5d7971f566b 100644
> --- a/drivers/nvme/target/core.c
> +++ b/drivers/nvme/target/core.c
> @@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
> wait_for_completion(&sq->confirm_done);
> wait_for_completion(&sq->free_done);
> percpu_ref_exit(&sq->ref);
> + nvmet_auth_sq_free(sq);
>
> if (ctrl) {
> /*
> @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req)
> req->cmd->common.opcode, req->sq->qid);
> return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
> }
> +
> + if (unlikely(!nvmet_check_auth_status(req))) {
> + pr_warn("qid %d not authenticated\n", req->sq->qid);
> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
> + }
> return 0;
> }
>
> @@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)
> flush_work(&ctrl->async_event_work);
> cancel_work_sync(&ctrl->fatal_err_work);
>
> + nvmet_reset_auth(ctrl);
> +
> ida_simple_remove(&cntlid_ida, ctrl->cntlid);
>
> nvmet_async_events_free(ctrl);
> diff --git a/drivers/nvme/target/fabrics-cmd-auth.c
> b/drivers/nvme/target/fabrics-cmd-auth.c new file mode 100644
> index 000000000000..962f9f5e9d89
> --- /dev/null
> +++ b/drivers/nvme/target/fabrics-cmd-auth.c
> @@ -0,0 +1,460 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * NVMe over Fabrics DH-HMAC-CHAP authentication command handling.
> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
> + * All rights reserved.
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +#include <linux/blkdev.h>
> +#include <linux/random.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include "nvmet.h"
> +#include "../host/auth.h"
> +
> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
> +{
> + /* Initialize in-band authentication */
> + req->sq->authenticated = false;
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
> + req->cqe->result.u32 |= 0x2 << 16;
> +}
> +
> +static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct nvmf_auth_dhchap_negotiate_data *data = d;
> + int i, hash_id, null_dh = -1;
> +
> + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d
> dhlen %d\n", + __func__, ctrl->cntlid, req->sq->qid,
> + data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
> + data->auth_protocol[0].dhchap.halen,
> + data->auth_protocol[0].dhchap.dhlen);
> + req->sq->dhchap_tid = le16_to_cpu(data->t_id);
> + if (data->sc_c)
> + return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
> +
> + if (data->napd != 1)
> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> +
> + if (data->auth_protocol[0].dhchap.authid != 0x01)
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> +
> + hash_id = nvme_auth_hmac_id(crypto_shash_alg_name(ctrl->shash_tfm));
> + for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
> + pr_debug("%s: ctrl %d qid %d checking hash %d for %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->auth_protocol[0].dhchap.idlist[i], hash_id);
> + if (hash_id != data->auth_protocol[0].dhchap.idlist[i])
> + continue;
> + req->sq->dhchap_hash_id = hash_id;
> + req->sq->dhchap_hash_len = crypto_shash_digestsize(ctrl-
>shash_tfm);
> + break;
> + }
> + if (req->sq->dhchap_hash_id == 0) {
> + pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
> + __func__, ctrl->cntlid, req->sq->qid);
> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + }
> +
> + for (i = data->auth_protocol[0].dhchap.halen;
> + i < data->auth_protocol[0].dhchap.halen +
> + data->auth_protocol[0].dhchap.dhlen; i++) {
> + int dhgid = data->auth_protocol[0].dhchap.idlist[i];
> +
> + if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + null_dh = dhgid;
> + continue;
> + }
> + if (nvmet_setup_dhgroup(ctrl, dhgid) == 0)
> + break;
> + }
> + if (!ctrl->dh_tfm && null_dh < 0) {
> + pr_debug("%s: ctrl %d qid %d: no DH group selected\n",
> + __func__, ctrl->cntlid, req->sq->qid);
> + return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + }
> + if (ctrl->dh_gid == -1) {
> + ctrl->dh_gid = null_dh;
> + ctrl->dh_tfm = NULL;
> + }
> + pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
> + return 0;
> +}
> +
> +static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct nvmf_auth_dhchap_reply_data *data = d;
> + u8 *response;
> +
> + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->hl, data->cvalid, data->dhvlen);
> + if (data->hl != req->sq->dhchap_hash_len)
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> +
> + if (data->dhvlen) {
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + }
> +
> + response = kmalloc(data->hl, GFP_KERNEL);

Again, align to CRYPTO_MINALIGN_ATTR?

> + if (!response)
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> +
> + if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
> + pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n",
> + ctrl->cntlid, req->sq->qid);
> + kfree(response);
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + }
> +
> + if (memcmp(data->rval, response, data->hl)) {
> + pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n",
> + ctrl->cntlid, req->sq->qid);
> + kfree(response);
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + }
> + kfree(response);
> + pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n",
> + ctrl->cntlid, req->sq->qid);
> + if (data->cvalid) {
> + req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL);
> + if (!req->sq->dhchap_c2)
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl);
> +
> + pr_debug("ctrl %d qid %d challenge %*ph\n",
> + ctrl->cntlid, req->sq->qid, data->hl,
> + req->sq->dhchap_c2);
> + req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
> + } else
> + req->sq->dhchap_c2 = NULL;
> +
> + return 0;
> +}
> +
> +static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d)
> +{
> + struct nvmf_auth_dhchap_failure_data *data = d;
> +
> + return data->reason_code_explanation;
> +}
> +
> +void nvmet_execute_auth_send(struct nvmet_req *req)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct nvmf_auth_dhchap_success2_data *data;
> + void *d;
> + u32 tl;
> + u16 status = 0;
> +
> + if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, secp);
> + goto done;
> + }
> + if (req->cmd->auth_send.spsp0 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, spsp0);
> + goto done;
> + }
> + if (req->cmd->auth_send.spsp1 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, spsp1);
> + goto done;
> + }
> + tl = le32_to_cpu(req->cmd->auth_send.tl);
> + if (!tl) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, tl);
> + goto done;
> + }
> + if (!nvmet_check_transfer_len(req, tl)) {
> + pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
> + return;
> + }
> +
> + d = kmalloc(tl, GFP_KERNEL);
> + if (!d) {
> + status = NVME_SC_INTERNAL;
> + goto done;
> + }
> +
> + status = nvmet_copy_from_sgl(req, 0, d, tl);
> + if (status) {
> + kfree(d);
> + goto done;
> + }
> +
> + data = d;
> + pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
> + ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id,
> + req->sq->dhchap_step);
> + if (data->auth_type != NVME_AUTH_COMMON_MESSAGES &&
> + data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
> + if (data->auth_id != req->sq->dhchap_step) {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else if (data->auth_id != NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else {
> + /* Validate negotiation parameters */
> + status = nvmet_auth_negotiate(req, d);
> + if (status == 0)
> + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
> + else {
> + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = status;
> + status = 0;
> + }
> + }
> + } else if (data->auth_type == NVME_AUTH_DHCHAP_MESSAGES) {
> + if (data->auth_id != req->sq->dhchap_step) {
> + pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->auth_id, req->sq->dhchap_step);
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) {
> + pr_debug("%s: ctrl %d qid %d invalid transaction %d
(expected %d)\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + le16_to_cpu(data->t_id),
> + req->sq->dhchap_tid);
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + } else {
> + switch (data->auth_id) {
> + case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
> + status = nvmet_auth_reply(req, d);
> + if (status == 0)
> + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
> + else {
> + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = status;
> + status = 0;
> + }
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
> + req->sq->authenticated = true;
> + pr_debug("%s: ctrl %d qid %d authenticated\n",
> + __func__, ctrl->cntlid, req->sq->qid);
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
> + status = nvmet_auth_failure2(req, d);
> + if (status) {
> + pr_warn("ctrl %d qid %d: DH-HMAC-CHAP
negotiation failed (%d)\n",
> + ctrl->cntlid, req->sq->qid,
> + status);
> + req->sq->dhchap_status = status;
> + status = 0;
> + }
> + break;
> + default:
> + req->sq->dhchap_status =
NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
> + break;
> + }
> + }
> + } else {
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
> + }
> + kfree(d);
> +done:
> + pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__,
> + ctrl->cntlid, req->sq->qid,
> + req->sq->dhchap_status, req->sq->dhchap_step);
> + if (status)
> + pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + status, req->error_loc);
> + req->cqe->result.u64 = 0;
> + nvmet_req_complete(req, status);
> + if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
> + req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
> + return;
> + /* Final states, clear up variables */
> + kfree(req->sq->dhchap_c1);
> + kfree(req->sq->dhchap_c2);
> + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
> + nvmet_ctrl_fatal_error(ctrl);
> +}
> +
> +static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
> +{
> + struct nvmf_auth_dhchap_challenge_data *data = d;
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + int ret = 0;
> + int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
> +
> + if (al < data_size) {
> + pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
> + al, data_size);
> + return -EINVAL;
> + }
> + memset(data, 0, data_size);
> + req->sq->dhchap_s1 = ctrl->dhchap_seqnum++;
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
> + data->t_id = cpu_to_le16(req->sq->dhchap_tid);
> + data->hashid = req->sq->dhchap_hash_id;
> + data->hl = req->sq->dhchap_hash_len;
> + data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
> + req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
> + if (!req->sq->dhchap_c1)
> + return -ENOMEM;
> + get_random_bytes(req->sq->dhchap_c1, data->hl);
> + memcpy(data->cval, req->sq->dhchap_c1, data->hl);
> + pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n",
> + __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
> + req->sq->dhchap_tid, data->hl, data->dhvlen);
> + return ret;
> +}
> +
> +static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al)
> +{
> + struct nvmf_auth_dhchap_success1_data *data = d;
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> +
> + WARN_ON(al < sizeof(*data));
> + memset(data, 0, sizeof(*data));
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
> + data->t_id = cpu_to_le16(req->sq->dhchap_tid);
> + data->hl = req->sq->dhchap_hash_len;
> + if (req->sq->dhchap_c2) {
> + if (nvmet_auth_ctrl_hash(req, data->rval, data->hl))
> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + data->rvalid = 1;
> + pr_debug("ctrl %d qid %d response %*ph\n",
> + ctrl->cntlid, req->sq->qid, data->hl, data->rval);
> + }
> + return 0;
> +}
> +
> +static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
> +{
> + struct nvmf_auth_dhchap_failure_data *data = d;
> +
> + WARN_ON(al < sizeof(*data));
> + data->auth_type = NVME_AUTH_COMMON_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + data->t_id = cpu_to_le32(req->sq->dhchap_tid);
> + data->reason_code = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
> + data->reason_code_explanation = req->sq->dhchap_status;
> +}
> +
> +void nvmet_execute_auth_receive(struct nvmet_req *req)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + void *d;
> + u32 al;
> + u16 status = 0;
> +
> + if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER)
{
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, secp);
> + goto done;
> + }
> + if (req->cmd->auth_receive.spsp0 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, spsp0);
> + goto done;
> + }
> + if (req->cmd->auth_receive.spsp1 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, spsp1);
> + goto done;
> + }
> + al = le32_to_cpu(req->cmd->auth_receive.al);
> + if (!al) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, al);
> + goto done;
> + }
> + if (!nvmet_check_transfer_len(req, al)) {
> + pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
> + return;
> + }
> +
> + d = kmalloc(al, GFP_KERNEL);
> + if (!d) {
> + status = NVME_SC_INTERNAL;
> + goto done;
> + }
> + pr_debug("%s: ctrl %d qid %d step %x\n", __func__,
> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
> + switch (req->sq->dhchap_step) {
> + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
> + status = nvmet_auth_challenge(req, d, al);
> + if (status < 0) {
> + pr_warn("ctrl %d qid %d: challenge error (%d)\n",
> + ctrl->cntlid, req->sq->qid, status);
> + status = NVME_SC_INTERNAL;
> + break;
> + }
> + if (status) {
> + req->sq->dhchap_status = status;
> + nvmet_auth_failure1(req, d, al);
> + pr_warn("ctrl %d qid %d: challenge status (%x)\n",
> + ctrl->cntlid, req->sq->qid,
> + req->sq->dhchap_status);
> + status = 0;
> + break;
> + }
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
> + status = nvmet_auth_success1(req, d, al);
> + if (status) {
> + req->sq->dhchap_status = status;
> + nvmet_auth_failure1(req, d, al);
> + pr_warn("ctrl %d qid %d: success1 status (%x)\n",
> + ctrl->cntlid, req->sq->qid,
> + req->sq->dhchap_status);
> + break;
> + }
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1:
> + nvmet_auth_failure1(req, d, al);
> + pr_warn("ctrl %d qid %d failure1 (%x)\n",
> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_status);
> + break;
> + default:
> + pr_warn("ctrl %d qid %d unhandled step (%d)\n",
> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + nvmet_auth_failure1(req, d, al);
> + status = 0;
> + break;
> + }
> +
> + status = nvmet_copy_to_sgl(req, 0, d, al);
> + kfree(d);
> +done:
> + req->cqe->result.u64 = 0;
> + nvmet_req_complete(req, status);
> + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
> + kfree(req->sq->dhchap_c1);
> + kfree(req->sq->dhchap_c2);
> + nvmet_ctrl_fatal_error(ctrl);
> + }
> +}
> diff --git a/drivers/nvme/target/fabrics-cmd.c
> b/drivers/nvme/target/fabrics-cmd.c index 7d0f3523fdab..53fb853cd8fe 100644
> --- a/drivers/nvme/target/fabrics-cmd.c
> +++ b/drivers/nvme/target/fabrics-cmd.c
> @@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)
> case nvme_fabrics_type_property_get:
> req->execute = nvmet_execute_prop_get;
> break;
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + case nvme_fabrics_type_auth_send:
> + req->execute = nvmet_execute_auth_send;
> + break;
> + case nvme_fabrics_type_auth_receive:
> + req->execute = nvmet_execute_auth_receive;
> + break;
> +#endif
> default:
> pr_debug("received unknown capsule type 0x%x\n",
> cmd->fabrics.fctype);
> @@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req
> *req) struct nvmf_connect_data *d;
> struct nvmet_ctrl *ctrl = NULL;
> u16 status = 0;
> + int ret;
>
> if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
> return;
> @@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct
> nvmet_req *req)
>
> uuid_copy(&ctrl->hostid, &d->hostid);
>
> + ret = nvmet_setup_auth(ctrl, req);
> + if (ret < 0) {
> + pr_err("Failed to setup authentication, error %d\n", ret);
> + nvmet_ctrl_put(ctrl);
> + if (ret == -EPERM)
> + status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
> + else
> + status = NVME_SC_INTERNAL;
> + goto out;
> + }
> +
> status = nvmet_install_queue(ctrl, req);
> if (status) {
> nvmet_ctrl_put(ctrl);
> goto out;
> }
>
> - pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
> + pr_info("creating controller %d for subsystem %s for NQN %s%s%s.\n",
> ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
> - ctrl->pi_support ? " T10-PI is enabled" : "");
> + ctrl->pi_support ? " T10-PI is enabled" : "",
> + nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
> req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
>
> + if (nvmet_has_auth(ctrl))
> + nvmet_init_auth(ctrl, req);
> out:
> kfree(d);
> complete:
> @@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req
> *req) }
>
> pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
> + req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
> + if (nvmet_has_auth(ctrl))
> + nvmet_init_auth(ctrl, req);
>
> out:
> kfree(d);
> diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
> index 06dd3d537f07..ef8815e137d7 100644
> --- a/drivers/nvme/target/nvmet.h
> +++ b/drivers/nvme/target/nvmet.h
> @@ -108,6 +108,20 @@ struct nvmet_sq {
> u16 size;
> u32 sqhd;
> bool sqhd_disabled;
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + bool authenticated;
> + u16 dhchap_tid;
> + u16 dhchap_status;
> + int dhchap_step;
> + u8 dhchap_hash_id;
> + u8 dhchap_hash_len;
> + u8 *dhchap_c1;
> + u8 *dhchap_c2;
> + u32 dhchap_s1;
> + u32 dhchap_s2;
> + u8 *dhchap_skey;
> + int dhchap_skey_len;
> +#endif
> struct completion free_done;
> struct completion confirm_done;
> };
> @@ -209,6 +223,15 @@ struct nvmet_ctrl {
> u64 err_counter;
> struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS];
> bool pi_support;
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + u32 dhchap_seqnum;
> + u8 *dhchap_key;
> + size_t dhchap_key_len;
> + struct crypto_shash *shash_tfm;
> + struct crypto_kpp *dh_tfm;
> + u32 dh_gid;
> + u32 dh_keysize;
> +#endif
> };
>
> struct nvmet_subsys {
> @@ -270,6 +293,10 @@ static inline struct nvmet_subsys
> *namespaces_to_subsys(
>
> struct nvmet_host {
> struct config_group group;
> + u8 *dhchap_secret;
> + u8 dhchap_key_hash;
> + u8 dhchap_hash_id;
> + u8 dhchap_dhgroup_id;
> };
>
> static inline struct nvmet_host *to_host(struct config_item *item)
> @@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req
> *req, struct bio *bio) bio_put(bio);
> }
>
> +#ifdef CONFIG_NVME_TARGET_AUTH
> +void nvmet_execute_auth_send(struct nvmet_req *req);
> +void nvmet_execute_auth_receive(struct nvmet_req *req);
> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret);
> +int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl);
> +void nvmet_auth_sq_free(struct nvmet_sq *sq);
> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id);
> +bool nvmet_check_auth_status(struct nvmet_req *req);
> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
> + unsigned int hash_len);
> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
> + unsigned int hash_len);
> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
> +{
> + return ctrl->shash_tfm != NULL;
> +}
> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
> + u8 *buf, int buf_size);
> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
> + u8 *buf, int buf_size);
> +#else
> +static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl,
> + struct nvmet_req *req)
> +{
> + return 0;
> +}
> +static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl,
> + struct nvmet_req *req) {};
> +static inline void nvmet_reset_auth(struct nvmet_ctrl *ctrl) {};
> +static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
> +static inline bool nvmet_check_auth_status(struct nvmet_req *req)
> +{
> + return true;
> +}
> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
> +{
> + return false;
> +}
> +static inline const char *nvmet_dhchap_dhgroup_name(int dhgid) { return
> NULL; } +#endif
> +
> #endif /* _NVMET_H */


Ciao
Stephan


2021-07-17 16:51:08

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 10/11] nvmet-auth: implement support for augmented challenge

Am Freitag, 16. Juli 2021, 13:04:27 CEST schrieb Hannes Reinecke:

Hi Hannes,

> Implement support for augmented challenge with FFDHE groups.
> This patch adds a new configfs attribute 'dhchap_dhgroup' to
> select the DH group to use.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/target/auth.c | 241 ++++++++++++++++++++++++-
> drivers/nvme/target/configfs.c | 31 ++++
> drivers/nvme/target/fabrics-cmd-auth.c | 14 +-
> 3 files changed, 281 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
> index 00c7d051dfb1..cc7f12a7c8bf 100644
> --- a/drivers/nvme/target/auth.c
> +++ b/drivers/nvme/target/auth.c
> @@ -58,11 +58,56 @@ int nvmet_auth_set_host_key(struct nvmet_host *host,
> const char *secret)
>
> int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
> {
> + struct nvmet_host_link *p;
> + struct nvmet_host *host = NULL;
> + const char *dhgroup_kpp;
> int ret = -ENOTSUPP;
>
> if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
> return 0;
>
> + down_read(&nvmet_config_sem);
> + if (ctrl->subsys->type == NVME_NQN_DISC)
> + goto out_unlock;
> +
> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
> + continue;
> + host = p->host;
> + break;
> + }
> + if (!host) {
> + pr_debug("host %s not found\n", ctrl->hostnqn);
> + ret = -ENXIO;
> + goto out_unlock;
> + }
> +
> + if (host->dhchap_dhgroup_id != dhgroup_id) {
> + ret = -EINVAL;
> + goto out_unlock;
> + }
> + dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
> + if (!dhgroup_kpp) {
> + ret = -EINVAL;
> + goto out_unlock;
> + }
> + ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0);
> + if (IS_ERR(ctrl->dh_tfm)) {
> + pr_debug("failed to setup DH group %d, err %ld\n",
> + dhgroup_id, PTR_ERR(ctrl->dh_tfm));
> + ret = PTR_ERR(ctrl->dh_tfm);
> + ctrl->dh_tfm = NULL;
> + } else {
> + ctrl->dh_gid = dhgroup_id;
> + ctrl->dh_keysize = nvme_auth_dhgroup_pubkey_size(dhgroup_id);
> + pr_debug("select DH group %d keysize %d\n",
> + ctrl->dh_gid, ctrl->dh_keysize);
> + ret = 0;
> + }
> +
> +out_unlock:
> + up_read(&nvmet_config_sem);
> +
> return ret;
> }
>
> @@ -192,6 +237,101 @@ bool nvmet_check_auth_status(struct nvmet_req *req)
> return true;
> }
>
> +static int nvmet_auth_hash_sesskey(struct nvmet_req *req, u8 *hashed_key)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + const char *hmac_name, *digest_name;
> + struct crypto_shash *tfm;
> + int hmac_id, ret;
> +
> + if (!ctrl->shash_tfm) {
> + pr_debug("%s: hash alg not set\n", __func__);
> + return -EINVAL;
> + }
> + hmac_name = crypto_shash_alg_name(ctrl->shash_tfm);
> + hmac_id = nvme_auth_hmac_id(hmac_name);
> + if (hmac_id < 0) {
> + pr_debug("%s: unsupported hmac %s\n", __func__,
> + hmac_name);
> + return -EINVAL;
> + }
> + digest_name = nvme_auth_digest_name(hmac_id);
> + if (!digest_name) {
> + pr_debug("%s: failed to get digest for %s\n", __func__,
> + hmac_name);
> + return -EINVAL;
> + }
> + tfm = crypto_alloc_shash(digest_name, 0, 0);
> + if (IS_ERR(tfm))
> + return -ENOMEM;
> +
> + ret = crypto_shash_tfm_digest(tfm, req->sq->dhchap_skey,
> + req->sq->dhchap_skey_len, hashed_key);
> + if (ret < 0)
> + pr_debug("%s: Failed to hash digest len %d\n", __func__,
> + req->sq->dhchap_skey_len);
> +
> + crypto_free_shash(tfm);
> + return ret;
> +}
> +
> +static int nvmet_auth_augmented_challenge(struct nvmet_req *req,
> + u8 *challenge, u8 *aug)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct crypto_shash *tfm;
> + struct shash_desc *desc;
> + u8 *hashed_key;
> + const char *hash_name;
> + int hash_len = req->sq->dhchap_hash_len;
> + int ret;
> +
> + hashed_key = kmalloc(hash_len, GFP_KERNEL);
> + if (!hashed_key)
> + return -ENOMEM;
> +
> + ret = nvmet_auth_hash_sesskey(req, hashed_key);
> + if (ret < 0) {
> + pr_debug("failed to hash session key, err %d\n", ret);
> + kfree(hashed_key);
> + return ret;
> + }
> + hash_name = crypto_shash_alg_name(ctrl->shash_tfm);
> + if (!hash_name) {
> + pr_debug("Invalid hash algoritm\n");
> + return -EINVAL;
> + }
> + tfm = crypto_alloc_shash(hash_name, 0, 0);
> + if (IS_ERR(tfm)) {
> + ret = PTR_ERR(tfm);
> + goto out_free_key;
> + }
> + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
> + GFP_KERNEL);
> + if (!desc) {
> + ret = -ENOMEM;
> + goto out_free_hash;
> + }
> + desc->tfm = tfm;
> +
> + ret = crypto_shash_setkey(tfm, hashed_key, hash_len);
> + if (ret)
> + goto out_free_desc;
> + ret = crypto_shash_init(desc);
> + if (ret)
> + goto out_free_desc;
> + crypto_shash_update(desc, challenge, hash_len);
> + crypto_shash_final(desc, aug);
> +
> +out_free_desc:
> + kfree_sensitive(desc);
> +out_free_hash:
> + crypto_free_shash(tfm);
> +out_free_key:
> + kfree(hashed_key);
> + return ret;
> +}
> +
> int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
> unsigned int shash_len)
> {
> @@ -202,8 +342,15 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8
> *response, int ret;
>
> if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> - ret = -ENOTSUPP;
> - goto out;
> + challenge = kmalloc(shash_len, GFP_KERNEL);

Alignment?

> + if (!challenge) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + ret = nvmet_auth_augmented_challenge(req, req->sq->dhchap_c1,
> + challenge);
> + if (ret)
> + goto out;
> }
>
> shash->tfm = ctrl->shash_tfm;
> @@ -264,8 +411,15 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8
> *response, ctrl->cntlid, ctrl->hostnqn);
>
> if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> - ret = -ENOTSUPP;
> - goto out;
> + challenge = kmalloc(shash_len, GFP_KERNEL);

dto.
> + if (!challenge) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + ret = nvmet_auth_augmented_challenge(req, req->sq->dhchap_c2,
> + challenge);
> + if (ret)
> + goto out;
> }
>
> shash->tfm = ctrl->shash_tfm;
> @@ -307,6 +461,85 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8
> *response, return 0;
> }
>
> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
> + u8 *buf, int buf_size)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct kpp_request *kpp_req;
> + struct crypto_wait wait;
> + char *pkey;
> + struct scatterlist dst;
> + int ret, pkey_len;
> +
> + if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) {
> + struct dh p = {0};
> + int bits = nvme_auth_dhgroup_pubkey_size(ctrl->dh_gid) << 3;
> +
> + ret = crypto_ffdhe_params(&p, bits);
> + if (ret)
> + return ret;
> +
> + p.key = ctrl->dhchap_key;
> + p.key_size = ctrl->dhchap_key_len;
> +
> + pkey_len = crypto_dh_key_len(&p);
> + pkey = kmalloc(pkey_len, GFP_KERNEL);
> + if (!pkey)
> + return -ENOMEM;
> +
> + get_random_bytes(pkey, pkey_len);
> + ret = crypto_dh_encode_key(pkey, pkey_len, &p);
> + if (ret) {
> + pr_debug("failed to encode private key, error %d\n",
> + ret);
> + goto out;
> + }
> + } else {
> + pr_warn("invalid dh group %d\n", ctrl->dh_gid);
> + return -EINVAL;
> + }
> + ret = crypto_kpp_set_secret(ctrl->dh_tfm, pkey, pkey_len);
> + if (ret) {
> + pr_debug("failed to set private key, error %d\n", ret);
> + goto out;
> + }
> +
> + kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
> + if (!kpp_req) {
> + pr_debug("cannot allocate kpp request\n");
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + crypto_init_wait(&wait);
> + kpp_request_set_input(kpp_req, NULL, 0);
> + sg_init_one(&dst, buf, buf_size);
> + kpp_request_set_output(kpp_req, &dst, buf_size);
> + kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_generate_public_key(kpp_req), &wait);
> + kpp_request_free(kpp_req);
> + if (ret == -EOVERFLOW) {
> + pr_debug("public key buffer too small, need %d is %d\n",
> + crypto_kpp_maxsize(ctrl->dh_tfm), buf_size);
> + ret = -ENOKEY;
> + } else if (ret) {
> + pr_debug("failed to generate public key, err %d\n", ret);
> + ret = -ENOKEY;
> + } else
> + pr_debug("%s: ctrl public key %*ph\n", __func__,
> + (int)buf_size, buf);
> +
> +out:
> + kfree_sensitive(pkey);
> + return ret;
> +}

In general: the target/host authentication code looks very similar. Is there
no way to have a common code base?
> +
> int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
> u8 *pkey, int pkey_size)
> {
> diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
> index e0760911a761..e9b8884a83b0 100644
> --- a/drivers/nvme/target/configfs.c
> +++ b/drivers/nvme/target/configfs.c
> @@ -1712,9 +1712,40 @@ static ssize_t nvmet_host_dhchap_hash_store(struct
> config_item *item,
>
> CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
>
> +static ssize_t nvmet_host_dhchap_dhgroup_show(struct config_item *item,
> + char *page)
> +{
> + struct nvmet_host *host = to_host(item);
> + const char *dhgroup = nvme_auth_dhgroup_name(host->dhchap_dhgroup_id);
> +
> + return sprintf(page, "%s\n", dhgroup ? dhgroup : "none");
> +}
> +
> +static ssize_t nvmet_host_dhchap_dhgroup_store(struct config_item *item,
> + const char *page, size_t count)
> +{
> + struct nvmet_host *host = to_host(item);
> + int dhgroup_id;
> +
> + dhgroup_id = nvme_auth_dhgroup_id(page);
> + if (dhgroup_id < 0)
> + return -EINVAL;
> + if (dhgroup_id != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + const char *kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
> +
> + if (!crypto_has_kpp(kpp, 0, 0))
> + return -EINVAL;
> + }
> + host->dhchap_dhgroup_id = dhgroup_id;
> + return count;
> +}
> +
> +CONFIGFS_ATTR(nvmet_host_, dhchap_dhgroup);
> +
> static struct configfs_attribute *nvmet_host_attrs[] = {
> &nvmet_host_attr_dhchap_key,
> &nvmet_host_attr_dhchap_hash,
> + &nvmet_host_attr_dhchap_dhgroup,
> NULL,
> };
> #endif /* CONFIG_NVME_TARGET_AUTH */
> diff --git a/drivers/nvme/target/fabrics-cmd-auth.c
> b/drivers/nvme/target/fabrics-cmd-auth.c index 962f9f5e9d89..478ac351c645
> 100644
> --- a/drivers/nvme/target/fabrics-cmd-auth.c
> +++ b/drivers/nvme/target/fabrics-cmd-auth.c
> @@ -98,7 +98,11 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void
> *d) return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>
> if (data->dhvlen) {
> - return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + if (!ctrl->dh_tfm)
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + if (nvmet_auth_ctrl_sesskey(req, data->rval + 2 * data->hl,
> + data->dhvlen) < 0)
> + return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> }
>
> response = kmalloc(data->hl, GFP_KERNEL);
> @@ -299,6 +303,8 @@ static int nvmet_auth_challenge(struct nvmet_req *req,
> void *d, int al) int ret = 0;
> int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
>
> + if (ctrl->dh_tfm)
> + data_size += ctrl->dh_keysize;
> if (al < data_size) {
> pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
> al, data_size);
> @@ -317,6 +323,12 @@ static int nvmet_auth_challenge(struct nvmet_req *req,
> void *d, int al) return -ENOMEM;
> get_random_bytes(req->sq->dhchap_c1, data->hl);
> memcpy(data->cval, req->sq->dhchap_c1, data->hl);
> + if (ctrl->dh_tfm) {
> + data->dhgid = ctrl->dh_gid;
> + data->dhvlen = ctrl->dh_keysize;
> + ret = nvmet_auth_ctrl_exponential(req, data->cval + data->hl,
> + data->dhvlen);
> + }
> pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n",
> __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
> req->sq->dhchap_tid, data->hl, data->dhvlen);


Ciao
Stephan


2021-07-18 12:22:57

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters

On 7/17/21 5:03 PM, Stephan Müller wrote:
> Am Freitag, 16. Juli 2021, 13:04:20 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> +#include <linux/module.h>
>> +#include <crypto/internal/kpp.h>
>> +#include <crypto/kpp.h>
>> +#include <crypto/dh.h>
>> +#include <linux/mpi.h>
>> +
>> +/*
>> + * ffdhe2048 generator (g), modulus (p) and group size (q)
>> + */
>> +const u8 ffdhe2048_g[] = { 0x02 };
>
> What about using static const here (and for all the following groups)?
>
Yes, of course. Will be fixing it.

Cheers,

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

2021-07-18 12:23:59

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

On 7/17/21 9:22 AM, Sagi Grimberg wrote:
>> Implement NVMe-oF In-Band authentication. This patch adds two new
>> fabric options 'dhchap_key' to specify the PSK
>
> pre-shared-key.
>
> Also, we need a sysfs knob to rotate the key that will trigger
> re-authentication or even a simple controller(s-plural) reset, so this
> should go beyond just the connection string.
>

Yeah, re-authentication currently is not implemented. I first wanted to
get this patchset out such that we can settle on the userspace interface
(both from host and target).
I'll have to think on how we should handle authentication; one of the
really interesting cases would be when one malicious admin will _just_
send a 'negotiate' command to the controller. As per spec the controller
will be waiting for an 'authentication receive' command to send a
'challenge' payload back to the host. But that will never come, so as it
stands currently the controller is required to abort the connection.
Not very nice.

> P.S. can you add also the nvme-cli code in the next go?
>
Oh, sure. It's already sitting around in my local repo (surprise,
surprise); will be ending it out next time.

>> and 'dhchap_authenticate'
>> to request bi-directional authentication of both the host and the
>> controller.
>
> bidirectional? not uni-directional?
>

Yeah, that's a bit of a misnomer. When a PSK is specified, the
controller will start the authentication protocol such that the
_controller_ can validate the host. If the host wants to authenticate
the controller is needs to set this flag.
Hence bi-directional authentication.
But I'm the first to admit that this is poor wording for the flag.

>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>>   drivers/nvme/host/Kconfig   |  11 +
>>   drivers/nvme/host/Makefile  |   1 +
>>   drivers/nvme/host/auth.c    | 813 ++++++++++++++++++++++++++++++++++++
>>   drivers/nvme/host/auth.h    |  23 +
>>   drivers/nvme/host/core.c    |  77 +++-
>>   drivers/nvme/host/fabrics.c |  65 ++-
>>   drivers/nvme/host/fabrics.h |   8 +
>>   drivers/nvme/host/nvme.h    |  15 +
>>   drivers/nvme/host/trace.c   |  32 ++
>>   9 files changed, 1041 insertions(+), 4 deletions(-)
>>   create mode 100644 drivers/nvme/host/auth.c
>>   create mode 100644 drivers/nvme/host/auth.h
>>
>> diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
>> index c3f3d77f1aac..853c546305e9 100644
>> --- a/drivers/nvme/host/Kconfig
>> +++ b/drivers/nvme/host/Kconfig
>> @@ -85,3 +85,14 @@ config NVME_TCP
>>         from https://github.com/linux-nvme/nvme-cli.
>>         If unsure, say N.
>> +
>> +config NVME_AUTH
>> +    bool "NVM Express over Fabrics In-Band Authentication"
>> +    depends on NVME_TCP
>> +    select CRYPTO_SHA256
>> +    select CRYPTO_SHA512
>> +    help
>> +      This provides support for NVMe over Fabrics In-Band Authentication
>> +      for the NVMe over TCP transport.
>
> In this form, nothing is specific to nvme-tcp here afaict.
>

Indeed. I guess we can leave out the nvme-tcp reference here.

>> +
>> +      If unsure, say N.
>> diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
>> index cbc509784b2e..03748a55a12b 100644
>> --- a/drivers/nvme/host/Makefile
>> +++ b/drivers/nvme/host/Makefile
>> @@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON)        += hwmon.o
>>   nvme-y                    += pci.o
>>   nvme-fabrics-y                += fabrics.o
>> +nvme-fabrics-$(CONFIG_NVME_AUTH)    += auth.o
>>   nvme-rdma-y                += rdma.o
>> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
>> new file mode 100644
>> index 000000000000..448a3adebea6
>> --- /dev/null
>> +++ b/drivers/nvme/host/auth.c
>> @@ -0,0 +1,813 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
>> + */
>> +
>> +#include <linux/crc32.h>
>> +#include <linux/base64.h>
>> +#include <asm/unaligned.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include "nvme.h"
>> +#include "fabrics.h"
>> +#include "auth.h"
>> +
>> +static u32 nvme_dhchap_seqnum;
>> +
>> +struct nvme_dhchap_context {
>
> Maybe nvme_dhchap_queue_context ?
>
> I'm thinking that we should perhaps split
> it to host-wide, subsys-wide and queue specific
> auth contexts?
>
> Let's see...
>

Interestingly enough, that's what I did for the target side.
For the host side I found it easier that way, as then we'll have a
single authentication context which can be deleted after authentication
finished, and be sure that we removed _all_ information.
Security and all that.
Splitting it off would require to remove information on three different
places, and observing life-time rules for them.
So more of a chance to mess things up :-)

>> +    struct crypto_shash *shash_tfm;
>> +    unsigned char *key;
>> +    size_t key_len;
>> +    int qid;
>> +    u32 s1;
>> +    u32 s2;
>> +    u16 transaction;
>> +    u8 status;
>> +    u8 hash_id;
>> +    u8 hash_len;
>> +    u8 c1[64];
>> +    u8 c2[64];
>> +    u8 response[64];
>> +    u8 *ctrl_key;
>> +    int ctrl_key_len;
>> +    u8 *host_key;
>> +    int host_key_len;
>> +    u8 *sess_key;
>> +    int sess_key_len;
>> +};
>> +
>> +struct nvmet_dhchap_hash_map {
>
> nvmet?
>

Yeah; originally I coded that for the target side, and only later moved
it into the host side to have it usable for both.
Will be fixing it up.

>> +    int id;
>> +    int hash_len;
>> +    const char hmac[15];
>> +    const char digest[15];
>> +} hash_map[] = {
>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>> +     .hash_len = 32,
>> +     .hmac = "hmac(sha256)", .digest = "sha256" },
>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>> +     .hash_len = 48,
>> +     .hmac = "hmac(sha384)", .digest = "sha384" },
>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>> +     .hash_len = 64,
>> +     .hmac = "hmac(sha512)", .digest = "sha512" },
>> +};
>> +
>> +const char *nvme_auth_hmac_name(int hmac_id)
>
> Should these arrays be static?
>

Definitely.

>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> +        if (hash_map[i].id == hmac_id)
>> +            return hash_map[i].hmac;
>> +    }
>> +    return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
>> +
>> +const char *nvme_auth_digest_name(int hmac_id)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> +        if (hash_map[i].id == hmac_id)
>> +            return hash_map[i].digest;
>> +    }
>> +    return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
>> +
>> +int nvme_auth_hmac_len(int hmac_id)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> +        if (hash_map[i].id == hmac_id)
>> +            return hash_map[i].hash_len;
>> +    }
>> +    return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_len);
>> +
>> +int nvme_auth_hmac_id(const char *hmac_name)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> +        if (!strncmp(hash_map[i].hmac, hmac_name,
>> +                 strlen(hash_map[i].hmac)))
>> +            return hash_map[i].id;
>> +    }
>> +    return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
>> +
>> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
>> +                    size_t *dhchap_key_len)
>> +{
>> +    unsigned char *dhchap_key;
>> +    u32 crc;
>> +    int key_len;
>> +    size_t allocated_len;
>> +
>> +    allocated_len = strlen(dhchap_secret) - 10;
>
> the 10 feels like a magic here, should at least note this is the
> "DHHC-1:..." prefix.
>

It _is_ magic. And it might even be better to just pass in the string
_without_ the DHHC-1: prefix.

>> +    dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
>> +    if (!dhchap_key)
>> +        return ERR_PTR(-ENOMEM);
>> +
>> +    key_len = base64_decode(dhchap_secret + 10,
>> +                allocated_len, dhchap_key);
>> +    if (key_len != 36 && key_len != 52 &&
>> +        key_len != 68) {
>> +        pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
>> +             key_len);
>> +        kfree(dhchap_key);
>> +        return ERR_PTR(-EINVAL);
>> +    }
>> +    pr_debug("DH-HMAC-CHAP Key: %*ph\n",
>> +         (int)key_len, dhchap_key);
>
> One can argue if even printing this is problematic..
>

Debugging scaffolding. You wouldn't believe how many things can go wrong...

And yes, that should be removed.

>> +
>> +    /* The last four bytes is the CRC in little-endian format */
>> +    key_len -= 4;
>> +    /*
>> +     * The linux implementation doesn't do pre- and post-increments,
>> +     * so we have to do it manually.
>> +     */
>> +    crc = ~crc32(~0, dhchap_key, key_len);
>> +
>> +    if (get_unaligned_le32(dhchap_key + key_len) != crc) {
>> +        pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n",
>> +               get_unaligned_le32(dhchap_key + key_len), crc);
>> +        kfree(dhchap_key);
>> +        return ERR_PTR(-EKEYREJECTED);
>> +    }
>> +    *dhchap_key_len = key_len;
>> +    return dhchap_key;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
>> +
>> +static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
>> +              void *data, size_t tl)
>> +{
>> +    struct nvme_command cmd = {};
>> +    blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
>> +        0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
>> +    struct request_queue *q = qid == NVME_QID_ANY ?
>> +        ctrl->fabrics_q : ctrl->connect_q;
>> +    int ret;
>> +
>> +    cmd.auth_send.opcode = nvme_fabrics_command;
>> +    cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
>> +    cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
>> +    cmd.auth_send.spsp0 = 0x01;
>> +    cmd.auth_send.spsp1 = 0x01;
>> +    cmd.auth_send.tl = tl;
>> +
>> +    ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid,
>> +                     0, flags);
>> +    if (ret)
>> +        dev_dbg(ctrl->device,
>> +            "%s: qid %d error %d\n", __func__, qid, ret);
>
> Maybe a little more informative print rather than __func__ ?
>

Yes, can do.

>> +    return ret;
>> +}
>> +
>> +static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
>> +                 void *buf, size_t al,
>> +                 u16 transaction, u8 expected_msg )
>> +{
>> +    struct nvme_command cmd = {};
>> +    struct nvmf_auth_dhchap_failure_data *data = buf;
>> +    blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
>> +        0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
>> +    struct request_queue *q = qid == NVME_QID_ANY ?
>> +        ctrl->fabrics_q : ctrl->connect_q;
>> +    int ret;
>> +
>> +    cmd.auth_receive.opcode = nvme_fabrics_command;
>> +    cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
>> +    cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
>> +    cmd.auth_receive.spsp0 = 0x01;
>> +    cmd.auth_receive.spsp1 = 0x01;
>> +    cmd.auth_receive.al = al;
>> +
>> +    ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid,
>> +                     0, flags);
>> +    if (ret > 0) {
>> +        dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n",
>> +            __func__, qid, ret);
>> +        ret = -EIO;
>> +    }
>> +    if (ret < 0) {
>> +        dev_dbg(ctrl->device, "%s: qid %d error %d\n",
>> +            __func__, qid, ret);
>> +        return ret;
>> +    }
>> +    dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
>> +        __func__, qid, data->auth_type, data->auth_id);
>> +    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
>> +        data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
>> +        return data->reason_code_explanation;
>> +    }
>> +    if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
>> +        data->auth_id != expected_msg) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d invalid message %02x/%02x\n",
>> +             qid, data->auth_type, data->auth_id);
>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +    }
>> +    if (le16_to_cpu(data->t_id) != transaction) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d invalid transaction ID %d\n",
>> +             qid, le16_to_cpu(data->t_id));
>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
>> +                      struct nvme_dhchap_context *chap,
>> +                      void *buf, size_t buf_size)
>
> Maybe nvme_auth_set_dhchap_negotiate_data ?
>

These are the individual steps in the state machine later on, so I
wanted to keep the names identical.
But I'm open to suggestions.

>> +{
>> +    struct nvmf_auth_dhchap_negotiate_data *data = buf;
>> +    size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
>> +
>> +    if (buf_size < size)
>> +        return -EINVAL;
>> +
>> +    memset((u8 *)buf, 0, size);
>> +    data->auth_type = NVME_AUTH_COMMON_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
>> +    data->t_id = cpu_to_le16(chap->transaction);
>> +    data->sc_c = 0; /* No secure channel concatenation */
>> +    data->napd = 1;
>> +    data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
>> +    data->auth_protocol[0].dhchap.halen = 3;
>> +    data->auth_protocol[0].dhchap.dhlen = 1;
>> +    data->auth_protocol[0].dhchap.idlist[0] =
>> NVME_AUTH_DHCHAP_HASH_SHA256;
>> +    data->auth_protocol[0].dhchap.idlist[1] =
>> NVME_AUTH_DHCHAP_HASH_SHA384;
>> +    data->auth_protocol[0].dhchap.idlist[2] =
>> NVME_AUTH_DHCHAP_HASH_SHA512;
>> +    data->auth_protocol[0].dhchap.idlist[3] =
>> NVME_AUTH_DHCHAP_DHGROUP_NULL;
> You should comment that this routine expects buf to have enough
> room for both negotiate and auth_proto structures.
>
Hmm. I do a check for the overall size at the start, so I'm not sure
what this will buy us.
And actually, anyone wanting to make sense of the implementation would
need to look at the spec anyway.

>> +
>> +    return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
>> +                      struct nvme_dhchap_context *chap,
>> +                      void *buf, size_t buf_size)
>
> Maybe nvme_auth_process_dhchap_challange ?
>

See above. I'd rather have consistent names for the state machine.
But I can change them to 'nvme_process_chchap_<statename>'

>> +{
>> +    struct nvmf_auth_dhchap_challenge_data *data = buf;
>> +    size_t size = sizeof(*data) + data->hl + data->dhvlen;
>> +    const char *gid_name;
>> +
>> +    if (buf_size < size) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +        return -ENOMSG;
>> +    }
>> +
>> +    if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 &&
>> +        data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 &&
>> +        data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n",
>> +             chap->qid, data->hashid);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +        return -EPROTO;
>> +    }
>> +    switch (data->dhgid) {
>> +    case NVME_AUTH_DHCHAP_DHGROUP_NULL:
>> +        gid_name = "null";
>> +        break;
>> +    default:
>> +        gid_name = NULL;
>> +        break;
>> +    }
>> +    if (!gid_name) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
>> +             chap->qid, data->dhgid);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> +        return -EPROTO;
>> +    }
>
> Maybe some spaces between condition blocks?
>

Ok.

>> +    if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> +        return -EPROTO;
>> +    }
>> +    if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen
>> != 0) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
>> +            chap->qid);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> +        return -EPROTO;
>> +    }
>> +    dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n",
>> +        __func__, chap->qid, data->hashid);
>> +    if (nvme_auth_hmac_len(data->hashid) != data->hl) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d: DH-HMAC-CHAP: invalid hash length\n",
>> +            chap->qid);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +        return -EPROTO;
>> +    }
>> +    chap->hash_id = data->hashid;
>> +    chap->hash_len = data->hl;
>> +    chap->s1 = le32_to_cpu(data->seqnum);
>> +    memcpy(chap->c1, data->cval, chap->hash_len);
>> +
>> +    return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
>> +                  struct nvme_dhchap_context *chap,
>> +                  void *buf, size_t buf_size)
>
> nvme_auth_set_dhchap_reply
>

Ah. Now I see what you're getting at.
Okay, will be changing it.

>> +{
>> +    struct nvmf_auth_dhchap_reply_data *data = buf;
>> +    size_t size = sizeof(*data);
>> +
>> +    size += 2 * chap->hash_len;
>> +    if (ctrl->opts->dhchap_auth) {
>
> The ctrl opts is not clear to me. what is dhchap_auth
> mean?
>
As stated above, this is for bi-directional authentication.
And yes, it is poor wording.

'dhchap_bidirectional' ?

> Also shouldn't these params be lifted to the subsys?
>

I kinda like to have it all encapsulated in a common per-queue
structure; on the host side this one isn't even attached to anything, so
any new authentication attempt will allocate a new one, with no chance
of accidentally re-using existing values.
I thought this to be a rather nice property for a state-machine.

>> +        get_random_bytes(chap->c2, chap->hash_len);
>> +        chap->s2 = nvme_dhchap_seqnum++;
>> +    } else
>> +        memset(chap->c2, 0, chap->hash_len);
>> +
>> +    if (chap->host_key_len)
>> +        size += chap->host_key_len;
>> +
>> +    if (buf_size < size)
>> +        return -EINVAL;
>> +
>> +    memset(buf, 0, size);
>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
>> +    data->t_id = cpu_to_le16(chap->transaction);
>> +    data->hl = chap->hash_len;
>> +    data->dhvlen = chap->host_key_len;
>> +    data->seqnum = cpu_to_le32(chap->s2);
>> +    memcpy(data->rval, chap->response, chap->hash_len);
>> +    if (ctrl->opts->dhchap_auth) {
>> +        dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
>> +            __func__, chap->qid,
>> +            chap->hash_len, chap->c2);
>> +        data->cvalid = 1;
>> +        memcpy(data->rval + chap->hash_len, chap->c2,
>> +               chap->hash_len);
>> +    }
>> +    if (chap->host_key_len)
>> +        memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
>> +               chap->host_key_len);
>> +
>> +    return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_success1(struct nvme_ctrl *ctrl,
>> +                     struct nvme_dhchap_context *chap,
>> +                     void *buf, size_t buf_size)
>
> nvme_auth_process_dhchap_success1
>

OK.

>> +{
>> +    struct nvmf_auth_dhchap_success1_data *data = buf;
>> +    size_t size = sizeof(*data);
>> +
>> +    if (ctrl->opts->dhchap_auth)
>> +        size += chap->hash_len;
>> +
>> +
>> +    if (buf_size < size) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +        return -ENOMSG;
>> +    }
>> +
>> +    if (data->hl != chap->hash_len) {
>> +        dev_warn(ctrl->device,
>> +             "qid %d: DH-HMAC-CHAP: invalid hash length %d\n",
>> +             chap->qid, data->hl);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +        return -EPROTO;
>> +    }
>> +
>> +    if (!data->rvalid)
>> +        return 0;
>> +
>> +    /* Validate controller response */
>> +    if (memcmp(chap->response, data->rval, data->hl)) {
>> +        dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
>> +            __func__, chap->qid, chap->hash_len, data->rval);
>> +        dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
>> +            __func__, chap->qid, chap->hash_len, chap->response);
>> +        dev_warn(ctrl->device,
>> +             "qid %d: DH-HMAC-CHAP: controller authentication failed\n",
>> +             chap->qid);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +        return -EPROTO;
>> +    }
>> +    dev_info(ctrl->device,
>> +         "qid %d: DH-HMAC-CHAP: controller authenticated\n",
>> +        chap->qid);
>> +    return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
>> +                     struct nvme_dhchap_context *chap,
>> +                     void *buf, size_t buf_size)
>
> same
>
>> +{
>> +    struct nvmf_auth_dhchap_success2_data *data = buf;
>> +    size_t size = sizeof(*data);
>> +
>> +    memset(buf, 0, size);
>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
>> +    data->t_id = cpu_to_le16(chap->transaction);
>> +
>> +    return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
>> +                     struct nvme_dhchap_context *chap,
>> +                     void *buf, size_t buf_size)
>
> same
>
>> +{
>> +    struct nvmf_auth_dhchap_failure_data *data = buf;
>> +    size_t size = sizeof(*data);
>> +
>> +    memset(buf, 0, size);
>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> +    data->t_id = cpu_to_le16(chap->transaction);
>> +    data->reason_code = 1;
>> +    data->reason_code_explanation = chap->status;
>> +
>> +    return size;
>> +}
>> +
>> +int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>> +              struct nvme_dhchap_context *chap)
>
> Maybe _select_hf (hash function)? not a must, just sticks
> to the spec language.
>

Hmm. Will be checking.

>> +{
>> +    char *hash_name;
>> +    int ret;
>> +
>> +    switch (chap->hash_id) {
>> +    case NVME_AUTH_DHCHAP_HASH_SHA256:
>> +        hash_name = "hmac(sha256)";
>> +        break;
>> +    case NVME_AUTH_DHCHAP_HASH_SHA384:
>> +        hash_name = "hmac(sha384)";
>> +        break;
>> +    case NVME_AUTH_DHCHAP_HASH_SHA512:
>> +        hash_name = "hmac(sha512)";
>> +        break;
>> +    default:
>> +        hash_name = NULL;
>> +        break;
>> +    }
>> +    if (!hash_name) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        return -EPROTO;
>> +    }
>> +    chap->shash_tfm = crypto_alloc_shash(hash_name, 0,
>> +                         CRYPTO_ALG_ALLOCATES_MEMORY);
>> +    if (IS_ERR(chap->shash_tfm)) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        chap->shash_tfm = NULL;
>> +        return -EPROTO;
>> +    }
>> +    if (!chap->key) {
>> +        dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
>> +             chap->qid);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        crypto_free_shash(chap->shash_tfm);
>
> Wouldn't it better to check this before allocating the tfm?
>

Indeed. Will be changing it.

>> +        chap->shash_tfm = NULL;
>> +        return -EINVAL;
>> +    }
>> +    ret = crypto_shash_setkey(chap->shash_tfm, chap->key,
>> chap->key_len);
>> +    if (ret) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        crypto_free_shash(chap->shash_tfm);
>> +        chap->shash_tfm = NULL;
>> +        return ret;
>> +    }
>> +    dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>> +         chap->qid, hash_name);
>> +    return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
>> +                      struct nvme_dhchap_context *chap)
>> +{
>> +    SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>> +    u8 buf[4], *challenge = chap->c1;
>> +    int ret;
>> +
>> +    dev_dbg(ctrl->device, "%s: qid %d host response seq %d
>> transaction %d\n",
>> +        __func__, chap->qid, chap->s1, chap->transaction);
>> +    shash->tfm = chap->shash_tfm;
>> +    ret = crypto_shash_init(shash);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, challenge, chap->hash_len);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le32(chap->s1, buf);
>> +    ret = crypto_shash_update(shash, buf, 4);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le16(chap->transaction, buf);
>> +    ret = crypto_shash_update(shash, buf, 2);
>> +    if (ret)
>> +        goto out;
>> +    memset(buf, 0, sizeof(buf));
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, "HostHost", 8);
>
> HostHost ? Can you refer me to the specific section
> that talks about this?
>

NVMe 2.0 section DH-HMAC-CHAP_Reply Message, paragraph Response Value.
HostHost.

> Would be good to have a comment on the format fed to the
> shash.
>

Yes, will be doing so.

>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>> +                  strlen(ctrl->opts->host->nqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>> +                strlen(ctrl->opts->subsysnqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_final(shash, chap->response);
>> +out:
>> +    return ret;
>> +}
>> +
>> +static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
>> +                      struct nvme_dhchap_context *chap)
>> +{
>> +    SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>> +    u8 buf[4], *challenge = chap->c2;
>> +    int ret;
>> +
>> +    dev_dbg(ctrl->device, "%s: qid %d host response seq %d
>> transaction %d\n",
>> +        __func__, chap->qid, chap->s2, chap->transaction);
>> +    dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
>> +        __func__, chap->qid, chap->hash_len, challenge);
>> +    dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
>> +        __func__, chap->qid, ctrl->opts->subsysnqn);
>> +    dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
>> +        __func__, chap->qid, ctrl->opts->host->nqn);
>> +    shash->tfm = chap->shash_tfm;
>> +    ret = crypto_shash_init(shash);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, challenge, chap->hash_len);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le32(chap->s2, buf);
>> +    ret = crypto_shash_update(shash, buf, 4);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le16(chap->transaction, buf);
>> +    ret = crypto_shash_update(shash, buf, 2);
>> +    if (ret)
>> +        goto out;
>> +    memset(buf, 0, 4);
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, "Controller", 10);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>> +                  strlen(ctrl->opts->subsysnqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>> +                  strlen(ctrl->opts->host->nqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_final(shash, chap->response);
>> +out:
>> +    return ret;
>> +}
>> +
>> +int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
>> +               struct nvme_dhchap_context *chap)
>> +{
>> +    int ret;
>> +    u8 key_hash;
>> +    const char *hmac_name;
>> +    struct crypto_shash *key_tfm;
>> +
>> +    if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
>> +           &key_hash) != 1)
>> +        return -EINVAL;
>
> I'd expect that the user will pass in a secret key (as binary)
> the the driver will build the spec compliant formatted string no?
> > Am I not reading this correctly?
>

I'm under the impression that this is the format into which the
User/Admin will get hold of the secret key.
Spec says:

'... all NVMe over Fabrics entities shall support the following ASCII
representation of secrets ...'

And as the userspace interface is the only way how the user/admin
_could_ interact with the NVMe over Fabrics entities in Linux I guess
we'll need to be able to parse it.

We sure could allow a binary secret, too, but then what would be the
point in converting it into the secret representation?
The protocol revolves around the binary secret, not the transport
representation.

>> +
>> +    chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
>> +                         &chap->key_len);
>> +    if (IS_ERR(chap->key)) {
>> +        ret = PTR_ERR(chap->key);
>> +        chap->key = NULL;
>> +        return ret;
>> +    }
>> +
>> +    if (key_hash == 0)
>> +        return 0;
>> +
>> +    hmac_name = nvme_auth_hmac_name(key_hash);
>> +    if (!hmac_name) {
>> +        pr_debug("Invalid key hash id %d\n", key_hash);
>> +        return -EKEYREJECTED;
>> +    }
>
> Why does the user influence the hmac used? isn't that is driven
> by the susbsystem?
>
> I don't think that the user should choose in this level.
>

That is another weirdness of the spec.
The _secret_ will be hashed with a specific function, and that function
is stated in the transport representation.
(Cf section "DH-HMAC-CHAP Security Requirements").
This is _not_ the hash function used by the authentication itself, which
will be selected by the protocol.
So it's not the user here, but rather the transport specification of the
key which selects the hash algorithm.

>> +
>> +    key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
>> +    if (IS_ERR(key_tfm)) {
>> +        kfree(chap->key);
>> +        chap->key = NULL;
>> +        ret = PTR_ERR(key_tfm);
>
> You set ret and later return 0? I think that the success
> path in the else clause is hard to read and error prone...
>

Do I? Will need to fix it up.

>> +    } else {
>> +        SHASH_DESC_ON_STACK(shash, key_tfm);
>> +
>> +        shash->tfm = key_tfm;
>> +        ret = crypto_shash_setkey(key_tfm, chap->key,
>> +                      chap->key_len);
>> +        if (ret < 0) {
>> +            crypto_free_shash(key_tfm);
>> +            kfree(chap->key);
>> +            chap->key = NULL;
>> +            return ret;
>> +        }
>> +        crypto_shash_init(shash);
>> +        crypto_shash_update(shash, ctrl->opts->host->nqn,
>> +                    strlen(ctrl->opts->host->nqn));
>> +        crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
>> +        crypto_shash_final(shash, chap->key);
>> +        crypto_free_shash(key_tfm);
>
> Shouldn't these be done when preparing the dh-hmac-chap reply?
>

By setting the hash here I avoid having to pass the required hash
function for the secret transformation.
I could be doing the entire secret transformation thingie when preparing
the reply; reason why I did it here is that _having_ a secret is the
precondition to everything else, so I wanted to check upfront for that.
But I'll check what would happen if I move it.

>> +    }
>> +    return 0;
>> +}
>> +
>> +void nvme_auth_free(struct nvme_dhchap_context *chap)
>> +{
>> +    if (chap->shash_tfm)
>> +        crypto_free_shash(chap->shash_tfm);
>> +    if (chap->key)
>> +        kfree(chap->key);
>> +    if (chap->ctrl_key)
>> +        kfree(chap->ctrl_key);
>> +    if (chap->host_key)
>> +        kfree(chap->host_key);
>> +    if (chap->sess_key)
>> +        kfree(chap->sess_key);
>
> No need to check null for kfree...
>

Will be fixing it up.

>> +    kfree(chap);
>> +}
>> +
>> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
>> +{
>> +    struct nvme_dhchap_context *chap;
>> +    void *buf;
>> +    size_t buf_size, tl;
>> +    int ret = 0;
>> +
>> +    chap = kzalloc(sizeof(*chap), GFP_KERNEL);
>> +    if (!chap)
>> +        return -ENOMEM;
>> +    chap->qid = qid;
>> +    chap->transaction = ctrl->transaction++;
>> +
>> +    ret = nvme_auth_generate_key(ctrl, chap);
>> +    if (ret) {
>> +        dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n",
>> +            __func__, ret);
>> +        nvme_auth_free(chap);
>> +        return ret;
>> +    }
>> +
>> +    /*
>> +     * Allocate a large enough buffer for the entire negotiation:
>> +     * 4k should be enough to ffdhe8192.
>> +     */
>> +    buf_size = 4096;
>> +    buf = kzalloc(buf_size, GFP_KERNEL);
>> +    if (!buf) {
>> +        ret = -ENOMEM;
>> +        goto out;
>> +    }
>> +
>> +    /* DH-HMAC-CHAP Step 1: send negotiate */
>
> I'd consider breaking these into sub-routines.
>

Which ones? The preparation step?
Sure, can do.

>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n",
>> +        __func__, qid);
>> +    ret = nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size);
>> +    if (ret < 0)
>> +        goto out;
>> +    tl = ret;
>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>> +    if (ret)
>> +        goto out;
>> +
>> +    memset(buf, 0, buf_size);
>> +    ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
>> +                NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
>> +    if (ret < 0) {
>> +        dev_dbg(ctrl->device,
>> +            "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n",
>> +            __func__, qid);
>> +        goto out;
>> +    }
>> +    if (ret > 0) {
>> +        chap->status = ret;
>> +        goto fail1;
>> +    }
>> +
>> +    /* DH-HMAC-CHAP Step 2: receive challenge */
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n",
>> +        __func__, qid);
>> +
>> +    ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size);
>> +    if (ret) {
>> +        /* Invalid parameters for negotiate */
>> +        goto fail2;
>> +    }
>> +
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n",
>> +        __func__, qid);
>> +    ret = nvme_auth_select_hash(ctrl, chap);
>> +    if (ret)
>> +        goto fail2;
>> +
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
>> +        __func__, qid);
>> +    ret = nvme_auth_dhchap_host_response(ctrl, chap);
>> +    if (ret)
>> +        goto fail2;
>> +
>> +    /* DH-HMAC-CHAP Step 3: send reply */
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n",
>> +        __func__, qid);
>> +    ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size);
>> +    if (ret < 0)
>> +        goto fail2;
>> +
>> +    tl = ret;
>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>> +    if (ret)
>> +        goto fail2;
>> +
>> +    memset(buf, 0, buf_size);
>> +    ret = nvme_auth_receive(ctrl, qid, buf, buf_size, chap->transaction,
>> +                NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
>> +    if (ret < 0) {
>> +        dev_dbg(ctrl->device,
>> +            "%s: qid %d DH-HMAC-CHAP failed to receive success1\n",
>> +            __func__, qid);
>> +        goto out;
>> +    }
>> +    if (ret > 0) {
>> +        chap->status = ret;
>> +        goto fail1;
>> +    }
>> +
>> +    if (ctrl->opts->dhchap_auth) {
>> +        dev_dbg(ctrl->device,
>> +            "%s: qid %d DH-HMAC-CHAP controller response\n",
>> +            __func__, qid);
>> +        ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
>> +        if (ret)
>> +            goto fail2;
>> +    }
>> +
>> +    /* DH-HMAC-CHAP Step 4: receive success1 */
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n",
>> +        __func__, qid);
>> +    ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size);
>> +    if (ret < 0) {
>> +        /* Controller authentication failed */
>> +        goto fail2;
>> +    }
>> +    tl = ret;
>> +    /* DH-HMAC-CHAP Step 5: send success2 */
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n",
>> +        __func__, qid);
>> +    tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size);
>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>> +    if (!ret)
>> +        goto out;
>> +
>> +fail1:
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status
>> %x\n",
>> +        __func__, qid, chap->status);
>> +    goto out;
>> +
>> +fail2:
>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status
>> %x\n",
>> +        __func__, qid, chap->status);
>> +    tl = nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size);
>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>> +
>> +out:
>> +    if (!ret && chap->status)
>> +        ret = -EPROTO;
>> +    if (!ret) {
>> +        ctrl->dhchap_hash = chap->hash_id;
>> +    }
>> +    kfree(buf);
>> +    nvme_auth_free(chap);
>> +    return ret;
>> +}
>> diff --git a/drivers/nvme/host/auth.h b/drivers/nvme/host/auth.h
>> new file mode 100644
>> index 000000000000..4950b1cb9470
>> --- /dev/null
>> +++ b/drivers/nvme/host/auth.h
>> @@ -0,0 +1,23 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
>> + */
>> +
>> +#ifndef _NVME_AUTH_H
>> +#define _NVME_AUTH_H
>> +
>> +const char *nvme_auth_dhgroup_name(int dhgroup_id);
>> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id);
>> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id);
>> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id);
>> +int nvme_auth_dhgroup_id(const char *dhgroup_name);
>> +
>> +const char *nvme_auth_hmac_name(int hmac_id);
>> +const char *nvme_auth_digest_name(int hmac_id);
>> +int nvme_auth_hmac_id(const char *hmac_name);
>> +int nvme_auth_hmac_len(int hmac_len);
>> +
>> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
>> +                    size_t *dhchap_key_len);
>> +
>> +#endif /* _NVME_AUTH_H */
>> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
>> index 11779be42186..7ce9b666dc09 100644
>> --- a/drivers/nvme/host/core.c
>> +++ b/drivers/nvme/host/core.c
>> @@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl,
>> struct request *rq,
>>           switch (ctrl->state) {
>>           case NVME_CTRL_CONNECTING:
>>               if (blk_rq_is_passthrough(rq) &&
>> nvme_is_fabrics(req->cmd) &&
>> -                req->cmd->fabrics.fctype == nvme_fabrics_type_connect)
>> +                (req->cmd->fabrics.fctype ==
>> nvme_fabrics_type_connect ||
>> +                 req->cmd->fabrics.fctype ==
>> nvme_fabrics_type_auth_send ||
>> +                 req->cmd->fabrics.fctype ==
>> nvme_fabrics_type_auth_receive))
>>                   return true;
>>               break;
>>           default:
>> @@ -3426,6 +3428,66 @@ static ssize_t
>> nvme_ctrl_fast_io_fail_tmo_store(struct device *dev,
>>   static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
>>       nvme_ctrl_fast_io_fail_tmo_show, nvme_ctrl_fast_io_fail_tmo_store);
>> +#ifdef CONFIG_NVME_AUTH
>> +struct nvmet_dhchap_hash_map {
>> +    int id;
>> +    const char name[15];
>> +} hash_map[] = {
>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>> +     .name = "hmac(sha256)", },
>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>> +     .name = "hmac(sha384)", },
>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>> +     .name = "hmac(sha512)", },
>> +};
>> +
>> +static ssize_t dhchap_hash_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> +        if (hash_map[i].id == ctrl->dhchap_hash)
>> +            return sprintf(buf, "%s\n", hash_map[i].name);
>> +    }
>> +    return sprintf(buf, "none\n");
>> +}
>> +DEVICE_ATTR_RO(dhchap_hash);
>> +
>> +struct nvmet_dhchap_group_map {
>> +    int id;
>> +    const char name[15];
>> +} dhgroup_map[] = {
>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
>> +     .name = "NULL", },
>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_2048,
>> +     .name = "ffdhe2048", },
>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_3072,
>> +     .name = "ffdhe3072", },
>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_4096,
>> +     .name = "ffdhe4096", },
>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_6144,
>> +     .name = "ffdhe6144", },
>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_8192,
>> +     .name = "ffdhe8192", },
>> +};
>> +
>> +static ssize_t dhchap_dhgroup_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
>> +    int i;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>> +        if (hash_map[i].id == ctrl->dhchap_dhgroup)
>> +            return sprintf(buf, "%s\n", dhgroup_map[i].name);
>> +    }
>> +    return sprintf(buf, "none\n");
>> +}
>> +DEVICE_ATTR_RO(dhchap_dhgroup);
>> +#endif
>> +
>>   static struct attribute *nvme_dev_attrs[] = {
>>       &dev_attr_reset_controller.attr,
>>       &dev_attr_rescan_controller.attr,
>> @@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = {
>>       &dev_attr_reconnect_delay.attr,
>>       &dev_attr_fast_io_fail_tmo.attr,
>>       &dev_attr_kato.attr,
>> +#ifdef CONFIG_NVME_AUTH
>> +    &dev_attr_dhchap_hash.attr,
>> +    &dev_attr_dhchap_dhgroup.attr,
>> +#endif
>>       NULL
>>   };
>> @@ -3470,6 +3536,10 @@ static umode_t
>> nvme_dev_attrs_are_visible(struct kobject *kobj,
>>           return 0;
>>       if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
>>           return 0;
>> +#ifdef CONFIG_NVME_AUTH
>> +    if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts)
>> +        return 0;
>> +#endif
>>       return a->mode;
>>   }
>> @@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)
>>       BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
>>       BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
>>       BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8);
>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16);
>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16);
>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16);
>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16);
>>   }
>> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
>> index a5469fd9d4c3..6404ab9b604b 100644
>> --- a/drivers/nvme/host/fabrics.c
>> +++ b/drivers/nvme/host/fabrics.c
>> @@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
>>       union nvme_result res;
>>       struct nvmf_connect_data *data;
>>       int ret;
>> +    u32 result;
>>       cmd.connect.opcode = nvme_fabrics_command;
>>       cmd.connect.fctype = nvme_fabrics_type_connect;
>> @@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
>>           goto out_free_data;
>>       }
>> -    ctrl->cntlid = le16_to_cpu(res.u16);
>> -
>> +    result = le32_to_cpu(res.u32);
>> +    ctrl->cntlid = result & 0xFFFF;
>> +    if ((result >> 16) & 2) {
>> +        /* Authentication required */
>> +        ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
>> +        if (ret)
>> +            dev_warn(ctrl->device,
>> +                 "qid 0: authentication failed\n");
>> +        else
>> +            dev_info(ctrl->device,
>> +                 "qid 0: authenticated\n");
>
> info is too chatty.
>

Hmm. I know I need to work on logging...

>> +    }
>>   out_free_data:
>>       kfree(data);
>>       return ret;
>> @@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl,
>> u16 qid)
>>       struct nvmf_connect_data *data;
>>       union nvme_result res;
>>       int ret;
>> +    u32 result;
>>       cmd.connect.opcode = nvme_fabrics_command;
>>       cmd.connect.fctype = nvme_fabrics_type_connect;
>> @@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl,
>> u16 qid)
>>           nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32),
>>                          &cmd, data);
>>       }
>> +    result = le32_to_cpu(res.u32);
>> +    if ((result >> 16) & 2) {
>> +        /* Authentication required */
>> +        ret = nvme_auth_negotiate(ctrl, qid);
>> +        if (ret)
>> +            dev_warn(ctrl->device,
>> +                 "qid %u: authentication failed\n", qid);
>> +        else
>> +            dev_info(ctrl->device,
>> +                 "qid %u: authenticated\n", qid);
>> +    }
>>       kfree(data);
>>       return ret;
>>   }
>> @@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
>>       { NVMF_OPT_NR_POLL_QUEUES,    "nr_poll_queues=%d"    },
>>       { NVMF_OPT_TOS,            "tos=%d"        },
>>       { NVMF_OPT_FAIL_FAST_TMO,    "fast_io_fail_tmo=%d"    },
>> +    { NVMF_OPT_DHCHAP_SECRET,    "dhchap_secret=%s"    },
>> +    { NVMF_OPT_DHCHAP_AUTH,        "authenticate"        },
>> +    { NVMF_OPT_DHCHAP_GROUP,    "dhchap_group=%s"    },
>
> Isn't the group driven by the subsystem? also why is there a
> "authenticate" boolean? what is it good for?
>
Ah. Right. Of course, the 'group' is pointless here.
And the 'authenticate' bool is the abovementioned bidirectional
authentication.
I _do_ need to give it another name.

>>       { NVMF_OPT_ERR,            NULL            }
>>   };
>> @@ -824,6 +850,35 @@ static int nvmf_parse_options(struct
>> nvmf_ctrl_options *opts,
>>               }
>>               opts->tos = token;
>>               break;
>> +        case NVMF_OPT_DHCHAP_SECRET:
>> +            p = match_strdup(args);
>> +            if (!p) {
>> +                ret = -ENOMEM;
>> +                goto out;
>> +            }
>> +            if (strncmp(p, "DHHC-1:00:", 10)) {
>> +                pr_err("Invalid DH-CHAP secret %s\n", p);
>> +                ret = -EINVAL;
>> +                goto out;
>> +            }
>> +            kfree(opts->dhchap_secret);
>> +            opts->dhchap_secret = p;
>> +            break;
>> +        case NVMF_OPT_DHCHAP_AUTH:
>> +            opts->dhchap_auth = true;
>> +            break;
>> +        case NVMF_OPT_DHCHAP_GROUP:
>> +            if (match_int(args, &token)) {
>> +                ret = -EINVAL;
>> +                goto out;
>> +            }
>> +            if (token <= 0) {
>> +                pr_err("Invalid dhchap_group %d\n", token);
>> +                ret = -EINVAL;
>> +                goto out;
>> +            }
>> +            opts->dhchap_group = token;
>> +            break;
>>           default:
>>               pr_warn("unknown parameter or missing value '%s' in ctrl
>> creation request\n",
>>                   p);
>> @@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options
>> *opts)
>>       kfree(opts->subsysnqn);
>>       kfree(opts->host_traddr);
>>       kfree(opts->host_iface);
>> +    kfree(opts->dhchap_secret);
>>       kfree(opts);
>>   }
>>   EXPORT_SYMBOL_GPL(nvmf_free_options);
>> @@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options);
>>                    NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \
>>                    NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
>>                    NVMF_OPT_DISABLE_SQFLOW |\
>> -                 NVMF_OPT_FAIL_FAST_TMO)
>> +                 NVMF_OPT_CTRL_LOSS_TMO |\
>> +                 NVMF_OPT_FAIL_FAST_TMO |\
>> +                 NVMF_OPT_DHCHAP_SECRET |\
>> +                 NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)
>>   static struct nvme_ctrl *
>>   nvmf_create_ctrl(struct device *dev, const char *buf)
>> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
>> index a146cb903869..535bc544f0f6 100644
>> --- a/drivers/nvme/host/fabrics.h
>> +++ b/drivers/nvme/host/fabrics.h
>> @@ -67,6 +67,9 @@ enum {
>>       NVMF_OPT_TOS        = 1 << 19,
>>       NVMF_OPT_FAIL_FAST_TMO    = 1 << 20,
>>       NVMF_OPT_HOST_IFACE    = 1 << 21,
>> +    NVMF_OPT_DHCHAP_SECRET    = 1 << 22,
>> +    NVMF_OPT_DHCHAP_AUTH    = 1 << 23,
>> +    NVMF_OPT_DHCHAP_GROUP    = 1 << 24,
>>   };
>>   /**
>> @@ -96,6 +99,8 @@ enum {
>>    * @max_reconnects: maximum number of allowed reconnect attempts
>> before removing
>>    *              the controller, (-1) means reconnect forever, zero
>> means remove
>>    *              immediately;
>> + * @dhchap_secret: DH-HMAC-CHAP secret
>> + * @dhchap_auth: DH-HMAC-CHAP authenticate controller
>>    * @disable_sqflow: disable controller sq flow control
>>    * @hdr_digest: generate/verify header digest (TCP)
>>    * @data_digest: generate/verify data digest (TCP)
>> @@ -120,6 +125,9 @@ struct nvmf_ctrl_options {
>>       unsigned int        kato;
>>       struct nvmf_host    *host;
>>       int            max_reconnects;
>> +    char            *dhchap_secret;
>> +    int            dhchap_group;
>> +    bool            dhchap_auth;
>>       bool            disable_sqflow;
>>       bool            hdr_digest;
>>       bool            data_digest;
>> diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
>> index 18ef8dd03a90..bcd5b8276c26 100644
>> --- a/drivers/nvme/host/nvme.h
>> +++ b/drivers/nvme/host/nvme.h
>> @@ -328,6 +328,12 @@ struct nvme_ctrl {
>>       struct work_struct ana_work;
>>   #endif
>> +#ifdef CONFIG_NVME_AUTH
>> +    u16 transaction;
>> +    u8 dhchap_hash;
>> +    u8 dhchap_dhgroup;
>
> Do multiple controllers in the same subsystem have different
> params? no, so I think these should be lifted to subsys.
>

It doesn't actually say in the spec; it always refers to the params as
being set by the controller.
So it could be either; maybe we should ask for clafication at the fmds call.

>> +#endif
>> +
>>       /* Power saving configuration */
>>       u64 ps_max_latency_us;
>>       bool apst_enabled;
>> @@ -874,6 +880,15 @@ static inline bool nvme_ctrl_sgl_supported(struct
>> nvme_ctrl *ctrl)
>>       return ctrl->sgls & ((1 << 0) | (1 << 1));
>>   }
>> +#ifdef CONFIG_NVME_AUTH
>> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid);
>> +#else
>> +static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
>> +{
>> +    return -EPROTONOSUPPORT;
>> +}
>> +#endif
>> +
>>   u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
>>                u8 opcode);
>>   int nvme_execute_passthru_rq(struct request *rq);
>> diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c
>> index 6543015b6121..66f75d8ea925 100644
>> --- a/drivers/nvme/host/trace.c
>> +++ b/drivers/nvme/host/trace.c
>
> I'd split out the tracing logic.
>
Okay.

Cheers,

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

2021-07-18 12:26:24

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 10/11] nvmet-auth: implement support for augmented challenge

On 7/17/21 6:49 PM, Stephan Müller wrote:
> Am Freitag, 16. Juli 2021, 13:04:27 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> Implement support for augmented challenge with FFDHE groups.
>> This patch adds a new configfs attribute 'dhchap_dhgroup' to
>> select the DH group to use.
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>> drivers/nvme/target/auth.c | 241 ++++++++++++++++++++++++-
>> drivers/nvme/target/configfs.c | 31 ++++
>> drivers/nvme/target/fabrics-cmd-auth.c | 14 +-
>> 3 files changed, 281 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
>> index 00c7d051dfb1..cc7f12a7c8bf 100644
>> --- a/drivers/nvme/target/auth.c
>> +++ b/drivers/nvme/target/auth.c
>> @@ -58,11 +58,56 @@ int nvmet_auth_set_host_key(struct nvmet_host *host,
>> const char *secret)
>>
>> int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
>> {
>> + struct nvmet_host_link *p;
>> + struct nvmet_host *host = NULL;
>> + const char *dhgroup_kpp;
>> int ret = -ENOTSUPP;
>>
>> if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
>> return 0;
>>
>> + down_read(&nvmet_config_sem);
>> + if (ctrl->subsys->type == NVME_NQN_DISC)
>> + goto out_unlock;
>> +
>> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
>> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
>> + continue;
>> + host = p->host;
>> + break;
>> + }
>> + if (!host) {
>> + pr_debug("host %s not found\n", ctrl->hostnqn);
>> + ret = -ENXIO;
>> + goto out_unlock;
>> + }
>> +
>> + if (host->dhchap_dhgroup_id != dhgroup_id) {
>> + ret = -EINVAL;
>> + goto out_unlock;
>> + }
>> + dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id);
>> + if (!dhgroup_kpp) {
>> + ret = -EINVAL;
>> + goto out_unlock;
>> + }
>> + ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0);
>> + if (IS_ERR(ctrl->dh_tfm)) {
>> + pr_debug("failed to setup DH group %d, err %ld\n",
>> + dhgroup_id, PTR_ERR(ctrl->dh_tfm));
>> + ret = PTR_ERR(ctrl->dh_tfm);
>> + ctrl->dh_tfm = NULL;
>> + } else {
>> + ctrl->dh_gid = dhgroup_id;
>> + ctrl->dh_keysize = nvme_auth_dhgroup_pubkey_size(dhgroup_id);
>> + pr_debug("select DH group %d keysize %d\n",
>> + ctrl->dh_gid, ctrl->dh_keysize);
>> + ret = 0;
>> + }
>> +
>> +out_unlock:
>> + up_read(&nvmet_config_sem);
>> +
>> return ret;
>> }
>>
>> @@ -192,6 +237,101 @@ bool nvmet_check_auth_status(struct nvmet_req *req)
>> return true;
>> }
>>
>> +static int nvmet_auth_hash_sesskey(struct nvmet_req *req, u8 *hashed_key)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + const char *hmac_name, *digest_name;
>> + struct crypto_shash *tfm;
>> + int hmac_id, ret;
>> +
>> + if (!ctrl->shash_tfm) {
>> + pr_debug("%s: hash alg not set\n", __func__);
>> + return -EINVAL;
>> + }
>> + hmac_name = crypto_shash_alg_name(ctrl->shash_tfm);
>> + hmac_id = nvme_auth_hmac_id(hmac_name);
>> + if (hmac_id < 0) {
>> + pr_debug("%s: unsupported hmac %s\n", __func__,
>> + hmac_name);
>> + return -EINVAL;
>> + }
>> + digest_name = nvme_auth_digest_name(hmac_id);
>> + if (!digest_name) {
>> + pr_debug("%s: failed to get digest for %s\n", __func__,
>> + hmac_name);
>> + return -EINVAL;
>> + }
>> + tfm = crypto_alloc_shash(digest_name, 0, 0);
>> + if (IS_ERR(tfm))
>> + return -ENOMEM;
>> +
>> + ret = crypto_shash_tfm_digest(tfm, req->sq->dhchap_skey,
>> + req->sq->dhchap_skey_len, hashed_key);
>> + if (ret < 0)
>> + pr_debug("%s: Failed to hash digest len %d\n", __func__,
>> + req->sq->dhchap_skey_len);
>> +
>> + crypto_free_shash(tfm);
>> + return ret;
>> +}
>> +
>> +static int nvmet_auth_augmented_challenge(struct nvmet_req *req,
>> + u8 *challenge, u8 *aug)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + struct crypto_shash *tfm;
>> + struct shash_desc *desc;
>> + u8 *hashed_key;
>> + const char *hash_name;
>> + int hash_len = req->sq->dhchap_hash_len;
>> + int ret;
>> +
>> + hashed_key = kmalloc(hash_len, GFP_KERNEL);
>> + if (!hashed_key)
>> + return -ENOMEM;
>> +
>> + ret = nvmet_auth_hash_sesskey(req, hashed_key);
>> + if (ret < 0) {
>> + pr_debug("failed to hash session key, err %d\n", ret);
>> + kfree(hashed_key);
>> + return ret;
>> + }
>> + hash_name = crypto_shash_alg_name(ctrl->shash_tfm);
>> + if (!hash_name) {
>> + pr_debug("Invalid hash algoritm\n");
>> + return -EINVAL;
>> + }
>> + tfm = crypto_alloc_shash(hash_name, 0, 0);
>> + if (IS_ERR(tfm)) {
>> + ret = PTR_ERR(tfm);
>> + goto out_free_key;
>> + }
>> + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
>> + GFP_KERNEL);
>> + if (!desc) {
>> + ret = -ENOMEM;
>> + goto out_free_hash;
>> + }
>> + desc->tfm = tfm;
>> +
>> + ret = crypto_shash_setkey(tfm, hashed_key, hash_len);
>> + if (ret)
>> + goto out_free_desc;
>> + ret = crypto_shash_init(desc);
>> + if (ret)
>> + goto out_free_desc;
>> + crypto_shash_update(desc, challenge, hash_len);
>> + crypto_shash_final(desc, aug);
>> +
>> +out_free_desc:
>> + kfree_sensitive(desc);
>> +out_free_hash:
>> + crypto_free_shash(tfm);
>> +out_free_key:
>> + kfree(hashed_key);
>> + return ret;
>> +}
>> +
>> int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
>> unsigned int shash_len)
>> {
>> @@ -202,8 +342,15 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8
>> *response, int ret;
>>
>> if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> - ret = -ENOTSUPP;
>> - goto out;
>> + challenge = kmalloc(shash_len, GFP_KERNEL);
>
> Alignment?
>

With what?
And why?

>> + if (!challenge) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> + ret = nvmet_auth_augmented_challenge(req, req->sq->dhchap_c1,
>> + challenge);
>> + if (ret)
>> + goto out;
>> }
>>
>> shash->tfm = ctrl->shash_tfm;
>> @@ -264,8 +411,15 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8
>> *response, ctrl->cntlid, ctrl->hostnqn);
>>
>> if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> - ret = -ENOTSUPP;
>> - goto out;
>> + challenge = kmalloc(shash_len, GFP_KERNEL);
>
> dto.

dto.

>> + if (!challenge) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> + ret = nvmet_auth_augmented_challenge(req, req->sq->dhchap_c2,
>> + challenge);
>> + if (ret)
>> + goto out;
>> }
>>
>> shash->tfm = ctrl->shash_tfm;
>> @@ -307,6 +461,85 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8
>> *response, return 0;
>> }
>>
>> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
>> + u8 *buf, int buf_size)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + struct kpp_request *kpp_req;
>> + struct crypto_wait wait;
>> + char *pkey;
>> + struct scatterlist dst;
>> + int ret, pkey_len;
>> +
>> + if (ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
>> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
>> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
>> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
>> + ctrl->dh_gid == NVME_AUTH_DHCHAP_DHGROUP_8192) {
>> + struct dh p = {0};
>> + int bits = nvme_auth_dhgroup_pubkey_size(ctrl->dh_gid) << 3;
>> +
>> + ret = crypto_ffdhe_params(&p, bits);
>> + if (ret)
>> + return ret;
>> +
>> + p.key = ctrl->dhchap_key;
>> + p.key_size = ctrl->dhchap_key_len;
>> +
>> + pkey_len = crypto_dh_key_len(&p);
>> + pkey = kmalloc(pkey_len, GFP_KERNEL);
>> + if (!pkey)
>> + return -ENOMEM;
>> +
>> + get_random_bytes(pkey, pkey_len);
>> + ret = crypto_dh_encode_key(pkey, pkey_len, &p);
>> + if (ret) {
>> + pr_debug("failed to encode private key, error %d\n",
>> + ret);
>> + goto out;
>> + }
>> + } else {
>> + pr_warn("invalid dh group %d\n", ctrl->dh_gid);
>> + return -EINVAL;
>> + }
>> + ret = crypto_kpp_set_secret(ctrl->dh_tfm, pkey, pkey_len);
>> + if (ret) {
>> + pr_debug("failed to set private key, error %d\n", ret);
>> + goto out;
>> + }
>> +
>> + kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
>> + if (!kpp_req) {
>> + pr_debug("cannot allocate kpp request\n");
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + crypto_init_wait(&wait);
>> + kpp_request_set_input(kpp_req, NULL, 0);
>> + sg_init_one(&dst, buf, buf_size);
>> + kpp_request_set_output(kpp_req, &dst, buf_size);
>> + kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + crypto_req_done, &wait);
>> +
>> + ret = crypto_wait_req(crypto_kpp_generate_public_key(kpp_req), &wait);
>> + kpp_request_free(kpp_req);
>> + if (ret == -EOVERFLOW) {
>> + pr_debug("public key buffer too small, need %d is %d\n",
>> + crypto_kpp_maxsize(ctrl->dh_tfm), buf_size);
>> + ret = -ENOKEY;
>> + } else if (ret) {
>> + pr_debug("failed to generate public key, err %d\n", ret);
>> + ret = -ENOKEY;
>> + } else
>> + pr_debug("%s: ctrl public key %*ph\n", __func__,
>> + (int)buf_size, buf);
>> +
>> +out:
>> + kfree_sensitive(pkey);
>> + return ret;
>> +}
>
> In general: the target/host authentication code looks very similar. Is there
> no way to have a common code base?

That is the plan, but I would need to rework the parameter passing for
the target code to also use a 'dhchap' authentication block like the
host code does.
But Sagi is not entirely happy with it, so I'll rework the code once we
have consensus there.

Cheers,

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

2021-07-18 12:29:22

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 07/11] nvme-auth: augmented challenge support

On 7/17/21 6:49 PM, Stephan Müller wrote:
> Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> Implement support for augmented challenge using FFDHE groups.
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>> drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
>> 1 file changed, 371 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
>> index 448a3adebea6..754343aced19 100644
>> --- a/drivers/nvme/host/auth.c
>> +++ b/drivers/nvme/host/auth.c
>> @@ -8,6 +8,8 @@
>> #include <asm/unaligned.h>
>> #include <crypto/hash.h>
>> #include <crypto/kpp.h>
>> +#include <crypto/dh.h>
>> +#include <crypto/ffdhe.h>
>> #include "nvme.h"
>> #include "fabrics.h"
>> #include "auth.h"
>> @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;
>>
>> struct nvme_dhchap_context {
>> struct crypto_shash *shash_tfm;
>> + struct crypto_shash *digest_tfm;
>> + struct crypto_kpp *dh_tfm;
>> unsigned char *key;
>> size_t key_len;
>> int qid;
>> @@ -25,6 +29,8 @@ struct nvme_dhchap_context {
>> u8 status;
>> u8 hash_id;
>> u8 hash_len;
>> + u8 dhgroup_id;
>> + u16 dhgroup_size;
>> u8 c1[64];
>> u8 c2[64];
>> u8 response[64];
>> @@ -36,6 +42,94 @@ struct nvme_dhchap_context {
>> int sess_key_len;
>> };
>>
>> +struct nvme_auth_dhgroup_map {
>> + int id;
>> + const char name[16];
>> + const char kpp[16];
>> + int privkey_size;
>> + int pubkey_size;
>> +} dhgroup_map[] = {
>> + { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
>> + .name = "NULL", .kpp = "NULL",
>> + .privkey_size = 0, .pubkey_size = 0 },
>> + { .id = NVME_AUTH_DHCHAP_DHGROUP_2048,
>> + .name = "ffdhe2048", .kpp = "dh",
>> + .privkey_size = 256, .pubkey_size = 256 },
>> + { .id = NVME_AUTH_DHCHAP_DHGROUP_3072,
>> + .name = "ffdhe3072", .kpp = "dh",
>> + .privkey_size = 384, .pubkey_size = 384 },
>> + { .id = NVME_AUTH_DHCHAP_DHGROUP_4096,
>> + .name = "ffdhe4096", .kpp = "dh",
>> + .privkey_size = 512, .pubkey_size = 512 },
>> + { .id = NVME_AUTH_DHCHAP_DHGROUP_6144,
>> + .name = "ffdhe6144", .kpp = "dh",
>> + .privkey_size = 768, .pubkey_size = 768 },
>> + { .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
>> + .name = "ffdhe8192", .kpp = "dh",
>> + .privkey_size = 1024, .pubkey_size = 1024 },
>> +};
>> +
>> +const char *nvme_auth_dhgroup_name(int dhgroup_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>> + if (dhgroup_map[i].id == dhgroup_id)
>> + return dhgroup_map[i].name;
>> + }
>> + return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
>> +
>> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>> + if (dhgroup_map[i].id == dhgroup_id)
>> + return dhgroup_map[i].pubkey_size;
>> + }
>> + return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_pubkey_size);
>> +
>> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>> + if (dhgroup_map[i].id == dhgroup_id)
>> + return dhgroup_map[i].privkey_size;
>> + }
>> + return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_privkey_size);
>> +
>> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>> + if (dhgroup_map[i].id == dhgroup_id)
>> + return dhgroup_map[i].kpp;
>> + }
>> + return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
>> +
>> +int nvme_auth_dhgroup_id(const char *dhgroup_name)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>> + if (!strncmp(dhgroup_map[i].name, dhgroup_name,
>> + strlen(dhgroup_map[i].name)))
>> + return dhgroup_map[i].id;
>> + }
>> + return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
>> +
>> struct nvmet_dhchap_hash_map {
>> int id;
>> int hash_len;
>> @@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl
>> *ctrl, data->napd = 1;
>> data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
>> data->auth_protocol[0].dhchap.halen = 3;
>> - data->auth_protocol[0].dhchap.dhlen = 1;
>> + data->auth_protocol[0].dhchap.dhlen = 6;
>> data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
>> data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
>> data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
>> data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
>> + data->auth_protocol[0].dhchap.idlist[4] = NVME_AUTH_DHCHAP_DHGROUP_2048;
>> + data->auth_protocol[0].dhchap.idlist[5] = NVME_AUTH_DHCHAP_DHGROUP_3072;
>> + data->auth_protocol[0].dhchap.idlist[6] = NVME_AUTH_DHCHAP_DHGROUP_4096;
>> + data->auth_protocol[0].dhchap.idlist[7] = NVME_AUTH_DHCHAP_DHGROUP_6144;
>> + data->auth_protocol[0].dhchap.idlist[8] = NVME_AUTH_DHCHAP_DHGROUP_8192;
>>
>> return size;
>> }
>> @@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl
>> *ctrl, chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> return -EPROTO;
>> }
>> - switch (data->dhgid) {
>> - case NVME_AUTH_DHCHAP_DHGROUP_NULL:
>> - gid_name = "null";
>> - break;
>> - default:
>> - gid_name = NULL;
>> - break;
>> - }
>> + gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
>> if (!gid_name) {
>> dev_warn(ctrl->device,
>> "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
>> @@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl
>> *ctrl, return -EPROTO;
>> }
>> if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> - chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> - return -EPROTO;
>> - }
>> - if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
>> + if (data->dhvlen == 0) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: empty DH value\n",
>> + chap->qid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> + return -EPROTO;
>> + }
>> + chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
>> + if (IS_ERR(chap->dh_tfm)) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
>> + chap->qid, gid_name);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> + chap->dh_tfm = NULL;
>> + return -EPROTO;
>> + }
>> + chap->dhgroup_id = data->dhgid;
>> + } else if (data->dhvlen != 0) {
>> dev_warn(ctrl->device,
>> "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
>> chap->qid);
>> @@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl
>> *ctrl, chap->hash_len = data->hl;
>> chap->s1 = le32_to_cpu(data->seqnum);
>> memcpy(chap->c1, data->cval, chap->hash_len);
>> + if (data->dhvlen) {
>> + chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL);
>> + if (!chap->ctrl_key)
>> + return -ENOMEM;
>> + chap->ctrl_key_len = data->dhvlen;
>> + memcpy(chap->ctrl_key, data->cval + chap->hash_len,
>> + data->dhvlen);
>> + dev_dbg(ctrl->device, "ctrl public key %*ph\n",
>> + (int)chap->ctrl_key_len, chap->ctrl_key);
>> + }
>>
>> return 0;
>> }
>> @@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl
>> *ctrl, memcpy(data->rval + chap->hash_len, chap->c2,
>> chap->hash_len);
>> }
>> - if (chap->host_key_len)
>> + if (chap->host_key_len) {
>> + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
>> + __func__, chap->qid,
>> + chap->host_key_len, chap->host_key);
>> memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
>> chap->host_key_len);
>> -
>> + }
>> return size;
>> }
>>
>> @@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl
>> *ctrl, int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>> struct nvme_dhchap_context *chap)
>> {
>> - char *hash_name;
>> + const char *hash_name, *digest_name;
>> int ret;
>>
>> - switch (chap->hash_id) {
>> - case NVME_AUTH_DHCHAP_HASH_SHA256:
>> - hash_name = "hmac(sha256)";
>> - break;
>> - case NVME_AUTH_DHCHAP_HASH_SHA384:
>> - hash_name = "hmac(sha384)";
>> - break;
>> - case NVME_AUTH_DHCHAP_HASH_SHA512:
>> - hash_name = "hmac(sha512)";
>> - break;
>> - default:
>> - hash_name = NULL;
>> - break;
>> - }
>> + hash_name = nvme_auth_hmac_name(chap->hash_id);
>> if (!hash_name) {
>> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> return -EPROTO;
>> @@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>> chap->shash_tfm = NULL;
>> return -EPROTO;
>> }
>> + digest_name = nvme_auth_digest_name(chap->hash_id);
>> + if (!digest_name) {
>> + crypto_free_shash(chap->shash_tfm);
>> + chap->shash_tfm = NULL;
>> + return -EPROTO;
>> + }
>> + chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0);
>> + if (IS_ERR(chap->digest_tfm)) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + crypto_free_shash(chap->shash_tfm);
>> + chap->shash_tfm = NULL;
>> + chap->digest_tfm = NULL;
>> + return -EPROTO;
>> + }
>> if (!chap->key) {
>> dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
>> chap->qid);
>> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + crypto_free_shash(chap->digest_tfm);
>> crypto_free_shash(chap->shash_tfm);
>> chap->shash_tfm = NULL;
>> + chap->digest_tfm = NULL;
>> return -EINVAL;
>> }
>> ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
>> if (ret) {
>> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + crypto_free_shash(chap->digest_tfm);
>> crypto_free_shash(chap->shash_tfm);
>> chap->shash_tfm = NULL;
>> + chap->digest_tfm = NULL;
>> return ret;
>> }
>> - dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>> - chap->qid, hash_name);
>> + dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>> + chap->qid, hash_name);
>> return 0;
>> }
>>
>> +static int nvme_auth_augmented_challenge(struct nvme_dhchap_context *chap,
>> + u8 *challenge, u8 *aug)
>> +{
>> + struct crypto_shash *tfm;
>> + struct shash_desc *desc;
>> + u8 *hashed_key;
>> + const char *hash_name;
>> + int ret;
>> +
>> + hashed_key = kmalloc(chap->hash_len, GFP_KERNEL);
>> + if (!hashed_key)
>> + return -ENOMEM;
>> +
>> + ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key,
>> + chap->sess_key_len, hashed_key);
>> + if (ret < 0) {
>> + pr_debug("failed to hash session key, err %d\n", ret);
>> + kfree(hashed_key);
>> + return ret;
>> + }
>> + hash_name = crypto_shash_alg_name(chap->shash_tfm);
>> + if (!hash_name) {
>> + pr_debug("Invalid hash algoritm\n");
>> + return -EINVAL;
>> + }
>> + tfm = crypto_alloc_shash(hash_name, 0, 0);
>> + if (IS_ERR(tfm)) {
>> + ret = PTR_ERR(tfm);
>> + goto out_free_key;
>> + }
>> + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
>> + GFP_KERNEL);
>> + if (!desc) {
>> + ret = -ENOMEM;
>> + goto out_free_hash;
>> + }
>> + desc->tfm = tfm;
>> +
>> + ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len);
>> + if (ret)
>> + goto out_free_desc;
>> + ret = crypto_shash_init(desc);
>> + if (ret)
>> + goto out_free_desc;
>> + crypto_shash_update(desc, challenge, chap->hash_len);
>> + crypto_shash_final(desc, aug);
>> +
>> +out_free_desc:
>> + kfree_sensitive(desc);
>> +out_free_hash:
>> + crypto_free_shash(tfm);
>> +out_free_key:
>> + kfree(hashed_key);
>> + return ret;
>> +}
>> +
>> static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
>> struct nvme_dhchap_context *chap)
>> {
>> @@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct
>> nvme_ctrl *ctrl,
>>
>> dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
> %d\n",
>> __func__, chap->qid, chap->s1, chap->transaction);
>> + if (chap->dh_tfm) {
>> + challenge = kmalloc(chap->hash_len, GFP_KERNEL);
>
> Again, alignment?
>

Again, why?

Cheers,

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

2021-07-18 12:38:21

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/17/21 6:49 PM, Stephan Müller wrote:
> Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> Implement support for NVMe-oF In-Band authentication. This patch
>> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
>> to the 'host' configfs directory. The 'dhchap_key' needs to be
>> specified in the format outlined in the base spec.
>> Augmented challenge support is not implemented, and concatenation
>> with TLS encryption is not supported.
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>> drivers/nvme/target/Kconfig | 10 +
>> drivers/nvme/target/Makefile | 1 +
>> drivers/nvme/target/admin-cmd.c | 4 +
>> drivers/nvme/target/auth.c | 352 +++++++++++++++++++
>> drivers/nvme/target/configfs.c | 71 +++-
>> drivers/nvme/target/core.c | 8 +
>> drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
>> drivers/nvme/target/fabrics-cmd.c | 30 +-
>> drivers/nvme/target/nvmet.h | 71 ++++
>> 9 files changed, 1004 insertions(+), 3 deletions(-)
>> create mode 100644 drivers/nvme/target/auth.c
>> create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
>>
>> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
>> index 4be2ececbc45..d5656ef1559e 100644
>> --- a/drivers/nvme/target/Kconfig
>> +++ b/drivers/nvme/target/Kconfig
>> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
>> devices over TCP.
>>
>> If unsure, say N.
>> +
>> +config NVME_TARGET_AUTH
>> + bool "NVMe over Fabrics In-band Authentication support"
>> + depends on NVME_TARGET
>> + select CRYPTO_SHA256
>> + select CRYPTO_SHA512
>> + help
>> + This enables support for NVMe over Fabrics In-band Authentication
>> +
>> + If unsure, say N.
>> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
>> index 9837e580fa7e..c66820102493 100644
>> --- a/drivers/nvme/target/Makefile
>> +++ b/drivers/nvme/target/Makefile
>> @@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o
> fabrics-cmd.o \
>> discovery.o io-cmd-file.o io-cmd-bdev.o
>> nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
>> nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
>> +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o
>> nvme-loop-y += loop.o
>> nvmet-rdma-y += rdma.o
>> nvmet-fc-y += fc.o
>> diff --git a/drivers/nvme/target/admin-cmd.c
>> b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644
>> --- a/drivers/nvme/target/admin-cmd.c
>> +++ b/drivers/nvme/target/admin-cmd.c
>> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
>>
>> if (nvme_is_fabrics(cmd))
>> return nvmet_parse_fabrics_cmd(req);
>> +
>> + if (unlikely(!nvmet_check_auth_status(req)))
>> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
>> +
>> if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
>> return nvmet_parse_discovery_cmd(req);
>>
>> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
>> new file mode 100644
>> index 000000000000..00c7d051dfb1
>> --- /dev/null
>> +++ b/drivers/nvme/target/auth.c
>> @@ -0,0 +1,352 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
>> + * All rights reserved.
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/err.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include <crypto/dh.h>
>> +#include <crypto/ffdhe.h>
>> +#include <linux/crc32.h>
>> +#include <linux/base64.h>
>> +#include <linux/ctype.h>
>> +#include <linux/random.h>
>> +#include <asm/unaligned.h>
>> +
>> +#include "nvmet.h"
>> +#include "../host/auth.h"
>> +
>> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
>> +{
>> + if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
>> + return -EINVAL;
>> + if (host->dhchap_key_hash > 3) {
>> + pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
>> + host->dhchap_key_hash);
>> + return -EINVAL;
>> + }
>> + if (host->dhchap_key_hash > 0) {
>> + /* Validate selected hash algorithm */
>> + const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
>> +
>> + if (!crypto_has_shash(hmac, 0, 0)) {
>> + pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
>> + host->dhchap_key_hash = -1;
>> + return -EAGAIN;
>> + }
>> + /* Use this hash as default */
>> + if (!host->dhchap_hash_id)
>> + host->dhchap_hash_id = host->dhchap_key_hash;
>> + }
>> + host->dhchap_secret = kstrdup(secret, GFP_KERNEL);
>
> Just like before - are you sure that the secret is an ASCII string and no
> binary blob?
>

That is ensured by the transport encoding (cf NVMe Base Specification
version 2.0). Also, this information is being passed in via the configfs
interface, so it's bounded by PAGE_SIZE. But yes, we should be inserting
a terminating 'NULL' character at the end of the page to ensure we don't
incur an buffer overrun. Any other failure will be checked for during
base64 decoding.

>> + if (!host->dhchap_secret)
>> + return -ENOMEM;
>> + /* Default to SHA256 */
>> + if (!host->dhchap_hash_id)
>> + host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;
>> +
>> + pr_debug("Using hash %s\n",
>> + nvme_auth_hmac_name(host->dhchap_hash_id));
>> + return 0;
>> +}
>> +
>> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
>> +{
>> + int ret = -ENOTSUPP;
>> +
>> + if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
>> + return 0;
>> +
>> + return ret;
>> +}
>> +
>> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
>> +{
>> + int ret = 0;
>> + struct nvmet_host_link *p;
>> + struct nvmet_host *host = NULL;
>> + const char *hash_name;
>> +
>> + down_read(&nvmet_config_sem);
>> + if (ctrl->subsys->type == NVME_NQN_DISC)
>> + goto out_unlock;
>> +
>> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
>> + pr_debug("check %s\n", nvmet_host_name(p->host));
>> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
>> + continue;
>> + host = p->host;
>> + break;
>> + }
>> + if (!host) {
>> + pr_debug("host %s not found\n", ctrl->hostnqn);
>> + ret = -EPERM;
>> + goto out_unlock;
>> + }
>> + if (!host->dhchap_secret) {
>> + pr_debug("No authentication provided\n");
>> + goto out_unlock;
>> + }
>> +
>> + hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
>> + if (!hash_name) {
>> + pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);
>> + ret = -EINVAL;
>> + goto out_unlock;
>> + }
>> + ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
>> + CRYPTO_ALG_ALLOCATES_MEMORY);
>> + if (IS_ERR(ctrl->shash_tfm)) {
>> + pr_debug("failed to allocate shash %s\n", hash_name);
>> + ret = PTR_ERR(ctrl->shash_tfm);
>> + ctrl->shash_tfm = NULL;
>> + goto out_unlock;
>> + }
>> +
>> + ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
>> + &ctrl->dhchap_key_len);
>> + if (IS_ERR(ctrl->dhchap_key)) {
>> + pr_debug("failed to extract host key, error %d\n", ret);
>> + ret = PTR_ERR(ctrl->dhchap_key);
>> + ctrl->dhchap_key = NULL;
>> + goto out_free_hash;
>> + }
>> + if (host->dhchap_key_hash) {
>> + struct crypto_shash *key_tfm;
>> +
>> + hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
>> + key_tfm = crypto_alloc_shash(hash_name, 0, 0);
>> + if (IS_ERR(key_tfm)) {
>> + ret = PTR_ERR(key_tfm);
>> + goto out_free_hash;
>> + } else {
>> + SHASH_DESC_ON_STACK(shash, key_tfm);
>> +
>> + shash->tfm = key_tfm;
>> + ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
>> + ctrl->dhchap_key_len);
>> + crypto_shash_init(shash);
>> + crypto_shash_update(shash, ctrl->subsys->subsysnqn,
>> + strlen(ctrl->subsys->subsysnqn));
>> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
>> + crypto_shash_final(shash, ctrl->dhchap_key);
>> + crypto_free_shash(key_tfm);
>> + }
>> + }
>> + pr_debug("%s: using key %*ph\n", __func__,
>> + (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
>> + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
>
> Is it truly necessary to keep the key around in ctrl->dhchap_key? It looks to
> me that this buffer is only used here and thus could be turned into a local
> variable. Keys flying around in memory is not a good idea. :-)
>
The key is also used when using the ffdhe algorithm.
Note: I _think_ that I need to use this key for the ffdhe algorithm,
because the implementation I came up with is essentially plain DH with
pre-defined 'p', 'q' and 'g' values. But the DH implementation also
requires a 'key', and for that I'm using this key here.

It might be that I'm completely off, and don't need to use a key for our
DH implementation. In that case you are correct.
(And that's why I said I'll need a review of the FFDHE implementation).
But for now I'll need the key for FFDHE.

>> + ctrl->dhchap_key_len);
>> +out_free_hash:
>> + if (ret) {
>> + if (ctrl->dhchap_key) {
>> + kfree(ctrl->dhchap_key);
>
> kfree_sensitive?
>

Yes, will be fixing it.

>> + ctrl->dhchap_key = NULL;
>> + }
>> + crypto_free_shash(ctrl->shash_tfm);
>> + ctrl->shash_tfm = NULL;
>> + }
>> +out_unlock:
>> + up_read(&nvmet_config_sem);
>> +
>> + return ret;
>> +}
>> +
>> +void nvmet_auth_sq_free(struct nvmet_sq *sq)
>> +{
>> + if (sq->dhchap_c1)
>> + kfree(sq->dhchap_c1);
>> + if (sq->dhchap_c2)
>> + kfree(sq->dhchap_c2);
>> + if (sq->dhchap_skey)
>> + kfree(sq->dhchap_skey);
>
> kfree_sensitive?
>

Yes.

>> +}
>> +
>> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl)
>> +{
>> + if (ctrl->shash_tfm) {
>> + crypto_free_shash(ctrl->shash_tfm);
>> + ctrl->shash_tfm = NULL;
>> + }
>> + if (ctrl->dh_tfm) {
>> + crypto_free_kpp(ctrl->dh_tfm);
>> + ctrl->dh_tfm = NULL;
>> + }
>> + if (ctrl->dhchap_key) {
>> + kfree(ctrl->dhchap_key);
>
> kfree_sensitive?
>

Yes.

>> + ctrl->dhchap_key = NULL;
>> + }
>> +}
>> +
>> +bool nvmet_check_auth_status(struct nvmet_req *req)
>> +{
>> + if (req->sq->ctrl->shash_tfm &&
>> + !req->sq->authenticated)
>> + return false;
>> + return true;
>> +}
>> +
>> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
>> + unsigned int shash_len)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
>> + u8 *challenge = req->sq->dhchap_c1;
>> + u8 buf[4];
>> + int ret;
>> +
>> + if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> + ret = -ENOTSUPP;
>> + goto out;
>> + }
>> +
>> + shash->tfm = ctrl->shash_tfm;
>> + ret = crypto_shash_init(shash);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, challenge, shash_len);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le32(req->sq->dhchap_s1, buf);
>> + ret = crypto_shash_update(shash, buf, 4);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le16(req->sq->dhchap_tid, buf);
>> + ret = crypto_shash_update(shash, buf, 2);
>> + if (ret)
>> + goto out;
>> + memset(buf, 0, 4);
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, "HostHost", 8);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->subsysnqn,
>> + strlen(ctrl->subsysnqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_final(shash, response);
>> +out:
>> + if (challenge != req->sq->dhchap_c1)
>> + kfree(challenge);
>> + return 0;
>> +}
>> +
>> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
>> + unsigned int shash_len)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
>> + u8 *challenge = req->sq->dhchap_c2;
>> + u8 buf[4];
>> + int ret;
>> +
>> + pr_debug("%s: ctrl %d hash seq %d transaction %u\n", __func__,
>> + ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid);
>> + pr_debug("%s: ctrl %d challenge %*ph\n", __func__,
>> + ctrl->cntlid, shash_len, req->sq->dhchap_c2);
>> + pr_debug("%s: ctrl %d subsysnqn %s\n", __func__,
>> + ctrl->cntlid, ctrl->subsysnqn);
>> + pr_debug("%s: ctrl %d hostnqn %s\n", __func__,
>> + ctrl->cntlid, ctrl->hostnqn);
>> +
>> + if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> + ret = -ENOTSUPP;
>> + goto out;
>> + }
>> +
>> + shash->tfm = ctrl->shash_tfm;
>> + ret = crypto_shash_init(shash);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, challenge, shash_len);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le32(req->sq->dhchap_s2, buf);
>> + ret = crypto_shash_update(shash, buf, 4);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le16(req->sq->dhchap_tid, buf);
>> + ret = crypto_shash_update(shash, buf, 2);
>> + if (ret)
>> + goto out;
>> + memset(buf, 0, 4);
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, "Controller", 10);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->subsysnqn,
>> + strlen(ctrl->subsysnqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_final(shash, response);
>> +out:
>> + if (challenge != req->sq->dhchap_c2)
>> + kfree(challenge);
>> + return 0;
>> +}
>> +
>> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
>> + u8 *pkey, int pkey_size)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + struct kpp_request *kpp_req;
>> + struct crypto_wait wait;
>> + struct scatterlist src, dst;
>> + int ret;
>> +
>> + req->sq->dhchap_skey_len =
>> + nvme_auth_dhgroup_privkey_size(ctrl->dh_gid);
>> + req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
>> + if (!req->sq->dhchap_skey)
>> + return -ENOMEM;
>> + kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
>> + if (!kpp_req) {
>> + kfree(req->sq->dhchap_skey);
>> + req->sq->dhchap_skey = NULL;
>> + return -ENOMEM;
>> + }
>> +
>> + pr_debug("%s: host public key %*ph\n", __func__,
>> + (int)pkey_size, pkey);
>> + crypto_init_wait(&wait);
>> + sg_init_one(&src, pkey, pkey_size);
>> + kpp_request_set_input(kpp_req, &src, pkey_size);
>> + sg_init_one(&dst, req->sq->dhchap_skey,
>> + req->sq->dhchap_skey_len);
>> + kpp_request_set_output(kpp_req, &dst, req->sq->dhchap_skey_len);
>> + kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> + crypto_req_done, &wait);
>> +
>> + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(kpp_req), &wait);
>> + kpp_request_free(kpp_req);
>> + if (ret)
>> + pr_debug("failed to compute shared secred, err %d\n", ret);
>> + else
>> + pr_debug("%s: shared secret %*ph\n", __func__,
>> + (int)req->sq->dhchap_skey_len,
>> + req->sq->dhchap_skey);
>> +
>> + return ret;
>> +}
>> diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
>> index 273555127188..e0760911a761 100644
>> --- a/drivers/nvme/target/configfs.c
>> +++ b/drivers/nvme/target/configfs.c
>> @@ -11,8 +11,13 @@
>> #include <linux/ctype.h>
>> #include <linux/pci.h>
>> #include <linux/pci-p2pdma.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>>
>> #include "nvmet.h"
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +#include "../host/auth.h"
>> +#endif
>>
>> static const struct config_item_type nvmet_host_type;
>> static const struct config_item_type nvmet_subsys_type;
>> @@ -1656,10 +1661,71 @@ static const struct config_item_type
>> nvmet_ports_type = { static struct config_group nvmet_subsystems_group;
>> static struct config_group nvmet_ports_group;
>>
>> -static void nvmet_host_release(struct config_item *item)
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
>> + char *page)
>> +{
>> + u8 *dhchap_secret = to_host(item)->dhchap_secret;
>> +
>> + if (!dhchap_secret)
>> + return sprintf(page, "\n");
>> + return sprintf(page, "%s\n", dhchap_secret);
>> +}
>> +
>> +static ssize_t nvmet_host_dhchap_key_store(struct config_item *item,
>> + const char *page, size_t count)
>> {
>> struct nvmet_host *host = to_host(item);
>> + int ret;
>>
>> + ret = nvmet_auth_set_host_key(host, page);
>> + if (ret < 0)
>> + return ret;
>> + return count;
>> +}
>> +
>> +CONFIGFS_ATTR(nvmet_host_, dhchap_key);
>> +
>> +static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item,
>> + char *page)
>> +{
>> + struct nvmet_host *host = to_host(item);
>> + const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
>> +
>> + return sprintf(page, "%s\n", hash_name ? hash_name : "none");
>> +}
>> +
>> +static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,
>> + const char *page, size_t count)
>> +{
>> + struct nvmet_host *host = to_host(item);
>> + int hmac_id;
>> +
>> + hmac_id = nvme_auth_hmac_id(page);
>> + if (hmac_id < 0)
>> + return -EINVAL;
>> + if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0))
>> + return -ENOTSUPP;
>> + host->dhchap_hash_id = hmac_id;
>> + return count;
>> +}
>> +
>> +CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
>> +
>> +static struct configfs_attribute *nvmet_host_attrs[] = {
>> + &nvmet_host_attr_dhchap_key,
>> + &nvmet_host_attr_dhchap_hash,
>> + NULL,
>> +};
>> +#endif /* CONFIG_NVME_TARGET_AUTH */
>> +
>> +static void nvmet_host_release(struct config_item *item)
>> +{
>> + struct nvmet_host *host = to_host(item);
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> + if (host->dhchap_secret)
>> + kfree(host->dhchap_secret);
>> +#endif
>> kfree(host);
>> }
>>
>> @@ -1669,6 +1735,9 @@ static struct configfs_item_operations
>> nvmet_host_item_ops = {
>>
>> static const struct config_item_type nvmet_host_type = {
>> .ct_item_ops = &nvmet_host_item_ops,
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> + .ct_attrs = nvmet_host_attrs,
>> +#endif
>> .ct_owner = THIS_MODULE,
>> };
>>
>> diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
>> index 163f7dc1a929..b5d7971f566b 100644
>> --- a/drivers/nvme/target/core.c
>> +++ b/drivers/nvme/target/core.c
>> @@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
>> wait_for_completion(&sq->confirm_done);
>> wait_for_completion(&sq->free_done);
>> percpu_ref_exit(&sq->ref);
>> + nvmet_auth_sq_free(sq);
>>
>> if (ctrl) {
>> /*
>> @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req)
>> req->cmd->common.opcode, req->sq->qid);
>> return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
>> }
>> +
>> + if (unlikely(!nvmet_check_auth_status(req))) {
>> + pr_warn("qid %d not authenticated\n", req->sq->qid);
>> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
>> + }
>> return 0;
>> }
>>
>> @@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)
>> flush_work(&ctrl->async_event_work);
>> cancel_work_sync(&ctrl->fatal_err_work);
>>
>> + nvmet_reset_auth(ctrl);
>> +
>> ida_simple_remove(&cntlid_ida, ctrl->cntlid);
>>
>> nvmet_async_events_free(ctrl);
>> diff --git a/drivers/nvme/target/fabrics-cmd-auth.c
>> b/drivers/nvme/target/fabrics-cmd-auth.c new file mode 100644
>> index 000000000000..962f9f5e9d89
>> --- /dev/null
>> +++ b/drivers/nvme/target/fabrics-cmd-auth.c
>> @@ -0,0 +1,460 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * NVMe over Fabrics DH-HMAC-CHAP authentication command handling.
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
>> + * All rights reserved.
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +#include <linux/blkdev.h>
>> +#include <linux/random.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include "nvmet.h"
>> +#include "../host/auth.h"
>> +
>> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
>> +{
>> + /* Initialize in-band authentication */
>> + req->sq->authenticated = false;
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
>> + req->cqe->result.u32 |= 0x2 << 16;
>> +}
>> +
>> +static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + struct nvmf_auth_dhchap_negotiate_data *data = d;
>> + int i, hash_id, null_dh = -1;
>> +
>> + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d
>> dhlen %d\n", + __func__, ctrl->cntlid, req->sq->qid,
>> + data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
>> + data->auth_protocol[0].dhchap.halen,
>> + data->auth_protocol[0].dhchap.dhlen);
>> + req->sq->dhchap_tid = le16_to_cpu(data->t_id);
>> + if (data->sc_c)
>> + return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
>> +
>> + if (data->napd != 1)
>> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +
>> + if (data->auth_protocol[0].dhchap.authid != 0x01)
>> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +
>> + hash_id = nvme_auth_hmac_id(crypto_shash_alg_name(ctrl->shash_tfm));
>> + for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
>> + pr_debug("%s: ctrl %d qid %d checking hash %d for %d\n",
>> + __func__, ctrl->cntlid, req->sq->qid,
>> + data->auth_protocol[0].dhchap.idlist[i], hash_id);
>> + if (hash_id != data->auth_protocol[0].dhchap.idlist[i])
>> + continue;
>> + req->sq->dhchap_hash_id = hash_id;
>> + req->sq->dhchap_hash_len = crypto_shash_digestsize(ctrl-
>> shash_tfm);
>> + break;
>> + }
>> + if (req->sq->dhchap_hash_id == 0) {
>> + pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
>> + __func__, ctrl->cntlid, req->sq->qid);
>> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> + }
>> +
>> + for (i = data->auth_protocol[0].dhchap.halen;
>> + i < data->auth_protocol[0].dhchap.halen +
>> + data->auth_protocol[0].dhchap.dhlen; i++) {
>> + int dhgid = data->auth_protocol[0].dhchap.idlist[i];
>> +
>> + if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> + null_dh = dhgid;
>> + continue;
>> + }
>> + if (nvmet_setup_dhgroup(ctrl, dhgid) == 0)
>> + break;
>> + }
>> + if (!ctrl->dh_tfm && null_dh < 0) {
>> + pr_debug("%s: ctrl %d qid %d: no DH group selected\n",
>> + __func__, ctrl->cntlid, req->sq->qid);
>> + return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> + }
>> + if (ctrl->dh_gid == -1) {
>> + ctrl->dh_gid = null_dh;
>> + ctrl->dh_tfm = NULL;
>> + }
>> + pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n",
>> + __func__, ctrl->cntlid, req->sq->qid,
>> + nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
>> + return 0;
>> +}
>> +
>> +static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + struct nvmf_auth_dhchap_reply_data *data = d;
>> + u8 *response;
>> +
>> + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
>> + __func__, ctrl->cntlid, req->sq->qid,
>> + data->hl, data->cvalid, data->dhvlen);
>> + if (data->hl != req->sq->dhchap_hash_len)
>> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +
>> + if (data->dhvlen) {
>> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + }
>> +
>> + response = kmalloc(data->hl, GFP_KERNEL);
>
> Again, align to CRYPTO_MINALIGN_ATTR?
>

Ah, _that_ alignment.
Wasn't aware that I need to align to anything.
But if that's required, sure, I'll be fixing it.

>> + if (!response)
>> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> +
>> + if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
>> + pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n",
>> + ctrl->cntlid, req->sq->qid);
>> + kfree(response);
>> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> + }
>> +
>> + if (memcmp(data->rval, response, data->hl)) {
>> + pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n",
>> + ctrl->cntlid, req->sq->qid);
>> + kfree(response);
>> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> + }
>> + kfree(response);
>> + pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n",
>> + ctrl->cntlid, req->sq->qid);
>> + if (data->cvalid) {
>> + req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL);
>> + if (!req->sq->dhchap_c2)
>> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> + memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl);
>> +
>> + pr_debug("ctrl %d qid %d challenge %*ph\n",
>> + ctrl->cntlid, req->sq->qid, data->hl,
>> + req->sq->dhchap_c2);
>> + req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
>> + } else
>> + req->sq->dhchap_c2 = NULL;
>> +
>> + return 0;
>> +}
>> +
>> +static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d)
>> +{
>> + struct nvmf_auth_dhchap_failure_data *data = d;
>> +
>> + return data->reason_code_explanation;
>> +}
>> +
>> +void nvmet_execute_auth_send(struct nvmet_req *req)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + struct nvmf_auth_dhchap_success2_data *data;
>> + void *d;
>> + u32 tl;
>> + u16 status = 0;
>> +
>> + if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_send_command, secp);
>> + goto done;
>> + }
>> + if (req->cmd->auth_send.spsp0 != 0x01) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_send_command, spsp0);
>> + goto done;
>> + }
>> + if (req->cmd->auth_send.spsp1 != 0x01) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_send_command, spsp1);
>> + goto done;
>> + }
>> + tl = le32_to_cpu(req->cmd->auth_send.tl);
>> + if (!tl) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_send_command, tl);
>> + goto done;
>> + }
>> + if (!nvmet_check_transfer_len(req, tl)) {
>> + pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
>> + return;
>> + }
>> +
>> + d = kmalloc(tl, GFP_KERNEL);
>> + if (!d) {
>> + status = NVME_SC_INTERNAL;
>> + goto done;
>> + }
>> +
>> + status = nvmet_copy_from_sgl(req, 0, d, tl);
>> + if (status) {
>> + kfree(d);
>> + goto done;
>> + }
>> +
>> + data = d;
>> + pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
>> + ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id,
>> + req->sq->dhchap_step);
>> + if (data->auth_type != NVME_AUTH_COMMON_MESSAGES &&
>> + data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) {
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> + } else if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
>> + if (data->auth_id != req->sq->dhchap_step) {
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status =
> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> + } else if (data->auth_id != NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status =
> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> + } else {
>> + /* Validate negotiation parameters */
>> + status = nvmet_auth_negotiate(req, d);
>> + if (status == 0)
>> + req->sq->dhchap_step =
> NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
>> + else {
>> + req->sq->dhchap_step =
> NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status = status;
>> + status = 0;
>> + }
>> + }
>> + } else if (data->auth_type == NVME_AUTH_DHCHAP_MESSAGES) {
>> + if (data->auth_id != req->sq->dhchap_step) {
>> + pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n",
>> + __func__, ctrl->cntlid, req->sq->qid,
>> + data->auth_id, req->sq->dhchap_step);
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status =
> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> + } else if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) {
>> + pr_debug("%s: ctrl %d qid %d invalid transaction %d
> (expected %d)\n",
>> + __func__, ctrl->cntlid, req->sq->qid,
>> + le16_to_cpu(data->t_id),
>> + req->sq->dhchap_tid);
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status =
> NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + } else {
>> + switch (data->auth_id) {
>> + case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
>> + status = nvmet_auth_reply(req, d);
>> + if (status == 0)
>> + req->sq->dhchap_step =
> NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
>> + else {
>> + req->sq->dhchap_step =
> NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status = status;
>> + status = 0;
>> + }
>> + break;
>> + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
>> + req->sq->authenticated = true;
>> + pr_debug("%s: ctrl %d qid %d authenticated\n",
>> + __func__, ctrl->cntlid, req->sq->qid);
>> + break;
>> + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
>> + status = nvmet_auth_failure2(req, d);
>> + if (status) {
>> + pr_warn("ctrl %d qid %d: DH-HMAC-CHAP
> negotiation failed (%d)\n",
>> + ctrl->cntlid, req->sq->qid,
>> + status);
>> + req->sq->dhchap_status = status;
>> + status = 0;
>> + }
>> + break;
>> + default:
>> + req->sq->dhchap_status =
> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> + req->sq->dhchap_step =
> NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> + break;
>> + }
>> + }
>> + } else {
>> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> + }
>> + kfree(d);
>> +done:
>> + pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__,
>> + ctrl->cntlid, req->sq->qid,
>> + req->sq->dhchap_status, req->sq->dhchap_step);
>> + if (status)
>> + pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n",
>> + __func__, ctrl->cntlid, req->sq->qid,
>> + status, req->error_loc);
>> + req->cqe->result.u64 = 0;
>> + nvmet_req_complete(req, status);
>> + if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
>> + req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
>> + return;
>> + /* Final states, clear up variables */
>> + kfree(req->sq->dhchap_c1);
>> + kfree(req->sq->dhchap_c2);
>> + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
>> + nvmet_ctrl_fatal_error(ctrl);
>> +}
>> +
>> +static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
>> +{
>> + struct nvmf_auth_dhchap_challenge_data *data = d;
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + int ret = 0;
>> + int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
>> +
>> + if (al < data_size) {
>> + pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
>> + al, data_size);
>> + return -EINVAL;
>> + }
>> + memset(data, 0, data_size);
>> + req->sq->dhchap_s1 = ctrl->dhchap_seqnum++;
>> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
>> + data->t_id = cpu_to_le16(req->sq->dhchap_tid);
>> + data->hashid = req->sq->dhchap_hash_id;
>> + data->hl = req->sq->dhchap_hash_len;
>> + data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
>> + req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
>> + if (!req->sq->dhchap_c1)
>> + return -ENOMEM;
>> + get_random_bytes(req->sq->dhchap_c1, data->hl);
>> + memcpy(data->cval, req->sq->dhchap_c1, data->hl);
>> + pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n",
>> + __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
>> + req->sq->dhchap_tid, data->hl, data->dhvlen);
>> + return ret;
>> +}
>> +
>> +static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al)
>> +{
>> + struct nvmf_auth_dhchap_success1_data *data = d;
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +
>> + WARN_ON(al < sizeof(*data));
>> + memset(data, 0, sizeof(*data));
>> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
>> + data->t_id = cpu_to_le16(req->sq->dhchap_tid);
>> + data->hl = req->sq->dhchap_hash_len;
>> + if (req->sq->dhchap_c2) {
>> + if (nvmet_auth_ctrl_hash(req, data->rval, data->hl))
>> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> + data->rvalid = 1;
>> + pr_debug("ctrl %d qid %d response %*ph\n",
>> + ctrl->cntlid, req->sq->qid, data->hl, data->rval);
>> + }
>> + return 0;
>> +}
>> +
>> +static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
>> +{
>> + struct nvmf_auth_dhchap_failure_data *data = d;
>> +
>> + WARN_ON(al < sizeof(*data));
>> + data->auth_type = NVME_AUTH_COMMON_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + data->t_id = cpu_to_le32(req->sq->dhchap_tid);
>> + data->reason_code = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
>> + data->reason_code_explanation = req->sq->dhchap_status;
>> +}
>> +
>> +void nvmet_execute_auth_receive(struct nvmet_req *req)
>> +{
>> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> + void *d;
>> + u32 al;
>> + u16 status = 0;
>> +
>> + if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER)
> {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_receive_command, secp);
>> + goto done;
>> + }
>> + if (req->cmd->auth_receive.spsp0 != 0x01) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_receive_command, spsp0);
>> + goto done;
>> + }
>> + if (req->cmd->auth_receive.spsp1 != 0x01) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_receive_command, spsp1);
>> + goto done;
>> + }
>> + al = le32_to_cpu(req->cmd->auth_receive.al);
>> + if (!al) {
>> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> + req->error_loc =
>> + offsetof(struct nvmf_auth_receive_command, al);
>> + goto done;
>> + }
>> + if (!nvmet_check_transfer_len(req, al)) {
>> + pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
>> + return;
>> + }
>> +
>> + d = kmalloc(al, GFP_KERNEL);
>> + if (!d) {
>> + status = NVME_SC_INTERNAL;
>> + goto done;
>> + }
>> + pr_debug("%s: ctrl %d qid %d step %x\n", __func__,
>> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
>> + switch (req->sq->dhchap_step) {
>> + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
>> + status = nvmet_auth_challenge(req, d, al);
>> + if (status < 0) {
>> + pr_warn("ctrl %d qid %d: challenge error (%d)\n",
>> + ctrl->cntlid, req->sq->qid, status);
>> + status = NVME_SC_INTERNAL;
>> + break;
>> + }
>> + if (status) {
>> + req->sq->dhchap_status = status;
>> + nvmet_auth_failure1(req, d, al);
>> + pr_warn("ctrl %d qid %d: challenge status (%x)\n",
>> + ctrl->cntlid, req->sq->qid,
>> + req->sq->dhchap_status);
>> + status = 0;
>> + break;
>> + }
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
>> + break;
>> + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
>> + status = nvmet_auth_success1(req, d, al);
>> + if (status) {
>> + req->sq->dhchap_status = status;
>> + nvmet_auth_failure1(req, d, al);
>> + pr_warn("ctrl %d qid %d: success1 status (%x)\n",
>> + ctrl->cntlid, req->sq->qid,
>> + req->sq->dhchap_status);
>> + break;
>> + }
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
>> + break;
>> + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1:
>> + nvmet_auth_failure1(req, d, al);
>> + pr_warn("ctrl %d qid %d failure1 (%x)\n",
>> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_status);
>> + break;
>> + default:
>> + pr_warn("ctrl %d qid %d unhandled step (%d)\n",
>> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
>> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> + nvmet_auth_failure1(req, d, al);
>> + status = 0;
>> + break;
>> + }
>> +
>> + status = nvmet_copy_to_sgl(req, 0, d, al);
>> + kfree(d);
>> +done:
>> + req->cqe->result.u64 = 0;
>> + nvmet_req_complete(req, status);
>> + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
>> + kfree(req->sq->dhchap_c1);
>> + kfree(req->sq->dhchap_c2);
>> + nvmet_ctrl_fatal_error(ctrl);
>> + }
>> +}
>> diff --git a/drivers/nvme/target/fabrics-cmd.c
>> b/drivers/nvme/target/fabrics-cmd.c index 7d0f3523fdab..53fb853cd8fe 100644
>> --- a/drivers/nvme/target/fabrics-cmd.c
>> +++ b/drivers/nvme/target/fabrics-cmd.c
>> @@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)
>> case nvme_fabrics_type_property_get:
>> req->execute = nvmet_execute_prop_get;
>> break;
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> + case nvme_fabrics_type_auth_send:
>> + req->execute = nvmet_execute_auth_send;
>> + break;
>> + case nvme_fabrics_type_auth_receive:
>> + req->execute = nvmet_execute_auth_receive;
>> + break;
>> +#endif
>> default:
>> pr_debug("received unknown capsule type 0x%x\n",
>> cmd->fabrics.fctype);
>> @@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req
>> *req) struct nvmf_connect_data *d;
>> struct nvmet_ctrl *ctrl = NULL;
>> u16 status = 0;
>> + int ret;
>>
>> if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
>> return;
>> @@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct
>> nvmet_req *req)
>>
>> uuid_copy(&ctrl->hostid, &d->hostid);
>>
>> + ret = nvmet_setup_auth(ctrl, req);
>> + if (ret < 0) {
>> + pr_err("Failed to setup authentication, error %d\n", ret);
>> + nvmet_ctrl_put(ctrl);
>> + if (ret == -EPERM)
>> + status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
>> + else
>> + status = NVME_SC_INTERNAL;
>> + goto out;
>> + }
>> +
>> status = nvmet_install_queue(ctrl, req);
>> if (status) {
>> nvmet_ctrl_put(ctrl);
>> goto out;
>> }
>>
>> - pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
>> + pr_info("creating controller %d for subsystem %s for NQN %s%s%s.\n",
>> ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
>> - ctrl->pi_support ? " T10-PI is enabled" : "");
>> + ctrl->pi_support ? " T10-PI is enabled" : "",
>> + nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
>> req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
>>
>> + if (nvmet_has_auth(ctrl))
>> + nvmet_init_auth(ctrl, req);
>> out:
>> kfree(d);
>> complete:
>> @@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req
>> *req) }
>>
>> pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
>> + req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
>> + if (nvmet_has_auth(ctrl))
>> + nvmet_init_auth(ctrl, req);
>>
>> out:
>> kfree(d);
>> diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
>> index 06dd3d537f07..ef8815e137d7 100644
>> --- a/drivers/nvme/target/nvmet.h
>> +++ b/drivers/nvme/target/nvmet.h
>> @@ -108,6 +108,20 @@ struct nvmet_sq {
>> u16 size;
>> u32 sqhd;
>> bool sqhd_disabled;
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> + bool authenticated;
>> + u16 dhchap_tid;
>> + u16 dhchap_status;
>> + int dhchap_step;
>> + u8 dhchap_hash_id;
>> + u8 dhchap_hash_len;
>> + u8 *dhchap_c1;
>> + u8 *dhchap_c2;
>> + u32 dhchap_s1;
>> + u32 dhchap_s2;
>> + u8 *dhchap_skey;
>> + int dhchap_skey_len;
>> +#endif
>> struct completion free_done;
>> struct completion confirm_done;
>> };
>> @@ -209,6 +223,15 @@ struct nvmet_ctrl {
>> u64 err_counter;
>> struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS];
>> bool pi_support;
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> + u32 dhchap_seqnum;
>> + u8 *dhchap_key;
>> + size_t dhchap_key_len;
>> + struct crypto_shash *shash_tfm;
>> + struct crypto_kpp *dh_tfm;
>> + u32 dh_gid;
>> + u32 dh_keysize;
>> +#endif
>> };
>>
>> struct nvmet_subsys {
>> @@ -270,6 +293,10 @@ static inline struct nvmet_subsys
>> *namespaces_to_subsys(
>>
>> struct nvmet_host {
>> struct config_group group;
>> + u8 *dhchap_secret;
>> + u8 dhchap_key_hash;
>> + u8 dhchap_hash_id;
>> + u8 dhchap_dhgroup_id;
>> };
>>
>> static inline struct nvmet_host *to_host(struct config_item *item)
>> @@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req
>> *req, struct bio *bio) bio_put(bio);
>> }
>>
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +void nvmet_execute_auth_send(struct nvmet_req *req);
>> +void nvmet_execute_auth_receive(struct nvmet_req *req);
>> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret);
>> +int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
>> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
>> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
>> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl);
>> +void nvmet_auth_sq_free(struct nvmet_sq *sq);
>> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id);
>> +bool nvmet_check_auth_status(struct nvmet_req *req);
>> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
>> + unsigned int hash_len);
>> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
>> + unsigned int hash_len);
>> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
>> +{
>> + return ctrl->shash_tfm != NULL;
>> +}
>> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
>> + u8 *buf, int buf_size);
>> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
>> + u8 *buf, int buf_size);
>> +#else
>> +static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl,
>> + struct nvmet_req *req)
>> +{
>> + return 0;
>> +}
>> +static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl,
>> + struct nvmet_req *req) {};
>> +static inline void nvmet_reset_auth(struct nvmet_ctrl *ctrl) {};
>> +static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
>> +static inline bool nvmet_check_auth_status(struct nvmet_req *req)
>> +{
>> + return true;
>> +}
>> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
>> +{
>> + return false;
>> +}
>> +static inline const char *nvmet_dhchap_dhgroup_name(int dhgid) { return
>> NULL; } +#endif
>> +
>> #endif /* _NVMET_H */

Thanks for the review!

Cheers,

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

2021-07-18 12:44:03

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

On 7/17/21 6:49 PM, Stephan Müller wrote:
> Am Freitag, 16. Juli 2021, 13:04:23 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> Implement NVMe-oF In-Band authentication. This patch adds two new
>> fabric options 'dhchap_key' to specify the PSK and 'dhchap_authenticate'
>> to request bi-directional authentication of both the host and the
>> controller.
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>> drivers/nvme/host/Kconfig | 11 +
>> drivers/nvme/host/Makefile | 1 +
>> drivers/nvme/host/auth.c | 813 ++++++++++++++++++++++++++++++++++++
>> drivers/nvme/host/auth.h | 23 +
>> drivers/nvme/host/core.c | 77 +++-
>> drivers/nvme/host/fabrics.c | 65 ++-
>> drivers/nvme/host/fabrics.h | 8 +
>> drivers/nvme/host/nvme.h | 15 +
>> drivers/nvme/host/trace.c | 32 ++
>> 9 files changed, 1041 insertions(+), 4 deletions(-)
>> create mode 100644 drivers/nvme/host/auth.c
>> create mode 100644 drivers/nvme/host/auth.h
>>
>> diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
>> index c3f3d77f1aac..853c546305e9 100644
>> --- a/drivers/nvme/host/Kconfig
>> +++ b/drivers/nvme/host/Kconfig
>> @@ -85,3 +85,14 @@ config NVME_TCP
>> from https://github.com/linux-nvme/nvme-cli.
>>
>> If unsure, say N.
>> +
>> +config NVME_AUTH
>> + bool "NVM Express over Fabrics In-Band Authentication"
>> + depends on NVME_TCP
>> + select CRYPTO_SHA256
>> + select CRYPTO_SHA512
>
> What about adding CRYPTO_HMAC here?
>

Yes, you are correct. Will be fixing it.

>> + help
>> + This provides support for NVMe over Fabrics In-Band Authentication
>> + for the NVMe over TCP transport.
>> +
>> + If unsure, say N.
>> diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
>> index cbc509784b2e..03748a55a12b 100644
>> --- a/drivers/nvme/host/Makefile
>> +++ b/drivers/nvme/host/Makefile
>> @@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o
>> nvme-y += pci.o
>>
>> nvme-fabrics-y += fabrics.o
>> +nvme-fabrics-$(CONFIG_NVME_AUTH) += auth.o
>>
>> nvme-rdma-y += rdma.o
>>
>> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
>> new file mode 100644
>> index 000000000000..448a3adebea6
>> --- /dev/null
>> +++ b/drivers/nvme/host/auth.c
>> @@ -0,0 +1,813 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
>> + */
>> +
>> +#include <linux/crc32.h>
>> +#include <linux/base64.h>
>> +#include <asm/unaligned.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include "nvme.h"
>> +#include "fabrics.h"
>> +#include "auth.h"
>> +
>> +static u32 nvme_dhchap_seqnum;
>> +
>> +struct nvme_dhchap_context {
>> + struct crypto_shash *shash_tfm;
>> + unsigned char *key;
>> + size_t key_len;
>> + int qid;
>> + u32 s1;
>> + u32 s2;
>> + u16 transaction;
>> + u8 status;
>> + u8 hash_id;
>> + u8 hash_len;
>> + u8 c1[64];
>> + u8 c2[64];
>> + u8 response[64];
>> + u8 *ctrl_key;
>> + int ctrl_key_len;
>> + u8 *host_key;
>> + int host_key_len;
>> + u8 *sess_key;
>> + int sess_key_len;
>> +};
>> +
>> +struct nvmet_dhchap_hash_map {
>> + int id;
>> + int hash_len;
>> + const char hmac[15];
>> + const char digest[15];
>> +} hash_map[] = {
>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>> + .hash_len = 32,
>> + .hmac = "hmac(sha256)", .digest = "sha256" },
>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>> + .hash_len = 48,
>> + .hmac = "hmac(sha384)", .digest = "sha384" },
>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>> + .hash_len = 64,
>> + .hmac = "hmac(sha512)", .digest = "sha512" },
>> +};
>> +
>> +const char *nvme_auth_hmac_name(int hmac_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> + if (hash_map[i].id == hmac_id)
>> + return hash_map[i].hmac;
>> + }
>> + return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
>> +
>> +const char *nvme_auth_digest_name(int hmac_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> + if (hash_map[i].id == hmac_id)
>> + return hash_map[i].digest;
>> + }
>> + return NULL;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
>> +
>> +int nvme_auth_hmac_len(int hmac_id)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> + if (hash_map[i].id == hmac_id)
>> + return hash_map[i].hash_len;
>> + }
>> + return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_len);
>> +
>> +int nvme_auth_hmac_id(const char *hmac_name)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>> + if (!strncmp(hash_map[i].hmac, hmac_name,
>> + strlen(hash_map[i].hmac)))
>> + return hash_map[i].id;
>> + }
>> + return -1;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
>> +
>> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
>> + size_t *dhchap_key_len)
>> +{
>> + unsigned char *dhchap_key;
>> + u32 crc;
>> + int key_len;
>> + size_t allocated_len;
>> +
>> + allocated_len = strlen(dhchap_secret) - 10;
>
> Are you sure that the string is always at least 10 bytes long? If so, can you
> please add a comment to it?
>
> Also, is it guaranteed that we have an ASCII string? Note, a secret sounds to
> be like a binary string which may contain \0 as an appropriate value.
>

The string will always be in the transport encoding as specified in the
NVMe Base specification v2.0. Any other string will be rejected by the
ioctl interface.

>> + dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
>
> What about aligning it to CRYPTO_MINALIGN_ATTR to save a memcpy in
> shash_final?
>

Wasn't aware that I need to do that. Will be fixing it up.

>> + if (!dhchap_key)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + key_len = base64_decode(dhchap_secret + 10,
>> + allocated_len, dhchap_key);
>> + if (key_len != 36 && key_len != 52 &&
>> + key_len != 68) {
>> + pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
>> + key_len);
>> + kfree(dhchap_key);
>> + return ERR_PTR(-EINVAL);
>> + }
>> + pr_debug("DH-HMAC-CHAP Key: %*ph\n",
>> + (int)key_len, dhchap_key);
>> +
>> + /* The last four bytes is the CRC in little-endian format */
>> + key_len -= 4;
>> + /*
>> + * The linux implementation doesn't do pre- and post-increments,
>> + * so we have to do it manually.
>> + */
>> + crc = ~crc32(~0, dhchap_key, key_len);
>> +
>> + if (get_unaligned_le32(dhchap_key + key_len) != crc) {
>> + pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n",
>> + get_unaligned_le32(dhchap_key + key_len), crc);
>> + kfree(dhchap_key);
>> + return ERR_PTR(-EKEYREJECTED);
>> + }
>> + *dhchap_key_len = key_len;
>> + return dhchap_key;
>> +}
>> +EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
>> +
>> +static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
>> + void *data, size_t tl)
>> +{
>> + struct nvme_command cmd = {};
>> + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
>> + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
>> + struct request_queue *q = qid == NVME_QID_ANY ?
>> + ctrl->fabrics_q : ctrl->connect_q;
>> + int ret;
>> +
>> + cmd.auth_send.opcode = nvme_fabrics_command;
>> + cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
>> + cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
>> + cmd.auth_send.spsp0 = 0x01;
>> + cmd.auth_send.spsp1 = 0x01;
>> + cmd.auth_send.tl = tl;
>> +
>> + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid,
>> + 0, flags);
>> + if (ret)
>> + dev_dbg(ctrl->device,
>> + "%s: qid %d error %d\n", __func__, qid, ret);
>> + return ret;
>> +}
>> +
>> +static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
>> + void *buf, size_t al,
>> + u16 transaction, u8 expected_msg )
>> +{
>> + struct nvme_command cmd = {};
>> + struct nvmf_auth_dhchap_failure_data *data = buf;
>> + blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
>> + 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
>> + struct request_queue *q = qid == NVME_QID_ANY ?
>> + ctrl->fabrics_q : ctrl->connect_q;
>> + int ret;
>> +
>> + cmd.auth_receive.opcode = nvme_fabrics_command;
>> + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
>> + cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
>> + cmd.auth_receive.spsp0 = 0x01;
>> + cmd.auth_receive.spsp1 = 0x01;
>> + cmd.auth_receive.al = al;
>> +
>> + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid,
>> + 0, flags);
>> + if (ret > 0) {
>> + dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n",
>> + __func__, qid, ret);
>> + ret = -EIO;
>> + }
>> + if (ret < 0) {
>> + dev_dbg(ctrl->device, "%s: qid %d error %d\n",
>> + __func__, qid, ret);
>> + return ret;
>> + }
>> + dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
>> + __func__, qid, data->auth_type, data->auth_id);
>> + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
>> + data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
>> + return data->reason_code_explanation;
>> + }
>> + if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
>> + data->auth_id != expected_msg) {
>> + dev_warn(ctrl->device,
>> + "qid %d invalid message %02x/%02x\n",
>> + qid, data->auth_type, data->auth_id);
>> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + }
>> + if (le16_to_cpu(data->t_id) != transaction) {
>> + dev_warn(ctrl->device,
>> + "qid %d invalid transaction ID %d\n",
>> + qid, le16_to_cpu(data->t_id));
>> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap,
>> + void *buf, size_t buf_size)
>> +{
>> + struct nvmf_auth_dhchap_negotiate_data *data = buf;
>> + size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
>> +
>> + if (buf_size < size)
>> + return -EINVAL;
>> +
>> + memset((u8 *)buf, 0, size);
>> + data->auth_type = NVME_AUTH_COMMON_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
>> + data->t_id = cpu_to_le16(chap->transaction);
>> + data->sc_c = 0; /* No secure channel concatenation */
>> + data->napd = 1;
>> + data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
>> + data->auth_protocol[0].dhchap.halen = 3;
>> + data->auth_protocol[0].dhchap.dhlen = 1;
>> + data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
>> + data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
>> + data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
>> + data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
>> +
>> + return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap,
>> + void *buf, size_t buf_size)
>> +{
>> + struct nvmf_auth_dhchap_challenge_data *data = buf;
>> + size_t size = sizeof(*data) + data->hl + data->dhvlen;
>> + const char *gid_name;
>> +
>> + if (buf_size < size) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + return -ENOMSG;
>> + }
>> +
>> + if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 &&
>> + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 &&
>> + data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n",
>> + chap->qid, data->hashid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> + return -EPROTO;
>> + }
>> + switch (data->dhgid) {
>> + case NVME_AUTH_DHCHAP_DHGROUP_NULL:
>> + gid_name = "null";
>> + break;
>> + default:
>> + gid_name = NULL;
>> + break;
>> + }
>> + if (!gid_name) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
>> + chap->qid, data->dhgid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> + return -EPROTO;
>> + }
>> + if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> + return -EPROTO;
>> + }
>> + if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
>> + chap->qid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> + return -EPROTO;
>> + }
>> + dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n",
>> + __func__, chap->qid, data->hashid);
>> + if (nvme_auth_hmac_len(data->hashid) != data->hl) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: invalid hash length\n",
>> + chap->qid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> + return -EPROTO;
>> + }
>> + chap->hash_id = data->hashid;
>> + chap->hash_len = data->hl;
>> + chap->s1 = le32_to_cpu(data->seqnum);
>> + memcpy(chap->c1, data->cval, chap->hash_len);
>> +
>> + return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap,
>> + void *buf, size_t buf_size)
>> +{
>> + struct nvmf_auth_dhchap_reply_data *data = buf;
>> + size_t size = sizeof(*data);
>> +
>> + size += 2 * chap->hash_len;
>> + if (ctrl->opts->dhchap_auth) {
>> + get_random_bytes(chap->c2, chap->hash_len);
>
> Why are you using CRYPTO_RNG_DEFAULT when you are using get_random_bytes here?
>

Errm ... do I?
Seems that my crypto ignorance is showing here; 'get_random_bytes()' is
the usual function we're using for drivers; if there is another way for
crypto please enlighten me.

>> + chap->s2 = nvme_dhchap_seqnum++;
>> + } else
>> + memset(chap->c2, 0, chap->hash_len);
>> +
>> + if (chap->host_key_len)
>> + size += chap->host_key_len;
>> +
>> + if (buf_size < size)
>> + return -EINVAL;
>> +
>> + memset(buf, 0, size);
>> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
>> + data->t_id = cpu_to_le16(chap->transaction);
>> + data->hl = chap->hash_len;
>> + data->dhvlen = chap->host_key_len;
>> + data->seqnum = cpu_to_le32(chap->s2);
>> + memcpy(data->rval, chap->response, chap->hash_len);
>> + if (ctrl->opts->dhchap_auth) {
>> + dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
>> + __func__, chap->qid,
>> + chap->hash_len, chap->c2);
>> + data->cvalid = 1;
>> + memcpy(data->rval + chap->hash_len, chap->c2,
>> + chap->hash_len);
>> + }
>> + if (chap->host_key_len)
>> + memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
>> + chap->host_key_len);
>> +
>> + return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_success1(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap,
>> + void *buf, size_t buf_size)
>> +{
>> + struct nvmf_auth_dhchap_success1_data *data = buf;
>> + size_t size = sizeof(*data);
>> +
>> + if (ctrl->opts->dhchap_auth)
>> + size += chap->hash_len;
>> +
>> +
>> + if (buf_size < size) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + return -ENOMSG;
>> + }
>> +
>> + if (data->hl != chap->hash_len) {
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: invalid hash length %d\n",
>> + chap->qid, data->hl);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> + return -EPROTO;
>> + }
>> +
>> + if (!data->rvalid)
>> + return 0;
>> +
>> + /* Validate controller response */
>> + if (memcmp(chap->response, data->rval, data->hl)) {
>> + dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
>> + __func__, chap->qid, chap->hash_len, data->rval);
>> + dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
>> + __func__, chap->qid, chap->hash_len, chap->response);
>> + dev_warn(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: controller authentication failed\n",
>> + chap->qid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> + return -EPROTO;
>> + }
>> + dev_info(ctrl->device,
>> + "qid %d: DH-HMAC-CHAP: controller authenticated\n",
>> + chap->qid);
>> + return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap,
>> + void *buf, size_t buf_size)
>> +{
>> + struct nvmf_auth_dhchap_success2_data *data = buf;
>> + size_t size = sizeof(*data);
>> +
>> + memset(buf, 0, size);
>> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
>> + data->t_id = cpu_to_le16(chap->transaction);
>> +
>> + return size;
>> +}
>> +
>> +static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap,
>> + void *buf, size_t buf_size)
>> +{
>> + struct nvmf_auth_dhchap_failure_data *data = buf;
>> + size_t size = sizeof(*data);
>> +
>> + memset(buf, 0, size);
>> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> + data->t_id = cpu_to_le16(chap->transaction);
>> + data->reason_code = 1;
>> + data->reason_code_explanation = chap->status;
>> +
>> + return size;
>> +}
>> +
>> +int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap)
>> +{
>> + char *hash_name;
>> + int ret;
>> +
>> + switch (chap->hash_id) {
>> + case NVME_AUTH_DHCHAP_HASH_SHA256:
>> + hash_name = "hmac(sha256)";
>> + break;
>> + case NVME_AUTH_DHCHAP_HASH_SHA384:
>> + hash_name = "hmac(sha384)";
>> + break;
>> + case NVME_AUTH_DHCHAP_HASH_SHA512:
>> + hash_name = "hmac(sha512)";
>> + break;
>> + default:
>> + hash_name = NULL;
>> + break;
>> + }
>> + if (!hash_name) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + return -EPROTO;
>> + }
>> + chap->shash_tfm = crypto_alloc_shash(hash_name, 0,
>> + CRYPTO_ALG_ALLOCATES_MEMORY);
>> + if (IS_ERR(chap->shash_tfm)) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + chap->shash_tfm = NULL;
>> + return -EPROTO;
>> + }
>> + if (!chap->key) {
>> + dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
>> + chap->qid);
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + crypto_free_shash(chap->shash_tfm);
>> + chap->shash_tfm = NULL;
>> + return -EINVAL;
>> + }
>> + ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
>> + if (ret) {
>> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> + crypto_free_shash(chap->shash_tfm);
>> + chap->shash_tfm = NULL;
>> + return ret;
>> + }
>> + dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>> + chap->qid, hash_name);
>> + return 0;
>> +}
>> +
>> +static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap)
>> +{
>> + SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>> + u8 buf[4], *challenge = chap->c1;
>> + int ret;
>> +
>> + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
> %d\n",
>> + __func__, chap->qid, chap->s1, chap->transaction);
>> + shash->tfm = chap->shash_tfm;
>> + ret = crypto_shash_init(shash);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, challenge, chap->hash_len);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le32(chap->s1, buf);
>> + ret = crypto_shash_update(shash, buf, 4);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le16(chap->transaction, buf);
>> + ret = crypto_shash_update(shash, buf, 2);
>> + if (ret)
>> + goto out;
>> + memset(buf, 0, sizeof(buf));
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, "HostHost", 8);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>> + strlen(ctrl->opts->host->nqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>> + strlen(ctrl->opts->subsysnqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_final(shash, chap->response);
>> +out:
>> + return ret;
>> +}
>> +
>> +static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap)
>> +{
>> + SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>> + u8 buf[4], *challenge = chap->c2;
>> + int ret;
>> +
>> + dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction
> %d\n",
>> + __func__, chap->qid, chap->s2, chap->transaction);
>> + dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
>> + __func__, chap->qid, chap->hash_len, challenge);
>> + dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
>> + __func__, chap->qid, ctrl->opts->subsysnqn);
>> + dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
>> + __func__, chap->qid, ctrl->opts->host->nqn);
>> + shash->tfm = chap->shash_tfm;
>> + ret = crypto_shash_init(shash);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, challenge, chap->hash_len);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le32(chap->s2, buf);
>> + ret = crypto_shash_update(shash, buf, 4);
>> + if (ret)
>> + goto out;
>> + put_unaligned_le16(chap->transaction, buf);
>> + ret = crypto_shash_update(shash, buf, 2);
>> + if (ret)
>> + goto out;
>> + memset(buf, 0, 4);
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, "Controller", 10);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>> + strlen(ctrl->opts->subsysnqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, buf, 1);
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>> + strlen(ctrl->opts->host->nqn));
>> + if (ret)
>> + goto out;
>> + ret = crypto_shash_final(shash, chap->response);
>> +out:
>> + return ret;
>> +}
>> +
>> +int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
>> + struct nvme_dhchap_context *chap)
>> +{
>> + int ret;
>> + u8 key_hash;
>> + const char *hmac_name;
>> + struct crypto_shash *key_tfm;
>> +
>> + if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
>> + &key_hash) != 1)
>> + return -EINVAL;
>> +
>> + chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
>> + &chap->key_len);
>> + if (IS_ERR(chap->key)) {
>> + ret = PTR_ERR(chap->key);
>> + chap->key = NULL;
>> + return ret;
>> + }
>> +
>> + if (key_hash == 0)
>> + return 0;
>> +
>> + hmac_name = nvme_auth_hmac_name(key_hash);
>> + if (!hmac_name) {
>> + pr_debug("Invalid key hash id %d\n", key_hash);
>> + return -EKEYREJECTED;
>> + }
>> +
>> + key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
>> + if (IS_ERR(key_tfm)) {
>> + kfree(chap->key);
>> + chap->key = NULL;
>> + ret = PTR_ERR(key_tfm);
>> + } else {
>> + SHASH_DESC_ON_STACK(shash, key_tfm);
>> +
>> + shash->tfm = key_tfm;
>> + ret = crypto_shash_setkey(key_tfm, chap->key,
>> + chap->key_len);
>> + if (ret < 0) {
>> + crypto_free_shash(key_tfm);
>> + kfree(chap->key);
>> + chap->key = NULL;
>> + return ret;
>> + }
>> + crypto_shash_init(shash);
>> + crypto_shash_update(shash, ctrl->opts->host->nqn,
>> + strlen(ctrl->opts->host->nqn));
>> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
>> + crypto_shash_final(shash, chap->key);
>> + crypto_free_shash(key_tfm);
>> + }
>> + return 0;
>> +}
>> +
>> +void nvme_auth_free(struct nvme_dhchap_context *chap)
>> +{
>> + if (chap->shash_tfm)
>> + crypto_free_shash(chap->shash_tfm);
>> + if (chap->key)
>> + kfree(chap->key);
>> + if (chap->ctrl_key)
>> + kfree(chap->ctrl_key);
>> + if (chap->host_key)
>> + kfree(chap->host_key);
>> + if (chap->sess_key)
>> + kfree(chap->sess_key);
>> + kfree(chap);
>
> kfree_sensitive in all cases as all buffers have sensitive data?
>

Yes, will be fixing it up.

>> +}
>> +
>> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
>> +{
>> + struct nvme_dhchap_context *chap;
>> + void *buf;
>> + size_t buf_size, tl;
>> + int ret = 0;
>> +
>> + chap = kzalloc(sizeof(*chap), GFP_KERNEL);
>
> Suggestion: make sure that chap->response is aligned to CRYPTO_MINALIGN_ATTR -
> then you would save a memcpy in crypto_shash_final
>

Ok.

Cheers,

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

2021-07-18 12:44:54

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms

On 7/17/21 6:50 PM, Stephan Müller wrote:
> Am Freitag, 16. Juli 2021, 13:04:28 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE
>
> curve25519?
>

Of course.

Cheers,

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

2021-07-18 12:47:52

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

Am Sonntag, 18. Juli 2021, 14:43:43 CEST schrieb Hannes Reinecke:

Hi Hannes,

> >> + size += 2 * chap->hash_len;
> >> + if (ctrl->opts->dhchap_auth) {
> >> + get_random_bytes(chap->c2, chap->hash_len);
> >
> > Why are you using CRYPTO_RNG_DEFAULT when you are using get_random_bytes
> > here?
> Errm ... do I?
> Seems that my crypto ignorance is showing here; 'get_random_bytes()' is
> the usual function we're using for drivers; if there is another way for
> crypto please enlighten me.

Apologies, I was looking at CONFIG_RNG where you set CRYPTO_RNG_DEFAULT.
Please ignore my comment here.

Ciao
Stephan


2021-07-18 13:00:35

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:

Hi Hannes,

> On 7/17/21 6:49 PM, Stephan M?ller wrote:
> > Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:
> >
> > Hi Hannes,
> >
> >> Implement support for NVMe-oF In-Band authentication. This patch
> >> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
> >> to the 'host' configfs directory. The 'dhchap_key' needs to be
> >> specified in the format outlined in the base spec.
> >> Augmented challenge support is not implemented, and concatenation
> >> with TLS encryption is not supported.
> >>
> >> Signed-off-by: Hannes Reinecke <[email protected]>
> >> ---
> >>
> >> drivers/nvme/target/Kconfig | 10 +
> >> drivers/nvme/target/Makefile | 1 +
> >> drivers/nvme/target/admin-cmd.c | 4 +
> >> drivers/nvme/target/auth.c | 352 +++++++++++++++++++
> >> drivers/nvme/target/configfs.c | 71 +++-
> >> drivers/nvme/target/core.c | 8 +
> >> drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
> >> drivers/nvme/target/fabrics-cmd.c | 30 +-
> >> drivers/nvme/target/nvmet.h | 71 ++++
> >> 9 files changed, 1004 insertions(+), 3 deletions(-)
> >> create mode 100644 drivers/nvme/target/auth.c
> >> create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
> >>
> >> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
> >> index 4be2ececbc45..d5656ef1559e 100644
> >> --- a/drivers/nvme/target/Kconfig
> >> +++ b/drivers/nvme/target/Kconfig
> >> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
> >>
> >> devices over TCP.
> >>
> >> If unsure, say N.
> >>
> >> +
> >> +config NVME_TARGET_AUTH
> >> + bool "NVMe over Fabrics In-band Authentication support"
> >> + depends on NVME_TARGET
> >> + select CRYPTO_SHA256
> >> + select CRYPTO_SHA512
> >> + help
> >> + This enables support for NVMe over Fabrics In-band Authentication
> >> +
> >> + If unsure, say N.
> >> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
> >> index 9837e580fa7e..c66820102493 100644
> >> --- a/drivers/nvme/target/Makefile
> >> +++ b/drivers/nvme/target/Makefile
> >> @@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o
> >
> > fabrics-cmd.o \
> >
> >> discovery.o io-cmd-file.o io-cmd-bdev.o
> >>
> >> nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
> >> nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
> >>
> >> +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o
> >>
> >> nvme-loop-y += loop.o
> >> nvmet-rdma-y += rdma.o
> >> nvmet-fc-y += fc.o
> >>
> >> diff --git a/drivers/nvme/target/admin-cmd.c
> >> b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644
> >> --- a/drivers/nvme/target/admin-cmd.c
> >> +++ b/drivers/nvme/target/admin-cmd.c
> >> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
> >>
> >> if (nvme_is_fabrics(cmd))
> >>
> >> return nvmet_parse_fabrics_cmd(req);
> >>
> >> +
> >> + if (unlikely(!nvmet_check_auth_status(req)))
> >> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
> >> +
> >>
> >> if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
> >>
> >> return nvmet_parse_discovery_cmd(req);
> >>
> >> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
> >> new file mode 100644
> >> index 000000000000..00c7d051dfb1
> >> --- /dev/null
> >> +++ b/drivers/nvme/target/auth.c
> >> @@ -0,0 +1,352 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
> >> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
> >> + * All rights reserved.
> >> + */
> >> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> >> +#include <linux/module.h>
> >> +#include <linux/init.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/err.h>
> >> +#include <crypto/hash.h>
> >> +#include <crypto/kpp.h>
> >> +#include <crypto/dh.h>
> >> +#include <crypto/ffdhe.h>
> >> +#include <linux/crc32.h>
> >> +#include <linux/base64.h>
> >> +#include <linux/ctype.h>
> >> +#include <linux/random.h>
> >> +#include <asm/unaligned.h>
> >> +
> >> +#include "nvmet.h"
> >> +#include "../host/auth.h"
> >> +
> >> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
> >> +{
> >> + if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
> >> + return -EINVAL;
> >> + if (host->dhchap_key_hash > 3) {
> >> + pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
> >> + host->dhchap_key_hash);
> >> + return -EINVAL;
> >> + }
> >> + if (host->dhchap_key_hash > 0) {
> >> + /* Validate selected hash algorithm */
> >> + const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
> >> +
> >> + if (!crypto_has_shash(hmac, 0, 0)) {
> >> + pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
> >> + host->dhchap_key_hash = -1;
> >> + return -EAGAIN;
> >> + }
> >> + /* Use this hash as default */
> >> + if (!host->dhchap_hash_id)
> >> + host->dhchap_hash_id = host->dhchap_key_hash;
> >> + }
> >> + host->dhchap_secret = kstrdup(secret, GFP_KERNEL);
> >
> > Just like before - are you sure that the secret is an ASCII string and no
> > binary blob?
>
> That is ensured by the transport encoding (cf NVMe Base Specification
> version 2.0). Also, this information is being passed in via the configfs
> interface, so it's bounded by PAGE_SIZE. But yes, we should be inserting
> a terminating 'NULL' character at the end of the page to ensure we don't
> incur an buffer overrun. Any other failure will be checked for during
> base64 decoding.
>
> >> + if (!host->dhchap_secret)
> >> + return -ENOMEM;
> >> + /* Default to SHA256 */
> >> + if (!host->dhchap_hash_id)
> >> + host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;
> >> +
> >> + pr_debug("Using hash %s\n",
> >> + nvme_auth_hmac_name(host->dhchap_hash_id));
> >> + return 0;
> >> +}
> >> +
> >> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
> >> +{
> >> + int ret = -ENOTSUPP;
> >> +
> >> + if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
> >> + return 0;
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
> >> +{
> >> + int ret = 0;
> >> + struct nvmet_host_link *p;
> >> + struct nvmet_host *host = NULL;
> >> + const char *hash_name;
> >> +
> >> + down_read(&nvmet_config_sem);
> >> + if (ctrl->subsys->type == NVME_NQN_DISC)
> >> + goto out_unlock;
> >> +
> >> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
> >> + pr_debug("check %s\n", nvmet_host_name(p->host));
> >> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
> >> + continue;
> >> + host = p->host;
> >> + break;
> >> + }
> >> + if (!host) {
> >> + pr_debug("host %s not found\n", ctrl->hostnqn);
> >> + ret = -EPERM;
> >> + goto out_unlock;
> >> + }
> >> + if (!host->dhchap_secret) {
> >> + pr_debug("No authentication provided\n");
> >> + goto out_unlock;
> >> + }
> >> +
> >> + hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
> >> + if (!hash_name) {
> >> + pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);
> >> + ret = -EINVAL;
> >> + goto out_unlock;
> >> + }
> >> + ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
> >> + CRYPTO_ALG_ALLOCATES_MEMORY);
> >> + if (IS_ERR(ctrl->shash_tfm)) {
> >> + pr_debug("failed to allocate shash %s\n", hash_name);
> >> + ret = PTR_ERR(ctrl->shash_tfm);
> >> + ctrl->shash_tfm = NULL;
> >> + goto out_unlock;
> >> + }
> >> +
> >> + ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
> >> + &ctrl->dhchap_key_len);
> >> + if (IS_ERR(ctrl->dhchap_key)) {
> >> + pr_debug("failed to extract host key, error %d\n", ret);
> >> + ret = PTR_ERR(ctrl->dhchap_key);
> >> + ctrl->dhchap_key = NULL;
> >> + goto out_free_hash;
> >> + }
> >> + if (host->dhchap_key_hash) {
> >> + struct crypto_shash *key_tfm;
> >> +
> >> + hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
> >> + key_tfm = crypto_alloc_shash(hash_name, 0, 0);
> >> + if (IS_ERR(key_tfm)) {
> >> + ret = PTR_ERR(key_tfm);
> >> + goto out_free_hash;
> >> + } else {
> >> + SHASH_DESC_ON_STACK(shash, key_tfm);
> >> +
> >> + shash->tfm = key_tfm;
> >> + ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
> >> + ctrl->dhchap_key_len);
> >> + crypto_shash_init(shash);
> >> + crypto_shash_update(shash, ctrl->subsys->subsysnqn,
> >> + strlen(ctrl->subsys->subsysnqn));
> >> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
> >> + crypto_shash_final(shash, ctrl->dhchap_key);
> >> + crypto_free_shash(key_tfm);
> >> + }
> >> + }
> >> + pr_debug("%s: using key %*ph\n", __func__,
> >> + (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
> >> + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
> >
> > Is it truly necessary to keep the key around in ctrl->dhchap_key? It looks
> > to me that this buffer is only used here and thus could be turned into a
> > local variable. Keys flying around in memory is not a good idea. :-)
>
> The key is also used when using the ffdhe algorithm.
> Note: I _think_ that I need to use this key for the ffdhe algorithm,
> because the implementation I came up with is essentially plain DH with
> pre-defined 'p', 'q' and 'g' values. But the DH implementation also
> requires a 'key', and for that I'm using this key here.
>
> It might be that I'm completely off, and don't need to use a key for our
> DH implementation. In that case you are correct.
> (And that's why I said I'll need a review of the FFDHE implementation).
> But for now I'll need the key for FFDHE.

Do I understand you correctly that the dhchap_key is used as the input to the
DH - i.e. it is the remote public key then? It looks strange that this is used
for DH but then it is changed here by hashing it together with something else
to form a new dhchap_key. Maybe that is what the protocol says. But it sounds
strange to me, especially when you think that dhchap_key would be, say, 2048
bits if it is truly the remote public key and then after the hashing it is 256
or 512 bits depending on the HMAC type. This means that after the hashing,
this dhchap_key cannot be used for FFC-DH.

Or are you using the dhchap_key for two different purposes?

It seems I miss something here.


> >> + response = kmalloc(data->hl, GFP_KERNEL);
> >
> > Again, align to CRYPTO_MINALIGN_ATTR?
>
> Ah, _that_ alignment.
> Wasn't aware that I need to align to anything.
> But if that's required, sure, I'll be fixing it.

Again, that only saves you a memcpy in shash_final.

Ciao
Stephan


2021-07-18 13:01:25

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 07/11] nvme-auth: augmented challenge support

Am Sonntag, 18. Juli 2021, 14:27:48 CEST schrieb Hannes Reinecke:

Hi Hannes,

> On 7/17/21 6:49 PM, Stephan M?ller wrote:
> > Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:
> >
> > Hi Hannes,
> >
> >> Implement support for augmented challenge using FFDHE groups.
> >>
> >> Signed-off-by: Hannes Reinecke <[email protected]>
> >> ---
> >>
> >> drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
> >> 1 file changed, 371 insertions(+), 32 deletions(-)
> >>
> >> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
> >> index 448a3adebea6..754343aced19 100644
> >> --- a/drivers/nvme/host/auth.c
> >> +++ b/drivers/nvme/host/auth.c
> >> @@ -8,6 +8,8 @@
> >>
> >> #include <asm/unaligned.h>
> >> #include <crypto/hash.h>
> >> #include <crypto/kpp.h>
> >>
> >> +#include <crypto/dh.h>
> >> +#include <crypto/ffdhe.h>
> >>
> >> #include "nvme.h"
> >> #include "fabrics.h"
> >> #include "auth.h"
> >>
> >> @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;
> >>
> >> struct nvme_dhchap_context {
> >>
> >> struct crypto_shash *shash_tfm;
> >>
> >> + struct crypto_shash *digest_tfm;
> >> + struct crypto_kpp *dh_tfm;
> >>
> >> unsigned char *key;
> >> size_t key_len;
> >> int qid;
> >>
> >> @@ -25,6 +29,8 @@ struct nvme_dhchap_context {
> >>
> >> u8 status;
> >> u8 hash_id;
> >> u8 hash_len;
> >>
> >> + u8 dhgroup_id;
> >> + u16 dhgroup_size;
> >>
> >> u8 c1[64];
> >> u8 c2[64];
> >> u8 response[64];
> >>
> >> @@ -36,6 +42,94 @@ struct nvme_dhchap_context {
> >>
> >> int sess_key_len;
> >>
> >> };
> >>
> >> +struct nvme_auth_dhgroup_map {
> >> + int id;
> >> + const char name[16];
> >> + const char kpp[16];
> >> + int privkey_size;
> >> + int pubkey_size;
> >> +} dhgroup_map[] = {
> >> + { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
> >> + .name = "NULL", .kpp = "NULL",
> >> + .privkey_size = 0, .pubkey_size = 0 },
> >> + { .id = NVME_AUTH_DHCHAP_DHGROUP_2048,
> >> + .name = "ffdhe2048", .kpp = "dh",
> >> + .privkey_size = 256, .pubkey_size = 256 },
> >> + { .id = NVME_AUTH_DHCHAP_DHGROUP_3072,
> >> + .name = "ffdhe3072", .kpp = "dh",
> >> + .privkey_size = 384, .pubkey_size = 384 },
> >> + { .id = NVME_AUTH_DHCHAP_DHGROUP_4096,
> >> + .name = "ffdhe4096", .kpp = "dh",
> >> + .privkey_size = 512, .pubkey_size = 512 },
> >> + { .id = NVME_AUTH_DHCHAP_DHGROUP_6144,
> >> + .name = "ffdhe6144", .kpp = "dh",
> >> + .privkey_size = 768, .pubkey_size = 768 },
> >> + { .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
> >> + .name = "ffdhe8192", .kpp = "dh",
> >> + .privkey_size = 1024, .pubkey_size = 1024 },
> >> +};
> >> +
> >> +const char *nvme_auth_dhgroup_name(int dhgroup_id)
> >> +{
> >> + int i;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> >> + if (dhgroup_map[i].id == dhgroup_id)
> >> + return dhgroup_map[i].name;
> >> + }
> >> + return NULL;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
> >> +
> >> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id)
> >> +{
> >> + int i;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> >> + if (dhgroup_map[i].id == dhgroup_id)
> >> + return dhgroup_map[i].pubkey_size;
> >> + }
> >> + return -1;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_pubkey_size);
> >> +
> >> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id)
> >> +{
> >> + int i;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> >> + if (dhgroup_map[i].id == dhgroup_id)
> >> + return dhgroup_map[i].privkey_size;
> >> + }
> >> + return -1;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_privkey_size);
> >> +
> >> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id)
> >> +{
> >> + int i;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> >> + if (dhgroup_map[i].id == dhgroup_id)
> >> + return dhgroup_map[i].kpp;
> >> + }
> >> + return NULL;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
> >> +
> >> +int nvme_auth_dhgroup_id(const char *dhgroup_name)
> >> +{
> >> + int i;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> >> + if (!strncmp(dhgroup_map[i].name, dhgroup_name,
> >> + strlen(dhgroup_map[i].name)))
> >> + return dhgroup_map[i].id;
> >> + }
> >> + return -1;
> >> +}
> >> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
> >> +
> >>
> >> struct nvmet_dhchap_hash_map {
> >>
> >> int id;
> >> int hash_len;
> >>
> >> @@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct
> >> nvme_ctrl *ctrl, data->napd = 1;
> >>
> >> data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
> >> data->auth_protocol[0].dhchap.halen = 3;
> >>
> >> - data->auth_protocol[0].dhchap.dhlen = 1;
> >> + data->auth_protocol[0].dhchap.dhlen = 6;
> >>
> >> data->auth_protocol[0].dhchap.idlist[0] =
> >> NVME_AUTH_DHCHAP_HASH_SHA256;
> >> data->auth_protocol[0].dhchap.idlist[1] =
> >> NVME_AUTH_DHCHAP_HASH_SHA384;
> >> data->auth_protocol[0].dhchap.idlist[2] =
> >> NVME_AUTH_DHCHAP_HASH_SHA512;
> >> data->auth_protocol[0].dhchap.idlist[3] =
> >> NVME_AUTH_DHCHAP_DHGROUP_NULL;
> >>
> >> + data->auth_protocol[0].dhchap.idlist[4] =
> >> NVME_AUTH_DHCHAP_DHGROUP_2048;
> >> + data->auth_protocol[0].dhchap.idlist[5] =
> >> NVME_AUTH_DHCHAP_DHGROUP_3072;
> >> + data->auth_protocol[0].dhchap.idlist[6] =
> >> NVME_AUTH_DHCHAP_DHGROUP_4096;
> >> + data->auth_protocol[0].dhchap.idlist[7] =
> >> NVME_AUTH_DHCHAP_DHGROUP_6144;
> >> + data->auth_protocol[0].dhchap.idlist[8] =
> >> NVME_AUTH_DHCHAP_DHGROUP_8192;
> >>
> >> return size;
> >>
> >> }
> >>
> >> @@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct
> >> nvme_ctrl
> >> *ctrl, chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> >>
> >> return -EPROTO;
> >>
> >> }
> >>
> >> - switch (data->dhgid) {
> >> - case NVME_AUTH_DHCHAP_DHGROUP_NULL:
> >> - gid_name = "null";
> >> - break;
> >> - default:
> >> - gid_name = NULL;
> >> - break;
> >> - }
> >> + gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
> >>
> >> if (!gid_name) {
> >>
> >> dev_warn(ctrl->device,
> >>
> >> "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
> >>
> >> @@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct
> >> nvme_ctrl *ctrl, return -EPROTO;
> >>
> >> }
> >> if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> >>
> >> - chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> >> - return -EPROTO;
> >> - }
> >> - if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0)
> >> {
> >> + if (data->dhvlen == 0) {
> >> + dev_warn(ctrl->device,
> >> + "qid %d: DH-HMAC-CHAP: empty DH value\n",
> >> + chap->qid);
> >> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> >> + return -EPROTO;
> >> + }
> >> + chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
> >> + if (IS_ERR(chap->dh_tfm)) {
> >> + dev_warn(ctrl->device,
> >> + "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
> >> + chap->qid, gid_name);
> >> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> >> + chap->dh_tfm = NULL;
> >> + return -EPROTO;
> >> + }
> >> + chap->dhgroup_id = data->dhgid;
> >> + } else if (data->dhvlen != 0) {
> >>
> >> dev_warn(ctrl->device,
> >>
> >> "qid %d: DH-HMAC-CHAP: invalid DH value for NULL
DH\n",
> >>
> >> chap->qid);
> >>
> >> @@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct
> >> nvme_ctrl
> >> *ctrl, chap->hash_len = data->hl;
> >>
> >> chap->s1 = le32_to_cpu(data->seqnum);
> >> memcpy(chap->c1, data->cval, chap->hash_len);
> >>
> >> + if (data->dhvlen) {
> >> + chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL);
> >> + if (!chap->ctrl_key)
> >> + return -ENOMEM;
> >> + chap->ctrl_key_len = data->dhvlen;
> >> + memcpy(chap->ctrl_key, data->cval + chap->hash_len,
> >> + data->dhvlen);
> >> + dev_dbg(ctrl->device, "ctrl public key %*ph\n",
> >> + (int)chap->ctrl_key_len, chap->ctrl_key);
> >> + }
> >>
> >> return 0;
> >>
> >> }
> >>
> >> @@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl
> >> *ctrl, memcpy(data->rval + chap->hash_len, chap->c2,
> >>
> >> chap->hash_len);
> >>
> >> }
> >>
> >> - if (chap->host_key_len)
> >> + if (chap->host_key_len) {
> >> + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
> >> + __func__, chap->qid,
> >> + chap->host_key_len, chap->host_key);
> >>
> >> memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
> >>
> >> chap->host_key_len);
> >>
> >> -
> >> + }
> >>
> >> return size;
> >>
> >> }
> >>
> >> @@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct
> >> nvme_ctrl
> >> *ctrl, int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> >>
> >> struct nvme_dhchap_context *chap)
> >>
> >> {
> >>
> >> - char *hash_name;
> >> + const char *hash_name, *digest_name;
> >>
> >> int ret;
> >>
> >> - switch (chap->hash_id) {
> >> - case NVME_AUTH_DHCHAP_HASH_SHA256:
> >> - hash_name = "hmac(sha256)";
> >> - break;
> >> - case NVME_AUTH_DHCHAP_HASH_SHA384:
> >> - hash_name = "hmac(sha384)";
> >> - break;
> >> - case NVME_AUTH_DHCHAP_HASH_SHA512:
> >> - hash_name = "hmac(sha512)";
> >> - break;
> >> - default:
> >> - hash_name = NULL;
> >> - break;
> >> - }
> >> + hash_name = nvme_auth_hmac_name(chap->hash_id);
> >>
> >> if (!hash_name) {
> >>
> >> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> >> return -EPROTO;
> >>
> >> @@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> >>
> >> chap->shash_tfm = NULL;
> >> return -EPROTO;
> >>
> >> }
> >>
> >> + digest_name = nvme_auth_digest_name(chap->hash_id);
> >> + if (!digest_name) {
> >> + crypto_free_shash(chap->shash_tfm);
> >> + chap->shash_tfm = NULL;
> >> + return -EPROTO;
> >> + }
> >> + chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0);
> >> + if (IS_ERR(chap->digest_tfm)) {
> >> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> >> + crypto_free_shash(chap->shash_tfm);
> >> + chap->shash_tfm = NULL;
> >> + chap->digest_tfm = NULL;
> >> + return -EPROTO;
> >> + }
> >>
> >> if (!chap->key) {
> >>
> >> dev_warn(ctrl->device, "qid %d: cannot select hash, no
key\n",
> >>
> >> chap->qid);
> >>
> >> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> >>
> >> + crypto_free_shash(chap->digest_tfm);
> >>
> >> crypto_free_shash(chap->shash_tfm);
> >> chap->shash_tfm = NULL;
> >>
> >> + chap->digest_tfm = NULL;
> >>
> >> return -EINVAL;
> >>
> >> }
> >> ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap-
>key_len);
> >> if (ret) {
> >>
> >> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> >>
> >> + crypto_free_shash(chap->digest_tfm);
> >>
> >> crypto_free_shash(chap->shash_tfm);
> >> chap->shash_tfm = NULL;
> >>
> >> + chap->digest_tfm = NULL;
> >>
> >> return ret;
> >>
> >> }
> >>
> >> - dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> >> - chap->qid, hash_name);
> >> + dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> >> + chap->qid, hash_name);
> >>
> >> return 0;
> >>
> >> }
> >>
> >> +static int nvme_auth_augmented_challenge(struct nvme_dhchap_context
> >> *chap,
> >> + u8 *challenge, u8 *aug)
> >> +{
> >> + struct crypto_shash *tfm;
> >> + struct shash_desc *desc;
> >> + u8 *hashed_key;
> >> + const char *hash_name;
> >> + int ret;
> >> +
> >> + hashed_key = kmalloc(chap->hash_len, GFP_KERNEL);
> >> + if (!hashed_key)
> >> + return -ENOMEM;
> >> +
> >> + ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key,
> >> + chap->sess_key_len, hashed_key);
> >> + if (ret < 0) {
> >> + pr_debug("failed to hash session key, err %d\n", ret);
> >> + kfree(hashed_key);
> >> + return ret;
> >> + }
> >> + hash_name = crypto_shash_alg_name(chap->shash_tfm);
> >> + if (!hash_name) {
> >> + pr_debug("Invalid hash algoritm\n");
> >> + return -EINVAL;
> >> + }
> >> + tfm = crypto_alloc_shash(hash_name, 0, 0);
> >> + if (IS_ERR(tfm)) {
> >> + ret = PTR_ERR(tfm);
> >> + goto out_free_key;
> >> + }
> >> + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
> >> + GFP_KERNEL);
> >> + if (!desc) {
> >> + ret = -ENOMEM;
> >> + goto out_free_hash;
> >> + }
> >> + desc->tfm = tfm;
> >> +
> >> + ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len);
> >> + if (ret)
> >> + goto out_free_desc;
> >> + ret = crypto_shash_init(desc);
> >> + if (ret)
> >> + goto out_free_desc;
> >> + crypto_shash_update(desc, challenge, chap->hash_len);
> >> + crypto_shash_final(desc, aug);
> >> +
> >> +out_free_desc:
> >> + kfree_sensitive(desc);
> >> +out_free_hash:
> >> + crypto_free_shash(tfm);
> >> +out_free_key:
> >> + kfree(hashed_key);
> >> + return ret;
> >> +}
> >> +
> >>
> >> static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
> >>
> >> struct nvme_dhchap_context *chap)
> >>
> >> {
> >>
> >> @@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct
> >> nvme_ctrl *ctrl,
> >>
> >> dev_dbg(ctrl->device, "%s: qid %d host response seq %d
transaction
> >
> > %d\n",
> >
> >> __func__, chap->qid, chap->s1, chap->transaction);
> >>
> >> + if (chap->dh_tfm) {
> >> + challenge = kmalloc(chap->hash_len, GFP_KERNEL);
> >
> > Again, alignment?
>
> Again, why?

This buffer is the digest buffer of a shash_final, no? check
crypto_shash_final and see that you could spare yourself some extra work if
aligned.


Ciao
Stephan


2021-07-18 13:27:46

by Herbert Xu

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On Sun, Jul 18, 2021 at 02:37:34PM +0200, Hannes Reinecke wrote:
>
> > > + response = kmalloc(data->hl, GFP_KERNEL);
> >
> > Again, align to CRYPTO_MINALIGN_ATTR?
>
> Ah, _that_ alignment.
> Wasn't aware that I need to align to anything.
> But if that's required, sure, I'll be fixing it.

Memory returned by kmalloc is guaranteed to be aligned to
CRYPTO_MINALIGN_ATTR, in fact that's the whole point of that
attribute.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2021-07-19 08:59:10

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
> On 7/18/21 2:56 PM, Stephan Müller wrote:
> > Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:

> > > The key is also used when using the ffdhe algorithm.
> > > Note: I _think_ that I need to use this key for the ffdhe algorithm,
> > > because the implementation I came up with is essentially plain DH with
> > > pre-defined 'p', 'q' and 'g' values. But the DH implementation also
> > > requires a 'key', and for that I'm using this key here.
> > >
> > > It might be that I'm completely off, and don't need to use a key for our
> > > DH implementation. In that case you are correct.
> > > (And that's why I said I'll need a review of the FFDHE implementation).
> > > But for now I'll need the key for FFDHE.
> >
> > Do I understand you correctly that the dhchap_key is used as the input to
> > the
> > DH - i.e. it is the remote public key then? It looks strange that this is
> > used
> > for DH but then it is changed here by hashing it together with something
> > else
> > to form a new dhchap_key. Maybe that is what the protocol says. But it
> > sounds
> > strange to me, especially when you think that dhchap_key would be, say,
> > 2048
> > bits if it is truly the remote public key and then after the hashing it is
> > 256
> > this dhchap_key cannot be used for FFC-DH.
> >
> > Or are you using the dhchap_key for two different purposes?
> >
> > It seems I miss something here.
> >
> No, not entirely. It's me who buggered it up.
> I got carried away by the fact that there is a crypto_dh_encode_key()
> function, and thought I need to use it here.

Thank you for clarifying that. It sounds to me that there is no defined
protocol (or if there, I would be wondering how the code would have worked
with a different implementation). Would it make sense to first specify a
protocol for authentication and have it discussed? I personally think it is a
bit difficult to fully understand the protocol from the code and discuss
protocol-level items based on the code.

Thanks
Stephan

2021-07-19 09:14:17

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication


>>> Implement NVMe-oF In-Band authentication. This patch adds two new
>>> fabric options 'dhchap_key' to specify the PSK
>>
>> pre-shared-key.
>>
>> Also, we need a sysfs knob to rotate the key that will trigger
>> re-authentication or even a simple controller(s-plural) reset, so this
>> should go beyond just the connection string.
>>
>
> Yeah, re-authentication currently is not implemented. I first wanted to
> get this patchset out such that we can settle on the userspace interface
> (both from host and target).

I think this is mandatory from the start (at the very least from the
host side) because credentials get rotated.

The flow imo needs to be that the target allows to update the
credentials even when the subsystem is exposed and connected, but
keep all existing hosts connected such that only new connections will
need the new credentials. (an incremental step, which would be optional
is to allow it for a configurable grace period and then disconnect
existing hosts, which would force new connections with updated
credentials).

From the host side, we need to expose a controller (or more
appropriately a subsystem) sysfs file to override the existing key. That
can initially just trigger a controller reset, and incrementally can do
a graceful re-authentication).

> I'll have to think on how we should handle authentication; one of the
> really interesting cases would be when one malicious admin will _just_
> send a 'negotiate' command to the controller. As per spec the controller
> will be waiting for an 'authentication receive' command to send a
> 'challenge' payload back to the host. But that will never come, so as it
> stands currently the controller is required to abort the connection.
> Not very nice.

Well, at the moment we can keep it open and discuss this in the TWG.

>> P.S. can you add also the nvme-cli code in the next go?
>>
> Oh, sure. It's already sitting around in my local repo (surprise,
> surprise); will be ending it out next time.

Great, thanks.

>>> and 'dhchap_authenticate'
>>> to request bi-directional authentication of both the host and the
>>> controller.
>>
>> bidirectional? not uni-directional?
>>
>
> Yeah, that's a bit of a misnomer. When a PSK is specified, the
> controller will start the authentication protocol such that the
> _controller_ can validate the host. If the host wants to authenticate
> the controller is needs to set this flag.
> Hence bi-directional authentication.
> But I'm the first to admit that this is poor wording for the flag.

It is misleading. But I'll need to go look again because I didn't
see the host authenticating the controller..

>>> Signed-off-by: Hannes Reinecke <[email protected]>
>>> ---
>>>   drivers/nvme/host/Kconfig   |  11 +
>>>   drivers/nvme/host/Makefile  |   1 +
>>>   drivers/nvme/host/auth.c    | 813 ++++++++++++++++++++++++++++++++++++
>>>   drivers/nvme/host/auth.h    |  23 +
>>>   drivers/nvme/host/core.c    |  77 +++-
>>>   drivers/nvme/host/fabrics.c |  65 ++-
>>>   drivers/nvme/host/fabrics.h |   8 +
>>>   drivers/nvme/host/nvme.h    |  15 +
>>>   drivers/nvme/host/trace.c   |  32 ++
>>>   9 files changed, 1041 insertions(+), 4 deletions(-)
>>>   create mode 100644 drivers/nvme/host/auth.c
>>>   create mode 100644 drivers/nvme/host/auth.h
>>>
>>> diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig
>>> index c3f3d77f1aac..853c546305e9 100644
>>> --- a/drivers/nvme/host/Kconfig
>>> +++ b/drivers/nvme/host/Kconfig
>>> @@ -85,3 +85,14 @@ config NVME_TCP
>>>         from https://github.com/linux-nvme/nvme-cli.
>>>         If unsure, say N.
>>> +
>>> +config NVME_AUTH
>>> +    bool "NVM Express over Fabrics In-Band Authentication"
>>> +    depends on NVME_TCP
>>> +    select CRYPTO_SHA256
>>> +    select CRYPTO_SHA512
>>> +    help
>>> +      This provides support for NVMe over Fabrics In-Band
>>> Authentication
>>> +      for the NVMe over TCP transport.
>>
>> In this form, nothing is specific to nvme-tcp here afaict.
>>
>
> Indeed. I guess we can leave out the nvme-tcp reference here.
>
>>> +
>>> +      If unsure, say N.
>>> diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile
>>> index cbc509784b2e..03748a55a12b 100644
>>> --- a/drivers/nvme/host/Makefile
>>> +++ b/drivers/nvme/host/Makefile
>>> @@ -20,6 +20,7 @@ nvme-core-$(CONFIG_NVME_HWMON)        += hwmon.o
>>>   nvme-y                    += pci.o
>>>   nvme-fabrics-y                += fabrics.o
>>> +nvme-fabrics-$(CONFIG_NVME_AUTH)    += auth.o
>>>   nvme-rdma-y                += rdma.o
>>> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
>>> new file mode 100644
>>> index 000000000000..448a3adebea6
>>> --- /dev/null
>>> +++ b/drivers/nvme/host/auth.c
>>> @@ -0,0 +1,813 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux
>>> + */
>>> +
>>> +#include <linux/crc32.h>
>>> +#include <linux/base64.h>
>>> +#include <asm/unaligned.h>
>>> +#include <crypto/hash.h>
>>> +#include <crypto/kpp.h>
>>> +#include "nvme.h"
>>> +#include "fabrics.h"
>>> +#include "auth.h"
>>> +
>>> +static u32 nvme_dhchap_seqnum;
>>> +
>>> +struct nvme_dhchap_context {
>>
>> Maybe nvme_dhchap_queue_context ?
>>
>> I'm thinking that we should perhaps split
>> it to host-wide, subsys-wide and queue specific
>> auth contexts?
>>
>> Let's see...
>>
>
> Interestingly enough, that's what I did for the target side.
> For the host side I found it easier that way, as then we'll have a
> single authentication context which can be deleted after authentication
> finished, and be sure that we removed _all_ information.
> Security and all that.
> Splitting it off would require to remove information on three different
> places, and observing life-time rules for them.
> So more of a chance to mess things up :-)

I understand what you are saying, but this way it will be challanging
to cross check stuff. It is also where things sort of belong...

>>> +    struct crypto_shash *shash_tfm;
>>> +    unsigned char *key;
>>> +    size_t key_len;
>>> +    int qid;
>>> +    u32 s1;
>>> +    u32 s2;
>>> +    u16 transaction;
>>> +    u8 status;
>>> +    u8 hash_id;
>>> +    u8 hash_len;
>>> +    u8 c1[64];
>>> +    u8 c2[64];
>>> +    u8 response[64];
>>> +    u8 *ctrl_key;
>>> +    int ctrl_key_len;
>>> +    u8 *host_key;
>>> +    int host_key_len;
>>> +    u8 *sess_key;
>>> +    int sess_key_len;
>>> +};
>>> +
>>> +struct nvmet_dhchap_hash_map {
>>
>> nvmet?
>>
>
> Yeah; originally I coded that for the target side, and only later moved
> it into the host side to have it usable for both.
> Will be fixing it up.
>
>>> +    int id;
>>> +    int hash_len;
>>> +    const char hmac[15];
>>> +    const char digest[15];
>>> +} hash_map[] = {
>>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>>> +     .hash_len = 32,
>>> +     .hmac = "hmac(sha256)", .digest = "sha256" },
>>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>>> +     .hash_len = 48,
>>> +     .hmac = "hmac(sha384)", .digest = "sha384" },
>>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>>> +     .hash_len = 64,
>>> +     .hmac = "hmac(sha512)", .digest = "sha512" },
>>> +};
>>> +
>>> +const char *nvme_auth_hmac_name(int hmac_id)
>>
>> Should these arrays be static?
>>
>
> Definitely.
>
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>>> +        if (hash_map[i].id == hmac_id)
>>> +            return hash_map[i].hmac;
>>> +    }
>>> +    return NULL;
>>> +}
>>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_name);
>>> +
>>> +const char *nvme_auth_digest_name(int hmac_id)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>>> +        if (hash_map[i].id == hmac_id)
>>> +            return hash_map[i].digest;
>>> +    }
>>> +    return NULL;
>>> +}
>>> +EXPORT_SYMBOL_GPL(nvme_auth_digest_name);
>>> +
>>> +int nvme_auth_hmac_len(int hmac_id)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>>> +        if (hash_map[i].id == hmac_id)
>>> +            return hash_map[i].hash_len;
>>> +    }
>>> +    return -1;
>>> +}
>>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_len);
>>> +
>>> +int nvme_auth_hmac_id(const char *hmac_name)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>>> +        if (!strncmp(hash_map[i].hmac, hmac_name,
>>> +                 strlen(hash_map[i].hmac)))
>>> +            return hash_map[i].id;
>>> +    }
>>> +    return -1;
>>> +}
>>> +EXPORT_SYMBOL_GPL(nvme_auth_hmac_id);
>>> +
>>> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
>>> +                    size_t *dhchap_key_len)
>>> +{
>>> +    unsigned char *dhchap_key;
>>> +    u32 crc;
>>> +    int key_len;
>>> +    size_t allocated_len;
>>> +
>>> +    allocated_len = strlen(dhchap_secret) - 10;
>>
>> the 10 feels like a magic here, should at least note this is the
>> "DHHC-1:..." prefix.
>>
>
> It _is_ magic. And it might even be better to just pass in the string
> _without_ the DHHC-1: prefix.

I don't think that the user should pass in a key in that form at all,
its none of its concern the compliant NVMe representation is.

>
>>> +    dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
>>> +    if (!dhchap_key)
>>> +        return ERR_PTR(-ENOMEM);
>>> +
>>> +    key_len = base64_decode(dhchap_secret + 10,
>>> +                allocated_len, dhchap_key);
>>> +    if (key_len != 36 && key_len != 52 &&
>>> +        key_len != 68) {
>>> +        pr_debug("Invalid DH-HMAC-CHAP key len %d\n",
>>> +             key_len);
>>> +        kfree(dhchap_key);
>>> +        return ERR_PTR(-EINVAL);
>>> +    }
>>> +    pr_debug("DH-HMAC-CHAP Key: %*ph\n",
>>> +         (int)key_len, dhchap_key);
>>
>> One can argue if even printing this is problematic..
>>
>
> Debugging scaffolding. You wouldn't believe how many things can go wrong...
>
> And yes, that should be removed.

Cool.

>>> +
>>> +    /* The last four bytes is the CRC in little-endian format */
>>> +    key_len -= 4;
>>> +    /*
>>> +     * The linux implementation doesn't do pre- and post-increments,
>>> +     * so we have to do it manually.
>>> +     */
>>> +    crc = ~crc32(~0, dhchap_key, key_len);
>>> +
>>> +    if (get_unaligned_le32(dhchap_key + key_len) != crc) {
>>> +        pr_debug("DH-HMAC-CHAP crc mismatch (key %08x, crc %08x)\n",
>>> +               get_unaligned_le32(dhchap_key + key_len), crc);
>>> +        kfree(dhchap_key);
>>> +        return ERR_PTR(-EKEYREJECTED);
>>> +    }
>>> +    *dhchap_key_len = key_len;
>>> +    return dhchap_key;
>>> +}
>>> +EXPORT_SYMBOL_GPL(nvme_auth_extract_secret);
>>> +
>>> +static int nvme_auth_send(struct nvme_ctrl *ctrl, int qid,
>>> +              void *data, size_t tl)
>>> +{
>>> +    struct nvme_command cmd = {};
>>> +    blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
>>> +        0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
>>> +    struct request_queue *q = qid == NVME_QID_ANY ?
>>> +        ctrl->fabrics_q : ctrl->connect_q;
>>> +    int ret;
>>> +
>>> +    cmd.auth_send.opcode = nvme_fabrics_command;
>>> +    cmd.auth_send.fctype = nvme_fabrics_type_auth_send;
>>> +    cmd.auth_send.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
>>> +    cmd.auth_send.spsp0 = 0x01;
>>> +    cmd.auth_send.spsp1 = 0x01;
>>> +    cmd.auth_send.tl = tl;
>>> +
>>> +    ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, tl, 0, qid,
>>> +                     0, flags);
>>> +    if (ret)
>>> +        dev_dbg(ctrl->device,
>>> +            "%s: qid %d error %d\n", __func__, qid, ret);
>>
>> Maybe a little more informative print rather than __func__ ?
>>
>
> Yes, can do.
>
>>> +    return ret;
>>> +}
>>> +
>>> +static int nvme_auth_receive(struct nvme_ctrl *ctrl, int qid,
>>> +                 void *buf, size_t al,
>>> +                 u16 transaction, u8 expected_msg )
>>> +{
>>> +    struct nvme_command cmd = {};
>>> +    struct nvmf_auth_dhchap_failure_data *data = buf;
>>> +    blk_mq_req_flags_t flags = qid == NVME_QID_ANY ?
>>> +        0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED;
>>> +    struct request_queue *q = qid == NVME_QID_ANY ?
>>> +        ctrl->fabrics_q : ctrl->connect_q;
>>> +    int ret;
>>> +
>>> +    cmd.auth_receive.opcode = nvme_fabrics_command;
>>> +    cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive;
>>> +    cmd.auth_receive.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER;
>>> +    cmd.auth_receive.spsp0 = 0x01;
>>> +    cmd.auth_receive.spsp1 = 0x01;
>>> +    cmd.auth_receive.al = al;
>>> +
>>> +    ret = __nvme_submit_sync_cmd(q, &cmd, NULL, buf, al, 0, qid,
>>> +                     0, flags);
>>> +    if (ret > 0) {
>>> +        dev_dbg(ctrl->device, "%s: qid %d nvme status %x\n",
>>> +            __func__, qid, ret);
>>> +        ret = -EIO;
>>> +    }
>>> +    if (ret < 0) {
>>> +        dev_dbg(ctrl->device, "%s: qid %d error %d\n",
>>> +            __func__, qid, ret);
>>> +        return ret;
>>> +    }
>>> +    dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n",
>>> +        __func__, qid, data->auth_type, data->auth_id);
>>> +    if (data->auth_type == NVME_AUTH_COMMON_MESSAGES &&
>>> +        data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
>>> +        return data->reason_code_explanation;
>>> +    }
>>> +    if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES ||
>>> +        data->auth_id != expected_msg) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d invalid message %02x/%02x\n",
>>> +             qid, data->auth_type, data->auth_id);
>>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>>> +    }
>>> +    if (le16_to_cpu(data->t_id) != transaction) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d invalid transaction ID %d\n",
>>> +             qid, le16_to_cpu(data->t_id));
>>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
>>> +                      struct nvme_dhchap_context *chap,
>>> +                      void *buf, size_t buf_size)
>>
>> Maybe nvme_auth_set_dhchap_negotiate_data ?
>>
>
> These are the individual steps in the state machine later on, so I
> wanted to keep the names identical.
> But I'm open to suggestions.
>
>>> +{
>>> +    struct nvmf_auth_dhchap_negotiate_data *data = buf;
>>> +    size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol);
>>> +
>>> +    if (buf_size < size)
>>> +        return -EINVAL;
>>> +
>>> +    memset((u8 *)buf, 0, size);
>>> +    data->auth_type = NVME_AUTH_COMMON_MESSAGES;
>>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
>>> +    data->t_id = cpu_to_le16(chap->transaction);
>>> +    data->sc_c = 0; /* No secure channel concatenation */
>>> +    data->napd = 1;
>>> +    data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
>>> +    data->auth_protocol[0].dhchap.halen = 3;
>>> +    data->auth_protocol[0].dhchap.dhlen = 1;
>>> +    data->auth_protocol[0].dhchap.idlist[0] =
>>> NVME_AUTH_DHCHAP_HASH_SHA256;
>>> +    data->auth_protocol[0].dhchap.idlist[1] =
>>> NVME_AUTH_DHCHAP_HASH_SHA384;
>>> +    data->auth_protocol[0].dhchap.idlist[2] =
>>> NVME_AUTH_DHCHAP_HASH_SHA512;
>>> +    data->auth_protocol[0].dhchap.idlist[3] =
>>> NVME_AUTH_DHCHAP_DHGROUP_NULL;
>> You should comment that this routine expects buf to have enough
>> room for both negotiate and auth_proto structures.
>>
> Hmm. I do a check for the overall size at the start, so I'm not sure
> what this will buy us.
> And actually, anyone wanting to make sense of the implementation would
> need to look at the spec anyway.

Unrelated to the spec, just makes the code more readable. There is
an assumption on the buffer size passed, so it would be nice to
document it in the code (so it is less likely to break in the future
in the presence of assumptions).

>>> +
>>> +    return size;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
>>> +                      struct nvme_dhchap_context *chap,
>>> +                      void *buf, size_t buf_size)
>>
>> Maybe nvme_auth_process_dhchap_challange ?
>>
>
> See above. I'd rather have consistent names for the state machine.
> But I can change them to 'nvme_process_chchap_<statename>'
>
>>> +{
>>> +    struct nvmf_auth_dhchap_challenge_data *data = buf;
>>> +    size_t size = sizeof(*data) + data->hl + data->dhvlen;
>>> +    const char *gid_name;
>>> +
>>> +    if (buf_size < size) {
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>>> +        return -ENOMSG;
>>> +    }
>>> +
>>> +    if (data->hashid != NVME_AUTH_DHCHAP_HASH_SHA256 &&
>>> +        data->hashid != NVME_AUTH_DHCHAP_HASH_SHA384 &&
>>> +        data->hashid != NVME_AUTH_DHCHAP_HASH_SHA512) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d: DH-HMAC-CHAP: invalid HASH ID %d\n",
>>> +             chap->qid, data->hashid);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>>> +        return -EPROTO;
>>> +    }
>>> +    switch (data->dhgid) {
>>> +    case NVME_AUTH_DHCHAP_DHGROUP_NULL:
>>> +        gid_name = "null";
>>> +        break;
>>> +    default:
>>> +        gid_name = NULL;
>>> +        break;
>>> +    }
>>> +    if (!gid_name) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
>>> +             chap->qid, data->dhgid);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>>> +        return -EPROTO;
>>> +    }
>>
>> Maybe some spaces between condition blocks?
>>
>
> Ok.
>
>>> +    if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>>> +        return -EPROTO;
>>> +    }
>>> +    if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen
>>> != 0) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
>>> +            chap->qid);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>>> +        return -EPROTO;
>>> +    }
>>> +    dev_dbg(ctrl->device, "%s: qid %d requested hash id %d\n",
>>> +        __func__, chap->qid, data->hashid);
>>> +    if (nvme_auth_hmac_len(data->hashid) != data->hl) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d: DH-HMAC-CHAP: invalid hash length\n",
>>> +            chap->qid);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>>> +        return -EPROTO;
>>> +    }
>>> +    chap->hash_id = data->hashid;
>>> +    chap->hash_len = data->hl;
>>> +    chap->s1 = le32_to_cpu(data->seqnum);
>>> +    memcpy(chap->c1, data->cval, chap->hash_len);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
>>> +                  struct nvme_dhchap_context *chap,
>>> +                  void *buf, size_t buf_size)
>>
>> nvme_auth_set_dhchap_reply
>>
>
> Ah. Now I see what you're getting at.
> Okay, will be changing it.
>
>>> +{
>>> +    struct nvmf_auth_dhchap_reply_data *data = buf;
>>> +    size_t size = sizeof(*data);
>>> +
>>> +    size += 2 * chap->hash_len;
>>> +    if (ctrl->opts->dhchap_auth) {
>>
>> The ctrl opts is not clear to me. what is dhchap_auth
>> mean?
>>
> As stated above, this is for bi-directional authentication.
> And yes, it is poor wording.
>
> 'dhchap_bidirectional' ?

dhchap_auth_ctrl maybe.

>> Also shouldn't these params be lifted to the subsys?
>>
>
> I kinda like to have it all encapsulated in a common per-queue
> structure; on the host side this one isn't even attached to anything, so
> any new authentication attempt will allocate a new one, with no chance
> of accidentally re-using existing values.
> I thought this to be a rather nice property for a state-machine.

I understand, but different controllers for a single subsystem
should not behave differently. Meaning for one you authenticate
and the other you don't (or with different keys).

>
>>> +        get_random_bytes(chap->c2, chap->hash_len);
>>> +        chap->s2 = nvme_dhchap_seqnum++;
>>> +    } else
>>> +        memset(chap->c2, 0, chap->hash_len);
>>> +
>>> +    if (chap->host_key_len)
>>> +        size += chap->host_key_len;
>>> +
>>> +    if (buf_size < size)
>>> +        return -EINVAL;
>>> +
>>> +    memset(buf, 0, size);
>>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
>>> +    data->t_id = cpu_to_le16(chap->transaction);
>>> +    data->hl = chap->hash_len;
>>> +    data->dhvlen = chap->host_key_len;
>>> +    data->seqnum = cpu_to_le32(chap->s2);
>>> +    memcpy(data->rval, chap->response, chap->hash_len);
>>> +    if (ctrl->opts->dhchap_auth) {
>>> +        dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n",
>>> +            __func__, chap->qid,
>>> +            chap->hash_len, chap->c2);
>>> +        data->cvalid = 1;
>>> +        memcpy(data->rval + chap->hash_len, chap->c2,
>>> +               chap->hash_len);
>>> +    }
>>> +    if (chap->host_key_len)
>>> +        memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
>>> +               chap->host_key_len);
>>> +
>>> +    return size;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_success1(struct nvme_ctrl *ctrl,
>>> +                     struct nvme_dhchap_context *chap,
>>> +                     void *buf, size_t buf_size)
>>
>> nvme_auth_process_dhchap_success1
>>
>
> OK.
>
>>> +{
>>> +    struct nvmf_auth_dhchap_success1_data *data = buf;
>>> +    size_t size = sizeof(*data);
>>> +
>>> +    if (ctrl->opts->dhchap_auth)
>>> +        size += chap->hash_len;
>>> +
>>> +
>>> +    if (buf_size < size) {
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>>> +        return -ENOMSG;
>>> +    }
>>> +
>>> +    if (data->hl != chap->hash_len) {
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d: DH-HMAC-CHAP: invalid hash length %d\n",
>>> +             chap->qid, data->hl);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>>> +        return -EPROTO;
>>> +    }
>>> +
>>> +    if (!data->rvalid)
>>> +        return 0;
>>> +
>>> +    /* Validate controller response */
>>> +    if (memcmp(chap->response, data->rval, data->hl)) {
>>> +        dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
>>> +            __func__, chap->qid, chap->hash_len, data->rval);
>>> +        dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
>>> +            __func__, chap->qid, chap->hash_len, chap->response);
>>> +        dev_warn(ctrl->device,
>>> +             "qid %d: DH-HMAC-CHAP: controller authentication
>>> failed\n",
>>> +             chap->qid);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>>> +        return -EPROTO;
>>> +    }
>>> +    dev_info(ctrl->device,
>>> +         "qid %d: DH-HMAC-CHAP: controller authenticated\n",
>>> +        chap->qid);
>>> +    return 0;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
>>> +                     struct nvme_dhchap_context *chap,
>>> +                     void *buf, size_t buf_size)
>>
>> same
>>
>>> +{
>>> +    struct nvmf_auth_dhchap_success2_data *data = buf;
>>> +    size_t size = sizeof(*data);
>>> +
>>> +    memset(buf, 0, size);
>>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
>>> +    data->t_id = cpu_to_le16(chap->transaction);
>>> +
>>> +    return size;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
>>> +                     struct nvme_dhchap_context *chap,
>>> +                     void *buf, size_t buf_size)
>>
>> same
>>
>>> +{
>>> +    struct nvmf_auth_dhchap_failure_data *data = buf;
>>> +    size_t size = sizeof(*data);
>>> +
>>> +    memset(buf, 0, size);
>>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>>> +    data->t_id = cpu_to_le16(chap->transaction);
>>> +    data->reason_code = 1;
>>> +    data->reason_code_explanation = chap->status;
>>> +
>>> +    return size;
>>> +}
>>> +
>>> +int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>>> +              struct nvme_dhchap_context *chap)
>>
>> Maybe _select_hf (hash function)? not a must, just sticks
>> to the spec language.
>>
>
> Hmm. Will be checking.
>
>>> +{
>>> +    char *hash_name;
>>> +    int ret;
>>> +
>>> +    switch (chap->hash_id) {
>>> +    case NVME_AUTH_DHCHAP_HASH_SHA256:
>>> +        hash_name = "hmac(sha256)";
>>> +        break;
>>> +    case NVME_AUTH_DHCHAP_HASH_SHA384:
>>> +        hash_name = "hmac(sha384)";
>>> +        break;
>>> +    case NVME_AUTH_DHCHAP_HASH_SHA512:
>>> +        hash_name = "hmac(sha512)";
>>> +        break;
>>> +    default:
>>> +        hash_name = NULL;
>>> +        break;
>>> +    }
>>> +    if (!hash_name) {
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>>> +        return -EPROTO;
>>> +    }
>>> +    chap->shash_tfm = crypto_alloc_shash(hash_name, 0,
>>> +                         CRYPTO_ALG_ALLOCATES_MEMORY);
>>> +    if (IS_ERR(chap->shash_tfm)) {
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>>> +        chap->shash_tfm = NULL;
>>> +        return -EPROTO;
>>> +    }
>>> +    if (!chap->key) {
>>> +        dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
>>> +             chap->qid);
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>>> +        crypto_free_shash(chap->shash_tfm);
>>
>> Wouldn't it better to check this before allocating the tfm?
>>
>
> Indeed. Will be changing it.
>
>>> +        chap->shash_tfm = NULL;
>>> +        return -EINVAL;
>>> +    }
>>> +    ret = crypto_shash_setkey(chap->shash_tfm, chap->key,
>>> chap->key_len);
>>> +    if (ret) {
>>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>>> +        crypto_free_shash(chap->shash_tfm);
>>> +        chap->shash_tfm = NULL;
>>> +        return ret;
>>> +    }
>>> +    dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>>> +         chap->qid, hash_name);
>>> +    return 0;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
>>> +                      struct nvme_dhchap_context *chap)
>>> +{
>>> +    SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>>> +    u8 buf[4], *challenge = chap->c1;
>>> +    int ret;
>>> +
>>> +    dev_dbg(ctrl->device, "%s: qid %d host response seq %d
>>> transaction %d\n",
>>> +        __func__, chap->qid, chap->s1, chap->transaction);
>>> +    shash->tfm = chap->shash_tfm;
>>> +    ret = crypto_shash_init(shash);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, challenge, chap->hash_len);
>>> +    if (ret)
>>> +        goto out;
>>> +    put_unaligned_le32(chap->s1, buf);
>>> +    ret = crypto_shash_update(shash, buf, 4);
>>> +    if (ret)
>>> +        goto out;
>>> +    put_unaligned_le16(chap->transaction, buf);
>>> +    ret = crypto_shash_update(shash, buf, 2);
>>> +    if (ret)
>>> +        goto out;
>>> +    memset(buf, 0, sizeof(buf));
>>> +    ret = crypto_shash_update(shash, buf, 1);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, "HostHost", 8);
>>
>> HostHost ? Can you refer me to the specific section
>> that talks about this?
>>
>
> NVMe 2.0 section DH-HMAC-CHAP_Reply Message, paragraph Response Value.
> HostHost.
>
>> Would be good to have a comment on the format fed to the
>> shash.
>>
>
> Yes, will be doing so.
>
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>>> +                  strlen(ctrl->opts->host->nqn));
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, buf, 1);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>>> +                strlen(ctrl->opts->subsysnqn));
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_final(shash, chap->response);
>>> +out:
>>> +    return ret;
>>> +}
>>> +
>>> +static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
>>> +                      struct nvme_dhchap_context *chap)
>>> +{
>>> +    SHASH_DESC_ON_STACK(shash, chap->shash_tfm);
>>> +    u8 buf[4], *challenge = chap->c2;
>>> +    int ret;
>>> +
>>> +    dev_dbg(ctrl->device, "%s: qid %d host response seq %d
>>> transaction %d\n",
>>> +        __func__, chap->qid, chap->s2, chap->transaction);
>>> +    dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
>>> +        __func__, chap->qid, chap->hash_len, challenge);
>>> +    dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n",
>>> +        __func__, chap->qid, ctrl->opts->subsysnqn);
>>> +    dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n",
>>> +        __func__, chap->qid, ctrl->opts->host->nqn);
>>> +    shash->tfm = chap->shash_tfm;
>>> +    ret = crypto_shash_init(shash);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, challenge, chap->hash_len);
>>> +    if (ret)
>>> +        goto out;
>>> +    put_unaligned_le32(chap->s2, buf);
>>> +    ret = crypto_shash_update(shash, buf, 4);
>>> +    if (ret)
>>> +        goto out;
>>> +    put_unaligned_le16(chap->transaction, buf);
>>> +    ret = crypto_shash_update(shash, buf, 2);
>>> +    if (ret)
>>> +        goto out;
>>> +    memset(buf, 0, 4);
>>> +    ret = crypto_shash_update(shash, buf, 1);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, "Controller", 10);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, ctrl->opts->subsysnqn,
>>> +                  strlen(ctrl->opts->subsysnqn));
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, buf, 1);
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_update(shash, ctrl->opts->host->nqn,
>>> +                  strlen(ctrl->opts->host->nqn));
>>> +    if (ret)
>>> +        goto out;
>>> +    ret = crypto_shash_final(shash, chap->response);
>>> +out:
>>> +    return ret;
>>> +}
>>> +
>>> +int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
>>> +               struct nvme_dhchap_context *chap)
>>> +{
>>> +    int ret;
>>> +    u8 key_hash;
>>> +    const char *hmac_name;
>>> +    struct crypto_shash *key_tfm;
>>> +
>>> +    if (sscanf(ctrl->opts->dhchap_secret, "DHHC-1:%hhd:%*s:",
>>> +           &key_hash) != 1)
>>> +        return -EINVAL;
>>
>> I'd expect that the user will pass in a secret key (as binary)
>> the the driver will build the spec compliant formatted string no?
>>  > Am I not reading this correctly?
>>
>
> I'm under the impression that this is the format into which the
> User/Admin will get hold of the secret key.
> Spec says:
>
> '... all NVMe over Fabrics entities shall support the following ASCII
> representation of secrets ...'
>
> And as the userspace interface is the only way how the user/admin
> _could_ interact with the NVMe over Fabrics entities in Linux I guess
> we'll need to be able to parse it.

Right... But who is responsible for crcing and encoding it? nvme-cli?

> We sure could allow a binary secret, too, but then what would be the
> point in converting it into the secret representation?
> The protocol revolves around the binary secret, not the transport
> representation.

I am not sure I understand who is responsible for represnting the key
this way in Linux?

>>> +
>>> +    chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
>>> +                         &chap->key_len);
>>> +    if (IS_ERR(chap->key)) {
>>> +        ret = PTR_ERR(chap->key);
>>> +        chap->key = NULL;
>>> +        return ret;
>>> +    }
>>> +
>>> +    if (key_hash == 0)
>>> +        return 0;
>>> +
>>> +    hmac_name = nvme_auth_hmac_name(key_hash);
>>> +    if (!hmac_name) {
>>> +        pr_debug("Invalid key hash id %d\n", key_hash);
>>> +        return -EKEYREJECTED;
>>> +    }
>>
>> Why does the user influence the hmac used? isn't that is driven
>> by the susbsystem?
>>
>> I don't think that the user should choose in this level.
>>
>
> That is another weirdness of the spec.
> The _secret_ will be hashed with a specific function, and that function
> is stated in the transport representation.
> (Cf section "DH-HMAC-CHAP Security Requirements").
> This is _not_ the hash function used by the authentication itself, which
> will be selected by the protocol.

Yes, I see it now, and it is indeed confusing.

> So it's not the user here, but rather the transport specification of the
> key which selects the hash algorithm.

What do you mean by the transport specification?

>>> +
>>> +    key_tfm = crypto_alloc_shash(hmac_name, 0, 0);
>>> +    if (IS_ERR(key_tfm)) {
>>> +        kfree(chap->key);
>>> +        chap->key = NULL;
>>> +        ret = PTR_ERR(key_tfm);
>>
>> You set ret and later return 0? I think that the success
>> path in the else clause is hard to read and error prone...
>>
>
> Do I? Will need to fix it up.
>
>>> +    } else {
>>> +        SHASH_DESC_ON_STACK(shash, key_tfm);
>>> +
>>> +        shash->tfm = key_tfm;
>>> +        ret = crypto_shash_setkey(key_tfm, chap->key,
>>> +                      chap->key_len);
>>> +        if (ret < 0) {
>>> +            crypto_free_shash(key_tfm);
>>> +            kfree(chap->key);
>>> +            chap->key = NULL;
>>> +            return ret;
>>> +        }
>>> +        crypto_shash_init(shash);
>>> +        crypto_shash_update(shash, ctrl->opts->host->nqn,
>>> +                    strlen(ctrl->opts->host->nqn));
>>> +        crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
>>> +        crypto_shash_final(shash, chap->key);
>>> +        crypto_free_shash(key_tfm);
>>
>> Shouldn't these be done when preparing the dh-hmac-chap reply?
>>
>
> By setting the hash here I avoid having to pass the required hash
> function for the secret transformation.
> I could be doing the entire secret transformation thingie when preparing
> the reply; reason why I did it here is that _having_ a secret is the
> precondition to everything else, so I wanted to check upfront for that.
> But I'll check what would happen if I move it.

Now that I understand that this is not the authentication transformation
its ok I guess. Please add a comment in the code so its clearer.

>>> +    }
>>> +    return 0;
>>> +}
>>> +
>>> +void nvme_auth_free(struct nvme_dhchap_context *chap)
>>> +{
>>> +    if (chap->shash_tfm)
>>> +        crypto_free_shash(chap->shash_tfm);
>>> +    if (chap->key)
>>> +        kfree(chap->key);
>>> +    if (chap->ctrl_key)
>>> +        kfree(chap->ctrl_key);
>>> +    if (chap->host_key)
>>> +        kfree(chap->host_key);
>>> +    if (chap->sess_key)
>>> +        kfree(chap->sess_key);
>>
>> No need to check null for kfree...
>>
>
> Will be fixing it up.
>
>>> +    kfree(chap);
>>> +}
>>> +
>>> +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
>>> +{
>>> +    struct nvme_dhchap_context *chap;
>>> +    void *buf;
>>> +    size_t buf_size, tl;
>>> +    int ret = 0;
>>> +
>>> +    chap = kzalloc(sizeof(*chap), GFP_KERNEL);
>>> +    if (!chap)
>>> +        return -ENOMEM;
>>> +    chap->qid = qid;
>>> +    chap->transaction = ctrl->transaction++;
>>> +
>>> +    ret = nvme_auth_generate_key(ctrl, chap);
>>> +    if (ret) {
>>> +        dev_dbg(ctrl->device, "%s: failed to generate key, error %d\n",
>>> +            __func__, ret);
>>> +        nvme_auth_free(chap);
>>> +        return ret;
>>> +    }
>>> +
>>> +    /*
>>> +     * Allocate a large enough buffer for the entire negotiation:
>>> +     * 4k should be enough to ffdhe8192.
>>> +     */
>>> +    buf_size = 4096;
>>> +    buf = kzalloc(buf_size, GFP_KERNEL);
>>> +    if (!buf) {
>>> +        ret = -ENOMEM;
>>> +        goto out;
>>> +    }
>>> +
>>> +    /* DH-HMAC-CHAP Step 1: send negotiate */
>>
>> I'd consider breaking these into sub-routines.
>>
>
> Which ones? The preparation step?

I'm thinking:
1. nvme_auth_initiate_negotiation
- nvme_auth_set_dhchap_negotiate_data
- nvme_auth_send
2. nvme_auth_do_challange
- nvme_auth_receive
- nvme_auth_process_dhchap_challange
- nvme_auth_select_hash
- nvme_auth_dhchap_host_response
- nvme_auth_set_dhchap_reply
- nvme_auth_send
- nvme_auth_receive
- nvme_auth_process_dhchap_success1
3. if (ctrl->opts->dhchap_auth_ctrl)
- nvme_auth_dhchap_authenticate_ctrl
(e.g. nvme_auth_dhchap_ctrl_response)
4. nvme_auth_acknowledge_transaction
- nvme_auth_set_dhchap_success2
- nvme_auth_send

if steps 1,2,3 failed, goto target will have a func:
5. nvme_auth_fail_transaction
- nvme_auth_set_dhchap_failure2
- nvme_auth_send

> Sure, can do.
>
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP negotiate\n",
>>> +        __func__, qid);
>>> +    ret = nvme_auth_dhchap_negotiate(ctrl, chap, buf, buf_size);
>>> +    if (ret < 0)
>>> +        goto out;
>>> +    tl = ret;
>>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>>> +    if (ret)
>>> +        goto out;
>>> +
>>> +    memset(buf, 0, buf_size);
>>> +    ret = nvme_auth_receive(ctrl, qid, buf, buf_size,
>>> chap->transaction,
>>> +                NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE);
>>> +    if (ret < 0) {
>>> +        dev_dbg(ctrl->device,
>>> +            "%s: qid %d DH-HMAC-CHAP failed to receive challenge\n",
>>> +            __func__, qid);
>>> +        goto out;
>>> +    }
>>> +    if (ret > 0) {
>>> +        chap->status = ret;
>>> +        goto fail1;
>>> +    }
>>> +
>>> +    /* DH-HMAC-CHAP Step 2: receive challenge */
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP challenge\n",
>>> +        __func__, qid);
>>> +
>>> +    ret = nvme_auth_dhchap_challenge(ctrl, chap, buf, buf_size);
>>> +    if (ret) {
>>> +        /* Invalid parameters for negotiate */
>>> +        goto fail2;
>>> +    }
>>> +
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP select hash\n",
>>> +        __func__, qid);
>>> +    ret = nvme_auth_select_hash(ctrl, chap);
>>> +    if (ret)
>>> +        goto fail2;
>>> +
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
>>> +        __func__, qid);
>>> +    ret = nvme_auth_dhchap_host_response(ctrl, chap);
>>> +    if (ret)
>>> +        goto fail2;
>>> +
>>> +    /* DH-HMAC-CHAP Step 3: send reply */
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP reply\n",
>>> +        __func__, qid);
>>> +    ret = nvme_auth_dhchap_reply(ctrl, chap, buf, buf_size);
>>> +    if (ret < 0)
>>> +        goto fail2;
>>> +
>>> +    tl = ret;
>>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>>> +    if (ret)
>>> +        goto fail2;
>>> +
>>> +    memset(buf, 0, buf_size);
>>> +    ret = nvme_auth_receive(ctrl, qid, buf, buf_size,
>>> chap->transaction,
>>> +                NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1);
>>> +    if (ret < 0) {
>>> +        dev_dbg(ctrl->device,
>>> +            "%s: qid %d DH-HMAC-CHAP failed to receive success1\n",
>>> +            __func__, qid);
>>> +        goto out;
>>> +    }
>>> +    if (ret > 0) {
>>> +        chap->status = ret;
>>> +        goto fail1;
>>> +    }
>>> +
>>> +    if (ctrl->opts->dhchap_auth) {
>>> +        dev_dbg(ctrl->device,
>>> +            "%s: qid %d DH-HMAC-CHAP controller response\n",
>>> +            __func__, qid);
>>> +        ret = nvme_auth_dhchap_ctrl_response(ctrl, chap);
>>> +        if (ret)
>>> +            goto fail2;
>>> +    }
>>> +
>>> +    /* DH-HMAC-CHAP Step 4: receive success1 */
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success1\n",
>>> +        __func__, qid);
>>> +    ret = nvme_auth_dhchap_success1(ctrl, chap, buf, buf_size);
>>> +    if (ret < 0) {
>>> +        /* Controller authentication failed */
>>> +        goto fail2;
>>> +    }
>>> +    tl = ret;
>>> +    /* DH-HMAC-CHAP Step 5: send success2 */
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP success2\n",
>>> +        __func__, qid);
>>> +    tl = nvme_auth_dhchap_success2(ctrl, chap, buf, buf_size);
>>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>>> +    if (!ret)
>>> +        goto out;
>>> +
>>> +fail1:
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure1, status
>>> %x\n",
>>> +        __func__, qid, chap->status);
>>> +    goto out;
>>> +
>>> +fail2:
>>> +    dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP failure2, status
>>> %x\n",
>>> +        __func__, qid, chap->status);
>>> +    tl = nvme_auth_dhchap_failure2(ctrl, chap, buf, buf_size);
>>> +    ret = nvme_auth_send(ctrl, qid, buf, tl);
>>> +
>>> +out:
>>> +    if (!ret && chap->status)
>>> +        ret = -EPROTO;
>>> +    if (!ret) {
>>> +        ctrl->dhchap_hash = chap->hash_id;
>>> +    }
>>> +    kfree(buf);
>>> +    nvme_auth_free(chap);
>>> +    return ret;
>>> +}
>>> diff --git a/drivers/nvme/host/auth.h b/drivers/nvme/host/auth.h
>>> new file mode 100644
>>> index 000000000000..4950b1cb9470
>>> --- /dev/null
>>> +++ b/drivers/nvme/host/auth.h
>>> @@ -0,0 +1,23 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/*
>>> + * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions
>>> + */
>>> +
>>> +#ifndef _NVME_AUTH_H
>>> +#define _NVME_AUTH_H
>>> +
>>> +const char *nvme_auth_dhgroup_name(int dhgroup_id);
>>> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id);
>>> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id);
>>> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id);
>>> +int nvme_auth_dhgroup_id(const char *dhgroup_name);
>>> +
>>> +const char *nvme_auth_hmac_name(int hmac_id);
>>> +const char *nvme_auth_digest_name(int hmac_id);
>>> +int nvme_auth_hmac_id(const char *hmac_name);
>>> +int nvme_auth_hmac_len(int hmac_len);
>>> +
>>> +unsigned char *nvme_auth_extract_secret(unsigned char *dhchap_secret,
>>> +                    size_t *dhchap_key_len);
>>> +
>>> +#endif /* _NVME_AUTH_H */
>>> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
>>> index 11779be42186..7ce9b666dc09 100644
>>> --- a/drivers/nvme/host/core.c
>>> +++ b/drivers/nvme/host/core.c
>>> @@ -708,7 +708,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl,
>>> struct request *rq,
>>>           switch (ctrl->state) {
>>>           case NVME_CTRL_CONNECTING:
>>>               if (blk_rq_is_passthrough(rq) &&
>>> nvme_is_fabrics(req->cmd) &&
>>> -                req->cmd->fabrics.fctype == nvme_fabrics_type_connect)
>>> +                (req->cmd->fabrics.fctype ==
>>> nvme_fabrics_type_connect ||
>>> +                 req->cmd->fabrics.fctype ==
>>> nvme_fabrics_type_auth_send ||
>>> +                 req->cmd->fabrics.fctype ==
>>> nvme_fabrics_type_auth_receive))
>>>                   return true;
>>>               break;
>>>           default:
>>> @@ -3426,6 +3428,66 @@ static ssize_t
>>> nvme_ctrl_fast_io_fail_tmo_store(struct device *dev,
>>>   static DEVICE_ATTR(fast_io_fail_tmo, S_IRUGO | S_IWUSR,
>>>       nvme_ctrl_fast_io_fail_tmo_show,
>>> nvme_ctrl_fast_io_fail_tmo_store);
>>> +#ifdef CONFIG_NVME_AUTH
>>> +struct nvmet_dhchap_hash_map {
>>> +    int id;
>>> +    const char name[15];
>>> +} hash_map[] = {
>>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>>> +     .name = "hmac(sha256)", },
>>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>>> +     .name = "hmac(sha384)", },
>>> +    {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>>> +     .name = "hmac(sha512)", },
>>> +};
>>> +
>>> +static ssize_t dhchap_hash_show(struct device *dev,
>>> +    struct device_attribute *attr, char *buf)
>>> +{
>>> +    struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(hash_map); i++) {
>>> +        if (hash_map[i].id == ctrl->dhchap_hash)
>>> +            return sprintf(buf, "%s\n", hash_map[i].name);
>>> +    }
>>> +    return sprintf(buf, "none\n");
>>> +}
>>> +DEVICE_ATTR_RO(dhchap_hash);
>>> +
>>> +struct nvmet_dhchap_group_map {
>>> +    int id;
>>> +    const char name[15];
>>> +} dhgroup_map[] = {
>>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
>>> +     .name = "NULL", },
>>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_2048,
>>> +     .name = "ffdhe2048", },
>>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_3072,
>>> +     .name = "ffdhe3072", },
>>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_4096,
>>> +     .name = "ffdhe4096", },
>>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_6144,
>>> +     .name = "ffdhe6144", },
>>> +    {.id = NVME_AUTH_DHCHAP_DHGROUP_8192,
>>> +     .name = "ffdhe8192", },
>>> +};
>>> +
>>> +static ssize_t dhchap_dhgroup_show(struct device *dev,
>>> +    struct device_attribute *attr, char *buf)
>>> +{
>>> +    struct nvme_ctrl *ctrl = dev_get_drvdata(dev);
>>> +    int i;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
>>> +        if (hash_map[i].id == ctrl->dhchap_dhgroup)
>>> +            return sprintf(buf, "%s\n", dhgroup_map[i].name);
>>> +    }
>>> +    return sprintf(buf, "none\n");
>>> +}
>>> +DEVICE_ATTR_RO(dhchap_dhgroup);
>>> +#endif
>>> +
>>>   static struct attribute *nvme_dev_attrs[] = {
>>>       &dev_attr_reset_controller.attr,
>>>       &dev_attr_rescan_controller.attr,
>>> @@ -3447,6 +3509,10 @@ static struct attribute *nvme_dev_attrs[] = {
>>>       &dev_attr_reconnect_delay.attr,
>>>       &dev_attr_fast_io_fail_tmo.attr,
>>>       &dev_attr_kato.attr,
>>> +#ifdef CONFIG_NVME_AUTH
>>> +    &dev_attr_dhchap_hash.attr,
>>> +    &dev_attr_dhchap_dhgroup.attr,
>>> +#endif
>>>       NULL
>>>   };
>>> @@ -3470,6 +3536,10 @@ static umode_t
>>> nvme_dev_attrs_are_visible(struct kobject *kobj,
>>>           return 0;
>>>       if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts)
>>>           return 0;
>>> +#ifdef CONFIG_NVME_AUTH
>>> +    if (a == &dev_attr_dhchap_hash.attr && !ctrl->opts)
>>> +        return 0;
>>> +#endif
>>>       return a->mode;
>>>   }
>>> @@ -4581,6 +4651,11 @@ static inline void _nvme_check_size(void)
>>>       BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512);
>>>       BUILD_BUG_ON(sizeof(struct nvme_dbbuf) != 64);
>>>       BUILD_BUG_ON(sizeof(struct nvme_directive_cmd) != 64);
>>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8);
>>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16);
>>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16);
>>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16);
>>> +    BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16);
>>>   }
>>> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
>>> index a5469fd9d4c3..6404ab9b604b 100644
>>> --- a/drivers/nvme/host/fabrics.c
>>> +++ b/drivers/nvme/host/fabrics.c
>>> @@ -366,6 +366,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl)
>>>       union nvme_result res;
>>>       struct nvmf_connect_data *data;
>>>       int ret;
>>> +    u32 result;
>>>       cmd.connect.opcode = nvme_fabrics_command;
>>>       cmd.connect.fctype = nvme_fabrics_type_connect;
>>> @@ -398,8 +399,18 @@ int nvmf_connect_admin_queue(struct nvme_ctrl
>>> *ctrl)
>>>           goto out_free_data;
>>>       }
>>> -    ctrl->cntlid = le16_to_cpu(res.u16);
>>> -
>>> +    result = le32_to_cpu(res.u32);
>>> +    ctrl->cntlid = result & 0xFFFF;
>>> +    if ((result >> 16) & 2) {
>>> +        /* Authentication required */
>>> +        ret = nvme_auth_negotiate(ctrl, NVME_QID_ANY);
>>> +        if (ret)
>>> +            dev_warn(ctrl->device,
>>> +                 "qid 0: authentication failed\n");
>>> +        else
>>> +            dev_info(ctrl->device,
>>> +                 "qid 0: authenticated\n");
>>
>> info is too chatty.
>>
>
> Hmm. I know I need to work on logging...
>
>>> +    }
>>>   out_free_data:
>>>       kfree(data);
>>>       return ret;
>>> @@ -432,6 +443,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl,
>>> u16 qid)
>>>       struct nvmf_connect_data *data;
>>>       union nvme_result res;
>>>       int ret;
>>> +    u32 result;
>>>       cmd.connect.opcode = nvme_fabrics_command;
>>>       cmd.connect.fctype = nvme_fabrics_type_connect;
>>> @@ -457,6 +469,17 @@ int nvmf_connect_io_queue(struct nvme_ctrl
>>> *ctrl, u16 qid)
>>>           nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32),
>>>                          &cmd, data);
>>>       }
>>> +    result = le32_to_cpu(res.u32);
>>> +    if ((result >> 16) & 2) {
>>> +        /* Authentication required */
>>> +        ret = nvme_auth_negotiate(ctrl, qid);
>>> +        if (ret)
>>> +            dev_warn(ctrl->device,
>>> +                 "qid %u: authentication failed\n", qid);
>>> +        else
>>> +            dev_info(ctrl->device,
>>> +                 "qid %u: authenticated\n", qid);
>>> +    }
>>>       kfree(data);
>>>       return ret;
>>>   }
>>> @@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
>>>       { NVMF_OPT_NR_POLL_QUEUES,    "nr_poll_queues=%d"    },
>>>       { NVMF_OPT_TOS,            "tos=%d"        },
>>>       { NVMF_OPT_FAIL_FAST_TMO,    "fast_io_fail_tmo=%d"    },
>>> +    { NVMF_OPT_DHCHAP_SECRET,    "dhchap_secret=%s"    },
>>> +    { NVMF_OPT_DHCHAP_AUTH,        "authenticate"        },
>>> +    { NVMF_OPT_DHCHAP_GROUP,    "dhchap_group=%s"    },
>>
>> Isn't the group driven by the subsystem? also why is there a
>> "authenticate" boolean? what is it good for?
>>
> Ah. Right. Of course, the 'group' is pointless here.
> And the 'authenticate' bool is the abovementioned bidirectional
> authentication.
> I _do_ need to give it another name.
>
>>>       { NVMF_OPT_ERR,            NULL            }
>>>   };
>>> @@ -824,6 +850,35 @@ static int nvmf_parse_options(struct
>>> nvmf_ctrl_options *opts,
>>>               }
>>>               opts->tos = token;
>>>               break;
>>> +        case NVMF_OPT_DHCHAP_SECRET:
>>> +            p = match_strdup(args);
>>> +            if (!p) {
>>> +                ret = -ENOMEM;
>>> +                goto out;
>>> +            }
>>> +            if (strncmp(p, "DHHC-1:00:", 10)) {
>>> +                pr_err("Invalid DH-CHAP secret %s\n", p);
>>> +                ret = -EINVAL;
>>> +                goto out;
>>> +            }
>>> +            kfree(opts->dhchap_secret);
>>> +            opts->dhchap_secret = p;
>>> +            break;
>>> +        case NVMF_OPT_DHCHAP_AUTH:
>>> +            opts->dhchap_auth = true;
>>> +            break;
>>> +        case NVMF_OPT_DHCHAP_GROUP:
>>> +            if (match_int(args, &token)) {
>>> +                ret = -EINVAL;
>>> +                goto out;
>>> +            }
>>> +            if (token <= 0) {
>>> +                pr_err("Invalid dhchap_group %d\n", token);
>>> +                ret = -EINVAL;
>>> +                goto out;
>>> +            }
>>> +            opts->dhchap_group = token;
>>> +            break;
>>>           default:
>>>               pr_warn("unknown parameter or missing value '%s' in
>>> ctrl creation request\n",
>>>                   p);
>>> @@ -942,6 +997,7 @@ void nvmf_free_options(struct nvmf_ctrl_options
>>> *opts)
>>>       kfree(opts->subsysnqn);
>>>       kfree(opts->host_traddr);
>>>       kfree(opts->host_iface);
>>> +    kfree(opts->dhchap_secret);
>>>       kfree(opts);
>>>   }
>>>   EXPORT_SYMBOL_GPL(nvmf_free_options);
>>> @@ -951,7 +1007,10 @@ EXPORT_SYMBOL_GPL(nvmf_free_options);
>>>                    NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \
>>>                    NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\
>>>                    NVMF_OPT_DISABLE_SQFLOW |\
>>> -                 NVMF_OPT_FAIL_FAST_TMO)
>>> +                 NVMF_OPT_CTRL_LOSS_TMO |\
>>> +                 NVMF_OPT_FAIL_FAST_TMO |\
>>> +                 NVMF_OPT_DHCHAP_SECRET |\
>>> +                 NVMF_OPT_DHCHAP_AUTH | NVMF_OPT_DHCHAP_GROUP)
>>>   static struct nvme_ctrl *
>>>   nvmf_create_ctrl(struct device *dev, const char *buf)
>>> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
>>> index a146cb903869..535bc544f0f6 100644
>>> --- a/drivers/nvme/host/fabrics.h
>>> +++ b/drivers/nvme/host/fabrics.h
>>> @@ -67,6 +67,9 @@ enum {
>>>       NVMF_OPT_TOS        = 1 << 19,
>>>       NVMF_OPT_FAIL_FAST_TMO    = 1 << 20,
>>>       NVMF_OPT_HOST_IFACE    = 1 << 21,
>>> +    NVMF_OPT_DHCHAP_SECRET    = 1 << 22,
>>> +    NVMF_OPT_DHCHAP_AUTH    = 1 << 23,
>>> +    NVMF_OPT_DHCHAP_GROUP    = 1 << 24,
>>>   };
>>>   /**
>>> @@ -96,6 +99,8 @@ enum {
>>>    * @max_reconnects: maximum number of allowed reconnect attempts
>>> before removing
>>>    *              the controller, (-1) means reconnect forever, zero
>>> means remove
>>>    *              immediately;
>>> + * @dhchap_secret: DH-HMAC-CHAP secret
>>> + * @dhchap_auth: DH-HMAC-CHAP authenticate controller
>>>    * @disable_sqflow: disable controller sq flow control
>>>    * @hdr_digest: generate/verify header digest (TCP)
>>>    * @data_digest: generate/verify data digest (TCP)
>>> @@ -120,6 +125,9 @@ struct nvmf_ctrl_options {
>>>       unsigned int        kato;
>>>       struct nvmf_host    *host;
>>>       int            max_reconnects;
>>> +    char            *dhchap_secret;
>>> +    int            dhchap_group;
>>> +    bool            dhchap_auth;
>>>       bool            disable_sqflow;
>>>       bool            hdr_digest;
>>>       bool            data_digest;
>>> diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
>>> index 18ef8dd03a90..bcd5b8276c26 100644
>>> --- a/drivers/nvme/host/nvme.h
>>> +++ b/drivers/nvme/host/nvme.h
>>> @@ -328,6 +328,12 @@ struct nvme_ctrl {
>>>       struct work_struct ana_work;
>>>   #endif
>>> +#ifdef CONFIG_NVME_AUTH
>>> +    u16 transaction;
>>> +    u8 dhchap_hash;
>>> +    u8 dhchap_dhgroup;
>>
>> Do multiple controllers in the same subsystem have different
>> params? no, so I think these should be lifted to subsys.
>>
>
> It doesn't actually say in the spec; it always refers to the params as
> being set by the controller.
> So it could be either; maybe we should ask for clafication at the fmds
> call.

We should, but I'd be surprised that different controllers in the same
subsystem can authenticate difrerently...

2021-07-19 09:21:41

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 07/11] nvme-auth: augmented challenge support



On 7/16/21 4:04 AM, Hannes Reinecke wrote:
> Implement support for augmented challenge using FFDHE groups.

Please some more info for the change log...

>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
> 1 file changed, 371 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
> index 448a3adebea6..754343aced19 100644
> --- a/drivers/nvme/host/auth.c
> +++ b/drivers/nvme/host/auth.c
> @@ -8,6 +8,8 @@
> #include <asm/unaligned.h>
> #include <crypto/hash.h>
> #include <crypto/kpp.h>
> +#include <crypto/dh.h>
> +#include <crypto/ffdhe.h>
> #include "nvme.h"
> #include "fabrics.h"
> #include "auth.h"
> @@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;
>
> struct nvme_dhchap_context {
> struct crypto_shash *shash_tfm;
> + struct crypto_shash *digest_tfm;
> + struct crypto_kpp *dh_tfm;
> unsigned char *key;
> size_t key_len;
> int qid;
> @@ -25,6 +29,8 @@ struct nvme_dhchap_context {
> u8 status;
> u8 hash_id;
> u8 hash_len;
> + u8 dhgroup_id;
> + u16 dhgroup_size;
> u8 c1[64];
> u8 c2[64];
> u8 response[64];
> @@ -36,6 +42,94 @@ struct nvme_dhchap_context {
> int sess_key_len;
> };
>
> +struct nvme_auth_dhgroup_map {
> + int id;
> + const char name[16];
> + const char kpp[16];
> + int privkey_size;
> + int pubkey_size;
> +} dhgroup_map[] = {
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
> + .name = "NULL", .kpp = "NULL",
> + .privkey_size = 0, .pubkey_size = 0 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_2048,
> + .name = "ffdhe2048", .kpp = "dh",
> + .privkey_size = 256, .pubkey_size = 256 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_3072,
> + .name = "ffdhe3072", .kpp = "dh",
> + .privkey_size = 384, .pubkey_size = 384 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_4096,
> + .name = "ffdhe4096", .kpp = "dh",
> + .privkey_size = 512, .pubkey_size = 512 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_6144,
> + .name = "ffdhe6144", .kpp = "dh",
> + .privkey_size = 768, .pubkey_size = 768 },
> + { .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
> + .name = "ffdhe8192", .kpp = "dh",
> + .privkey_size = 1024, .pubkey_size = 1024 },
> +};
> +
> +const char *nvme_auth_dhgroup_name(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].name;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
> +
> +int nvme_auth_dhgroup_pubkey_size(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].pubkey_size;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_pubkey_size);
> +
> +int nvme_auth_dhgroup_privkey_size(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].privkey_size;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_privkey_size);
> +
> +const char *nvme_auth_dhgroup_kpp(int dhgroup_id)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (dhgroup_map[i].id == dhgroup_id)
> + return dhgroup_map[i].kpp;
> + }
> + return NULL;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
> +
> +int nvme_auth_dhgroup_id(const char *dhgroup_name)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
> + if (!strncmp(dhgroup_map[i].name, dhgroup_name,
> + strlen(dhgroup_map[i].name)))
> + return dhgroup_map[i].id;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
> +
> struct nvmet_dhchap_hash_map {
> int id;
> int hash_len;
> @@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct nvme_ctrl *ctrl,
> data->napd = 1;
> data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
> data->auth_protocol[0].dhchap.halen = 3;
> - data->auth_protocol[0].dhchap.dhlen = 1;
> + data->auth_protocol[0].dhchap.dhlen = 6;
> data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_DHCHAP_HASH_SHA256;
> data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_DHCHAP_HASH_SHA384;
> data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_DHCHAP_HASH_SHA512;
> data->auth_protocol[0].dhchap.idlist[3] = NVME_AUTH_DHCHAP_DHGROUP_NULL;
> + data->auth_protocol[0].dhchap.idlist[4] = NVME_AUTH_DHCHAP_DHGROUP_2048;
> + data->auth_protocol[0].dhchap.idlist[5] = NVME_AUTH_DHCHAP_DHGROUP_3072;
> + data->auth_protocol[0].dhchap.idlist[6] = NVME_AUTH_DHCHAP_DHGROUP_4096;
> + data->auth_protocol[0].dhchap.idlist[7] = NVME_AUTH_DHCHAP_DHGROUP_6144;
> + data->auth_protocol[0].dhchap.idlist[8] = NVME_AUTH_DHCHAP_DHGROUP_8192;
>
> return size;
> }
> @@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
> chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> return -EPROTO;
> }
> - switch (data->dhgid) {
> - case NVME_AUTH_DHCHAP_DHGROUP_NULL:
> - gid_name = "null";
> - break;
> - default:
> - gid_name = NULL;
> - break;
> - }
> + gid_name = nvme_auth_dhgroup_kpp(data->dhgid);
> if (!gid_name) {
> dev_warn(ctrl->device,
> "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
> @@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
> return -EPROTO;
> }
> if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> - chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> - return -EPROTO;
> - }
> - if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0) {
> + if (data->dhvlen == 0) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: empty DH value\n",
> + chap->qid);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + return -EPROTO;
> + }
> + chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
> + if (IS_ERR(chap->dh_tfm)) {
> + dev_warn(ctrl->device,
> + "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
> + chap->qid, gid_name);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + chap->dh_tfm = NULL;
> + return -EPROTO;

Why not propogate the error?

> + }
> + chap->dhgroup_id = data->dhgid;
> + } else if (data->dhvlen != 0) {
> dev_warn(ctrl->device,
> "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
> chap->qid);
> @@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl,
> chap->hash_len = data->hl;
> chap->s1 = le32_to_cpu(data->seqnum);
> memcpy(chap->c1, data->cval, chap->hash_len);
> + if (data->dhvlen) {
> + chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL);
> + if (!chap->ctrl_key)
> + return -ENOMEM;
> + chap->ctrl_key_len = data->dhvlen;
> + memcpy(chap->ctrl_key, data->cval + chap->hash_len,
> + data->dhvlen);
> + dev_dbg(ctrl->device, "ctrl public key %*ph\n",
> + (int)chap->ctrl_key_len, chap->ctrl_key);
> + }
>
> return 0;
> }
> @@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl,
> memcpy(data->rval + chap->hash_len, chap->c2,
> chap->hash_len);
> }
> - if (chap->host_key_len)
> + if (chap->host_key_len) {
> + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
> + __func__, chap->qid,
> + chap->host_key_len, chap->host_key);
> memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
> chap->host_key_len);
> -
> + }

Is this change only adding the debug print?

> return size;
> }
>
> @@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct nvme_ctrl *ctrl,
> int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> struct nvme_dhchap_context *chap)
> {
> - char *hash_name;
> + const char *hash_name, *digest_name;
> int ret;
>
> - switch (chap->hash_id) {
> - case NVME_AUTH_DHCHAP_HASH_SHA256:
> - hash_name = "hmac(sha256)";
> - break;
> - case NVME_AUTH_DHCHAP_HASH_SHA384:
> - hash_name = "hmac(sha384)";
> - break;
> - case NVME_AUTH_DHCHAP_HASH_SHA512:
> - hash_name = "hmac(sha512)";
> - break;
> - default:
> - hash_name = NULL;
> - break;
> - }
> + hash_name = nvme_auth_hmac_name(chap->hash_id);
> if (!hash_name) {
> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> return -EPROTO;
> @@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
> chap->shash_tfm = NULL;
> return -EPROTO;
> }
> + digest_name = nvme_auth_digest_name(chap->hash_id);
> + if (!digest_name) {
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + return -EPROTO;
> + }
> + chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0);
> + if (IS_ERR(chap->digest_tfm)) {
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->shash_tfm);
> + chap->shash_tfm = NULL;
> + chap->digest_tfm = NULL;
> + return -EPROTO;
> + }
> if (!chap->key) {
> dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
> chap->qid);
> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->digest_tfm);
> crypto_free_shash(chap->shash_tfm);
> chap->shash_tfm = NULL;
> + chap->digest_tfm = NULL;
> return -EINVAL;

Please have a structured goto targets in reverse order, this repeated
cleanup is a mess...

> }
> ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap->key_len);
> if (ret) {
> chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
> + crypto_free_shash(chap->digest_tfm);
> crypto_free_shash(chap->shash_tfm);
> chap->shash_tfm = NULL;
> + chap->digest_tfm = NULL;
> return ret;
> }
> - dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> - chap->qid, hash_name);
> + dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
> + chap->qid, hash_name);
> return 0;
> }
>
> +static int nvme_auth_augmented_challenge(struct nvme_dhchap_context *chap,
> + u8 *challenge, u8 *aug)
> +{
> + struct crypto_shash *tfm;
> + struct shash_desc *desc;
> + u8 *hashed_key;
> + const char *hash_name;
> + int ret;
> +
> + hashed_key = kmalloc(chap->hash_len, GFP_KERNEL);
> + if (!hashed_key)
> + return -ENOMEM;
> +
> + ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key,
> + chap->sess_key_len, hashed_key);
> + if (ret < 0) {
> + pr_debug("failed to hash session key, err %d\n", ret);
> + kfree(hashed_key);

Same here...

> + return ret;
> + }

Spaces between if conditions please?

> + hash_name = crypto_shash_alg_name(chap->shash_tfm);
> + if (!hash_name) {
> + pr_debug("Invalid hash algoritm\n");
> + return -EINVAL;
> + }
> + tfm = crypto_alloc_shash(hash_name, 0, 0);
> + if (IS_ERR(tfm)) {
> + ret = PTR_ERR(tfm);
> + goto out_free_key;
> + }
> + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
> + GFP_KERNEL);
> + if (!desc) {
> + ret = -ENOMEM;
> + goto out_free_hash;
> + }
> + desc->tfm = tfm;
> +
> + ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len);
> + if (ret)
> + goto out_free_desc;
> + ret = crypto_shash_init(desc);
> + if (ret)
> + goto out_free_desc;
> + crypto_shash_update(desc, challenge, chap->hash_len);
> + crypto_shash_final(desc, aug);
> +
> +out_free_desc:
> + kfree_sensitive(desc);
> +out_free_hash:
> + crypto_free_shash(tfm);
> +out_free_key:
> + kfree(hashed_key);
> + return ret;
> +}
> +
> static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
> struct nvme_dhchap_context *chap)
> {
> @@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
>
> dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
> __func__, chap->qid, chap->s1, chap->transaction);
> + if (chap->dh_tfm) {
> + challenge = kmalloc(chap->hash_len, GFP_KERNEL);
> + if (!challenge) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + ret = nvme_auth_augmented_challenge(chap, chap->c1, challenge);
> + if (ret)
> + goto out;
> + }
> shash->tfm = chap->shash_tfm;
> ret = crypto_shash_init(shash);
> if (ret)
> @@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
> goto out;
> ret = crypto_shash_final(shash, chap->response);
> out:
> + if (challenge != chap->c1)
> + kfree(challenge);
> return ret;
> }
>
> @@ -542,6 +734,17 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
> u8 buf[4], *challenge = chap->c2;
> int ret;
>
> + if (chap->dh_tfm) {
> + challenge = kmalloc(chap->hash_len, GFP_KERNEL);
> + if (!challenge) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + ret = nvme_auth_augmented_challenge(chap, chap->c2,
> + challenge);
> + if (ret)
> + goto out;
> + }
> dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction %d\n",
> __func__, chap->qid, chap->s2, chap->transaction);
> dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
> @@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
> goto out;
> ret = crypto_shash_final(shash, chap->response);
> out:
> + if (challenge != chap->c2)
> + kfree(challenge);

Just free ?! what about failing?

> return ret;
> }
>
> @@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl,
> return 0;
> }
>
> +static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl,
> + struct nvme_dhchap_context *chap)
> +{
> + struct kpp_request *req;
> + struct crypto_wait wait;
> + struct scatterlist src, dst;
> + u8 *pkey;
> + int ret, pkey_len;
> +
> + if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
> + chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_8192) {
> + struct dh p = {0};
> + int pubkey_size = nvme_auth_dhgroup_pubkey_size(chap->dhgroup_id);
> +
> + ret = crypto_ffdhe_params(&p, pubkey_size << 3);
> + if (ret) {
> + dev_dbg(ctrl->device,
> + "failed to generate ffdhe params, error %d\n",
> + ret);
> + return ret;
> + }
> + p.key = chap->key;
> + p.key_size = chap->key_len;
> +
> + pkey_len = crypto_dh_key_len(&p);
> + pkey = kzalloc(pkey_len, GFP_KERNEL);
> +
> + get_random_bytes(pkey, pkey_len);
> + ret = crypto_dh_encode_key(pkey, pkey_len, &p);
> + if (ret) {
> + dev_dbg(ctrl->device,
> + "failed to encode pkey, error %d\n", ret);
> + kfree(pkey);
> + return ret;
> + }
> + chap->host_key_len = pubkey_size;
> + chap->sess_key_len = pubkey_size;
> + } else {
> + dev_warn(ctrl->device, "Invalid DH group id %d\n",
> + chap->dhgroup_id);
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return -EINVAL;
> + }
> +
> + ret = crypto_kpp_set_secret(chap->dh_tfm, pkey, pkey_len);
> + if (ret) {
> + dev_dbg(ctrl->dev, "failed to set secret, error %d\n", ret);
> + kfree(pkey);
> + return ret;
> + }
> + req = kpp_request_alloc(chap->dh_tfm, GFP_KERNEL);
> + if (!req) {
> + ret = -ENOMEM;
> + goto out_free_exp;
> + }
> +
> + chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL);
> + if (!chap->host_key) {
> + ret = -ENOMEM;
> + goto out_free_req;
> + }
> + crypto_init_wait(&wait);
> + kpp_request_set_input(req, NULL, 0);
> + sg_init_one(&dst, chap->host_key, chap->host_key_len);
> + kpp_request_set_output(req, &dst, chap->host_key_len);
> + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
> + if (ret == -EOVERFLOW) {
> + dev_dbg(ctrl->dev,
> + "public key buffer too small, wants %d is %d\n",
> + crypto_kpp_maxsize(chap->dh_tfm), chap->host_key_len);
> + goto out_free_host;

Is this a specific retcode of intereset? Why did you specifically add
special casing here?

> + } else if (ret) {
> + dev_dbg(ctrl->dev,
> + "failed to generate public key, error %d\n", ret);
> + goto out_free_host;
> + }
> +
> + chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL);
> + if (!chap->sess_key)
> + goto out_free_host;
> +
> + crypto_init_wait(&wait);
> + sg_init_one(&src, chap->ctrl_key, chap->ctrl_key_len);
> + kpp_request_set_input(req, &src, chap->ctrl_key_len);
> + sg_init_one(&dst, chap->sess_key, chap->sess_key_len);
> + kpp_request_set_output(req, &dst, chap->sess_key_len);
> + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait);
> + if (ret) {
> + dev_dbg(ctrl->dev,
> + "failed to generate shared secret, error %d\n", ret);
> + kfree_sensitive(chap->sess_key);
> + chap->sess_key = NULL;
> + chap->sess_key_len = 0;
> + } else
> + dev_dbg(ctrl->dev, "shared secret %*ph\n",
> + (int)chap->sess_key_len, chap->sess_key);
> +out_free_host:
> + if (ret) {
> + kfree(chap->host_key);
> + chap->host_key = NULL;
> + chap->host_key_len = 0;
> + }
> +out_free_req:
> + kpp_request_free(req);
> +out_free_exp:
> + kfree_sensitive(pkey);
> + if (ret)
> + chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + return ret;
> +}
> +
> void nvme_auth_free(struct nvme_dhchap_context *chap)
> {
> if (chap->shash_tfm)
> crypto_free_shash(chap->shash_tfm);
> + if (chap->digest_tfm)
> + crypto_free_shash(chap->digest_tfm);
> + if (chap->dh_tfm)
> + crypto_free_kpp(chap->dh_tfm);
> if (chap->key)
> kfree(chap->key);
> if (chap->ctrl_key)
> @@ -732,6 +1061,15 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
> if (ret)
> goto fail2;
>
> + if (chap->ctrl_key_len) {
> + dev_dbg(ctrl->device,
> + "%s: qid %d DH-HMAC-DHAP DH exponential\n",
> + __func__, qid);
> + ret = nvme_auth_dhchap_exponential(ctrl, chap);
> + if (ret)
> + goto fail2;
> + }
> +
> dev_dbg(ctrl->device, "%s: qid %d DH-HMAC-CHAP host response\n",
> __func__, qid);
> ret = nvme_auth_dhchap_host_response(ctrl, chap);
> @@ -806,6 +1144,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
> ret = -EPROTO;
> if (!ret) {
> ctrl->dhchap_hash = chap->hash_id;
> + ctrl->dhchap_dhgroup = chap->dhgroup_id;
> }
> kfree(buf);
> nvme_auth_free(chap);
>

2021-07-19 09:25:01

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms


> TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE
> groups, and these are already implemented in the kernel.

So?

> So add support for these non-standard groups for NVMe in-band
> authentication to validate the augmented challenge implementation.

Why? why should users come to expect controllers to support it?

2021-07-19 09:35:16

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/18/21 2:56 PM, Stephan Müller wrote:
> Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
>
> Hi Hannes,
>
>> On 7/17/21 6:49 PM, Stephan Müller wrote:
>>> Am Freitag, 16. Juli 2021, 13:04:26 CEST schrieb Hannes Reinecke:
>>>
>>> Hi Hannes,
>>>
>>>> Implement support for NVMe-oF In-Band authentication. This patch
>>>> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
>>>> to the 'host' configfs directory. The 'dhchap_key' needs to be
>>>> specified in the format outlined in the base spec.
>>>> Augmented challenge support is not implemented, and concatenation
>>>> with TLS encryption is not supported.
>>>>
>>>> Signed-off-by: Hannes Reinecke <[email protected]>
>>>> ---
>>>>
>>>> drivers/nvme/target/Kconfig | 10 +
>>>> drivers/nvme/target/Makefile | 1 +
>>>> drivers/nvme/target/admin-cmd.c | 4 +
>>>> drivers/nvme/target/auth.c | 352 +++++++++++++++++++
>>>> drivers/nvme/target/configfs.c | 71 +++-
>>>> drivers/nvme/target/core.c | 8 +
>>>> drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
>>>> drivers/nvme/target/fabrics-cmd.c | 30 +-
>>>> drivers/nvme/target/nvmet.h | 71 ++++
>>>> 9 files changed, 1004 insertions(+), 3 deletions(-)
>>>> create mode 100644 drivers/nvme/target/auth.c
>>>> create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
>>>>
>>>> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
>>>> index 4be2ececbc45..d5656ef1559e 100644
>>>> --- a/drivers/nvme/target/Kconfig
>>>> +++ b/drivers/nvme/target/Kconfig
>>>> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
>>>>
>>>> devices over TCP.
>>>>
>>>> If unsure, say N.
>>>>
>>>> +
>>>> +config NVME_TARGET_AUTH
>>>> + bool "NVMe over Fabrics In-band Authentication support"
>>>> + depends on NVME_TARGET
>>>> + select CRYPTO_SHA256
>>>> + select CRYPTO_SHA512
>>>> + help
>>>> + This enables support for NVMe over Fabrics In-band Authentication
>>>> +
>>>> + If unsure, say N.
>>>> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
>>>> index 9837e580fa7e..c66820102493 100644
>>>> --- a/drivers/nvme/target/Makefile
>>>> +++ b/drivers/nvme/target/Makefile
>>>> @@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o
>>>
>>> fabrics-cmd.o \
>>>
>>>> discovery.o io-cmd-file.o io-cmd-bdev.o
>>>>
>>>> nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
>>>> nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
>>>>
>>>> +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o
>>>>
>>>> nvme-loop-y += loop.o
>>>> nvmet-rdma-y += rdma.o
>>>> nvmet-fc-y += fc.o
>>>>
>>>> diff --git a/drivers/nvme/target/admin-cmd.c
>>>> b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644
>>>> --- a/drivers/nvme/target/admin-cmd.c
>>>> +++ b/drivers/nvme/target/admin-cmd.c
>>>> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
>>>>
>>>> if (nvme_is_fabrics(cmd))
>>>>
>>>> return nvmet_parse_fabrics_cmd(req);
>>>>
>>>> +
>>>> + if (unlikely(!nvmet_check_auth_status(req)))
>>>> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
>>>> +
>>>>
>>>> if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
>>>>
>>>> return nvmet_parse_discovery_cmd(req);
>>>>
>>>> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
>>>> new file mode 100644
>>>> index 000000000000..00c7d051dfb1
>>>> --- /dev/null
>>>> +++ b/drivers/nvme/target/auth.c
>>>> @@ -0,0 +1,352 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
>>>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
>>>> + * All rights reserved.
>>>> + */
>>>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>>> +#include <linux/module.h>
>>>> +#include <linux/init.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/err.h>
>>>> +#include <crypto/hash.h>
>>>> +#include <crypto/kpp.h>
>>>> +#include <crypto/dh.h>
>>>> +#include <crypto/ffdhe.h>
>>>> +#include <linux/crc32.h>
>>>> +#include <linux/base64.h>
>>>> +#include <linux/ctype.h>
>>>> +#include <linux/random.h>
>>>> +#include <asm/unaligned.h>
>>>> +
>>>> +#include "nvmet.h"
>>>> +#include "../host/auth.h"
>>>> +
>>>> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
>>>> +{
>>>> + if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
>>>> + return -EINVAL;
>>>> + if (host->dhchap_key_hash > 3) {
>>>> + pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
>>>> + host->dhchap_key_hash);
>>>> + return -EINVAL;
>>>> + }
>>>> + if (host->dhchap_key_hash > 0) {
>>>> + /* Validate selected hash algorithm */
>>>> + const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
>>>> +
>>>> + if (!crypto_has_shash(hmac, 0, 0)) {
>>>> + pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
>>>> + host->dhchap_key_hash = -1;
>>>> + return -EAGAIN;
>>>> + }
>>>> + /* Use this hash as default */
>>>> + if (!host->dhchap_hash_id)
>>>> + host->dhchap_hash_id = host->dhchap_key_hash;
>>>> + }
>>>> + host->dhchap_secret = kstrdup(secret, GFP_KERNEL);
>>>
>>> Just like before - are you sure that the secret is an ASCII string and no
>>> binary blob?
>>
>> That is ensured by the transport encoding (cf NVMe Base Specification
>> version 2.0). Also, this information is being passed in via the configfs
>> interface, so it's bounded by PAGE_SIZE. But yes, we should be inserting
>> a terminating 'NULL' character at the end of the page to ensure we don't
>> incur an buffer overrun. Any other failure will be checked for during
>> base64 decoding.
>>
>>>> + if (!host->dhchap_secret)
>>>> + return -ENOMEM;
>>>> + /* Default to SHA256 */
>>>> + if (!host->dhchap_hash_id)
>>>> + host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;
>>>> +
>>>> + pr_debug("Using hash %s\n",
>>>> + nvme_auth_hmac_name(host->dhchap_hash_id));
>>>> + return 0;
>>>> +}
>>>> +
>>>> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
>>>> +{
>>>> + int ret = -ENOTSUPP;
>>>> +
>>>> + if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
>>>> + return 0;
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
>>>> +{
>>>> + int ret = 0;
>>>> + struct nvmet_host_link *p;
>>>> + struct nvmet_host *host = NULL;
>>>> + const char *hash_name;
>>>> +
>>>> + down_read(&nvmet_config_sem);
>>>> + if (ctrl->subsys->type == NVME_NQN_DISC)
>>>> + goto out_unlock;
>>>> +
>>>> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
>>>> + pr_debug("check %s\n", nvmet_host_name(p->host));
>>>> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
>>>> + continue;
>>>> + host = p->host;
>>>> + break;
>>>> + }
>>>> + if (!host) {
>>>> + pr_debug("host %s not found\n", ctrl->hostnqn);
>>>> + ret = -EPERM;
>>>> + goto out_unlock;
>>>> + }
>>>> + if (!host->dhchap_secret) {
>>>> + pr_debug("No authentication provided\n");
>>>> + goto out_unlock;
>>>> + }
>>>> +
>>>> + hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
>>>> + if (!hash_name) {
>>>> + pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);
>>>> + ret = -EINVAL;
>>>> + goto out_unlock;
>>>> + }
>>>> + ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
>>>> + CRYPTO_ALG_ALLOCATES_MEMORY);
>>>> + if (IS_ERR(ctrl->shash_tfm)) {
>>>> + pr_debug("failed to allocate shash %s\n", hash_name);
>>>> + ret = PTR_ERR(ctrl->shash_tfm);
>>>> + ctrl->shash_tfm = NULL;
>>>> + goto out_unlock;
>>>> + }
>>>> +
>>>> + ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
>>>> + &ctrl->dhchap_key_len);
>>>> + if (IS_ERR(ctrl->dhchap_key)) {
>>>> + pr_debug("failed to extract host key, error %d\n", ret);
>>>> + ret = PTR_ERR(ctrl->dhchap_key);
>>>> + ctrl->dhchap_key = NULL;
>>>> + goto out_free_hash;
>>>> + }
>>>> + if (host->dhchap_key_hash) {
>>>> + struct crypto_shash *key_tfm;
>>>> +
>>>> + hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
>>>> + key_tfm = crypto_alloc_shash(hash_name, 0, 0);
>>>> + if (IS_ERR(key_tfm)) {
>>>> + ret = PTR_ERR(key_tfm);
>>>> + goto out_free_hash;
>>>> + } else {
>>>> + SHASH_DESC_ON_STACK(shash, key_tfm);
>>>> +
>>>> + shash->tfm = key_tfm;
>>>> + ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
>>>> + ctrl->dhchap_key_len);
>>>> + crypto_shash_init(shash);
>>>> + crypto_shash_update(shash, ctrl->subsys->subsysnqn,
>>>> + strlen(ctrl->subsys->subsysnqn));
>>>> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
>>>> + crypto_shash_final(shash, ctrl->dhchap_key);
>>>> + crypto_free_shash(key_tfm);
>>>> + }
>>>> + }
>>>> + pr_debug("%s: using key %*ph\n", __func__,
>>>> + (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
>>>> + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
>>>
>>> Is it truly necessary to keep the key around in ctrl->dhchap_key? It looks
>>> to me that this buffer is only used here and thus could be turned into a
>>> local variable. Keys flying around in memory is not a good idea. :-)
>>
>> The key is also used when using the ffdhe algorithm.
>> Note: I _think_ that I need to use this key for the ffdhe algorithm,
>> because the implementation I came up with is essentially plain DH with
>> pre-defined 'p', 'q' and 'g' values. But the DH implementation also
>> requires a 'key', and for that I'm using this key here.
>>
>> It might be that I'm completely off, and don't need to use a key for our
>> DH implementation. In that case you are correct.
>> (And that's why I said I'll need a review of the FFDHE implementation).
>> But for now I'll need the key for FFDHE.
>
> Do I understand you correctly that the dhchap_key is used as the input to the
> DH - i.e. it is the remote public key then? It looks strange that this is used
> for DH but then it is changed here by hashing it together with something else
> to form a new dhchap_key. Maybe that is what the protocol says. But it sounds
> strange to me, especially when you think that dhchap_key would be, say, 2048
> bits if it is truly the remote public key and then after the hashing it is 256
> or 512 bits depending on the HMAC type. This means that after the hashing,
> this dhchap_key cannot be used for FFC-DH.
>
> Or are you using the dhchap_key for two different purposes?
>
> It seems I miss something here.
>
No, not entirely. It's me who buggered it up.
I got carried away by the fact that there is a crypto_dh_encode_key()
function, and thought I need to use it here.

Which I don't (apparently).
Will be fixing it up.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-19 09:57:46

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms

On 7/19/21 11:23 AM, Sagi Grimberg wrote:
>
>> TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE
>> groups, and these are already implemented in the kernel.
>
> So?
>
>> So add support for these non-standard groups for NVMe in-band
>> authentication to validate the augmented challenge implementation.
>
> Why? why should users come to expect controllers to support it?

Having ECDH and curve25517 algorithms (which are known-good
implementations) allows one to validate the ffdhe implementation, ie to
ensure that the remainder of the protocol works as designed, even if the
ffdhe implementation might not.
And one could argue that TLS1.3 specifies all of these algorithms, so
NVMe with it's explicit reference to TLS should do so, too.

But I don't insist on it; it's just nice for debugging, that's all.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-19 09:58:13

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/19/21 10:51 AM, Stephan Mueller wrote:
> Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
>> On 7/18/21 2:56 PM, Stephan Müller wrote:
>>> Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
>
>>>> The key is also used when using the ffdhe algorithm.
>>>> Note: I _think_ that I need to use this key for the ffdhe algorithm,
>>>> because the implementation I came up with is essentially plain DH with
>>>> pre-defined 'p', 'q' and 'g' values. But the DH implementation also
>>>> requires a 'key', and for that I'm using this key here.
>>>>
>>>> It might be that I'm completely off, and don't need to use a key for our
>>>> DH implementation. In that case you are correct.
>>>> (And that's why I said I'll need a review of the FFDHE implementation).
>>>> But for now I'll need the key for FFDHE.
>>>
>>> Do I understand you correctly that the dhchap_key is used as the input to
>>> the
>>> DH - i.e. it is the remote public key then? It looks strange that this is
>>> used
>>> for DH but then it is changed here by hashing it together with something
>>> else
>>> to form a new dhchap_key. Maybe that is what the protocol says. But it
>>> sounds
>>> strange to me, especially when you think that dhchap_key would be, say,
>>> 2048
>>> bits if it is truly the remote public key and then after the hashing it is
>>> 256
>>> this dhchap_key cannot be used for FFC-DH.
>>>
>>> Or are you using the dhchap_key for two different purposes?
>>>
>>> It seems I miss something here.
>>>
>> No, not entirely. It's me who buggered it up.
>> I got carried away by the fact that there is a crypto_dh_encode_key()
>> function, and thought I need to use it here.
>
> Thank you for clarifying that. It sounds to me that there is no defined
> protocol (or if there, I would be wondering how the code would have worked
> with a different implementation). Would it make sense to first specify a
> protocol for authentication and have it discussed? I personally think it is a
> bit difficult to fully understand the protocol from the code and discuss
> protocol-level items based on the code.
>
Oh, the protocol _is_ specified:

https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf

It's just that I have issues translating that spec onto what the kernel
provides.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-19 10:03:10

by Simo Sorce

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support

On Fri, 2021-07-16 at 13:04 +0200, Hannes Reinecke wrote:
> Hi all,
>
> recent updates to the NVMe spec have added definitions for in-band
> authentication, and seeing that it provides some real benefit especially
> for NVMe-TCP here's an attempt to implement it.
>
> Tricky bit here is that the specification orients itself on TLS 1.3,
> but supports only the FFDHE groups. Which of course the kernel doesn't
> support. I've been able to come up with a patch for this, but as this
> is my first attempt to fix anything in the crypto area I would invite
> people more familiar with these matters to have a look.
>
> Also note that this is just for in-band authentication. Secure concatenation
> (ie starting TLS with the negotiated parameters) is not implemented; one would
> need to update the kernel TLS implementation for this, which at this time is
> beyond scope.
>
> As usual, comments and reviews are welcome.

Hi Hannes,
could you please reference the specific standards that describe the
NVMe authentication protocols?

Thanks,
Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc




2021-07-19 10:20:50

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
> On 7/19/21 10:51 AM, Stephan Mueller wrote:
> > Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
> > > On 7/18/21 2:56 PM, Stephan Müller wrote:
> > > > Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
> >
> > > > > The key is also used when using the ffdhe algorithm.
> > > > > Note: I _think_ that I need to use this key for the ffdhe algorithm,
> > > > > because the implementation I came up with is essentially plain DH
> > > > > with
> > > > > pre-defined 'p', 'q' and 'g' values. But the DH implementation also
> > > > > requires a 'key', and for that I'm using this key here.
> > > > >
> > > > > It might be that I'm completely off, and don't need to use a key for
> > > > > our
> > > > > DH implementation. In that case you are correct.
> > > > > (And that's why I said I'll need a review of the FFDHE
> > > > > implementation).
> > > > > But for now I'll need the key for FFDHE.
> > > >
> > > > Do I understand you correctly that the dhchap_key is used as the input
> > > > to
> > > > the
> > > > DH - i.e. it is the remote public key then? It looks strange that this
> > > > is
> > > > used
> > > > for DH but then it is changed here by hashing it together with
> > > > something
> > > > else
> > > > to form a new dhchap_key. Maybe that is what the protocol says. But it
> > > > sounds
> > > > strange to me, especially when you think that dhchap_key would be,
> > > > say,
> > > > 2048
> > > > bits if it is truly the remote public key and then after the hashing
> > > > it is
> > > > 256
> > > > this dhchap_key cannot be used for FFC-DH.
> > > >
> > > > Or are you using the dhchap_key for two different purposes?
> > > >
> > > > It seems I miss something here.
> > > >
> > > No, not entirely. It's me who buggered it up.
> > > I got carried away by the fact that there is a crypto_dh_encode_key()
> > > function, and thought I need to use it here.
> >
> > Thank you for clarifying that. It sounds to me that there is no defined
> > protocol (or if there, I would be wondering how the code would have worked
> > with a different implementation). Would it make sense to first specify a
> > protocol for authentication and have it discussed? I personally think it
> > is a
> > bit difficult to fully understand the protocol from the code and discuss
> > protocol-level items based on the code.
> >
> Oh, the protocol _is_ specified:
>
> https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
>
> It's just that I have issues translating that spec onto what the kernel
> provides.

according to the naming conventions there in figures 447 and following:

- x and y: DH private key (kernel calls it secret set with dh_set_secret or
encoded into param.key)

- g^x mod p / g^y mod p: DH public keys from either end that is communicated
over the wire (corresponding to the the DH private keys of x and y) - to set
it, you initialize a dh request and set the public key to it with
kpp_request_set_input. After performing the crypto_kpp_compute_shared_secret
you receive the shared secret

- g^xy mod p: DH shared secret - this is the one that is to be used for the
subsequent hashing /HMAC operations as this is the one that is identical on
both, the host and the controller.

Ciao
Stephan

2021-07-19 11:12:15

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support

On 7/19/21 12:02 PM, Simo Sorce wrote:
> On Fri, 2021-07-16 at 13:04 +0200, Hannes Reinecke wrote:
>> Hi all,
>>
>> recent updates to the NVMe spec have added definitions for in-band
>> authentication, and seeing that it provides some real benefit especially
>> for NVMe-TCP here's an attempt to implement it.
>>
>> Tricky bit here is that the specification orients itself on TLS 1.3,
>> but supports only the FFDHE groups. Which of course the kernel doesn't
>> support. I've been able to come up with a patch for this, but as this
>> is my first attempt to fix anything in the crypto area I would invite
>> people more familiar with these matters to have a look.
>>
>> Also note that this is just for in-band authentication. Secure concatenation
>> (ie starting TLS with the negotiated parameters) is not implemented; one would
>> need to update the kernel TLS implementation for this, which at this time is
>> beyond scope.
>>
>> As usual, comments and reviews are welcome.
>
> Hi Hannes,
> could you please reference the specific standards that describe the
> NVMe authentication protocols?
>

https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf

Section '8.13 NVMe-over-Fabrics In-band authentication'

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-19 11:13:11

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/19/21 12:19 PM, Stephan Mueller wrote:
> Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
>> On 7/19/21 10:51 AM, Stephan Mueller wrote:
>>> Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
>>>> On 7/18/21 2:56 PM, Stephan Müller wrote:
>>>>> Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
>>>
>>>>>> The key is also used when using the ffdhe algorithm.
>>>>>> Note: I _think_ that I need to use this key for the ffdhe algorithm,
>>>>>> because the implementation I came up with is essentially plain DH
>>>>>> with
>>>>>> pre-defined 'p', 'q' and 'g' values. But the DH implementation also
>>>>>> requires a 'key', and for that I'm using this key here.
>>>>>>
>>>>>> It might be that I'm completely off, and don't need to use a key for
>>>>>> our
>>>>>> DH implementation. In that case you are correct.
>>>>>> (And that's why I said I'll need a review of the FFDHE
>>>>>> implementation).
>>>>>> But for now I'll need the key for FFDHE.
>>>>>
>>>>> Do I understand you correctly that the dhchap_key is used as the input
>>>>> to
>>>>> the
>>>>> DH - i.e. it is the remote public key then? It looks strange that this
>>>>> is
>>>>> used
>>>>> for DH but then it is changed here by hashing it together with
>>>>> something
>>>>> else
>>>>> to form a new dhchap_key. Maybe that is what the protocol says. But it
>>>>> sounds
>>>>> strange to me, especially when you think that dhchap_key would be,
>>>>> say,
>>>>> 2048
>>>>> bits if it is truly the remote public key and then after the hashing
>>>>> it is
>>>>> 256
>>>>> this dhchap_key cannot be used for FFC-DH.
>>>>>
>>>>> Or are you using the dhchap_key for two different purposes?
>>>>>
>>>>> It seems I miss something here.
>>>>>
>>>> No, not entirely. It's me who buggered it up.
>>>> I got carried away by the fact that there is a crypto_dh_encode_key()
>>>> function, and thought I need to use it here.
>>>
>>> Thank you for clarifying that. It sounds to me that there is no defined
>>> protocol (or if there, I would be wondering how the code would have worked
>>> with a different implementation). Would it make sense to first specify a
>>> protocol for authentication and have it discussed? I personally think it
>>> is a
>>> bit difficult to fully understand the protocol from the code and discuss
>>> protocol-level items based on the code.
>>>
>> Oh, the protocol _is_ specified:
>>
>> https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
>>
>> It's just that I have issues translating that spec onto what the kernel
>> provides.
>
> according to the naming conventions there in figures 447 and following:
>
> - x and y: DH private key (kernel calls it secret set with dh_set_secret or
> encoded into param.key)
>

But that's were I got confused; one needs a private key here, but there
is no obvious candidate for it. But reading it more closely I guess the
private key is just a random number (cf the spec: g^y mod p, where y is
a random number selected by the host that shall be at least 256 bits
long). So I'll fix it up with the next round.

> - g^x mod p / g^y mod p: DH public keys from either end that is communicated
> over the wire (corresponding to the the DH private keys of x and y) - to set
> it, you initialize a dh request and set the public key to it with
> kpp_request_set_input. After performing the crypto_kpp_compute_shared_secret
> you receive the shared secret
>
> - g^xy mod p: DH shared secret - this is the one that is to be used for the
> subsequent hashing /HMAC operations as this is the one that is identical on
> both, the host and the controller.
>
Thanks. Will be checking the code if I do it correctly.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-19 11:53:41

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
> On 7/19/21 12:19 PM, Stephan Mueller wrote:
> > Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
> > > On 7/19/21 10:51 AM, Stephan Mueller wrote:
> > > > Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
> > > > > On 7/18/21 2:56 PM, Stephan Müller wrote:
> > > > > > Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
> > > >
> > > > > > > The key is also used when using the ffdhe algorithm.
> > > > > > > Note: I _think_ that I need to use this key for the ffdhe
> > > > > > > algorithm,
> > > > > > > because the implementation I came up with is essentially plain
> > > > > > > DH
> > > > > > > with
> > > > > > > pre-defined 'p', 'q' and 'g' values. But the DH implementation
> > > > > > > also
> > > > > > > requires a 'key', and for that I'm using this key here.
> > > > > > >
> > > > > > > It might be that I'm completely off, and don't need to use a key
> > > > > > > for
> > > > > > > our
> > > > > > > DH implementation. In that case you are correct.
> > > > > > > (And that's why I said I'll need a review of the FFDHE
> > > > > > > implementation).
> > > > > > > But for now I'll need the key for FFDHE.
> > > > > >
> > > > > > Do I understand you correctly that the dhchap_key is used as the
> > > > > > input
> > > > > > to
> > > > > > the
> > > > > > DH - i.e. it is the remote public key then? It looks strange that
> > > > > > this
> > > > > > is
> > > > > > used
> > > > > > for DH but then it is changed here by hashing it together with
> > > > > > something
> > > > > > else
> > > > > > to form a new dhchap_key. Maybe that is what the protocol says.
> > > > > > But it
> > > > > > sounds
> > > > > > strange to me, especially when you think that dhchap_key would be,
> > > > > > say,
> > > > > > 2048
> > > > > > bits if it is truly the remote public key and then after the
> > > > > > hashing
> > > > > > it is
> > > > > > 256
> > > > > > this dhchap_key cannot be used for FFC-DH.
> > > > > >
> > > > > > Or are you using the dhchap_key for two different purposes?
> > > > > >
> > > > > > It seems I miss something here.
> > > > > >
> > > > > No, not entirely. It's me who buggered it up.
> > > > > I got carried away by the fact that there is a
> > > > > crypto_dh_encode_key()
> > > > > function, and thought I need to use it here.
> > > >
> > > > Thank you for clarifying that. It sounds to me that there is no
> > > > defined
> > > > protocol (or if there, I would be wondering how the code would have
> > > > worked
> > > > with a different implementation). Would it make sense to first specify
> > > > a
> > > > protocol for authentication and have it discussed? I personally think
> > > > it
> > > > is a
> > > > bit difficult to fully understand the protocol from the code and
> > > > discuss
> > > > protocol-level items based on the code.
> > > >
> > > Oh, the protocol _is_ specified:
> > >
> > >
> > > https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
> > >
> > > It's just that I have issues translating that spec onto what the kernel
> > > provides.
> >
> > according to the naming conventions there in figures 447 and following:
> >
> > - x and y: DH private key (kernel calls it secret set with dh_set_secret
> > or
> > encoded into param.key)
> >
>
> But that's were I got confused; one needs a private key here, but there
> is no obvious candidate for it. But reading it more closely I guess the
> private key is just a random number (cf the spec: g^y mod p, where y is
> a random number selected by the host that shall be at least 256 bits
> long). So I'll fix it up with the next round.

Here comes the crux: the kernel has an ECC private key generation function
ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL key, but it
has no FFC-DH counterpart.

That said, generating a random number is the most obvious choice, but not the
right one.

The correct one would be following SP800-56A rev 3 and here either section
5.6.1.1.3 or 5.6.1.1.4.

Ciao
Stephan
>
> > - g^x mod p  / g^y mod p: DH public keys from either end that is
> > communicated
> > over the wire (corresponding to the the DH private keys of x and y) - to
> > set
> > it, you initialize a dh request and set the public key to it with
> > kpp_request_set_input. After performing the
> > crypto_kpp_compute_shared_secret
> > you receive the shared secret
> >
> > - g^xy mod p: DH shared secret - this is the one that is to be used for
> > the
> > subsequent hashing /HMAC operations as this is the one that is identical
> > on
> > both, the host and the controller.
> >
> Thanks. Will be checking the code if I do it correctly.
>
> Cheers,
>
> Hannes


2021-07-19 12:08:39

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/19/21 1:52 PM, Stephan Mueller wrote:
> Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
>> On 7/19/21 12:19 PM, Stephan Mueller wrote:
>>> Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
>>>> On 7/19/21 10:51 AM, Stephan Mueller wrote:
>>>>> Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
>>>>>> On 7/18/21 2:56 PM, Stephan Müller wrote:
>>>>>>> Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
>>>>>
>>>>>>>> The key is also used when using the ffdhe algorithm.
>>>>>>>> Note: I _think_ that I need to use this key for the ffdhe
>>>>>>>> algorithm,
>>>>>>>> because the implementation I came up with is essentially plain
>>>>>>>> DH
>>>>>>>> with
>>>>>>>> pre-defined 'p', 'q' and 'g' values. But the DH implementation
>>>>>>>> also
>>>>>>>> requires a 'key', and for that I'm using this key here.
>>>>>>>>
>>>>>>>> It might be that I'm completely off, and don't need to use a key
>>>>>>>> for
>>>>>>>> our
>>>>>>>> DH implementation. In that case you are correct.
>>>>>>>> (And that's why I said I'll need a review of the FFDHE
>>>>>>>> implementation).
>>>>>>>> But for now I'll need the key for FFDHE.
>>>>>>>
>>>>>>> Do I understand you correctly that the dhchap_key is used as the
>>>>>>> input
>>>>>>> to
>>>>>>> the
>>>>>>> DH - i.e. it is the remote public key then? It looks strange that
>>>>>>> this
>>>>>>> is
>>>>>>> used
>>>>>>> for DH but then it is changed here by hashing it together with
>>>>>>> something
>>>>>>> else
>>>>>>> to form a new dhchap_key. Maybe that is what the protocol says.
>>>>>>> But it
>>>>>>> sounds
>>>>>>> strange to me, especially when you think that dhchap_key would be,
>>>>>>> say,
>>>>>>> 2048
>>>>>>> bits if it is truly the remote public key and then after the
>>>>>>> hashing
>>>>>>> it is
>>>>>>> 256
>>>>>>> this dhchap_key cannot be used for FFC-DH.
>>>>>>>
>>>>>>> Or are you using the dhchap_key for two different purposes?
>>>>>>>
>>>>>>> It seems I miss something here.
>>>>>>>
>>>>>> No, not entirely. It's me who buggered it up.
>>>>>> I got carried away by the fact that there is a
>>>>>> crypto_dh_encode_key()
>>>>>> function, and thought I need to use it here.
>>>>>
>>>>> Thank you for clarifying that. It sounds to me that there is no
>>>>> defined
>>>>> protocol (or if there, I would be wondering how the code would have
>>>>> worked
>>>>> with a different implementation). Would it make sense to first specify
>>>>> a
>>>>> protocol for authentication and have it discussed? I personally think
>>>>> it
>>>>> is a
>>>>> bit difficult to fully understand the protocol from the code and
>>>>> discuss
>>>>> protocol-level items based on the code.
>>>>>
>>>> Oh, the protocol _is_ specified:
>>>>
>>>>
>>>> https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
>>>>
>>>> It's just that I have issues translating that spec onto what the kernel
>>>> provides.
>>>
>>> according to the naming conventions there in figures 447 and following:
>>>
>>> - x and y: DH private key (kernel calls it secret set with dh_set_secret
>>> or
>>> encoded into param.key)
>>>
>>
>> But that's were I got confused; one needs a private key here, but there
>> is no obvious candidate for it. But reading it more closely I guess the
>> private key is just a random number (cf the spec: g^y mod p, where y is
>> a random number selected by the host that shall be at least 256 bits
>> long). So I'll fix it up with the next round.
>
> Here comes the crux: the kernel has an ECC private key generation function
> ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL key, but it
> has no FFC-DH counterpart.
>
> That said, generating a random number is the most obvious choice, but not the
> right one.
>
> The correct one would be following SP800-56A rev 3 and here either section
> 5.6.1.1.3 or 5.6.1.1.4.
>

Oh fsck. Of course. Would've been too easy.
Well, more coding required then. Let's see how it goes.
But thanks for your help!

(And I wouldn't say no if someone would step in an provide a 'real'
FFDHE crypto algorithm; now that the parameters are already present :-)

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-19 21:38:30

by Sagi Grimberg

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication



On 7/16/21 4:04 AM, Hannes Reinecke wrote:
> Implement support for NVMe-oF In-Band authentication. This patch
> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
> to the 'host' configfs directory. The 'dhchap_key' needs to be
> specified in the format outlined in the base spec.
> Augmented challenge support is not implemented, and concatenation
> with TLS encryption is not supported.
>
> Signed-off-by: Hannes Reinecke <[email protected]>
> ---
> drivers/nvme/target/Kconfig | 10 +
> drivers/nvme/target/Makefile | 1 +
> drivers/nvme/target/admin-cmd.c | 4 +
> drivers/nvme/target/auth.c | 352 +++++++++++++++++++
> drivers/nvme/target/configfs.c | 71 +++-
> drivers/nvme/target/core.c | 8 +
> drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
> drivers/nvme/target/fabrics-cmd.c | 30 +-
> drivers/nvme/target/nvmet.h | 71 ++++
> 9 files changed, 1004 insertions(+), 3 deletions(-)
> create mode 100644 drivers/nvme/target/auth.c
> create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
>
> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
> index 4be2ececbc45..d5656ef1559e 100644
> --- a/drivers/nvme/target/Kconfig
> +++ b/drivers/nvme/target/Kconfig
> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
> devices over TCP.
>
> If unsure, say N.
> +
> +config NVME_TARGET_AUTH
> + bool "NVMe over Fabrics In-band Authentication support"
> + depends on NVME_TARGET
> + select CRYPTO_SHA256
> + select CRYPTO_SHA512
> + help
> + This enables support for NVMe over Fabrics In-band Authentication
> +
> + If unsure, say N.
> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
> index 9837e580fa7e..c66820102493 100644
> --- a/drivers/nvme/target/Makefile
> +++ b/drivers/nvme/target/Makefile
> @@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \
> discovery.o io-cmd-file.o io-cmd-bdev.o
> nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o
> nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o
> +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o
> nvme-loop-y += loop.o
> nvmet-rdma-y += rdma.o
> nvmet-fc-y += fc.o
> diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
> index 0cb98f2bbc8c..320cefc64ee0 100644
> --- a/drivers/nvme/target/admin-cmd.c
> +++ b/drivers/nvme/target/admin-cmd.c
> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
>
> if (nvme_is_fabrics(cmd))
> return nvmet_parse_fabrics_cmd(req);
> +
> + if (unlikely(!nvmet_check_auth_status(req)))
> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
> +
> if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
> return nvmet_parse_discovery_cmd(req);
>
> diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
> new file mode 100644
> index 000000000000..00c7d051dfb1
> --- /dev/null
> +++ b/drivers/nvme/target/auth.c
> @@ -0,0 +1,352 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
> + * All rights reserved.
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include <crypto/dh.h>
> +#include <crypto/ffdhe.h>
> +#include <linux/crc32.h>
> +#include <linux/base64.h>
> +#include <linux/ctype.h>
> +#include <linux/random.h>
> +#include <asm/unaligned.h>
> +
> +#include "nvmet.h"
> +#include "../host/auth.h"
> +
> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
> +{
> + if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
> + return -EINVAL;
> + if (host->dhchap_key_hash > 3) {
> + pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
> + host->dhchap_key_hash);
> + return -EINVAL;
> + }
> + if (host->dhchap_key_hash > 0) {
> + /* Validate selected hash algorithm */
> + const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
> +
> + if (!crypto_has_shash(hmac, 0, 0)) {
> + pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);

pr_err

> + host->dhchap_key_hash = -1;
> + return -EAGAIN;

Why EAGAIN?

> + }
> + /* Use this hash as default */
> + if (!host->dhchap_hash_id)
> + host->dhchap_hash_id = host->dhchap_key_hash;

Why?

> + }
> + host->dhchap_secret = kstrdup(secret, GFP_KERNEL);
> + if (!host->dhchap_secret)
> + return -ENOMEM;
> + /* Default to SHA256 */
> + if (!host->dhchap_hash_id)
> + host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;

What is the thought here?

> +
> + pr_debug("Using hash %s\n",
> + nvme_auth_hmac_name(host->dhchap_hash_id));
> + return 0;
> +}
> +
> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
> +{
> + int ret = -ENOTSUPP;
> +
> + if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
> + return 0;
> +
> + return ret;
> +}
> +
> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
> +{
> + int ret = 0;
> + struct nvmet_host_link *p;
> + struct nvmet_host *host = NULL;
> + const char *hash_name;
> +
> + down_read(&nvmet_config_sem);
> + if (ctrl->subsys->type == NVME_NQN_DISC)
> + goto out_unlock;
> +
> + list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
> + pr_debug("check %s\n", nvmet_host_name(p->host));
> + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
> + continue;
> + host = p->host;
> + break;
> + }
> + if (!host) {
> + pr_debug("host %s not found\n", ctrl->hostnqn);
> + ret = -EPERM;

I think you should propogate the nvme status code instead...

> + goto out_unlock;
> + }
> + if (!host->dhchap_secret) {
> + pr_debug("No authentication provided\n");
> + goto out_unlock;
> + }
> +
> + hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
> + if (!hash_name) {

Can this actually happen?

> + pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);

warning, not debug.

> + ret = -EINVAL;
> + goto out_unlock;
> + }
> + ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
> + CRYPTO_ALG_ALLOCATES_MEMORY);
> + if (IS_ERR(ctrl->shash_tfm)) {
> + pr_debug("failed to allocate shash %s\n", hash_name);
> + ret = PTR_ERR(ctrl->shash_tfm);
> + ctrl->shash_tfm = NULL;
> + goto out_unlock;
> + }
> +
> + ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
> + &ctrl->dhchap_key_len);
> + if (IS_ERR(ctrl->dhchap_key)) {
> + pr_debug("failed to extract host key, error %d\n", ret);
> + ret = PTR_ERR(ctrl->dhchap_key);
> + ctrl->dhchap_key = NULL;
> + goto out_free_hash;
> + }
> + if (host->dhchap_key_hash) {
> + struct crypto_shash *key_tfm;
> +
> + hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
> + key_tfm = crypto_alloc_shash(hash_name, 0, 0);
> + if (IS_ERR(key_tfm)) {
> + ret = PTR_ERR(key_tfm);
> + goto out_free_hash;
> + } else {
> + SHASH_DESC_ON_STACK(shash, key_tfm);
> +
> + shash->tfm = key_tfm;
> + ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
> + ctrl->dhchap_key_len);
> + crypto_shash_init(shash);
> + crypto_shash_update(shash, ctrl->subsys->subsysnqn,
> + strlen(ctrl->subsys->subsysnqn));
> + crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
> + crypto_shash_final(shash, ctrl->dhchap_key);
> + crypto_free_shash(key_tfm);
> + }
> + }
> + pr_debug("%s: using key %*ph\n", __func__,
> + (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
> + ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
> + ctrl->dhchap_key_len);
> +out_free_hash:
> + if (ret) {
> + if (ctrl->dhchap_key) {
> + kfree(ctrl->dhchap_key);
> + ctrl->dhchap_key = NULL;
> + }
> + crypto_free_shash(ctrl->shash_tfm);
> + ctrl->shash_tfm = NULL;
> + }
> +out_unlock:
> + up_read(&nvmet_config_sem);
> +
> + return ret;
> +}
> +
> +void nvmet_auth_sq_free(struct nvmet_sq *sq)
> +{
> + if (sq->dhchap_c1)
> + kfree(sq->dhchap_c1);

just kfree, no need to if

> + if (sq->dhchap_c2)
> + kfree(sq->dhchap_c2);
> + if (sq->dhchap_skey)
> + kfree(sq->dhchap_skey);
> +}
> +
> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl)

Shouldn't this be nvmet_destroy_auth? reset indicates
it can be reused again...

> +{
> + if (ctrl->shash_tfm) {
> + crypto_free_shash(ctrl->shash_tfm);
> + ctrl->shash_tfm = NULL;
> + }
> + if (ctrl->dh_tfm) {
> + crypto_free_kpp(ctrl->dh_tfm);
> + ctrl->dh_tfm = NULL;
> + }
> + if (ctrl->dhchap_key) {
> + kfree(ctrl->dhchap_key);
> + ctrl->dhchap_key = NULL;
> + }
> +}
> +
> +bool nvmet_check_auth_status(struct nvmet_req *req)
> +{
> + if (req->sq->ctrl->shash_tfm &&
> + !req->sq->authenticated)
> + return false;
> + return true;
> +}
> +
> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
> + unsigned int shash_len)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
> + u8 *challenge = req->sq->dhchap_c1;
> + u8 buf[4];
> + int ret;
> +
> + if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + ret = -ENOTSUPP;
> + goto out;
> + }
> +
> + shash->tfm = ctrl->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, shash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(req->sq->dhchap_s1, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(req->sq->dhchap_tid, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, 4);
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "HostHost", 8);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->subsysnqn,
> + strlen(ctrl->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, response);
> +out:
> + if (challenge != req->sq->dhchap_c1)
> + kfree(challenge);

What about actually failing?

> + return 0;
> +}
> +
> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
> + unsigned int shash_len)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
> + u8 *challenge = req->sq->dhchap_c2;
> + u8 buf[4];
> + int ret;
> +
> + pr_debug("%s: ctrl %d hash seq %d transaction %u\n", __func__,
> + ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid);
> + pr_debug("%s: ctrl %d challenge %*ph\n", __func__,
> + ctrl->cntlid, shash_len, req->sq->dhchap_c2);
> + pr_debug("%s: ctrl %d subsysnqn %s\n", __func__,
> + ctrl->cntlid, ctrl->subsysnqn);
> + pr_debug("%s: ctrl %d hostnqn %s\n", __func__,
> + ctrl->cntlid, ctrl->hostnqn);
> +
> + if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + ret = -ENOTSUPP;
> + goto out;
> + }
> +
> + shash->tfm = ctrl->shash_tfm;
> + ret = crypto_shash_init(shash);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, challenge, shash_len);
> + if (ret)
> + goto out;
> + put_unaligned_le32(req->sq->dhchap_s2, buf);
> + ret = crypto_shash_update(shash, buf, 4);
> + if (ret)
> + goto out;
> + put_unaligned_le16(req->sq->dhchap_tid, buf);
> + ret = crypto_shash_update(shash, buf, 2);
> + if (ret)
> + goto out;
> + memset(buf, 0, 4);
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, "Controller", 10);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->subsysnqn,
> + strlen(ctrl->subsysnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, buf, 1);
> + if (ret)
> + goto out;
> + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
> + if (ret)
> + goto out;
> + ret = crypto_shash_final(shash, response);
> +out:
> + if (challenge != req->sq->dhchap_c2)
> + kfree(challenge);
> + return 0;
> +}
> +
> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
> + u8 *pkey, int pkey_size)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct kpp_request *kpp_req;
> + struct crypto_wait wait;
> + struct scatterlist src, dst;
> + int ret;
> +
> + req->sq->dhchap_skey_len =
> + nvme_auth_dhgroup_privkey_size(ctrl->dh_gid);
> + req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL);
> + if (!req->sq->dhchap_skey)
> + return -ENOMEM;
> + kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
> + if (!kpp_req) {
> + kfree(req->sq->dhchap_skey);
> + req->sq->dhchap_skey = NULL;
> + return -ENOMEM;
> + }
> +
> + pr_debug("%s: host public key %*ph\n", __func__,
> + (int)pkey_size, pkey);
> + crypto_init_wait(&wait);
> + sg_init_one(&src, pkey, pkey_size);
> + kpp_request_set_input(kpp_req, &src, pkey_size);
> + sg_init_one(&dst, req->sq->dhchap_skey,
> + req->sq->dhchap_skey_len);
> + kpp_request_set_output(kpp_req, &dst, req->sq->dhchap_skey_len);
> + kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
> + crypto_req_done, &wait);
> +
> + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(kpp_req), &wait);
> + kpp_request_free(kpp_req);
> + if (ret)
> + pr_debug("failed to compute shared secred, err %d\n", ret);
> + else
> + pr_debug("%s: shared secret %*ph\n", __func__,
> + (int)req->sq->dhchap_skey_len,
> + req->sq->dhchap_skey);
> +
> + return ret;
> +}
> diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
> index 273555127188..e0760911a761 100644
> --- a/drivers/nvme/target/configfs.c
> +++ b/drivers/nvme/target/configfs.c
> @@ -11,8 +11,13 @@
> #include <linux/ctype.h>
> #include <linux/pci.h>
> #include <linux/pci-p2pdma.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
>
> #include "nvmet.h"
> +#ifdef CONFIG_NVME_TARGET_AUTH
> +#include "../host/auth.h"
> +#endif
>
> static const struct config_item_type nvmet_host_type;
> static const struct config_item_type nvmet_subsys_type;
> @@ -1656,10 +1661,71 @@ static const struct config_item_type nvmet_ports_type = {
> static struct config_group nvmet_subsystems_group;
> static struct config_group nvmet_ports_group;
>
> -static void nvmet_host_release(struct config_item *item)
> +#ifdef CONFIG_NVME_TARGET_AUTH
> +static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
> + char *page)
> +{
> + u8 *dhchap_secret = to_host(item)->dhchap_secret;
> +
> + if (!dhchap_secret)
> + return sprintf(page, "\n");
> + return sprintf(page, "%s\n", dhchap_secret);
> +}
> +
> +static ssize_t nvmet_host_dhchap_key_store(struct config_item *item,
> + const char *page, size_t count)
> {
> struct nvmet_host *host = to_host(item);
> + int ret;
>
> + ret = nvmet_auth_set_host_key(host, page);
> + if (ret < 0)
> + return ret;
> + return count;
> +}
> +
> +CONFIGFS_ATTR(nvmet_host_, dhchap_key);
> +
> +static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item,
> + char *page)
> +{
> + struct nvmet_host *host = to_host(item);
> + const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
> +
> + return sprintf(page, "%s\n", hash_name ? hash_name : "none");
> +}
> +
> +static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,
> + const char *page, size_t count)
> +{
> + struct nvmet_host *host = to_host(item);
> + int hmac_id;
> +
> + hmac_id = nvme_auth_hmac_id(page);
> + if (hmac_id < 0)
> + return -EINVAL;
> + if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0))
> + return -ENOTSUPP;
> + host->dhchap_hash_id = hmac_id;
> + return count;
> +}
> +
> +CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
> +
> +static struct configfs_attribute *nvmet_host_attrs[] = {
> + &nvmet_host_attr_dhchap_key,
> + &nvmet_host_attr_dhchap_hash,
> + NULL,
> +};
> +#endif /* CONFIG_NVME_TARGET_AUTH */
> +
> +static void nvmet_host_release(struct config_item *item)
> +{
> + struct nvmet_host *host = to_host(item);
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + if (host->dhchap_secret)
> + kfree(host->dhchap_secret);

No need for if condition.

> +#endif
> kfree(host);
> }
>
> @@ -1669,6 +1735,9 @@ static struct configfs_item_operations nvmet_host_item_ops = {
>
> static const struct config_item_type nvmet_host_type = {
> .ct_item_ops = &nvmet_host_item_ops,
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + .ct_attrs = nvmet_host_attrs,
> +#endif
> .ct_owner = THIS_MODULE,
> };
>
> diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
> index 163f7dc1a929..b5d7971f566b 100644
> --- a/drivers/nvme/target/core.c
> +++ b/drivers/nvme/target/core.c
> @@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
> wait_for_completion(&sq->confirm_done);
> wait_for_completion(&sq->free_done);
> percpu_ref_exit(&sq->ref);
> + nvmet_auth_sq_free(sq);
>
> if (ctrl) {
> /*
> @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req)
> req->cmd->common.opcode, req->sq->qid);
> return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
> }
> +
> + if (unlikely(!nvmet_check_auth_status(req))) {
> + pr_warn("qid %d not authenticated\n", req->sq->qid);
> + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
> + }
> return 0;
> }
>
> @@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)
> flush_work(&ctrl->async_event_work);
> cancel_work_sync(&ctrl->fatal_err_work);
>
> + nvmet_reset_auth(ctrl);
> +
> ida_simple_remove(&cntlid_ida, ctrl->cntlid);
>
> nvmet_async_events_free(ctrl);
> diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
> new file mode 100644
> index 000000000000..962f9f5e9d89
> --- /dev/null
> +++ b/drivers/nvme/target/fabrics-cmd-auth.c
> @@ -0,0 +1,460 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * NVMe over Fabrics DH-HMAC-CHAP authentication command handling.
> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
> + * All rights reserved.
> + */
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +#include <linux/blkdev.h>
> +#include <linux/random.h>
> +#include <crypto/hash.h>
> +#include <crypto/kpp.h>
> +#include "nvmet.h"
> +#include "../host/auth.h"
> +
> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
> +{
> + /* Initialize in-band authentication */
> + req->sq->authenticated = false;
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
> + req->cqe->result.u32 |= 0x2 << 16;

Can you add a define for this: NVME_CONNECT_AUTHREQ_INBAND

> +}
> +
> +static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct nvmf_auth_dhchap_negotiate_data *data = d;
> + int i, hash_id, null_dh = -1;
> +
> + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
> + data->auth_protocol[0].dhchap.halen,
> + data->auth_protocol[0].dhchap.dhlen);
> + req->sq->dhchap_tid = le16_to_cpu(data->t_id);
> + if (data->sc_c)
> + return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
> +
> + if (data->napd != 1)
> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> +
> + if (data->auth_protocol[0].dhchap.authid != 0x01)
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> +
> + hash_id = nvme_auth_hmac_id(crypto_shash_alg_name(ctrl->shash_tfm));
> + for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
> + pr_debug("%s: ctrl %d qid %d checking hash %d for %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->auth_protocol[0].dhchap.idlist[i], hash_id);
> + if (hash_id != data->auth_protocol[0].dhchap.idlist[i])
> + continue;
> + req->sq->dhchap_hash_id = hash_id;
> + req->sq->dhchap_hash_len = crypto_shash_digestsize(ctrl->shash_tfm);
> + break;
> + }
> + if (req->sq->dhchap_hash_id == 0) {
> + pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
> + __func__, ctrl->cntlid, req->sq->qid);
> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + }
> +
> + for (i = data->auth_protocol[0].dhchap.halen;
> + i < data->auth_protocol[0].dhchap.halen +
> + data->auth_protocol[0].dhchap.dhlen; i++) {
> + int dhgid = data->auth_protocol[0].dhchap.idlist[i];
> +
> + if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) {
> + null_dh = dhgid;
> + continue;
> + }
> + if (nvmet_setup_dhgroup(ctrl, dhgid) == 0)
> + break;
> + }
> + if (!ctrl->dh_tfm && null_dh < 0) {
> + pr_debug("%s: ctrl %d qid %d: no DH group selected\n",
> + __func__, ctrl->cntlid, req->sq->qid);
> + return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
> + }
> + if (ctrl->dh_gid == -1) {
> + ctrl->dh_gid = null_dh;
> + ctrl->dh_tfm = NULL;
> + }
> + pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
> + return 0;
> +}
> +
> +static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct nvmf_auth_dhchap_reply_data *data = d;
> + u8 *response;
> +
> + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->hl, data->cvalid, data->dhvlen);
> + if (data->hl != req->sq->dhchap_hash_len)
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> +
> + if (data->dhvlen) {
> + return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + }
> +
> + response = kmalloc(data->hl, GFP_KERNEL);
> + if (!response)
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> +
> + if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
> + pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n",
> + ctrl->cntlid, req->sq->qid);
> + kfree(response);
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + }
> +
> + if (memcmp(data->rval, response, data->hl)) {
> + pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n",
> + ctrl->cntlid, req->sq->qid);
> + kfree(response);
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + }
> + kfree(response);
> + pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n",
> + ctrl->cntlid, req->sq->qid);
> + if (data->cvalid) {
> + req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL);
> + if (!req->sq->dhchap_c2)
> + return NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl);
> +
> + pr_debug("ctrl %d qid %d challenge %*ph\n",
> + ctrl->cntlid, req->sq->qid, data->hl,
> + req->sq->dhchap_c2);
> + req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
> + } else
> + req->sq->dhchap_c2 = NULL;
> +
> + return 0;
> +}
> +
> +static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d)
> +{
> + struct nvmf_auth_dhchap_failure_data *data = d;
> +
> + return data->reason_code_explanation;
> +}
> +
> +void nvmet_execute_auth_send(struct nvmet_req *req)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + struct nvmf_auth_dhchap_success2_data *data;
> + void *d;
> + u32 tl;
> + u16 status = 0;
> +
> + if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, secp);
> + goto done;
> + }
> + if (req->cmd->auth_send.spsp0 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, spsp0);
> + goto done;
> + }
> + if (req->cmd->auth_send.spsp1 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, spsp1);
> + goto done;
> + }
> + tl = le32_to_cpu(req->cmd->auth_send.tl);
> + if (!tl) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_send_command, tl);
> + goto done;
> + }
> + if (!nvmet_check_transfer_len(req, tl)) {
> + pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
> + return;
> + }
> +
> + d = kmalloc(tl, GFP_KERNEL);
> + if (!d) {
> + status = NVME_SC_INTERNAL;
> + goto done;
> + }
> +
> + status = nvmet_copy_from_sgl(req, 0, d, tl);
> + if (status) {
> + kfree(d);
> + goto done;
> + }
> +

This whole block below should move to something like
nvmet_process_auth_send_data()

> + data = d;
> + pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
> + ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id,
> + req->sq->dhchap_step);
> + if (data->auth_type != NVME_AUTH_COMMON_MESSAGES &&
> + data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
> + if (data->auth_id != req->sq->dhchap_step) {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else if (data->auth_id != NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else {
> + /* Validate negotiation parameters */
> + status = nvmet_auth_negotiate(req, d);
> + if (status == 0)
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
> + else {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = status;
> + status = 0;
> + }
> + }
> + } else if (data->auth_type == NVME_AUTH_DHCHAP_MESSAGES) {
> + if (data->auth_id != req->sq->dhchap_step) {
> + pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + data->auth_id, req->sq->dhchap_step);
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + } else if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) {
> + pr_debug("%s: ctrl %d qid %d invalid transaction %d (expected %d)\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + le16_to_cpu(data->t_id),
> + req->sq->dhchap_tid);
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
> + } else {
> + switch (data->auth_id) {
> + case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
> + status = nvmet_auth_reply(req, d);
> + if (status == 0)
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
> + else {
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = status;
> + status = 0;
> + }
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
> + req->sq->authenticated = true;
> + pr_debug("%s: ctrl %d qid %d authenticated\n",
> + __func__, ctrl->cntlid, req->sq->qid);
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
> + status = nvmet_auth_failure2(req, d);
> + if (status) {
> + pr_warn("ctrl %d qid %d: DH-HMAC-CHAP negotiation failed (%d)\n",
> + ctrl->cntlid, req->sq->qid,
> + status);
> + req->sq->dhchap_status = status;
> + status = 0;
> + }
> + break;
> + default:
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
> + break;
> + }
> + }
> + } else {
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
> + }
> + kfree(d);
> +done:
> + pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__,
> + ctrl->cntlid, req->sq->qid,
> + req->sq->dhchap_status, req->sq->dhchap_step);
> + if (status)
> + pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n",
> + __func__, ctrl->cntlid, req->sq->qid,
> + status, req->error_loc);
> + req->cqe->result.u64 = 0;
> + nvmet_req_complete(req, status);
> + if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
> + req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
> + return;
> + /* Final states, clear up variables */
> + kfree(req->sq->dhchap_c1);
> + kfree(req->sq->dhchap_c2);
> + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
> + nvmet_ctrl_fatal_error(ctrl);
> +}
> +
> +static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)

nvmet_auth_set_challange

> +{
> + struct nvmf_auth_dhchap_challenge_data *data = d;
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + int ret = 0;
> + int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
> +
> + if (al < data_size) {
> + pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
> + al, data_size);
> + return -EINVAL;
> + }
> + memset(data, 0, data_size);
> + req->sq->dhchap_s1 = ctrl->dhchap_seqnum++;
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
> + data->t_id = cpu_to_le16(req->sq->dhchap_tid);
> + data->hashid = req->sq->dhchap_hash_id;
> + data->hl = req->sq->dhchap_hash_len;
> + data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
> + req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
> + if (!req->sq->dhchap_c1)
> + return -ENOMEM;
> + get_random_bytes(req->sq->dhchap_c1, data->hl);
> + memcpy(data->cval, req->sq->dhchap_c1, data->hl);
> + pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n",
> + __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
> + req->sq->dhchap_tid, data->hl, data->dhvlen);
> + return ret;
> +}
> +
> +static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al)
> +{
> + struct nvmf_auth_dhchap_success1_data *data = d;
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> +
> + WARN_ON(al < sizeof(*data));
> + memset(data, 0, sizeof(*data));
> + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
> + data->t_id = cpu_to_le16(req->sq->dhchap_tid);
> + data->hl = req->sq->dhchap_hash_len;
> + if (req->sq->dhchap_c2) {
> + if (nvmet_auth_ctrl_hash(req, data->rval, data->hl))
> + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
> + data->rvalid = 1;
> + pr_debug("ctrl %d qid %d response %*ph\n",
> + ctrl->cntlid, req->sq->qid, data->hl, data->rval);
> + }
> + return 0;
> +}
> +
> +static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
> +{
> + struct nvmf_auth_dhchap_failure_data *data = d;
> +
> + WARN_ON(al < sizeof(*data));
> + data->auth_type = NVME_AUTH_COMMON_MESSAGES;
> + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + data->t_id = cpu_to_le32(req->sq->dhchap_tid);
> + data->reason_code = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
> + data->reason_code_explanation = req->sq->dhchap_status;
> +}
> +
> +void nvmet_execute_auth_receive(struct nvmet_req *req)
> +{
> + struct nvmet_ctrl *ctrl = req->sq->ctrl;
> + void *d;
> + u32 al;
> + u16 status = 0;
> +
> + if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, secp);
> + goto done;
> + }
> + if (req->cmd->auth_receive.spsp0 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, spsp0);
> + goto done;
> + }
> + if (req->cmd->auth_receive.spsp1 != 0x01) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, spsp1);
> + goto done;
> + }
> + al = le32_to_cpu(req->cmd->auth_receive.al);
> + if (!al) {
> + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
> + req->error_loc =
> + offsetof(struct nvmf_auth_receive_command, al);
> + goto done;
> + }
> + if (!nvmet_check_transfer_len(req, al)) {
> + pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
> + return;
> + }
> +
> + d = kmalloc(al, GFP_KERNEL);
> + if (!d) {
> + status = NVME_SC_INTERNAL;
> + goto done;
> + }
> + pr_debug("%s: ctrl %d qid %d step %x\n", __func__,
> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
> + switch (req->sq->dhchap_step) {
> + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
> + status = nvmet_auth_challenge(req, d, al);
> + if (status < 0) {
> + pr_warn("ctrl %d qid %d: challenge error (%d)\n",
> + ctrl->cntlid, req->sq->qid, status);
> + status = NVME_SC_INTERNAL;
> + break;
> + }
> + if (status) {
> + req->sq->dhchap_status = status;
> + nvmet_auth_failure1(req, d, al);
> + pr_warn("ctrl %d qid %d: challenge status (%x)\n",
> + ctrl->cntlid, req->sq->qid,
> + req->sq->dhchap_status);
> + status = 0;
> + break;
> + }
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
> + status = nvmet_auth_success1(req, d, al);
> + if (status) {
> + req->sq->dhchap_status = status;
> + nvmet_auth_failure1(req, d, al);
> + pr_warn("ctrl %d qid %d: success1 status (%x)\n",
> + ctrl->cntlid, req->sq->qid,
> + req->sq->dhchap_status);
> + break;
> + }
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
> + break;
> + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1:
> + nvmet_auth_failure1(req, d, al);
> + pr_warn("ctrl %d qid %d failure1 (%x)\n",
> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_status);
> + break;
> + default:
> + pr_warn("ctrl %d qid %d unhandled step (%d)\n",
> + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
> + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
> + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
> + nvmet_auth_failure1(req, d, al);
> + status = 0;
> + break;
> + }
> +
> + status = nvmet_copy_to_sgl(req, 0, d, al);
> + kfree(d);
> +done:
> + req->cqe->result.u64 = 0;
> + nvmet_req_complete(req, status);
> + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
> + kfree(req->sq->dhchap_c1);
> + kfree(req->sq->dhchap_c2);
> + nvmet_ctrl_fatal_error(ctrl);
> + }
> +}
> diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c
> index 7d0f3523fdab..53fb853cd8fe 100644
> --- a/drivers/nvme/target/fabrics-cmd.c
> +++ b/drivers/nvme/target/fabrics-cmd.c
> @@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)
> case nvme_fabrics_type_property_get:
> req->execute = nvmet_execute_prop_get;
> break;
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + case nvme_fabrics_type_auth_send:
> + req->execute = nvmet_execute_auth_send;
> + break;
> + case nvme_fabrics_type_auth_receive:
> + req->execute = nvmet_execute_auth_receive;
> + break;
> +#endif
> default:
> pr_debug("received unknown capsule type 0x%x\n",
> cmd->fabrics.fctype);
> @@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
> struct nvmf_connect_data *d;
> struct nvmet_ctrl *ctrl = NULL;
> u16 status = 0;
> + int ret;
>
> if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data)))
> return;
> @@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
>
> uuid_copy(&ctrl->hostid, &d->hostid);
>
> + ret = nvmet_setup_auth(ctrl, req);
> + if (ret < 0) {
> + pr_err("Failed to setup authentication, error %d\n", ret);
> + nvmet_ctrl_put(ctrl);
> + if (ret == -EPERM)
> + status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
> + else
> + status = NVME_SC_INTERNAL;
> + goto out;
> + }
> +
> status = nvmet_install_queue(ctrl, req);
> if (status) {
> nvmet_ctrl_put(ctrl);
> goto out;
> }
>
> - pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
> + pr_info("creating controller %d for subsystem %s for NQN %s%s%s.\n",
> ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
> - ctrl->pi_support ? " T10-PI is enabled" : "");
> + ctrl->pi_support ? " T10-PI is enabled" : "",
> + nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
> req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
>
> + if (nvmet_has_auth(ctrl))
> + nvmet_init_auth(ctrl, req);
> out:
> kfree(d);
> complete:
> @@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
> }
>
> pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
> + req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);

Is this related to the patch?

> + if (nvmet_has_auth(ctrl))
> + nvmet_init_auth(ctrl, req);
>
> out:
> kfree(d);
> diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
> index 06dd3d537f07..ef8815e137d7 100644
> --- a/drivers/nvme/target/nvmet.h
> +++ b/drivers/nvme/target/nvmet.h
> @@ -108,6 +108,20 @@ struct nvmet_sq {
> u16 size;
> u32 sqhd;
> bool sqhd_disabled;
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + bool authenticated;
> + u16 dhchap_tid;
> + u16 dhchap_status;
> + int dhchap_step;
> + u8 dhchap_hash_id;
> + u8 dhchap_hash_len;
> + u8 *dhchap_c1;
> + u8 *dhchap_c2;
> + u32 dhchap_s1;
> + u32 dhchap_s2;
> + u8 *dhchap_skey;
> + int dhchap_skey_len;
> +#endif
> struct completion free_done;
> struct completion confirm_done;
> };
> @@ -209,6 +223,15 @@ struct nvmet_ctrl {
> u64 err_counter;
> struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS];
> bool pi_support;
> +#ifdef CONFIG_NVME_TARGET_AUTH
> + u32 dhchap_seqnum;
> + u8 *dhchap_key;
> + size_t dhchap_key_len;
> + struct crypto_shash *shash_tfm;
> + struct crypto_kpp *dh_tfm;
> + u32 dh_gid;
> + u32 dh_keysize;
> +#endif
> };
>
> struct nvmet_subsys {
> @@ -270,6 +293,10 @@ static inline struct nvmet_subsys *namespaces_to_subsys(
>
> struct nvmet_host {
> struct config_group group;
> + u8 *dhchap_secret;
> + u8 dhchap_key_hash;
> + u8 dhchap_hash_id;
> + u8 dhchap_dhgroup_id;
> };
>
> static inline struct nvmet_host *to_host(struct config_item *item)
> @@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio)
> bio_put(bio);
> }
>
> +#ifdef CONFIG_NVME_TARGET_AUTH
> +void nvmet_execute_auth_send(struct nvmet_req *req);
> +void nvmet_execute_auth_receive(struct nvmet_req *req);
> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret);
> +int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl);
> +void nvmet_auth_sq_free(struct nvmet_sq *sq);
> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id);
> +bool nvmet_check_auth_status(struct nvmet_req *req);
> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
> + unsigned int hash_len);
> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
> + unsigned int hash_len);
> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
> +{
> + return ctrl->shash_tfm != NULL;
> +}
> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
> + u8 *buf, int buf_size);
> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
> + u8 *buf, int buf_size);
> +#else
> +static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl,
> + struct nvmet_req *req)
> +{
> + return 0;
> +}
> +static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl,
> + struct nvmet_req *req) {};
> +static inline void nvmet_reset_auth(struct nvmet_ctrl *ctrl) {};
> +static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
> +static inline bool nvmet_check_auth_status(struct nvmet_req *req)
> +{
> + return true;
> +}
> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
> +{
> + return false;
> +}
> +static inline const char *nvmet_dhchap_dhgroup_name(int dhgid) { return NULL; }
> +#endif
> +
> #endif /* _NVMET_H */
>

2021-07-20 06:09:10

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/19/21 10:38 PM, Sagi Grimberg wrote:
>
>
> On 7/16/21 4:04 AM, Hannes Reinecke wrote:
>> Implement support for NVMe-oF In-Band authentication. This patch
>> adds two additional configfs entries 'dhchap_key' and 'dhchap_hash'
>> to the 'host' configfs directory. The 'dhchap_key' needs to be
>> specified in the format outlined in the base spec.
>> Augmented challenge support is not implemented, and concatenation
>> with TLS encryption is not supported.
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>>   drivers/nvme/target/Kconfig            |  10 +
>>   drivers/nvme/target/Makefile           |   1 +
>>   drivers/nvme/target/admin-cmd.c        |   4 +
>>   drivers/nvme/target/auth.c             | 352 +++++++++++++++++++
>>   drivers/nvme/target/configfs.c         |  71 +++-
>>   drivers/nvme/target/core.c             |   8 +
>>   drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
>>   drivers/nvme/target/fabrics-cmd.c      |  30 +-
>>   drivers/nvme/target/nvmet.h            |  71 ++++
>>   9 files changed, 1004 insertions(+), 3 deletions(-)
>>   create mode 100644 drivers/nvme/target/auth.c
>>   create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
>>
>> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
>> index 4be2ececbc45..d5656ef1559e 100644
>> --- a/drivers/nvme/target/Kconfig
>> +++ b/drivers/nvme/target/Kconfig
>> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
>>         devices over TCP.
>>           If unsure, say N.
>> +
>> +config NVME_TARGET_AUTH
>> +    bool "NVMe over Fabrics In-band Authentication support"
>> +    depends on NVME_TARGET
>> +    select CRYPTO_SHA256
>> +    select CRYPTO_SHA512
>> +    help
>> +      This enables support for NVMe over Fabrics In-band Authentication
>> +
>> +      If unsure, say N.
>> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
>> index 9837e580fa7e..c66820102493 100644
>> --- a/drivers/nvme/target/Makefile
>> +++ b/drivers/nvme/target/Makefile
>> @@ -13,6 +13,7 @@ nvmet-y        += core.o configfs.o admin-cmd.o
>> fabrics-cmd.o \
>>               discovery.o io-cmd-file.o io-cmd-bdev.o
>>   nvmet-$(CONFIG_NVME_TARGET_PASSTHRU)    += passthru.o
>>   nvmet-$(CONFIG_BLK_DEV_ZONED)        += zns.o
>> +nvmet-$(CONFIG_NVME_TARGET_AUTH)    += fabrics-cmd-auth.o auth.o
>>   nvme-loop-y    += loop.o
>>   nvmet-rdma-y    += rdma.o
>>   nvmet-fc-y    += fc.o
>> diff --git a/drivers/nvme/target/admin-cmd.c
>> b/drivers/nvme/target/admin-cmd.c
>> index 0cb98f2bbc8c..320cefc64ee0 100644
>> --- a/drivers/nvme/target/admin-cmd.c
>> +++ b/drivers/nvme/target/admin-cmd.c
>> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
>>         if (nvme_is_fabrics(cmd))
>>           return nvmet_parse_fabrics_cmd(req);
>> +
>> +    if (unlikely(!nvmet_check_auth_status(req)))
>> +        return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
>> +
>>       if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
>>           return nvmet_parse_discovery_cmd(req);
>>   diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
>> new file mode 100644
>> index 000000000000..00c7d051dfb1
>> --- /dev/null
>> +++ b/drivers/nvme/target/auth.c
>> @@ -0,0 +1,352 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
>> + * All rights reserved.
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/err.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include <crypto/dh.h>
>> +#include <crypto/ffdhe.h>
>> +#include <linux/crc32.h>
>> +#include <linux/base64.h>
>> +#include <linux/ctype.h>
>> +#include <linux/random.h>
>> +#include <asm/unaligned.h>
>> +
>> +#include "nvmet.h"
>> +#include "../host/auth.h"
>> +
>> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
>> +{
>> +    if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
>> +        return -EINVAL;
>> +    if (host->dhchap_key_hash > 3) {
>> +        pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
>> +             host->dhchap_key_hash);
>> +        return -EINVAL;
>> +    }
>> +    if (host->dhchap_key_hash > 0) {
>> +        /* Validate selected hash algorithm */
>> +        const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
>> +
>> +        if (!crypto_has_shash(hmac, 0, 0)) {
>> +            pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
>
> pr_err
>
>> +            host->dhchap_key_hash = -1;
>> +            return -EAGAIN;
>
> Why EAGAIN?
>

What else? ENOTSUPP?

>> +        }
>> +        /* Use this hash as default */
>> +        if (!host->dhchap_hash_id)
>> +            host->dhchap_hash_id = host->dhchap_key_hash;
>
> Why?
>

Because there is no mechanism how the controller selects the DHCHAP hmac
algorithm.
The host will send a list of supported hmac algorithms, and the
controller has to pick one of them.

And as we are sure that the hmac algorithm from the PSK will be
supported on the controller I set that as default (if nothing was
specified otherwise).

>> +    }
>> +    host->dhchap_secret = kstrdup(secret, GFP_KERNEL);
>> +    if (!host->dhchap_secret)
>> +        return -ENOMEM;
>> +    /* Default to SHA256 */
>> +    if (!host->dhchap_hash_id)
>> +        host->dhchap_hash_id = NVME_AUTH_DHCHAP_HASH_SHA256;
>
> What is the thought here?
>

This case is triggered when the user specifies a PSK without a hmac
algorithm (ie DHHC-1:00:XXXXX).
Then the above mechanism doesn't work, but we still have to specify a
default HMAC algorithm such that the selection mechanism can work.

>> +
>> +    pr_debug("Using hash %s\n",
>> +         nvme_auth_hmac_name(host->dhchap_hash_id));
>> +    return 0;
>> +}
>> +
>> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id)
>> +{
>> +    int ret = -ENOTSUPP;
>> +
>> +    if (dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_NULL)
>> +        return 0;
>> +
>> +    return ret;
>> +}
>> +
>> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
>> +{
>> +    int ret = 0;
>> +    struct nvmet_host_link *p;
>> +    struct nvmet_host *host = NULL;
>> +    const char *hash_name;
>> +
>> +    down_read(&nvmet_config_sem);
>> +    if (ctrl->subsys->type == NVME_NQN_DISC)
>> +        goto out_unlock;
>> +
>> +    list_for_each_entry(p, &ctrl->subsys->hosts, entry) {
>> +        pr_debug("check %s\n", nvmet_host_name(p->host));
>> +        if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn))
>> +            continue;
>> +        host = p->host;
>> +        break;
>> +    }
>> +    if (!host) {
>> +        pr_debug("host %s not found\n", ctrl->hostnqn);
>> +        ret = -EPERM;
>
> I think you should propogate the nvme status code instead...
>

I _thought_ it got translated into one; but yeah, can do.

>> +        goto out_unlock;
>> +    }
>> +    if (!host->dhchap_secret) {
>> +        pr_debug("No authentication provided\n");
>> +        goto out_unlock;
>> +    }
>> +
>> +    hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
>> +    if (!hash_name) {
>
> Can this actually happen?
>

Good question. I don't think so; will be changing it into a WARN_ON().

>> +        pr_debug("Hash ID %d invalid\n", host->dhchap_hash_id);
>
> warning, not debug.
>

See above. Yes.

>> +        ret = -EINVAL;
>> +        goto out_unlock;
>> +    }
>> +    ctrl->shash_tfm = crypto_alloc_shash(hash_name, 0,
>> +                         CRYPTO_ALG_ALLOCATES_MEMORY);
>> +    if (IS_ERR(ctrl->shash_tfm)) {
>> +        pr_debug("failed to allocate shash %s\n", hash_name);
>> +        ret = PTR_ERR(ctrl->shash_tfm);
>> +        ctrl->shash_tfm = NULL;
>> +        goto out_unlock;
>> +    }
>> +
>> +    ctrl->dhchap_key = nvme_auth_extract_secret(host->dhchap_secret,
>> +                            &ctrl->dhchap_key_len);
>> +    if (IS_ERR(ctrl->dhchap_key)) {
>> +        pr_debug("failed to extract host key, error %d\n", ret);
>> +        ret = PTR_ERR(ctrl->dhchap_key);
>> +        ctrl->dhchap_key = NULL;
>> +        goto out_free_hash;
>> +    }
>> +    if (host->dhchap_key_hash) {
>> +        struct crypto_shash *key_tfm;
>> +
>> +        hash_name = nvme_auth_hmac_name(host->dhchap_key_hash);
>> +        key_tfm = crypto_alloc_shash(hash_name, 0, 0);
>> +        if (IS_ERR(key_tfm)) {
>> +            ret = PTR_ERR(key_tfm);
>> +            goto out_free_hash;
>> +        } else {
>> +            SHASH_DESC_ON_STACK(shash, key_tfm);
>> +
>> +            shash->tfm = key_tfm;
>> +            ret = crypto_shash_setkey(key_tfm, ctrl->dhchap_key,
>> +                          ctrl->dhchap_key_len);
>> +            crypto_shash_init(shash);
>> +            crypto_shash_update(shash, ctrl->subsys->subsysnqn,
>> +                        strlen(ctrl->subsys->subsysnqn));
>> +            crypto_shash_update(shash, "NVMe-over-Fabrics", 17);
>> +            crypto_shash_final(shash, ctrl->dhchap_key);
>> +            crypto_free_shash(key_tfm);
>> +        }
>> +    }
>> +    pr_debug("%s: using key %*ph\n", __func__,
>> +         (int)ctrl->dhchap_key_len, ctrl->dhchap_key);
>> +    ret = crypto_shash_setkey(ctrl->shash_tfm, ctrl->dhchap_key,
>> +                  ctrl->dhchap_key_len);
>> +out_free_hash:
>> +    if (ret) {
>> +        if (ctrl->dhchap_key) {
>> +            kfree(ctrl->dhchap_key);
>> +            ctrl->dhchap_key = NULL;
>> +        }
>> +        crypto_free_shash(ctrl->shash_tfm);
>> +        ctrl->shash_tfm = NULL;
>> +    }
>> +out_unlock:
>> +    up_read(&nvmet_config_sem);
>> +
>> +    return ret;
>> +}
>> +
>> +void nvmet_auth_sq_free(struct nvmet_sq *sq)
>> +{
>> +    if (sq->dhchap_c1)
>> +        kfree(sq->dhchap_c1);
>
> just kfree, no need to if
>

Yeah.

>> +    if (sq->dhchap_c2)
>> +        kfree(sq->dhchap_c2);
>> +    if (sq->dhchap_skey)
>> +        kfree(sq->dhchap_skey);
>> +}
>> +
>> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl)
>
> Shouldn't this be nvmet_destroy_auth? reset indicates
> it can be reused again...
>

Oh, it should. I've coded nvmet_destroy_auth() pretty late in the game,
so I missed that one.

>> +{
>> +    if (ctrl->shash_tfm) {
>> +        crypto_free_shash(ctrl->shash_tfm);
>> +        ctrl->shash_tfm = NULL;
>> +    }
>> +    if (ctrl->dh_tfm) {
>> +        crypto_free_kpp(ctrl->dh_tfm);
>> +        ctrl->dh_tfm = NULL;
>> +    }
>> +    if (ctrl->dhchap_key) {
>> +        kfree(ctrl->dhchap_key);
>> +        ctrl->dhchap_key = NULL;
>> +    }
>> +}
>> +
>> +bool nvmet_check_auth_status(struct nvmet_req *req)
>> +{
>> +    if (req->sq->ctrl->shash_tfm &&
>> +        !req->sq->authenticated)
>> +        return false;
>> +    return true;
>> +}
>> +
>> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
>> +             unsigned int shash_len)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
>> +    u8 *challenge = req->sq->dhchap_c1;
>> +    u8 buf[4];
>> +    int ret;
>> +
>> +    if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> +        ret = -ENOTSUPP;
>> +        goto out;
>> +    }
>> +
>> +    shash->tfm = ctrl->shash_tfm;
>> +    ret = crypto_shash_init(shash);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, challenge, shash_len);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le32(req->sq->dhchap_s1, buf);
>> +    ret = crypto_shash_update(shash, buf, 4);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le16(req->sq->dhchap_tid, buf);
>> +    ret = crypto_shash_update(shash, buf, 2);
>> +    if (ret)
>> +        goto out;
>> +    memset(buf, 0, 4);
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, "HostHost", 8);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->hostnqn,
>> strlen(ctrl->hostnqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->subsysnqn,
>> +                  strlen(ctrl->subsysnqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_final(shash, response);
>> +out:
>> +    if (challenge != req->sq->dhchap_c1)
>> +        kfree(challenge);
>
> What about actually failing?
>

Ho-hum. Of course.

>> +    return 0;
>> +}
>> +
>> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
>> +             unsigned int shash_len)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    SHASH_DESC_ON_STACK(shash, ctrl->shash_tfm);
>> +    u8 *challenge = req->sq->dhchap_c2;
>> +    u8 buf[4];
>> +    int ret;
>> +
>> +    pr_debug("%s: ctrl %d hash seq %d transaction %u\n", __func__,
>> +         ctrl->cntlid, req->sq->dhchap_s2, req->sq->dhchap_tid);
>> +    pr_debug("%s: ctrl %d challenge %*ph\n", __func__,
>> +         ctrl->cntlid, shash_len, req->sq->dhchap_c2);
>> +    pr_debug("%s: ctrl %d subsysnqn %s\n", __func__,
>> +         ctrl->cntlid, ctrl->subsysnqn);
>> +    pr_debug("%s: ctrl %d hostnqn %s\n", __func__,
>> +         ctrl->cntlid, ctrl->hostnqn);
>> +
>> +    if (ctrl->dh_gid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> +        ret = -ENOTSUPP;
>> +        goto out;
>> +    }
>> +
>> +    shash->tfm = ctrl->shash_tfm;
>> +    ret = crypto_shash_init(shash);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, challenge, shash_len);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le32(req->sq->dhchap_s2, buf);
>> +    ret = crypto_shash_update(shash, buf, 4);
>> +    if (ret)
>> +        goto out;
>> +    put_unaligned_le16(req->sq->dhchap_tid, buf);
>> +    ret = crypto_shash_update(shash, buf, 2);
>> +    if (ret)
>> +        goto out;
>> +    memset(buf, 0, 4);
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, "Controller", 10);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->subsysnqn,
>> +                strlen(ctrl->subsysnqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, buf, 1);
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_update(shash, ctrl->hostnqn,
>> strlen(ctrl->hostnqn));
>> +    if (ret)
>> +        goto out;
>> +    ret = crypto_shash_final(shash, response);
>> +out:
>> +    if (challenge != req->sq->dhchap_c2)
>> +        kfree(challenge);
>> +    return 0;
>> +}
>> +
>> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
>> +                u8 *pkey, int pkey_size)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    struct kpp_request *kpp_req;
>> +    struct crypto_wait wait;
>> +    struct scatterlist src, dst;
>> +    int ret;
>> +
>> +    req->sq->dhchap_skey_len =
>> +        nvme_auth_dhgroup_privkey_size(ctrl->dh_gid);
>> +    req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len,
>> GFP_KERNEL);
>> +    if (!req->sq->dhchap_skey)
>> +        return -ENOMEM;
>> +    kpp_req = kpp_request_alloc(ctrl->dh_tfm, GFP_KERNEL);
>> +    if (!kpp_req) {
>> +        kfree(req->sq->dhchap_skey);
>> +        req->sq->dhchap_skey = NULL;
>> +        return -ENOMEM;
>> +    }
>> +
>> +    pr_debug("%s: host public key %*ph\n", __func__,
>> +         (int)pkey_size, pkey);
>> +    crypto_init_wait(&wait);
>> +    sg_init_one(&src, pkey, pkey_size);
>> +    kpp_request_set_input(kpp_req, &src, pkey_size);
>> +    sg_init_one(&dst, req->sq->dhchap_skey,
>> +        req->sq->dhchap_skey_len);
>> +    kpp_request_set_output(kpp_req, &dst, req->sq->dhchap_skey_len);
>> +    kpp_request_set_callback(kpp_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> +                 crypto_req_done, &wait);
>> +
>> +    ret = crypto_wait_req(crypto_kpp_compute_shared_secret(kpp_req),
>> &wait);
>> +    kpp_request_free(kpp_req);
>> +    if (ret)
>> +        pr_debug("failed to compute shared secred, err %d\n", ret);
>> +    else
>> +        pr_debug("%s: shared secret %*ph\n", __func__,
>> +             (int)req->sq->dhchap_skey_len,
>> +             req->sq->dhchap_skey);
>> +
>> +    return ret;
>> +}
>> diff --git a/drivers/nvme/target/configfs.c
>> b/drivers/nvme/target/configfs.c
>> index 273555127188..e0760911a761 100644
>> --- a/drivers/nvme/target/configfs.c
>> +++ b/drivers/nvme/target/configfs.c
>> @@ -11,8 +11,13 @@
>>   #include <linux/ctype.h>
>>   #include <linux/pci.h>
>>   #include <linux/pci-p2pdma.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>>     #include "nvmet.h"
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +#include "../host/auth.h"
>> +#endif
>>     static const struct config_item_type nvmet_host_type;
>>   static const struct config_item_type nvmet_subsys_type;
>> @@ -1656,10 +1661,71 @@ static const struct config_item_type
>> nvmet_ports_type = {
>>   static struct config_group nvmet_subsystems_group;
>>   static struct config_group nvmet_ports_group;
>>   -static void nvmet_host_release(struct config_item *item)
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +static ssize_t nvmet_host_dhchap_key_show(struct config_item *item,
>> +        char *page)
>> +{
>> +    u8 *dhchap_secret = to_host(item)->dhchap_secret;
>> +
>> +    if (!dhchap_secret)
>> +        return sprintf(page, "\n");
>> +    return sprintf(page, "%s\n", dhchap_secret);
>> +}
>> +
>> +static ssize_t nvmet_host_dhchap_key_store(struct config_item *item,
>> +        const char *page, size_t count)
>>   {
>>       struct nvmet_host *host = to_host(item);
>> +    int ret;
>>   +    ret = nvmet_auth_set_host_key(host, page);
>> +    if (ret < 0)
>> +        return ret;
>> +    return count;
>> +}
>> +
>> +CONFIGFS_ATTR(nvmet_host_, dhchap_key);
>> +
>> +static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item,
>> +        char *page)
>> +{
>> +    struct nvmet_host *host = to_host(item);
>> +    const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id);
>> +
>> +    return sprintf(page, "%s\n", hash_name ? hash_name : "none");
>> +}
>> +
>> +static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,
>> +        const char *page, size_t count)
>> +{
>> +    struct nvmet_host *host = to_host(item);
>> +    int hmac_id;
>> +
>> +    hmac_id = nvme_auth_hmac_id(page);
>> +    if (hmac_id < 0)
>> +        return -EINVAL;
>> +    if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0))
>> +        return -ENOTSUPP;
>> +    host->dhchap_hash_id = hmac_id;
>> +    return count;
>> +}
>> +
>> +CONFIGFS_ATTR(nvmet_host_, dhchap_hash);
>> +
>> +static struct configfs_attribute *nvmet_host_attrs[] = {
>> +    &nvmet_host_attr_dhchap_key,
>> +    &nvmet_host_attr_dhchap_hash,
>> +    NULL,
>> +};
>> +#endif /* CONFIG_NVME_TARGET_AUTH */
>> +
>> +static void nvmet_host_release(struct config_item *item)
>> +{
>> +    struct nvmet_host *host = to_host(item);
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +    if (host->dhchap_secret)
>> +        kfree(host->dhchap_secret);
>
> No need for if condition.
>

Ok.

>> +#endif
>>       kfree(host);
>>   }
>>   @@ -1669,6 +1735,9 @@ static struct configfs_item_operations
>> nvmet_host_item_ops = {
>>     static const struct config_item_type nvmet_host_type = {
>>       .ct_item_ops        = &nvmet_host_item_ops,
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +    .ct_attrs        = nvmet_host_attrs,
>> +#endif
>>       .ct_owner        = THIS_MODULE,
>>   };
>>   diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
>> index 163f7dc1a929..b5d7971f566b 100644
>> --- a/drivers/nvme/target/core.c
>> +++ b/drivers/nvme/target/core.c
>> @@ -793,6 +793,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
>>       wait_for_completion(&sq->confirm_done);
>>       wait_for_completion(&sq->free_done);
>>       percpu_ref_exit(&sq->ref);
>> +    nvmet_auth_sq_free(sq);
>>         if (ctrl) {
>>           /*
>> @@ -1264,6 +1265,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req)
>>                  req->cmd->common.opcode, req->sq->qid);
>>           return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR;
>>       }
>> +
>> +    if (unlikely(!nvmet_check_auth_status(req))) {
>> +        pr_warn("qid %d not authenticated\n", req->sq->qid);
>> +        return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
>> +    }
>>       return 0;
>>   }
>>   @@ -1456,6 +1462,8 @@ static void nvmet_ctrl_free(struct kref *ref)
>>       flush_work(&ctrl->async_event_work);
>>       cancel_work_sync(&ctrl->fatal_err_work);
>>   +    nvmet_reset_auth(ctrl);
>> +
>>       ida_simple_remove(&cntlid_ida, ctrl->cntlid);
>>         nvmet_async_events_free(ctrl);
>> diff --git a/drivers/nvme/target/fabrics-cmd-auth.c
>> b/drivers/nvme/target/fabrics-cmd-auth.c
>> new file mode 100644
>> index 000000000000..962f9f5e9d89
>> --- /dev/null
>> +++ b/drivers/nvme/target/fabrics-cmd-auth.c
>> @@ -0,0 +1,460 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * NVMe over Fabrics DH-HMAC-CHAP authentication command handling.
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
>> + * All rights reserved.
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +#include <linux/blkdev.h>
>> +#include <linux/random.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include "nvmet.h"
>> +#include "../host/auth.h"
>> +
>> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
>> +{
>> +    /* Initialize in-band authentication */
>> +    req->sq->authenticated = false;
>> +    req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
>> +    req->cqe->result.u32 |= 0x2 << 16;
>
> Can you add a define for this: NVME_CONNECT_AUTHREQ_INBAND
>

Sure.

>> +}
>> +
>> +static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    struct nvmf_auth_dhchap_negotiate_data *data = d;
>> +    int i, hash_id, null_dh = -1;
>> +
>> +    pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d
>> halen %d dhlen %d\n",
>> +         __func__, ctrl->cntlid, req->sq->qid,
>> +         data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
>> +         data->auth_protocol[0].dhchap.halen,
>> +         data->auth_protocol[0].dhchap.dhlen);
>> +    req->sq->dhchap_tid = le16_to_cpu(data->t_id);
>> +    if (data->sc_c)
>> +        return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
>> +
>> +    if (data->napd != 1)
>> +        return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +
>> +    if (data->auth_protocol[0].dhchap.authid != 0x01)
>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +
>> +    hash_id = nvme_auth_hmac_id(crypto_shash_alg_name(ctrl->shash_tfm));
>> +    for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
>> +        pr_debug("%s: ctrl %d qid %d checking hash %d for %d\n",
>> +             __func__, ctrl->cntlid, req->sq->qid,
>> +             data->auth_protocol[0].dhchap.idlist[i], hash_id);
>> +        if (hash_id != data->auth_protocol[0].dhchap.idlist[i])
>> +            continue;
>> +        req->sq->dhchap_hash_id = hash_id;
>> +        req->sq->dhchap_hash_len =
>> crypto_shash_digestsize(ctrl->shash_tfm);
>> +        break;
>> +    }
>> +    if (req->sq->dhchap_hash_id == 0) {
>> +        pr_debug("%s: ctrl %d qid %d: no usable hash found\n",
>> +             __func__, ctrl->cntlid, req->sq->qid);
>> +        return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +    }
>> +
>> +    for (i = data->auth_protocol[0].dhchap.halen;
>> +         i < data->auth_protocol[0].dhchap.halen +
>> +             data->auth_protocol[0].dhchap.dhlen; i++) {
>> +        int dhgid = data->auth_protocol[0].dhchap.idlist[i];
>> +
>> +        if (dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> +            null_dh = dhgid;
>> +            continue;
>> +        }
>> +        if (nvmet_setup_dhgroup(ctrl, dhgid) == 0)
>> +            break;
>> +    }
>> +    if (!ctrl->dh_tfm && null_dh < 0) {
>> +        pr_debug("%s: ctrl %d qid %d: no DH group selected\n",
>> +             __func__, ctrl->cntlid, req->sq->qid);
>> +        return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> +    }
>> +    if (ctrl->dh_gid == -1) {
>> +        ctrl->dh_gid = null_dh;
>> +        ctrl->dh_tfm = NULL;
>> +    }
>> +    pr_debug("%s: ctrl %d qid %d: DH group %s (%d)\n",
>> +         __func__, ctrl->cntlid, req->sq->qid,
>> +         nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
>> +    return 0;
>> +}
>> +
>> +static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    struct nvmf_auth_dhchap_reply_data *data = d;
>> +    u8 *response;
>> +
>> +    pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %d\n",
>> +         __func__, ctrl->cntlid, req->sq->qid,
>> +         data->hl, data->cvalid, data->dhvlen);
>> +    if (data->hl != req->sq->dhchap_hash_len)
>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +
>> +    if (data->dhvlen) {
>> +        return NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +    }
>> +
>> +    response = kmalloc(data->hl, GFP_KERNEL);
>> +    if (!response)
>> +        return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> +
>> +    if (nvmet_auth_host_hash(req, response, data->hl) < 0) {
>> +        pr_debug("ctrl %d qid %d DH-HMAC-CHAP hash failed\n",
>> +             ctrl->cntlid, req->sq->qid);
>> +        kfree(response);
>> +        return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> +    }
>> +
>> +    if (memcmp(data->rval, response, data->hl)) {
>> +        pr_info("ctrl %d qid %d DH-HMAC-CHAP response mismatch\n",
>> +            ctrl->cntlid, req->sq->qid);
>> +        kfree(response);
>> +        return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> +    }
>> +    kfree(response);
>> +    pr_info("ctrl %d qid %d DH-HMAC-CHAP host authenticated\n",
>> +        ctrl->cntlid, req->sq->qid);
>> +    if (data->cvalid) {
>> +        req->sq->dhchap_c2 = kmalloc(data->hl, GFP_KERNEL);
>> +        if (!req->sq->dhchap_c2)
>> +            return NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> +        memcpy(req->sq->dhchap_c2, data->rval + data->hl, data->hl);
>> +
>> +        pr_debug("ctrl %d qid %d challenge %*ph\n",
>> +             ctrl->cntlid, req->sq->qid, data->hl,
>> +             req->sq->dhchap_c2);
>> +        req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
>> +    } else
>> +        req->sq->dhchap_c2 = NULL;
>> +
>> +    return 0;
>> +}
>> +
>> +static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d)
>> +{
>> +    struct nvmf_auth_dhchap_failure_data *data = d;
>> +
>> +    return data->reason_code_explanation;
>> +}
>> +
>> +void nvmet_execute_auth_send(struct nvmet_req *req)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    struct nvmf_auth_dhchap_success2_data *data;
>> +    void *d;
>> +    u32 tl;
>> +    u16 status = 0;
>> +
>> +    if (req->cmd->auth_send.secp !=
>> NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_send_command, secp);
>> +        goto done;
>> +    }
>> +    if (req->cmd->auth_send.spsp0 != 0x01) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_send_command, spsp0);
>> +        goto done;
>> +    }
>> +    if (req->cmd->auth_send.spsp1 != 0x01) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_send_command, spsp1);
>> +        goto done;
>> +    }
>> +    tl = le32_to_cpu(req->cmd->auth_send.tl);
>> +    if (!tl) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_send_command, tl);
>> +        goto done;
>> +    }
>> +    if (!nvmet_check_transfer_len(req, tl)) {
>> +        pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl);
>> +        return;
>> +    }
>> +
>> +    d = kmalloc(tl, GFP_KERNEL);
>> +    if (!d) {
>> +        status = NVME_SC_INTERNAL;
>> +        goto done;
>> +    }
>> +
>> +    status = nvmet_copy_from_sgl(req, 0, d, tl);
>> +    if (status) {
>> +        kfree(d);
>> +        goto done;
>> +    }
>> +
>
> This whole block below should move to something like
> nvmet_process_auth_send_data()
>

Ok.

>> +    data = d;
>> +    pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__,
>> +         ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id,
>> +         req->sq->dhchap_step);
>> +    if (data->auth_type != NVME_AUTH_COMMON_MESSAGES &&
>> +        data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) {
>> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +        req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> +    } else if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) {
>> +        if (data->auth_id != req->sq->dhchap_step) {
>> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +            req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> +        } else if (data->auth_id !=
>> NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) {
>> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +            req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> +        } else {
>> +            /* Validate negotiation parameters */
>> +            status = nvmet_auth_negotiate(req, d);
>> +            if (status == 0)
>> +                req->sq->dhchap_step =
>> NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
>> +            else {
>> +                req->sq->dhchap_step =
>> NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +                req->sq->dhchap_status = status;
>> +                status = 0;
>> +            }
>> +        }
>> +    } else if (data->auth_type == NVME_AUTH_DHCHAP_MESSAGES) {
>> +        if (data->auth_id != req->sq->dhchap_step) {
>> +            pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n",
>> +                 __func__, ctrl->cntlid, req->sq->qid,
>> +                 data->auth_id, req->sq->dhchap_step);
>> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +            req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> +        } else if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) {
>> +            pr_debug("%s: ctrl %d qid %d invalid transaction %d
>> (expected %d)\n",
>> +                 __func__, ctrl->cntlid, req->sq->qid,
>> +                 le16_to_cpu(data->t_id),
>> +                 req->sq->dhchap_tid);
>> +            req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +            req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +        } else {
>> +            switch (data->auth_id) {
>> +            case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
>> +                status = nvmet_auth_reply(req, d);
>> +                if (status == 0)
>> +                    req->sq->dhchap_step =
>> NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
>> +                else {
>> +                    req->sq->dhchap_step =
>> NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +                    req->sq->dhchap_status = status;
>> +                    status = 0;
>> +                }
>> +                break;
>> +            case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
>> +                req->sq->authenticated = true;
>> +                pr_debug("%s: ctrl %d qid %d authenticated\n",
>> +                     __func__, ctrl->cntlid, req->sq->qid);
>> +                break;
>> +            case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2:
>> +                status = nvmet_auth_failure2(req, d);
>> +                if (status) {
>> +                    pr_warn("ctrl %d qid %d: DH-HMAC-CHAP negotiation
>> failed (%d)\n",
>> +                        ctrl->cntlid, req->sq->qid,
>> +                        status);
>> +                    req->sq->dhchap_status = status;
>> +                    status = 0;
>> +                }
>> +                break;
>> +            default:
>> +                req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> +                req->sq->dhchap_step =
>> NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> +                break;
>> +            }
>> +        }
>> +    } else {
>> +        req->sq->dhchap_status =
>> NVME_AUTH_DHCHAP_FAILURE_INVALID_MESSAGE;
>> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;
>> +    }
>> +    kfree(d);
>> +done:
>> +    pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__,
>> +         ctrl->cntlid, req->sq->qid,
>> +         req->sq->dhchap_status, req->sq->dhchap_step);
>> +    if (status)
>> +        pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n",
>> +             __func__, ctrl->cntlid, req->sq->qid,
>> +             status, req->error_loc);
>> +    req->cqe->result.u64 = 0;
>> +    nvmet_req_complete(req, status);
>> +    if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 &&
>> +        req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
>> +        return;
>> +    /* Final states, clear up variables */
>> +    kfree(req->sq->dhchap_c1);
>> +    kfree(req->sq->dhchap_c2);
>> +    if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2)
>> +        nvmet_ctrl_fatal_error(ctrl);
>> +}
>> +
>> +static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
>
> nvmet_auth_set_challenge
>

Ok.

>> +{
>> +    struct nvmf_auth_dhchap_challenge_data *data = d;
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    int ret = 0;
>> +    int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
>> +
>> +    if (al < data_size) {
>> +        pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
>> +             al, data_size);
>> +        return -EINVAL;
>> +    }
>> +    memset(data, 0, data_size);
>> +    req->sq->dhchap_s1 = ctrl->dhchap_seqnum++;
>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
>> +    data->t_id = cpu_to_le16(req->sq->dhchap_tid);
>> +    data->hashid = req->sq->dhchap_hash_id;
>> +    data->hl = req->sq->dhchap_hash_len;
>> +    data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
>> +    req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
>> +    if (!req->sq->dhchap_c1)
>> +        return -ENOMEM;
>> +    get_random_bytes(req->sq->dhchap_c1, data->hl);
>> +    memcpy(data->cval, req->sq->dhchap_c1, data->hl);
>> +    pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen
>> %d\n",
>> +         __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1,
>> +         req->sq->dhchap_tid, data->hl, data->dhvlen);
>> +    return ret;
>> +}
>> +
>> +static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al)
>> +{
>> +    struct nvmf_auth_dhchap_success1_data *data = d;
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +
>> +    WARN_ON(al < sizeof(*data));
>> +    memset(data, 0, sizeof(*data));
>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
>> +    data->t_id = cpu_to_le16(req->sq->dhchap_tid);
>> +    data->hl = req->sq->dhchap_hash_len;
>> +    if (req->sq->dhchap_c2) {
>> +        if (nvmet_auth_ctrl_hash(req, data->rval, data->hl))
>> +            return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
>> +        data->rvalid = 1;
>> +        pr_debug("ctrl %d qid %d response %*ph\n",
>> +             ctrl->cntlid, req->sq->qid, data->hl, data->rval);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
>> +{
>> +    struct nvmf_auth_dhchap_failure_data *data = d;
>> +
>> +    WARN_ON(al < sizeof(*data));
>> +    data->auth_type = NVME_AUTH_COMMON_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +    data->t_id = cpu_to_le32(req->sq->dhchap_tid);
>> +    data->reason_code = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED;
>> +    data->reason_code_explanation = req->sq->dhchap_status;
>> +}
>> +
>> +void nvmet_execute_auth_receive(struct nvmet_req *req)
>> +{
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    void *d;
>> +    u32 al;
>> +    u16 status = 0;
>> +
>> +    if (req->cmd->auth_receive.secp !=
>> NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_receive_command, secp);
>> +        goto done;
>> +    }
>> +    if (req->cmd->auth_receive.spsp0 != 0x01) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_receive_command, spsp0);
>> +        goto done;
>> +    }
>> +    if (req->cmd->auth_receive.spsp1 != 0x01) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_receive_command, spsp1);
>> +        goto done;
>> +    }
>> +    al = le32_to_cpu(req->cmd->auth_receive.al);
>> +    if (!al) {
>> +        status = NVME_SC_INVALID_FIELD | NVME_SC_DNR;
>> +        req->error_loc =
>> +            offsetof(struct nvmf_auth_receive_command, al);
>> +        goto done;
>> +    }
>> +    if (!nvmet_check_transfer_len(req, al)) {
>> +        pr_debug("%s: transfer length mismatch (%u)\n", __func__, al);
>> +        return;
>> +    }
>> +
>> +    d = kmalloc(al, GFP_KERNEL);
>> +    if (!d) {
>> +        status = NVME_SC_INTERNAL;
>> +        goto done;
>> +    }
>> +    pr_debug("%s: ctrl %d qid %d step %x\n", __func__,
>> +         ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
>> +    switch (req->sq->dhchap_step) {
>> +    case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
>> +        status = nvmet_auth_challenge(req, d, al);
>> +        if (status < 0) {
>> +            pr_warn("ctrl %d qid %d: challenge error (%d)\n",
>> +                ctrl->cntlid, req->sq->qid, status);
>> +            status = NVME_SC_INTERNAL;
>> +            break;
>> +        }
>> +        if (status) {
>> +            req->sq->dhchap_status = status;
>> +            nvmet_auth_failure1(req, d, al);
>> +            pr_warn("ctrl %d qid %d: challenge status (%x)\n",
>> +                ctrl->cntlid, req->sq->qid,
>> +                req->sq->dhchap_status);
>> +            status = 0;
>> +            break;
>> +        }
>> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY;
>> +        break;
>> +    case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
>> +        status = nvmet_auth_success1(req, d, al);
>> +        if (status) {
>> +            req->sq->dhchap_status = status;
>> +            nvmet_auth_failure1(req, d, al);
>> +            pr_warn("ctrl %d qid %d: success1 status (%x)\n",
>> +                ctrl->cntlid, req->sq->qid,
>> +                req->sq->dhchap_status);
>> +            break;
>> +        }
>> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2;
>> +        break;
>> +    case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1:
>> +        nvmet_auth_failure1(req, d, al);
>> +        pr_warn("ctrl %d qid %d failure1 (%x)\n",
>> +            ctrl->cntlid, req->sq->qid, req->sq->dhchap_status);
>> +        break;
>> +    default:
>> +        pr_warn("ctrl %d qid %d unhandled step (%d)\n",
>> +            ctrl->cntlid, req->sq->qid, req->sq->dhchap_step);
>> +        req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;
>> +        req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED;
>> +        nvmet_auth_failure1(req, d, al);
>> +        status = 0;
>> +        break;
>> +    }
>> +
>> +    status = nvmet_copy_to_sgl(req, 0, d, al);
>> +    kfree(d);
>> +done:
>> +    req->cqe->result.u64 = 0;
>> +    nvmet_req_complete(req, status);
>> +    if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) {
>> +        kfree(req->sq->dhchap_c1);
>> +        kfree(req->sq->dhchap_c2);
>> +        nvmet_ctrl_fatal_error(ctrl);
>> +    }
>> +}
>> diff --git a/drivers/nvme/target/fabrics-cmd.c
>> b/drivers/nvme/target/fabrics-cmd.c
>> index 7d0f3523fdab..53fb853cd8fe 100644
>> --- a/drivers/nvme/target/fabrics-cmd.c
>> +++ b/drivers/nvme/target/fabrics-cmd.c
>> @@ -93,6 +93,14 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req)
>>       case nvme_fabrics_type_property_get:
>>           req->execute = nvmet_execute_prop_get;
>>           break;
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +    case nvme_fabrics_type_auth_send:
>> +        req->execute = nvmet_execute_auth_send;
>> +        break;
>> +    case nvme_fabrics_type_auth_receive:
>> +        req->execute = nvmet_execute_auth_receive;
>> +        break;
>> +#endif
>>       default:
>>           pr_debug("received unknown capsule type 0x%x\n",
>>               cmd->fabrics.fctype);
>> @@ -155,6 +163,7 @@ static void nvmet_execute_admin_connect(struct
>> nvmet_req *req)
>>       struct nvmf_connect_data *d;
>>       struct nvmet_ctrl *ctrl = NULL;
>>       u16 status = 0;
>> +    int ret;
>>         if (!nvmet_check_transfer_len(req, sizeof(struct
>> nvmf_connect_data)))
>>           return;
>> @@ -197,17 +206,31 @@ static void nvmet_execute_admin_connect(struct
>> nvmet_req *req)
>>         uuid_copy(&ctrl->hostid, &d->hostid);
>>   +    ret = nvmet_setup_auth(ctrl, req);
>> +    if (ret < 0) {
>> +        pr_err("Failed to setup authentication, error %d\n", ret);
>> +        nvmet_ctrl_put(ctrl);
>> +        if (ret == -EPERM)
>> +            status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR);
>> +        else
>> +            status = NVME_SC_INTERNAL;
>> +        goto out;
>> +    }
>> +
>>       status = nvmet_install_queue(ctrl, req);
>>       if (status) {
>>           nvmet_ctrl_put(ctrl);
>>           goto out;
>>       }
>>   -    pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
>> +    pr_info("creating controller %d for subsystem %s for NQN %s%s%s.\n",
>>           ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
>> -        ctrl->pi_support ? " T10-PI is enabled" : "");
>> +        ctrl->pi_support ? " T10-PI is enabled" : "",
>> +        nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
>>       req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
>>   +    if (nvmet_has_auth(ctrl))
>> +        nvmet_init_auth(ctrl, req);
>>   out:
>>       kfree(d);
>>   complete:
>> @@ -267,6 +290,9 @@ static void nvmet_execute_io_connect(struct
>> nvmet_req *req)
>>       }
>>         pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid);
>> +    req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
>
> Is this related to the patch?
>

Have to check. I thought it did, but now that you mention it...

>> +    if (nvmet_has_auth(ctrl))
>> +        nvmet_init_auth(ctrl, req);
>>     out:
>>       kfree(d);
>> diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
>> index 06dd3d537f07..ef8815e137d7 100644
>> --- a/drivers/nvme/target/nvmet.h
>> +++ b/drivers/nvme/target/nvmet.h
>> @@ -108,6 +108,20 @@ struct nvmet_sq {
>>       u16            size;
>>       u32            sqhd;
>>       bool            sqhd_disabled;
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +    bool            authenticated;
>> +    u16            dhchap_tid;
>> +    u16            dhchap_status;
>> +    int            dhchap_step;
>> +    u8            dhchap_hash_id;
>> +    u8            dhchap_hash_len;
>> +    u8            *dhchap_c1;
>> +    u8            *dhchap_c2;
>> +    u32            dhchap_s1;
>> +    u32            dhchap_s2;
>> +    u8            *dhchap_skey;
>> +    int            dhchap_skey_len;
>> +#endif
>>       struct completion    free_done;
>>       struct completion    confirm_done;
>>   };
>> @@ -209,6 +223,15 @@ struct nvmet_ctrl {
>>       u64            err_counter;
>>       struct nvme_error_slot    slots[NVMET_ERROR_LOG_SLOTS];
>>       bool            pi_support;
>> +#ifdef CONFIG_NVME_TARGET_AUTH
>> +    u32            dhchap_seqnum;
>> +    u8            *dhchap_key;
>> +    size_t            dhchap_key_len;
>> +    struct crypto_shash    *shash_tfm;
>> +    struct crypto_kpp    *dh_tfm;
>> +    u32            dh_gid;
>> +    u32            dh_keysize;
>> +#endif
>>   };
>>     struct nvmet_subsys {
>> @@ -270,6 +293,10 @@ static inline struct nvmet_subsys
>> *namespaces_to_subsys(
>>     struct nvmet_host {
>>       struct config_group    group;
>> +    u8            *dhchap_secret;
>> +    u8            dhchap_key_hash;
>> +    u8            dhchap_hash_id;
>> +    u8            dhchap_dhgroup_id;
>>   };
>>     static inline struct nvmet_host *to_host(struct config_item *item)
>> @@ -659,4 +686,48 @@ static inline void nvmet_req_bio_put(struct
>> nvmet_req *req, struct bio *bio)
>>           bio_put(bio);
>>   }
>>   +#ifdef CONFIG_NVME_TARGET_AUTH
>> +void nvmet_execute_auth_send(struct nvmet_req *req);
>> +void nvmet_execute_auth_receive(struct nvmet_req *req);
>> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char
>> *secret);
>> +int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash);
>> +int nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
>> +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req);
>> +void nvmet_reset_auth(struct nvmet_ctrl *ctrl);
>> +void nvmet_auth_sq_free(struct nvmet_sq *sq);
>> +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, int dhgroup_id);
>> +bool nvmet_check_auth_status(struct nvmet_req *req);
>> +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
>> +             unsigned int hash_len);
>> +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
>> +             unsigned int hash_len);
>> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
>> +{
>> +    return ctrl->shash_tfm != NULL;
>> +}
>> +int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
>> +                u8 *buf, int buf_size);
>> +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
>> +                u8 *buf, int buf_size);
>> +#else
>> +static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl,
>> +                   struct nvmet_req *req)
>> +{
>> +    return 0;
>> +}
>> +static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl,
>> +                   struct nvmet_req *req) {};
>> +static inline void nvmet_reset_auth(struct nvmet_ctrl *ctrl) {};
>> +static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
>> +static inline bool nvmet_check_auth_status(struct nvmet_req *req)
>> +{
>> +    return true;
>> +}
>> +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
>> +{
>> +    return false;
>> +}
>> +static inline const char *nvmet_dhchap_dhgroup_name(int dhgid) {
>> return NULL; }
>> +#endif
>> +
>>   #endif /* _NVMET_H */
>>
>

Thanks for the review!

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-20 10:16:29

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/19/21 1:52 PM, Stephan Mueller wrote:
> Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
>> On 7/19/21 12:19 PM, Stephan Mueller wrote:
>>> Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
>>>> On 7/19/21 10:51 AM, Stephan Mueller wrote:
[ .. ]
>>>>>
>>>>> Thank you for clarifying that. It sounds to me that there is no
>>>>> defined protocol (or if there, I would be wondering how the code would have
>>>>> worked
>>>>> with a different implementation). Would it make sense to first specify
>>>>> a protocol for authentication and have it discussed? I personally think
>>>>> it is a bit difficult to fully understand the protocol from the code and
>>>>> discuss protocol-level items based on the code.
>>>>>
>>>> Oh, the protocol _is_ specified:
>>>>
>>>>
>>>> https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
>>>>
>>>> It's just that I have issues translating that spec onto what the kernel
>>>> provides.
>>>
>>> according to the naming conventions there in figures 447 and following:
>>>
>>> - x and y: DH private key (kernel calls it secret set with dh_set_secret
>>> or
>>> encoded into param.key)
>>>
>>
>> But that's were I got confused; one needs a private key here, but there
>> is no obvious candidate for it. But reading it more closely I guess the
>> private key is just a random number (cf the spec: g^y mod p, where y is
>> a random number selected by the host that shall be at least 256 bits
>> long). So I'll fix it up with the next round.
>
> Here comes the crux: the kernel has an ECC private key generation function
> ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL key, but it
> has no FFC-DH counterpart.
>
> That said, generating a random number is the most obvious choice, but not the
> right one.
>
> The correct one would be following SP800-56A rev 3 and here either section
> 5.6.1.1.3 or 5.6.1.1.4.
>
Hmm. Okay. But after having read section 5.6.1.1.4, I still do have some
questions.

Assume we will be using a bit length of 512 for FFDHE, then we will
trivially pass Step 2 for all supported FFDHE groups (the maximum
symmetric-equivalent strength for ffdhe8192 is 192 bits).

From my understanding, the random number generator will fill out all
available bytes in the string (and nothing more), so we trivially
satisfy step 3 and 4.

And as q is always larger than the random number, step 6 reduces to
'if (c > 2^N - 2)', ie we just need to check if the random number is a
string of 0xff characters. Which hardly is a random number at all, so
it'll be impossible to get this.

Which then would mean that our 'x' is simply the random number + 1,
which arguably is slightly pointless (one more than a random number is
as random as the number itself), so I do feel justified with just
returning a random number here.

Am I wrong with that reasoning?

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-20 10:57:04

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On Tue, 2021-07-20 at 12:14 +0200, Hannes Reinecke wrote:
> On 7/19/21 1:52 PM, Stephan Mueller wrote:
> > Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
> > > On 7/19/21 12:19 PM, Stephan Mueller wrote:
> > > > Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
> > > > > On 7/19/21 10:51 AM, Stephan Mueller wrote:
> [ .. ]
> > > > > >
> > > > > > Thank you for clarifying that. It sounds to me that there is no
> > > > > > defined protocol (or if there, I would be wondering how the code would have
> > > > > > worked
> > > > > > with a different implementation). Would it make sense to first specify
> > > > > > a protocol for authentication and have it discussed? I personally think
> > > > > > it is a bit difficult to fully understand the protocol from the code and
> > > > > > discuss protocol-level items based on the code.
> > > > > >
> > > > > Oh, the protocol _is_ specified:
> > > > >
> > > > >
> > > > > https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
> > > > >
> > > > > It's just that I have issues translating that spec onto what the kernel
> > > > > provides.
> > > >
> > > > according to the naming conventions there in figures 447 and following:
> > > >
> > > > - x and y: DH private key (kernel calls it secret set with dh_set_secret
> > > > or
> > > > encoded into param.key)
> > > >
> > >
> > > But that's were I got confused; one needs a private key here, but there
> > > is no obvious candidate for it. But reading it more closely I guess the
> > > private key is just a random number (cf the spec: g^y mod p, where y is
> > > a random number selected by the host that shall be at least 256 bits
> > > long). So I'll fix it up with the next round.
> >
> > Here comes the crux: the kernel has an ECC private key generation function
> > ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL key, but it
> > has no FFC-DH counterpart.
> >
> > That said, generating a random number is the most obvious choice, but not the
> > right one.
> >
> > The correct one would be following SP800-56A rev 3 and here either section
> > 5.6.1.1.3 or 5.6.1.1.4.
> >
> Hmm. Okay. But after having read section 5.6.1.1.4, I still do have some
> questions.
>
> Assume we will be using a bit length of 512 for FFDHE, then we will
> trivially pass Step 2 for all supported FFDHE groups (the maximum
> symmetric-equivalent strength for ffdhe8192 is 192 bits).

N = 512 is not a good choice, minimum length these days for DH should
be 2048 or more.

> From my understanding, the random number generator will fill out all
> available bytes in the string (and nothing more), so we trivially
> satisfy step 3 and 4.
>
> And as q is always larger than the random number, step 6 reduces to
> 'if (c > 2^N - 2)',

Where is this coming from ?
It seem you assume M = 2^N but M = min(2^N, q)

The point here is to make sure the number X you return is:
0 < X < (q-1)

> ie we just need to check if the random number is a
> string of 0xff characters. Which hardly is a random number at all, so
> it'll be impossible to get this.
>
> Which then would mean that our 'x' is simply the random number + 1,

This is an artifact due to the random number being 0 <= c < 2^N - 1,
therefore 1 needs to be added to make sure you never return 0.

> which arguably is slightly pointless (one more than a random number is
> as random as the number itself), so I do feel justified with just
> returning a random number here.
>
> Am I wrong with that reasoning?

Looks to me you are not accounting for the fact that N = 512 is too
small and a random number falling in the interval (q - 2) < X < 2^N is
unsuitable?

Simo.

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc




2021-07-20 11:32:28

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On 7/20/21 12:49 PM, Simo Sorce wrote:
> On Tue, 2021-07-20 at 12:14 +0200, Hannes Reinecke wrote:
>> On 7/19/21 1:52 PM, Stephan Mueller wrote:
>>> Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
>>>> On 7/19/21 12:19 PM, Stephan Mueller wrote:
>>>>> Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
>>>>>> On 7/19/21 10:51 AM, Stephan Mueller wrote:
>> [ .. ]
>>>>>>>
>>>>>>> Thank you for clarifying that. It sounds to me that there is no
>>>>>>> defined protocol (or if there, I would be wondering how the code would have
>>>>>>> worked
>>>>>>> with a different implementation). Would it make sense to first specify
>>>>>>> a protocol for authentication and have it discussed? I personally think
>>>>>>> it is a bit difficult to fully understand the protocol from the code and
>>>>>>> discuss protocol-level items based on the code.
>>>>>>>
>>>>>> Oh, the protocol _is_ specified:
>>>>>>
>>>>>>
>>>>>> https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
>>>>>>
>>>>>> It's just that I have issues translating that spec onto what the kernel
>>>>>> provides.
>>>>>
>>>>> according to the naming conventions there in figures 447 and following:
>>>>>
>>>>> - x and y: DH private key (kernel calls it secret set with dh_set_secret
>>>>> or
>>>>> encoded into param.key)
>>>>>
>>>>
>>>> But that's were I got confused; one needs a private key here, but there
>>>> is no obvious candidate for it. But reading it more closely I guess the
>>>> private key is just a random number (cf the spec: g^y mod p, where y is
>>>> a random number selected by the host that shall be at least 256 bits
>>>> long). So I'll fix it up with the next round.
>>>
>>> Here comes the crux: the kernel has an ECC private key generation function
>>> ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL key, but it
>>> has no FFC-DH counterpart.
>>>
>>> That said, generating a random number is the most obvious choice, but not the
>>> right one.
>>>
>>> The correct one would be following SP800-56A rev 3 and here either section
>>> 5.6.1.1.3 or 5.6.1.1.4.
>>>
>> Hmm. Okay. But after having read section 5.6.1.1.4, I still do have some
>> questions.
>>
>> Assume we will be using a bit length of 512 for FFDHE, then we will
>> trivially pass Step 2 for all supported FFDHE groups (the maximum
>> symmetric-equivalent strength for ffdhe8192 is 192 bits).
>
> N = 512 is not a good choice, minimum length these days for DH should
> be 2048 or more.
>

According to RFC7919:
Peers using ffdhe8192 that want to optimize their key exchange with a
short exponent (Section 5.2) should choose a secret key of at least
400 bits.

So what is wrong with 512 bits?

>> From my understanding, the random number generator will fill out all
>> available bytes in the string (and nothing more), so we trivially
>> satisfy step 3 and 4.
>>
>> And as q is always larger than the random number, step 6 reduces to
>> 'if (c > 2^N - 2)',
>
> Where is this coming from ?
> It seem you assume M = 2^N but M = min(2^N, q)
>
> The point here is to make sure the number X you return is:
> 0 < X < (q-1)
>

Which is what I've tried to argue. For 512 bits private key and the
smallest possible FFDHE group (which has 2048 bits, with the top bit
non-zero) 2^N is always smaller than (q - 1).
As the other FFHDE groups are using even larger 'q' values, this is true
for all FFHDE groups.

>> ie we just need to check if the random number is a
>> string of 0xff characters. Which hardly is a random number at all, so
>> it'll be impossible to get this.
>>
>> Which then would mean that our 'x' is simply the random number + 1,
>
> This is an artifact due to the random number being 0 <= c < 2^N - 1,
> therefore 1 needs to be added to make sure you never return 0.
>

And my argument here is that all zeros (and all ones) are not a value I
would expect from our RNG.

>> which arguably is slightly pointless (one more than a random number is
>> as random as the number itself), so I do feel justified with just
>> returning a random number here.
>>
>> Am I wrong with that reasoning?
>
> Looks to me you are not accounting for the fact that N = 512 is too
> small and a random number falling in the interval (q - 2) < X < 2^N is
> unsuitable?
>

Only if (q - 2) < 2^N. And my point is that it's not.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-20 13:14:37

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 07/11] nvme-auth: augmented challenge support

On 7/19/21 11:21 AM, Sagi Grimberg wrote:
>
>
> On 7/16/21 4:04 AM, Hannes Reinecke wrote:
>> Implement support for augmented challenge using FFDHE groups.
>
> Please some more info for the change log...
>
>>
>> Signed-off-by: Hannes Reinecke <[email protected]>
>> ---
>>   drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
>>   1 file changed, 371 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
>> index 448a3adebea6..754343aced19 100644
>> --- a/drivers/nvme/host/auth.c
>> +++ b/drivers/nvme/host/auth.c
[ .. ]
>> @@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct
>> nvme_ctrl *ctrl,
>>           return -EPROTO;
>>       }
>>       if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {
>> -        chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> -        return -EPROTO;
>> -    }
>> -    if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen
>> != 0) {
>> +        if (data->dhvlen == 0) {
>> +            dev_warn(ctrl->device,
>> +                 "qid %d: DH-HMAC-CHAP: empty DH value\n",
>> +                 chap->qid);
>> +            chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> +            return -EPROTO;
>> +        }
>> +        chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
>> +        if (IS_ERR(chap->dh_tfm)) {
>> +            dev_warn(ctrl->device,
>> +                 "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
>> +                 chap->qid, gid_name);
>> +            chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
>> +            chap->dh_tfm = NULL;
>> +            return -EPROTO;
>
> Why not propogate the error?
>

Will be doing so.

>> +        }
>> +        chap->dhgroup_id = data->dhgid;
>> +    } else if (data->dhvlen != 0) {
>>           dev_warn(ctrl->device,
>>                "qid %d: DH-HMAC-CHAP: invalid DH value for NULL DH\n",
>>               chap->qid);
>> @@ -313,6 +419,16 @@ static int nvme_auth_dhchap_challenge(struct
>> nvme_ctrl *ctrl,
>>       chap->hash_len = data->hl;
>>       chap->s1 = le32_to_cpu(data->seqnum);
>>       memcpy(chap->c1, data->cval, chap->hash_len);
>> +    if (data->dhvlen) {
>> +        chap->ctrl_key = kmalloc(data->dhvlen, GFP_KERNEL);
>> +        if (!chap->ctrl_key)
>> +            return -ENOMEM;
>> +        chap->ctrl_key_len = data->dhvlen;
>> +        memcpy(chap->ctrl_key, data->cval + chap->hash_len,
>> +               data->dhvlen);
>> +        dev_dbg(ctrl->device, "ctrl public key %*ph\n",
>> +             (int)chap->ctrl_key_len, chap->ctrl_key);
>> +    }
>>         return 0;
>>   }
>> @@ -353,10 +469,13 @@ static int nvme_auth_dhchap_reply(struct
>> nvme_ctrl *ctrl,
>>           memcpy(data->rval + chap->hash_len, chap->c2,
>>                  chap->hash_len);
>>       }
>> -    if (chap->host_key_len)
>> +    if (chap->host_key_len) {
>> +        dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n",
>> +            __func__, chap->qid,
>> +            chap->host_key_len, chap->host_key);
>>           memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
>>                  chap->host_key_len);
>> -
>> +    }
>
> Is this change only adding the debug print?
>

Might. I'll check.

>>       return size;
>>   }
>>   @@ -440,23 +559,10 @@ static int nvme_auth_dhchap_failure2(struct
>> nvme_ctrl *ctrl,
>>   int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>>                 struct nvme_dhchap_context *chap)
>>   {
>> -    char *hash_name;
>> +    const char *hash_name, *digest_name;
>>       int ret;
>>   -    switch (chap->hash_id) {
>> -    case NVME_AUTH_DHCHAP_HASH_SHA256:
>> -        hash_name = "hmac(sha256)";
>> -        break;
>> -    case NVME_AUTH_DHCHAP_HASH_SHA384:
>> -        hash_name = "hmac(sha384)";
>> -        break;
>> -    case NVME_AUTH_DHCHAP_HASH_SHA512:
>> -        hash_name = "hmac(sha512)";
>> -        break;
>> -    default:
>> -        hash_name = NULL;
>> -        break;
>> -    }
>> +    hash_name = nvme_auth_hmac_name(chap->hash_id);
>>       if (!hash_name) {
>>           chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>>           return -EPROTO;
>> @@ -468,26 +574,100 @@ int nvme_auth_select_hash(struct nvme_ctrl *ctrl,
>>           chap->shash_tfm = NULL;
>>           return -EPROTO;
>>       }
>> +    digest_name = nvme_auth_digest_name(chap->hash_id);
>> +    if (!digest_name) {
>> +        crypto_free_shash(chap->shash_tfm);
>> +        chap->shash_tfm = NULL;
>> +        return -EPROTO;
>> +    }
>> +    chap->digest_tfm = crypto_alloc_shash(digest_name, 0, 0);
>> +    if (IS_ERR(chap->digest_tfm)) {
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        crypto_free_shash(chap->shash_tfm);
>> +        chap->shash_tfm = NULL;
>> +        chap->digest_tfm = NULL;
>> +        return -EPROTO;
>> +    }
>>       if (!chap->key) {
>>           dev_warn(ctrl->device, "qid %d: cannot select hash, no key\n",
>>                chap->qid);
>>           chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        crypto_free_shash(chap->digest_tfm);
>>           crypto_free_shash(chap->shash_tfm);
>>           chap->shash_tfm = NULL;
>> +        chap->digest_tfm = NULL;
>>           return -EINVAL;
>
> Please have a structured goto targets in reverse order, this repeated
> cleanup is a mess...
>

Already done.

>>       }
>>       ret = crypto_shash_setkey(chap->shash_tfm, chap->key,
>> chap->key_len);
>>       if (ret) {
>>           chap->status = NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE;
>> +        crypto_free_shash(chap->digest_tfm);
>>           crypto_free_shash(chap->shash_tfm);
>>           chap->shash_tfm = NULL;
>> +        chap->digest_tfm = NULL;
>>           return ret;
>>       }
>> -    dev_info(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>> -         chap->qid, hash_name);
>> +    dev_dbg(ctrl->device, "qid %d: DH-HMAC_CHAP: selected hash %s\n",
>> +        chap->qid, hash_name);
>>       return 0;
>>   }
>>   +static int nvme_auth_augmented_challenge(struct nvme_dhchap_context
>> *chap,
>> +                     u8 *challenge, u8 *aug)
>> +{
>> +    struct crypto_shash *tfm;
>> +    struct shash_desc *desc;
>> +    u8 *hashed_key;
>> +    const char *hash_name;
>> +    int ret;
>> +
>> +    hashed_key = kmalloc(chap->hash_len, GFP_KERNEL);
>> +    if (!hashed_key)
>> +        return -ENOMEM;
>> +
>> +    ret = crypto_shash_tfm_digest(chap->digest_tfm, chap->sess_key,
>> +                      chap->sess_key_len, hashed_key);
>> +    if (ret < 0) {
>> +        pr_debug("failed to hash session key, err %d\n", ret);
>> +        kfree(hashed_key);
>
> Same here...
>
>> +        return ret;
>> +    }
>
> Spaces between if conditions please?
>
>> +    hash_name = crypto_shash_alg_name(chap->shash_tfm);
>> +    if (!hash_name) {
>> +        pr_debug("Invalid hash algoritm\n");
>> +        return -EINVAL;
>> +    }
>> +    tfm = crypto_alloc_shash(hash_name, 0, 0);
>> +    if (IS_ERR(tfm)) {
>> +        ret = PTR_ERR(tfm);
>> +        goto out_free_key;
>> +    }
>> +    desc = kmalloc(sizeof(struct shash_desc) +
>> crypto_shash_descsize(tfm),
>> +               GFP_KERNEL);
>> +    if (!desc) {
>> +        ret = -ENOMEM;
>> +        goto out_free_hash;
>> +    }
>> +    desc->tfm = tfm;
>> +
>> +    ret = crypto_shash_setkey(tfm, hashed_key, chap->hash_len);
>> +    if (ret)
>> +        goto out_free_desc;
>> +    ret = crypto_shash_init(desc);
>> +    if (ret)
>> +        goto out_free_desc;
>> +    crypto_shash_update(desc, challenge, chap->hash_len);
>> +    crypto_shash_final(desc, aug);
>> +
>> +out_free_desc:
>> +    kfree_sensitive(desc);
>> +out_free_hash:
>> +    crypto_free_shash(tfm);
>> +out_free_key:
>> +    kfree(hashed_key);
>> +    return ret;
>> +}
>> +
>>   static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl,
>>                         struct nvme_dhchap_context *chap)
>>   {
>> @@ -497,6 +677,16 @@ static int nvme_auth_dhchap_host_response(struct
>> nvme_ctrl *ctrl,
>>         dev_dbg(ctrl->device, "%s: qid %d host response seq %d
>> transaction %d\n",
>>           __func__, chap->qid, chap->s1, chap->transaction);
>> +    if (chap->dh_tfm) {
>> +        challenge = kmalloc(chap->hash_len, GFP_KERNEL);
>> +        if (!challenge) {
>> +            ret = -ENOMEM;
>> +            goto out;
>> +        }
>> +        ret = nvme_auth_augmented_challenge(chap, chap->c1, challenge);
>> +        if (ret)
>> +            goto out;
>> +    }
>>       shash->tfm = chap->shash_tfm;
>>       ret = crypto_shash_init(shash);
>>       if (ret)
>> @@ -532,6 +722,8 @@ static int nvme_auth_dhchap_host_response(struct
>> nvme_ctrl *ctrl,
>>           goto out;
>>       ret = crypto_shash_final(shash, chap->response);
>>   out:
>> +    if (challenge != chap->c1)
>> +        kfree(challenge);
>>       return ret;
>>   }
>>   @@ -542,6 +734,17 @@ static int
>> nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,
>>       u8 buf[4], *challenge = chap->c2;
>>       int ret;
>>   +    if (chap->dh_tfm) {
>> +        challenge = kmalloc(chap->hash_len, GFP_KERNEL);
>> +        if (!challenge) {
>> +            ret = -ENOMEM;
>> +            goto out;
>> +        }
>> +        ret = nvme_auth_augmented_challenge(chap, chap->c2,
>> +                            challenge);
>> +        if (ret)
>> +            goto out;
>> +    }
>>       dev_dbg(ctrl->device, "%s: qid %d host response seq %d
>> transaction %d\n",
>>           __func__, chap->qid, chap->s2, chap->transaction);
>>       dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n",
>> @@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct
>> nvme_ctrl *ctrl,
>>           goto out;
>>       ret = crypto_shash_final(shash, chap->response);
>>   out:
>> +    if (challenge != chap->c2)
>> +        kfree(challenge);
>
> Just free ?! what about failing?
>

This is not an error condition, but rather the case when we need to
construct an augmented challenge; in that case we'll allocate a
temporary buffer in 'challenge', and copy it over into 'c2'.

>>       return ret;
>>   }
>>   @@ -644,10 +849,134 @@ int nvme_auth_generate_key(struct nvme_ctrl
>> *ctrl,
>>       return 0;
>>   }
>>   +static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl,
>> +                    struct nvme_dhchap_context *chap)
>> +{
>> +    struct kpp_request *req;
>> +    struct crypto_wait wait;
>> +    struct scatterlist src, dst;
>> +    u8 *pkey;
>> +    int ret, pkey_len;
>> +
>> +    if (chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_2048 ||
>> +        chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_3072 ||
>> +        chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_4096 ||
>> +        chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_6144 ||
>> +        chap->dhgroup_id == NVME_AUTH_DHCHAP_DHGROUP_8192) {
>> +        struct dh p = {0};
>> +        int pubkey_size =
>> nvme_auth_dhgroup_pubkey_size(chap->dhgroup_id);
>> +
>> +        ret = crypto_ffdhe_params(&p, pubkey_size << 3);
>> +        if (ret) {
>> +            dev_dbg(ctrl->device,
>> +                "failed to generate ffdhe params, error %d\n",
>> +                ret);
>> +            return ret;
>> +        }
>> +        p.key = chap->key;
>> +        p.key_size = chap->key_len;
>> +
>> +        pkey_len = crypto_dh_key_len(&p);
>> +        pkey = kzalloc(pkey_len, GFP_KERNEL);
>> +
>> +        get_random_bytes(pkey, pkey_len);
>> +        ret = crypto_dh_encode_key(pkey, pkey_len, &p);
>> +        if (ret) {
>> +            dev_dbg(ctrl->device,
>> +                "failed to encode pkey, error %d\n", ret);
>> +            kfree(pkey);
>> +            return ret;
>> +        }
>> +        chap->host_key_len = pubkey_size;
>> +        chap->sess_key_len = pubkey_size;
>> +    } else {
>> +        dev_warn(ctrl->device, "Invalid DH group id %d\n",
>> +             chap->dhgroup_id);
>> +        chap->status = NVME_AUTH_DHCHAP_FAILURE_INVALID_PAYLOAD;
>> +        return -EINVAL;
>> +    }
>> +
>> +    ret = crypto_kpp_set_secret(chap->dh_tfm, pkey, pkey_len);
>> +    if (ret) {
>> +        dev_dbg(ctrl->dev, "failed to set secret, error %d\n", ret);
>> +        kfree(pkey);
>> +        return ret;
>> +    }
>> +    req = kpp_request_alloc(chap->dh_tfm, GFP_KERNEL);
>> +    if (!req) {
>> +        ret = -ENOMEM;
>> +        goto out_free_exp;
>> +    }
>> +
>> +    chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL);
>> +    if (!chap->host_key) {
>> +        ret = -ENOMEM;
>> +        goto out_free_req;
>> +    }
>> +    crypto_init_wait(&wait);
>> +    kpp_request_set_input(req, NULL, 0);
>> +    sg_init_one(&dst, chap->host_key, chap->host_key_len);
>> +    kpp_request_set_output(req, &dst, chap->host_key_len);
>> +    kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
>> +                 crypto_req_done, &wait);
>> +
>> +    ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait);
>> +    if (ret == -EOVERFLOW) {
>> +        dev_dbg(ctrl->dev,
>> +            "public key buffer too small, wants %d is %d\n",
>> +            crypto_kpp_maxsize(chap->dh_tfm), chap->host_key_len);
>> +        goto out_free_host;
>
> Is this a specific retcode of intereset? Why did you specifically add
> special casing here?
>

Because that's the specific error code from the DH code, indicating that
the length isn't correct. And I needed that during development of the
FFDHE code.
But yeah, it can be removed.


Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-20 15:03:42

by Stephan Müller

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

Am Dienstag, dem 20.07.2021 um 10:44 -0400 schrieb Simo Sorce:
> On Tue, 2021-07-20 at 13:31 +0200, Hannes Reinecke wrote:
> > On 7/20/21 12:49 PM, Simo Sorce wrote:
> > > On Tue, 2021-07-20 at 12:14 +0200, Hannes Reinecke wrote:
> > > > On 7/19/21 1:52 PM, Stephan Mueller wrote:
> > > > > Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
> > > > > > On 7/19/21 12:19 PM, Stephan Mueller wrote:
> > > > > > > Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes
> > > > > > > Reinecke:
> > > > > > > > On 7/19/21 10:51 AM, Stephan Mueller wrote:
> > > > [ .. ]
> > > > > > > > >
> > > > > > > > > Thank you for clarifying that. It sounds to me that there is
> > > > > > > > > no
> > > > > > > > > defined protocol (or if there, I would be wondering how the
> > > > > > > > > code would have
> > > > > > > > > worked
> > > > > > > > > with a different implementation). Would it make sense to
> > > > > > > > > first specify
> > > > > > > > > a protocol for authentication and have it discussed? I
> > > > > > > > > personally think
> > > > > > > > > it is a bit difficult to fully understand the protocol from
> > > > > > > > > the code and
> > > > > > > > > discuss protocol-level items based on the code.
> > > > > > > > >
> > > > > > > > Oh, the protocol _is_ specified:
> > > > > > > >
> > > > > > > >  
> > > > > > > > https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
> > > > > > > >
> > > > > > > > It's just that I have issues translating that spec onto what
> > > > > > > > the kernel
> > > > > > > > provides.
> > > > > > >
> > > > > > > according to the naming conventions there in figures 447 and
> > > > > > > following:
> > > > > > >
> > > > > > > - x and y: DH private key (kernel calls it secret set with
> > > > > > > dh_set_secret
> > > > > > > or
> > > > > > > encoded into param.key)
> > > > > > >
> > > > > >
> > > > > > But that's were I got confused; one needs a private key here, but
> > > > > > there
> > > > > > is no obvious candidate for it. But reading it more closely I
> > > > > > guess the
> > > > > > private key is just a random number (cf the spec: g^y mod p, where
> > > > > > y is
> > > > > > a random number selected by the host that shall be at least 256
> > > > > > bits
> > > > > > long). So I'll fix it up with the next round.
> > > > >
> > > > > Here comes the crux: the kernel has an ECC private key generation
> > > > > function
> > > > > ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL
> > > > > key, but it
> > > > > has no FFC-DH counterpart.
> > > > >
> > > > > That said, generating a random number is the most obvious choice,
> > > > > but not the
> > > > > right one.
> > > > >
> > > > > The correct one would be following SP800-56A rev 3 and here either
> > > > > section
> > > > > 5.6.1.1.3 or 5.6.1.1.4.
> > > > >
> > > > Hmm. Okay. But after having read section 5.6.1.1.4, I still do have
> > > > some
> > > > questions.
> > > >
> > > > Assume we will be using a bit length of 512 for FFDHE, then we will
> > > > trivially pass Step 2 for all supported FFDHE groups (the maximum
> > > > symmetric-equivalent strength for ffdhe8192 is 192 bits).
> > >
> > > N = 512 is not a good choice, minimum length these days for DH should
> > > be 2048 or more.
> > >
> >
> > According to RFC7919:
> > Peers using ffdhe8192 that want to optimize their key exchange with a
> > short exponent (Section 5.2) should choose a secret key of at least
> > 400 bits.
> >
> > So what is wrong with 512 bits?
>
>
> RFC7519 is TLS Specific.
> I do not know if short-exponents are safe to use in all use cases.
>
> If it is safe, your choice is fine and your arguments will follow, but
> then a comment that explains the choice and warns about key checks if
> it is changed would be a good idea.
>
> Otherwise the default should be to use N = len(q), which implies the
> proper checks need to be applied.

Agreed.

Ciao
Stephan
>
> Simo.
>
> > > > From my understanding, the random number generator will fill out all
> > > > available bytes in the string (and nothing more), so we trivially
> > > > satisfy step 3 and 4.
> > > >
> > > > And as q is always larger than the random number, step 6 reduces to
> > > > 'if (c > 2^N - 2)',
> > >
> > > Where is this coming from ?
> > > It seem you assume M = 2^N but M = min(2^N, q)
> > >
> > > The point here is to make sure the number X you return is:
> > > 0 < X < (q-1)
> > >
> >
> > Which is what I've tried to argue. For 512 bits private key and the
> > smallest possible FFDHE group (which has 2048 bits, with the top bit
> > non-zero) 2^N is always smaller than (q - 1).
> > As the other FFHDE groups are using even larger 'q' values, this is true
> > for all FFHDE groups.
> >
> > > >  ie we just need to check if the random number is a
> > > > string of 0xff characters. Which hardly is a random number at all, so
> > > > it'll be impossible to get this.
> > > >
> > > > Which then would mean that our 'x' is simply the random number + 1,
> > >
> > > This is an artifact due to the random number being 0 <= c < 2^N - 1,
> > > therefore 1 needs to be added to make sure you never return 0.
> > >
> >
> > And my argument here is that all zeros (and all ones) are not a value I
> > would expect from our RNG.
> >
> > > > which arguably is slightly pointless (one more than a random number is
> > > > as random as the number itself), so I do feel justified with just
> > > > returning a random number here.
> > > >
> > > > Am I wrong with that reasoning?
> > >
> > > Looks to me you are not accounting for the fact that N = 512 is too
> > > small and a random number falling in the interval (q - 2) < X < 2^N is
> > > unsuitable?
> > >
> >
> > Only if (q - 2) < 2^N. And my point is that it's not.
> >
> > Cheers,
> >
> > Hannes
>


2021-07-20 15:04:18

by Simo Sorce

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication

On Tue, 2021-07-20 at 13:31 +0200, Hannes Reinecke wrote:
> On 7/20/21 12:49 PM, Simo Sorce wrote:
> > On Tue, 2021-07-20 at 12:14 +0200, Hannes Reinecke wrote:
> > > On 7/19/21 1:52 PM, Stephan Mueller wrote:
> > > > Am Montag, dem 19.07.2021 um 13:10 +0200 schrieb Hannes Reinecke:
> > > > > On 7/19/21 12:19 PM, Stephan Mueller wrote:
> > > > > > Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
> > > > > > > On 7/19/21 10:51 AM, Stephan Mueller wrote:
> > > [ .. ]
> > > > > > > >
> > > > > > > > Thank you for clarifying that. It sounds to me that there is no
> > > > > > > > defined protocol (or if there, I would be wondering how the code would have
> > > > > > > > worked
> > > > > > > > with a different implementation). Would it make sense to first specify
> > > > > > > > a protocol for authentication and have it discussed? I personally think
> > > > > > > > it is a bit difficult to fully understand the protocol from the code and
> > > > > > > > discuss protocol-level items based on the code.
> > > > > > > >
> > > > > > > Oh, the protocol _is_ specified:
> > > > > > >
> > > > > > >
> > > > > > > https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
> > > > > > >
> > > > > > > It's just that I have issues translating that spec onto what the kernel
> > > > > > > provides.
> > > > > >
> > > > > > according to the naming conventions there in figures 447 and following:
> > > > > >
> > > > > > - x and y: DH private key (kernel calls it secret set with dh_set_secret
> > > > > > or
> > > > > > encoded into param.key)
> > > > > >
> > > > >
> > > > > But that's were I got confused; one needs a private key here, but there
> > > > > is no obvious candidate for it. But reading it more closely I guess the
> > > > > private key is just a random number (cf the spec: g^y mod p, where y is
> > > > > a random number selected by the host that shall be at least 256 bits
> > > > > long). So I'll fix it up with the next round.
> > > >
> > > > Here comes the crux: the kernel has an ECC private key generation function
> > > > ecdh_set_secret triggered with crypto_kpp_set_secret using a NULL key, but it
> > > > has no FFC-DH counterpart.
> > > >
> > > > That said, generating a random number is the most obvious choice, but not the
> > > > right one.
> > > >
> > > > The correct one would be following SP800-56A rev 3 and here either section
> > > > 5.6.1.1.3 or 5.6.1.1.4.
> > > >
> > > Hmm. Okay. But after having read section 5.6.1.1.4, I still do have some
> > > questions.
> > >
> > > Assume we will be using a bit length of 512 for FFDHE, then we will
> > > trivially pass Step 2 for all supported FFDHE groups (the maximum
> > > symmetric-equivalent strength for ffdhe8192 is 192 bits).
> >
> > N = 512 is not a good choice, minimum length these days for DH should
> > be 2048 or more.
> >
>
> According to RFC7919:
> Peers using ffdhe8192 that want to optimize their key exchange with a
> short exponent (Section 5.2) should choose a secret key of at least
> 400 bits.
>
> So what is wrong with 512 bits?


RFC7519 is TLS Specific.
I do not know if short-exponents are safe to use in all use cases.

If it is safe, your choice is fine and your arguments will follow, but
then a comment that explains the choice and warns about key checks if
it is changed would be a good idea.

Otherwise the default should be to use N = len(q), which implies the
proper checks need to be applied.

Simo.

> > > From my understanding, the random number generator will fill out all
> > > available bytes in the string (and nothing more), so we trivially
> > > satisfy step 3 and 4.
> > >
> > > And as q is always larger than the random number, step 6 reduces to
> > > 'if (c > 2^N - 2)',
> >
> > Where is this coming from ?
> > It seem you assume M = 2^N but M = min(2^N, q)
> >
> > The point here is to make sure the number X you return is:
> > 0 < X < (q-1)
> >
>
> Which is what I've tried to argue. For 512 bits private key and the
> smallest possible FFDHE group (which has 2048 bits, with the top bit
> non-zero) 2^N is always smaller than (q - 1).
> As the other FFHDE groups are using even larger 'q' values, this is true
> for all FFHDE groups.
>
> > > ie we just need to check if the random number is a
> > > string of 0xff characters. Which hardly is a random number at all, so
> > > it'll be impossible to get this.
> > >
> > > Which then would mean that our 'x' is simply the random number + 1,
> >
> > This is an artifact due to the random number being 0 <= c < 2^N - 1,
> > therefore 1 needs to be added to make sure you never return 0.
> >
>
> And my argument here is that all zeros (and all ones) are not a value I
> would expect from our RNG.
>
> > > which arguably is slightly pointless (one more than a random number is
> > > as random as the number itself), so I do feel justified with just
> > > returning a random number here.
> > >
> > > Am I wrong with that reasoning?
> >
> > Looks to me you are not accounting for the fact that N = 512 is too
> > small and a random number falling in the interval (q - 2) < X < 2^N is
> > unsuitable?
> >
>
> Only if (q - 2) < 2^N. And my point is that it's not.
>
> Cheers,
>
> Hannes

--
Simo Sorce
RHEL Crypto Team
Red Hat, Inc




2021-07-20 20:50:30

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support

Hi,

Great to see those patches coming! After some review, they look to be
very well done. Some comments/suggestions below.

1. I strongly recommend to implement DH exponentials reuse (g x mod p /
g y mod p as well as g xy mod p) as specified in section 8.13.5.7
"DH-HMAC-CHAP Security Requirements". When I was working on TP 8006 I
had a prototype that demonstrated that DH math has quite significant
latency, something like (as far as I remember) 30ms for 4K group and few
hundreds of ms for 8K group. For single connection it is not a big deal,
but imagine AMD EPYC with 128 cores. Since all connections are created
sequentially, even with 30 ms per connection time to complete full
remote device connection would become 128*30 => almost 4 seconds. With
8K group it might be more than 10 seconds. Users are unlikely going to
be happy with this, especially in cases, when connecting multiple of
NVMe-oF devices is a part of a server or VM boot sequence.

If DH exponential reuse implemented, for all subsequent connections the
DH math is excluded, so authentication overhead becomes pretty much
negligible.

In my prototype I implemented DH exponential reuse as a simple
per-host/target cache that keeps DH exponentials (including g xy mod p)
for up to 10 seconds. Simple and sufficient.

Another, might be ever more significant reason why DH exponential reuse
is important is that without it x (or y on the host side) must always be
randomly generated each time a new connection is established. Which
means, for instance, for 8K groups for each connection 1KB of random
bytes must be taken from the random pool. With 128 connections it is now
128KB. Quite a big pressure on the random pool that DH exponential reuse
mostly avoids.

Those are the 2 reasons why we added this DH exponential reuse sentence
in the spec. In the original TP 8006 there was a small informative piece
explaining reasonings behind that, but for some reasons it was removed
from the final version.

2. What is the status of this code from perspective of stability in face
of malicious host behavior? Seems implementation is carefully done, but,
for instance, at the first look I was not able to find a code to clean
up if host in not acting for too long in the middle of exchange. Other
observation is that in nvmet_execute_auth_send()
nvmet_check_transfer_len() does not check if tl size is reasonable,
i.e., for instance, not 1GB.

For sure, we don't want to allow remote hosts to hang or crash target.
For instance, because of OOM conditions that happened, because malicious
host asked target to allocate too much memory or open to many being
authenticated connections in which the host is not going to reply in the
middle of exchange.

Asking, because don't want to go in my review too far ahead from the
author ;)

In this regard, it would be great if you add in your test application
ability to perform authentication with random parameters and randomly
stop responding. Overnight running of such test would give us good
degree of confidence that it will always work as expected.

Vlad

On 7/16/21 2:04 PM, Hannes Reinecke wrote:
> Hi all,
>
> recent updates to the NVMe spec have added definitions for in-band
> authentication, and seeing that it provides some real benefit especially
> for NVMe-TCP here's an attempt to implement it.
>
> Tricky bit here is that the specification orients itself on TLS 1.3,
> but supports only the FFDHE groups. Which of course the kernel doesn't
> support. I've been able to come up with a patch for this, but as this
> is my first attempt to fix anything in the crypto area I would invite
> people more familiar with these matters to have a look.
>
> Also note that this is just for in-band authentication. Secure concatenation
> (ie starting TLS with the negotiated parameters) is not implemented; one would
> need to update the kernel TLS implementation for this, which at this time is
> beyond scope.
>
> As usual, comments and reviews are welcome.
>
> Hannes Reinecke (11):
> crypto: add crypto_has_shash()
> crypto: add crypto_has_kpp()
> crypto/ffdhe: Finite Field DH Ephemeral Parameters
> lib/base64: RFC4648-compliant base64 encoding
> nvme: add definitions for NVMe In-Band authentication
> nvme: Implement In-Band authentication
> nvme-auth: augmented challenge support
> nvmet: Parse fabrics commands on all queues
> nvmet: Implement basic In-Band Authentication
> nvmet-auth: implement support for augmented challenge
> nvme: add non-standard ECDH and curve25517 algorithms
>
> crypto/Kconfig | 8 +
> crypto/Makefile | 1 +
> crypto/ffdhe_helper.c | 877 +++++++++++++++++
> crypto/kpp.c | 6 +
> crypto/shash.c | 6 +
> drivers/nvme/host/Kconfig | 11 +
> drivers/nvme/host/Makefile | 1 +
> drivers/nvme/host/auth.c | 1188 ++++++++++++++++++++++++
> drivers/nvme/host/auth.h | 23 +
> drivers/nvme/host/core.c | 77 +-
> drivers/nvme/host/fabrics.c | 65 +-
> drivers/nvme/host/fabrics.h | 8 +
> drivers/nvme/host/nvme.h | 15 +
> drivers/nvme/host/trace.c | 32 +
> drivers/nvme/target/Kconfig | 10 +
> drivers/nvme/target/Makefile | 1 +
> drivers/nvme/target/admin-cmd.c | 4 +
> drivers/nvme/target/auth.c | 608 ++++++++++++
> drivers/nvme/target/configfs.c | 102 +-
> drivers/nvme/target/core.c | 10 +
> drivers/nvme/target/fabrics-cmd-auth.c | 472 ++++++++++
> drivers/nvme/target/fabrics-cmd.c | 30 +-
> drivers/nvme/target/nvmet.h | 71 ++
> include/crypto/ffdhe.h | 24 +
> include/crypto/hash.h | 2 +
> include/crypto/kpp.h | 2 +
> include/linux/base64.h | 16 +
> include/linux/nvme.h | 187 +++-
> lib/Makefile | 2 +-
> lib/base64.c | 111 +++
> 30 files changed, 3961 insertions(+), 9 deletions(-)
> create mode 100644 crypto/ffdhe_helper.c
> create mode 100644 drivers/nvme/host/auth.c
> create mode 100644 drivers/nvme/host/auth.h
> create mode 100644 drivers/nvme/target/auth.c
> create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
> create mode 100644 include/crypto/ffdhe.h
> create mode 100644 include/linux/base64.h
> create mode 100644 lib/base64.c
>

2021-07-20 20:50:45

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication


On 7/18/21 3:21 PM, Hannes Reinecke wrote:
> On 7/17/21 9:22 AM, Sagi Grimberg wrote:
>>> Implement NVMe-oF In-Band authentication. This patch adds two new
>>> fabric options 'dhchap_key' to specify the PSK
>>
>> pre-shared-key.
>>
>> Also, we need a sysfs knob to rotate the key that will trigger
>> re-authentication or even a simple controller(s-plural) reset, so this
>> should go beyond just the connection string.
>>
>
> Yeah, re-authentication currently is not implemented. I first wanted to
> get this patchset out such that we can settle on the userspace interface
> (both from host and target).
> I'll have to think on how we should handle authentication; one of the
> really interesting cases would be when one malicious admin will _just_
> send a 'negotiate' command to the controller. As per spec the controller
> will be waiting for an 'authentication receive' command to send a
> 'challenge' payload back to the host. But that will never come, so as it
> stands currently the controller is required to abort the connection.
> Not very nice.

Yes, in this case after some reasonable timeout (I would suggest 10-15
seconds) the controller expected to abort connection and clean up all
allocated resources.

To handle DoS possibility to make too many such "orphan" negotiations,
hence consume all controller memory, some additional handling is needed.
For simplicity as a first step I would suggest to have a global limit on
number of currently being authenticated connections.

[...]

>>> +    chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
>>> +                         &chap->key_len);
>>> +    if (IS_ERR(chap->key)) {
>>> +        ret = PTR_ERR(chap->key);
>>> +        chap->key = NULL;
>>> +        return ret;
>>> +    }
>>> +
>>> +    if (key_hash == 0)
>>> +        return 0;
>>> +
>>> +    hmac_name = nvme_auth_hmac_name(key_hash);
>>> +    if (!hmac_name) {
>>> +        pr_debug("Invalid key hash id %d\n", key_hash);
>>> +        return -EKEYREJECTED;
>>> +    }
>>
>> Why does the user influence the hmac used? isn't that is driven
>> by the susbsystem?
>>
>> I don't think that the user should choose in this level.
>>
>
> That is another weirdness of the spec.
> The _secret_ will be hashed with a specific function, and that function
> is stated in the transport representation.
> (Cf section "DH-HMAC-CHAP Security Requirements").
> This is _not_ the hash function used by the authentication itself, which
> will be selected by the protocol.
> So it's not the user here, but rather the transport specification of the
> key which selects the hash algorithm.

Yes, good catch. It looks as a minor errata material to specify that
hash function here is implementation specific.

I would suggest to just hardcode SHA512 here. Users don't have to be
confused by this.

Vlad

2021-07-20 20:51:28

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication


On 7/16/21 2:04 PM, Hannes Reinecke wrote:

[...]

> +struct nvmet_dhchap_hash_map {
> + int id;
> + int hash_len;
> + const char hmac[15];
> + const char digest[15];
> +} hash_map[] = {
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
> + .hash_len = 32,
> + .hmac = "hmac(sha256)", .digest = "sha256" },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
> + .hash_len = 48,
> + .hmac = "hmac(sha384)", .digest = "sha384" },
> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
> + .hash_len = 64,
> + .hmac = "hmac(sha512)", .digest = "sha512" },
> +};

"hmac()" is always here, so why not to just auto-generate hmac(sha512)
from sha512?

Vlad

2021-07-20 20:52:06

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication


On 7/17/21 5:04 PM, Hannes Reinecke wrote:
> On 7/17/21 8:30 AM, Sagi Grimberg wrote:

[...]

>>> +
>>> +enum {
>>> +    NVME_AUTH_DHCHAP_HASH_SHA256    = 0x01,
>>
>> Maybe s/HASH/HF/ (stands for hash function, which is
>> a better description).
>
> Or HMAC, as this is what it's used for...

I would better do just NVME_AUTH_DHCHAP_SHA256. "HASH" is redundant
here. Better to keep it shorter.

Vlad

2021-07-21 06:08:09

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support

On 7/20/21 10:26 PM, Vladislav Bolkhovitin wrote:
> Hi,
>
> Great to see those patches coming! After some review, they look to be
> very well done. Some comments/suggestions below.
>
> 1. I strongly recommend to implement DH exponentials reuse (g x mod p /
> g y mod p as well as g xy mod p) as specified in section 8.13.5.7
> "DH-HMAC-CHAP Security Requirements". When I was working on TP 8006 I
> had a prototype that demonstrated that DH math has quite significant
> latency, something like (as far as I remember) 30ms for 4K group and few
> hundreds of ms for 8K group. For single connection it is not a big deal,
> but imagine AMD EPYC with 128 cores. Since all connections are created
> sequentially, even with 30 ms per connection time to complete full
> remote device connection would become 128*30 => almost 4 seconds. With
> 8K group it might be more than 10 seconds. Users are unlikely going to
> be happy with this, especially in cases, when connecting multiple of
> NVMe-oF devices is a part of a server or VM boot sequence.
>
Oh, indeed, I can confirm that. FFDHE calculations are quite time-consuming.
But incidentally, ECDH and curve25519 are reasonably fast, so maybe
there _is_ a value in having a TPAR asking for them to be specified, too ...

> If DH exponential reuse implemented, for all subsequent connections the
> DH math is excluded, so authentication overhead becomes pretty much
> negligible.
>
> In my prototype I implemented DH exponential reuse as a simple
> per-host/target cache that keeps DH exponentials (including g xy mod p)
> for up to 10 seconds. Simple and sufficient.
>

Frankly, I hadn't looked at exponential reuse; this implementation
really is just a first step to get feedback from people if this is a
direction they want to go.

> Another, might be ever more significant reason why DH exponential reuse
> is important is that without it x (or y on the host side) must always be
> randomly generated each time a new connection is established. Which
> means, for instance, for 8K groups for each connection 1KB of random
> bytes must be taken from the random pool. With 128 connections it is now
> 128KB. Quite a big pressure on the random pool that DH exponential reuse
> mostly avoids.
>
> Those are the 2 reasons why we added this DH exponential reuse sentence
> in the spec. In the original TP 8006 there was a small informative piece
> explaining reasonings behind that, but for some reasons it was removed
> from the final version.
>

Thanks for the hint. I'll be adding exponential reuse to the code.

> 2. What is the status of this code from perspective of stability in face
> of malicious host behavior? Seems implementation is carefully done, but,
> for instance, at the first look I was not able to find a code to clean
> up if host in not acting for too long in the middle of exchange. Other
> observation is that in nvmet_execute_auth_send()
> nvmet_check_transfer_len() does not check if tl size is reasonable,
> i.e., for instance, not 1GB.
>

That is true; exchange timeouts are missing. Will be adding them, of
course. And haven't thought of checking for tl size overflows; will be
adding them, too.

> For sure, we don't want to allow remote hosts to hang or crash target.
> For instance, because of OOM conditions that happened, because malicious
> host asked target to allocate too much memory or open to many being
> authenticated connections in which the host is not going to reply in the
> middle of exchange.
>
This is something I'll need to look at, anyway. What we do not want is a
userspace application chipping in and send a 'negotiate' command without
any subsequent steps, thereby invalidating the existing authentication.

> Asking, because don't want to go in my review too far ahead from the
> author ;)
>
> In this regard, it would be great if you add in your test application
> ability to perform authentication with random parameters and randomly
> stop responding. Overnight running of such test would give us good
> degree of confidence that it will always work as expected.
>

That indeed would be good; let me think on how something like that can
be implemented.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-21 06:08:51

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

On 7/20/21 10:27 PM, Vladislav Bolkhovitin wrote:
>
> On 7/16/21 2:04 PM, Hannes Reinecke wrote:
>
> [...]
>
>> +struct nvmet_dhchap_hash_map {
>> + int id;
>> + int hash_len;
>> + const char hmac[15];
>> + const char digest[15];
>> +} hash_map[] = {
>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>> + .hash_len = 32,
>> + .hmac = "hmac(sha256)", .digest = "sha256" },
>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>> + .hash_len = 48,
>> + .hmac = "hmac(sha384)", .digest = "sha384" },
>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>> + .hash_len = 64,
>> + .hmac = "hmac(sha512)", .digest = "sha512" },
>> +};
>
> "hmac()" is always here, so why not to just auto-generate hmac(sha512)
> from sha512?
>

... all part of the learning curve ...
If that's true then of course I can auto-generate the hmac name.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-21 06:12:22

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication

On 7/20/21 10:28 PM, Vladislav Bolkhovitin wrote:
>
> On 7/18/21 3:21 PM, Hannes Reinecke wrote:
>> On 7/17/21 9:22 AM, Sagi Grimberg wrote:
>>>> Implement NVMe-oF In-Band authentication. This patch adds two new
>>>> fabric options 'dhchap_key' to specify the PSK
>>>
>>> pre-shared-key.
>>>
>>> Also, we need a sysfs knob to rotate the key that will trigger
>>> re-authentication or even a simple controller(s-plural) reset, so this
>>> should go beyond just the connection string.
>>>
>>
>> Yeah, re-authentication currently is not implemented. I first wanted to
>> get this patchset out such that we can settle on the userspace interface
>> (both from host and target).
>> I'll have to think on how we should handle authentication; one of the
>> really interesting cases would be when one malicious admin will _just_
>> send a 'negotiate' command to the controller. As per spec the controller
>> will be waiting for an 'authentication receive' command to send a
>> 'challenge' payload back to the host. But that will never come, so as it
>> stands currently the controller is required to abort the connection.
>> Not very nice.
>
> Yes, in this case after some reasonable timeout (I would suggest 10-15
> seconds) the controller expected to abort connection and clean up all
> allocated resources.
>
> To handle DoS possibility to make too many such "orphan" negotiations,
> hence consume all controller memory, some additional handling is needed.
> For simplicity as a first step I would suggest to have a global limit on
> number of currently being authenticated connections.
>
> [...]
>
>>>> +    chap->key = nvme_auth_extract_secret(ctrl->opts->dhchap_secret,
>>>> +                         &chap->key_len);
>>>> +    if (IS_ERR(chap->key)) {
>>>> +        ret = PTR_ERR(chap->key);
>>>> +        chap->key = NULL;
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    if (key_hash == 0)
>>>> +        return 0;
>>>> +
>>>> +    hmac_name = nvme_auth_hmac_name(key_hash);
>>>> +    if (!hmac_name) {
>>>> +        pr_debug("Invalid key hash id %d\n", key_hash);
>>>> +        return -EKEYREJECTED;
>>>> +    }
>>>
>>> Why does the user influence the hmac used? isn't that is driven
>>> by the susbsystem?
>>>
>>> I don't think that the user should choose in this level.
>>>
>>
>> That is another weirdness of the spec.
>> The _secret_ will be hashed with a specific function, and that function
>> is stated in the transport representation.
>> (Cf section "DH-HMAC-CHAP Security Requirements").
>> This is _not_ the hash function used by the authentication itself, which
>> will be selected by the protocol.
>> So it's not the user here, but rather the transport specification of the
>> key which selects the hash algorithm.
>
> Yes, good catch. It looks as a minor errata material to specify that
> hash function here is implementation specific.
>
> I would suggest to just hardcode SHA512 here. Users don't have to be
> confused by this.
>
Sure, can do. My reasoning was that the target absolutely has to support
the hash functions specified in the PSK, so that will be a safe bet to
choose for the hash function in the protocol itself.
(Any other hash function _might_ not be preset on the target.)
But if the PSK does not specify a hash the target need to pick one; and
for that of course we can use SHA512.

Cheers,

Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
[email protected] +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

2021-07-21 12:12:19

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [PATCH 06/11] nvme: Implement In-Band authentication


On 7/21/21 9:08 AM, Hannes Reinecke wrote:
> On 7/20/21 10:27 PM, Vladislav Bolkhovitin wrote:
>>
>> On 7/16/21 2:04 PM, Hannes Reinecke wrote:
>>
>> [...]
>>
>>> +struct nvmet_dhchap_hash_map {
>>> + int id;
>>> + int hash_len;
>>> + const char hmac[15];
>>> + const char digest[15];
>>> +} hash_map[] = {
>>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA256,
>>> + .hash_len = 32,
>>> + .hmac = "hmac(sha256)", .digest = "sha256" },
>>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA384,
>>> + .hash_len = 48,
>>> + .hmac = "hmac(sha384)", .digest = "sha384" },
>>> + {.id = NVME_AUTH_DHCHAP_HASH_SHA512,
>>> + .hash_len = 64,
>>> + .hmac = "hmac(sha512)", .digest = "sha512" },
>>> +};
>>
>> "hmac()" is always here, so why not to just auto-generate hmac(sha512)
>> from sha512?
>>
>
> ... all part of the learning curve ...
> If that's true then of course I can auto-generate the hmac name.

As far as I understand, this naming convention is as stable as any other
interface in the kernel.

Vlad

2021-07-21 12:14:01

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support


On 7/21/21 9:06 AM, Hannes Reinecke wrote:
> On 7/20/21 10:26 PM, Vladislav Bolkhovitin wrote:
>> Hi,
>>
>> Great to see those patches coming! After some review, they look to be
>> very well done. Some comments/suggestions below.
>>
>> 1. I strongly recommend to implement DH exponentials reuse (g x mod p /
>> g y mod p as well as g xy mod p) as specified in section 8.13.5.7
>> "DH-HMAC-CHAP Security Requirements". When I was working on TP 8006 I
>> had a prototype that demonstrated that DH math has quite significant
>> latency, something like (as far as I remember) 30ms for 4K group and few
>> hundreds of ms for 8K group. For single connection it is not a big deal,
>> but imagine AMD EPYC with 128 cores. Since all connections are created
>> sequentially, even with 30 ms per connection time to complete full
>> remote device connection would become 128*30 => almost 4 seconds. With
>> 8K group it might be more than 10 seconds. Users are unlikely going to
>> be happy with this, especially in cases, when connecting multiple of
>> NVMe-oF devices is a part of a server or VM boot sequence.
>>
> Oh, indeed, I can confirm that. FFDHE calculations are quite time-consuming.
> But incidentally, ECDH and curve25519 are reasonably fast,

Yes, EC calculations are very fast, this is why EC cryptography is
gaining more and more popularity.

> so maybe
> there _is_ a value in having a TPAR asking for them to be specified, too ...

There's too much politics and procedures involved here. Even in the
current scope it took more, than 2 years to get the spec officially done
(I started proposing it early 2018). Maybe, in future, if someone comes
in the the committee with the corresponding proposal and value
justification.

Although, frankly speaking, with DH exponentials reuse I personally
don't see much value in ECDH in this application. Maybe, only for very
small embedded devices with really limited computational capabilities.

>> If DH exponential reuse implemented, for all subsequent connections the
>> DH math is excluded, so authentication overhead becomes pretty much
>> negligible.
>>
>> In my prototype I implemented DH exponential reuse as a simple
>> per-host/target cache that keeps DH exponentials (including g xy mod p)
>> for up to 10 seconds. Simple and sufficient.
>>
>
> Frankly, I hadn't looked at exponential reuse; this implementation
> really is just a first step to get feedback from people if this is a
> direction they want to go.

Sure, I understand.

>> Another, might be ever more significant reason why DH exponential reuse
>> is important is that without it x (or y on the host side) must always be
>> randomly generated each time a new connection is established. Which
>> means, for instance, for 8K groups for each connection 1KB of random
>> bytes must be taken from the random pool. With 128 connections it is now
>> 128KB. Quite a big pressure on the random pool that DH exponential reuse
>> mostly avoids.
>>
>> Those are the 2 reasons why we added this DH exponential reuse sentence
>> in the spec. In the original TP 8006 there was a small informative piece
>> explaining reasonings behind that, but for some reasons it was removed
>> from the final version.
>>
>
> Thanks for the hint. I'll be adding exponential reuse to the code.

Yes, please. Otherwise, people might start talking that Linux NVMe-oF
authentication is too bad and slow.

Vlad

2021-07-23 20:04:00

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication


On 7/19/21 1:19 PM, Stephan Mueller wrote:
> Am Montag, dem 19.07.2021 um 11:57 +0200 schrieb Hannes Reinecke:
>> On 7/19/21 10:51 AM, Stephan Mueller wrote:
>>> Am Montag, dem 19.07.2021 um 10:15 +0200 schrieb Hannes Reinecke:
>>>> On 7/18/21 2:56 PM, Stephan Müller wrote:
>>>>> Am Sonntag, 18. Juli 2021, 14:37:34 CEST schrieb Hannes Reinecke:
>>>
>>>>>> The key is also used when using the ffdhe algorithm.
>>>>>> Note: I _think_ that I need to use this key for the ffdhe algorithm,
>>>>>> because the implementation I came up with is essentially plain DH
>>>>>> with
>>>>>> pre-defined 'p', 'q' and 'g' values. But the DH implementation also
>>>>>> requires a 'key', and for that I'm using this key here.
>>>>>>
>>>>>> It might be that I'm completely off, and don't need to use a key for
>>>>>> our
>>>>>> DH implementation. In that case you are correct.
>>>>>> (And that's why I said I'll need a review of the FFDHE
>>>>>> implementation).
>>>>>> But for now I'll need the key for FFDHE.
>>>>>
>>>>> Do I understand you correctly that the dhchap_key is used as the input
>>>>> to
>>>>> the
>>>>> DH - i.e. it is the remote public key then? It looks strange that this
>>>>> is
>>>>> used
>>>>> for DH but then it is changed here by hashing it together with
>>>>> something
>>>>> else
>>>>> to form a new dhchap_key. Maybe that is what the protocol says. But it
>>>>> sounds
>>>>> strange to me, especially when you think that dhchap_key would be,
>>>>> say,
>>>>> 2048
>>>>> bits if it is truly the remote public key and then after the hashing
>>>>> it is
>>>>> 256
>>>>> this dhchap_key cannot be used for FFC-DH.
>>>>>
>>>>> Or are you using the dhchap_key for two different purposes?
>>>>>
>>>>> It seems I miss something here.
>>>>>
>>>> No, not entirely. It's me who buggered it up.
>>>> I got carried away by the fact that there is a crypto_dh_encode_key()
>>>> function, and thought I need to use it here.
>>>
>>> Thank you for clarifying that. It sounds to me that there is no defined
>>> protocol (or if there, I would be wondering how the code would have worked
>>> with a different implementation). Would it make sense to first specify a
>>> protocol for authentication and have it discussed? I personally think it
>>> is a
>>> bit difficult to fully understand the protocol from the code and discuss
>>> protocol-level items based on the code.
>>>
>> Oh, the protocol _is_ specified:
>>
>> https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-2_0-2021.06.02-Ratified-5.pdf
>>
>> It's just that I have issues translating that spec onto what the kernel
>> provides.
>
> according to the naming conventions there in figures 447 and following:
>
> - x and y: DH private key (kernel calls it secret set with dh_set_secret or
> encoded into param.key)

x and y are defined in Figure 444: "Random numbers used as exponents in
a DH exchange". Sections 8.13.5.3 "DH-HMAC-CHAP_Challenge Message" and
8.13.5.4 "DH-HMAC-CHAP_Reply Message" additionally specify that "x is a
random number selected by the controller that shall be at least 256 bits
long" and "y is a random number selected by the host that shall be at
least 256 bits long".

So, x and y are just random numbers, no need to overcomplicate their
generation in the way that is beyond of the standard scope.

Vlad

2021-07-23 20:04:10

by Vladislav Bolkhovitin

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support

Another comment is that better to perform CRC check of dhchap_secret and
generation of dhchap_key right where the secret was specified (e.g.,
nvmet_auth_set_host_key() on the target side).

No need to do it every time for every connection and by that increase
authentication latency. As I wrote in the other comment, it might be 128
or more connections established during connecting to a single NVMe-oF
device.

Vlad

2021-07-24 11:20:24

by Hannes Reinecke

[permalink] [raw]
Subject: Re: [RFC PATCH 00/11] nvme: In-band authentication support

On 7/23/21 10:02 PM, Vladislav Bolkhovitin wrote:
> Another comment is that better to perform CRC check of dhchap_secret and
> generation of dhchap_key right where the secret was specified (e.g.,
> nvmet_auth_set_host_key() on the target side).
>
> No need to do it every time for every connection and by that increase
> authentication latency. As I wrote in the other comment, it might be 128
> or more connections established during connecting to a single NVMe-oF
> device.
>
And this is something I did deliberately.
The primary issue here is that the user might want/need to change the
PSK for re-authentication. But for that he might need to check what the
original/current PSK is, so I think we need to keep the original PSK as
passed in from the user.
And I found it a waste of space to store the decoded secret in addition
to the original one, seeing that it can be derived from the original one.
But your argument about the many connections required for a single NVMe
association is certainly true, to it would make sense to keep the decode
one around, too, just to speed up computation.
Will be updating the patchset.

Cheers,

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