Skip to contentSkip to conversation

nvme: In-band authentication support

RFC PATCH Superseded Hannes Reinecke 7 participants 75 messages Jul 16, 2021 – Jul 24, 2021 Page 1 of 2

Subsystems (5)

v1 of 18 Superseded Latest is v18 →

lore
Share & tools
lore

Hannes Reinecke

author maintainer
#0 lore raw

cover nvme: In-band authentication support

Hi all,

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

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

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

As usual, comments and reviews are welcome.

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

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

PATCH 1/11 crypto: add crypto_has_shash()

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

---
 crypto/shash.c        | 6 ++++++
 include/crypto/hash.h | 2 ++
 2 files changed, 8 insertions(+)
crypto/shash.c +6 -0
index 0a0a50cb694f..4c88e63b3350 100644
@@ -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;
include/crypto/hash.h +2 -0
index f140e4643949..f5841992dc9b 100644
@@ -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;
Signature · 1 line
2.29.2
maintainer
#2 lore raw

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

Quoted 1 line

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.

author maintainer
#3 lore raw

PATCH 2/11 crypto: add crypto_has_kpp()

In reply to Hannes Reinecke

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

---
 crypto/kpp.c         | 6 ++++++
 include/crypto/kpp.h | 2 ++
 2 files changed, 8 insertions(+)
crypto/kpp.c +6 -0
index 313b2c699963..416e8a1a03ee 100644
@@ -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;
include/crypto/kpp.h +2 -0
index cccceadc164b..24d01e9877c1 100644
@@ -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;
Signature · 1 line
2.29.2
maintainer
#4 lore raw

Re: [PATCH 02/11] crypto: add crypto_has_kpp()

Quoted 1 line

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

Same comment.

author maintainer
#5 lore raw

PATCH 3/11 crypto/ffdhe: Finite Field DH Ephemeral Parameters

In reply to Hannes Reinecke

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

---
 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
crypto/Kconfig +8 -0
index ca3b02dcbbfa..1bea506ba56f 100644
@@ -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
 
crypto/Makefile +1 -0
index 10526d4559b8..d3bc79fba23f 100644
@@ -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
crypto/ffdhe_helper.c new file +877 -0

877 lines not on this page · raw message

include/crypto/ffdhe.h new file +24 -0
new file mode 100644
index 000000000000..6cb9253ddb34
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Finite-Field Diffie-Hellman definition according to RFC 7919
+ *
+ * Copyright (c) 2021, SUSE Software Products
+ * Authors: Hannes Reinecke <[email protected]>
+ */
+#ifndef _CRYPTO_FFDHE_
+#define _CRYPTO_FFDHE_
+
+/**
+ * crypto_ffdhe_params() - Generate FFDHE params
+ * @params: DH params
+ * @bits: Bitsize of the FFDHE parameters
+ *
+ * This functions sets the FFDHE parameter for @bits in @params.
+ * Valid bit sizes are 2048, 3072, 4096, 6144, or 8194.
+ *
+ * Returns: 0 on success, errno on failure.
+ */
+
+int crypto_ffdhe_params(struct dh *p, int bits);
+
+#endif /* _CRYPTO_FFDHE_H */
Signature · 1 line
2.29.2
maintainer
#6 lore raw

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

Quoted 18 lines

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

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

crypto/Kconfig +0 -0
index ca3b02dcbbfa..1bea506ba56f 100644

crypto/Kconfig config CRYPTO_DH · line 235

Quoted 5 lines
crypto/Kconfig +2 -0
@@ -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.

crypto/Kconfig config CRYPTO_DH · line 240

Quoted 5 lines
crypto/Kconfig +5 -0
+	select CRYPTO_DH
+	select CRYPTO_KPP
+	select CRYPTO_RNG_DEFAULT
+	help
+	  Generic implementation of the Finite Field DH algorithm

936 lines not shown · raw message

Part of this message is not shown here. View the raw message.

author maintainer
#7 lore raw

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

Quoted 13 lines · Sagi Grimberg
Quoted 13 lines

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

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

Quoted 12 lines
Quoted 9 lines
crypto/Kconfig +0 -0
index ca3b02dcbbfa..1bea506ba56f 100644
@@ -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.

Quoted 11 lines
Quoted 5 lines

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

Quoted 31 lines
Quoted 31 lines

+
  config CRYPTO_ECC
      tristate

crypto/Makefile +0 -0
index 10526d4559b8..d3bc79fba23f 100644
@@ -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

crypto/ffdhe_helper.c new file +11 -0
new file mode 100644
index 000000000000..dc023e30c4e5
@@ -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>
Quoted 9 lines

crypto/ffdhe_helper.c line 16

Quoted 5 lines
crypto/ffdhe_helper.c new file +5 -0
+#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

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

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

In reply to Hannes Reinecke

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

Hi Hannes,

crypto/ffdhe_helper.c line 18

Quoted 10 lines
crypto/ffdhe_helper.c new file +10 -0
+#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

author maintainer
#9 lore raw

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

Quoted 4 lines · Stephan Müller

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

Hi Hannes,

Quoted 13 lines

crypto/ffdhe_helper.c line 18

Quoted 10 lines
crypto/ffdhe_helper.c new file +10 -0
+#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

Signature · 4 lines
Dr. Hannes Reinecke                Kernel Storage Architect
[email protected]                              +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
author maintainer
#10 lore raw

PATCH 4/11 lib/base64: RFC4648-compliant base64 encoding

In reply to Hannes Reinecke

Add RFC4648-compliant base64 encoding and decoding routines.

---
 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
include/linux/base64.h new file +16 -0
new file mode 100644
index 000000000000..660d4cb1ef31
@@ -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 */
lib/Makefile +1 -1
index 6d765d5fb8ac..92a428aa0e5f 100644
@@ -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
lib/base64.c new file +111 -0

111 lines not on this page · raw message

Signature · 1 line
2.29.2
maintainer
#11 lore raw

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

Quoted 2 lines

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?

author maintainer
#12 lore raw

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

Quoted 5 lines · Sagi Grimberg
Quoted 2 lines

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

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

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

Quoted 10 lines · Hannes Reinecke
Quoted 5 lines · Sagi Grimberg
Quoted 2 lines

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

#14 lore raw

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

In reply to Hannes Reinecke
Quoted 37 lines · Hannes Reinecke
lib/base64.c new file +37 -0
+/**
+ * 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;

lib/base64.c line 111

Quoted 10 lines
lib/base64.c new file +10 -0
+                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

author maintainer
#15 lore raw

PATCH 5/11 nvme: add definitions for NVMe In-Band authentication

In reply to Hannes Reinecke
---
 include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 184 insertions(+), 1 deletion(-)
include/linux/nvme.h +184 -1
index b7c4c4130b65..7b94abacfd08 100644
@@ -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;
 	};
Signature · 1 line
2.29.2
maintainer
#16 lore raw

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

Quoted 44 lines · Hannes Reinecke
---
  include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++-
  1 file changed, 184 insertions(+), 1 deletion(-)
include/linux/nvme.h +9 -1
index b7c4c4130b65..7b94abacfd08 100644
@@ -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;

include/linux/nvme.h struct nvmf_property_get_command { · line 1413

Quoted 10 lines
include/linux/nvme.h +10 -0
+	__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.

Quoted 44 lines
include/linux/nvme.h +44 -0
+
+};
+
+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;

include/linux/nvme.h struct nvmf_property_get_command { · line 1467

Quoted 10 lines
include/linux/nvme.h +10 -0
+	__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).

Quoted 37 lines
include/linux/nvme.h +37 -0
+	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;

include/linux/nvme.h struct nvmf_property_get_command { · line 1514

Quoted 10 lines
include/linux/nvme.h +10 -0
+	__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...

Quoted 18 lines
include/linux/nvme.h +18 -0
+	__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 {

include/linux/nvme.h struct nvmf_property_get_command { · line 1542

Quoted 10 lines
include/linux/nvme.h +10 -0
+	__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...

Quoted 7 lines
include/linux/nvme.h +7 -0
+};
+
+struct nvmf_auth_dhchap_success2_data {
+	__u8		auth_type;
+	__u8		auth_id;
+	__u8		rsvd1[2];
+	__le16		t_id;

include/linux/nvme.h struct nvmf_property_get_command { · line 1559

Quoted 10 lines
include/linux/nvme.h +10 -0
+	__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

Quoted 4 lines
include/linux/nvme.h +4 -0
+};
+
+enum {
+	NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED	= 0x01,

include/linux/nvme.h struct nvmf_property_get_command { · line 1573

Quoted 10 lines
include/linux/nvme.h +10 -0
+};
+
+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"?

Quoted 16 lines
include/linux/nvme.h +5 -0
+};
+
+
  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;
  	};
author maintainer
#17 lore raw

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

Quoted 48 lines · Sagi Grimberg
Quoted 45 lines · Hannes Reinecke

---
  include/linux/nvme.h | 185 ++++++++++++++++++++++++++++++++++++++++++-

  1 file changed, 184 insertions(+), 1 deletion(-)
include/linux/nvme.h +15 -1
index b7c4c4130b65..7b94abacfd08 100644
@@ -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;
Quoted 9 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1413

Quoted 4 lines
include/linux/nvme.h +4 -0
+    __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.

Quoted 49 lines
Quoted 49 lines
include/linux/nvme.h +49 -0
+
+};
+
+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 {
Quoted 9 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1467

Quoted 5 lines
include/linux/nvme.h +5 -0
+    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...

Quoted 42 lines
Quoted 42 lines
include/linux/nvme.h +42 -0
+    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 */
+};
Quoted 9 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1514

