Commit 42cd204d authored by Lucas Dietrich's avatar Lucas Dietrich Committed by Carles Cufi
Browse files

net: sockets: tls: Support for DER cert chain and NOCOPY optimisation



Add TLS socket option "TLS_CERT_NOCOPY" to prevent the copy of
certificates to mbedTLS heap if possible.

Add support to provide a chain of DER certificates by registering
them with multiple tags.

Signed-off-by: default avatarLucas Dietrich <ld.adecy@gmail.com>
parent 707162ce
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -139,6 +139,12 @@ struct zsock_pollfd {
#define TLS_DTLS_HANDSHAKE_TIMEOUT_MIN 8
#define TLS_DTLS_HANDSHAKE_TIMEOUT_MAX 9

/** Socket option for preventing certificates from being copied to the mbedTLS
 *  heap if possible. The option is only effective for DER certificates and is
 *  ignored for PEM certificates.
 */
#define TLS_CERT_NOCOPY	       10

/** @} */

/* Valid values for TLS_PEER_VERIFY option */
@@ -150,6 +156,10 @@ struct zsock_pollfd {
#define TLS_DTLS_ROLE_CLIENT 0 /**< Client role in a DTLS session. */
#define TLS_DTLS_ROLE_SERVER 1 /**< Server role in a DTLS session. */

/* Valid values for TLS_CERT_NOCOPY option */
#define TLS_CERT_NOCOPY_NONE 0     /**< Cert duplicated in heap */
#define TLS_CERT_NOCOPY_OPTIONAL 1 /**< Cert not copied in heap if DER */

struct zsock_addrinfo {
	struct zsock_addrinfo *ai_next;
	int ai_flags;
+108 −29
Original line number Diff line number Diff line
@@ -113,6 +113,11 @@ __net_socket struct tls_context {
		/** Peer verification level. */
		int8_t verify_level;

		/** Indicating on whether DER certificates should not be copied
		 * to the heap.
		 */
		int8_t cert_nocopy;

		/** DTLS role, client by default. */
		int8_t role;

@@ -679,12 +684,30 @@ static int tls_rx(void *ctx, unsigned char *buf, size_t len)
	return received;
}

#if defined(MBEDTLS_X509_CRT_PARSE_C)
static bool crt_is_pem(const unsigned char *buf, size_t buflen)
{
	return (buflen != 0 && buf[buflen - 1] == '\0' &&
		strstr((const char *)buf, "-----BEGIN CERTIFICATE-----") != NULL);
}
#endif

static int tls_add_ca_certificate(struct tls_context *tls,
				  struct tls_credential *ca_cert)
{
#if defined(MBEDTLS_X509_CRT_PARSE_C)
	int err = mbedtls_x509_crt_parse(&tls->ca_chain,
					 ca_cert->buf, ca_cert->len);
	int err;

	if (tls->options.cert_nocopy == TLS_CERT_NOCOPY_NONE ||
	    crt_is_pem(ca_cert->buf, ca_cert->len)) {
		err = mbedtls_x509_crt_parse(&tls->ca_chain, ca_cert->buf,
					     ca_cert->len);
	} else {
		err = mbedtls_x509_crt_parse_der_nocopy(&tls->ca_chain,
							ca_cert->buf,
							ca_cert->len);
	}

	if (err != 0) {
		return -EINVAL;
	}
@@ -704,29 +727,59 @@ static void tls_set_ca_chain(struct tls_context *tls)
#endif /* MBEDTLS_X509_CRT_PARSE_C */
}

static int tls_set_own_cert(struct tls_context *tls,
			    struct tls_credential *own_cert,
			    struct tls_credential *priv_key)
static int tls_add_own_cert(struct tls_context *tls,
			    struct tls_credential *own_cert)
{
#if defined(MBEDTLS_X509_CRT_PARSE_C)
	int err = mbedtls_x509_crt_parse(&tls->own_cert,
	int err;

	if (tls->options.cert_nocopy == TLS_CERT_NOCOPY_NONE ||
	    crt_is_pem(own_cert->buf, own_cert->len)) {
		err = mbedtls_x509_crt_parse(&tls->own_cert,
					     own_cert->buf, own_cert->len);
	if (err != 0) {
		return -EINVAL;
	} else {
		err = mbedtls_x509_crt_parse_der_nocopy(&tls->own_cert,
							own_cert->buf,
							own_cert->len);
	}

	err = mbedtls_pk_parse_key(&tls->priv_key, priv_key->buf,
				   priv_key->len, NULL, 0);
	if (err != 0) {
		return -EINVAL;
	}

	err = mbedtls_ssl_conf_own_cert(&tls->config, &tls->own_cert,
	return 0;
#endif /* MBEDTLS_X509_CRT_PARSE_C */

	return -ENOTSUP;
}

static int tls_set_own_cert(struct tls_context *tls)
{
#if defined(MBEDTLS_X509_CRT_PARSE_C)
	int err = mbedtls_ssl_conf_own_cert(&tls->config, &tls->own_cert,
					    &tls->priv_key);
	if (err != 0) {
		err = -ENOMEM;
	}

	return err;
#endif /* MBEDTLS_X509_CRT_PARSE_C */

	return -ENOTSUP;
}

static int tls_set_private_key(struct tls_context *tls,
			       struct tls_credential *priv_key)
{
#if defined(MBEDTLS_X509_CRT_PARSE_C)
	int err;

	err = mbedtls_pk_parse_key(&tls->priv_key, priv_key->buf,
				   priv_key->len, NULL, 0);
	if (err != 0) {
		return -EINVAL;
	}

	return 0;
#endif /* MBEDTLS_X509_CRT_PARSE_C */

@@ -760,20 +813,10 @@ static int tls_set_credential(struct tls_context *tls,
		return tls_add_ca_certificate(tls, cred);

	case TLS_CREDENTIAL_SERVER_CERTIFICATE:
	{
		struct tls_credential *priv_key =
			credential_get(cred->tag, TLS_CREDENTIAL_PRIVATE_KEY);
		if (!priv_key) {
			return -ENOENT;
		}

		return tls_set_own_cert(tls, cred, priv_key);
	}
		return tls_add_own_cert(tls, cred);

	case TLS_CREDENTIAL_PRIVATE_KEY:
		/* Ignore private key - it will be used together
		 * with public certificate
		 */
		return tls_set_private_key(tls, cred);
	break;

	case TLS_CREDENTIAL_PSK:
@@ -805,7 +848,7 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls)
	struct tls_credential *cred;
	sec_tag_t tag;
	int i, err = 0;
	bool tag_found, ca_cert_present = false;
	bool tag_found, ca_cert_present = false, own_cert_present = false;

	credentials_lock();

@@ -824,6 +867,8 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls)

			if (cred->type == TLS_CREDENTIAL_CA_CERTIFICATE) {
				ca_cert_present = true;
			} else if (cred->type == TLS_CREDENTIAL_SERVER_CERTIFICATE) {
				own_cert_present = true;
			}
		}

@@ -836,9 +881,14 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls)
exit:
	credentials_unlock();

	if (err == 0 && ca_cert_present) {
	if (err == 0) {
		if (ca_cert_present) {
			tls_set_ca_chain(tls);
		}
		if (own_cert_present) {
			err = tls_set_own_cert(tls);
		}
	}

	return err;
}
@@ -1312,6 +1362,31 @@ static int tls_opt_peer_verify_set(struct tls_context *context,
	return 0;
}

static int tls_opt_cert_nocopy_set(struct tls_context *context,
				   const void *optval, socklen_t optlen)
{
	int *cert_nocopy;

	if (!optval) {
		return -EINVAL;
	}

	if (optlen != sizeof(int)) {
		return -EINVAL;
	}

	cert_nocopy = (int *)optval;

	if (*cert_nocopy != TLS_CERT_NOCOPY_NONE &&
	    *cert_nocopy != TLS_CERT_NOCOPY_OPTIONAL) {
		return -EINVAL;
	}

	context->options.cert_nocopy = *cert_nocopy;

	return 0;
}

static int tls_opt_dtls_role_set(struct tls_context *context,
				 const void *optval, socklen_t optlen)
{
@@ -2340,6 +2415,10 @@ int ztls_setsockopt_ctx(struct tls_context *ctx, int level, int optname,
		err = tls_opt_peer_verify_set(ctx, optval, optlen);
		break;

	case TLS_CERT_NOCOPY:
		err = tls_opt_cert_nocopy_set(ctx, optval, optlen);
		break;

	case TLS_DTLS_ROLE:
		err = tls_opt_dtls_role_set(ctx, optval, optlen);
		break;