Skip to content

Commit

Permalink
dtls.c: add dtls_user_parameters_t.
Browse files Browse the repository at this point in the history
Callback for user parameters.
Includes cipher suites and flags to enforce security features.
Cleanup test dtls-client.

Signed-off-by: Achim Kraus <[email protected]>
  • Loading branch information
boaks committed Jan 21, 2023
1 parent 84f834a commit 56b49ec
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 59 deletions.
17 changes: 16 additions & 1 deletion crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ typedef uint8_t dtls_cipher_index_t;
/** Index in cipher parameter table for NULL cipher */
#define DTLS_CIPHER_INDEX_NULL 0

/** Maximum number of cipher suites */
#define DTLS_MAX_CIPHER_SUITES 4

typedef enum { AES128=0
} dtls_crypto_alg;
Expand Down Expand Up @@ -128,6 +130,19 @@ typedef struct {

struct netq_t;

/**
* Set of user parameters used by the handshake.
*/
typedef struct dtls_user_parameters_t {
/**
* The list of cipher suites.
* The list must be terminated by TLS_NULL_WITH_NULL_NULL.
*/
dtls_cipher_t cipher_suites[DTLS_MAX_CIPHER_SUITES + 1];
unsigned int force_extended_master_secret:1; /** force extended master secret extension (RFC7627) */
unsigned int force_renegotiation_info:1; /** force renegotiation info extension (RFC5746) */
} dtls_user_parameters_t;

typedef struct {
union {
struct random_t {
Expand All @@ -141,7 +156,7 @@ typedef struct {
dtls_hs_state_t hs_state; /**< handshake protocol status */

dtls_compression_t compression; /**< compression method */
const dtls_cipher_t* cipher_suites; /**< list of cipher suites, TLS_NULL_WITH_NULL_NULL terminated */
dtls_user_parameters_t user_parameters; /**< user parameters */
dtls_cipher_index_t cipher_index; /**< internal index for cipher_suite_params, DTLS_CIPHER_INDEX_NULL for TLS_NULL_WITH_NULL_NULL */
unsigned int do_client_auth:1;
unsigned int extended_master_secret:1;
Expand Down
75 changes: 43 additions & 32 deletions dtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ memarray_t dtlscontext_storage;
#define DTLS_HS_LENGTH sizeof(dtls_handshake_header_t)
#define DTLS_CH_LENGTH sizeof(dtls_client_hello_t) /* no variable length fields! */
#define DTLS_COOKIE_LENGTH_MAX 32
#define DTLS_CH_LENGTH_MAX DTLS_CH_LENGTH + DTLS_COOKIE_LENGTH_MAX + 18 + 26 + 12
#define DTLS_CH_LENGTH_MAX DTLS_CH_LENGTH + DTLS_COOKIE_LENGTH_MAX + 10 + (2 * DTLS_MAX_CIPHER_SUITES) + 26 + 12
#define DTLS_HV_LENGTH sizeof(dtls_hello_verify_t)
#define DTLS_SH_LENGTH (2 + DTLS_RANDOM_LENGTH + 1 + 2 + 1)
#define DTLS_SKEXEC_LENGTH (1 + 2 + 1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE + 1 + 1 + 2 + 70)
Expand Down Expand Up @@ -588,26 +588,28 @@ dtls_set_handshake_header(uint8 type,
return buf;
}

static const dtls_cipher_t default_cipher_suites[] =
static const dtls_user_parameters_t default_user_parameters = {
.cipher_suites =
#ifdef DTLS_DEFAULT_CIPHER_SUITES
DTLS_DEFAULT_CIPHER_SUITES ;
#else
{
DTLS_DEFAULT_CIPHER_SUITES ,
#else /* DTLS_DEFAULT_CIPHER_SUITES */
{
#ifdef DTLS_ECC
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
#endif /* DTLS_ECC */
#ifdef DTLS_PSK
TLS_PSK_WITH_AES_128_CCM_8,
TLS_PSK_WITH_AES_128_CCM,
TLS_PSK_WITH_AES_128_CCM_8,
TLS_PSK_WITH_AES_128_CCM,
#endif /* DTLS_PSK */
/* RFC 5746, pseudo cipher suite to indicate secure renegotiation */
TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
/* TLS_NULL_WITH_NULL_NULL must always be the last entry as it
* indicates the stop marker for the traversal of this table. */
TLS_NULL_WITH_NULL_NULL
TLS_NULL_WITH_NULL_NULL
},
#endif /* DTLS_DEFAULT_CIPHER_SUITES */
.force_extended_master_secret = 1,
.force_renegotiation_info = 1,
};
#endif

/** only one compression method is currently defined */
static uint8 compression_methods[] = {
Expand Down Expand Up @@ -1100,14 +1102,15 @@ dtls_check_tls_extension(dtls_peer_t *peer,
int ext_client_cert_type = 0;
int ext_server_cert_type = 0;
int ext_ec_point_formats = 0;
const int ecdsa = is_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index);
dtls_handshake_parameters_t *config = peer->handshake_params;
const int ecdsa = is_key_exchange_ecdhe_ecdsa(config->cipher_index);

if (data_length < sizeof(uint16)) {
/* no tls extensions specified */
if (ecdsa) {
goto error;
}
return 0;
goto check_forced_extensions;
}

/* get the length of the tls extension list */
Expand Down Expand Up @@ -1174,7 +1177,7 @@ dtls_check_tls_extension(dtls_peer_t *peer,
dtls_info("skipped encrypt-then-mac extension\n");
break;
case TLS_EXT_EXTENDED_MASTER_SECRET:
peer->handshake_params->extended_master_secret = 1;
config->extended_master_secret = 1;
break;
case TLS_EXT_SIG_HASH_ALGO:
if (verify_ext_sig_hash_algo(data, j))
Expand All @@ -1183,7 +1186,7 @@ dtls_check_tls_extension(dtls_peer_t *peer,
case TLS_EXT_RENEGOTIATION_INFO:
/* RFC 5746, minimal version, only empty info is supported */
if (j == 1 && *data == 0) {
peer->handshake_params->renegotiation_info = 1;
config->renegotiation_info = 1;
} else {
dtls_warn("only empty renegotiation info is supported.\n");
goto error;
Expand All @@ -1209,6 +1212,18 @@ dtls_check_tls_extension(dtls_peer_t *peer,
}
}
}

check_forced_extensions:
if (config->user_parameters.force_extended_master_secret) {
if (!config->extended_master_secret) {
goto error;
}
}
if (config->user_parameters.force_renegotiation_info) {
if (!config->renegotiation_info) {
goto error;
}
}
return 0;

error:
Expand Down Expand Up @@ -1281,19 +1296,17 @@ dtls_update_parameters(dtls_context_t *ctx,
data += sizeof(uint16);
data_length -= sizeof(uint16) + i;

if (ctx->h->get_cipher_suites != NULL) {
ctx->h->get_cipher_suites(ctx, &peer->session, &config->cipher_suites);
}
if (!config->cipher_suites) {
config->cipher_suites = default_cipher_suites;
config->user_parameters = default_user_parameters;
if (ctx->h->get_user_parameters != NULL) {
ctx->h->get_user_parameters(ctx, &peer->session, &config->user_parameters);
}

ok = 0;
while ((i >= (int)sizeof(uint16)) && (!ok || !config->renegotiation_info)) {
if (dtls_uint16_to_int(data) == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) {
config->renegotiation_info = 1;
} else if (!ok) {
config->cipher_index = get_cipher_index(config->cipher_suites, dtls_uint16_to_int(data));
config->cipher_index = get_cipher_index(config->user_parameters.cipher_suites, dtls_uint16_to_int(data));
ok = known_cipher(ctx, config->cipher_index, 0);
}
i -= sizeof(uint16);
Expand Down Expand Up @@ -2977,11 +2990,9 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer,
#endif
dtls_handshake_parameters_t *handshake = peer->handshake_params;

if (ctx->h->get_cipher_suites != NULL) {
ctx->h->get_cipher_suites(ctx, &peer->session, &(handshake->cipher_suites));
}
if (!handshake->cipher_suites) {
handshake->cipher_suites = default_cipher_suites;
handshake->user_parameters = default_user_parameters;
if (ctx->h->get_user_parameters != NULL) {
ctx->h->get_user_parameters(ctx, &peer->session, &(handshake->user_parameters));
}

dtls_int_to_uint16(p, DTLS_VERSION);
Expand Down Expand Up @@ -3018,9 +3029,9 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer,
p += sizeof(uint16);

/* add known cipher(s) */
for (index = 0; handshake->cipher_suites[index] != TLS_NULL_WITH_NULL_NULL; ++index) {
dtls_cipher_t code = handshake->cipher_suites[index];
dtls_cipher_index_t cipher_index = get_cipher_index(handshake->cipher_suites, code);
for (index = 0; handshake->user_parameters.cipher_suites[index] != TLS_NULL_WITH_NULL_NULL; ++index) {
dtls_cipher_t code = handshake->user_parameters.cipher_suites[index];
dtls_cipher_index_t cipher_index = get_cipher_index(handshake->user_parameters.cipher_suites, code);
if (known_cipher(ctx, cipher_index, 1)) {
dtls_int_to_uint16(p, code);
p += sizeof(uint16);
Expand Down Expand Up @@ -3231,7 +3242,7 @@ check_server_hello(dtls_context_t *ctx,

/* Check if the cipher suite selected by the server
* is in our list of cipher suites. */
handshake->cipher_index = get_cipher_index(handshake->cipher_suites, dtls_uint16_to_int(data));
handshake->cipher_index = get_cipher_index(handshake->user_parameters.cipher_suites, dtls_uint16_to_int(data));

if (!known_cipher(ctx, handshake->cipher_index, 1)) {
dtls_alert("unsupported cipher 0x%02x 0x%02x\n", data[0], data[1]);
Expand Down
8 changes: 3 additions & 5 deletions dtls.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,10 @@ typedef struct {
*
* @param ctx The current dtls context.
* @param session The session where the cipher suites will be used.
* @param cipher_suites The pointer to return the list of cipher suites.
* The list must be terminated by TLS_NULL_WITH_NULL_NULL
* If NULL is assigned, the default cipher suites
* will be used.
* @param parameters The pointer to user parameters.
* The user parameters are initialized with the default values.
*/
void (*get_cipher_suites)(struct dtls_context_t *ctx, session_t *session, const dtls_cipher_t **cipher_suites);
void (*get_user_parameters)(struct dtls_context_t *ctx, session_t *session, dtls_user_parameters_t *parameters);

#ifdef DTLS_PSK
/**
Expand Down
59 changes: 45 additions & 14 deletions tests/dtls-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,20 @@ try_send(struct dtls_context_t *ctx, session_t *dst, size_t len, char *buf) {
}

static void
handle_stdin(size_t *len, char *buf) {
if (fgets(buf + *len, sizeof(buf) - *len, stdin))
handle_stdin(size_t *len, char *buf, size_t max_len) {
if (fgets(buf + *len, max_len - *len, stdin))
*len += strlen(buf + *len);
}

static int
read_from_peer(struct dtls_context_t *ctx,
session_t *session, uint8 *data, size_t len) {
size_t i;
(void)ctx;
(void)session;

for (i = 0; i < len; i++)
printf("%c", data[i]);
if (write(STDOUT_FILENO, data, len) == -1)
dtls_debug("write failed: %s\n", strerror(errno));

return 0;
}

Expand All @@ -216,13 +216,29 @@ send_to_peer(struct dtls_context_t *ctx,
}

static const dtls_cipher_t* ciphers = NULL;
static unsigned int force_extended_master_secret = 0;
static unsigned int force_renegotiation_info = 0;

static void
get_cipher_suites(struct dtls_context_t *ctx,
session_t *session, const dtls_cipher_t **cipher_suites) {
get_user_parameters(struct dtls_context_t *ctx,
session_t *session, dtls_user_parameters_t *user_parameters) {
(void) ctx;
(void) session;
*cipher_suites = ciphers;
user_parameters->force_extended_master_secret = force_extended_master_secret;
user_parameters->force_renegotiation_info = force_renegotiation_info;
if (ciphers) {
int index = 0;
while (index < DTLS_MAX_CIPHER_SUITES) {
user_parameters->cipher_suites[index] = ciphers[index];
if (ciphers[index] == TLS_NULL_WITH_NULL_NULL) {
break;
}
++index;
}
if (index == DTLS_MAX_CIPHER_SUITES) {
user_parameters->cipher_suites[index] = TLS_NULL_WITH_NULL_NULL;
}
}
}

static int
Expand Down Expand Up @@ -319,25 +335,27 @@ usage( const char *program, const char *version) {
fprintf(stderr, "%s v%s -- DTLS client implementation\n"
"(c) 2011-2014 Olaf Bergmann <[email protected]>\n\n"
#ifdef DTLS_PSK
"usage: %s [-i file] [-k file] [-o file] [-p port] [-v num] [-c cipher-suites] addr [port]\n"
"usage: %s [-i file] [-k file] [-o file] [-p port] [-v num] [-c cipher-suites] [-r] [-e] addr [port]\n"
#else /* DTLS_PSK */
"usage: %s [-o file] [-p port] [-v num] [-c cipher-suites] addr [port]\n"
"usage: %s [-o file] [-p port] [-v num] [-c cipher-suites] [-r] [-e] addr [port]\n"
#endif /* DTLS_PSK */
#ifdef DTLS_PSK
"\t-i file\t\tread PSK identity from file\n"
"\t-k file\t\tread pre-shared key from file\n"
#endif /* DTLS_PSK */
"\t-o file\t\toutput received data to this file (use '-' for STDOUT)\n"
"\t-p port\t\tlisten on specified port (default is %d)\n"
"\t-v num\t\tverbosity level (default: 3)\n",
"\t-v num\t\tverbosity level (default: 3)\n"
"\t-r\t\tforce rehandshake info (RFC5746)\n"
"\t-e\t\tforce extended master secret (RFC7627)\n",
program, version, program, DEFAULT_PORT);
cipher_suites_usage(stderr, "\t");
}

static dtls_handler_t cb = {
.write = send_to_peer,
.read = read_from_peer,
.get_cipher_suites = get_cipher_suites,
.get_user_parameters = get_user_parameters,
.event = NULL,
#ifdef DTLS_PSK
.get_psk_info = get_psk_info,
Expand All @@ -357,6 +375,8 @@ static dtls_handler_t cb = {
*/
#define DTLS_CLIENT_CMD_REHANDSHAKE "client:rehandshake"

#define DTLS_CLIENT_CMD_EXIT "client:exit"

int
main(int argc, char **argv) {
fd_set rfds, wfds;
Expand All @@ -383,7 +403,7 @@ main(int argc, char **argv) {
memcpy(psk_key, PSK_DEFAULT_KEY, psk_key_length);
#endif /* DTLS_PSK */

while ((opt = getopt(argc, argv, "p:o:v:c:" PSK_OPTIONS)) != -1) {
while ((opt = getopt(argc, argv, "rep:o:v:c:" PSK_OPTIONS)) != -1) {
switch (opt) {
#ifdef DTLS_PSK
case 'i' :
Expand Down Expand Up @@ -425,6 +445,12 @@ main(int argc, char **argv) {
case 'c' :
ciphers = init_cipher_suites(optarg);
break;
case 'r' :
force_renegotiation_info = 1;
break;
case 'e' :
force_extended_master_secret = 1;
break;
default:
usage(argv[0], dtls_package_version());
exit(1);
Expand Down Expand Up @@ -524,7 +550,7 @@ main(int argc, char **argv) {
else if (FD_ISSET(fd, &rfds))
dtls_handle_read(dtls_context);
else if (FD_ISSET(fileno(stdin), &rfds))
handle_stdin(&len, buf);
handle_stdin(&len, buf, sizeof(buf));
}

if (len) {
Expand All @@ -533,6 +559,10 @@ main(int argc, char **argv) {
printf("client: closing connection\n");
dtls_close(dtls_context, &dst);
len = 0;
} else if (len >= strlen(DTLS_CLIENT_CMD_EXIT) &&
!memcmp(buf, DTLS_CLIENT_CMD_EXIT, strlen(DTLS_CLIENT_CMD_EXIT))) {
printf("client: exit\n");
break;
} else if (len >= strlen(DTLS_CLIENT_CMD_REHANDSHAKE) &&
!memcmp(buf, DTLS_CLIENT_CMD_REHANDSHAKE, strlen(DTLS_CLIENT_CMD_REHANDSHAKE))) {
printf("client: rehandshake connection\n");
Expand All @@ -553,6 +583,7 @@ main(int argc, char **argv) {
len = 0;
} else {
try_send(dtls_context, &dst, len, buf);
len = 0;
}
}
}
Expand Down
Loading

0 comments on commit 56b49ec

Please sign in to comment.