Quoted 5 lines
include/linux/nvme.h +5 -0
+
+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.

Quoted 23 lines
Quoted 23 lines
include/linux/nvme.h +23 -0
+    __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;
Quoted 9 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1542

Quoted 5 lines
include/linux/nvme.h +5 -0
+    __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...

Quoted 13 lines
Quoted 13 lines
include/linux/nvme.h +13 -0
+};
+
+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;
Quoted 9 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1559

Quoted 4 lines
include/linux/nvme.h +4 -0
+    __u8        rsvd1[2];
+    __le16        t_id;
+    __u8        reason_code;
+    __u8        reason_code_explanation;

I'd maybe do those shorter;
rescode
rescode_exp

Ok.

Quoted 9 lines
Quoted 9 lines
include/linux/nvme.h +9 -0
+};
+
+enum {
+    NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED    = 0x01,
+};
+
+enum {
+    NVME_AUTH_DHCHAP_FAILURE_FAILED            = 0x01,
+    NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE        = 0x02,
Quoted 9 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1573

Quoted 5 lines
include/linux/nvme.h +5 -0
+    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.

Quoted 8 lines
Quoted 8 lines
include/linux/nvme.h +3 -0
+};
+
+
   struct nvme_dbbuf {
       __u8            opcode;
       __u8            flags;
@@ -1436,6 +1617,8 @@ struct nvme_command {
           struct nvmf_connect_command connect;
Quoted 8 lines

include/linux/nvme.h struct nvme_command { · line 1624

Quoted 8 lines
include/linux/nvme.h +2 -0
           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

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

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

Quoted 1 line · Hannes Reinecke

On 7/17/21 8:30 AM, Sagi Grimberg wrote:

[...]

Quoted 8 lines
Quoted 6 lines

include/linux/nvme.h struct nvmf_property_get_command { · line 1467

Quoted 3 lines
include/linux/nvme.h +3 -0
+
+enum {
+    NVME_AUTH_DHCHAP_HASH_SHA256    = 0x01,

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

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

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

Vlad

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#19 lore raw

PATCH 6/11 nvme: Implement In-Band authentication

In reply to 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.

---
 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
drivers/nvme/host/Kconfig +11 -0
index c3f3d77f1aac..853c546305e9 100644
@@ -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.
drivers/nvme/host/Makefile +1 -0
index cbc509784b2e..03748a55a12b 100644
@@ -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
 
drivers/nvme/host/auth.c new file +813 -0

813 lines not on this page · raw message

drivers/nvme/host/auth.h new file +23 -0
new file mode 100644
index 000000000000..4950b1cb9470
@@ -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 */
drivers/nvme/host/core.c +76 -1

107 lines not on this page · raw message

drivers/nvme/host/fabrics.c +62 -3

113 lines not on this page · raw message

drivers/nvme/host/fabrics.h +8 -0
index a146cb903869..535bc544f0f6 100644
@@ -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;
drivers/nvme/host/nvme.h +15 -0
index 18ef8dd03a90..bcd5b8276c26 100644
@@ -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);
drivers/nvme/host/trace.c +32 -0
index 6543015b6121..66f75d8ea925 100644
@@ -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);
 	}
Signature · 1 line
2.29.2
maintainer
#20 lore raw

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

Quoted 2 lines

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?

Quoted 2 lines

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

bidirectional? not uni-directional?

Quoted 23 lines
---
  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

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

drivers/nvme/host/Kconfig config NVME_TCP · line 96

Quoted 10 lines
drivers/nvme/host/Kconfig +9 -0
  	  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.

Quoted 27 lines
drivers/nvme/host/Kconfig +2 -0
+
+	  If unsure, say N.
drivers/nvme/host/Makefile +1 -0
index cbc509784b2e..03748a55a12b 100644
@@ -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
 
drivers/nvme/host/auth.c new file +7 -0
new file mode 100644
index 000000000000..448a3adebea6
@@ -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>

drivers/nvme/host/auth.c line 17

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+#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...

Quoted 12 lines
drivers/nvme/host/auth.c new file +12 -0
+	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];

drivers/nvme/host/auth.c line 39

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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?

Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+	int id;
+	int hash_len;
+	const char hmac[15];
+	const char digest[15];
+} hash_map[] = {
+	{.id = NVME_AUTH_DHCHAP_HASH_SHA256,
+	 .hash_len = 32,

drivers/nvme/host/auth.c line 56

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	 .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?

Quoted 47 lines
drivers/nvme/host/auth.c new file +47 -0
+{
+	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);

drivers/nvme/host/auth.c line 113

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+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.

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+	dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
+	if (!dhchap_key)
+		return ERR_PTR(-ENOMEM);
+
+	key_len = base64_decode(dhchap_secret + 10,

drivers/nvme/host/auth.c line 128

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+				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..

Quoted 32 lines
drivers/nvme/host/auth.c new file +32 -0
+
+	/* 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;

drivers/nvme/host/auth.c line 170

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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__ ?

Quoted 50 lines
drivers/nvme/host/auth.c new file +50 -0
+	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",

drivers/nvme/host/auth.c line 230

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+			 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 ?

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+{
+	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;

drivers/nvme/host/auth.c line 250

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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.

drivers/nvme/host/auth.c line 257

Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+
+	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 ?

Quoted 24 lines
drivers/nvme/host/auth.c new file +24 -0
+{
+	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:

drivers/nvme/host/auth.c line 291

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+		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?

Quoted 21 lines
drivers/nvme/host/auth.c new file +21 -0
+	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;

drivers/nvme/host/auth.c line 322

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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

drivers/nvme/host/auth.c line 328

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+{
+	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?

Quoted 27 lines
drivers/nvme/host/auth.c new file +27 -0
+		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);
+	}

drivers/nvme/host/auth.c line 365

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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

Quoted 35 lines
drivers/nvme/host/auth.c new file +35 -0
+{
+	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;

drivers/nvme/host/auth.c line 410

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	}
+	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

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+{
+	struct nvmf_auth_dhchap_success2_data *data = buf;
+	size_t size = sizeof(*data);
+
+	memset(buf, 0, size);

drivers/nvme/host/auth.c line 425

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+{
+	struct nvmf_auth_dhchap_failure_data *data = buf;
+	size_t size = sizeof(*data);
+
+	memset(buf, 0, size);
+	data->auth_type = NVME_AUTH_DHCHAP_MESSAGES;

drivers/nvme/host/auth.c line 441

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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.

Quoted 24 lines
drivers/nvme/host/auth.c new file +24 -0
+{
+	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);

drivers/nvme/host/auth.c line 475

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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?

Quoted 34 lines
drivers/nvme/host/auth.c new file +34 -0
+		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)

drivers/nvme/host/auth.c line 519

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+		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.

Quoted 72 lines
drivers/nvme/host/auth.c new file +72 -0
+	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,

drivers/nvme/host/auth.c line 601

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+			   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?

Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+
+	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;

drivers/nvme/host/auth.c line 618

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	}
+
+	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.

drivers/nvme/host/auth.c line 624

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+
+	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...

Quoted 8 lines
drivers/nvme/host/auth.c new file +8 -0
+	} 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);

drivers/nvme/host/auth.c line 642

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+			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?

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+	}
+	return 0;
+}
+
+void nvme_auth_free(struct nvme_dhchap_context *chap)
+{

drivers/nvme/host/auth.c line 658

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	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...

Quoted 26 lines
drivers/nvme/host/auth.c new file +26 -0
+	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:

drivers/nvme/host/auth.c line 694

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	 * 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.

Quoted 283 lines
drivers/nvme/host/auth.c new file +119 -0
+	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;
+}
drivers/nvme/host/auth.h new file +23 -0
new file mode 100644
index 000000000000..4950b1cb9470
@@ -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 */
drivers/nvme/host/core.c +76 -1
index 11779be42186..7ce9b666dc09 100644
@@ -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);
  }
 
 
drivers/nvme/host/fabrics.c +2 -2
index a5469fd9d4c3..6404ab9b604b 100644
@@ -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);

drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 412

Quoted 10 lines
drivers/nvme/host/fabrics.c +10 -0
+	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.

Quoted 31 lines
drivers/nvme/host/fabrics.c +13 -0
+	}
  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;
  }

