Thread (75 messages) 75 messages, 7 authors, 2021-07-24

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

From: Stephan Müller <hidden>
Date: 2021-07-18 12:58:33
Also in: linux-nvme

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

Hi Hannes,
On 7/17/21 6:49 PM, Stephan Müller wrote:
quoted
Am Freitag, 16. Juli 2021, 13:04:24 CEST schrieb Hannes Reinecke:

Hi Hannes,
quoted
Implement support for augmented challenge using FFDHE groups.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---

  drivers/nvme/host/auth.c | 403 +++++++++++++++++++++++++++++++++++----
  1 file changed, 371 insertions(+), 32 deletions(-)
diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
index 448a3adebea6..754343aced19 100644
--- a/drivers/nvme/host/auth.c
+++ b/drivers/nvme/host/auth.c
@@ -8,6 +8,8 @@

  #include <asm/unaligned.h>
  #include <crypto/hash.h>
  #include <crypto/kpp.h>

+#include <crypto/dh.h>
+#include <crypto/ffdhe.h>

  #include "nvme.h"
  #include "fabrics.h"
  #include "auth.h"
@@ -16,6 +18,8 @@ static u32 nvme_dhchap_seqnum;

  struct nvme_dhchap_context {
  
  	struct crypto_shash *shash_tfm;

+	struct crypto_shash *digest_tfm;
+	struct crypto_kpp *dh_tfm;

  	unsigned char *key;
  	size_t key_len;
  	int qid;
@@ -25,6 +29,8 @@ struct nvme_dhchap_context {

  	u8 status;
  	u8 hash_id;
  	u8 hash_len;

+	u8 dhgroup_id;
+	u16 dhgroup_size;

  	u8 c1[64];
  	u8 c2[64];
  	u8 response[64];
@@ -36,6 +42,94 @@ struct nvme_dhchap_context {

  	int sess_key_len;
  
  };

+struct nvme_auth_dhgroup_map {
+	int id;
+	const char name[16];
+	const char kpp[16];
+	int privkey_size;
+	int pubkey_size;
+} dhgroup_map[] = {
+	{ .id = NVME_AUTH_DHCHAP_DHGROUP_NULL,
+	  .name = "NULL", .kpp = "NULL",
+	  .privkey_size = 0, .pubkey_size = 0 },
+	{ .id = NVME_AUTH_DHCHAP_DHGROUP_2048,
+	  .name = "ffdhe2048", .kpp = "dh",
+	  .privkey_size = 256, .pubkey_size = 256 },
+	{ .id = NVME_AUTH_DHCHAP_DHGROUP_3072,
+	  .name = "ffdhe3072", .kpp = "dh",
+	  .privkey_size = 384, .pubkey_size = 384 },
+	{ .id = NVME_AUTH_DHCHAP_DHGROUP_4096,
+	  .name = "ffdhe4096", .kpp = "dh",
+	  .privkey_size = 512, .pubkey_size = 512 },
+	{ .id = NVME_AUTH_DHCHAP_DHGROUP_6144,
+	  .name = "ffdhe6144", .kpp = "dh",
+	  .privkey_size = 768, .pubkey_size = 768 },
+	{ .id = NVME_AUTH_DHCHAP_DHGROUP_8192,
+	  .name = "ffdhe8192", .kpp = "dh",
+	  .privkey_size = 1024, .pubkey_size = 1024 },
+};
+
+const char *nvme_auth_dhgroup_name(int dhgroup_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+		if (dhgroup_map[i].id == dhgroup_id)
+			return dhgroup_map[i].name;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name);
+
+int nvme_auth_dhgroup_pubkey_size(int dhgroup_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+		if (dhgroup_map[i].id == dhgroup_id)
+			return dhgroup_map[i].pubkey_size;
+	}
+	return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_pubkey_size);
+
+int nvme_auth_dhgroup_privkey_size(int dhgroup_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+		if (dhgroup_map[i].id == dhgroup_id)
+			return dhgroup_map[i].privkey_size;
+	}
+	return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_privkey_size);
+
+const char *nvme_auth_dhgroup_kpp(int dhgroup_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+		if (dhgroup_map[i].id == dhgroup_id)
+			return dhgroup_map[i].kpp;
+	}
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp);
+
+int nvme_auth_dhgroup_id(const char *dhgroup_name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) {
+		if (!strncmp(dhgroup_map[i].name, dhgroup_name,
+			     strlen(dhgroup_map[i].name)))
+			return dhgroup_map[i].id;
+	}
+	return -1;
+}
+EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id);
+

  struct nvmet_dhchap_hash_map {
  
  	int id;
  	int hash_len;
@@ -243,11 +337,16 @@ static int nvme_auth_dhchap_negotiate(struct
nvme_ctrl *ctrl, data->napd = 1;

  	data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID;
  	data->auth_protocol[0].dhchap.halen = 3;

-	data->auth_protocol[0].dhchap.dhlen = 1;
+	data->auth_protocol[0].dhchap.dhlen = 6;

  	data->auth_protocol[0].dhchap.idlist[0] =
  	NVME_AUTH_DHCHAP_HASH_SHA256;
  	data->auth_protocol[0].dhchap.idlist[1] =
  	NVME_AUTH_DHCHAP_HASH_SHA384;
  	data->auth_protocol[0].dhchap.idlist[2] =
  	NVME_AUTH_DHCHAP_HASH_SHA512;
  	data->auth_protocol[0].dhchap.idlist[3] =
  	NVME_AUTH_DHCHAP_DHGROUP_NULL;

+	data->auth_protocol[0].dhchap.idlist[4] =
NVME_AUTH_DHCHAP_DHGROUP_2048;
+	data->auth_protocol[0].dhchap.idlist[5] =
NVME_AUTH_DHCHAP_DHGROUP_3072;
+	data->auth_protocol[0].dhchap.idlist[6] =
NVME_AUTH_DHCHAP_DHGROUP_4096;
+	data->auth_protocol[0].dhchap.idlist[7] =
NVME_AUTH_DHCHAP_DHGROUP_6144;
+	data->auth_protocol[0].dhchap.idlist[8] =
NVME_AUTH_DHCHAP_DHGROUP_8192;

  	return size;
  
  }
@@ -274,14 +373,7 @@ static int nvme_auth_dhchap_challenge(struct
nvme_ctrl
*ctrl, chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;

  		return -EPROTO;
  	
  	}

-	switch (data->dhgid) {
-	case NVME_AUTH_DHCHAP_DHGROUP_NULL:
-		gid_name = "null";
-		break;
-	default:
-		gid_name = NULL;
-		break;
-	}
+	gid_name = nvme_auth_dhgroup_kpp(data->dhgid);

  	if (!gid_name) {
  	
  		dev_warn(ctrl->device,
  		
  			 "qid %d: DH-HMAC-CHAP: invalid DH group id %d\n",
@@ -290,10 +382,24 @@ static int nvme_auth_dhchap_challenge(struct
nvme_ctrl *ctrl, return -EPROTO;

  	}
  	if (data->dhgid != NVME_AUTH_DHCHAP_DHGROUP_NULL) {

-		chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
-		return -EPROTO;
-	}
-	if (data->dhgid == NVME_AUTH_DHCHAP_DHGROUP_NULL && data->dhvlen != 0)
{
+		if (data->dhvlen == 0) {
+			dev_warn(ctrl->device,
+				 "qid %d: DH-HMAC-CHAP: empty DH value\n",
+				 chap->qid);
+			chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+			return -EPROTO;
+		}
+		chap->dh_tfm = crypto_alloc_kpp(gid_name, 0, 0);
+		if (IS_ERR(chap->dh_tfm)) {
+			dev_warn(ctrl->device,
+				 "qid %d: DH-HMAC-CHAP: failed to initialize %s\n",
+				 chap->qid, gid_name);
+			chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE;
+			chap->dh_tfm = NULL;
+			return -EPROTO;
+		}
+		chap->dhgroup_id = data->dhgid;
+	} else if (data->dhvlen != 0) {

  		dev_warn(ctrl->device,
  		
  			 "qid %d: DH-HMAC-CHAP: invalid DH value for NULL 
DH\n",
quoted
quoted
  			
  			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",
quoted
quoted
  		
  			 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);
quoted
quoted
  	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
quoted
%d\n",
quoted
  		__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

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help