linux-nvme.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 00/11] nvme: In-band authentication support
@ 2021-07-16 11:04 Hannes Reinecke
  2021-07-16 11:04 ` [PATCH 01/11] crypto: add crypto_has_shash() Hannes Reinecke
                   ` (13 more replies)
  0 siblings, 14 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 01/11] crypto: add crypto_has_shash()
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17  6:08   ` Sagi Grimberg
  2021-07-16 11:04 ` [PATCH 02/11] crypto: add crypto_has_kpp() Hannes Reinecke
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 02/11] crypto: add crypto_has_kpp()
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
  2021-07-16 11:04 ` [PATCH 01/11] crypto: add crypto_has_shash() Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17  6:08   ` Sagi Grimberg
  2021-07-16 11:04 ` [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters Hannes Reinecke
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

Add helper function to determine if a given key-agreement protocol primitive is supported.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 crypto/kpp.c         | 6 ++++++
 include/crypto/kpp.h | 2 ++
 2 files changed, 8 insertions(+)

diff --git a/crypto/kpp.c b/crypto/kpp.c
index 313b2c699963..416e8a1a03ee 100644
--- a/crypto/kpp.c
+++ b/crypto/kpp.c
@@ -87,6 +87,12 @@ struct crypto_kpp *crypto_alloc_kpp(const char *alg_name, u32 type, u32 mask)
 }
 EXPORT_SYMBOL_GPL(crypto_alloc_kpp);
 
+int crypto_has_kpp(const char *alg_name, u32 type, u32 mask)
+{
+	return crypto_type_has_alg(alg_name, &crypto_kpp_type, type, mask);
+}
+EXPORT_SYMBOL_GPL(crypto_has_kpp);
+
 static void kpp_prepare_alg(struct kpp_alg *alg)
 {
 	struct crypto_alg *base = &alg->base;
diff --git a/include/crypto/kpp.h b/include/crypto/kpp.h
index cccceadc164b..24d01e9877c1 100644
--- a/include/crypto/kpp.h
+++ b/include/crypto/kpp.h
@@ -104,6 +104,8 @@ struct kpp_alg {
  */
 struct crypto_kpp *crypto_alloc_kpp(const char *alg_name, u32 type, u32 mask);
 
+int crypto_has_kpp(const char *alg_name, u32 type, u32 mask);
+
 static inline struct crypto_tfm *crypto_kpp_tfm(struct crypto_kpp *tfm)
 {
 	return &tfm->base;
-- 
2.29.2


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
  2021-07-16 11:04 ` [PATCH 01/11] crypto: add crypto_has_shash() Hannes Reinecke
  2021-07-16 11:04 ` [PATCH 02/11] crypto: add crypto_has_kpp() Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17  6:14   ` Sagi Grimberg
  2021-07-17 15:03   ` Stephan Müller
  2021-07-16 11:04 ` [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding Hannes Reinecke
                   ` (10 subsequent siblings)
  13 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 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 <hare@suse.de>
+ */
+#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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (2 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17  6:16   ` Sagi Grimberg
  2021-07-17 14:20   ` Eric Biggers
  2021-07-16 11:04 ` [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication Hannes Reinecke
                   ` (9 subsequent siblings)
  13 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

Add RFC4648-compliant base64 encoding and decoding routines.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (3 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17  6:30   ` Sagi Grimberg
  2021-07-16 11:04 ` [PATCH 06/11] nvme: Implement " Hannes Reinecke
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (4 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17  7:22   ` Sagi Grimberg
                     ` (2 more replies)
  2021-07-16 11:04 ` [PATCH 07/11] nvme-auth: augmented challenge support Hannes Reinecke
                   ` (7 subsequent siblings)
  13 siblings, 3 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 07/11] nvme-auth: augmented challenge support
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (5 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 06/11] nvme: Implement " Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17 16:49   ` Stephan Müller
  2021-07-19  9:21   ` Sagi Grimberg
  2021-07-16 11:04 ` [PATCH 08/11] nvmet: Parse fabrics commands on all queues Hannes Reinecke
                   ` (6 subsequent siblings)
  13 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

Implement support for augmented challenge using FFDHE groups.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 08/11] nvmet: Parse fabrics commands on all queues
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (6 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 07/11] nvme-auth: augmented challenge support Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-19  9:21   ` Sagi Grimberg
  2021-07-16 11:04 ` [PATCH 09/11] nvmet: Implement basic In-Band Authentication Hannes Reinecke
                   ` (5 subsequent siblings)
  13 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (7 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 08/11] nvmet: Parse fabrics commands on all queues Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17 16:49   ` Stephan Müller
  2021-07-19 20:38   ` Sagi Grimberg
  2021-07-16 11:04 ` [PATCH 10/11] nvmet-auth: implement support for augmented challenge Hannes Reinecke
                   ` (4 subsequent siblings)
  13 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 10/11] nvmet-auth: implement support for augmented challenge
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (8 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 09/11] nvmet: Implement basic In-Band Authentication Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17 16:49   ` Stephan Müller
  2021-07-16 11:04 ` [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms Hannes Reinecke
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (9 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 10/11] nvmet-auth: implement support for augmented challenge Hannes Reinecke
@ 2021-07-16 11:04 ` Hannes Reinecke
  2021-07-17 16:50   ` Stephan Müller
  2021-07-19  9:23   ` Sagi Grimberg
  2021-07-17  6:06 ` [RFC PATCH 00/11] nvme: In-band authentication support Sagi Grimberg
                   ` (2 subsequent siblings)
  13 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-16 11:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
---
 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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [RFC PATCH 00/11] nvme: In-band authentication support
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (10 preceding siblings ...)
  2021-07-16 11:04 ` [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms Hannes Reinecke
@ 2021-07-17  6:06 ` Sagi Grimberg
  2021-07-19 10:02 ` Simo Sorce
  2021-07-20 20:26 ` Vladislav Bolkhovitin
  13 siblings, 0 replies; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  6:06 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto


> 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...

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 01/11] crypto: add crypto_has_shash()
  2021-07-16 11:04 ` [PATCH 01/11] crypto: add crypto_has_shash() Hannes Reinecke
@ 2021-07-17  6:08   ` Sagi Grimberg
  0 siblings, 0 replies; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  6:08 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

> 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.

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 02/11] crypto: add crypto_has_kpp()
  2021-07-16 11:04 ` [PATCH 02/11] crypto: add crypto_has_kpp() Hannes Reinecke
@ 2021-07-17  6:08   ` Sagi Grimberg
  0 siblings, 0 replies; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  6:08 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

> Add helper function to determine if a given key-agreement protocol primitive is supported.

Same comment.

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
  2021-07-16 11:04 ` [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters Hannes Reinecke
@ 2021-07-17  6:14   ` Sagi Grimberg
  2021-07-17 13:57     ` Hannes Reinecke
  2021-07-17 15:03   ` Stephan Müller
  1 sibling, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  6:14 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

> Add helper functions to generaten Finite Field DH Ephemeral Parameters as
> specified in RFC 7919.
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> ---
>   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 <hare@suse.de>
> + */
> +#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 */
> 

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
  2021-07-16 11:04 ` [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding Hannes Reinecke
@ 2021-07-17  6:16   ` Sagi Grimberg
  2021-07-17 14:00     ` Hannes Reinecke
  2021-07-17 14:20   ` Eric Biggers
  1 sibling, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  6:16 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto


> 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?

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication
  2021-07-16 11:04 ` [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication Hannes Reinecke
@ 2021-07-17  6:30   ` Sagi Grimberg
  2021-07-17 14:04     ` Hannes Reinecke
  0 siblings, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  6:30 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto



On 7/16/21 4:04 AM, Hannes Reinecke wrote:
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> ---
>   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;
>   	};
> 

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-16 11:04 ` [PATCH 06/11] nvme: Implement " Hannes Reinecke
@ 2021-07-17  7:22   ` Sagi Grimberg
  2021-07-18 12:21     ` Hannes Reinecke
  2021-07-17 16:49   ` Stephan Müller
  2021-07-20 20:27   ` Vladislav Bolkhovitin
  2 siblings, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-17  7:22 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

> 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 <hare@suse.de>
> ---
>   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);
>   	}
> 

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
  2021-07-17  6:14   ` Sagi Grimberg
@ 2021-07-17 13:57     ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-17 13:57 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
  2021-07-17  6:16   ` Sagi Grimberg
@ 2021-07-17 14:00     ` Hannes Reinecke
  2021-07-17 14:12       ` Eric Biggers
  0 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-17 14:00 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 05/11] nvme: add definitions for NVMe In-Band authentication
  2021-07-17  6:30   ` Sagi Grimberg
@ 2021-07-17 14:04     ` Hannes Reinecke
  2021-07-20 20:26       ` Vladislav Bolkhovitin
  0 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-17 14:04 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
  2021-07-17 14:00     ` Hannes Reinecke
@ 2021-07-17 14:12       ` Eric Biggers
  0 siblings, 0 replies; 88+ messages in thread
From: Eric Biggers @ 2021-07-17 14:12 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: Sagi Grimberg, Christoph Hellwig, Keith Busch, linux-nvme,
	Herbert Xu, David S . Miller, linux-crypto

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

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding
  2021-07-16 11:04 ` [PATCH 04/11] lib/base64: RFC4648-compliant base64 encoding Hannes Reinecke
  2021-07-17  6:16   ` Sagi Grimberg
@ 2021-07-17 14:20   ` Eric Biggers
  1 sibling, 0 replies; 88+ messages in thread
From: Eric Biggers @ 2021-07-17 14:20 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: Christoph Hellwig, Sagi Grimberg, Keith Busch, linux-nvme,
	Herbert Xu, David S . Miller, linux-crypto

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

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
  2021-07-16 11:04 ` [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters Hannes Reinecke
  2021-07-17  6:14   ` Sagi Grimberg
@ 2021-07-17 15:03   ` Stephan Müller
  2021-07-18 12:22     ` Hannes Reinecke
  1 sibling, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-17 15:03 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 10/11] nvmet-auth: implement support for augmented challenge
  2021-07-16 11:04 ` [PATCH 10/11] nvmet-auth: implement support for augmented challenge Hannes Reinecke
@ 2021-07-17 16:49   ` Stephan Müller
  2021-07-18 12:25     ` Hannes Reinecke
  0 siblings, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-17 16:49 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
> ---
>  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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 07/11] nvme-auth: augmented challenge support
  2021-07-16 11:04 ` [PATCH 07/11] nvme-auth: augmented challenge support Hannes Reinecke
@ 2021-07-17 16:49   ` Stephan Müller
  2021-07-18 12:27     ` Hannes Reinecke
  2021-07-19  9:21   ` Sagi Grimberg
  1 sibling, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-17 16:49 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
> ---
>  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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-16 11:04 ` [PATCH 09/11] nvmet: Implement basic In-Band Authentication Hannes Reinecke
@ 2021-07-17 16:49   ` Stephan Müller
  2021-07-18 12:37     ` Hannes Reinecke
  2021-07-19 20:38   ` Sagi Grimberg
  1 sibling, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-17 16:49 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
> ---
>  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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-16 11:04 ` [PATCH 06/11] nvme: Implement " Hannes Reinecke
  2021-07-17  7:22   ` Sagi Grimberg
@ 2021-07-17 16:49   ` Stephan Müller
  2021-07-18 12:43     ` Hannes Reinecke
  2021-07-20 20:27   ` Vladislav Bolkhovitin
  2 siblings, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-17 16:49 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
> ---
>  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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms
  2021-07-16 11:04 ` [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms Hannes Reinecke
@ 2021-07-17 16:50   ` Stephan Müller
  2021-07-18 12:44     ` Hannes Reinecke
  2021-07-19  9:23   ` Sagi Grimberg
  1 sibling, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-17 16:50 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto, Hannes Reinecke

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 <hare@suse.de>
> ---
>  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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-17  7:22   ` Sagi Grimberg
@ 2021-07-18 12:21     ` Hannes Reinecke
  2021-07-19  8:47       ` Sagi Grimberg
  2021-07-20 20:28       ` Vladislav Bolkhovitin
  0 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:21 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/11] crypto/ffdhe: Finite Field DH Ephemeral Parameters
  2021-07-17 15:03   ` Stephan Müller
@ 2021-07-18 12:22     ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:22 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 10/11] nvmet-auth: implement support for augmented challenge
  2021-07-17 16:49   ` Stephan Müller
@ 2021-07-18 12:25     ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:25 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 07/11] nvme-auth: augmented challenge support
  2021-07-17 16:49   ` Stephan Müller
@ 2021-07-18 12:27     ` Hannes Reinecke
  2021-07-18 12:57       ` Stephan Müller
  0 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:27 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-17 16:49   ` Stephan Müller
@ 2021-07-18 12:37     ` Hannes Reinecke
  2021-07-18 12:56       ` Stephan Müller
  2021-07-18 13:26       ` Herbert Xu
  0 siblings, 2 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:37 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-17 16:49   ` Stephan Müller
@ 2021-07-18 12:43     ` Hannes Reinecke
  2021-07-18 12:47       ` Stephan Müller
  0 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:43 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms
  2021-07-17 16:50   ` Stephan Müller
@ 2021-07-18 12:44     ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-18 12:44 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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
hare@suse.de                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-18 12:43     ` Hannes Reinecke
@ 2021-07-18 12:47       ` Stephan Müller
  0 siblings, 0 replies; 88+ messages in thread
From: Stephan Müller @ 2021-07-18 12:47 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-18 12:37     ` Hannes Reinecke
@ 2021-07-18 12:56       ` Stephan Müller
  2021-07-19  8:15         ` Hannes Reinecke
  2021-07-18 13:26       ` Herbert Xu
  1 sibling, 1 reply; 88+ messages in thread
From: Stephan Müller @ 2021-07-18 12:56 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
> >> ---
> >> 
> >>   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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 07/11] nvme-auth: augmented challenge support
  2021-07-18 12:27     ` Hannes Reinecke
@ 2021-07-18 12:57       ` Stephan Müller
  0 siblings, 0 replies; 88+ messages in thread
From: Stephan Müller @ 2021-07-18 12:57 UTC (permalink / raw)
  To: Christoph Hellwig, Hannes Reinecke
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
> >> ---
> >> 
> >>   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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-18 12:37     ` Hannes Reinecke
  2021-07-18 12:56       ` Stephan Müller
@ 2021-07-18 13:26       ` Herbert Xu
  1 sibling, 0 replies; 88+ messages in thread
From: Herbert Xu @ 2021-07-18 13:26 UTC (permalink / raw)
  To: Hannes Reinecke
  Cc: Stephan Müller, Christoph Hellwig, Sagi Grimberg,
	Keith Busch, linux-nvme, David S . Miller, linux-crypto

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 <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-18 12:56       ` Stephan Müller
@ 2021-07-19  8:15         ` Hannes Reinecke
  2021-07-19  8:51           ` Stephan Mueller
  0 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-19  8:15 UTC (permalink / raw)
  To: Stephan Müller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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 <hare@suse.de>
>>>> ---
>>>>
>>>>   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
hare@suse.de			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/11] nvme: Implement In-Band authentication
  2021-07-18 12:21     ` Hannes Reinecke
@ 2021-07-19  8:47       ` Sagi Grimberg
  2021-07-20 20:28       ` Vladislav Bolkhovitin
  1 sibling, 0 replies; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-19  8:47 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto


>>> 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 <hare@suse.de>
>>> ---
>>>   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...

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19  8:15         ` Hannes Reinecke
@ 2021-07-19  8:51           ` Stephan Mueller
  2021-07-19  9:57             ` Hannes Reinecke
  0 siblings, 1 reply; 88+ messages in thread
From: Stephan Mueller @ 2021-07-19  8:51 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 07/11] nvme-auth: augmented challenge support
  2021-07-16 11:04 ` [PATCH 07/11] nvme-auth: augmented challenge support Hannes Reinecke
  2021-07-17 16:49   ` Stephan Müller
@ 2021-07-19  9:21   ` Sagi Grimberg
  2021-07-20 13:12     ` Hannes Reinecke
  1 sibling, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-19  9:21 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto



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 <hare@suse.de>
> ---
>   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);
> 

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 08/11] nvmet: Parse fabrics commands on all queues
  2021-07-16 11:04 ` [PATCH 08/11] nvmet: Parse fabrics commands on all queues Hannes Reinecke
@ 2021-07-19  9:21   ` Sagi Grimberg
  0 siblings, 0 replies; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-19  9:21 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

