Commit 7a50a6bc authored by Krzysztof Karas's avatar Krzysztof Karas Committed by Jim Harris
Browse files

nvme_tcp: generate retained PSK



Transform raw PSK into retained psk to be
stored inside TCP transport. The same transformation
has to be done on the NVMe driver's side, to later
derive the same TLS PSK. TLS PSK is going to be
introduced in the next patch.

To avoid supporting two types of PSKs (hexadecimal strings
and byte arrays) drop unhexlifying PSK in SSL socket code
and make sure other places (hello_sock app) that do not
use NVMe TCP driver are responsible for this transformation.

Since PSK is now stored as binary data rather than
char array, add a field "psk_size" to tcp_psk_entry
structure.

Change-Id: I921faa54714c333cf64c59c35cf96cf1f6eda6d6
Signed-off-by: default avatarKrzysztof Karas <krzysztof.karas@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16246


Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 5d5053c8
Loading
Loading
Loading
Loading
+38 −2
Original line number Diff line number Diff line
@@ -11,6 +11,8 @@
#include "spdk/string.h"

#include "spdk/sock.h"
#include "spdk/hexlify.h"
#include "spdk/nvmf.h"

#define ACCEPT_TIMEOUT_US 1000
#define CLOSE_TIMEOUT_US 1000000
@@ -233,11 +235,12 @@ hello_sock_connect(struct hello_context_t *ctx)
	struct spdk_sock_impl_opts impl_opts;
	size_t impl_opts_size = sizeof(impl_opts);
	struct spdk_sock_opts opts;
	char psk[SPDK_TLS_PSK_MAX_LEN] = {};
	char *unhexlified;

	spdk_sock_impl_get_opts(ctx->sock_impl_name, &impl_opts, &impl_opts_size);
	impl_opts.enable_ktls = ctx->ktls;
	impl_opts.tls_version = ctx->tls_version;
	impl_opts.psk_key = ctx->psk_key;
	impl_opts.psk_identity = ctx->psk_identity;

	opts.opts_size = sizeof(opts);
@@ -246,6 +249,22 @@ hello_sock_connect(struct hello_context_t *ctx)
	opts.impl_opts = &impl_opts;
	opts.impl_opts_size = sizeof(impl_opts);

	if (ctx->psk_key) {
		impl_opts.psk_key_size = strlen(ctx->psk_key) / 2;
		if (impl_opts.psk_key_size > SPDK_TLS_PSK_MAX_LEN) {
			SPDK_ERRLOG("Insufficient buffer size for PSK");
			return -EINVAL;
		}
		unhexlified = spdk_unhexlify(ctx->psk_key);
		if (unhexlified == NULL) {
			SPDK_ERRLOG("Could not unhexlify PSK");
			return -EINVAL;
		}
		memcpy(psk, unhexlified, impl_opts.psk_key_size);
		free(unhexlified);
		impl_opts.psk_key = psk;
	}

	SPDK_NOTICELOG("Connecting to the server on %s:%d with sock_impl(%s)\n", ctx->host, ctx->port,
		       ctx->sock_impl_name);

@@ -379,11 +398,12 @@ hello_sock_listen(struct hello_context_t *ctx)
	struct spdk_sock_impl_opts impl_opts;
	size_t impl_opts_size = sizeof(impl_opts);
	struct spdk_sock_opts opts;
	static char psk[SPDK_TLS_PSK_MAX_LEN] = {};
	char *unhexlified;

	spdk_sock_impl_get_opts(ctx->sock_impl_name, &impl_opts, &impl_opts_size);
	impl_opts.enable_ktls = ctx->ktls;
	impl_opts.tls_version = ctx->tls_version;
	impl_opts.psk_key = ctx->psk_key;
	impl_opts.psk_identity = ctx->psk_identity;

	opts.opts_size = sizeof(opts);