drivers/nvme/host/fabrics.c static const match_table_t opt_tokens = { · line 576

Quoted 6 lines
drivers/nvme/host/fabrics.c +3 -0
@@ -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?

Quoted 97 lines
drivers/nvme/host/fabrics.c +34 -1
  	{ 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)
drivers/nvme/host/fabrics.h +8 -0
index a146cb903869..535bc544f0f6 100644
@@ -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;
drivers/nvme/host/nvme.h +0 -0
index 18ef8dd03a90..bcd5b8276c26 100644

drivers/nvme/host/nvme.h struct nvme_ctrl { · line 334

Quoted 7 lines
drivers/nvme/host/nvme.h +4 -0
@@ -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.

drivers/nvme/host/trace.c

Quoted 25 lines
drivers/nvme/host/nvme.h +11 -0
+#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);
drivers/nvme/host/trace.c +0 -0
index 6543015b6121..66f75d8ea925 100644

I'd split out the tracing logic.

Quoted 47 lines
drivers/nvme/host/trace.c +32 -0
@@ -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);
  	}
author maintainer
#21 lore raw

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

Quoted 9 lines · Sagi Grimberg
Quoted 2 lines

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.

Quoted 2 lines

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.

Quoted 5 lines
Quoted 2 lines

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.

Quoted 26 lines
Quoted 26 lines

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

drivers/nvme/host/Kconfig +0 -0
index c3f3d77f1aac..853c546305e9 100644
@@ -85,3 +85,14 @@ config NVME_TCP

        from https://github.com/linux-nvme/nvme-cli.
        If unsure, say N.

drivers/nvme/host/Kconfig +3 -0
+
+config NVME_AUTH
+    bool "NVM Express over Fabrics In-Band Authentication"
Quoted 9 lines
Quoted 6 lines

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

Quoted 37 lines
Quoted 24 lines
drivers/nvme/host/Kconfig +2 -0
+
+      If unsure, say N.
drivers/nvme/host/Makefile +0 -0
index cbc509784b2e..03748a55a12b 100644
@@ -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

drivers/nvme/host/auth.c new file +7 -0
new file mode 100644
index 000000000000..448a3adebea6
@@ -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>

drivers/nvme/host/auth.c line 17

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+#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 ?

Quoted 6 lines

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 :-)

Quoted 16 lines
Quoted 16 lines
drivers/nvme/host/auth.c new file +16 -0
+    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;
Quoted 9 lines

drivers/nvme/host/auth.c line 39

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    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.

Quoted 11 lines
Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+    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" },
Quoted 9 lines

drivers/nvme/host/auth.c line 56

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    {.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.

Quoted 52 lines
Quoted 52 lines
drivers/nvme/host/auth.c new file +52 -0
+{
+    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;
Quoted 9 lines

drivers/nvme/host/auth.c line 113

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+    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.

Quoted 16 lines
Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+    dhchap_key = kzalloc(allocated_len, GFP_KERNEL);
+    if (!dhchap_key)
+        return ERR_PTR(-ENOMEM);
+
+    key_len = base64_decode(dhchap_secret + 10,

drivers/nvme/host/auth.c line 128

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+                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);
Quoted 2 lines

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.

Quoted 36 lines
Quoted 36 lines
drivers/nvme/host/auth.c new file +36 -0
+
+    /* 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;
Quoted 9 lines

drivers/nvme/host/auth.c line 170

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+
+    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.

Quoted 54 lines
Quoted 54 lines
drivers/nvme/host/auth.c new file +54 -0
+    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;
+    }
+
Quoted 9 lines

drivers/nvme/host/auth.c line 230

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    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.

Quoted 18 lines
Quoted 18 lines
drivers/nvme/host/auth.c new file +17 -0
+{
+    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;

Quoted 9 lines
Quoted 6 lines

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

Quoted 10 lines

drivers/nvme/host/auth.c line 257

Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+
+    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>'

Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28 -0
+{
+    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) {
Quoted 9 lines
Quoted 6 lines

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

Quoted 26 lines
Quoted 26 lines

+    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);
+

Quoted 9 lines

drivers/nvme/host/auth.c line 322

Quoted 6 lines

+    return 0;

drivers/nvme/host/auth.c new file +5 -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.

Quoted 10 lines

drivers/nvme/host/auth.c line 328

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+{
+    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' ?

Quoted 2 lines

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.

Quoted 31 lines
Quoted 31 lines

+        get_random_bytes(chap->c2, chap->hash_len); +        chap->s2 = nvme_dhchap_seqnum++;
+    } else
+        memset(chap->c2, 0, chap->hash_len);

drivers/nvme/host/auth.c new file +27 -0
+
+    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);
+
Quoted 9 lines

drivers/nvme/host/auth.c line 365

Quoted 6 lines

+    return size;

drivers/nvme/host/auth.c new file +5 -0
+}
+
+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.

Quoted 77 lines
Quoted 35 lines
drivers/nvme/host/auth.c new file +35 -0
+{
+    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;

drivers/nvme/host/auth.c line 410

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+    }
+    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

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+{
+    struct nvmf_auth_dhchap_success2_data *data = buf;
+    size_t size = sizeof(*data);
+
+    memset(buf, 0, size);

drivers/nvme/host/auth.c line 425

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+    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

Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+{
+    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;
+
Quoted 9 lines

drivers/nvme/host/auth.c line 441

Quoted 5 lines

+    return size;

drivers/nvme/host/auth.c new file +4 -0
+}
+
+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.

Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28 -0
+{
+    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;
Quoted 9 lines
Quoted 6 lines

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

Quoted 41 lines
Quoted 41 lines

+        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;

drivers/nvme/host/auth.c new file +10 -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;

Quoted 9 lines
Quoted 5 lines

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

Quoted 3 lines

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

Yes, will be doing so.

Quoted 78 lines
Quoted 78 lines

+    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);

drivers/nvme/host/auth.c new file +12 -0
+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);

drivers/nvme/host/auth.c new file +10 -0
+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;
Quoted 10 lines
Quoted 5 lines

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

Quoted 1 line

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.

Quoted 21 lines
Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+
+    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;

drivers/nvme/host/auth.c line 618

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+    }
+
+    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?

Quoted 2 lines

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.

Quoted 10 lines
Quoted 6 lines

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

Quoted 19 lines
Quoted 10 lines

+    } 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;

Quoted 8 lines

+            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);

Quoted 2 lines

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.

Quoted 10 lines
Quoted 10 lines

+    }
+    return 0;

drivers/nvme/host/auth.c new file +8 -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);
Quoted 9 lines
Quoted 6 lines

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

Quoted 30 lines
Quoted 30 lines

+    kfree(chap);

drivers/nvme/host/auth.c new file +29 -0
+}
+
+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);
Quoted 9 lines
Quoted 6 lines

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

Quoted 288 lines
Quoted 288 lines

+    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;

drivers/nvme/host/auth.c new file +91 -0
+
+    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;

drivers/nvme/host/auth.c new file +3 -0
+
+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);

drivers/nvme/host/auth.c new file +11 -0
+
+out:
+    if (!ret && chap->status)
+        ret = -EPROTO;
+    if (!ret) {
+        ctrl->dhchap_hash = chap->hash_id;
+    }
+    kfree(buf);
+    nvme_auth_free(chap);
+    return ret;
+}
drivers/nvme/host/auth.h new file +23 -0
new file mode 100644
index 000000000000..4950b1cb9470
@@ -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 */
drivers/nvme/host/core.c +0 -0
index 11779be42186..7ce9b666dc09 100644
@@ -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:

drivers/nvme/host/core.c +64 -0
@@ -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
  };

drivers/nvme/host/core.c +9 -0
@@ -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);
   }
drivers/nvme/host/fabrics.c +6 -2
index a5469fd9d4c3..6404ab9b604b 100644
@@ -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);
Quoted 9 lines

drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 412

Quoted 6 lines
drivers/nvme/host/fabrics.c +6 -0
+        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...

Quoted 33 lines
Quoted 33 lines
drivers/nvme/host/fabrics.c +1 -0
+    }

  out_free_data:
      kfree(data);
      return ret;

drivers/nvme/host/fabrics.c +12 -0
@@ -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;

  }

drivers/nvme/host/fabrics.c +0 -0
@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
       { NVMF_OPT_NR_POLL_QUEUES,    "nr_poll_queues=%d"    },
Quoted 9 lines

drivers/nvme/host/fabrics.c static const match_table_t opt_tokens = { · line 576

Quoted 5 lines
drivers/nvme/host/fabrics.c +3 -0
       { 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.

Quoted 97 lines
Quoted 97 lines
drivers/nvme/host/fabrics.c +29 -0
       { 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);

drivers/nvme/host/fabrics.c +1 -0
@@ -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);

drivers/nvme/host/fabrics.c +4 -1
@@ -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)

