diff --git a/crypto.h b/crypto.h index a759cf66..53547589 100644 --- a/crypto.h +++ b/crypto.h @@ -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; @@ -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 { @@ -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; diff --git a/dtls.c b/dtls.c index ef98ecbc..f95222ed 100644 --- a/dtls.c +++ b/dtls.c @@ -553,26 +553,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[] = { @@ -1065,14 +1067,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 */ @@ -1139,7 +1142,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)) @@ -1148,7 +1151,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; @@ -1174,6 +1177,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: @@ -1246,11 +1261,9 @@ 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; @@ -1258,7 +1271,7 @@ dtls_update_parameters(dtls_context_t *ctx, 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); @@ -2932,11 +2945,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); @@ -2973,9 +2984,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); @@ -3186,7 +3197,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]); diff --git a/dtls.h b/dtls.h index c43f816b..4a6528b5 100644 --- a/dtls.h +++ b/dtls.h @@ -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 /** diff --git a/tests/dtls-client.c b/tests/dtls-client.c index 68253212..16b74788 100644 --- a/tests/dtls-client.c +++ b/tests/dtls-client.c @@ -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; } @@ -216,13 +216,26 @@ 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; + } + } } static int @@ -319,9 +332,9 @@ usage( const char *program, const char *version) { fprintf(stderr, "%s v%s -- DTLS client implementation\n" "(c) 2011-2014 Olaf Bergmann \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" @@ -329,7 +342,9 @@ usage( const char *program, const char *version) { #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"); } @@ -337,7 +352,7 @@ usage( const char *program, const char *version) { 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, @@ -357,6 +372,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; @@ -383,7 +400,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' : @@ -425,6 +442,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); @@ -524,7 +547,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) { @@ -533,6 +556,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"); @@ -553,6 +580,7 @@ main(int argc, char **argv) { len = 0; } else { try_send(dtls_context, &dst, len, buf); + len = 0; } } } diff --git a/tests/dtls-server.c b/tests/dtls-server.c index 15246308..4a2f1f3c 100644 --- a/tests/dtls-server.c +++ b/tests/dtls-server.c @@ -137,6 +137,7 @@ verify_ecdsa_key(struct dtls_context_t *ctx, #endif /* DTLS_ECC */ #define DTLS_SERVER_CMD_CLOSE "server:close" +#define DTLS_SERVER_CMD_EXIT "server:exit" static int read_from_peer(struct dtls_context_t *ctx, @@ -149,6 +150,11 @@ read_from_peer(struct dtls_context_t *ctx, dtls_close(ctx, session); return len; } + if (len >= strlen(DTLS_SERVER_CMD_EXIT) && + !memcmp(data, DTLS_SERVER_CMD_EXIT, strlen(DTLS_SERVER_CMD_EXIT))) { + printf("server: exit\n"); + exit(-2); + } return dtls_write(ctx, session, data, len); } @@ -163,13 +169,26 @@ 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; + } + } } static int @@ -261,10 +280,12 @@ usage(const char *program, const char *version) { fprintf(stderr, "%s v%s -- DTLS server implementation\n" "(c) 2011-2014 Olaf Bergmann \n\n" - "usage: %s [-A address] [-p port] [-v num] [-c cipher-suites]\n" + "usage: %s [-A address] [-p port] [-v num] [-c cipher-suites] [-r] [-e]\n" "\t-A address\t\tlisten on specified address (default is ::)\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"); } @@ -272,7 +293,7 @@ usage(const char *program, const char *version) { 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, @@ -307,7 +328,7 @@ main(int argc, char **argv) { listen_addr.sin6_family = AF_INET6; listen_addr.sin6_addr = in6addr_any; - while ((opt = getopt(argc, argv, "A:p:v:c:")) != -1) { + while ((opt = getopt(argc, argv, "reA:p:v:c:")) != -1) { switch (opt) { case 'A' : if (resolve_address(optarg, (struct sockaddr *)&listen_addr) < 0) { @@ -324,6 +345,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);