Commit 169ee6c3 authored by Krzysztof Karas's avatar Krzysztof Karas Committed by Konrad Sztyber
Browse files

TCP: prepare for usage of supported cipher suites



Prepare for cipher suite selection introduced in the next patch.
Cipher suites are going to be selected based on size of provided
PSK in interchange format.

Choosing hash function for retained PSK will be
introduced in a future patch along with PSK interchange
format.

Use TLS_AES_128_GCM_SHA256 until information
about cipher suite can be derived from length of PSK
configured.

Cipher TLS_AES_256_GCM_SHA384 requires us to use newer
version of OpenSSL callbacks:
instead of setting them with SSL_set_psk_server_callback()
we'll need to use SSL_set_psk_find_session_callback() for
server callbacks. As a part of this patch introduce
required server side changes.

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


Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
Reviewed-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
parent dd824d4e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -413,6 +413,7 @@ hello_sock_listen(struct hello_context_t *ctx)
	impl_opts.enable_ktls = ctx->ktls;
	impl_opts.tls_version = ctx->tls_version;
	impl_opts.psk_identity = ctx->psk_identity;
	impl_opts.tls_cipher_suites = "TLS_AES_128_GCM_SHA256";

	opts.opts_size = sizeof(opts);
	spdk_sock_get_default_opts(&opts);
+40 −15
Original line number Diff line number Diff line
@@ -70,6 +70,13 @@
/* The maximum size of hkdf_info is defined by RFC 8446, 514B (2 + 256 + 256). */
#define NVME_TCP_HKDF_INFO_MAX_LEN 514

#define PSK_ID_PREFIX "NVMe0R"

enum nvme_tcp_cipher_suite {
	NVME_TCP_CIPHER_AES_128_GCM_SHA256,
	NVME_TCP_CIPHER_AES_256_GCM_SHA384,
};

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

struct nvme_tcp_pdu {
@@ -571,23 +578,29 @@ nvme_tcp_pdu_calc_psh_len(struct nvme_tcp_pdu *pdu, bool hdgst_enable)
}

static inline int
nvme_tcp_generate_psk_identity(char *out_id, size_t out_id_len,
			       const char *hostnqn, const char *subnqn)