drivers/nvme/host/fabrics.h +8 -0
index a146cb903869..535bc544f0f6 100644
@@ -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;
drivers/nvme/host/nvme.h +0 -0
index 18ef8dd03a90..bcd5b8276c26 100644
@@ -328,6 +328,12 @@ struct nvme_ctrl {
       struct work_struct ana_work;
Quoted 9 lines

drivers/nvme/host/nvme.h struct nvme_ctrl { · line 334

Quoted 5 lines
drivers/nvme/host/nvme.h +4 -0
   #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.

Quoted 19 lines
Quoted 19 lines
drivers/nvme/host/nvme.h +11 -0
+#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,
Quoted 9 lines

drivers/nvme/host/trace.c

Quoted 6 lines
drivers/nvme/host/nvme.h +0 -0
                u8 opcode);
   int nvme_execute_passthru_rq(struct request *rq);
drivers/nvme/host/trace.c +0 -0
index 6543015b6121..66f75d8ea925 100644

I'd split out the tracing logic.

Okay.

Cheers,

Hannes

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

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

Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

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

Quoted 8 lines
Quoted 6 lines

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

Quoted 2 lines

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.

Quoted 3 lines
Quoted 2 lines

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.

Quoted 9 lines
Quoted 5 lines
Quoted 2 lines

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

Quoted 79 lines
Quoted 26 lines
Quoted 26 lines

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

drivers/nvme/host/Kconfig +0 -0
index c3f3d77f1aac..853c546305e9 100644
@@ -85,3 +85,14 @@ config NVME_TCP

        from https://github.com/linux-nvme/nvme-cli.
        If unsure, say N.

drivers/nvme/host/Kconfig +4 -0
+
+config NVME_AUTH
+    bool "NVM Express over Fabrics In-Band Authentication"
+    depends on NVME_TCP
Quoted 9 lines
Quoted 6 lines

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

Quoted 41 lines
Quoted 24 lines
drivers/nvme/host/Kconfig +2 -0
+
+      If unsure, say N.
drivers/nvme/host/Makefile +0 -0
index cbc509784b2e..03748a55a12b 100644
@@ -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

drivers/nvme/host/auth.c new file +7 -0
new file mode 100644
index 000000000000..448a3adebea6
@@ -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>

drivers/nvme/host/auth.c line 17

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+#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?

Quoted 8 lines
Quoted 2 lines

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

Quoted 114 lines
Quoted 16 lines
Quoted 16 lines
drivers/nvme/host/auth.c new file +16 -0
+    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;
Quoted 9 lines

drivers/nvme/host/auth.c line 39

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    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.

Quoted 11 lines
Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+    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" },
Quoted 9 lines

drivers/nvme/host/auth.c line 56

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    {.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.

Quoted 52 lines
Quoted 52 lines
drivers/nvme/host/auth.c new file +52 -0
+{
+    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;
Quoted 9 lines

drivers/nvme/host/auth.c line 113

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+    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.

Quoted 1 line

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.

Quoted 17 lines
Quoted 17 lines
Quoted 16 lines
drivers/nvme/host/auth.c new file +15 -0
+    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);
Quoted 6 lines
Quoted 2 lines

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.

Quoted 142 lines
Quoted 36 lines
Quoted 36 lines
drivers/nvme/host/auth.c new file +36 -0
+
+    /* 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;
Quoted 9 lines

drivers/nvme/host/auth.c line 170

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+
+    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.

Quoted 54 lines
Quoted 54 lines
drivers/nvme/host/auth.c new file +54 -0
+    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;
+    }
+
Quoted 9 lines

drivers/nvme/host/auth.c line 230

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+    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.

Quoted 18 lines
Quoted 18 lines
drivers/nvme/host/auth.c new file +17 -0
+{
+    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;

Quoted 9 lines
Quoted 6 lines

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

Quoted 2 lines

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

Quoted 103 lines
Quoted 10 lines

drivers/nvme/host/auth.c line 257

Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+
+    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>'

Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28 -0
+{
+    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) {
Quoted 9 lines
Quoted 6 lines

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

Quoted 26 lines
Quoted 26 lines

+    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);
+

Quoted 9 lines

drivers/nvme/host/auth.c line 322

Quoted 6 lines

+    return 0;

drivers/nvme/host/auth.c new file +5 -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.

Quoted 10 lines

drivers/nvme/host/auth.c line 328

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+{
+    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?

Quoted 4 lines

As stated above, this is for bi-directional authentication. And yes, it is poor wording.

'dhchap_bidirectional' ?

dhchap_auth_ctrl maybe.

Quoted 5 lines
Quoted 2 lines

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

Quoted 323 lines
Quoted 32 lines
Quoted 32 lines

+        get_random_bytes(chap->c2, chap->hash_len); +        chap->s2 = nvme_dhchap_seqnum++;
+    } else
+        memset(chap->c2, 0, chap->hash_len);

drivers/nvme/host/auth.c new file +27 -0
+
+    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);
+
Quoted 9 lines

drivers/nvme/host/auth.c line 365

Quoted 6 lines

+    return size;

drivers/nvme/host/auth.c new file +5 -0
+}
+
+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.

Quoted 78 lines
Quoted 41 lines
drivers/nvme/host/auth.c new file +32 -0
+{
+    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;

drivers/nvme/host/auth.c line 410

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+}
+
+static int nvme_auth_dhchap_success2(struct nvme_ctrl *ctrl,
+                     struct nvme_dhchap_context *chap,
+                     void *buf, size_t buf_size)

same

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+{
+    struct nvmf_auth_dhchap_success2_data *data = buf;
+    size_t size = sizeof(*data);
+
+    memset(buf, 0, size);

drivers/nvme/host/auth.c line 425

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+    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

Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+{
+    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;
+
Quoted 9 lines

drivers/nvme/host/auth.c line 441

Quoted 5 lines

+    return size;

drivers/nvme/host/auth.c new file +4 -0
+}
+
+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.

Quoted 28 lines
Quoted 28 lines
drivers/nvme/host/auth.c new file +28 -0
+{
+    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;
Quoted 9 lines
Quoted 6 lines

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

Quoted 41 lines
Quoted 41 lines

+        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;

drivers/nvme/host/auth.c new file +10 -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;

Quoted 9 lines
Quoted 5 lines

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

Quoted 3 lines

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

Yes, will be doing so.

Quoted 79 lines
Quoted 79 lines

+    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);

drivers/nvme/host/auth.c new file +12 -0
+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);

drivers/nvme/host/auth.c new file +11 -0
+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;
Quoted 9 lines
Quoted 4 lines

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

Quoted 7 lines

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?

Quoted 2 lines

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?

Quoted 21 lines
Quoted 21 lines
Quoted 7 lines
drivers/nvme/host/auth.c new file +7 -0
+
+    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;

drivers/nvme/host/auth.c line 618

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+    }
+
+    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?

Quoted 7 lines
Quoted 2 lines

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.

Quoted 1 line

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?

Quoted 32 lines
Quoted 10 lines
Quoted 6 lines

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

Quoted 19 lines
Quoted 18 lines

+    } 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);

Quoted 6 lines
Quoted 2 lines

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.

Quoted 62 lines
Quoted 10 lines
Quoted 10 lines

+    }
+    return 0;

drivers/nvme/host/auth.c new file +8 -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);
Quoted 9 lines
Quoted 6 lines

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

Quoted 30 lines
Quoted 30 lines

+    kfree(chap);

drivers/nvme/host/auth.c new file +29 -0
+}
+
+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);
Quoted 9 lines
Quoted 6 lines

+    if (!buf) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    /* DH-HMAC-CHAP Step 1: send negotiate */

I'd consider breaking these into sub-routines.

Quoted 1 line

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

Quoted 458 lines

Sure, can do.

Quoted 291 lines
Quoted 291 lines

+    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;
+    }

drivers/nvme/host/auth.c new file +37 -0
+
+    /* 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;
+    }

drivers/nvme/host/auth.c new file +29 -0
+
+    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;

drivers/nvme/host/auth.c new file +3 -0
+
+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);

drivers/nvme/host/auth.c new file +11 -0
+
+out:
+    if (!ret && chap->status)
+        ret = -EPROTO;
+    if (!ret) {
+        ctrl->dhchap_hash = chap->hash_id;
+    }
+    kfree(buf);
+    nvme_auth_free(chap);
+    return ret;
+}
drivers/nvme/host/auth.h new file +23 -0
new file mode 100644
index 000000000000..4950b1cb9470
@@ -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 */
drivers/nvme/host/core.c +0 -0
index 11779be42186..7ce9b666dc09 100644
@@ -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:

drivers/nvme/host/core.c +64 -0
@@ -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
  };

drivers/nvme/host/core.c +9 -0
@@ -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);
   }
drivers/nvme/host/fabrics.c +6 -2
index a5469fd9d4c3..6404ab9b604b 100644
@@ -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);
Quoted 9 lines

drivers/nvme/host/fabrics.c int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) · line 412

Quoted 6 lines
drivers/nvme/host/fabrics.c +6 -0
+        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...

Quoted 33 lines
Quoted 33 lines
drivers/nvme/host/fabrics.c +1 -0
+    }

  out_free_data:
      kfree(data);
      return ret;

drivers/nvme/host/fabrics.c +12 -0
@@ -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;

  }

