Commit 71972b9f authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Keith Busch
Browse files

nvme: add nvme_auth_generate_digest()



Add a function to calculate the PSK digest as specified in TP8018.

Signed-off-by: default avatarHannes Reinecke <hare@kernel.org>
Reviewed-by: default avatarSagi Grimberg <sagi@grimberg.me>
Signed-off-by: default avatarKeith Busch <kbusch@kernel.org>
parent 5c12a9cd
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
@@ -558,5 +558,139 @@ int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len,
}
EXPORT_SYMBOL_GPL(nvme_auth_generate_psk);

/**
 * nvme_auth_generate_digest - Generate TLS PSK digest
 * @hmac_id: Hash function identifier
 * @psk: Generated input PSK
 * @psk_len: Length of @psk
 * @subsysnqn: NQN of the subsystem
 * @hostnqn: NQN of the host
 * @ret_digest: Pointer to the returned digest
 *
 * Generate a TLS PSK digest as specified in TP8018 Section 3.6.1.3:
 *   TLS PSK and PSK identity Derivation
 *
 * The PSK digest shall be computed by encoding in Base64 (refer to RFC
 * 4648) the result of the application of the HMAC function using the hash
 * function specified in item 4 above (ie the hash function of the cipher
 * suite associated with the PSK identity) with the PSK as HMAC key to the
 * concatenation of:
 * - the NQN of the host (i.e., NQNh) not including the null terminator;
 * - a space character;
 * - the NQN of the NVM subsystem (i.e., NQNc) not including the null
 *   terminator;
 * - a space character; and
 * - the seventeen ASCII characters "NVMe-over-Fabrics"
 * (i.e., <PSK digest> = Base64(HMAC(PSK, NQNh || " " || NQNc || " " ||
 *  "NVMe-over-Fabrics"))).
 * The length of the PSK digest depends on the hash function used to compute
 * it as follows:
 * - If the SHA-256 hash function is used, the resulting PSK digest is 44
 *   characters long; or
 * - If the SHA-384 hash function is used, the resulting PSK digest is 64
 *   characters long.
 *
 * Returns 0 on success with a valid digest pointer in @ret_digest, or a
 * negative error number on failure.
 */
int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len,
		char *subsysnqn, char *hostnqn, u8 **ret_digest)
{
	struct crypto_shash *tfm;
	SHASH_DESC_ON_STACK(shash, tfm);
	u8 *digest, *enc;
	const char *hmac_name;
	size_t digest_len, hmac_len;
	int ret;

	if (WARN_ON(!subsysnqn || !hostnqn))
		return -EINVAL;

	hmac_name = nvme_auth_hmac_name(hmac_id);
	if (!hmac_name) {
		pr_warn("%s: invalid hash algorithm %d\n",
			__func__, hmac_id);
		return -EINVAL;
	}

	switch (nvme_auth_hmac_hash_len(hmac_id)) {
	case 32:
		hmac_len = 44;
		break;
	case 48:
		hmac_len = 64;
		break;
	default:
		pr_warn("%s: invalid hash algorithm '%s'\n",
			__func__, hmac_name);
		return -EINVAL;
	}

	enc = kzalloc(hmac_len + 1, GFP_KERNEL);
	if (!enc)
		return -ENOMEM;

	tfm = crypto_alloc_shash(hmac_name, 0, 0);
	if (IS_ERR(tfm)) {
		ret = PTR_ERR(tfm);
		goto out_free_enc;
	}

	digest_len = crypto_shash_digestsize(tfm);
	digest = kzalloc(digest_len, GFP_KERNEL);
	if (!digest) {
		ret = -ENOMEM;
		goto out_free_tfm;
	}

	shash->tfm = tfm;
	ret = crypto_shash_setkey(tfm, psk, psk_len);
	if (ret)
		goto out_free_digest;

	ret = crypto_shash_init(shash);
	if (ret)
		goto out_free_digest;

	ret = crypto_shash_update(shash, hostnqn, strlen(hostnqn));
	if (ret)
		goto out_free_digest;

	ret = crypto_shash_update(shash, " ", 1);
	if (ret)
		goto out_free_digest;

	ret = crypto_shash_update(shash, subsysnqn, strlen(subsysnqn));
	if (ret)
		goto out_free_digest;

	ret = crypto_shash_update(shash, " NVMe-over-Fabrics", 18);
	if (ret)
		goto out_free_digest;

	ret = crypto_shash_final(shash, digest);
	if (ret)
		goto out_free_digest;

	ret = base64_encode(digest, digest_len, enc);
	if (ret < hmac_len) {
		ret = -ENOKEY;
		goto out_free_digest;
	}
	*ret_digest = enc;
	ret = 0;

out_free_digest:
	kfree_sensitive(digest);
out_free_tfm:
	crypto_free_shash(tfm);
out_free_enc:
	if (ret)
		kfree_sensitive(enc);

	return ret;
}
EXPORT_SYMBOL_GPL(nvme_auth_generate_digest);

MODULE_DESCRIPTION("NVMe Authentication framework");
MODULE_LICENSE("GPL v2");
+2 −0
Original line number Diff line number Diff line
@@ -43,5 +43,7 @@ int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm,
int nvme_auth_generate_psk(u8 hmac_id, u8 *skey, size_t skey_len,
			   u8 *c1, u8 *c2, size_t hash_len,
			   u8 **ret_psk, size_t *ret_len);
int nvme_auth_generate_digest(u8 hmac_id, u8 *psk, size_t psk_len,
		char *subsysnqn, char *hostnqn, u8 **ret_digest);

#endif /* _NVME_AUTH_H */