Skip to content

Commit

Permalink
fixes #1846 Add support for TLS PSK
Browse files Browse the repository at this point in the history
  • Loading branch information
gdamore committed Jul 21, 2024
1 parent 8c5142a commit cc6f1c6
Show file tree
Hide file tree
Showing 8 changed files with 481 additions and 8 deletions.
1 change: 1 addition & 0 deletions docs/man/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ if (NNG_ENABLE_DOC)
nng_tls_config_free
nng_tls_config_hold
nng_tls_config_own_cert
nng_tls_config_psk
nng_tls_config_server_name
nng_tls_engine_description
nng_tls_engine_fips_mode
Expand Down
3 changes: 2 additions & 1 deletion docs/man/libnng.3.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= libnng(3)
//
// Copyright 2023 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
// Copyright 2020 Dirac Research <robert.bielik@dirac.com>
Expand Down Expand Up @@ -471,6 +471,7 @@ with TLS support.
|xref:nng_tls_config_ca_chain.3tls.adoc[nng_tls_config_ca_chain()]|set certificate authority chain
|xref:nng_tls_config_ca_file.3tls.adoc[nng_tls_config_ca_file()]|load certificate authority from file
|xref:nng_tls_config_cert_key_file.3tls.adoc[nng_tls_config_cert_key_file()]|load own certificate and key from file
|xref:nng_tls_config_psk.3tls.adoc[nng_tls_config_psk()]|set pre-shared key and identity
|xref:nng_tls_config_own_cert.3tls.adoc[nng_tls_config_own_cert()]|set own certificate and key
|xref:nng_tls_config_free.3tls.adoc[nng_tls_config_free()]|free TLS configuration
|xref:nng_tls_config_server_name.3tls.adoc[nng_tls_config_server_name()]|set remote server name
Expand Down
65 changes: 65 additions & 0 deletions docs/man/nng_tls_config_psk.3tls.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
= nng_tls_config_psk(3tls)
//
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
//
// This document is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//

== NAME

nng_tls_config_psk - configure pre-shared keys (PSK) for TLS

== SYNOPSIS

[source, c]
----
#include <nng/nng.h>
#include <nng/supplemental/tls/tls.h>
int nng_tls_config_psk(nng_tls_config *cfg, const char *identity,
const uint8_t *key, size_t key_len);
----

== DESCRIPTION

The `nng_tls_config_psk()` function configures a pre-shared secret to use for TLS connections.

Client mode configurations can call this just once, to set their own _identity_
and a single _key_ of __key_len__ bytes in size.

Server mode configurations can call this multiple times,
and servers will look up the appropriate key to use when a client connects.

The _identity_ may be thought of at some level as a public value like a user name,
and the _key_ of course is the confidential material used to establish keys.
Both parties my have the same values for _identity_, _key_, and __key_len__.

Implementations may impose limits on whether this functionality is supported, as well
as limitations on the length of keys or identities, but generally key lengths of up
to 32 bytes as well as identities of up to 64 bytes will be supported wherever PSK
configurations are present.

Note that while some implementations may allow arbitrary byte patterns in the identity,
this implementation does not support embedded zero bytes, and assumes that the values
are printable (for logging).

== RETURN VALUES

This function returns 0 on success, and non-zero otherwise.

== ERRORS

[horizontal]
`NNG_ENOMEM`:: Insufficient memory is available.
`NNG_EBUSY`:: The configuration _cfg_ is already in use, and cannot be modified.
`NNG_EINVAL`:: Invalid parameters were supplied.

== SEE ALSO