drivers/nvme/host/fabrics.c +0 -0
@@ -548,6 +571,9 @@ static const match_table_t opt_tokens = {
       { NVMF_OPT_NR_POLL_QUEUES,    "nr_poll_queues=%d"    },
Quoted 9 lines

drivers/nvme/host/fabrics.c static const match_table_t opt_tokens = { · line 576

Quoted 5 lines
drivers/nvme/host/fabrics.c +3 -0
       { 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.

Quoted 97 lines
Quoted 97 lines
drivers/nvme/host/fabrics.c +29 -0
       { 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);

drivers/nvme/host/fabrics.c +1 -0
@@ -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);

drivers/nvme/host/fabrics.c +4 -1
@@ -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)

drivers/nvme/host/fabrics.h +8 -0
index a146cb903869..535bc544f0f6 100644
@@ -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;
drivers/nvme/host/nvme.h +0 -0
index 18ef8dd03a90..bcd5b8276c26 100644
@@ -328,6 +328,12 @@ struct nvme_ctrl {
       struct work_struct ana_work;
Quoted 9 lines

drivers/nvme/host/nvme.h struct nvme_ctrl { · line 334

Quoted 5 lines
drivers/nvme/host/nvme.h +4 -0
   #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.

Quoted 2 lines

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
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

#23 lore raw

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

In reply to Hannes Reinecke
Quoted 11 lines · Hannes Reinecke
Quoted 9 lines · Sagi Grimberg
Quoted 2 lines

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.

Quoted 10 lines

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

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

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

[...]

Quoted 23 lines
Quoted 20 lines
Quoted 7 lines

+    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;
+    }

drivers/nvme/host/auth.c line 618

Quoted 9 lines
drivers/nvme/host/auth.c new file +9 -0
+
+    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?

Quoted 2 lines

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

Quoted 8 lines

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

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

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

Vlad

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#24 lore raw

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

Quoted 67 lines · Vladislav Bolkhovitin
Quoted 11 lines · Hannes Reinecke
Quoted 9 lines · Sagi Grimberg
Quoted 2 lines

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.

Quoted 10 lines

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

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

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

[...]

Quoted 23 lines
Quoted 20 lines
Quoted 7 lines

+    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;
+    }

drivers/nvme/host/auth.c line 618

Quoted 9 lines
drivers/nvme/host/auth.c new file +9 -0
+
+    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?

Quoted 2 lines

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

Quoted 8 lines

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.

Quoted 6 lines

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

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

Sure, can do. My reasoning was that the target absolutely has to support the hash functions specified in the PSK, so that will be a safe bet to choose for the hash function in the protocol itself.
(Any other hash function _might_ not be preset on the target.) But if the PSK does not specify a hash the target need to pick one; and for that of course we can use SHA512.

Cheers,

Hannes

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

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

In reply to Hannes Reinecke

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

Hi Hannes,

Quoted 25 lines

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.

---
 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
drivers/nvme/host/Kconfig +0 -0
index c3f3d77f1aac..853c546305e9 100644

drivers/nvme/host/Kconfig config NVME_TCP · line 93

Quoted 9 lines
drivers/nvme/host/Kconfig +6 -0
@@ -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?

Quoted 126 lines
drivers/nvme/host/Kconfig +5 -0
+	help
+	  This provides support for NVMe over Fabrics In-Band Authentication
+	  for the NVMe over TCP transport.
+
+	  If unsure, say N.
drivers/nvme/host/Makefile +1 -0
index cbc509784b2e..03748a55a12b 100644
@@ -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
 
drivers/nvme/host/auth.c new file +103 -0
new file mode 100644
index 000000000000..448a3adebea6
@@ -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);

drivers/nvme/host/auth.c line 113

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+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.

drivers/nvme/host/auth.c line 114

Quoted 1 line
drivers/nvme/host/auth.c new file +1 -0
+	dhchap_key = kzalloc(allocated_len, GFP_KERNEL);

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

Quoted 205 lines
drivers/nvme/host/auth.c new file +205 -0
+	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;
+}
+

drivers/nvme/host/auth.c line 329

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -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?

Quoted 159 lines
drivers/nvme/host/auth.c new file +159 -0
+		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;

drivers/nvme/host/auth.c

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -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",

Quoted 37 lines
drivers/nvme/host/auth.c new file +37 -0
+		__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;

drivers/nvme/host/auth.c

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+}
+
+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",

Quoted 104 lines
drivers/nvme/host/auth.c new file +104 -0
+		__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)

drivers/nvme/host/auth.c line 659

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+		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?

drivers/nvme/host/auth.c line 669

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+}
+
+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

Quoted 178 lines
drivers/nvme/host/auth.c new file +144 -0
+	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;
+}
drivers/nvme/host/auth.h new file +23 -0
new file mode 100644
index 000000000000..4950b1cb9470
@@ -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 */
drivers/nvme/host/core.c +0 -0
index 11779be42186..7ce9b666dc09 100644

drivers/nvme/host/core.c bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct · line 710

Quoted 3 lines
drivers/nvme/host/core.c +0 -0
@@ -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) 

&&

drivers/nvme/host/core.c bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct · line 711

Quoted 2 lines
drivers/nvme/host/core.c +1 -1
-			    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))

Quoted 199 lines
drivers/nvme/host/core.c +0 -0
 				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,

drivers/nvme/host/core.c +73 -0
 	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);
 }
 
 
drivers/nvme/host/fabrics.c +49 -2
index a5469fd9d4c3..6404ab9b604b 100644
@@ -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;

drivers/nvme/host/fabrics.c static int nvmf_parse_options(struct nvmf_ctrl_options · line 883

Quoted 10 lines
drivers/nvme/host/fabrics.c +8 -0
+			}
+			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

Quoted 127 lines
drivers/nvme/host/fabrics.c +5 -1
 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)
drivers/nvme/host/fabrics.h +3 -0
index a146cb903869..535bc544f0f6 100644
@@ -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)

drivers/nvme/host/fabrics.h +3 -0
@@ -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;
drivers/nvme/host/nvme.h +15 -0
index 18ef8dd03a90..bcd5b8276c26 100644
@@ -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);
drivers/nvme/host/trace.c +26 -0
index 6543015b6121..66f75d8ea925 100644
@@ -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);

drivers/nvme/host/trace.c const char *nvme_trace_parse_fabrics_cmd(struct · line 327

Quoted 10 lines
drivers/nvme/host/trace.c +4 -0
@@ -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

author maintainer
#26 lore raw

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

Quoted 31 lines · Stephan Müller

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

Hi Hannes,

Quoted 27 lines

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.

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

drivers/nvme/host/Kconfig +0 -0
index c3f3d77f1aac..853c546305e9 100644
@@ -85,3 +85,14 @@ config NVME_TCP
  	  from https://github.com/linux-nvme/nvme-cli.
 
Quoted 10 lines

drivers/nvme/host/Kconfig config NVME_TCP · line 93

Quoted 7 lines
drivers/nvme/host/Kconfig +6 -0
  	  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.

Quoted 140 lines
Quoted 126 lines
drivers/nvme/host/Kconfig +5 -0
+	help
+	  This provides support for NVMe over Fabrics In-Band Authentication
+	  for the NVMe over TCP transport.
+
+	  If unsure, say N.
drivers/nvme/host/Makefile +1 -0
index cbc509784b2e..03748a55a12b 100644
@@ -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
 
drivers/nvme/host/auth.c new file +103 -0
new file mode 100644
index 000000000000..448a3adebea6
@@ -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);

drivers/nvme/host/auth.c line 113

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+
+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?

Quoted 3 lines

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.

Quoted 5 lines

drivers/nvme/host/auth.c line 114

Quoted 1 line
drivers/nvme/host/auth.c new file +1 -0
+	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.

Quoted 209 lines
Quoted 209 lines
drivers/nvme/host/auth.c new file +209 -0
+	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)
+{
Quoted 9 lines

drivers/nvme/host/auth.c line 329

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+	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.

Quoted 326 lines
Quoted 159 lines
drivers/nvme/host/auth.c new file +159 -0
+		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;

drivers/nvme/host/auth.c

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -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",

Quoted 37 lines
drivers/nvme/host/auth.c new file +37 -0
+		__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;

drivers/nvme/host/auth.c

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+}
+
+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",

Quoted 108 lines
drivers/nvme/host/auth.c new file +108 -0
+		__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)
Quoted 9 lines

drivers/nvme/host/auth.c line 659

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+		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.

Quoted 5 lines
Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+}
+
+int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
+{
+	struct nvme_dhchap_context *chap;
Quoted 9 lines

drivers/nvme/host/auth.c line 669

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+	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

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

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

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

Hi Hannes,

Quoted 10 lines
Quoted 6 lines

drivers/nvme/host/auth.c line 329

Quoted 3 lines
drivers/nvme/host/auth.c new file +3 -0
+	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

#28 lore raw

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

In reply to Hannes Reinecke

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

[...]

Quoted 6 lines
drivers/nvme/host/auth.c new file +6 -0
+struct nvmet_dhchap_hash_map {
+	int id;
+	int hash_len;
+	const char hmac[15];
+	const char digest[15];
+} hash_map[] = {

drivers/nvme/host/auth.c line 54

Quoted 10 lines
drivers/nvme/host/auth.c new file +10 -0
+	{.id = NVME_AUTH_DHCHAP_HASH_SHA256,
+	 .hash_len = 32,
+	 .hmac = "hmac(sha256)", .digest = "sha256" },
+	{.id = NVME_AUTH_DHCHAP_HASH_SHA384,
+	 .hash_len = 48,
+	 .hmac = "hmac(sha384)", .digest = "sha384" },
+	{.id = NVME_AUTH_DHCHAP_HASH_SHA512,
+	 .hash_len = 64,
+	 .hmac = "hmac(sha512)", .digest = "sha512" },
+};

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

Vlad

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme

author maintainer
#29 lore raw

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

Quoted 16 lines · Vladislav Bolkhovitin

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

[...]

Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+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,
Quoted 9 lines

drivers/nvme/host/auth.c line 54

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+	 .hmac = "hmac(sha384)", .digest = "sha384" },
+	{.id = NVME_AUTH_DHCHAP_HASH_SHA512,
+	 .hash_len = 64,
+	 .hmac = "hmac(sha512)", .digest = "sha512" },
+};

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

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