Reviewed-by: Sagi Grimberg <sagi@grimberg.me>

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms
  2021-07-16 11:04 ` [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms Hannes Reinecke
  2021-07-17 16:50   ` Stephan Müller
@ 2021-07-19  9:23   ` Sagi Grimberg
  2021-07-19  9:56     ` Hannes Reinecke
  1 sibling, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-19  9:23 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto


> 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?

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 11/11] nvme: add non-standard ECDH and curve25517 algorithms
  2021-07-19  9:23   ` Sagi Grimberg
@ 2021-07-19  9:56     ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-19  9:56 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

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
hare@suse.de			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19  8:51           ` Stephan Mueller
@ 2021-07-19  9:57             ` Hannes Reinecke
  2021-07-19 10:19               ` Stephan Mueller
  0 siblings, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-19  9:57 UTC (permalink / raw)
  To: Stephan Mueller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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
hare@suse.de			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [RFC PATCH 00/11] nvme: In-band authentication support
  2021-07-16 11:04 [RFC PATCH 00/11] nvme: In-band authentication support Hannes Reinecke
                   ` (11 preceding siblings ...)
  2021-07-17  6:06 ` [RFC PATCH 00/11] nvme: In-band authentication support Sagi Grimberg
@ 2021-07-19 10:02 ` Simo Sorce
  2021-07-19 11:11   ` Hannes Reinecke
  2021-07-20 20:26 ` Vladislav Bolkhovitin
  13 siblings, 1 reply; 88+ messages in thread
From: Simo Sorce @ 2021-07-19 10:02 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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





_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19  9:57             ` Hannes Reinecke
@ 2021-07-19 10:19               ` Stephan Mueller
  2021-07-19 11:10                 ` Hannes Reinecke
  2021-07-23 20:02                 ` Vladislav Bolkhovitin
  0 siblings, 2 replies; 88+ messages in thread
From: Stephan Mueller @ 2021-07-19 10:19 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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


_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19 10:19               ` Stephan Mueller
@ 2021-07-19 11:10                 ` Hannes Reinecke
  2021-07-19 11:52                   ` Stephan Mueller
  2021-07-23 20:02                 ` Vladislav Bolkhovitin
  1 sibling, 1 reply; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-19 11:10 UTC (permalink / raw)
  To: Stephan Mueller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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
hare@suse.de			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [RFC PATCH 00/11] nvme: In-band authentication support
  2021-07-19 10:02 ` Simo Sorce