[.text-left]
xref:nng_strerror.3.adoc[nng_strerror(3)],
xref:nng_tls_config_alloc.3tls.adoc[nng_tls_config_alloc(3tls)],
xref:nng.7.adoc[nng(7)]
13 changes: 10 additions & 3 deletions include/nng/supplemental/tls/engine.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
Expand Down Expand Up @@ -150,6 +150,12 @@ typedef struct nng_tls_engine_config_ops_s {
int (*own_cert)(
nng_tls_engine_config *, const char *, const char *, const char *);

// psk configures a PSK identity and key. This can be called
// once for clients, or multiple times for servers. However, not all
// implementations support multiple PSKs for a server.
int (*psk)(
nng_tls_engine_config *, const char *, const uint8_t *, size_t);

// version configures the minimum and maximum TLS versions. The
// engine should default to supporting TLS1.0 through 1.2, and
// optionally 1.3 if it can. The engine should restrict the
Expand All @@ -163,8 +169,9 @@ typedef struct nng_tls_engine_config_ops_s {

typedef enum nng_tls_engine_version_e {
NNG_TLS_ENGINE_V0 = 0,
NNG_TLS_ENGINE_V1 = 1,
NNG_TLS_ENGINE_VERSION = NNG_TLS_ENGINE_V1,
NNG_TLS_ENGINE_V1 = 1, // adds FIPS, TLS 1.3 support
NNG_TLS_ENGINE_V2 = 2, // adds PSK support
NNG_TLS_ENGINE_VERSION = NNG_TLS_ENGINE_V2,
} nng_tls_engine_version;

typedef struct nng_tls_engine_s {
Expand Down
12 changes: 11 additions & 1 deletion include/nng/supplemental/tls/tls.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
Expand All @@ -18,6 +18,8 @@ extern "C" {
#include <stddef.h>
#include <stdint.h>

#include <nng/nng.h>

// Note that TLS functions may be stubbed out if TLS is not enabled in
// the build.

Expand Down Expand Up @@ -116,6 +118,14 @@ NNG_DECL int nng_tls_config_ca_file(nng_tls_config *, const char *);
NNG_DECL int nng_tls_config_cert_key_file(
nng_tls_config *, const char *, const char *);

// nng_tls_config_psk_identity is used to pass TLS PSK parameters. The
// identity, and an associated key. Clients can only do this once.
// Servers can do it multiple times, potentially, to provide for different
// keys for different client identities. There is no way to remove these
// from a configuration.
NNG_DECL int nng_tls_config_psk(
nng_tls_config *, const char *, const uint8_t *, size_t);

// Configure supported TLS version. By default we usually restrict
// ourselves to TLS 1.2 and newer. We do not support older versions.
// If the implementation cannot support any version (for example if
Expand Down
109 changes: 108 additions & 1 deletion src/supplemental/tls/mbedtls/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#include "mbedtls/version.h" // Must be first in order to pick up version

#include "mbedtls/error.h"

#include "nng/nng.h"
#include "nng/supplemental/tls/tls.h"

// mbedTLS renamed this header for 2.4.0.
#if MBEDTLS_VERSION_MAJOR > 2 || MBEDTLS_VERSION_MINOR >= 4
Expand All @@ -38,6 +40,35 @@ typedef struct {
nni_list_node node;
} pair;

// psk holds an identity and preshared key
typedef struct {
// NB: Technically RFC 4279 requires this be UTF-8 string, although
// others suggest making it opaque bytes. We treat it as a C string,
// so we cannot support embedded zero bytes.
char *identity;
uint8_t *key;
size_t keylen;
nni_list_node node;
} psk;

static void
psk_free(psk *p)
{
if (p != NULL) {
NNI_ASSERT(!nni_list_node_active(&p->node));
if (p->identity != NULL) {
nni_strfree(p->identity);
p->identity = NULL;
}
if (p->key != NULL && p->keylen != 0) {
nni_free(p->key, p->keylen);
p->key = NULL;
p->keylen = 0;
}
NNI_FREE_STRUCT(p);
}
}

#ifdef NNG_TLS_USE_CTR_DRBG
// Use a global RNG if we're going to override the builtin.
static mbedtls_ctr_drbg_context rng_ctx;
Expand All @@ -56,7 +87,9 @@ struct nng_tls_engine_config {
mbedtls_x509_crl crl;
int min_ver;
int max_ver;
nng_tls_mode mode;
nni_list pairs;
nni_list psks;
};

static void
Expand Down Expand Up @@ -371,6 +404,7 @@ static void
config_fini(nng_tls_engine_config *cfg)
{
pair *p;
psk *psk;

mbedtls_ssl_config_free(&cfg->cfg_ctx);
#ifdef NNG_TLS_USE_CTR_DRBG
Expand All @@ -381,13 +415,17 @@ config_fini(nng_tls_engine_config *cfg)
if (cfg->server_name) {
nni_strfree(cfg->server_name);
}
while ((p = nni_list_first(&cfg->pairs))) {
while ((p = nni_list_first(&cfg->pairs)) != NULL) {
nni_list_remove(&cfg->pairs, p);
mbedtls_x509_crt_free(&p->crt);
mbedtls_pk_free(&p->key);

NNI_FREE_STRUCT(p);
}
while ((psk = nni_list_first(&cfg->psks)) != NULL) {
nni_list_remove(&cfg->psks, psk);
psk_free(psk);
}
}

static int
Expand All @@ -405,7 +443,9 @@ config_init(nng_tls_engine_config *cfg, enum nng_tls_mode mode)
auth_mode = MBEDTLS_SSL_VERIFY_REQUIRED;
}

cfg->mode = mode;
NNI_LIST_INIT(&cfg->pairs, pair, node);
NNI_LIST_INIT(&cfg->psks, psk, node);
mbedtls_ssl_config_init(&cfg->cfg_ctx);
mbedtls_x509_crt_init(&cfg->ca_certs);
mbedtls_x509_crl_init(&cfg->crl);
Expand Down Expand Up @@ -452,6 +492,72 @@ config_server_name(nng_tls_engine_config *cfg, const char *name)
return (0);
}

// callback used on the server side to select the right key
static int
config_psk_cb(void *arg, mbedtls_ssl_context *ssl,
const unsigned char *identity, size_t id_len)
{
nng_tls_engine_config *cfg = arg;
psk *psk;
NNI_LIST_FOREACH (&cfg->psks, psk) {
if (id_len == strlen(psk->identity) &&
(memcmp(identity, psk->identity, id_len) == 0)) {
nng_log_debug("NNG-TLS-PSK-IDENTITY",
"TLS client using PSK identity %s", psk->identity);
return (mbedtls_ssl_set_hs_psk(
ssl, psk->key, psk->keylen));
}
}
nng_log_warn(
"NNG-TLS-PSK-NO-IDENTITY", "TLS client PSK identity not found");
return (MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE);
}

static int
config_psk(nng_tls_engine_config *cfg, const char *identity,
const uint8_t *key, size_t key_len)
{
int rv;
psk *srch;
psk *newpsk;

if (((newpsk = NNI_ALLOC_STRUCT(newpsk)) == NULL) ||
((newpsk->identity = nni_strdup(identity)) == NULL) ||
((newpsk->key = nni_alloc(key_len)) == NULL)) {
psk_free(newpsk);
return (NNG_ENOMEM);

Check warning on line 528 in src/supplemental/tls/mbedtls/tls.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/tls/mbedtls/tls.c#L527-L528

Added lines #L527 - L528 were not covered by tests
}
newpsk->keylen = key_len;
memcpy(newpsk->key, key, key_len);

if (cfg->mode == NNG_TLS_MODE_SERVER) {
if (nni_list_empty(&cfg->psks)) {
mbedtls_ssl_conf_psk_cb(
&cfg->cfg_ctx, config_psk_cb, cfg);
}
} else {
if ((rv = mbedtls_ssl_conf_psk(&cfg->cfg_ctx, key, key_len,
(const unsigned char *) identity,
strlen(identity))) != 0) {
psk_free(newpsk);
tls_log_err("NNG-TLS-PSK-FAIL",
"Failed to configure PSK identity", rv);
return (tls_mk_err(rv));

Check warning on line 545 in src/supplemental/tls/mbedtls/tls.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/tls/mbedtls/tls.c#L542-L545

Added lines #L542 - L545 were not covered by tests
}
}

// If the identity was previously configured, replace it.
// The rule here is that last one wins, so we always append.
NNI_LIST_FOREACH (&cfg->psks, srch) {
if (strcmp(srch->identity, identity) == 0) {
nni_list_remove(&cfg->psks, srch);
psk_free(srch);
}
}
nni_list_append(&cfg->psks, newpsk);
return (0);
}

static int
config_auth_mode(nng_tls_engine_config *cfg, nng_tls_auth_mode mode)
{
Expand Down Expand Up @@ -630,6 +736,7 @@ static nng_tls_engine_config_ops config_ops = {
.ca_chain = config_ca_chain,
.own_cert = config_own_cert,
.server = config_server_name,
.psk = config_psk,
.version = config_version,
};

Expand Down
17 changes: 16 additions & 1 deletion src/supplemental/tls/tls_common.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright 2021 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
Expand Down Expand Up @@ -1380,6 +1380,21 @@ nng_tls_config_own_cert(
return (rv);
}

int
nng_tls_config_psk(nng_tls_config *cfg, const char *identity,
const uint8_t *key, size_t key_len)
{
int rv;
nni_mtx_lock(&cfg->lock);
if (cfg->busy != 0) {
rv = NNG_EBUSY;
} else {

Check warning on line 1391 in src/supplemental/tls/tls_common.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/tls/tls_common.c#L1390-L1391

Added lines #L1390 - L1391 were not covered by tests
rv = cfg->ops.psk((void *) (cfg + 1), identity, key, key_len);
}
nni_mtx_unlock(&cfg->lock);
return (rv);
}

int
nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode)
{
Expand Down
Loading

0 comments on commit cc6f1c6

Please sign in to comment.