Cheers,

Hannes

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

_______________________________________________
Linux-nvme mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-nvme
#30 lore raw

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

Quoted 27 lines · Hannes Reinecke
Quoted 16 lines · Vladislav Bolkhovitin

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

[...]

Quoted 11 lines
drivers/nvme/host/auth.c new file +11 -0
+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,
Quoted 9 lines

drivers/nvme/host/auth.c line 54

Quoted 5 lines
drivers/nvme/host/auth.c new file +5 -0
+	 .hmac = "hmac(sha384)", .digest = "sha384" },
+	{.id = NVME_AUTH_DHCHAP_HASH_SHA512,
+	 .hash_len = 64,
+	 .hmac = "hmac(sha512)", .digest = "sha512" },
+};

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

Quoted 2 lines

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

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

Vlad

author maintainer
#31 lore raw

PATCH 7/11 nvme-auth: augmented challenge support

In reply to Hannes Reinecke

Implement support for augmented challenge using FFDHE groups.

---
 drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
 1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +371 -32
index 448a3adebea6..754343aced19 100644
@@ -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);
Signature · 1 line
2.29.2
#32 lore raw

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

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

Hi Hannes,

Quoted 359 lines

Implement support for augmented challenge using FFDHE groups.

---
 drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
 1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +212 -32
index 448a3adebea6..754343aced19 100644
@@ -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)
 {

drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct · line 679

Quoted 3 lines
drivers/nvme/host/auth.c +0 -0
@@ -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",

drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, · line 681

Quoted 3 lines
drivers/nvme/host/auth.c +2 -0
 		__func__, chap->qid, chap->s1, chap->transaction);
+	if (chap->dh_tfm) {
+		challenge = kmalloc(chap->hash_len, GFP_KERNEL);

Again, alignment?

Quoted 21 lines
drivers/nvme/host/auth.c +8 -0
+		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);
drivers/nvme/host/auth.c +2 -0
 	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;
 }
 

drivers/nvme/host/auth.c static int nvme_auth_dhchap_ctrl_response(struct · line 738

Quoted 5 lines
drivers/nvme/host/auth.c +2 -0
@@ -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.

drivers/nvme/host/auth.c static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl,

Quoted 10 lines
drivers/nvme/host/auth.c +9 -0
+		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",

Quoted 163 lines
drivers/nvme/host/auth.c +135 -0
 		__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);

drivers/nvme/host/auth.c int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int · line 1150

Quoted 7 lines
drivers/nvme/host/auth.c +1 -0
@@ -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

author maintainer
#33 lore raw

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

Quoted 365 lines · Stephan Müller

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

Hi Hannes,

Quoted 361 lines

Implement support for augmented challenge using FFDHE groups.

---
  drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
  1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +212 -32
index 448a3adebea6..754343aced19 100644
@@ -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,
 
Quoted 8 lines

drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct · line 679

Quoted 1 line
drivers/nvme/host/auth.c +0 -0
  	dev_dbg(ctrl->device, "%s: qid %d host response seq %d transaction

%d\n",

drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, · line 681

Quoted 3 lines
drivers/nvme/host/auth.c +2 -0
  		__func__, chap->qid, chap->s1, chap->transaction);
+	if (chap->dh_tfm) {
+		challenge = kmalloc(chap->hash_len, GFP_KERNEL);

Again, alignment?

Again, why?

Cheers,

Hannes

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

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

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

Hi Hannes,

Quoted 243 lines · Stephan Müller
Quoted 242 lines

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

Hi Hannes,

Quoted 238 lines

Implement support for augmented challenge using FFDHE groups.

---

  drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
  1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +2 -0
index 448a3adebea6..754343aced19 100644
@@ -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"
drivers/nvme/host/auth.c +2 -0
@@ -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;

drivers/nvme/host/auth.c +2 -0
@@ -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];
drivers/nvme/host/auth.c +88 -0
@@ -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;

drivers/nvme/host/auth.c +2 -1
@@ -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;

drivers/nvme/host/auth.c +1 -0
+	data->auth_protocol[0].dhchap.idlist[5] =

NVME_AUTH_DHCHAP_DHGROUP_3072;

drivers/nvme/host/auth.c +1 -0
+	data->auth_protocol[0].dhchap.idlist[6] =

NVME_AUTH_DHCHAP_DHGROUP_4096;

drivers/nvme/host/auth.c +1 -0
+	data->auth_protocol[0].dhchap.idlist[7] =

NVME_AUTH_DHCHAP_DHGROUP_6144;

drivers/nvme/host/auth.c +1 -0
+	data->auth_protocol[0].dhchap.idlist[8] =

NVME_AUTH_DHCHAP_DHGROUP_8192;

return size;

  }
drivers/nvme/host/auth.c +0 -0
@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct

nvme_ctrl
*ctrl, chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;

return -EPROTO;

  	}
drivers/nvme/host/auth.c +19 -12
-	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) {
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

dev_warn(ctrl->device,

"qid %d: DH-HMAC-CHAP: invalid DH value for NULL

DH\n",

Quoted 105 lines
Quoted 105 lines
Quoted 105 lines

chap->qid);

drivers/nvme/host/auth.c +0 -0
@@ -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);
drivers/nvme/host/auth.c +14 -1
+	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);

drivers/nvme/host/auth.c +1 -1
-
+	}
 
  	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)

  {
drivers/nvme/host/auth.c +16 -15
-	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;
+	}
 
Quoted 3 lines
Quoted 3 lines

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl,

Quoted 3 lines
drivers/nvme/host/auth.c +0 -0
  	if (!chap->key) {
 
  		dev_warn(ctrl->device, "qid %d: cannot select hash, no 

key\n",

Quoted 101 lines
Quoted 10 lines
Quoted 10 lines
drivers/nvme/host/auth.c +1 -0
  			 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;
 
Quoted 6 lines

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl,

Quoted 6 lines
drivers/nvme/host/auth.c +1 -0
+		chap->digest_tfm = NULL;
 
  		return -EINVAL;
 
  	}
  	ret = crypto_shash_setkey(chap->shash_tfm, chap->key, chap-

key_len);

Quoted 84 lines
Quoted 84 lines
drivers/nvme/host/auth.c +5 -2
  	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,

drivers/nvme/host/auth.c +55 -0
+					 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,
Quoted 8 lines
Quoted 8 lines

drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct · line 679

Quoted 8 lines

struct nvme_dhchap_context *chap)

  {
drivers/nvme/host/auth.c +0 -0
@@ -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

Quoted 11 lines
Quoted 9 lines

%d\n",

drivers/nvme/host/auth.c static int nvme_auth_dhchap_host_response(struct nvme_ctrl *ctrl, · line 681

Quoted 4 lines
drivers/nvme/host/auth.c +2 -0
  		__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

maintainer
#35 lore raw

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

In reply to Hannes Reinecke
Quoted 1 line · Hannes Reinecke

Implement support for augmented challenge using FFDHE groups.

Please some more info for the change log...

Quoted 179 lines
---
  drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
  1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +106 -13
index 448a3adebea6..754343aced19 100644
@@ -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;

drivers/nvme/host/auth.c static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, · line 399

Quoted 10 lines
drivers/nvme/host/auth.c +10 -0
+			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?

Quoted 26 lines
drivers/nvme/host/auth.c +13 -0
+		}
+		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);

drivers/nvme/host/auth.c static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl, · line 478

Quoted 10 lines
drivers/nvme/host/auth.c +5 -2
  	}
-	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?

Quoted 46 lines
drivers/nvme/host/auth.c +15 -15
  	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;

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 599

Quoted 10 lines
drivers/nvme/host/auth.c +3 -0
+	}
  	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...

Quoted 25 lines
drivers/nvme/host/auth.c +12 -2
  	}
  	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;

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 632

Quoted 10 lines
drivers/nvme/host/auth.c +10 -0
+
+	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...

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 634

Quoted 2 lines
drivers/nvme/host/auth.c +2 -0
+		return ret;
+	}

Spaces between if conditions please?

Quoted 84 lines
drivers/nvme/host/auth.c +59 -0
+	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",

drivers/nvme/host/auth.c static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, · line 792

Quoted 5 lines
drivers/nvme/host/auth.c +2 -0
@@ -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?

