nvme: In-band authentication support
Subsystems (5)
Hannes Reinecke
maintainerv2 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.
Changes to the original submission:
- Included reviews from Vladislav
- Included reviews from Sagi
- Implemented re-authentication support
- Fixed up key handling
Hannes Reinecke (13):
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-fabrics: decode 'authentication required' connect error
nvme: Implement In-Band authentication
nvme-auth: Diffie-Hellman key exchange support
nvmet: Parse fabrics commands on all queues
nvmet: Implement basic In-Band Authentication
nvmet-auth: Diffie-Hellman key exchange support
nvmet-auth: expire authentication sessions
nvme: add non-standard ECDH and curve25517 algorithms
crypto/Kconfig | 8 + crypto/Makefile | 1 + crypto/ffdhe_helper.c | 880 ++++++++++++++ crypto/kpp.c | 6 + crypto/shash.c | 6 + drivers/nvme/host/Kconfig | 12 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1470 ++++++++++++++++++++++++ drivers/nvme/host/auth.h | 33 + drivers/nvme/host/core.c | 79 +- drivers/nvme/host/fabrics.c | 77 +- drivers/nvme/host/fabrics.h | 6 + drivers/nvme/host/nvme.h | 30 + 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 | 442 +++++++ drivers/nvme/target/configfs.c | 102 +- drivers/nvme/target/core.c | 10 + drivers/nvme/target/fabrics-cmd-auth.c | 506 ++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 70 ++ include/crypto/ffdhe.h | 24 + include/crypto/hash.h | 2 + include/crypto/kpp.h | 2 + include/linux/base64.h | 16 + include/linux/nvme.h | 188 ++- lib/Makefile | 2 +- lib/base64.c | 115 ++ 30 files changed, 4154 insertions(+), 11 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
Hannes Reinecke
maintainerPATCH v2 1/13 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
@@ -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
@@ -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
Hannes Reinecke
maintainerPATCH v2 2/13 crypto: add crypto_has_kpp()
In reply to Hannes ReineckeAdd 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
@@ -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
@@ -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
Hannes Reinecke
maintainerPATCH v2 3/13 crypto/ffdhe: Finite Field DH Ephemeral Parameters
In reply to Hannes ReineckeAdd helper functions to generaten Finite Field DH Ephemeral Parameters as specified in RFC 7919.
--- crypto/Kconfig | 8 + crypto/Makefile | 1 + crypto/ffdhe_helper.c | 880 +++++++++++++++++++++++++++++++++++++++++ include/crypto/ffdhe.h | 24 ++ 4 files changed, 913 insertions(+) create mode 100644 crypto/ffdhe_helper.c create mode 100644 include/crypto/ffdhe.h
crypto/Kconfig +8 -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" + 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
@@ -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 +880 -0
880 lines not on this page · raw message
include/crypto/ffdhe.h new file +24 -0
@@ -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
Hannes Reinecke
maintainerPATCH v2 4/13 lib/base64: RFC4648-compliant base64 encoding
In reply to Hannes ReineckeAdd RFC4648-compliant base64 encoding and decoding routines.
--- include/linux/base64.h | 16 ++++++ lib/Makefile | 2 +- lib/base64.c | 115 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 include/linux/base64.h create mode 100644 lib/base64.c
include/linux/base64.h new file +16 -0
@@ -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
@@ -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 +115 -0
115 lines not on this page · raw message
Signature · 1 line
2.29.2
Re: [PATCH 04/13] lib/base64: RFC4648-compliant base64 encoding
Quoted 17 lines · Hannes Reinecke
Add RFC4648-compliant base64 encoding and decoding routines.
Signed-off-by Hannes Reinecke <[email protected]>--- include/linux/base64.h | 16 ++++++ lib/Makefile | 2 +- lib/base64.c | 115 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 include/linux/base64.h create mode 100644 lib/base64.cinclude/linux/base64.h new file +0
-0
include/linux/base64.h line 4
Quoted 4 lines
include/linux/base64.h new file +4
-0@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* base64 encoding, lifted from fs/crypto/fname.c. +*/
As I mentioned previously, please make it very clear which variant of Base64 it is (including whether padding is included and required or not), and update all the comments accordingly. I've done so in fs/crypto/fname.c with
https://lkml.kernel.org/r/[email protected]. It would probably be best to start over with a copy of that and modify it accordingly to implement the desired variant of Base64.
include/linux/base64.h line 9
Quoted 5 lines
include/linux/base64.h new file +5
-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.
"@len: number of bytes to decode" is ambiguous as it could refer to either @src or @dst. I've fixed this in the fs/crypto/fname.c version.
include/linux/base64.h line 14
Quoted 5 lines
include/linux/base64.h new file +5
-0+* +* Decodes the base64-encoded bytes @src according to RFC 4648. +* +* Return: number of decoded bytes +*/
Shouldn't this return an error if the string is invalid? Again, see the latest fs/crypto/fname.c version.
Quoted 6 lines
include/linux/base64.h new file +6
-0+int base64_decode (const char *src,int len,u8 *dst) +{ +int i, bits =0 , pad =0 ; +u32 ac =0 ; +size_t dst_len =0 ; +
include/linux/base64.h
Quoted 10 lines
include/linux/base64.h new file +10
-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++) {
strlen() shouldn't be used in a loop condition like this.
- Eric
Hannes Reinecke
maintainerRe: [PATCH 04/13] lib/base64: RFC4648-compliant base64 encoding
Quoted 21 lines · Eric Biggers
Quoted 20 lines · Hannes Reinecke
Add RFC4648-compliant base64 encoding and decoding routines.
Signed-off-by Hannes Reinecke <[email protected]>--- include/linux/base64.h | 16 ++++++ lib/Makefile | 2 +- lib/base64.c | 115 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-)create mode 100644
include/linux/base64.h
create mode 100644lib/base64.cinclude/linux/base64.h new file +3
-0@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* base64 encoding, lifted from fs/crypto/fname.c.
Quoted 9 lines
include/linux/base64.h line 4
Quoted 1 line
include/linux/base64.h new file +1
-0+*/ As I mentioned previously, please make it very clear which variant of Base64 it is (including whether padding is included and required or not), and update all the comments accordingly. I've done so in
fs/crypto/fname.cwith
https://lkml.kernel.org/r/[email protected]. It would probably be best to start over with a copy of that and modify it accordingly to implement the desired variant of Base64.
Hmm. Looking into it.
Quoted 9 lines
include/linux/base64.h line 9
Quoted 5 lines
include/linux/base64.h new file +5
-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. "@len: number of bytes to decode" is ambiguous as it could refer to either @src or @dst. I've fixed this in the
fs/crypto/fname.cversion.
Ok, will be updating.
Quoted 9 lines
include/linux/base64.h line 14
Quoted 5 lines
include/linux/base64.h new file +5
-0+* +* Decodes the base64-encoded bytes @src according to RFC 4648. +* +* Return: number of decoded bytes +*/ Shouldn't this return an error if the string is invalid? Again, see the latest
fs/crypto/fname.cversion.
Indeed, it should. Will be fixing it up.
Quoted 10 lines
Quoted 10 lines
include/linux/base64.h new file +10
-0+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] =='=' ) {
Quoted 9 lines
include/linux/base64.h
Quoted 6 lines
include/linux/base64.h new file +6
-0+ pad++; +if (i +1 < len && src[i +1 ] =='=' ) + pad++; +break ; + } +for (c =0 ; c <strlen (lookup_table); c++) {
strlen()shouldn't be used in a loop condition like this.
Why not?
'lookup_table' is pretty much static, so where't the problem?
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
Hannes Reinecke
maintainerPATCH v2 5/13 nvme: add definitions for NVMe In-Band authentication
In reply to Hannes Reinecke--- include/linux/nvme.h | 186 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-)
include/linux/nvme.h +185 -1
@@ -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,183 @@ 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[16 ]; +}; + +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[16 ]; +}; + +/* 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 , +}; + +/* Defined hash functions for DH-HMAC-CHAP authentication */ +enum { +NVME_AUTH_DHCHAP_SHA256 =0x01 , +NVME_AUTH_DHCHAP_SHA384 =0x02 , +NVME_AUTH_DHCHAP_SHA512 =0x03 , +}; + +/* Defined Diffie-Hellman group identifiers for DH-HMAC-CHAP authentication */ +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; +__le16 rsvd; +__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; +__u16 rsvd1; +__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; +__le16 rsvd1; +__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; +__le16 rsvd1; +__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; +__le16 rsvd1; +__le16 t_id; +__u8 rsvd2[10 ]; +}; + +struct nvmf_auth_dhchap_failure_data { +__u8 auth_type; +__u8 auth_id; +__le16 rsvd1; +__le16 t_id; +__u8 rescode; +__u8 rescode_exp; +}; + +enum { +NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED =0x01 , +}; + +enum { +NVME_AUTH_DHCHAP_FAILURE_FAILED =0x01 , +NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE =0x02 , +NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH =0x03 , +NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE =0x04 , +NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE =0x05 , +NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD =0x06 , +NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE =0x07 , +}; + +struct nvme_dbbuf {__u8 opcode;__u8 flags;
@@ -1436,6 +1618,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
Hannes Reinecke
maintainerPATCH v2 6/13 nvme-fabrics: decode 'authentication required' connect error
In reply to Hannes ReineckeThe 'connect' command might fail with NVME_SC_AUTH_REQUIRED, so we should be decoding this error, too.
--- drivers/nvme/host/fabrics.c | 4 ++++ 1 file changed, 4 insertions(+)
drivers/nvme/host/fabrics.c +4 -0
@@ -332,6 +332,10 @@ static void nvmf_log_connect_error(struct nvme_ctrl *ctrl,dev_err (ctrl->device,"Connect command failed: host path error\n" );break ; +case NVME_SC_AUTH_REQUIRED : +dev_err (ctrl->device, +"Connect command failed: authentication required\n" ); +break ;default : dev_err(ctrl->device, "Connect command failed, error wo/DNR bit: %d\n",
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v2 7/13 nvme: Implement In-Band authentication
In reply to Hannes ReineckeImplement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two new fabric options 'dhchap_secret' to specify the pre-shared key (in ASCII respresentation according to NVMe 2.0 section 8.13.5.8 'Secret representation') and 'dhchap_bidi' to request bi-directional authentication of both the host and the controller. Re-authentication can be triggered by writing the PSK into the new controller sysfs attribute 'dhchap_secret'.
--- drivers/nvme/host/Kconfig | 12 + drivers/nvme/host/Makefile | 1 + drivers/nvme/host/auth.c | 1282 +++++++++++++++++++++++++++++++++++ drivers/nvme/host/auth.h | 25 + drivers/nvme/host/core.c | 79 ++- drivers/nvme/host/fabrics.c | 73 +- drivers/nvme/host/fabrics.h | 6 + drivers/nvme/host/nvme.h | 30 + drivers/nvme/host/trace.c | 32 + 9 files changed, 1534 insertions(+), 6 deletions(-) create mode 100644 drivers/nvme/host/auth.c create mode 100644 drivers/nvme/host/auth.h
drivers/nvme/host/Kconfig +12 -0
@@ -85,3 +85,15 @@ 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_CORE + select CRYPTO_HMAC + 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
@@ -16,6 +16,7 @@ nvme-core-$(CONFIG_NVM) += lightnvm.o nvme-core-$(CONFIG_BLK_DEV_ZONED) += zns.o nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o +nvme-core-$(CONFIG_NVME_AUTH) += auth.o nvme-y += pci.o
drivers/nvme/host/auth.c new file +1282 -0
1,282 lines not on this page · raw message
drivers/nvme/host/auth.h new file +25 -0
@@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions +*/ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +#include <crypto/kpp.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); + +unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret, +size_t *dhchap_key_len); +u8 *nvme_auth_transform_key (u8 *key,size_t key_len,u8 key_hash,char *nqn); + +#endif /* _NVME_AUTH_H */
drivers/nvme/host/core.c +76 -3
154 lines not on this page · raw message
drivers/nvme/host/fabrics.c +70 -3
128 lines not on this page · raw message
drivers/nvme/host/fabrics.h +6 -0
@@ -67,6 +67,8 @@ 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_BIDI = 1 << 23, };/**
@@ -96,6 +98,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_bidi: enable DH-HMAC-CHAP bi-directional authentication * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP)
@@ -120,6 +124,8 @@ struct nvmf_ctrl_options {unsigned int kato;struct nvmf_host *host;int max_reconnects; +char *dhchap_secret; +bool dhchap_bidi;bool disable_sqflow;bool hdr_digest;bool data_digest;
drivers/nvme/host/nvme.h +30 -0
@@ -328,6 +328,15 @@ struct nvme_ctrl {struct work_struct ana_work;#endif +#ifdef CONFIG_NVME_AUTH +struct work_struct dhchap_auth_work; +struct list_head dhchap_auth_list; +struct mutex dhchap_auth_mutex; +unsigned char *dhchap_key; +size_t dhchap_key_len; +u16 transaction; +#endif +/* Power saving configuration */ u64 ps_max_latency_us;bool apst_enabled;
@@ -874,6 +883,27 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl)return ctrl->sgls & ((1 <<0 ) | (1 <<1 )); } +#ifdef CONFIG_NVME_AUTH +void nvme_auth_init_ctrl (struct nvme_ctrl *ctrl); +void nvme_auth_stop (struct nvme_ctrl *ctrl); +int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid); +int nvme_auth_wait (struct nvme_ctrl *ctrl,int qid); +void nvme_auth_free (struct nvme_ctrl *ctrl); +int nvme_auth_generate_key (struct nvme_ctrl *ctrl); +#else +static inline void nvme_auth_init_ctrl (struct nvme_ctrl *ctrl) {}; +static inline void nvme_auth_stop (struct nvme_ctrl *ctrl) {}; +static inline int nvme_auth_negotiate (struct nvme_ctrl *ctrl,int qid) +{ +return -EPROTONOSUPPORT ; +} +static inline int nvme_auth_wait (struct nvme_ctrl *ctrl,int qid) +{ +return NVME_SC_AUTH_REQUIRED ; +} +static inline void nvme_auth_free (struct nvme_ctrl *ctrl) {}; +#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
@@ -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
Hannes Reinecke
maintainerPATCH v2 8/13 nvme-auth: Diffie-Hellman key exchange support
In reply to Hannes ReineckeImplement Diffie-Hellman key exchange using FFDHE groups for NVMe In-Band Authentication.
--- drivers/nvme/host/auth.c | 190 +++++++++++++++++++++++++++++++++++---- drivers/nvme/host/auth.h | 8 ++ 2 files changed, 181 insertions(+), 17 deletions(-)
drivers/nvme/host/auth.c +173 -17
@@ -36,6 +36,12 @@ struct nvme_dhchap_queue_context {u8 c2[64 ];u8 response[64 ];u8 *host_response; +u8 *ctrl_key; +int ctrl_key_len; +u8 *host_key; +int host_key_len; +u8 *sess_key; +int sess_key_len; }; static struct nvme_auth_dhgroup_map {
@@ -611,6 +617,7 @@ static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl,struct nvmf_auth_dhchap_challenge_data *data = chap->buf;size_t size =sizeof (*data) + data->hl + data->dhvlen;const char *hmac_name; +const char *kpp_name;if (chap->buf_size < size) { chap->status =NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ;
@@ -665,9 +672,9 @@ static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl, chap->hash_len = data->hl;dev_dbg (ctrl->device,"qid %d: selected hash %s\n" , chap->qid, hmac_name); - - gid_name =nvme_auth_dhgroup_kpp (data->dhgid); -if (!gid_name) { +select_kpp: + kpp_name =nvme_auth_dhgroup_kpp (data->dhgid); +if (!kpp_name) {dev_warn (ctrl->device,"qid %d: invalid DH group id %d\n" , chap->qid, data->dhgid);
@@ -676,6 +683,8 @@ static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl, }if (data->dhgid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { +const char *gid_name =nvme_auth_dhgroup_name (data->dhgid); +if (data->dhvlen ==0 ) { dev_warn(ctrl->device, "qid %d: empty DH value\n",
@@ -683,31 +692,55 @@ static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl, chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ;return -EPROTO ; } - chap->dh_tfm =crypto_alloc_kpp (gid_name,0 ,0 ); +if (chap->dh_tfm && chap->dhgroup_id == data->dhgid) { +dev_dbg (ctrl->device, +"qid %d: reuse existing DH group %s\n" , + chap->qid, gid_name); + goto skip_kpp; + } + chap->dh_tfm =crypto_alloc_kpp (kpp_name,0 ,0 );if (IS_ERR (chap->dh_tfm)) {int ret =PTR_ERR (chap->dh_tfm);dev_warn (ctrl->device, -"qid %d: failed to initialize %s\n" , +"qid %d: failed to initialize DH group %s\n" , chap->qid, gid_name); chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; chap->dh_tfm =NULL ;return ret; } - chap->dhgroup_id = data->dhgid; - }else if (data->dhvlen !=0 ) { -dev_warn (ctrl->device, -"qid %d: invalid DH value for NULL DH\n" , - chap->qid); - chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; -return -EPROTO ; +/* Clear host key to avoid accidental reuse */ +kfree_sensitive (chap->host_key); + chap->host_key_len =0 ; +dev_dbg (ctrl->device,"qid %d: selected DH group %s\n" , + chap->qid, gid_name); + }else { +if (data->dhvlen !=0 ) { +dev_warn (ctrl->device, +"qid %d: invalid DH value for NULL DH\n" , + chap->qid); + chap->status =NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; +return -EPROTO ; + } +if (chap->dh_tfm) { +crypto_free_kpp (chap->dh_tfm); + chap->dh_tfm =NULL ; + } } -dev_dbg (ctrl->device,"qid %d: selected DH group %s\n" , - chap->qid, gid_name); - -select_kpp: + chap->dhgroup_id = data->dhgid; +skip_kpp: 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 ; }
@@ -725,6 +758,8 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl, }else memset (chap->c2,0 , chap->hash_len); +if (chap->host_key_len) + size += chap->host_key_len;if (chap->buf_size < size) { chap->status =NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ;
@@ -735,7 +770,7 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl, data->auth_id =NVME_AUTH_DHCHAP_MESSAGE_REPLY ; data->t_id =cpu_to_le16 (chap->transaction); data->hl = chap->hash_len; - data->dhvlen =0 ; + 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_bidi) {
@@ -746,6 +781,13 @@ static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl,memcpy (data->rval + chap->hash_len, chap->c2, chap->hash_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; }
@@ -832,6 +874,27 @@ 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->host_response) { + chap->host_response =nvme_auth_transform_key (ctrl->dhchap_key, + chap->hash_len, chap->hash_id, + ctrl->opts->host->nqn); +if (IS_ERR (chap->host_response)) { + ret =PTR_ERR (chap->host_response); + chap->host_response =NULL ; +return ret; + } + } + ret =crypto_shash_setkey (chap->shash_tfm, + chap->host_response, chap->hash_len); +if (ret) { +dev_warn (ctrl->device,"qid %d: failed to set key, error %d\n" , + chap->qid, ret); + goto out; + } +dev_dbg (ctrl->device, +"%s: using key %*ph\n" , __func__, + (int )chap->hash_len, chap->host_response);if (chap->dh_tfm) { challenge =kmalloc (chap->hash_len,GFP_KERNEL );if (!challenge) {
@@ -890,9 +953,28 @@ static int nvme_auth_dhchap_ctrl_response(struct nvme_ctrl *ctrl, struct nvme_dhchap_queue_context *chap) {SHASH_DESC_ON_STACK (shash, chap->shash_tfm); +u8 *ctrl_response;u8 buf[4 ], *challenge = chap->c2;int ret; + ctrl_response =nvme_auth_transform_key (ctrl->dhchap_key, + chap->hash_len, chap->hash_id, + ctrl->opts->subsysnqn); +if (IS_ERR (ctrl_response)) { + ret =PTR_ERR (ctrl_response); +return ret; + } + ret =crypto_shash_setkey (chap->shash_tfm, + ctrl_response, ctrl->dhchap_key_len); +if (ret) { +dev_warn (ctrl->device,"qid %d: failed to set key, error %d\n" , + chap->qid, ret); + goto out; + } +dev_dbg (ctrl->device, +"%s: using key %*ph\n" , __func__, + (int )ctrl->dhchap_key_len, ctrl_response); +if (chap->dh_tfm) { challenge =kmalloc (chap->hash_len,GFP_KERNEL );if (!challenge) {
@@ -980,8 +1062,77 @@ int nvme_auth_generate_key(struct nvme_ctrl *ctrl) }EXPORT_SYMBOL_GPL (nvme_auth_generate_key); +static int nvme_auth_dhchap_exponential (struct nvme_ctrl *ctrl, +struct nvme_dhchap_queue_context *chap) +{ +int ret; + +if (chap->host_key && chap->host_key_len) { +dev_dbg (ctrl->device, +"qid %d: reusing host key\n" , chap->qid); + goto gen_sesskey; + } + ret =nvme_auth_gen_privkey (chap->dh_tfm, chap->dhgroup_id); +if (ret <0 ) { + chap->status =NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +return ret; + } + + chap->host_key_len = +nvme_auth_dhgroup_pubkey_size (chap->dhgroup_id); + + chap->host_key =kzalloc (chap->host_key_len,GFP_KERNEL ); +if (!chap->host_key) { + chap->host_key_len =0 ; + chap->status =NVME_AUTH_DHCHAP_FAILURE_FAILED ; +return -ENOMEM ; + } + ret =nvme_auth_gen_pubkey (chap->dh_tfm, + chap->host_key, chap->host_key_len); +if (ret) { +dev_dbg (ctrl->device, +"failed to generate public key, error %d\n" , ret); +kfree (chap->host_key); + chap->host_key =NULL ; + chap->host_key_len =0 ; + chap->status =NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +return ret; + } + +gen_sesskey: + chap->sess_key_len = chap->host_key_len; + chap->sess_key =kmalloc (chap->sess_key_len,GFP_KERNEL ); +if (!chap->sess_key) { + chap->sess_key_len =0 ; + chap->status =NVME_AUTH_DHCHAP_FAILURE_FAILED ; +return -ENOMEM ; + } + + ret =nvme_auth_gen_shared_secret (chap->dh_tfm, + chap->ctrl_key, chap->ctrl_key_len, + chap->sess_key, chap->sess_key_len); +if (ret) { +dev_dbg (ctrl->device, +"failed to generate shared secret, error %d\n" , ret); +kfree_sensitive (chap->sess_key); + chap->sess_key =NULL ; + chap->sess_key_len =0 ; + chap->status =NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +return ret; + } +dev_dbg (ctrl->device,"shared secret %*ph\n" , + (int )chap->sess_key_len, chap->sess_key); +return 0 ; +} +static void nvme_auth_reset (struct nvme_dhchap_queue_context *chap) { +kfree_sensitive (chap->ctrl_key); + chap->ctrl_key =NULL ; + chap->ctrl_key_len =0 ; +kfree_sensitive (chap->sess_key); + chap->sess_key =NULL ; + chap->sess_key_len =0 ; chap->status =0 ; chap->error =0 ; chap->s1 =0 ;
@@ -995,6 +1146,11 @@ static void __nvme_auth_free(struct nvme_dhchap_queue_context *chap) {if (chap->shash_tfm)crypto_free_shash (chap->shash_tfm); +if (chap->dh_tfm) +crypto_free_kpp (chap->dh_tfm); +kfree_sensitive (chap->ctrl_key); +kfree_sensitive (chap->host_key); +kfree_sensitive (chap->sess_key);kfree_sensitive (chap->host_response);kfree (chap->buf);kfree (chap);
drivers/nvme/host/auth.h +8 -0
@@ -21,5 +21,13 @@ int nvme_auth_hmac_id(const char *hmac_name);unsigned char *nvme_auth_extract_secret (unsigned char *dhchap_secret,size_t *dhchap_key_len);u8 *nvme_auth_transform_key (u8 *key,size_t key_len,u8 key_hash,char *nqn); +int nvme_auth_augmented_challenge (u8 hmac_id,u8 *skey,size_t skey_len, +u8 *challenge,u8 *aug,size_t hlen); +int nvme_auth_gen_privkey (struct crypto_kpp *dh_tfm,int dh_gid); +int nvme_auth_gen_pubkey (struct crypto_kpp *dh_tfm, +u8 *host_key,size_t host_key_len); +int nvme_auth_gen_shared_secret (struct crypto_kpp *dh_tfm, +u8 *ctrl_key,size_t ctrl_key_len, +u8 *sess_key,size_t sess_key_len);#endif /* _NVME_AUTH_H */
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v2 9/13 nvmet: Parse fabrics commands on all queues
In reply to Hannes ReineckeFabrics 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
@@ -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
Hannes Reinecke
maintainerPATCH v2 10/13 nvmet: Implement basic In-Band Authentication
In reply to Hannes ReineckeImplement NVMe-oF In-Band authentication according to NVMe TPAR 8006. This patch adds two additional configfs entries 'dhchap_key' and 'dhchap_hash' to the 'host' configfs directory. The 'dhchap_key' needs to be
in the ASCII format as specified in NVMe 2.0 section 8.13.5.8 'Secret representation'.
'dhchap_hash' is taken from the hash specified in the ASCII representation of the key, or defaults to 'hmac(sha256)' if no key transformation has been specified.
--- drivers/nvme/target/Kconfig | 10 + drivers/nvme/target/Makefile | 1 + drivers/nvme/target/admin-cmd.c | 4 + drivers/nvme/target/auth.c | 301 ++++++++++++++++ drivers/nvme/target/configfs.c | 71 +++- drivers/nvme/target/core.c | 8 + drivers/nvme/target/fabrics-cmd-auth.c | 464 +++++++++++++++++++++++++ drivers/nvme/target/fabrics-cmd.c | 30 +- drivers/nvme/target/nvmet.h | 63 ++++ 9 files changed, 949 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
@@ -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
@@ -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
@@ -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 +301 -0
301 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
@@ -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_destroy_auth (ctrl); +ida_simple_remove (&cntlid_ida, ctrl->cntlid);nvmet_async_events_free (ctrl);
drivers/nvme/target/fabrics-cmd-auth.c new file +464 -0
464 lines not on this page · raw message
drivers/nvme/target/fabrics-cmd.c +28 -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); +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 +63 -0
85 lines not on this page · raw message
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v2 11/13 nvmet-auth: Diffie-Hellman key exchange support
In reply to Hannes ReineckeImplement Diffie-Hellman key exchange using FFDHE groups for NVMe In-Band Authentication.
This patch adds a new host configfs attribute 'dhchap_dhgroup' to select the FFDHE group to use.
--- drivers/nvme/target/auth.c | 148 ++++++++++++++++++++++++- drivers/nvme/target/configfs.c | 31 ++++++ drivers/nvme/target/fabrics-cmd-auth.c | 30 ++++- drivers/nvme/target/nvmet.h | 6 + 4 files changed, 208 insertions(+), 7 deletions(-)
drivers/nvme/target/auth.c +144 -4
@@ -53,6 +53,71 @@ int nvmet_auth_set_host_key(struct nvmet_host *host, const char *secret)return 0 ; } +int nvmet_setup_dhgroup (struct nvmet_ctrl *ctrl,int dhgroup_id) +{ +struct nvmet_host_link *p; +struct nvmet_host *host =NULL ; +const char *dhgroup_kpp; +int ret = -ENOTSUPP ; + +if (dhgroup_id ==NVME_AUTH_DHCHAP_DHGROUP_NULL ) +return 0 ; + +down_read (&nvmet_config_sem); +if (ctrl->subsys->type ==NVME_NQN_DISC ) + goto out_unlock; + +list_for_each_entry (p, &ctrl->subsys->hosts, entry) { +if (strcmp (nvmet_host_name (p->host), ctrl->hostnqn)) +continue ; + host = p->host; +break ; + } +if (!host) { +pr_debug ("host %s not found\n" , ctrl->hostnqn); + ret = -ENXIO ; + goto out_unlock; + } + +if (host->dhchap_dhgroup_id != dhgroup_id) { + ret = -EINVAL ; + goto out_unlock; + } +if (ctrl->dh_tfm) { +if (ctrl->dh_gid == dhgroup_id) { +pr_debug ("reuse existing DH group %d\n" , dhgroup_id); + ret =0 ; + }else { +pr_debug ("DH group mismatch (selected %d, requested %d)\n" , + ctrl->dh_gid, dhgroup_id); + ret = -EINVAL ; + } + goto out_unlock; + } + + dhgroup_kpp =nvme_auth_dhgroup_kpp (dhgroup_id); +if (!dhgroup_kpp) { + ret = -EINVAL ; + goto out_unlock; + } + ctrl->dh_tfm =crypto_alloc_kpp (dhgroup_kpp,0 ,0 ); +if (IS_ERR (ctrl->dh_tfm)) { +pr_debug ("failed to setup DH group %d, err %ld\n" , + dhgroup_id,PTR_ERR (ctrl->dh_tfm)); + ret =PTR_ERR (ctrl->dh_tfm); + ctrl->dh_tfm =NULL ; + }else { + ctrl->dh_gid = dhgroup_id; + ctrl->dh_keysize =nvme_auth_dhgroup_pubkey_size (dhgroup_id); + ret =0 ; + } + +out_unlock: +up_read (&nvmet_config_sem); + +return ret; +} +int nvmet_setup_auth (struct nvmet_ctrl *ctrl) {int ret =0 ;
@@ -147,6 +212,11 @@ void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) ctrl->shash_tfm =NULL ; ctrl->shash_id =0 ; } +if (ctrl->dh_tfm) { +crypto_free_kpp (ctrl->dh_tfm); + ctrl->dh_tfm =NULL ; + ctrl->dh_gid =0 ; + }if (ctrl->dhchap_key) {kfree (ctrl->dhchap_key); ctrl->dhchap_key =NULL ;
@@ -182,8 +252,18 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,return ret; }if (ctrl->dh_gid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - ret = -ENOTSUPP ; - goto out; + challenge =kmalloc (shash_len,GFP_KERNEL ); +if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (ctrl->shash_id, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len, + req->sq->dhchap_c1, + challenge, shash_len); +if (ret) + goto out; } shash->tfm = ctrl->shash_tfm;
@@ -256,8 +336,18 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,return ret; }if (ctrl->dh_gid !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { - ret = -ENOTSUPP ; - goto out; + challenge =kmalloc (shash_len,GFP_KERNEL ); +if (!challenge) { + ret = -ENOMEM ; + goto out; + } + ret =nvme_auth_augmented_challenge (ctrl->shash_id, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len, + req->sq->dhchap_c2, + challenge, shash_len); +if (ret) + goto out; } shash->tfm = ctrl->shash_tfm;
@@ -299,3 +389,53 @@ int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,kfree_sensitive (ctrl_response);return 0 ; } + +int nvmet_auth_ctrl_exponential (struct nvmet_req *req, +u8 *buf,int buf_size) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +int ret; + +if (!ctrl->dh_tfm) { +pr_warn ("No DH algorithm!\n" ); +return -ENOKEY ; + } + ret =nvme_auth_gen_pubkey (ctrl->dh_tfm, buf, buf_size); +if (ret == -EOVERFLOW ) { +pr_debug ("public key buffer too small, need %d is %d\n" , +crypto_kpp_maxsize (ctrl->dh_tfm), buf_size); + ret = -ENOKEY ; + }else if (ret) { +pr_debug ("failed to generate public key, err %d\n" , ret); + ret = -ENOKEY ; + }else +pr_debug ("%s: ctrl public key %*ph\n" , __func__, + (int )buf_size, buf); + +return ret; +} + +int nvmet_auth_ctrl_sesskey (struct nvmet_req *req, +u8 *pkey,int pkey_size) +{ +struct nvmet_ctrl *ctrl = req->sq->ctrl; +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 ; + ret =nvme_auth_gen_shared_secret (ctrl->dh_tfm, + pkey, pkey_size, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len); +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 +31 -0
@@ -1712,9 +1712,40 @@ static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item,CONFIGFS_ATTR (nvmet_host_, dhchap_hash); +static ssize_t nvmet_host_dhchap_dhgroup_show (struct config_item *item, +char *page) +{ +struct nvmet_host *host =to_host (item); +const char *dhgroup =nvme_auth_dhgroup_name (host->dhchap_dhgroup_id); + +return sprintf (page,"%s\n" , dhgroup ? dhgroup :"none" ); +} + +static ssize_t nvmet_host_dhchap_dhgroup_store (struct config_item *item, +const char *page,size_t count) +{ +struct nvmet_host *host =to_host (item); +int dhgroup_id; + + dhgroup_id =nvme_auth_dhgroup_id (page); +if (dhgroup_id <0 ) +return -EINVAL ; +if (dhgroup_id !=NVME_AUTH_DHCHAP_DHGROUP_NULL ) { +const char *kpp =nvme_auth_dhgroup_kpp (dhgroup_id); + +if (!crypto_has_kpp (kpp,0 ,0 )) +return -EINVAL ; + } + host->dhchap_dhgroup_id = dhgroup_id; +return count; +} + +CONFIGFS_ATTR (nvmet_host_, dhchap_dhgroup); +static struct configfs_attribute *nvmet_host_attrs[] = { &nvmet_host_attr_dhchap_key, &nvmet_host_attr_dhchap_hash, + &nvmet_host_attr_dhchap_dhgroup,NULL , };#endif /* CONFIG_NVME_TARGET_AUTH */
drivers/nvme/target/fabrics-cmd-auth.c +27 -3
@@ -64,13 +64,24 @@ static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d) null_dh = dhgid;continue ; } +if (ctrl->dh_tfm && ctrl->dh_gid == dhgid) { +pr_debug ("%s: ctrl %d qid %d: reusing existing DH group %d\n" , + __func__, ctrl->cntlid, req->sq->qid, dhgid); +break ; + } +if (nvmet_setup_dhgroup (ctrl, dhgid) <0 ) +continue ; +if (nvme_auth_gen_privkey (ctrl->dh_tfm, dhgid) ==0 ) +break ; +crypto_free_kpp (ctrl->dh_tfm); + ctrl->dh_tfm =NULL ; + ctrl->dh_gid =0 ; } -if (null_dh <0 ) { +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 ; } - ctrl->dh_gid = null_dh;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);
@@ -91,7 +102,11 @@ static u16 nvmet_auth_reply(struct nvmet_req *req, void *d)return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ;if (data->dhvlen) { -return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +if (!ctrl->dh_tfm) +return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +if (nvmet_auth_ctrl_sesskey (req, data->rval +2 * data->hl, + data->dhvlen) <0 ) +return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE ; } response =kmalloc (data->hl,GFP_KERNEL );
@@ -232,6 +247,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; goto done_kfree; } +switch (data->auth_id) {case NVME_AUTH_DHCHAP_MESSAGE_REPLY : status =nvmet_auth_reply (req, d);
@@ -303,6 +319,8 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)int hash_len =crypto_shash_digestsize (ctrl->shash_tfm);int data_size =sizeof (*d) + hash_len; +if (ctrl->dh_tfm) + data_size += ctrl->dh_keysize;if (al < data_size) {pr_debug ("%s: buffer too small (al %d need %d)\n" , __func__, al, data_size);
@@ -321,6 +339,12 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)return -ENOMEM ;get_random_bytes (req->sq->dhchap_c1, data->hl);memcpy (data->cval, req->sq->dhchap_c1, data->hl); +if (ctrl->dh_tfm) { + data->dhgid = ctrl->dh_gid; + data->dhvlen = ctrl->dh_keysize; + ret =nvmet_auth_ctrl_exponential (req, data->cval + data->hl, + data->dhvlen); + }pr_debug ("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %d\n" , __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, req->sq->dhchap_tid, data->hl, data->dhvlen);
drivers/nvme/target/nvmet.h +6 -0
@@ -227,6 +227,7 @@ struct nvmet_ctrl {size_t dhchap_key_len;struct crypto_shash *shash_tfm;u8 shash_id; +struct crypto_kpp *dh_tfm;u32 dh_gid;u32 dh_keysize;#endif
@@ -693,6 +694,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl);void nvmet_init_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req);void nvmet_destroy_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);
@@ -702,6 +704,10 @@ 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) {
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v2 12/13 nvmet-auth: expire authentication sessions
In reply to Hannes ReineckeEach authentication step is required to be completed within the KATO interval (or two minutes if not set). So add a workqueue function to reset the transaction ID and the expected next protocol step; this will automatically the next authentication command referring to the terminated authentication.
--- drivers/nvme/target/auth.c | 1 + drivers/nvme/target/fabrics-cmd-auth.c | 20 +++++++++++++++++++- drivers/nvme/target/nvmet.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-)
drivers/nvme/target/auth.c +1 -0
@@ -197,6 +197,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl)void nvmet_auth_sq_free (struct nvmet_sq *sq) { +cancel_delayed_work (&sq->auth_expired_work);kfree (sq->dhchap_c1); sq->dhchap_c1 =NULL ;kfree (sq->dhchap_c2);
drivers/nvme/target/fabrics-cmd-auth.c +19 -1
@@ -12,9 +12,22 @@#include "nvmet.h" #include "../host/auth.h" +static void nvmet_auth_expired_work (struct work_struct *work) +{ +struct nvmet_sq *sq =container_of (to_delayed_work (work), + struct nvmet_sq, auth_expired_work); + +pr_debug ("%s: ctrl %d qid %d transaction %u expired, resetting\n" , + __func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid); + sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ; + sq->dhchap_tid =-1 ; +} +void nvmet_init_auth (struct nvmet_ctrl *ctrl,struct nvmet_req *req) {/* Initialize in-band authentication */ +INIT_DELAYED_WORK (&req->sq->auth_expired_work, + nvmet_auth_expired_work); req->sq->authenticated = false; req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ; req->cqe->result.u32 |=0x2 <<16 ;
@@ -303,8 +316,13 @@ void nvmet_execute_auth_send(struct nvmet_req *req) 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 ) + req->sq->dhchap_step !=NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 ) { +unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato :120 ; + +mod_delayed_work (system_wq, &req->sq->auth_expired_work, + auth_expire_secs *HZ );return ; + }/* Final states, clear up variables */ nvmet_auth_sq_free (req->sq);if (req->sq->dhchap_step ==NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 )
drivers/nvme/target/nvmet.h +1 -0
@@ -109,6 +109,7 @@ struct nvmet_sq {u32 sqhd;bool sqhd_disabled;#ifdef CONFIG_NVME_TARGET_AUTH +struct delayed_work auth_expired_work;bool authenticated;u16 dhchap_tid;u16 dhchap_status;
Signature · 1 line
2.29.2
Hannes Reinecke
maintainerPATCH v2 13/13 nvme: add non-standard ECDH and curve25517 algorithms
In reply to Hannes ReineckeTLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE groups, and these are already implemented in the kernel. So add support for these non-standard groups for NVMe in-band authentication to validate the augmented challenge implementation.
--- drivers/nvme/host/auth.c | 34 +++++++++++++++++++++++++++++++++- include/linux/nvme.h | 2 ++ 2 files changed, 35 insertions(+), 1 deletion(-)
drivers/nvme/host/auth.c +33 -1
@@ -9,6 +9,8 @@#include <crypto/hash.h> #include <crypto/dh.h> #include <crypto/ffdhe.h> +#include <crypto/ecdh.h> +#include <crypto/curve25519.h> #include "nvme.h" #include "fabrics.h" #include "auth.h"
@@ -69,6 +71,13 @@ static struct nvme_auth_dhgroup_map { { .id = NVME_AUTH_DHCHAP_DHGROUP_8192, .name = "ffdhe8192", .kpp = "dh", .privkey_size = 1024, .pubkey_size = 1024 }, + { .id = NVME_AUTH_DHCHAP_DHGROUP_ECDH, + .name = "ecdh", .kpp = "ecdh-nist-p256", + .privkey_size = 32, .pubkey_size = 64 }, + { .id = NVME_AUTH_DHCHAP_DHGROUP_25519, + .name = "curve25519", .kpp = "curve25519", + .privkey_size = CURVE25519_KEY_SIZE, + .pubkey_size = CURVE25519_KEY_SIZE }, };const char *nvme_auth_dhgroup_name (int dhgroup_id)
@@ -424,6 +433,27 @@ int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, int dh_gid)kfree_sensitive (dh_secret); goto out; } + }else if (dh_gid ==NVME_AUTH_DHCHAP_DHGROUP_ECDH ) { +struct ecdh p = {0 }; + + pkey_len =crypto_ecdh_key_len (&p); + pkey =kmalloc (pkey_len,GFP_KERNEL ); +if (!pkey) +return -ENOMEM ; + +get_random_bytes (pkey, pkey_len); + ret =crypto_ecdh_encode_key (pkey, pkey_len, &p); +if (ret) { +pr_debug ("failed to encode private key, error %d\n" , + ret); + goto out; + } + }else if (dh_gid ==NVME_AUTH_DHCHAP_DHGROUP_25519 ) { + pkey_len =CURVE25519_KEY_SIZE ; + pkey =kmalloc (pkey_len,GFP_KERNEL ); +if (!pkey) +return -ENOMEM ; +get_random_bytes (pkey, pkey_len); }else {pr_warn ("invalid dh group %d\n" , dh_gid);return -EINVAL ;
@@ -597,7 +627,7 @@ static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, data->napd =1 ; data->auth_protocol[0 ].dhchap.authid =NVME_AUTH_DHCHAP_AUTH_ID ; data->auth_protocol[0 ].dhchap.halen =3 ; - data->auth_protocol[0 ].dhchap.dhlen =6 ; + data->auth_protocol[0 ].dhchap.dhlen =8 ; data->auth_protocol[0 ].dhchap.idlist[0 ] =NVME_AUTH_DHCHAP_SHA256 ; data->auth_protocol[0 ].dhchap.idlist[1 ] =NVME_AUTH_DHCHAP_SHA384 ; data->auth_protocol[0 ].dhchap.idlist[2 ] =NVME_AUTH_DHCHAP_SHA512 ;
@@ -607,6 +637,8 @@ static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, data->auth_protocol[0 ].dhchap.idlist[6 ] =NVME_AUTH_DHCHAP_DHGROUP_4096 ; data->auth_protocol[0 ].dhchap.idlist[7 ] =NVME_AUTH_DHCHAP_DHGROUP_6144 ; data->auth_protocol[0 ].dhchap.idlist[8 ] =NVME_AUTH_DHCHAP_DHGROUP_8192 ; + data->auth_protocol[0 ].dhchap.idlist[9 ] =NVME_AUTH_DHCHAP_DHGROUP_ECDH ; + data->auth_protocol[0 ].dhchap.idlist[10 ] =NVME_AUTH_DHCHAP_DHGROUP_25519 ;return size; }
include/linux/nvme.h +2 -0
@@ -1477,6 +1477,8 @@ enum {NVME_AUTH_DHCHAP_DHGROUP_4096 =0x03 ,NVME_AUTH_DHCHAP_DHGROUP_6144 =0x04 ,NVME_AUTH_DHCHAP_DHGROUP_8192 =0x05 , +NVME_AUTH_DHCHAP_DHGROUP_ECDH =0x0e , + NVME_AUTH_DHCHAP_DHGROUP_25519 = 0x0f, }; union nvmf_auth_protocol {
Signature · 1 line
2.29.2
Christoph Hellwig
maintainerRe: [PATCH 13/13] nvme: add non-standard ECDH and curve25517 algorithms
Quoted 4 lines · Hannes Reinecke
TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE groups, and these are already implemented in the kernel. So add support for these non-standard groups for NVMe in-band authentication to validate the augmented challenge implementation.
If you think these are useful please add them to the standard.
Hannes Reinecke
maintainerRe: [PATCH 13/13] nvme: add non-standard ECDH and curve25517 algorithms
Quoted 8 lines · Christoph Hellwig
Quoted 4 lines · Hannes Reinecke
TLS 1.3 specifies ECDH and curve25517 in addition to the FFDHE groups, and these are already implemented in the kernel. So add support for these non-standard groups for NVMe in-band authentication to validate the augmented challenge implementation.
If you think these are useful please add them to the standard.
Ok, will be omitting that patch for the next submission. I've just added them for validation of the DH exchange; but now that ffdhe seems to be reasonably stable I don't have an issue with dropping them.
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
Sagi Grimberg
maintainerQuoted 13 lines
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.
Quoted 6 lines
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.
Hey Hannes,
First, can you also send the nvme-cli/nvmetcli bits as well?
Second, one thing that is not clear to me here is how this works with the discovery log page.
If the user issues a discovery log page to establish connections to the controllers entries, where it picks the appropriate secret?
In other words, when the user runs connect-all, how does it handle the secrets based on the content of the discovery log-page?
Hannes Reinecke
maintainerQuoted 18 lines · Sagi Grimberg
Quoted 18 lines
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.
Quoted 6 lines
Quoted 1 line
As usual, comments and reviews are welcome.
Hey Hannes,
First, can you also send the nvme-cli/nvmetcli bits as well?
Yes, will be done with the next round.
It first required a larger update to libnvme/nvme-cli to get everything in order :-)
Quoted 9 lines
Second, one thing that is not clear to me here is how this works with the discovery log page.
If the user issues a discovery log page to establish connections to the controllers entries, where it picks the appropriate secret?
In other words, when the user runs connect-all, how does it handle the secrets based on the content of the discovery log-page?
With the latest nvme-cli update I've implemented a json configuration, which allows to store configuration on a per-controller basis. This will be updated to hold the dhchap secrets, and we should be able to specify individual secrets for each controller.
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