@ 2021-07-19 11:11   ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-19 11:11 UTC (permalink / raw)
  To: Simo Sorce, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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
hare@suse.de			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19 11:10                 ` Hannes Reinecke
@ 2021-07-19 11:52                   ` Stephan Mueller
  2021-07-19 12:08                     ` Hannes Reinecke
  2021-07-20 10:14                     ` Hannes Reinecke
  0 siblings, 2 replies; 88+ messages in thread
From: Stephan Mueller @ 2021-07-19 11:52 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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



_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19 11:52                   ` Stephan Mueller
@ 2021-07-19 12:08                     ` Hannes Reinecke
  2021-07-20 10:14                     ` Hannes Reinecke
  1 sibling, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-19 12:08 UTC (permalink / raw)
  To: Stephan Mueller, Christoph Hellwig
  Cc: Sagi Grimberg, Keith Busch, linux-nvme, Herbert Xu,
	David S . Miller, linux-crypto

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
hare@suse.de			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-16 11:04 ` [PATCH 09/11] nvmet: Implement basic In-Band Authentication Hannes Reinecke
  2021-07-17 16:49   ` Stephan Müller
@ 2021-07-19 20:38   ` Sagi Grimberg
  2021-07-20  6:08     ` Hannes Reinecke
  1 sibling, 1 reply; 88+ messages in thread
From: Sagi Grimberg @ 2021-07-19 20:38 UTC (permalink / raw)
  To: Hannes Reinecke, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto



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 <hare@suse.de>
> ---
>   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 */
> 

_______________________________________________
Linux-nvme mailing list
Linux-nvme@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-nvme

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/11] nvmet: Implement basic In-Band Authentication
  2021-07-19 20:38   ` Sagi Grimberg