Quoted 74 lines
drivers/nvme/host/auth.c +67 -0
  	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);

drivers/nvme/host/auth.c int nvme_auth_generate_key(struct nvme_ctrl *ctrl, · line 928

Quoted 10 lines
drivers/nvme/host/auth.c +10 -0
+	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?

Quoted 79 lines
drivers/nvme/host/auth.c +57 -0
+	} 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);
author maintainer
#36 lore raw

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

Quoted 17 lines · Sagi Grimberg
Quoted 1 line · Hannes Reinecke

Implement support for augmented challenge using FFDHE groups.

Please some more info for the change log...

Quoted 10 lines

---
  drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----

  1 file changed, 371 insertions(+), 32 deletions(-)
drivers/nvme/host/auth.c +0 -0
index 448a3adebea6..754343aced19 100644

[ .. ]

Quoted 19 lines
Quoted 19 lines
drivers/nvme/host/auth.c +9 -4
@@ -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)) {
Quoted 9 lines

drivers/nvme/host/auth.c static int nvme_auth_dhchap_challenge(struct nvme_ctrl *ctrl, · line 399

Quoted 6 lines
drivers/nvme/host/auth.c +6 -0
+            dev_warn(ctrl->device,
+                 "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
+                 chap->qid, gid_name);
+            chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+            chap->dh_tfm = NULL;
+            return -EPROTO;

Why not propogate the error?

Will be doing so.

Quoted 31 lines
Quoted 31 lines
drivers/nvme/host/auth.c +3 -0
+        }
+        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);

drivers/nvme/host/auth.c +12 -1
@@ -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",
Quoted 9 lines

drivers/nvme/host/auth.c static int nvme_auth_dhchap_reply(struct nvme_ctrl *ctrl, · line 478

Quoted 6 lines
drivers/nvme/host/auth.c +3 -1
+            __func__, chap->qid,
+            chap->host_key_len, chap->host_key);
           memcpy(data->rval + 2 * chap->hash_len, chap->host_key,
                  chap->host_key_len);
-
+    }

Is this change only adding the debug print?

Might. I'll check.

Quoted 50 lines
Quoted 50 lines
drivers/nvme/host/auth.c +0 -0
       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;

drivers/nvme/host/auth.c +14 -0
@@ -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;
Quoted 9 lines

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 599

Quoted 5 lines
drivers/nvme/host/auth.c +2 -0
+        crypto_free_shash(chap->digest_tfm);
           crypto_free_shash(chap->shash_tfm);
           chap->shash_tfm = NULL;
+        chap->digest_tfm = NULL;
           return -EINVAL;

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

Already done.

Quoted 131 lines
Quoted 26 lines
drivers/nvme/host/auth.c +4 -2
       }
       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)

drivers/nvme/host/auth.c +6 -0
+{
+    struct crypto_shash *tfm;
+    struct shash_desc *desc;
+    u8 *hashed_key;
+    const char *hash_name;
+    int ret;

drivers/nvme/host/auth.c int nvme_auth_select_hash(struct nvme_ctrl *ctrl, · line 632

Quoted 10 lines
drivers/nvme/host/auth.c +10 -0
+
+    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...

Quoted 2 lines

+        return ret;
+    }

Spaces between if conditions please?

Quoted 87 lines

+    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;

drivers/nvme/host/auth.c +19 -0
+
+    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) {

drivers/nvme/host/auth.c +0 -0
@@ -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)

drivers/nvme/host/auth.c +0 -0
@@ -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",

drivers/nvme/host/auth.c +0 -0
@@ -585,6 +788,8 @@ static int nvme_auth_dhchap_ctrl_response(struct
Quoted 9 lines
Quoted 6 lines
drivers/nvme/host/auth.c +0 -0
 nvme_ctrl *ctrl,
           goto out;
       ret = crypto_shash_final(shash, chap->response);

  out:
+    if (challenge != chap->c2)
+        kfree(challenge);

Just free ?! what about failing?

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

Quoted 79 lines
Quoted 79 lines

      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)

drivers/nvme/host/auth.c +14 -0
+{
+    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);

drivers/nvme/host/auth.c +56 -0
+
+        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);
Quoted 9 lines
Quoted 5 lines

+    if (ret == -EOVERFLOW) {
+        dev_dbg(ctrl->dev,
+            "public key buffer too small, wants %d is %d\n", +            crypto_kpp_maxsize(chap->dh_tfm), chap->host_key_len); +        goto out_free_host;

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

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

Cheers,

Hannes

Signature · 4 lines
Dr. Hannes Reinecke		           Kernel Storage Architect
[email protected]			                  +49 911 74053 688
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), GF: Felix Imendörffer
author maintainer
#37 lore raw

PATCH 8/11 nvmet: Parse fabrics commands on all queues

In reply to Hannes Reinecke

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

---
 drivers/nvme/target/core.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/nvme/target/core.c +2 -0
index ac7210a3ea1c..163f7dc1a929 100644
@@ -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
Signature · 1 line
2.29.2
author maintainer
#39 lore raw

PATCH 9/11 nvmet: Implement basic In-Band Authentication

In reply to 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.

---
 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
drivers/nvme/target/Kconfig +10 -0
index 4be2ececbc45..d5656ef1559e 100644
@@ -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.
drivers/nvme/target/Makefile +1 -0
index 9837e580fa7e..c66820102493 100644
@@ -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
drivers/nvme/target/admin-cmd.c +4 -0
index 0cb98f2bbc8c..320cefc64ee0 100644
@@ -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);
 
drivers/nvme/target/auth.c new file +352 -0

352 lines not on this page · raw message

drivers/nvme/target/configfs.c +70 -1

94 lines not on this page · raw message

drivers/nvme/target/core.c +8 -0
index 163f7dc1a929..b5d7971f566b 100644
@@ -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);
drivers/nvme/target/fabrics-cmd-auth.c new file +460 -0

460 lines not on this page · raw message

drivers/nvme/target/fabrics-cmd.c +28 -2
index 7d0f3523fdab..53fb853cd8fe 100644
@@ -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);
drivers/nvme/target/nvmet.h +71 -0

93 lines not on this page · raw message

Signature · 1 line
2.29.2
#40 lore raw

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

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

Hi Hannes,

drivers/nvme/target/Makefile nvmet-y += core.o configfs.o admin-cmd.o

Quoted 45 lines

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.

---
 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
drivers/nvme/target/Kconfig +10 -0
index 4be2ececbc45..d5656ef1559e 100644
@@ -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.
drivers/nvme/target/Makefile +0 -0
index 9837e580fa7e..c66820102493 100644
@@ -13,6 +13,7 @@ nvmet-y		+= core.o configfs.o admin-cmd.o 

fabrics-cmd.o \

Quoted 65 lines
drivers/nvme/target/Makefile +1 -0
 			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
(quoted diff) +0 -0

b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644

drivers/nvme/target/admin-cmd.c +4 -0
@@ -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);
 
drivers/nvme/target/auth.c new file +37 -0
new file mode 100644
index 000000000000..00c7d051dfb1
@@ -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);
+

drivers/nvme/target/auth.c line 47

Quoted 10 lines
drivers/nvme/target/auth.c new file +10 -0
+		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?

Quoted 87 lines
drivers/nvme/target/auth.c new file +87 -0
+	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);

drivers/nvme/target/auth.c line 144

Quoted 10 lines
drivers/nvme/target/auth.c new file +10 -0
+			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. :-)

drivers/nvme/target/auth.c line 149

Quoted 5 lines
drivers/nvme/target/auth.c new file +5 -0
+				  ctrl->dhchap_key_len);
+out_free_hash:
+	if (ret) {
+		if (ctrl->dhchap_key) {
+			kfree(ctrl->dhchap_key);

kfree_sensitive?

Quoted 9 lines
drivers/nvme/target/auth.c new file +9 -0
+			ctrl->dhchap_key = NULL;
+		}
+		crypto_free_shash(ctrl->shash_tfm);
+		ctrl->shash_tfm = NULL;
+	}
+out_unlock:
+	up_read(&nvmet_config_sem);
+
+	return ret;

drivers/nvme/target/auth.c line 168

Quoted 10 lines
drivers/nvme/target/auth.c new file +10 -0
+}
+
+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?

Quoted 4 lines
drivers/nvme/target/auth.c new file +4 -0
+}
+
+void nvmet_reset_auth(struct nvmet_ctrl *ctrl)
+{

drivers/nvme/target/auth.c line 182

Quoted 10 lines
drivers/nvme/target/auth.c new file +10 -0
+	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?

Quoted 406 lines
drivers/nvme/target/auth.c new file +170 -0
+		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;
+}
drivers/nvme/target/configfs.c +70 -1
index 273555127188..e0760911a761 100644
@@ -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,
 };
drivers/nvme/target/core.c +8 -0
index 163f7dc1a929..b5d7971f566b 100644
@@ -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);
(quoted diff) +0 -0

b/drivers/nvme/target/fabrics-cmd-auth.c new file mode 100644 index 000000000000..962f9f5e9d89

drivers/nvme/target/fabrics-cmd-auth.c new file +29 -0
@@ -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,

