1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #16179 from leandrolanzieri/dev/net/sock/dtls_multi_cred

net/sock/dtls: allow using multiple credentials
This commit is contained in:
Peter Kietzmann 2021-04-06 10:04:45 +02:00 committed by GitHub
commit f3482071e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 687 additions and 76 deletions

View File

@ -24,6 +24,9 @@ USEPKG += tinydtls
USEMODULE += sock_dtls
USEMODULE += sock_udp
# extra utilities for endpoint printing
USEMODULE += sock_util
# tinydtls needs crypto secure PRNG
USEMODULE += prng_sha1prng

View File

@ -40,3 +40,37 @@ constraints that applies specifically to the stack are also applied here.
For tinydtls, please refer to [dtls-echo README][1].
[1]: https://github.com/RIOT-OS/RIOT/blob/master/examples/dtls-echo/README.md
### Cipher suite
You can select between `TLS_PSK_WITH_AES_128_CCM_8` (Pre-Shared Key) and
`TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8` (Raw Public Key - ECC) either by setting
the correspondent options in `make menuconfig` or via `CFLAGS`. See the `Makefile`
for more information.
## Credentials
### Pre-Shared Key
When using Pre-Shared Key (PSK), the client registers two keys to `credman` and
makes them available to the sock. The client registers a PSK callback function,
which allows the application to specify which credential to use with a
particular sock and endpoint. In this application the client will print the
server's endpoint and the sent hint, if any. As per the sock DTLS documentation,
if the application callback fails to determine which credential should be used,
an Identity Hint (https://tools.ietf.org/html/rfc4279#section-5.2) match is
attempted. `credential1` is assigned an Identity Hint, defined as
`PSK_DEFAULT_HINT` in `tinydtls_keys.h`. This hint is used by sock to select the
credential, in case the DTLS server sends such a hint. Finally, if none of the
above attempts succeed in determining which credential to use, sock DTLS will
pick the first valid credential registered in the sock.
The behaviour above can be tested, for example, by removing the hint from the
server (`sock_dtls_set_server_psk_id_hint`). As `credential0` is the first
registered credential in the client, it will be chosen. As the server does not
have this credential, the handshake will fail.
### ECC
When using ECC Raw Public Key (RPK), the server registers two keys to `credman`
and makes them available to the sock. It also registers an RPK callback
function, which allows the application to specify which credential to use with a
particular sock and endpoint (the client could as well do so). In the particular
case of this example the callback always returns the credential with tag
`SOCK_DTLS_SERVER_TAG_1`.

View File

@ -18,10 +18,14 @@
#include <stdio.h>
#include "kernel_defines.h"
#include "net/sock/udp.h"
#include "net/sock/dtls.h"
#include "net/sock/dtls/creds.h"
#include "net/ipv6/addr.h"
#include "net/credman.h"
#include "net/sock/util.h"
#include "tinydtls_keys.h"
@ -29,36 +33,41 @@
#define DTLS_DEFAULT_PORT 20220 /* DTLS default port */
#endif
#define SOCK_DTLS_CLIENT_TAG (2)
#define SOCK_DTLS_CLIENT_TAG_0 (2)
#define SOCK_DTLS_CLIENT_TAG_1 (3)
#ifdef CONFIG_DTLS_ECC
static const ecdsa_public_key_t other_pubkeys[] = {
{ .x = ecdsa_pub_key_x, .y = ecdsa_pub_key_y },
static const ecdsa_public_key_t other_pubkeys0[] = {
{ .x = ecdsa_pub_key0_x, .y = ecdsa_pub_key0_y },
};
static const credman_credential_t credential = {
static const credman_credential_t credential0 = {
.type = CREDMAN_TYPE_ECDSA,
.tag = SOCK_DTLS_CLIENT_TAG,
.tag = SOCK_DTLS_CLIENT_TAG_0,
.params = {
.ecdsa = {
.private_key = ecdsa_priv_key,
.private_key = ecdsa_priv_key0,
.public_key = {
.x = ecdsa_pub_key_x,
.y = ecdsa_pub_key_y,
.x = ecdsa_pub_key0_x,
.y = ecdsa_pub_key0_y,
},
.client_keys = (ecdsa_public_key_t *)other_pubkeys,
.client_keys_size = ARRAY_SIZE(other_pubkeys),
.client_keys = (ecdsa_public_key_t *)other_pubkeys0,
.client_keys_size = ARRAY_SIZE(other_pubkeys0),
}
},
};
#else /* ifdef CONFIG_DTLS_PSK */
static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY;
static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY;
static const uint8_t psk_id_0[] = PSK_WRONG_IDENTITY;
static const uint8_t psk_key_0[] = PSK_WRONG_KEY;
static const credman_credential_t credential = {
static const uint8_t psk_id_1[] = PSK_DEFAULT_IDENTITY;
static const uint8_t psk_key_1[] = PSK_DEFAULT_KEY;
static const char psk_id_1_hint[] = PSK_DEFAULT_HINT;
static const credman_credential_t credential0 = {
.type = CREDMAN_TYPE_PSK,
.tag = SOCK_DTLS_CLIENT_TAG,
.tag = SOCK_DTLS_CLIENT_TAG_0,
.params = {
.psk = {
.key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, },
@ -66,6 +75,39 @@ static const credman_credential_t credential = {
}
},
};
static const credman_credential_t credential1 = {
.type = CREDMAN_TYPE_PSK,
.tag = SOCK_DTLS_CLIENT_TAG_1,
.params = {
.psk = {
.key = { .s = psk_key_1, .len = sizeof(psk_key_1) - 1, },
.id = { .s = psk_id_1, .len = sizeof(psk_id_1) - 1, },
.hint = { .s = psk_id_1_hint, .len = sizeof(psk_id_1_hint) - 1, },
}
},
};
static credman_tag_t _client_psk_cb(sock_dtls_t *sock, sock_udp_ep_t *ep, credman_tag_t tags[],
unsigned tags_len, const char *hint, size_t hint_len)
{
(void) sock;
(void) tags;
(void) tags_len;
/* this callback is here just to show the functionality, it only prints the received hint */
char addrstr[IPV6_ADDR_MAX_STR_LEN];
uint16_t port;
sock_udp_ep_fmt(ep, addrstr, &port);
printf("From [%s]:%d\n", addrstr, port);
if (hint && hint_len) {
printf("Client got hint: %.*s\n", (unsigned)hint_len, hint);
}
return CREDMAN_TAG_EMPTY;
}
#endif
static int client_send(char *addr_str, char *data, size_t datalen)
@ -108,21 +150,39 @@ static int client_send(char *addr_str, char *data, size_t datalen)
return -1;
}
if (sock_dtls_create(&dtls_sock, &udp_sock,
SOCK_DTLS_CLIENT_TAG,
res = credman_add(&credential0);
if (res < 0 && res != CREDMAN_EXIST) {
/* ignore duplicate credentials */
printf("Error cannot add credential to system: %d\n", (int)res);
return -1;
}
if (sock_dtls_create(&dtls_sock, &udp_sock, SOCK_DTLS_CLIENT_TAG_0,
SOCK_DTLS_1_2, SOCK_DTLS_CLIENT) < 0) {
puts("Error creating DTLS sock");
sock_udp_close(&udp_sock);
return -1;
}
res = credman_add(&credential);
#if IS_ACTIVE(CONFIG_DTLS_PSK)
/* register a second PSK credential */
res = credman_add(&credential1);
if (res < 0 && res != CREDMAN_EXIST) {
/* ignore duplicate credentials */
printf("Error cannot add credential to system: %d\n", (int)res);
return -1;
}
/* make the new credential available to the sock */
if (sock_dtls_add_credential(&dtls_sock, SOCK_DTLS_CLIENT_TAG_1) < 0) {
printf("Error cannot add credential to the sock: %d\n", (int)res);
return -1;
}
/* register a callback for PSK credential selection */
sock_dtls_set_client_psk_cb(&dtls_sock, _client_psk_cb);
#endif
res = sock_dtls_session_init(&dtls_sock, &remote, &session);
if (res <= 0) {
return res;

View File

@ -20,6 +20,8 @@
#include "net/sock/udp.h"
#include "net/sock/dtls.h"
#include "net/sock/dtls/creds.h"
#include "net/sock/util.h"
#include "net/credman.h"
#include "msg.h"
#include "thread.h"
@ -31,7 +33,8 @@
#define DTLS_DEFAULT_PORT (20220) /* DTLS default port */
#endif
#define SOCK_DTLS_SERVER_TAG (10)
#define SOCK_DTLS_SERVER_TAG_0 (10)
#define SOCK_DTLS_SERVER_TAG_1 (11)
#define DTLS_STOP_SERVER_MSG 0x4001 /* Custom IPC type msg. */
#define READER_QUEUE_SIZE (8U)
@ -42,33 +45,73 @@ static kernel_pid_t _dtls_server_pid = KERNEL_PID_UNDEF;
#ifdef CONFIG_DTLS_ECC
static const ecdsa_public_key_t other_pubkeys[] = {
{ .x = ecdsa_pub_key_x, .y = ecdsa_pub_key_y },
{ .x = ecdsa_pub_key0_x, .y = ecdsa_pub_key0_y },
};
static const credman_credential_t credential = {
static const credman_credential_t credential0 = {
.type = CREDMAN_TYPE_ECDSA,
.tag = SOCK_DTLS_SERVER_TAG,
.tag = SOCK_DTLS_SERVER_TAG_0,
.params = {
.ecdsa = {
.private_key = ecdsa_priv_key,
.private_key = ecdsa_priv_key0,
.public_key = {
.x = ecdsa_pub_key_x,
.y = ecdsa_pub_key_y,
.x = ecdsa_pub_key0_x,
.y = ecdsa_pub_key0_y,
},
.client_keys = (ecdsa_public_key_t *)other_pubkeys,
.client_keys_size = ARRAY_SIZE(other_pubkeys),
},
},
};
static const ecdsa_public_key_t other_pubkeys1[] = {
{ .x = ecdsa_pub_key1_x, .y = ecdsa_pub_key1_y },
};
static const credman_credential_t credential1 = {
.type = CREDMAN_TYPE_ECDSA,
.tag = SOCK_DTLS_SERVER_TAG_1,
.params = {
.ecdsa = {
.private_key = ecdsa_priv_key1,
.public_key = {
.x = ecdsa_pub_key1_x,
.y = ecdsa_pub_key1_y,
},
.client_keys = (ecdsa_public_key_t *)other_pubkeys1,
.client_keys_size = ARRAY_SIZE(other_pubkeys1),
}
},
};
static credman_tag_t _rpk_cb(sock_dtls_t *sock, sock_udp_ep_t *ep, credman_tag_t tags[],
unsigned tags_len)
{
(void) sock;
(void) ep;
(void) tags;
(void) tags_len;
char addrstr[IPV6_ADDR_MAX_STR_LEN];
uint16_t port;
sock_udp_ep_fmt(ep, addrstr, &port);
printf("From [%s]:%d\n", addrstr, port);
return SOCK_DTLS_SERVER_TAG_1;
}
#else /* #ifdef CONFIG_DTLS_PSK */
static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY;
static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY;
static const credman_credential_t credential = {
static const credman_credential_t credential0 = {
.type = CREDMAN_TYPE_PSK,
.tag = SOCK_DTLS_SERVER_TAG,
.tag = SOCK_DTLS_SERVER_TAG_0,
.params = {
.psk = {
.key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, },
.id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, },
},
},
};
@ -93,20 +136,47 @@ void *dtls_server_wrapper(void *arg)
local.port = DTLS_DEFAULT_PORT;
sock_udp_create(&udp_sock, &local, NULL, 0);
res = sock_dtls_create(&sock, &udp_sock, SOCK_DTLS_SERVER_TAG,
res = credman_add(&credential0);
if (res < 0 && res != CREDMAN_EXIST) {
/* ignore duplicate credentials */
printf("Error cannot add credential to system: %d\n", (int)res);
return NULL;
}
res = sock_dtls_create(&sock, &udp_sock, SOCK_DTLS_SERVER_TAG_0,
SOCK_DTLS_1_2, SOCK_DTLS_SERVER);
if (res < 0) {
puts("Error creating DTLS sock");
return NULL;
}
res = credman_add(&credential);
/* set PSK Identity hint, this is optional and application-specific */
#if IS_ACTIVE(CONFIG_DTLS_PSK)
if (sock_dtls_set_server_psk_id_hint(&sock, PSK_DEFAULT_HINT) < 0) {
puts("Error setting PSK Identity hint");
return NULL;
}
#endif
#if IS_ACTIVE(CONFIG_DTLS_ECC)
/* register a second RPK */
res = credman_add(&credential1);
if (res < 0 && res != CREDMAN_EXIST) {
/* ignore duplicate credentials */
printf("Error cannot add credential to system: %d\n", (int)res);
return NULL;
}
/* make the new credential available to the sock */
if (sock_dtls_add_credential(&sock, SOCK_DTLS_SERVER_TAG_1) < 0) {
printf("Error cannot add credential to the sock: %d\n", (int)res);
return NULL;
}
/* register a callback for RPK credential selection */
sock_dtls_set_rpk_cb(&sock, _rpk_cb);
#endif
while (active) {
if ((msg_try_receive(&msg) == 1) &&
(msg.type == DTLS_STOP_SERVER_MSG)){

View File

@ -29,35 +29,57 @@ extern "C" {
* Default keys examples for tinyDTLS (for RIOT, Linux and Contiki)
*/
#ifdef CONFIG_DTLS_PSK
#define PSK_WRONG_IDENTITY "Wrong_identity"
#define PSK_WRONG_KEY "wrong_secret"
#define PSK_DEFAULT_IDENTITY "Client_identity"
#define PSK_DEFAULT_KEY "secretPSK"
#define PSK_OPTIONS "i:k:"
#define PSK_ID_MAXLEN 32
#define PSK_MAXLEN 32
#define PSK_DEFAULT_HINT "hint"
#endif /* CONFIG_DTLS_PSK */
#ifdef CONFIG_DTLS_ECC
static const unsigned char ecdsa_priv_key[] = {
static const unsigned char ecdsa_priv_key0[] = {
0x41, 0xC1, 0xCB, 0x6B, 0x51, 0x24, 0x7A, 0x14,
0x43, 0x21, 0x43, 0x5B, 0x7A, 0x80, 0xE7, 0x14,
0x89, 0x6A, 0x33, 0xBB, 0xAD, 0x72, 0x94, 0xCA,
0x40, 0x14, 0x55, 0xA1, 0x94, 0xA9, 0x49, 0xFA
};
static const unsigned char ecdsa_pub_key_x[] = {
static const unsigned char ecdsa_pub_key0_x[] = {
0x36, 0xDF, 0xE2, 0xC6, 0xF9, 0xF2, 0xED, 0x29,
0xDA, 0x0A, 0x9A, 0x8F, 0x62, 0x68, 0x4E, 0x91,
0x63, 0x75, 0xBA, 0x10, 0x30, 0x0C, 0x28, 0xC5,
0xE4, 0x7C, 0xFB, 0xF2, 0x5F, 0xA5, 0x8F, 0x52
};
static const unsigned char ecdsa_pub_key_y[] = {
static const unsigned char ecdsa_pub_key0_y[] = {
0x71, 0xA0, 0xD4, 0xFC, 0xDE, 0x1A, 0xB8, 0x78,
0x5A, 0x3C, 0x78, 0x69, 0x35, 0xA7, 0xCF, 0xAB,
0xE9, 0x3F, 0x98, 0x72, 0x09, 0xDA, 0xED, 0x0B,
0x4F, 0xAB, 0xC3, 0x6F, 0xC7, 0x72, 0xF8, 0x29
};
static const unsigned char ecdsa_priv_key1[] = {
0x99, 0x1b, 0x1c, 0xf1, 0x52, 0xa3, 0xf5, 0xac,
0xce, 0x58, 0x00, 0x45, 0xdc, 0xa7, 0x45, 0x45,
0x9e, 0xc6, 0xd8, 0x68, 0x21, 0xd4, 0x82, 0xb7,
0x17, 0x84, 0x0a, 0xdc, 0x1d, 0xf1, 0x09, 0x57
};
static const unsigned char ecdsa_pub_key1_x[] = {
0xb7, 0x4e, 0xa0, 0x62, 0x96, 0xc5, 0xb9, 0x09,
0xad, 0x36, 0x10, 0xab, 0xb1, 0xd8, 0x54, 0x69,
0xef, 0x2b, 0x15, 0x5a, 0xb5, 0x28, 0x21, 0x21,
0x9f, 0xa3, 0x9e, 0x6a, 0x02, 0xce, 0xb8, 0xb9
};
static const unsigned char ecdsa_pub_key1_y[] = {
0xcc, 0x0e, 0x88, 0x88, 0x91, 0x80, 0x7a, 0xdd,
0xf7, 0x4e, 0x2e, 0xe6, 0x6e, 0xd4, 0x22, 0xde,
0xbc, 0x68, 0xcd, 0x8f, 0xd9, 0x5a, 0xa0, 0xcd,
0x5f, 0x4a, 0x1a, 0xb7, 0x2f, 0x95, 0xfc, 0x76
};
#endif /* CONFIG_DTLS_ECC */
#ifdef __cplusplus
}

View File

@ -75,6 +75,19 @@ static dtls_handler_t _dtls_handler = {
#endif /* CONFIG_DTLS_ECC */
};
#ifdef CONFIG_DTLS_ECC
/**
* @brief Array of ECDSA keys to convert from credman credentials and send in
* the callback.
*/
typedef struct ecdsa_key_assignment {
dtls_ecdsa_key_t key;
const session_t *session;
} ecdsa_key_assignment_t;
static ecdsa_key_assignment_t _ecdsa_keys[CONFIG_DTLS_CREDENTIALS_MAX];
#endif
static int _read(struct dtls_context_t *ctx, session_t *session, uint8_t *buf,
size_t len)
{
@ -135,6 +148,18 @@ static int _event(struct dtls_context_t *ctx, session_t *session,
if (!level && (code != DTLS_EVENT_CONNECT)) {
mbox_put(&sock->mbox, &msg);
}
#if IS_ACTIVE(CONFIG_DTLS_ECC)
if (code == DTLS_EVENT_CONNECTED) {
for (unsigned i = 0; i < ARRAY_SIZE(_ecdsa_keys); i++) {
if (_ecdsa_keys[i].session && dtls_session_equals(session, _ecdsa_keys[i].session)) {
_ecdsa_keys[i].session = NULL;
break;
}
}
}
#endif
#ifdef SOCK_HAS_ASYNC
if (sock->async_cb != NULL) {
switch (code) {
@ -162,37 +187,103 @@ static int _get_psk_info(struct dtls_context_t *ctx, const session_t *session,
const unsigned char *desc, size_t desc_len,
unsigned char *result, size_t result_length)
{
(void)ctx;
(void)desc;
(void)desc_len;
(void)session;
int ret;
sock_dtls_t *sock = dtls_get_app_data(ctx);
credman_credential_t credential;
ret = credman_get(&credential, sock->tag, CREDMAN_TYPE_PSK);
if (ret < 0) {
DEBUG("sock_dtls: no matching PSK credential found\n");
return dtls_alert_fatal_create(DTLS_ALERT_DECRYPT_ERROR);
}
sock_udp_ep_t ep;
sock_dtls_t *sock = dtls_get_app_data(ctx);
const void *c = NULL;
size_t c_len = 0;
_session_to_ep(session, &ep);
switch (type) {
case DTLS_PSK_HINT:
DEBUG("sock_dtls: psk hint request\n");
/* Ignored. See https://tools.ietf.org/html/rfc4279#section-5.2 */
return 0;
/* return a hint to the client if set */
c_len = strlen(sock->psk_hint);
if (c_len) {
c = sock->psk_hint;
break;
}
else {
DEBUG("sock_dtls: no hint provided\n");
return 0;
}
case DTLS_PSK_IDENTITY:
DEBUG("sock_dtls: psk id request\n");
c = credential.params.psk.id.s;
c_len = credential.params.psk.id.len;
/* if the application set a callback , try to select credential from there */
if (sock->client_psk_cb) {
DEBUG("sock_dtls: requesting the application\n");
credential.tag = sock->client_psk_cb(sock, &ep, sock->tags, sock->tags_len,
(const char*)desc, desc_len);
if (credential.tag != CREDMAN_TAG_EMPTY) {
int ret = credman_get(&credential, credential.tag, CREDMAN_TYPE_PSK);
if (ret == CREDMAN_OK) {
c = credential.params.psk.id.s;
c_len = credential.params.psk.id.len;
break;
}
}
}
/* if no callback set or no valid credential returned, try to find a valid registered one */
DEBUG("sock_dtls: trying to get first PSK credential\n");
credman_credential_t first = { .tag = CREDMAN_TAG_EMPTY };
for (unsigned i = 0; i < sock->tags_len && !c; i++) {
if (credman_get(&credential, sock->tags[i], CREDMAN_TYPE_PSK) == CREDMAN_OK) {
/* if no hint was provided, settle for the first valid credential */
if (!desc) {
c = credential.params.psk.id.s;
c_len = credential.params.psk.id.len;
break;
}
/* save the first valid one in case we don't find the hint */
if (first.tag == CREDMAN_TAG_EMPTY) {
memcpy(&first, &credential, sizeof(credman_credential_t));
}
if (desc_len == credential.params.psk.hint.len &&
!strncmp(credential.params.psk.hint.s, (const char *)desc, desc_len)) {
c = credential.params.psk.id.s;
c_len = credential.params.psk.id.len;
}
}
}
/* if no credential so far, fallback to the first valid one, return alert otherwise */
if (!c) {
if (first.tag != CREDMAN_TAG_EMPTY) {
c = first.params.psk.id.s;
c_len = first.params.psk.id.len;
}
else {
DEBUG("sock_dtls: could not find a valid PSK credential\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
}
break;
case DTLS_PSK_KEY:
DEBUG("sock_dtls: psk key request\n");
c = credential.params.psk.key.s;
c_len = credential.params.psk.key.len;
if (desc) {
DEBUG("sock_dtls: looking for key for ID: %.*s\n", (unsigned)desc_len, desc);
/* try to find matching ID among the registered credentials */
for (unsigned i = 0; i < sock->tags_len; i++) {
if (credman_get(&credential, sock->tags[i], CREDMAN_TYPE_PSK) == CREDMAN_OK) {
DEBUG("sock_dtls: comparing to tag %d, with ID: %.*s\n", sock->tags[i],
(unsigned)credential.params.psk.id.len,
(char *)credential.params.psk.id.s);
if (desc_len == credential.params.psk.id.len &&
!memcmp(desc, credential.params.psk.id.s, desc_len)) {
DEBUG("sock_dtls: found\n");
c = credential.params.psk.key.s;
c_len = credential.params.psk.key.len;
break;
}
}
}
}
break;
default:
DEBUG("sock:dtls unsupported request type: %d\n", type);
@ -217,22 +308,63 @@ static int _get_ecdsa_key(struct dtls_context_t *ctx, const session_t *session,
const dtls_ecdsa_key_t **result)
{
(void)session;
int ret;
int ret = CREDMAN_ERROR;
sock_dtls_t *sock = (sock_dtls_t *)dtls_get_app_data(ctx);
sock_udp_ep_t ep;
_session_to_ep(session, &ep);
credman_credential_t credential;
ret = credman_get(&credential, sock->tag, CREDMAN_TYPE_ECDSA);
if (ret < 0) {
DEBUG("sock_dtls: no matching ecdsa credential found\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
credential.tag = CREDMAN_TAG_EMPTY;
DEBUG("sock_dtls: get ECDSA key\n");
/* if the application set a callback , try to select credential from there */
if (sock->rpk_cb) {
DEBUG("sock_dtls: requesting the application\n");
credential.tag = sock->rpk_cb(sock, &ep, sock->tags, sock->tags_len);
if (credential.tag != CREDMAN_TAG_EMPTY) {
ret = credman_get(&credential, credential.tag, CREDMAN_TYPE_ECDSA);
if (ret != CREDMAN_OK) {
credential.tag = CREDMAN_TAG_EMPTY;
}
}
}
static dtls_ecdsa_key_t key;
key.curve = DTLS_ECDH_CURVE_SECP256R1;
key.priv_key = credential.params.ecdsa.private_key;
key.pub_key_x = credential.params.ecdsa.public_key.x;
key.pub_key_y = credential.params.ecdsa.public_key.y;
*result = &key;
if (credential.tag == CREDMAN_TYPE_EMPTY) {
/* if could not get credential try to fetch the first valid credential */
for (unsigned i = 0; i < sock->tags_len; i++) {
ret = credman_get(&credential, sock->tags[i], CREDMAN_TYPE_ECDSA);
if (ret == CREDMAN_OK) {
break;
}
}
if (ret != CREDMAN_OK) {
DEBUG("sock_dtls: no valid credential registered\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
}
/* try to find a free ECDSA key assignment structure for the handshake. When unused, the session
* is not set. */
ecdsa_key_assignment_t *key = NULL;
for (unsigned i = 0; i < CONFIG_DTLS_CREDENTIALS_MAX; i++) {
if (!_ecdsa_keys[i].session) {
key = &_ecdsa_keys[i];
}
}
if (!key) {
DEBUG("sock_dtls: ECDSA keys are full\n");
return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
}
key->session = session;
key->key.curve = DTLS_ECDH_CURVE_SECP256R1;
key->key.priv_key = credential.params.ecdsa.private_key;
key->key.pub_key_x = credential.params.ecdsa.public_key.x;
key->key.pub_key_y = credential.params.ecdsa.public_key.y;
*result = &key->key;
return 0;
}
@ -279,13 +411,26 @@ int sock_dtls_create(sock_dtls_t *sock, sock_udp_t *udp_sock,
sock->udp_sock = udp_sock;
sock->buffer.data = NULL;
sock->psk_hint[0] = '\0';
sock->client_psk_cb = NULL;
sock->rpk_cb = NULL;
#ifdef SOCK_HAS_ASYNC
sock->async_cb = NULL;
sock->buf_ctx = NULL;
memset(&sock->async_cb_session, 0, sizeof(sock->async_cb_session));
#endif /* SOCK_HAS_ASYNC */
memset(sock->tags, CREDMAN_TAG_EMPTY, CONFIG_DTLS_CREDENTIALS_MAX * sizeof(credman_tag_t));
if (tag != CREDMAN_TAG_EMPTY) {
sock->tags_len = 1;
sock->tags[0] = tag;
}
else {
sock->tags_len = 0;
}
sock->role = role;
sock->tag = tag;
sock->dtls_ctx = dtls_new_context(sock);
if (!sock->dtls_ctx) {
DEBUG("sock_dtls: error getting DTLS context\n");
@ -296,6 +441,76 @@ int sock_dtls_create(sock_dtls_t *sock, sock_udp_t *udp_sock,
return 0;
}
int sock_dtls_set_server_psk_id_hint(sock_dtls_t *sock, const char *hint)
{
assert(sock);
if (strlen(hint) > CONFIG_DTLS_PSK_ID_HINT_MAX_SIZE) {
DEBUG("sock_dtls: could not set hint due to buffer size\n");
return -1;
}
strcpy(sock->psk_hint, hint);
return 0;
}
int sock_dtls_add_credential(sock_dtls_t *sock, credman_tag_t tag)
{
assert(sock);
if (sock->tags_len < CONFIG_DTLS_CREDENTIALS_MAX) {
DEBUG("sock_dtls: credential added in position %d\n", sock->tags_len);
sock->tags[sock->tags_len] = tag;
sock->tags_len++;
return 0;
}
DEBUG("sock_dtls: could not add new credential\n");
return -1;
}
int sock_dtls_remove_credential(sock_dtls_t *sock, credman_tag_t tag)
{
assert(sock);
int pos = -1;
for (unsigned i = 0; i < sock->tags_len; i++) {
if (sock->tags[i] == tag) {
pos = i;
DEBUG("sock_dtls: found credential to remove in position %i\n", pos);
break;
}
}
if (pos >= 0) {
sock->tags_len--;
for (; (unsigned)pos < sock->tags_len; pos++) {
sock->tags[pos] = sock->tags[pos + 1];
}
return 0;
}
else {
DEBUG("sock_dtls: could not find credential to remove\n");
return -1;
}
}
size_t sock_dtls_get_credentials(sock_dtls_t *sock, const credman_tag_t **out)
{
assert(sock);
assert(out);
*out = sock->tags;
return sock->tags_len;
}
void sock_dtls_set_client_psk_cb(sock_dtls_t *sock, sock_dtls_client_psk_cb_t cb)
{
assert(sock);
sock->client_psk_cb = cb;
}
void sock_dtls_set_rpk_cb(sock_dtls_t *sock, sock_dtls_rpk_cb_t cb)
{
assert(sock);
sock->rpk_cb = cb;
}
sock_udp_t *sock_dtls_get_udp_sock(sock_dtls_t *sock)
{
assert(sock);

View File

@ -22,6 +22,7 @@
#include "dtls.h"
#include "net/sock/udp.h"
#include "net/credman.h"
#include "net/sock/dtls/creds.h"
#ifdef SOCK_HAS_ASYNC
#include "net/sock/async/types.h"
#endif
@ -74,9 +75,12 @@ struct sock_dtls {
size_t datalen; /**< data length */
session_t *session; /**< Session information */
} buffer;
credman_tag_t tag; /**< Credential tag of a registered
(D)TLS credential */
char psk_hint[CONFIG_DTLS_PSK_ID_HINT_MAX_SIZE]; /**< PSK Identity hint */
credman_tag_t tags[CONFIG_DTLS_CREDENTIALS_MAX]; /**< Tags of the available credentials */
unsigned tags_len; /**< Number of tags in the list 'tags' */
dtls_peer_type role; /**< DTLS role of the socket */
sock_dtls_client_psk_cb_t client_psk_cb;/**< Callback to determine PSK credential for session */
sock_dtls_rpk_cb_t rpk_cb; /**< Callback to determine RPK credential for session */
};
/**

View File

@ -30,15 +30,26 @@
* - Server operation
* 1. Create UDP sock @ref sock_udp_create()
* 2. Create DTLS sock @ref sock_dtls_create() using role
* @ref SOCK_DTLS_SERVER
* 3. Start listening with @ref sock_dtls_recv()
* @ref SOCK_DTLS_SERVER.
* 3. Optionally:
* - when using PSK ciphersuites, set a hint @ref sock_dtls_set_server_psk_id_hint()
* - add extra credentials @ref sock_dtls_add_credential()
* - when using ECC ciphersuites, set a callback for credential selection
* @ref sock_dtls_set_rpk_cb()
* 4. Start listening with @ref sock_dtls_recv()
* - Client operation
* 1. Create UDP sock @ref sock_udp_create()
* 2. Create DTLS sock @ref sock_dtls_create() using role
* @ref SOCK_DTLS_CLIENT
* 3. Start handshake session to server @ref sock_dtls_session_init()
* 4. Handle incoming handshake packets with @ref sock_dtls_recv()
* 5. Send packet to server @ref sock_dtls_send()
* 3. Optionally:
* - add extra credentials @ref sock_dtls_add_credential()
* - when using PSK ciphersuites, set a callback for hint reception and credential
* selection @ref sock_dtls_set_client_psk_cb()
* - when using ECC ciphersuites, set a callback for credential selection
* @ref sock_dtls_set_rpk_cb()
* 4. Start handshake session to server @ref sock_dtls_session_init()
* 5. Handle incoming handshake packets with @ref sock_dtls_recv()
* 6. Send packet to server @ref sock_dtls_send()
*
* ## Makefile Includes
*
@ -156,10 +167,10 @@
* [type](@ref credman_credential_t::type).
*
* Next, we must assign a [tag](@ref credman_tag_t) for the credential. Tags
* are unsigned integer value that is used to identify which DTLS sock has
* access to which credential. Each DTLS sock will also be assigned a tag.
* are unsigned integer values used to identify which DTLS sock has
* access to which credentials. Each DTLS sock will also be assigned a list of tags.
* As a result, a sock can only use credentials that have the same tag as
* its assigned tag.
* the ones in the list.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
* if (credman_add(&psk_credential) < 0) {
@ -468,6 +479,34 @@
* return 0;
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ### Multi-credential handling
*
* Each sock needs at least one credential tag to operate. `sock_dtls_create` allows to optionally
* assign an initial credential. Extra credentials can be added and removed using
* @ref sock_dtls_add_credential and @ref sock_dtls_remove_credential respectively (found in
* `net/sock/dtls/creds.h`).
*
* #### Pre-shared Keys Cipher Suites
* In the case of PSK, a server can optionally indicate a hint to help the client to decide which
* PSK Identity to use, using @ref sock_dtls_set_server_psk_id_hint
* (see https://tools.ietf.org/html/rfc4279#section-5.2). The client application can
* decide which credential to use based on the sent hint and/or the session information, by
* registering a callback with @ref sock_dtls_set_client_psk_cb. If no callback is registered, or
* fails to chose a tag (i.e. it returns @ref CREDMAN_TAG_EMPTY), the credential is chosen as
* follows: if a hint is sent by the server, all credentials registered to the sock are checked for
* a matching @ref psk_params_t::hint "hint". A credential is selected on matching hint. If no
* credential matches the hint or no hint is provided, the first PSK credential registered in the
* sock is used.
*
* #### Elliptic Curve Cryptography Cipher Suites
* When using ECC both client and server applications can register a callback to decide which of
* the registered credentials should be used, based on the session information. This is done using
* @ref sock_dtls_set_rpk_cb.
*
* In both cases, if no callbacks are registered, the sock implementation will try to find a
* registered credential in the Sock's credential list, that matches the needed type. The first
* one that matches is used.
*
* @{
*
* @file
@ -613,7 +652,9 @@ void sock_dtls_init(void);
* @param[in] udp_sock Existing UDP sock initialized with
* @ref sock_udp_create() to be used underneath.
* @param[in] tag Credential tag of @p sock. The sock will only use
* credentials with the same tag given here.
* credentials with the tags registered to it
* (see @ref sock_dtls_add_credential). Set to @ref CREDMAN_TAG_EMPTY to
* create a sock with an empty tag list.
* @param[in] version [DTLS version](@ref sock_dtls_prot_version) to use.
* @param[in] role [Role](@ref sock_dtls_role) of the endpoint.
*

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2021 HAW Hamburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup net_sock_dtls_creds DTLS sock credentials API
* @ingroup net_sock_dtls
* @brief Credential handling for DTLS sock
* @{
*
* @file
* @brief DTLS sock definitions
*
* @author Leandro Lanzieri <leandro.lanzieri@haw-hamburg.de>
*/
#ifndef NET_SOCK_DTLS_CREDS_H
#define NET_SOCK_DTLS_CREDS_H
#include "net/sock/udp.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @ingroup net_sock_dtls_conf
* @{
*/
/**
* @brief Default maximum size for the PSK Identity hint string
*/
#ifndef CONFIG_DTLS_PSK_ID_HINT_MAX_SIZE
#define CONFIG_DTLS_PSK_ID_HINT_MAX_SIZE 32
#endif
/**
* @brief Default buffer size for TLS credential tags
*/
#ifndef CONFIG_DTLS_CREDENTIALS_MAX
#define CONFIG_DTLS_CREDENTIALS_MAX 4
#endif
/** @} */
/**
* @brief Pre-Shared Key client callback. Called during handshake to determine session credential.
*
* @param[in] sock DTLS sock object
* @param[in] ep Remove UDP endpoint representing the session
* @param[in] tags List of credential tags available for @p sock
* @param[in] tags_len Number of credentials in @p tags
* @param[in] hint Hint sent by the server. May be NULL
* @param[in] hint_len Length of @p hint
*
* @return Tag of the credential to use when a suitable one is found
* @retval CREDMAN_TAG_EMPTY otherwise
*/
typedef credman_tag_t (*sock_dtls_client_psk_cb_t)(sock_dtls_t *sock, sock_udp_ep_t *ep,
credman_tag_t tags[], unsigned tags_len,
const char* hint, size_t hint_len);
/**
* @brief Raw Public Key callback. Called during handshake to determine the session credential.
*
* @param[in] sock DTLS sock object
* @param[in] ep Remote UDP endpoint representing the session
* @param[in] tags List of credential tags available for @p sock
* @param[in] tags_len Number of credentials in @p tags
*
* @return Tag of the credential to use when a suitable one is found
* @retval CREDMAN_TAG_EMPTY otherwise
*/
typedef credman_tag_t (*sock_dtls_rpk_cb_t)(sock_dtls_t *sock, sock_udp_ep_t *ep,
credman_tag_t tags[], unsigned tags_len);
/**
* @brief Sets the PSK Identity hint to be sent to clients during handshake.
*
* This hint is optional. It helps clients to decide which PSK Identity to use.
*
* @param[in, out] sock The DTLS sock object to set the hint to.
* @param[in] hint PSK Identity hint as NULL-terminated string.
*
* @retval 0 on success
* @retval -1 on error
*/
int sock_dtls_set_server_psk_id_hint(sock_dtls_t *sock, const char *hint);
/**
* @brief Adds a credential tag to list of available credentials for @p sock.
*
* @pre sock != NULL
*
* @param[in] sock DTLS sock object
* @param[in] tag Tag of the credential to add
*
* @retval 0 on success
* @retval -1 otherwise
*/
int sock_dtls_add_credential(sock_dtls_t *sock, credman_tag_t tag);
/**
* @brief Removes a credential tag of the list of available credentials for @p sock.
*
* @pre sock != NULL
*
* @param[in] sock DTLS sock object
* @param[in] tag Tag of the credential to remove
*
* @retval 0 on success
* @retval -1 otherwise
*/
int sock_dtls_remove_credential(sock_dtls_t *sock, credman_tag_t tag);
/**
* @brief Returns an array of tags of the registered credentials in @p sock.
*
* @param[in] sock DTLS sock object
* @param[out] out Pointer to place the reference to a read-only array of @ref credman_tag_t
*
* @return Number of registered credentials
*/
size_t sock_dtls_get_credentials(sock_dtls_t *sock, const credman_tag_t **out);
/**
* @brief Sets the callback function for clients to specify a credential to use
* for a given connection.
*
* @param[in] sock The DTLS sock object to set the callback to.
* @param[in] cb Callback to set.
*/
void sock_dtls_set_client_psk_cb(sock_dtls_t *sock, sock_dtls_client_psk_cb_t cb);
/**
* @brief Sets the callback function to specify a credential to use for a given connection,
* when using Raw Public Keys.
*
* @param[in] sock The DTLS sock object to set the callback to.
* @param[in] cb Callback to set.
*/
void sock_dtls_set_rpk_cb(sock_dtls_t *sock, sock_dtls_rpk_cb_t cb);
#ifdef __cplusplus
}
#endif
#endif /* NET_SOCK_DTLS_CREDS_H */
/** @} */

View File

@ -44,4 +44,12 @@ config DTLS_HANDSHAKE_BUFSIZE_EXP
represents the exponent of 2^n, which will be used as the size of the
buffer. The buffer is used to hold credentials during DTLS handshakes.
config DTLS_CREDENTIALS_MAX
int "Buffer size for TLS credential tags"
default 4
config DTLS_PSK_ID_HINT_MAX_SIZE
int "Maximum size for a PSK Identity hint string"
default 32
endif # KCONFIG_USEMODULE_SOCK_DTLS

View File

@ -59,6 +59,7 @@ static const credman_credential_t credential = {
};
#else /* #ifdef CONFIG_DTLS_PSK */
static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY;
static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY;
static const credman_credential_t credential = {
.type = CREDMAN_TYPE_PSK,
@ -66,6 +67,7 @@ static const credman_credential_t credential = {
.params = {
.psk = {
.key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, },
.id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, },
},
},
};