@ 2021-07-20  6:08     ` Hannes Reinecke
  0 siblings, 0 replies; 88+ messages in thread
From: Hannes Reinecke @ 2021-07-20  6:08 UTC (permalink / raw)
  To: Sagi Grimberg, Christoph Hellwig
  Cc: Keith Busch, linux-nvme, Herbert Xu, David S . Miller, linux-crypto

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 <hare@suse.de>
>> ---
>>   drivers/nvme/target/Kconfig            |  10 +
>>   drivers/nvme/target/Makefile           |   1 +
>>   drivers/nvme/target/admin-cmd.c        |   4 +
>>   drivers/nvme/target/auth.c             | 352 +++++++++++++++++++
>>   drivers/nvme/target/configfs.c         |  71 +++-
>>   drivers/nvme/target/core.c             |   8 +
>>   drivers/nvme/target/fabrics-cmd-auth.c | 460 +++++++++++++++++++++++++
>>   drivers/nvme/target/fabrics-cmd.c      |  30 +-
>>   drivers/nvme/target/nvmet.h            |  71 ++++
>>   9 files changed, 1004 insertions(+), 3 deletions(-)
>>   create mode 100644 drivers/nvme/target/auth.c
>>   create mode 100644 drivers/nvme/target/fabrics-cmd-auth.c
>>
>> diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig
>> index 4be2ececbc45..d5656ef1559e 100644
>> --- a/drivers/nvme/target/Kconfig
>> +++ b/drivers/nvme/target/Kconfig
>> @@ -85,3 +85,13 @@ config NVME_TARGET_TCP
>>         devices over TCP.
>>           If unsure, say N.
>> +
>> +config NVME_TARGET_AUTH
>> +    bool "NVMe over Fabrics In-band Authentication support"
>> +    depends on NVME_TARGET
>> +    select CRYPTO_SHA256
>> +    select CRYPTO_SHA512
>> +    help
>> +      This enables support for NVMe over Fabrics In-band Authentication
>> +
>> +      If unsure, say N.
>> diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile
>> index 9837e580fa7e..c66820102493 100644
>> --- a/drivers/nvme/target/Makefile
>> +++ b/drivers/nvme/target/Makefile
>> @@ -13,6 +13,7 @@ nvmet-y        += core.o configfs.o admin-cmd.o
>> fabrics-cmd.o \
>>               discovery.o io-cmd-file.o io-cmd-bdev.o
>>   nvmet-$(CONFIG_NVME_TARGET_PASSTHRU)    += passthru.o
>>   nvmet-$(CONFIG_BLK_DEV_ZONED)        += zns.o
>> +nvmet-$(CONFIG_NVME_TARGET_AUTH)    += fabrics-cmd-auth.o auth.o
>>   nvme-loop-y    += loop.o
>>   nvmet-rdma-y    += rdma.o
>>   nvmet-fc-y    += fc.o
>> diff --git a/drivers/nvme/target/admin-cmd.c
>> b/drivers/nvme/target/admin-cmd.c
>> index 0cb98f2bbc8c..320cefc64ee0 100644
>> --- a/drivers/nvme/target/admin-cmd.c
>> +++ b/drivers/nvme/target/admin-cmd.c
>> @@ -1008,6 +1008,10 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
>>         if (nvme_is_fabrics(cmd))
>>           return nvmet_parse_fabrics_cmd(req);
>> +
>> +    if (unlikely(!nvmet_check_auth_status(req)))
>> +        return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR;
>> +
>>       if (nvmet_req_subsys(req)->type == NVME_NQN_DISC)
>>           return nvmet_parse_discovery_cmd(req);
>>   diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
>> new file mode 100644
>> index 000000000000..00c7d051dfb1
>> --- /dev/null
>> +++ b/drivers/nvme/target/auth.c
>> @@ -0,0 +1,352 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * NVMe over Fabrics DH-HMAC-CHAP authentication.
>> + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions.
>> + * All rights reserved.
>> + */
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/err.h>
>> +#include <crypto/hash.h>
>> +#include <crypto/kpp.h>
>> +#include <crypto/dh.h>
>> +#include <crypto/ffdhe.h>
>> +#include <linux/crc32.h>
>> +#include <linux/base64.h>
>> +#include <linux/ctype.h>
>> +#include <linux/random.h>
>> +#include <asm/unaligned.h>
>> +
>> +#include "nvmet.h"
>> +#include "../host/auth.h"
>> +
>> +int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)
>> +{
>> +    if (sscanf(secret, "DHHC-1:%hhd:%*s", &host->dhchap_key_hash) != 1)
>> +        return -EINVAL;
>> +    if (host->dhchap_key_hash > 3) {
>> +        pr_warn("Invalid DH-HMAC-CHAP hash id %d\n",
>> +             host->dhchap_key_hash);
>> +        return -EINVAL;
>> +    }
>> +    if (host->dhchap_key_hash > 0) {
>> +        /* Validate selected hash algorithm */
>> +        const char *hmac = nvme_auth_hmac_name(host->dhchap_key_hash);
>> +
>> +        if (!crypto_has_shash(hmac, 0, 0)) {
>> +            pr_warn("DH-HMAC-CHAP hash %s unsupported\n", hmac);
> 
> pr_err
> 
>> +            host->dhchap_key_hash = -1;
>> +            return -EAGAIN;
> 
> Why EAGAIN?
> 

What else? ENOTSUPP?

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

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

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

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

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

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

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

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

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

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

See above. Yes.

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

Yeah.

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

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

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

Ho-hum. Of course.

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

Ok.

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

Sure.

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

Ok.

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

Ok.

>> +{
>> +    struct nvmf_auth_dhchap_challenge_data *data = d;
>> +    struct nvmet_ctrl *ctrl = req->sq->ctrl;
>> +    int ret = 0;
>> +    int data_size = sizeof(*d) + req->sq->dhchap_hash_len;
>> +
>> +    if (al < data_size) {
>> +        pr_debug("%s: buffer too small (al %d need %d)\n", __func__,
>> +             al, data_size);
>> +        return -EINVAL;
>> +    }
>> +    memset(data, 0, data_size);
>> +    req->sq->dhchap_s1 = ctrl->dhchap_seqnum++;
>> +    data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;
>> +    data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
>> +    data->t_id = cpu_to_le16(req->sq->dhchap_tid);
>> +    data->hashid = req->sq->dhchap_hash_id;
>> +    data->hl = req->sq->dhchap_hash_len;
>> +    data->seqnum = cpu_to_le32(req->sq->dhchap_s1);
>> +    req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL);
>> +    if (!req->sq->dhchap_c1)
>> +        return -ENOMEM;
>> +    get_random_bytes(req->sq->dhchap_c1, data->hl);
>> +    memcpy(data->cval, req->sq->dhchap_c1, data->hl);
>> +    pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen
>> %d\n",
>> +