drivers/nvme/target/fabrics-cmd-auth.c new file +22 -0
+		 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);

drivers/nvme/target/fabrics-cmd-auth.c new file +42 -0
+		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",

drivers/nvme/target/fabrics-cmd-auth.c line 104

Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10 -0
+		 __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?

Quoted 94 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94 -0
+	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__,

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10 -0
+		 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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 3 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +3 -0
+		} 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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5 -0
+		} else {
+			/* Validate negotiation parameters */
+			status = nvmet_auth_negotiate(req, d);
+			if (status == 0)
+				req->sq->dhchap_step = 

NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2 -0
+			else {
+				req->sq->dhchap_step = 

NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 11 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +11 -0
+				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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2 -0
+		} 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",

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5 -0
+				 __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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 6 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +6 -0
+		} 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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2 -0
+				else {
+					req->sq->dhchap_step = 

NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 13 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +13 -0
+					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",

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 8 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +8 -0
+						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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 1 line
drivers/nvme/target/fabrics-cmd-auth.c new file +1 -0
+				req->sq->dhchap_step = 

NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;

Quoted 90 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +90 -0
+				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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10 -0
+}
+
+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) 

{

Quoted 265 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94 -0
+		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);
+	}
+}
(quoted diff) +0 -0

b/drivers/nvme/target/fabrics-cmd.c index 7d0f3523fdab..53fb853cd8fe 100644

drivers/nvme/target/fabrics-cmd.c +25 -2
@@ -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:

drivers/nvme/target/fabrics-cmd.c +3 -0
@@ -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);
drivers/nvme/target/nvmet.h +27 -0
index 06dd3d537f07..ef8815e137d7 100644
@@ -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)

drivers/nvme/target/nvmet.h +42 -0
@@ -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
Quoted 3 lines

NULL; } +#endif
+
#endif /* _NVMET_H */

Ciao
Stephan

author maintainer
#41 lore raw

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

Quoted 120 lines · Stephan Müller

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

Hi Hannes,

drivers/nvme/target/Makefile nvmet-y += core.o configfs.o admin-cmd.o

Quoted 45 lines

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.

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

drivers/nvme/target/Kconfig +10 -0
index 4be2ececbc45..d5656ef1559e 100644
@@ -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.
drivers/nvme/target/Makefile +0 -0
index 9837e580fa7e..c66820102493 100644
@@ -13,6 +13,7 @@ nvmet-y		+= core.o configfs.o admin-cmd.o

fabrics-cmd.o \

Quoted 70 lines
drivers/nvme/target/Makefile +1 -0
  			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
(quoted diff) +0 -0

b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644

drivers/nvme/target/admin-cmd.c +4 -0
@@ -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);
 
drivers/nvme/target/auth.c new file +42 -0
new file mode 100644
index 000000000000..00c7d051dfb1
@@ -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;
+		}
Quoted 9 lines

drivers/nvme/target/auth.c line 47

Quoted 5 lines
drivers/nvme/target/auth.c new file +5 -0
+		/* 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.

Quoted 93 lines
Quoted 93 lines
drivers/nvme/target/auth.c new file +93 -0
+	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);
+		}
Quoted 9 lines

drivers/nvme/target/auth.c line 144

Quoted 4 lines
drivers/nvme/target/auth.c new file +4 -0
+	}
+	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.

Quoted 8 lines

drivers/nvme/target/auth.c line 149

Quoted 5 lines
drivers/nvme/target/auth.c new file +5 -0
+				  ctrl->dhchap_key_len);
+out_free_hash:
+	if (ret) {
+		if (ctrl->dhchap_key) {
+			kfree(ctrl->dhchap_key);

kfree_sensitive?

Yes, will be fixing it.

Quoted 13 lines
Quoted 13 lines
drivers/nvme/target/auth.c new file +13 -0
+			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)
+{
Quoted 9 lines

drivers/nvme/target/auth.c line 168

Quoted 6 lines
drivers/nvme/target/auth.c new file +6 -0
+	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.

Quoted 8 lines
Quoted 8 lines
drivers/nvme/target/auth.c new file +8 -0
+}
+
+void nvmet_reset_auth(struct nvmet_ctrl *ctrl)
+{
+	if (ctrl->shash_tfm) {
+		crypto_free_shash(ctrl->shash_tfm);
+		ctrl->shash_tfm = NULL;
+	}
Quoted 9 lines

drivers/nvme/target/auth.c line 182

Quoted 6 lines
drivers/nvme/target/auth.c new file +6 -0
+	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.

Quoted 410 lines
Quoted 410 lines
drivers/nvme/target/auth.c new file +170 -0
+		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;
+}
drivers/nvme/target/configfs.c +70 -1
index 273555127188..e0760911a761 100644
@@ -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,
  };
drivers/nvme/target/core.c +8 -0
index 163f7dc1a929..b5d7971f566b 100644
@@ -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);
(quoted diff) +0 -0

b/drivers/nvme/target/fabrics-cmd-auth.c new file mode 100644 index 000000000000..962f9f5e9d89

drivers/nvme/target/fabrics-cmd-auth.c new file +29 -0
@@ -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,

drivers/nvme/target/fabrics-cmd-auth.c new file +22 -0
+		 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);

drivers/nvme/target/fabrics-cmd-auth.c new file +46 -0
+		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;
Quoted 9 lines

drivers/nvme/target/fabrics-cmd-auth.c line 104

Quoted 6 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +6 -0
+
+	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.

Quoted 535 lines
Quoted 94 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94 -0
+	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__,

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10 -0
+		 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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 3 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +3 -0
+		} 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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5 -0
+		} else {
+			/* Validate negotiation parameters */
+			status = nvmet_auth_negotiate(req, d);
+			if (status == 0)
+				req->sq->dhchap_step =

NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2 -0
+			else {
+				req->sq->dhchap_step =

NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 11 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +11 -0
+				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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2 -0
+		} 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",

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 5 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +5 -0
+				 __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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 6 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +6 -0
+		} 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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 2 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +2 -0
+				else {
+					req->sq->dhchap_step =

NVME_AUTH_DHCHAP_MESSAGE_FAILURE1;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 13 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +13 -0
+					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",

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 8 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +8 -0
+						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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 1 line
drivers/nvme/target/fabrics-cmd-auth.c new file +1 -0
+				req->sq->dhchap_step =

NVME_AUTH_DHCHAP_MESSAGE_FAILURE2;

Quoted 90 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +90 -0
+				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;

drivers/nvme/target/fabrics-cmd-auth.c

Quoted 10 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +10 -0
+}
+
+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)

{

Quoted 260 lines
drivers/nvme/target/fabrics-cmd-auth.c new file +94 -0
+		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);
+	}
+}
(quoted diff) +0 -0

b/drivers/nvme/target/fabrics-cmd.c index 7d0f3523fdab..53fb853cd8fe 100644

drivers/nvme/target/fabrics-cmd.c +25 -2
@@ -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:

drivers/nvme/target/fabrics-cmd.c +3 -0
@@ -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);
drivers/nvme/target/nvmet.h +27 -0
index 06dd3d537f07..ef8815e137d7 100644
@@ -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)

drivers/nvme/target/nvmet.h +37 -0
@@ -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;
+}
Quoted 8 lines
Quoted 8 lines
drivers/nvme/target/nvmet.h +5 -0
+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

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

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

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

Hi Hannes,

Quoted 258 lines · Stephan Müller
Quoted 133 lines

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

Hi Hannes,

drivers/nvme/target/Makefile nvmet-y += core.o configfs.o admin-cmd.o

Quoted 48 lines

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.

---

  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

drivers/nvme/target/Kconfig +10 -0
index 4be2ececbc45..d5656ef1559e 100644
@@ -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.
drivers/nvme/target/Makefile +0 -0
index 9837e580fa7e..c66820102493 100644
@@ -13,6 +13,7 @@ nvmet-y		+= core.o configfs.o admin-cmd.o

fabrics-cmd.o \

Quoted 78 lines
drivers/nvme/target/Makefile +1 -0
  			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

(quoted diff) +0 -0

b/drivers/nvme/target/admin-cmd.c index 0cb98f2bbc8c..320cefc64ee0 100644

drivers/nvme/target/admin-cmd.c +4 -0
@@ -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);
drivers/nvme/target/auth.c new file +42 -0
new file mode 100644
index 000000000000..00c7d051dfb1
@@ -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;
+		}
Quoted 8 lines

drivers/nvme/target/auth.c line 47

Quoted 5 lines
drivers/nvme/target/auth.c new file +5 -0
+		/* 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.

Quoted 93 lines
Quoted 93 lines
drivers/nvme/target/auth.c new file +93 -0
+	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);
+		}
Quoted 8 lines

drivers/nvme/target/auth.c line 144

Quoted 4 lines
drivers/nvme/target/auth.c new file +4 -0
+	}
+	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.

Quoted 4 lines

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.

Quoted 7 lines
Quoted 3 lines

drivers/nvme/target/auth.c line 145

Quoted 1 line
drivers/nvme/target/auth.c new file +1 -0
+	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