From fb19c3a8c6ffada77dc56ac17f95a8d8864df8a6 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Wed, 8 Dec 2021 16:28:42 +0100 Subject: [PATCH 1/3] dtls.c: add callback for peer specific selection of parameters as cipher suites. Adds dtls_user_parameters_t for these user parameters. Signed-off-by: Achim Kraus --- crypto.h | 15 ++++ dtls.c | 250 +++++++++++++++++++++++++++++++++++++++++-------------- dtls.h | 12 +++ 3 files changed, 215 insertions(+), 62 deletions(-) diff --git a/crypto.h b/crypto.h index 079ccea0..68096e10 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,18 @@ 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) */ +} dtls_user_parameters_t; + typedef struct { union { struct random_t { @@ -141,6 +155,7 @@ typedef struct { dtls_hs_state_t hs_state; /**< handshake protocol status */ dtls_compression_t compression; /**< compression method */ + 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 40042a42..c0871fc8 100644 --- a/dtls.c +++ b/dtls.c @@ -121,10 +121,40 @@ memarray_t dtlscontext_storage; #define DTLS_RH_LENGTH sizeof(dtls_record_header_t) #define DTLS_HS_LENGTH sizeof(dtls_handshake_header_t) +/* + * ClientHello: + * + * session_length := 1 byte + * session := 0 bytes + * cookie_length := 1 byte + * cookie := n bytes + * cipher_length := 2 bytes + * cipher suites (max) := 2 bytes + max * 2 + * compression_length := 1 byte + * compression := 1 byte + * extensions_length := 2 bytes => 10 + max * 2 + * + * client_cert_type := 6 bytes + * server_cert_type := 6 bytes + * ec curves := 8 bytes + * ec point format := 6 bytes => 26 + * sign. and hash algos := 8 bytes + * extended master secret := 4 bytes => 12 + */ #define DTLS_CH_LENGTH sizeof(dtls_client_hello_t) /* no variable length fields! */ #define DTLS_COOKIE_LENGTH_MAX 32 -#define DTLS_CH_LENGTH_MAX sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 16 + 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) +/* + * ServerHello: + * + * version := 2 bytes + * random := 32 bytes + * session_length := 1 byte + * session := 0 bytes + * cipher suite := 2 bytes + * compression := 1 byte + */ #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) #define DTLS_SKEXECPSK_LENGTH_MIN 2 @@ -553,6 +583,28 @@ dtls_set_handshake_header(uint8 type, return buf; } +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 */ + { +#ifdef DTLS_ECC + 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, +#endif /* DTLS_PSK */ + /* 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 + }, +#endif /* DTLS_DEFAULT_CIPHER_SUITES */ + .force_extended_master_secret = 1, +}; + /** only one compression method is currently defined */ static uint8 compression_methods[] = { TLS_COMPRESSION_NULL @@ -587,11 +639,38 @@ static const struct cipher_suite_param_t cipher_suite_params[] = { static const dtls_cipher_index_t last_cipher_suite_param = sizeof(cipher_suite_params) / sizeof(cipher_suite_param_t); +/** + * Check if cipher suite is contained in table. + * + * \param cipher_suites table with cipher-suites. Terminated with + * TLS_NULL_WITH_NULL_NULL. + * \param cipher_suite cipher suite + * \return 0, if not contained, != 0, if contained + */ +static inline uint8_t +contains_cipher_suite(const dtls_cipher_t* cipher_suites, const dtls_cipher_t cipher_suite) { + while ((*cipher_suites != cipher_suite) && + (*cipher_suites != TLS_NULL_WITH_NULL_NULL)) { + cipher_suites++; + } + return *cipher_suites == cipher_suite; +} + +/** + * Get index to cipher suite params. + * + * \param cipher_suites table with user-selected cipher-suites. Terminated with + * TLS_NULL_WITH_NULL_NULL. + * \param cipher cipher suite + * \return index to cipher suite params, DTLS_CIPHER_INDEX_NULL if not found. + */ static inline dtls_cipher_index_t -get_cipher_index(dtls_cipher_t cipher) { - for (int index = 0; index < last_cipher_suite_param ; ++index) { - if (cipher_suite_params[index].cipher_suite == cipher) { - return index; +get_cipher_index(const dtls_cipher_t* cipher_suites, dtls_cipher_t cipher) { + if (contains_cipher_suite(cipher_suites, cipher)) { + for (int index = 0; index < last_cipher_suite_param ; ++index) { + if (cipher_suite_params[index].cipher_suite == cipher) { + return index; + } } } return DTLS_CIPHER_INDEX_NULL; @@ -696,7 +775,7 @@ is_ecdsa_client_auth_supported(dtls_context_t *ctx) { * @param ctx The current DTLS context * @param cipher_index The index to cipher suite params to check * @param is_client 1 for a dtls client, 0 for server - * @return @c 1 iff @p code is recognized, + * @return @c 1 if @p code is recognized, */ static int known_cipher(dtls_context_t *ctx, dtls_cipher_index_t cipher_index, int is_client) { @@ -1033,21 +1112,22 @@ static int verify_ext_sig_hash_algo(uint8 *data, size_t data_length) { */ static int dtls_check_tls_extension(dtls_peer_t *peer, - uint8 *data, size_t data_length, int client_hello) + uint8 *data, size_t data_length, int is_client_hello) { uint16_t i, j; int ext_elliptic_curve = 0; 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 */ @@ -1084,7 +1164,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, break; case TLS_EXT_CLIENT_CERTIFICATE_TYPE: ext_client_cert_type = 1; - if (client_hello) { + if (is_client_hello) { if (verify_ext_cert_type(data, j)) goto error; } else { @@ -1094,7 +1174,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, break; case TLS_EXT_SERVER_CERTIFICATE_TYPE: ext_server_cert_type = 1; - if (client_hello) { + if (is_client_hello) { if (verify_ext_cert_type(data, j)) goto error; } else { @@ -1114,7 +1194,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)) @@ -1128,7 +1208,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, data_length -= j; } if (ecdsa) { - if (client_hello) { + if (is_client_hello) { if (!ext_elliptic_curve || !ext_client_cert_type || !ext_server_cert_type || !ext_ec_point_formats) { dtls_warn("not all required tls extensions found in client hello\n"); @@ -1141,10 +1221,17 @@ 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; + } + } return 0; error: - if (client_hello && peer->state == DTLS_STATE_CONNECTED) { + if (is_client_hello && peer->state == DTLS_STATE_CONNECTED) { return dtls_alert_create(DTLS_ALERT_LEVEL_WARNING, DTLS_ALERT_NO_RENEGOTIATION); } else { return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE); @@ -1213,9 +1300,14 @@ dtls_update_parameters(dtls_context_t *ctx, data += sizeof(uint16); data_length -= sizeof(uint16) + i; + 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->cipher_index = get_cipher_index(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); data += sizeof(uint16); @@ -1227,7 +1319,7 @@ dtls_update_parameters(dtls_context_t *ctx, if (!ok) { /* reset config cipher to a well-defined value */ config->cipher_index = DTLS_CIPHER_INDEX_NULL; - dtls_warn("No matching cipher found\n"); + dtls_warn("No matching cipher-suite found\n"); goto error; } @@ -2059,7 +2151,7 @@ dtls_0_verify_peer(dtls_context_t *ctx, } else if (len != DTLS_COOKIE_LENGTH) { dtls_debug("cookie len mismatch recv. %u != %u!\n", len, DTLS_COOKIE_LENGTH); } else if (memcmp(cookie, mycookie, len)) { - dtls_debug("not matching cookie!\n"); + dtls_debug_dump("not matching cookie", cookie, len); } else { dtls_debug("found matching cookie\n"); return 0; @@ -2280,8 +2372,17 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) { /* Ensure that the largest message to create fits in our source * buffer. (The size of the destination buffer is checked by the - * encoding function, so we do not need to guess.) */ - uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 8 + 6 + 4]; + * encoding function, so we do not need to guess.) + * + * extensions length := 2 bytes + * client certificate type := 5 bytes + * server certificate type := 5 bytes + * ec_point_formats := 6 bytes + * extended master secret := 4 bytes + * + * (no elliptic_curves in ServerHello.) + */ + uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 6 + 4 + 5]; uint8 *p; uint8 extension_size; dtls_handshake_parameters_t * const handshake = peer->handshake_params; @@ -2322,7 +2423,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) } if (ecdsa) { - /* client certificate type extension */ + /* client certificate type extension, 5 bytes */ dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERTIFICATE_TYPE); p += sizeof(uint16); @@ -2333,7 +2434,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY); p += sizeof(uint8); - /* client certificate type extension */ + /* client certificate type extension, 5 bytes */ dtls_int_to_uint16(p, TLS_EXT_SERVER_CERTIFICATE_TYPE); p += sizeof(uint16); @@ -2344,7 +2445,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY); p += sizeof(uint8); - /* ec_point_formats */ + /* ec_point_formats, 6 bytes */ dtls_int_to_uint16(p, TLS_EXT_EC_POINT_FORMATS); p += sizeof(uint16); @@ -2361,7 +2462,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) } if (handshake->extended_master_secret) { - /* extended master secret */ + /* extended master secret, 4 bytes */ dtls_int_to_uint16(p, TLS_EXT_EXTENDED_MASTER_SECRET); p += sizeof(uint16); @@ -2872,23 +2973,19 @@ static int dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, uint8 cookie[], size_t cookie_length) { uint8 buf[DTLS_CH_LENGTH_MAX]; - uint8 *p = buf; - uint8_t cipher_size; - uint8_t extension_size; - int psk; - int ecdsa; + uint8_t *p = buf; + uint8_t *p_cipher_suites_size = NULL; + uint8_t index = 0; + uint8_t cipher_suites_size = 0; + uint8_t extension_size = 4; /* extended master secret extension */ +#ifdef DTLS_ECC + uint8_t ecdsa = 0; +#endif dtls_handshake_parameters_t *handshake = peer->handshake_params; - psk = is_psk_supported(ctx); - ecdsa = is_ecdsa_supported(ctx, 1); - - /* 2 bytes per cipher suite - * 2 ECDSA cipher suites => 4, 2 PSK cipher suites => 4 */ - cipher_size = 2 + ((ecdsa) ? 4 : 0) + ((psk) ? 4 : 0); - extension_size = 4 + ((ecdsa) ? 6 + 6 + 8 + 6 + 8: 0); - - if (cipher_size == 0) { - dtls_crit("no cipher callbacks implemented\n"); + 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); @@ -2919,22 +3016,47 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, p += cookie_length; } - /* add known cipher(s) */ - dtls_int_to_uint16(p, cipher_size - 2); + /* keep pointer to size of cipher suites */ + p_cipher_suites_size = p; + /* skip size of cipher suites field */ p += sizeof(uint16); - if (ecdsa) { - dtls_int_to_uint16(p, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8); - p += sizeof(uint16); - dtls_int_to_uint16(p, TLS_ECDHE_ECDSA_WITH_AES_128_CCM); - p += sizeof(uint16); + /* add known cipher(s) */ + 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); +#ifdef DTLS_ECC + ecdsa = ecdsa || is_key_exchange_ecdhe_ecdsa(cipher_index); +#endif /* DTLS_ECC */ + } + /* ignore not supported cipher-suite + credentials callback is missing */ } - if (psk) { - dtls_int_to_uint16(p, TLS_PSK_WITH_AES_128_CCM_8); - p += sizeof(uint16); - dtls_int_to_uint16(p, TLS_PSK_WITH_AES_128_CCM); - p += sizeof(uint16); + + cipher_suites_size = (p - p_cipher_suites_size) - sizeof(uint16); + if (cipher_suites_size == 0) { + dtls_crit("no supported cipher suite provided!\n"); + return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE); + } + + /* set size of known cipher suites */ + dtls_int_to_uint16(p_cipher_suites_size, cipher_suites_size); + +#ifdef DTLS_ECC + if (ecdsa) { + /* + * client_cert_type := 6 bytes + * server_cert_type := 6 bytes + * ec curves := 8 bytes + * ec point format := 6 bytes + * sign. and hash algos := 8 bytes + */ + extension_size += 6 + 6 + 8 + 6 + 8; } +#endif /* compression method */ dtls_int_to_uint8(p, 1); @@ -2947,8 +3069,9 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, dtls_int_to_uint16(p, extension_size); p += sizeof(uint16); +#ifdef DTLS_ECC if (ecdsa) { - /* client certificate type extension */ + /* client certificate type extension, 6 bytes */ dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERTIFICATE_TYPE); p += sizeof(uint16); @@ -2963,7 +3086,7 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY); p += sizeof(uint8); - /* client certificate type extension */ + /* server certificate type extension, 6 bytes */ dtls_int_to_uint16(p, TLS_EXT_SERVER_CERTIFICATE_TYPE); p += sizeof(uint16); @@ -2978,7 +3101,7 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, dtls_int_to_uint8(p, TLS_CERT_TYPE_RAW_PUBLIC_KEY); p += sizeof(uint8); - /* elliptic_curves */ + /* elliptic_curves, 8 bytes */ dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES); p += sizeof(uint16); @@ -2993,7 +3116,7 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES_SECP256R1); p += sizeof(uint16); - /* ec_point_formats */ + /* ec_point_formats, 6 bytes */ dtls_int_to_uint16(p, TLS_EXT_EC_POINT_FORMATS); p += sizeof(uint16); @@ -3008,7 +3131,7 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, dtls_int_to_uint8(p, TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED); p += sizeof(uint8); - /* signature algorithms extension */ + /* signature algorithms extension, 8 bytes */ dtls_int_to_uint16(p, TLS_EXT_SIG_HASH_ALGO); p += sizeof(uint16); @@ -3029,7 +3152,9 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, p += sizeof(uint8); } - /* extended master secret */ +#endif /* DTLS_ECC */ + + /* extended master secret, 4 bytes */ dtls_int_to_uint16(p, TLS_EXT_EXTENDED_MASTER_SECRET); p += sizeof(uint16); @@ -3103,15 +3228,16 @@ check_server_hello(dtls_context_t *ctx, return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR); } - /* Check cipher suite. As we offer all we have, it is sufficient - * to check if the cipher suite selected by the server is in our - * list of known cipher suites. Subsets are not supported. */ - handshake->cipher_index = get_cipher_index(dtls_uint16_to_int(data)); + /* Check if the cipher suite selected by the server + * is in our list of cipher suites. */ + 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]); + dtls_alert("unsupported cipher 0x%02x 0x%02x\n", data[0], data[1]); + handshake->cipher_index = DTLS_CIPHER_INDEX_NULL; return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY); } + data += sizeof(uint16); data_length -= sizeof(uint16); diff --git a/dtls.h b/dtls.h index 82cae647..05f3385c 100644 --- a/dtls.h +++ b/dtls.h @@ -119,6 +119,18 @@ typedef struct { int (*event)(struct dtls_context_t *ctx, session_t *session, dtls_alert_level_t level, unsigned short code); + /** + * Called during handshake to get the user parameter. + * + * @param ctx The current dtls context. + * @param session The session where the cipher suites will be used. + * @param parameters The pointer to user parameters. + * The user parameters are initialized with the default + * values. + */ + void (*get_user_parameters)(struct dtls_context_t *ctx, session_t *session, + dtls_user_parameters_t *parameters); + #ifdef DTLS_PSK /** * Called during handshake to get information related to the From 2f6ed3c963d8d0bbccd61e0ed518def972828844 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Wed, 1 Mar 2023 19:23:45 +0100 Subject: [PATCH 2/3] Fix some typos and return values. Signed-off-by: Achim Kraus --- dtls.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/dtls.c b/dtls.c index c0871fc8..e0d686f6 100644 --- a/dtls.c +++ b/dtls.c @@ -623,7 +623,7 @@ typedef struct cipher_suite_param_t { } cipher_suite_param_t; static const struct cipher_suite_param_t cipher_suite_params[] = { - /* The TLS_NULL_WITH_NULL_NULL cipher-suite must be the first + /* The TLS_NULL_WITH_NULL_NULL cipher suite must be the first * in this table (index DTLS_CIPHER_INDEX_NULL) */ { TLS_NULL_WITH_NULL_NULL, 0, DTLS_KEY_EXCHANGE_NONE }, #ifdef DTLS_PSK @@ -642,13 +642,16 @@ static const dtls_cipher_index_t last_cipher_suite_param = /** * Check if cipher suite is contained in table. * - * \param cipher_suites table with cipher-suites. Terminated with + * \param cipher_suites table with cipher suites. Terminated with * TLS_NULL_WITH_NULL_NULL. * \param cipher_suite cipher suite - * \return 0, if not contained, != 0, if contained + * \return 0 if not contained, != 0 if contained */ static inline uint8_t contains_cipher_suite(const dtls_cipher_t* cipher_suites, const dtls_cipher_t cipher_suite) { + if (cipher_suite == TLS_NULL_WITH_NULL_NULL) { + return 0; + } while ((*cipher_suites != cipher_suite) && (*cipher_suites != TLS_NULL_WITH_NULL_NULL)) { cipher_suites++; @@ -659,7 +662,7 @@ contains_cipher_suite(const dtls_cipher_t* cipher_suites, const dtls_cipher_t ci /** * Get index to cipher suite params. * - * \param cipher_suites table with user-selected cipher-suites. Terminated with + * \param cipher_suites table with user-selected cipher suites. Terminated with * TLS_NULL_WITH_NULL_NULL. * \param cipher cipher suite * \return index to cipher suite params, DTLS_CIPHER_INDEX_NULL if not found. @@ -710,7 +713,7 @@ get_cipher_suite_mac_len(dtls_cipher_index_t cipher_index) { return cipher_suite_params[cipher_index].mac_length; } -/** returns true if the cipher-suite uses an ECDHE_ECDSA key exchange */ +/** returns true if the cipher suite uses an ECDHE_ECDSA key exchange */ static inline int is_key_exchange_ecdhe_ecdsa(dtls_cipher_index_t cipher_index) { #ifdef DTLS_ECC @@ -721,7 +724,7 @@ is_key_exchange_ecdhe_ecdsa(dtls_cipher_index_t cipher_index) { #endif /* DTLS_ECC */ } -/** returns true if the cipher-suite uses an PSK key exchange */ +/** returns true if the cipher suite uses an PSK key exchange */ static inline int is_key_exchange_psk(dtls_cipher_index_t cipher_index) { #ifdef DTLS_PSK @@ -775,7 +778,7 @@ is_ecdsa_client_auth_supported(dtls_context_t *ctx) { * @param ctx The current DTLS context * @param cipher_index The index to cipher suite params to check * @param is_client 1 for a dtls client, 0 for server - * @return @c 1 if @p code is recognized, + * @return @c 1 iff @p code is recognized, */ static int known_cipher(dtls_context_t *ctx, dtls_cipher_index_t cipher_index, int is_client) { @@ -1319,7 +1322,7 @@ dtls_update_parameters(dtls_context_t *ctx, if (!ok) { /* reset config cipher to a well-defined value */ config->cipher_index = DTLS_CIPHER_INDEX_NULL; - dtls_warn("No matching cipher-suite found\n"); + dtls_warn("No matching cipher suite found\n"); goto error; } @@ -2382,7 +2385,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) * * (no elliptic_curves in ServerHello.) */ - uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 6 + 4 + 5]; + uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 6 + 4]; uint8 *p; uint8 extension_size; dtls_handshake_parameters_t * const handshake = peer->handshake_params; @@ -3032,7 +3035,7 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, ecdsa = ecdsa || is_key_exchange_ecdhe_ecdsa(cipher_index); #endif /* DTLS_ECC */ } - /* ignore not supported cipher-suite + /* ignore not supported cipher suite credentials callback is missing */ } From 4a86dc78165de1467eaa17c73201f7e7fe172874 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Mon, 28 Nov 2022 16:01:12 +0100 Subject: [PATCH 3/3] Add cipher suite selection to test applications. Introduce new cli argument -c. Signed-off-by: Achim Kraus --- tests/CMakeLists.txt | 4 +- tests/Makefile.in | 6 ++- tests/dtls-client.c | 49 +++++++++++++++++--- tests/dtls-server.c | 49 ++++++++++++++++---- tests/dtls_ciphers_util.c | 96 +++++++++++++++++++++++++++++++++++++++ tests/dtls_ciphers_util.h | 26 +++++++++++ 6 files changed, 211 insertions(+), 19 deletions(-) create mode 100644 tests/dtls_ciphers_util.c create mode 100644 tests/dtls_ciphers_util.h diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 06988ffd..5bf6f54a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,7 @@ cmake_minimum_required(VERSION 3.5) project(tinydtls-tests) -add_executable(dtls-server dtls-server.c) +add_executable(dtls-server dtls-server.c dtls_ciphers_util.c) target_link_libraries(dtls-server LINK_PUBLIC tinydtls) target_compile_options(dtls-server PUBLIC -Wall -DTEST_INCLUDE -DDTLSv12 -DWITH_SHA256) if(${WARNING_TO_ERROR}) @@ -37,7 +37,7 @@ if(${WARNING_TO_ERROR}) target_compile_options(ccm-test PUBLIC -Werror) endif() -add_executable(dtls-client dtls-client.c) +add_executable(dtls-client dtls-client.c dtls_ciphers_util.c) target_link_libraries(dtls-client LINK_PUBLIC tinydtls) target_compile_options(dtls-client PUBLIC -Wall -DTEST_INCLUDE -DDTLSv12 -DWITH_SHA256) if(${WARNING_TO_ERROR}) diff --git a/tests/Makefile.in b/tests/Makefile.in index a3097d92..f125db54 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -28,10 +28,10 @@ top_srcdir:= @top_srcdir@ # files and flags SOURCES:= dtls-server.c ccm-test.c \ - dtls-client.c + dtls-client.c dtls_ciphers_util.c #cbc_aes128-test.c #dsrv-test.c OBJECTS:= $(patsubst %.c, %.o, $(SOURCES)) -PROGRAMS:= $(patsubst %.c, %, $(SOURCES)) +PROGRAMS:= dtls-server dtls-client ccm-test HEADERS:= CFLAGS:=-Wall -std=c99 @CFLAGS@ @WARNING_CFLAGS@ $(EXTRA_CFLAGS) -D_GNU_SOURCE CPPFLAGS:=-I$(top_srcdir) @CPPFLAGS@ @@ -47,6 +47,8 @@ FILES:=Makefile.in $(SOURCES) ccm-testdata.c #cbc_aes128-testdata.c all: $(PROGRAMS) +dtls-client dtls-server:: dtls_ciphers_util.o + check: echo DISTDIR: $(DISTDIR) echo top_builddir: $(top_builddir) diff --git a/tests/dtls-client.c b/tests/dtls-client.c index e6a4c9c2..84c530c6 100644 --- a/tests/dtls-client.c +++ b/tests/dtls-client.c @@ -35,6 +35,7 @@ #include "global.h" #include "dtls_debug.h" +#include "dtls_ciphers_util.h" #include "dtls.h" #define DEFAULT_PORT 20220 @@ -59,6 +60,9 @@ static dtls_str output_file = { 0, NULL }; /* output file name */ static dtls_context_t *dtls_context = NULL; static dtls_context_t *orig_dtls_context = NULL; +static const dtls_cipher_t* ciphers = NULL; +static unsigned int force_extended_master_secret = 0; + #ifdef DTLS_ECC static const unsigned char ecdsa_priv_key[] = { @@ -228,6 +232,27 @@ send_to_peer(struct dtls_context_t *ctx, &session->addr.sa, session->size); } +static void +get_user_parameters(struct dtls_context_t *ctx, + session_t *session, dtls_user_parameters_t *user_parameters) { + (void) ctx; + (void) session; + user_parameters->force_extended_master_secret = force_extended_master_secret; + 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 dtls_handle_read(struct dtls_context_t *ctx) { int fd; @@ -328,10 +353,13 @@ 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] addr [port]\n" + "usage: %s [-c cipher suites] [-e] [-i file] [-k file] [-o file] [-p port] [-v num] addr [port]\n", #else /* DTLS_PSK */ - "usage: %s [-o file] [-p port] [-v num] addr [port]\n" + "usage: %s [-c cipher suites] [-e] [-o file] [-p port] [-v num] addr [port]\n", #endif /* DTLS_PSK */ + program, version, program); + cipher_suites_usage(stderr, "\t"); + fprintf(stderr, "\t-e\t\tforce extended master secret (RFC7627)\n" #ifdef DTLS_PSK "\t-i file\t\tread PSK identity from file\n" "\t-k file\t\tread pre-shared key from file\n" @@ -339,12 +367,13 @@ usage( const char *program, const char *version) { "\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", - program, version, program, DEFAULT_PORT); + DEFAULT_PORT); } static dtls_handler_t cb = { .write = send_to_peer, .read = read_from_peer, + .get_user_parameters = get_user_parameters, .event = NULL, #ifdef DTLS_PSK .get_psk_info = get_psk_info, @@ -393,7 +422,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:" PSK_OPTIONS)) != -1) { + while ((opt = getopt(argc, argv, "c:eo:p:v:" PSK_OPTIONS)) != -1) { switch (opt) { #ifdef DTLS_PSK case 'i' : @@ -413,9 +442,11 @@ main(int argc, char **argv) { } break; #endif /* DTLS_PSK */ - case 'p' : - strncpy(port_str, optarg, NI_MAXSERV-1); - port_str[NI_MAXSERV - 1] = '\0'; + case 'c' : + ciphers = init_cipher_suites(optarg); + break; + case 'e' : + force_extended_master_secret = 1; break; case 'o' : output_file.length = strlen(optarg); @@ -429,6 +460,10 @@ main(int argc, char **argv) { memcpy(output_file.s, optarg, output_file.length + 1); } break; + case 'p' : + strncpy(port_str, optarg, NI_MAXSERV-1); + port_str[NI_MAXSERV - 1] = '\0'; + break; case 'v' : log_level = strtol(optarg, NULL, 10); break; diff --git a/tests/dtls-server.c b/tests/dtls-server.c index 5a3c03c4..309f0b5c 100644 --- a/tests/dtls-server.c +++ b/tests/dtls-server.c @@ -27,8 +27,9 @@ #include #include "tinydtls.h" -#include "dtls.h" #include "dtls_debug.h" +#include "dtls_ciphers_util.h" +#include "dtls.h" #ifdef IS_WINDOWS #include @@ -44,6 +45,9 @@ #define DEFAULT_PORT 20220 static dtls_context_t *the_context = NULL; +static volatile int cmd_exit = 0; +static const dtls_cipher_t* ciphers = NULL; +static unsigned int force_extended_master_secret = 0; #ifdef DTLS_ECC static const unsigned char ecdsa_priv_key[] = { @@ -151,8 +155,6 @@ verify_ecdsa_key(struct dtls_context_t *ctx, #define DTLS_SERVER_CMD_CLOSE "server:close" #define DTLS_SERVER_CMD_EXIT "server:exit" -static volatile int cmd_exit = 0; - static int is_command(const char* cmd, const uint8 *data, size_t len) { size_t cmd_len = strlen(cmd); @@ -191,6 +193,27 @@ send_to_peer(struct dtls_context_t *ctx, &session->addr.sa, session->size); } +static void +get_user_parameters(struct dtls_context_t *ctx, + session_t *session, dtls_user_parameters_t *user_parameters) { + (void) ctx; + (void) session; + user_parameters->force_extended_master_secret = force_extended_master_secret; + 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 dtls_handle_read(struct dtls_context_t *ctx) { int *fd; @@ -283,17 +306,21 @@ usage(const char *program, const char *version) { program = ++p; fprintf(stderr, "%s v%s -- DTLS server implementation\n" - "(c) 2011-2014 Olaf Bergmann \n\n" - "usage: %s [-A address] [-p port] [-v num]\n" - "\t-A address\t\tlisten on specified address (default is ::)\n" + "(c) 2011-2014 Olaf Bergmann \n\n" + "usage: %s [-A address] [-c cipher suites] [-e] [-p port] [-v num]\n" + "\t-A address\t\tlisten on specified address (default is ::)\n", + program, version, program); + cipher_suites_usage(stderr, "\t"); + fprintf(stderr, "\t-e\t\tforce extended master secret (RFC7627)\n" "\t-p port\t\tlisten on specified port (default is %d)\n" "\t-v num\t\tverbosity level (default: 3)\n", - program, version, program, DEFAULT_PORT); + DEFAULT_PORT); } static dtls_handler_t cb = { .write = send_to_peer, .read = read_from_peer, + .get_user_parameters = get_user_parameters, .event = NULL, #ifdef DTLS_PSK .get_psk_info = get_psk_info, @@ -328,7 +355,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:")) != -1) { + while ((opt = getopt(argc, argv, "A:c:ep:v:")) != -1) { switch (opt) { case 'A' : if (resolve_address(optarg, (struct sockaddr *)&listen_addr) < 0) { @@ -336,6 +363,12 @@ main(int argc, char **argv) { exit(-1); } break; + case 'c' : + ciphers = init_cipher_suites(optarg); + break; + case 'e' : + force_extended_master_secret = 1; + break; case 'p' : port = htons(atoi(optarg)); break; diff --git a/tests/dtls_ciphers_util.c b/tests/dtls_ciphers_util.c new file mode 100644 index 00000000..d9d548df --- /dev/null +++ b/tests/dtls_ciphers_util.c @@ -0,0 +1,96 @@ +/******************************************************************************* + * + * Copyright (c) 2022 Contributors to the Eclipse Foundation. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + *******************************************************************************/ + +#include +#include + +#include "dtls_ciphers_util.h" + + +struct cipher_entry { + const char* name; + const dtls_cipher_t cipher; +}; + +#define CIPHER_ENTRY(X) { .name = #X, .cipher = X } +#define ARRAY_LENGTH (sizeof(map)/sizeof(struct cipher_entry)) +#define SEP ':' + +static const struct cipher_entry map[] = { +#ifdef DTLS_PSK + CIPHER_ENTRY(TLS_PSK_WITH_AES_128_CCM), + CIPHER_ENTRY(TLS_PSK_WITH_AES_128_CCM_8), +#endif /* DTLS_PSK */ +#ifdef DTLS_ECC + CIPHER_ENTRY(TLS_ECDHE_ECDSA_WITH_AES_128_CCM), + CIPHER_ENTRY(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8), +#endif /* DTLS_ECC */ + { .name = NULL, .cipher = TLS_NULL_WITH_NULL_NULL} +}; + +static dtls_cipher_t ciphers_table[ARRAY_LENGTH] = { TLS_NULL_WITH_NULL_NULL }; + +static dtls_cipher_t find_cipher_suite(const char *arg) { + if (arg) { + size_t arg_len = strlen(arg); + for (size_t index=0; index < ARRAY_LENGTH - 1; ++index) { + size_t len = strlen(map[index].name); + if (len <= arg_len) { + if (strncmp(arg, map[index].name, len) == 0 && (arg[len] == 0 || arg[len] == SEP)) { + return map[index].cipher; + } + } + } + } + return TLS_NULL_WITH_NULL_NULL; +} + +static void add_cipher_suite(dtls_cipher_t cipher) { + for (size_t index=0; index < ARRAY_LENGTH - 1; ++index) { + if (ciphers_table[index] == cipher) { + return; + } + if (ciphers_table[index] == TLS_NULL_WITH_NULL_NULL) { + ciphers_table[index] = cipher; + ciphers_table[index + 1] = TLS_NULL_WITH_NULL_NULL; + return; + } + } +} + +const dtls_cipher_t* +init_cipher_suites(const char* arg) { + while (arg) { + dtls_cipher_t cipher = find_cipher_suite(arg); + if (cipher != TLS_NULL_WITH_NULL_NULL) { + add_cipher_suite(cipher); + } + arg = strchr(arg, SEP); + if (arg) { + ++arg; + } + } + return ciphers_table; +} + +void +cipher_suites_usage(FILE* file, const char* head) { + fprintf(file, "%s-c ciphers\tlist of cipher suites separated by ':'\n", head); + fprintf(file, "%s\t\t(default is %s", head, map[0].name); + for (int index = 1; map[index].name; ++index) { + fprintf(file, "\n%s\t\t :%s", head, map[index].name); + } + fprintf(file, ")\n"); +} + diff --git a/tests/dtls_ciphers_util.h b/tests/dtls_ciphers_util.h new file mode 100644 index 00000000..80f4c63f --- /dev/null +++ b/tests/dtls_ciphers_util.h @@ -0,0 +1,26 @@ +/******************************************************************************* + * + * Copyright (c) 2022 Contributors to the Eclipse Foundation. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v. 1.0 which accompanies this distribution. + * + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + *******************************************************************************/ + +#ifndef _DTLS_CIPHERS_UTIL_H_ +#define _DTLS_CIPHERS_UTIL_H_ + +#include + +#include "global.h" + +const dtls_cipher_t* init_cipher_suites(const char* arg); + +void cipher_suites_usage(FILE* file, const char* head); + +#endif /* _DTLS_CIPHERS_UTIL_H_ */