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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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 Mueller

[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,