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: Lucas Dietrich <ld.adecy@gmail.com>
This commit is contained in:
Lucas Dietrich 2021-11-19 09:46:22 +01:00 committed by Anas Nashif
commit 4e103bcb20
2 changed files with 118 additions and 29 deletions

View file

@ -139,6 +139,12 @@ struct zsock_pollfd {
#define TLS_DTLS_HANDSHAKE_TIMEOUT_MIN 8 #define TLS_DTLS_HANDSHAKE_TIMEOUT_MIN 8
#define TLS_DTLS_HANDSHAKE_TIMEOUT_MAX 9 #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 */ /* 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_CLIENT 0 /**< Client role in a DTLS session. */
#define TLS_DTLS_ROLE_SERVER 1 /**< Server 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 {
struct zsock_addrinfo *ai_next; struct zsock_addrinfo *ai_next;
int ai_flags; int ai_flags;

View file

@ -132,6 +132,11 @@ __net_socket struct tls_context {
/** Peer verification level. */ /** Peer verification level. */
int8_t verify_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. */ /** DTLS role, client by default. */
int8_t role; int8_t role;
@ -654,12 +659,30 @@ static int tls_rx(void *ctx, unsigned char *buf, size_t len)
return received; 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, static int tls_add_ca_certificate(struct tls_context *tls,
struct tls_credential *ca_cert) struct tls_credential *ca_cert)
{ {
#if defined(MBEDTLS_X509_CRT_PARSE_C) #if defined(MBEDTLS_X509_CRT_PARSE_C)
int err = mbedtls_x509_crt_parse(&tls->ca_chain, int err;
ca_cert->buf, ca_cert->len);
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) { if (err != 0) {
return -EINVAL; return -EINVAL;
} }
@ -679,17 +702,53 @@ static void tls_set_ca_chain(struct tls_context *tls)
#endif /* MBEDTLS_X509_CRT_PARSE_C */ #endif /* MBEDTLS_X509_CRT_PARSE_C */
} }
static int tls_set_own_cert(struct tls_context *tls, static int tls_add_own_cert(struct tls_context *tls,
struct tls_credential *own_cert, struct tls_credential *own_cert)
struct tls_credential *priv_key)
{ {
#if defined(MBEDTLS_X509_CRT_PARSE_C) #if defined(MBEDTLS_X509_CRT_PARSE_C)
int err = mbedtls_x509_crt_parse(&tls->own_cert, int err;
own_cert->buf, own_cert->len);
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);
} else {
err = mbedtls_x509_crt_parse_der_nocopy(&tls->own_cert,
own_cert->buf,
own_cert->len);
}
if (err != 0) { if (err != 0) {
return -EINVAL; return -EINVAL;
} }
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, err = mbedtls_pk_parse_key(&tls->priv_key, priv_key->buf,
priv_key->len, NULL, 0, priv_key->len, NULL, 0,
tls_ctr_drbg_random, NULL); tls_ctr_drbg_random, NULL);
@ -697,12 +756,6 @@ static int tls_set_own_cert(struct tls_context *tls,
return -EINVAL; return -EINVAL;
} }
err = mbedtls_ssl_conf_own_cert(&tls->config, &tls->own_cert,
&tls->priv_key);
if (err != 0) {
err = -ENOMEM;
}
return 0; return 0;
#endif /* MBEDTLS_X509_CRT_PARSE_C */ #endif /* MBEDTLS_X509_CRT_PARSE_C */
@ -736,21 +789,11 @@ static int tls_set_credential(struct tls_context *tls,
return tls_add_ca_certificate(tls, cred); return tls_add_ca_certificate(tls, cred);
case TLS_CREDENTIAL_SERVER_CERTIFICATE: case TLS_CREDENTIAL_SERVER_CERTIFICATE:
{ return tls_add_own_cert(tls, cred);
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);
}
case TLS_CREDENTIAL_PRIVATE_KEY: case TLS_CREDENTIAL_PRIVATE_KEY:
/* Ignore private key - it will be used together return tls_set_private_key(tls, cred);
* with public certificate break;
*/
break;
case TLS_CREDENTIAL_PSK: case TLS_CREDENTIAL_PSK:
{ {
@ -781,7 +824,7 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls)
struct tls_credential *cred; struct tls_credential *cred;
sec_tag_t tag; sec_tag_t tag;
int i, err = 0; int i, err = 0;
bool tag_found, ca_cert_present = false; bool tag_found, ca_cert_present = false, own_cert_present = false;
credentials_lock(); credentials_lock();
@ -800,6 +843,8 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls)
if (cred->type == TLS_CREDENTIAL_CA_CERTIFICATE) { if (cred->type == TLS_CREDENTIAL_CA_CERTIFICATE) {
ca_cert_present = true; ca_cert_present = true;
} else if (cred->type == TLS_CREDENTIAL_SERVER_CERTIFICATE) {
own_cert_present = true;
} }
} }
@ -812,8 +857,13 @@ static int tls_mbedtls_set_credentials(struct tls_context *tls)
exit: exit:
credentials_unlock(); credentials_unlock();
if (err == 0 && ca_cert_present) { if (err == 0) {
tls_set_ca_chain(tls); if (ca_cert_present) {
tls_set_ca_chain(tls);
}
if (own_cert_present) {
err = tls_set_own_cert(tls);
}
} }
return err; return err;
@ -1327,6 +1377,31 @@ static int tls_opt_peer_verify_set(struct tls_context *context,
return 0; 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, static int tls_opt_dtls_role_set(struct tls_context *context,
const void *optval, socklen_t optlen) const void *optval, socklen_t optlen)
{ {
@ -2464,6 +2539,10 @@ int ztls_setsockopt_ctx(struct tls_context *ctx, int level, int optname,
err = tls_opt_peer_verify_set(ctx, optval, optlen); err = tls_opt_peer_verify_set(ctx, optval, optlen);
break; break;
case TLS_CERT_NOCOPY:
err = tls_opt_cert_nocopy_set(ctx, optval, optlen);
break;
case TLS_DTLS_ROLE: case TLS_DTLS_ROLE:
err = tls_opt_dtls_role_set(ctx, optval, optlen); err = tls_opt_dtls_role_set(ctx, optval, optlen);
break; break;