@@ -392,6 +412,22 @@ hello_sock_listen(struct hello_context_t *ctx)
	opts.impl_opts = &impl_opts;
	opts.impl_opts_size = sizeof(impl_opts);

	if (ctx->psk_key) {
		impl_opts.psk_key_size = strlen(ctx->psk_key) / 2;
		if (impl_opts.psk_key_size > SPDK_TLS_PSK_MAX_LEN) {
			SPDK_ERRLOG("Insufficient buffer size for PSK");
			return -EINVAL;
		}
		unhexlified = spdk_unhexlify(ctx->psk_key);
		if (unhexlified == NULL) {
			SPDK_ERRLOG("Could not unhexlify PSK");
			return -EINVAL;
		}
		memcpy(psk, unhexlified, impl_opts.psk_key_size);
		free(unhexlified);
		impl_opts.psk_key = psk;
	}

	ctx->sock = spdk_sock_listen_ext(ctx->host, ctx->port, ctx->sock_impl_name, &opts);
	if (ctx->sock == NULL) {
		SPDK_ERRLOG("Cannot create server socket\n");
+1 −1
Original line number Diff line number Diff line
@@ -158,7 +158,7 @@ struct spdk_sock_impl_opts {
	/**
	 * Optional callback to retrieve PSK based on client's identity.
	 *
	 * \param out Buffer for PSK in hexadecimal format to be filled with found key.
	 * \param out Buffer for PSK in binary format to be filled with found key.
	 * \param out_len Length of "out" buffer.
	 * \param cipher Cipher suite to be set by this callback.
	 * \param psk_identity PSK identity for which the key needs to be found.
+95 −0
Original line number Diff line number Diff line
@@ -10,9 +10,15 @@
#include "spdk/likely.h"
#include "spdk/sock.h"
#include "spdk/dif.h"
#include "spdk/hexlify.h"
#include "spdk/nvmf_spec.h"

#include "sgl.h"

#include "openssl/evp.h"
#include "openssl/kdf.h"
#include "openssl/sha.h"

#define SPDK_CRC32C_XOR				0xffffffffUL
#define SPDK_NVME_TCP_DIGEST_LEN		4
#define SPDK_NVME_TCP_DIGEST_ALIGNMENT		4
@@ -61,6 +67,9 @@
 * 457 characters. */
#define NVMF_PSK_IDENTITY_LEN (SPDK_NVMF_NQN_MAX_LEN + SPDK_NVMF_NQN_MAX_LEN + 11)

/* The maximum size of hkdf_info is defined by RFC 8446, 514B (2 + 256 + 256). */
#define NVME_TCP_HKDF_INFO_MAX_LEN 514

typedef void (*nvme_tcp_qpair_xfer_complete_cb)(void *cb_arg);

struct nvme_tcp_pdu {
@@ -587,4 +596,90 @@ nvme_tcp_generate_psk_identity(char *out_id, size_t out_id_len,
	return 0;
}

static inline int
nvme_tcp_derive_retained_psk(const char *psk_in, const char *hostnqn, uint8_t *psk_out,
			     uint64_t psk_out_len)
{
	EVP_PKEY_CTX *ctx;
	uint64_t sha256_digest_len = SHA256_DIGEST_LENGTH;
	uint8_t hkdf_info[NVME_TCP_HKDF_INFO_MAX_LEN] = {};
	const char *label = "tls13 HostNQN";
	size_t pos, labellen, nqnlen;
	char *unhexlified = NULL;
	int rc, hkdf_info_size;

	labellen = strlen(label);
	nqnlen = strlen(hostnqn);
	assert(nqnlen <= SPDK_NVMF_NQN_MAX_LEN);

	*(uint16_t *)&hkdf_info[0] = htons(strlen(psk_in) / 2);
	pos = sizeof(uint16_t);
	hkdf_info[pos] = (uint8_t)labellen;
	pos += sizeof(uint8_t);
	memcpy(&hkdf_info[pos], label, labellen);
	hkdf_info[pos] = (uint8_t)nqnlen;
	pos += sizeof(uint8_t);
	memcpy(&hkdf_info[pos], hostnqn, nqnlen);

	hkdf_info_size = pos * sizeof(uint8_t) + nqnlen;

	if (sha256_digest_len > psk_out_len) {
		SPDK_ERRLOG("Insufficient buffer size for out key!\n");
		return -EINVAL;
	}

	ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
	if (!ctx) {
		SPDK_ERRLOG("Unable to initialize EVP_PKEY_CTX!\n");
		return -ENOMEM;
	}

	/* EVP_PKEY_* functions returns 1 as a success code and 0 or negative on failure. */
	if (EVP_PKEY_derive_init(ctx) != 1) {
		SPDK_ERRLOG("Unable to initialize key derivation ctx for HKDF!\n");
		rc = -ENOMEM;
		goto end;
	}
	if (EVP_PKEY_CTX_set_hkdf_md(ctx, EVP_sha256()) != 1) {
		SPDK_ERRLOG("Unable to set SHA256 method for HKDF!\n");
		rc = -EOPNOTSUPP;
		goto end;
	}

	unhexlified = spdk_unhexlify(psk_in);
	if (unhexlified == NULL) {
		SPDK_ERRLOG("Unable to unhexlify PSK!\n");
		rc = -EINVAL;
		goto end;
	}
	if (EVP_PKEY_CTX_set1_hkdf_key(ctx, unhexlified, strlen(psk_in) / 2) != 1) {
		SPDK_ERRLOG("Unable to set PSK key for HKDF!\n");
		rc = -ENOBUFS;
		goto end;
	}

	if (EVP_PKEY_CTX_add1_hkdf_info(ctx, hkdf_info, hkdf_info_size) != 1) {
		SPDK_ERRLOG("Unable to set info label for HKDF!\n");
		rc = -ENOBUFS;
		goto end;
	}
	if (EVP_PKEY_CTX_set1_hkdf_salt(ctx, NULL, 0) != 1) {
		SPDK_ERRLOG("Unable to set salt for HKDF!\n");
		rc = -EINVAL;
		goto end;
	}
	if (EVP_PKEY_derive(ctx, psk_out, &sha256_digest_len) != 1) {
		SPDK_ERRLOG("Unable to derive the PSK key!\n");
		rc = -EINVAL;
		goto end;
	}

	rc = sha256_digest_len;

end:
	free(unhexlified);
	EVP_PKEY_CTX_free(ctx);
	return rc;
}

#endif /* SPDK_INTERNAL_NVME_TCP_H */
+34 −8
Original line number Diff line number Diff line
@@ -1935,17 +1935,17 @@ nvme_tcp_qpair_connect_sock(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpai
		}
	}

	sock_impl_name = ctrlr->opts.psk[0] ? "ssl" : NULL;
	tcp_ctrlr = SPDK_CONTAINEROF(ctrlr, struct nvme_tcp_ctrlr, ctrlr);
	sock_impl_name = tcp_ctrlr->psk[0] ? "ssl" : NULL;
	SPDK_DEBUGLOG(nvme, "sock_impl_name is %s\n", sock_impl_name);

	if (sock_impl_name) {
		spdk_sock_impl_get_opts(sock_impl_name, &impl_opts, &impl_opts_size);
		impl_opts.enable_ktls = false;
		impl_opts.tls_version = SPDK_TLS_VERSION_1_3;
		impl_opts.psk_key = ctrlr->opts.psk;

		tcp_ctrlr = SPDK_CONTAINEROF(ctrlr, struct nvme_tcp_ctrlr, ctrlr);
		impl_opts.psk_identity = tcp_ctrlr->psk_identity;
		impl_opts.psk_key = tcp_ctrlr->psk;
		impl_opts.psk_key_size = tcp_ctrlr->psk_size;
	}
	opts.opts_size = sizeof(opts);
	spdk_sock_get_default_opts(&opts);
@@ -2144,6 +2144,31 @@ nvme_tcp_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
/* We have to use the typedef in the function declaration to appease astyle. */
typedef struct spdk_nvme_ctrlr spdk_nvme_ctrlr_t;

static int
nvme_tcp_generate_tls_credentials(struct nvme_tcp_ctrlr *tctrlr)
{
	int rc;

	assert(tctrlr != NULL);

	rc = nvme_tcp_generate_psk_identity(tctrlr->psk_identity, sizeof(tctrlr->psk_identity),
					    tctrlr->ctrlr.opts.hostnqn, tctrlr->ctrlr.trid.subnqn);
	if (rc) {
		SPDK_ERRLOG("could not generate PSK identity\n");
		return -EINVAL;
	}

	rc = nvme_tcp_derive_retained_psk(tctrlr->ctrlr.opts.psk, tctrlr->ctrlr.opts.hostnqn, tctrlr->psk,
					  sizeof(tctrlr->psk));
	if (rc < 0) {
		SPDK_ERRLOG("Unable to derive retained PSK!\n");
		return -EINVAL;
	}
	tctrlr->psk_size = rc;

	return 0;
}

static spdk_nvme_ctrlr_t *
nvme_tcp_ctrlr_construct(const struct spdk_nvme_transport_id *trid,
			 const struct spdk_nvme_ctrlr_opts *opts,
@@ -2162,10 +2187,11 @@ nvme_tcp_ctrlr_construct(const struct spdk_nvme_transport_id *trid,
	tctrlr->ctrlr.trid = *trid;

	if (opts->psk[0] != '\0') {
		rc = nvme_tcp_generate_psk_identity(tctrlr->psk_identity, sizeof(tctrlr->psk_identity),
						    tctrlr->ctrlr.opts.hostnqn, tctrlr->ctrlr.trid.subnqn);
		if (rc) {
			SPDK_ERRLOG("could not generate PSK identity\n");
		rc = nvme_tcp_generate_tls_credentials(tctrlr);
		spdk_memset_s(&tctrlr->ctrlr.opts.psk, sizeof(tctrlr->ctrlr.opts.psk), 0,
			      sizeof(tctrlr->ctrlr.opts.psk));

		if (rc != 0) {
			free(tctrlr);
			return NULL;
		}
+13 −4
Original line number Diff line number Diff line
@@ -347,6 +347,7 @@ struct tcp_psk_entry {
	char				subnqn[SPDK_NVMF_NQN_MAX_LEN + 1];
	char				psk_identity[NVMF_PSK_IDENTITY_LEN];
	uint8_t				psk[SPDK_TLS_PSK_MAX_LEN];
	uint32_t			psk_size;
	TAILQ_ENTRY(tcp_psk_entry)	link;
};

@@ -824,13 +825,13 @@ tcp_sock_get_key(uint8_t *out, int out_len, const char **cipher, const char *psk
			continue;
		}

		psk_len = strlen(entry->psk);
		if ((size_t)out_len <= psk_len) {
		psk_len = entry->psk_size;
		if ((size_t)out_len < psk_len) {
			SPDK_ERRLOG("Out buffer of size: %" PRIu32 " cannot fit PSK of len: %lu\n",
				    out_len, psk_len);
			return -ENOBUFS;
		}
		memcpy(out, entry->psk, psk_len + 1);
		memcpy(out, entry->psk, psk_len);
		return psk_len;
	}

@@ -3561,9 +3562,17 @@ nvmf_tcp_subsystem_add_host(struct spdk_nvmf_transport *transport,
		free(entry);
		goto end;
	}
	memcpy(entry->psk, opts.psk, strlen(opts.psk));

	/* Derive retained PSK. */
	rc = nvme_tcp_derive_retained_psk(opts.psk, hostnqn, entry->psk, SPDK_TLS_PSK_MAX_LEN);
	if (rc < 0) {
		SPDK_ERRLOG("Unable to derive retained PSK!\n");
		goto end;
	}
	entry->psk_size = rc;

	TAILQ_INSERT_TAIL(&ttransport->psks, entry, link);
	rc = 0;

end:
	key_len = strnlen(opts.psk, SPDK_TLS_PSK_MAX_LEN);
Loading