nvme_tcp_generate_psk_identity(char *out_id, size_t out_id_len, const char *hostnqn,
			       const char *subnqn, enum nvme_tcp_cipher_suite tls_cipher_suite)
{
	/* This hardcoded PSK identity prefix will remain until
	 * support for different hash functions to generate PSK
	 * key is introduced. */
	const char *psk_id_prefix = "NVMe0R01";
	int rc;

	assert(out_id != NULL);

	if (out_id_len < strlen(psk_id_prefix) + strlen(hostnqn) + strlen(subnqn) + 3) {
	if (out_id_len < strlen(PSK_ID_PREFIX) + strlen(hostnqn) + strlen(subnqn) + 5) {
		SPDK_ERRLOG("Out buffer too small!\n");
		return -1;
	}

	rc = snprintf(out_id, out_id_len, "%s %s %s", psk_id_prefix, hostnqn, subnqn);
	if (tls_cipher_suite == NVME_TCP_CIPHER_AES_128_GCM_SHA256) {
		rc = snprintf(out_id, out_id_len, "%s%s %s %s", PSK_ID_PREFIX, "01",
			      hostnqn, subnqn);
	} else if (tls_cipher_suite == NVME_TCP_CIPHER_AES_256_GCM_SHA384) {
		rc = snprintf(out_id, out_id_len, "%s%s %s %s", PSK_ID_PREFIX, "02",
			      hostnqn, subnqn);
	} else {
		SPDK_ERRLOG("Unknown cipher suite requested!\n");
		return -EOPNOTSUPP;
	}

	if (rc < 0) {
		SPDK_ERRLOG("Could not generate PSK identity\n");
		return -1;
@@ -685,15 +698,27 @@ end:

static inline int
nvme_tcp_derive_tls_psk(const uint8_t *psk_in, uint64_t psk_in_len, const char *psk_identity,
			uint8_t *psk_out, uint64_t psk_out_size)
			uint8_t *psk_out, uint64_t psk_out_size, enum nvme_tcp_cipher_suite tls_cipher_suite)
{
	EVP_PKEY_CTX *ctx;
	uint64_t sha256_digest_len = SHA256_DIGEST_LENGTH;
	uint64_t digest_len = 0;
	char hkdf_info[NVME_TCP_HKDF_INFO_MAX_LEN] = {};
	const char *label = "tls13 nvme-tls-psk";
	size_t pos, labellen, idlen;
	const EVP_MD *hash;
	int rc, hkdf_info_size;

	if (tls_cipher_suite == NVME_TCP_CIPHER_AES_128_GCM_SHA256) {
		digest_len = SHA256_DIGEST_LENGTH;
		hash = EVP_sha256();
	} else if (tls_cipher_suite == NVME_TCP_CIPHER_AES_256_GCM_SHA384) {
		digest_len = SHA384_DIGEST_LENGTH;
		hash = EVP_sha384();
	} else {
		SPDK_ERRLOG("Unknown cipher suite requested!\n");
		return -EOPNOTSUPP;
	}

	labellen = strlen(label);
	idlen = strlen(psk_identity);
	if (idlen > UINT8_MAX) {
@@ -713,7 +738,7 @@ nvme_tcp_derive_tls_psk(const uint8_t *psk_in, uint64_t psk_in_len, const char *
	pos += idlen;
	hkdf_info_size = pos;

	if (sha256_digest_len > psk_out_size) {
	if (digest_len > psk_out_size) {
		SPDK_ERRLOG("Insufficient buffer size for out key!\n");
		return -1;
	}
@@ -729,8 +754,8 @@ nvme_tcp_derive_tls_psk(const uint8_t *psk_in, uint64_t psk_in_len, const char *
		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");
	if (EVP_PKEY_CTX_set_hkdf_md(ctx, hash) != 1) {
		SPDK_ERRLOG("Unable to set hash method for HKDF!\n");
		rc = -EOPNOTSUPP;
		goto end;
	}
@@ -750,13 +775,13 @@ nvme_tcp_derive_tls_psk(const uint8_t *psk_in, uint64_t psk_in_len, const char *
		rc = -EINVAL;
		goto end;
	}
	if (EVP_PKEY_derive(ctx, psk_out, &sha256_digest_len) != 1) {
	if (EVP_PKEY_derive(ctx, psk_out, &digest_len) != 1) {
		SPDK_ERRLOG("Unable to derive the PSK key!\n");
		rc = -EINVAL;
		goto end;
	}

	rc = sha256_digest_len;
	rc = digest_len;

end:
	EVP_PKEY_CTX_free(ctx);
+7 −3
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ struct nvme_tcp_ctrlr {
	char					psk_identity[NVMF_PSK_IDENTITY_LEN];
	uint8_t					psk[SPDK_TLS_PSK_MAX_LEN];
	int					psk_size;
	char					*tls_cipher_suite;
};

struct nvme_tcp_poll_group {
@@ -1946,7 +1947,7 @@ nvme_tcp_qpair_connect_sock(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpai
		impl_opts.psk_identity = tcp_ctrlr->psk_identity;
		impl_opts.psk_key = tcp_ctrlr->psk;
		impl_opts.psk_key_size = tcp_ctrlr->psk_size;
		impl_opts.tls_cipher_suites = "TLS_AES_128_GCM_SHA256";
		impl_opts.tls_cipher_suites = tcp_ctrlr->tls_cipher_suite;
	}
	opts.opts_size = sizeof(opts);
	spdk_sock_get_default_opts(&opts);
@@ -2153,8 +2154,11 @@ nvme_tcp_generate_tls_credentials(struct nvme_tcp_ctrlr *tctrlr)

	assert(tctrlr != NULL);

	tctrlr->tls_cipher_suite = "TLS_AES_128_GCM_SHA256";

	rc = nvme_tcp_generate_psk_identity(tctrlr->psk_identity, sizeof(tctrlr->psk_identity),
					    tctrlr->ctrlr.opts.hostnqn, tctrlr->ctrlr.trid.subnqn);
					    tctrlr->ctrlr.opts.hostnqn, tctrlr->ctrlr.trid.subnqn,
					    NVME_TCP_CIPHER_AES_128_GCM_SHA256);
	if (rc) {
		SPDK_ERRLOG("could not generate PSK identity\n");
		return -EINVAL;
@@ -2168,7 +2172,7 @@ nvme_tcp_generate_tls_credentials(struct nvme_tcp_ctrlr *tctrlr)
	}

	rc = nvme_tcp_derive_tls_psk(psk_retained, rc, tctrlr->psk_identity, tctrlr->psk,
				     sizeof(tctrlr->psk));
				     sizeof(tctrlr->psk), NVME_TCP_CIPHER_AES_128_GCM_SHA256);
	if (rc < 0) {
		SPDK_ERRLOG("Could not generate TLS PSK!\n");
		return rc;
+18 −4
Original line number Diff line number Diff line
@@ -348,6 +348,7 @@ struct tcp_psk_entry {
	char				psk_identity[NVMF_PSK_IDENTITY_LEN];
	uint8_t				psk[SPDK_TLS_PSK_MAX_LEN];
	uint32_t			psk_size;
	enum nvme_tcp_cipher_suite	tls_cipher_suite;
	TAILQ_ENTRY(tcp_psk_entry)	link;
};

@@ -819,8 +820,6 @@ tcp_sock_get_key(uint8_t *out, int out_len, const char **cipher, const char *psk
	size_t psk_len;
	int rc;

	*cipher = NULL;

	TAILQ_FOREACH(entry, &ttransport->psks, link) {
		if (strcmp(psk_identity, entry->psk_identity) != 0) {
			continue;
@@ -834,11 +833,24 @@ tcp_sock_get_key(uint8_t *out, int out_len, const char **cipher, const char *psk
		}

		/* Convert PSK to the TLS PSK format. */
		rc = nvme_tcp_derive_tls_psk(entry->psk, psk_len, psk_identity, out, out_len);
		rc = nvme_tcp_derive_tls_psk(entry->psk, psk_len, psk_identity, out, out_len,
					     entry->tls_cipher_suite);
		if (rc < 0) {
			SPDK_ERRLOG("Could not generate TLS PSK\n");
		}

		switch (entry->tls_cipher_suite) {
		case NVME_TCP_CIPHER_AES_128_GCM_SHA256:
			*cipher = "TLS_AES_128_GCM_SHA256";
			break;
		case NVME_TCP_CIPHER_AES_256_GCM_SHA384:
			*cipher = "TLS_AES_256_GCM_SHA384";
			break;
		default:
			*cipher = NULL;
			return -ENOTSUP;
		}

		return rc;
	}

@@ -892,6 +904,7 @@ nvmf_tcp_listen(struct spdk_nvmf_transport *transport, const struct spdk_nvme_tr
		impl_opts.tls_version = SPDK_TLS_VERSION_1_3;
		impl_opts.get_key = tcp_sock_get_key;
		impl_opts.get_key_ctx = ttransport;
		impl_opts.tls_cipher_suites = "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256";
		opts.impl_opts = &impl_opts;
		opts.impl_opts_size = sizeof(impl_opts);
	}
@@ -3537,7 +3550,7 @@ nvmf_tcp_subsystem_add_host(struct spdk_nvmf_transport *transport,
	ttransport = SPDK_CONTAINEROF(transport, struct spdk_nvmf_tcp_transport, transport);
	/* Generate PSK identity. */
	rc = nvme_tcp_generate_psk_identity(psk_identity, NVMF_PSK_IDENTITY_LEN, hostnqn,
					    subsystem->subnqn);
					    subsystem->subnqn, NVME_TCP_CIPHER_AES_128_GCM_SHA256);
	if (rc) {
		rc = -EINVAL;
		goto end;
@@ -3582,6 +3595,7 @@ nvmf_tcp_subsystem_add_host(struct spdk_nvmf_transport *transport,
		free(entry);
		goto end;
	}
	entry->tls_cipher_suite = NVME_TCP_CIPHER_AES_128_GCM_SHA256;

	/* Derive retained PSK. */
	rc = nvme_tcp_derive_retained_psk(opts.psk, hostnqn, entry->psk, SPDK_TLS_PSK_MAX_LEN);
+74 −27
Original line number Diff line number Diff line
@@ -521,29 +521,27 @@ posix_fd_create(struct addrinfo *res, struct spdk_sock_opts *opts,
	return fd;
}

static unsigned int
posix_sock_tls_psk_server_cb(SSL *ssl,
			     const char *id,
			     unsigned char *psk,
			     unsigned int max_psk_len)
static int
posix_sock_psk_find_session_server_cb(SSL *ssl, const unsigned char *identity,
				      size_t identity_len, SSL_SESSION **sess)
{
	const char *cipher = NULL;
	struct spdk_sock_impl_opts *impl_opts;
	int rc;
	struct spdk_sock_impl_opts *impl_opts = SSL_get_app_data(ssl);
	uint8_t key[SSL_MAX_MASTER_KEY_LENGTH] = {};
	int keylen;
	int rc, i;
	STACK_OF(SSL_CIPHER) *ciphers;
	const SSL_CIPHER *cipher;
	const char *cipher_name;
	const char *user_cipher = NULL;
	bool found = false;

	impl_opts = SSL_get_app_data(ssl);
	SPDK_DEBUGLOG(sock_posix, "Received PSK ID '%s'\n", id);
	if (id == NULL) {
		SPDK_ERRLOG("Received empty PSK ID\n");
	if (impl_opts->get_key) {
		rc = impl_opts->get_key(key, sizeof(key), &user_cipher, identity, impl_opts->get_key_ctx);
		if (rc < 0) {
			SPDK_ERRLOG("Unable to find PSK for identity: %s\n", identity);
			return 0;
		}

	SPDK_DEBUGLOG(sock_posix, "Length of Client's PSK KEY %u\n", max_psk_len);

	if (impl_opts->get_key) {
		rc = impl_opts->get_key(psk, max_psk_len, &cipher, id, impl_opts->get_key_ctx);
		assert(cipher == NULL);
		return rc > 0 ? rc : 0;
		keylen = rc;
	} else {
		if (impl_opts->psk_key == NULL) {
			SPDK_ERRLOG("PSK is not set\n");
@@ -551,18 +549,67 @@ posix_sock_tls_psk_server_cb(SSL *ssl,
		}

		SPDK_DEBUGLOG(sock_posix, "Length of Client's PSK ID %lu\n", strlen(impl_opts->psk_identity));
		if (strcmp(impl_opts->psk_identity, id) != 0) {
		if (strcmp(impl_opts->psk_identity, identity) != 0) {
			SPDK_ERRLOG("Unknown Client's PSK ID\n");
			return 0;
		}
		if (impl_opts->psk_key_size > max_psk_len) {
			SPDK_ERRLOG("PSK too long\n");
		keylen = impl_opts->psk_key_size;

		memcpy(key, impl_opts->psk_key, keylen);
		user_cipher = impl_opts->tls_cipher_suites;
	}

	if (user_cipher == NULL) {
		SPDK_ERRLOG("Cipher suite not set\n");
		return 0;
	}

		memcpy(psk, impl_opts->psk_key, impl_opts->psk_key_size);
		return impl_opts->psk_key_size;
	*sess = SSL_SESSION_new();
	if (*sess == NULL) {
		SPDK_ERRLOG("Unable to allocate new SSL session\n");
		return 0;
	}

	ciphers = SSL_get_ciphers(ssl);
	for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
		cipher = sk_SSL_CIPHER_value(ciphers, i);
		cipher_name = SSL_CIPHER_get_name(cipher);

		if (strcmp(user_cipher, cipher_name) == 0) {
			rc = SSL_SESSION_set_cipher(*sess, cipher);
			if (rc != 1) {
				SPDK_ERRLOG("Unable to set cipher: %s\n", cipher_name);
				goto err;
			}
			found = true;
			break;
		}
	}
	if (found == false) {
		SPDK_ERRLOG("No suitable cipher found\n");
		goto err;
	}

	SPDK_DEBUGLOG(sock_posix, "Cipher selected: %s\n", cipher_name);

	rc = SSL_SESSION_set_protocol_version(*sess, TLS1_3_VERSION);
	if (rc != 1) {
		SPDK_ERRLOG("Unable to set TLS version: %d\n", TLS1_3_VERSION);
		goto err;
	}

	rc = SSL_SESSION_set1_master_key(*sess, key, keylen);
	if (rc != 1) {
		SPDK_ERRLOG("Unable to set PSK for session\n");
		goto err;
	}

	return 1;

err:
	SSL_SESSION_free(*sess);
	*sess = NULL;
	return 0;
}

static int
@@ -770,7 +817,7 @@ ssl_sock_accept_loop(SSL_CTX *ctx, int fd, struct spdk_sock_impl_opts *impl_opts
	}
	SSL_set_fd(ssl, fd);
	SSL_set_app_data(ssl, impl_opts);
	SSL_set_psk_server_callback(ssl, posix_sock_tls_psk_server_cb);
	SSL_set_psk_find_session_callback(ssl, posix_sock_psk_find_session_server_cb);
	SPDK_DEBUGLOG(sock_posix, "SSL object creation finished: %p\n", ssl);
	SPDK_DEBUGLOG(sock_posix, "%s = SSL_state_string_long(%p)\n", SSL_state_string_long(ssl), ssl);
	while ((rc = SSL_accept(ssl)) != 1) {
Loading