From da174780efb8d654612e343595b91d62cedd35a0 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Mon, 18 Oct 2021 22:12:09 +0200 Subject: [PATCH 1/8] dtls.c: add support for CCM cipher suites. Add cipher suites with full 16 byte MAC. Signed-off-by: Achim Kraus --- dtls.c | 142 +++++++++++++++++++++++++++++++++++-------------------- global.h | 2 + 2 files changed, 93 insertions(+), 51 deletions(-) diff --git a/dtls.c b/dtls.c index c60d3a81..a8f38c0d 100644 --- a/dtls.c +++ b/dtls.c @@ -123,7 +123,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 sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 12 + 26 + 12 +#define DTLS_CH_LENGTH_MAX sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 16 + 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) @@ -558,25 +558,48 @@ static uint8 compression_methods[] = { TLS_COMPRESSION_NULL }; -/** returns true if the cipher matches TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */ -static inline int is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(dtls_cipher_t cipher) +/** + * Get MAC length of cipher suite. + * \param cipher cipher suite + * \return MAC length of cipher. \c 0, if cipher is not supported. + */ +static inline int get_cipher_suite_mac_len(dtls_cipher_t cipher) +{ +#ifdef DTLS_ECC + if (cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) { + return 8; + } else if (cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM) { + return 16; + } +#endif /* DTLS_ECC */ +#ifdef DTLS_PSK + if (cipher == TLS_PSK_WITH_AES_128_CCM_8) { + return 8; + } else if (cipher == TLS_PSK_WITH_AES_128_CCM) { + return 16; + } +#endif /* DTLS_PSK */ + return 0; +} + +/** returns true if the cipher matches TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM */ +static inline int is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(dtls_cipher_t cipher) { #ifdef DTLS_ECC - return cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; + return cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 || cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM; #else (void) cipher; return 0; #endif /* DTLS_ECC */ } -/** returns true if the cipher matches TLS_PSK_WITH_AES_128_CCM_8 */ -static inline int is_tls_psk_with_aes_128_ccm_8(dtls_cipher_t cipher) +/** returns true if the cipher matches TLS_PSK_WITH_AES_128_CCM_8 or TLS_PSK_WITH_AES_128_CCM */ +static inline int is_tls_psk_with_aes_128_ccm_x(dtls_cipher_t cipher) { - (void) cipher; - #ifdef DTLS_PSK - return cipher == TLS_PSK_WITH_AES_128_CCM_8; + return cipher == TLS_PSK_WITH_AES_128_CCM_8 || cipher == TLS_PSK_WITH_AES_128_CCM; #else + (void) cipher; return 0; #endif /* DTLS_PSK */ } @@ -633,8 +656,8 @@ known_cipher(dtls_context_t *ctx, dtls_cipher_t code, int is_client) { psk = is_psk_supported(ctx); ecdsa = is_ecdsa_supported(ctx, is_client); - return (psk && is_tls_psk_with_aes_128_ccm_8(code)) || - (ecdsa && is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(code)); + return (psk && is_tls_psk_with_aes_128_ccm_x(code)) || + (ecdsa && is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(code)); } /** Dump out the cipher keys and IVs used for the symmetric cipher. */ @@ -744,6 +767,7 @@ calculate_key_block(dtls_context_t *ctx, switch (handshake->cipher) { #ifdef DTLS_PSK + case TLS_PSK_WITH_AES_128_CCM: case TLS_PSK_WITH_AES_128_CCM_8: { unsigned char psk[DTLS_PSK_MAX_KEY_LEN]; int len; @@ -773,6 +797,7 @@ calculate_key_block(dtls_context_t *ctx, } #endif /* DTLS_PSK */ #ifdef DTLS_ECC + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: { pre_master_len = dtls_ecdh_pre_master_secret(handshake->keyx.ecdsa.own_eph_priv, handshake->keyx.ecdsa.other_eph_pub_x, @@ -796,11 +821,13 @@ calculate_key_block(dtls_context_t *ctx, * default case as they do nothing but fall through. */ #ifndef DTLS_PSK + case TLS_PSK_WITH_AES_128_CCM: case TLS_PSK_WITH_AES_128_CCM_8: /* fall through to default */ #endif /* !DTLS_PSK */ #ifndef DTLS_ECC + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: /* fall through to default */ #endif /* !DTLS_ECC */ @@ -977,7 +1004,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, if (data_length < sizeof(uint16)) { /* no tls extensions specified */ - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) { + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher)) { goto error; } return 0; @@ -1060,16 +1087,17 @@ dtls_check_tls_extension(dtls_peer_t *peer, data += j; data_length -= j; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher) && 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"); - goto error; - } - } else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher) && !client_hello) { - if (!ext_server_cert_type) { - dtls_warn("not all required tls extensions found in server hello\n"); - goto error; + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher)) { + if (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"); + goto error; + } + } else { + if (!ext_server_cert_type) { + dtls_warn("not all required tls extensions found in server hello\n"); + goto error; + } } } return 0; @@ -1222,7 +1250,7 @@ check_client_keyexchange(dtls_context_t *ctx, (void) ctx; #ifdef DTLS_ECC - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) { + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher)) { if (length < DTLS_HS_LENGTH + DTLS_CKXEC_LENGTH) { dtls_debug("The client key exchange is too short\n"); @@ -1252,7 +1280,7 @@ check_client_keyexchange(dtls_context_t *ctx, } #endif /* DTLS_ECC */ #ifdef DTLS_PSK - if (is_tls_psk_with_aes_128_ccm_8(handshake->cipher)) { + if (is_tls_psk_with_aes_128_ccm_x(handshake->cipher)) { int id_length; if (length < DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN) { @@ -1431,7 +1459,7 @@ dtls_prepare_record(dtls_peer_t *peer, dtls_security_parameters_t *security, p += data_len_array[i]; res += data_len_array[i]; } - } else { /* TLS_PSK_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */ + } else { /* TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CCM, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM */ /** * length of additional_data for the AEAD cipher which consists of * seq_num(2+6) + type(1) + version(2) + length(2) @@ -1439,16 +1467,19 @@ dtls_prepare_record(dtls_peer_t *peer, dtls_security_parameters_t *security, #define A_DATA_LEN 13 unsigned char nonce[DTLS_CCM_BLOCKSIZE]; unsigned char A_DATA[A_DATA_LEN]; + uint8_t mac_len = get_cipher_suite_mac_len(security->cipher); /* For backwards-compatibility, dtls_encrypt_params is called with * M= and L=3. */ - const dtls_ccm_params_t params = { nonce, 8, 3 }; + const dtls_ccm_params_t params = { nonce, mac_len, 3 }; - if (is_tls_psk_with_aes_128_ccm_8(security->cipher)) { - dtls_debug("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_8\n"); - } else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(security->cipher)) { - dtls_debug("dtls_prepare_record(): encrypt using TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8\n"); + if (mac_len == 0) { + dtls_debug("dtls_prepare_record(): encrypt using unknown cipher\n"); } else { - dtls_debug("dtls_prepare_record(): encrypt using unknown cipher\n"); + if (is_tls_psk_with_aes_128_ccm_x(security->cipher)) { + dtls_debug("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_%d\n", mac_len); + } else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(security->cipher)) { + dtls_debug("dtls_prepare_record(): encrypt using TLS_ECDHE_ECDSA_WITH_AES_128_CCM_%d\n", mac_len); + } } /* set nonce @@ -2151,7 +2182,7 @@ check_client_certificate_verify(dtls_context_t *ctx, dtls_hash_ctx hs_hash; unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE]; - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher)); + assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(config->cipher)); data += DTLS_HS_LENGTH; data_length -= DTLS_HS_LENGTH; @@ -2202,7 +2233,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) uint8 extension_size; dtls_handshake_parameters_t *handshake = peer->handshake_params; - ecdsa = is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher); + ecdsa = is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher); extension_size = (handshake->extended_master_secret ? 4 : 0) + (ecdsa ? 5 + 5 + 6 : 0); @@ -2537,7 +2568,7 @@ dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) } #ifdef DTLS_ECC - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) { + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) { const dtls_ecdsa_key_t *ecdsa_key; res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key); @@ -2560,7 +2591,7 @@ dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) return res; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) && + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher) && is_ecdsa_client_auth_supported(ctx)) { res = dtls_send_server_certificate_request(ctx, peer); @@ -2573,7 +2604,7 @@ dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) #endif /* DTLS_ECC */ #ifdef DTLS_PSK - if (is_tls_psk_with_aes_128_ccm_8(peer->handshake_params->cipher)) { + if (is_tls_psk_with_aes_128_ccm_x(peer->handshake_params->cipher)) { unsigned char psk_hint[DTLS_PSK_MAX_CLIENT_IDENTITY_LEN]; int len; @@ -2628,6 +2659,7 @@ dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer) memset(buf, 0, sizeof(buf)); switch (handshake->cipher) { #ifdef DTLS_PSK + case TLS_PSK_WITH_AES_128_CCM: case TLS_PSK_WITH_AES_128_CCM_8: { int len; @@ -2659,6 +2691,7 @@ dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer) } #endif /* DTLS_PSK */ #ifdef DTLS_ECC + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: { uint8 *ephemeral_pub_x; uint8 *ephemeral_pub_y; @@ -2692,11 +2725,13 @@ dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer) * default case as they do nothing but fall through. */ #ifndef DTLS_PSK + case TLS_PSK_WITH_AES_128_CCM: case TLS_PSK_WITH_AES_128_CCM_8: /* fall through to default */ #endif /* !DTLS_PSK */ #ifndef DTLS_ECC + case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: /* fall through to default */ #endif /* !DTLS_ECC */ @@ -2801,7 +2836,7 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, psk = is_psk_supported(ctx); ecdsa = is_ecdsa_supported(ctx, 1); - cipher_size = 2 + ((ecdsa) ? 2 : 0) + ((psk) ? 2 : 0); + cipher_size = 2 + ((ecdsa) ? 4 : 0) + ((psk) ? 4 : 0); extension_size = 4 + ((ecdsa) ? 6 + 6 + 8 + 6 + 8: 0); if (cipher_size == 0) { @@ -2843,10 +2878,14 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, 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); } 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); } /* compression method */ @@ -3078,7 +3117,7 @@ check_server_certificate(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher)); + assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(config->cipher)); data += DTLS_HS_LENGTH; @@ -3129,7 +3168,7 @@ check_server_key_exchange_ecdsa(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher)); + assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(config->cipher)); data += DTLS_HS_LENGTH; data_length -= DTLS_HS_LENGTH; @@ -3216,7 +3255,7 @@ check_server_key_exchange_psk(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_psk_with_aes_128_ccm_8(config->cipher)); + assert(is_tls_psk_with_aes_128_ccm_x(config->cipher)); data += DTLS_HS_LENGTH; @@ -3260,7 +3299,7 @@ check_certificate_request(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)); + assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)); data += DTLS_HS_LENGTH; @@ -3427,7 +3466,7 @@ decrypt_verify(dtls_peer_t *peer, uint8 *packet, size_t length, if (security->cipher == TLS_NULL_WITH_NULL_NULL) { /* no cipher suite selected */ return clen; - } else { /* TLS_PSK_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */ + } else { /* TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CCM, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM */ /** * length of additional_data for the AEAD cipher which consists of * seq_num(2+6) + type(1) + version(2) + length(2) @@ -3435,11 +3474,12 @@ decrypt_verify(dtls_peer_t *peer, uint8 *packet, size_t length, #define A_DATA_LEN 13 unsigned char nonce[DTLS_CCM_BLOCKSIZE]; unsigned char A_DATA[A_DATA_LEN]; + uint8_t mac_len = get_cipher_suite_mac_len(security->cipher); /* For backwards-compatibility, dtls_encrypt_params is called with * M= and L=3. */ - const dtls_ccm_params_t params = { nonce, 8, 3 }; + const dtls_ccm_params_t params = { nonce, mac_len, 3 }; - if (clen < 16) /* need at least IV and MAC */ + if (clen < 8 + mac_len) /* need at least IV and MAC */ return -1; memset(nonce, 0, DTLS_CCM_BLOCKSIZE); @@ -3464,7 +3504,7 @@ decrypt_verify(dtls_peer_t *peer, uint8 *packet, size_t length, memcpy(A_DATA, &DTLS_RECORD_HEADER(packet)->epoch, 8); /* epoch and seq_num */ memcpy(A_DATA + 8, &DTLS_RECORD_HEADER(packet)->content_type, 3); /* type and version */ - dtls_int_to_uint16(A_DATA + 11, clen - 8); /* length without MAC */ + dtls_int_to_uint16(A_DATA + 11, clen - mac_len); /* length without MAC */ clen = dtls_decrypt_params(¶ms, *cleartext, clen, *cleartext, dtls_kb_remote_write_key(security, peer->role), @@ -3518,7 +3558,7 @@ handle_verified_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, if (err < 0) { return err; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) && + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher) && is_ecdsa_client_auth_supported(ctx)) peer->state = DTLS_STATE_WAIT_CLIENTCERTIFICATE; else @@ -3576,7 +3616,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t dtls_warn("error in check_server_hello err: %i\n", err); return err; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) peer->state = DTLS_STATE_WAIT_SERVERCERTIFICATE; else { peer->optional_handshake_message = DTLS_HT_SERVER_KEY_EXCHANGE; @@ -3614,7 +3654,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t } #ifdef DTLS_ECC - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) { + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) { if (state != DTLS_STATE_WAIT_SERVERKEYEXCHANGE) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } @@ -3623,7 +3663,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t } #endif /* DTLS_ECC */ #ifdef DTLS_PSK - if (is_tls_psk_with_aes_128_ccm_8(peer->handshake_params->cipher)) { + if (is_tls_psk_with_aes_128_ccm_x(peer->handshake_params->cipher)) { if (state != DTLS_STATE_WAIT_SERVERHELLODONE || peer->optional_handshake_message != DTLS_HT_SERVER_KEY_EXCHANGE) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } @@ -3662,7 +3702,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t if (state != DTLS_STATE_WAIT_SERVERHELLODONE || peer->optional_handshake_message != DTLS_HT_CERTIFICATE_REQUEST || - !is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) { + !is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } peer->optional_handshake_message = DTLS_HT_NO_OPTIONAL_MESSAGE; @@ -3738,7 +3778,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t &peer->handshake_params->hs_state.hs_hash, sizeof(peer->handshake_params->hs_state.ext_hash)); - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) && + if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher) && is_ecdsa_client_auth_supported(ctx)) peer->state = DTLS_STATE_WAIT_CERTIFICATEVERIFY; else diff --git a/global.h b/global.h index 841392d9..ffc12167 100644 --- a/global.h +++ b/global.h @@ -74,7 +74,9 @@ typedef unsigned char uint48[6]; /** Known cipher suites.*/ typedef enum { TLS_NULL_WITH_NULL_NULL = 0x0000, /**< NULL cipher */ + TLS_PSK_WITH_AES_128_CCM = 0xC0A4, /**< see RFC 6655 */ TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8, /**< see RFC 6655 */ + TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC, /**< see RFC 7251 */ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE /**< see RFC 7251 */ } dtls_cipher_t; From c568650a51671f05eb4e5c5c39c9fed5a9e6115a Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Sun, 19 Dec 2021 09:08:29 +0100 Subject: [PATCH 2/8] dtls.c: add cipher_suite_param_t Use cipher_suite_param_t for cipher-suite specific mac_len and key_exchange_algorithm. Introduce dtls_cipher_index_t for simplified cipher-suite parameter lookup. Cleanup old functions. Signed-off-by: Achim Kraus --- crypto.c | 2 +- crypto.h | 10 +- dtls.c | 375 ++++++++++++++++++++++++++++++------------------------- 3 files changed, 212 insertions(+), 175 deletions(-) diff --git a/crypto.c b/crypto.c index 8cfb7e68..be65a440 100644 --- a/crypto.c +++ b/crypto.c @@ -187,7 +187,7 @@ dtls_security_parameters_t *dtls_security_new(void) memset(security, 0, sizeof(*security)); - security->cipher = TLS_NULL_WITH_NULL_NULL; + security->cipher_index = DTLS_CIPHER_INDEX_NULL; security->compression = TLS_COMPRESSION_NULL; return security; diff --git a/crypto.h b/crypto.h index 52bda0c4..079ccea0 100644 --- a/crypto.h +++ b/crypto.h @@ -49,6 +49,12 @@ #define DTLS_MASTER_SECRET_LENGTH 48 #define DTLS_RANDOM_LENGTH 32 +/** Type of index in cipher parameter table */ +typedef uint8_t dtls_cipher_index_t; +/** Index in cipher parameter table for NULL cipher */ +#define DTLS_CIPHER_INDEX_NULL 0 + + typedef enum { AES128=0 } dtls_crypto_alg; @@ -105,7 +111,7 @@ typedef struct { typedef struct { dtls_compression_t compression; /**< compression method */ - dtls_cipher_t cipher; /**< cipher type */ + dtls_cipher_index_t cipher_index; /**< internal index for cipher_suite_params, DTLS_CIPHER_INDEX_NULL for TLS_NULL_WITH_NULL_NULL */ uint16_t epoch; /**< counter for cipher state changes*/ uint64_t rseq; /**< sequence number of last record sent */ @@ -135,7 +141,7 @@ typedef struct { dtls_hs_state_t hs_state; /**< handshake protocol status */ dtls_compression_t compression; /**< compression method */ - dtls_cipher_t cipher; /**< cipher type */ + 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; union { diff --git a/dtls.c b/dtls.c index a8f38c0d..fa9d2cd0 100644 --- a/dtls.c +++ b/dtls.c @@ -558,48 +558,95 @@ static uint8 compression_methods[] = { TLS_COMPRESSION_NULL }; +typedef enum { + DTLS_KEY_EXCHANGE_NONE, + DTLS_KEY_EXCHANGE_PSK, + DTLS_KEY_EXCHANGE_ECDHE_ECDSA +} cipher_suite_key_exchange_algorithm_t; + +typedef struct cipher_suite_param_t { + dtls_cipher_t cipher_suite; + uint8_t mac_length; + cipher_suite_key_exchange_algorithm_t key_exchange_algorithm; +} 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 + * in this table (index DTLS_CIPHER_INDEX_NULL) */ + { TLS_NULL_WITH_NULL_NULL, 0, DTLS_KEY_EXCHANGE_NONE }, +#ifdef DTLS_PSK + { TLS_PSK_WITH_AES_128_CCM_8, 8, DTLS_KEY_EXCHANGE_PSK }, + { TLS_PSK_WITH_AES_128_CCM, 16, DTLS_KEY_EXCHANGE_PSK }, +#endif /* DTLS_PSK */ +#ifdef DTLS_ECC + { TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, 8, DTLS_KEY_EXCHANGE_ECDHE_ECDSA }, + { TLS_ECDHE_ECDSA_WITH_AES_128_CCM, 16, DTLS_KEY_EXCHANGE_ECDHE_ECDSA }, +#endif /* DTLS_ECC */ + }; + +static const dtls_cipher_index_t last_cipher_suite_param = sizeof(cipher_suite_params) / sizeof(cipher_suite_param_t); + +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; + } + } + return DTLS_CIPHER_INDEX_NULL; +} + +/** + * Get cipher suite. + * \param cipher_index index to cipher suite params + * \return cipher suite. + */ +static inline dtls_cipher_t get_cipher_suite(dtls_cipher_index_t cipher_index) +{ + assert(cipher_index < last_cipher_suite_param); + return cipher_suite_params[cipher_index].cipher_suite; +} + +/** + * Get key exchange algorithm of cipher suite. + * \param cipher_index index to cipher suite params + * \return key exchange algorithm. \c DTLS_KEY_EXCHANGE_NONE, if cipher is not supported. + */ +static inline cipher_suite_key_exchange_algorithm_t get_key_exchange_algorithm(dtls_cipher_index_t cipher_index) +{ + assert(cipher_index < last_cipher_suite_param); + return cipher_suite_params[cipher_index].key_exchange_algorithm; +} + /** * Get MAC length of cipher suite. - * \param cipher cipher suite + * \param cipher_index index to cipher suite params * \return MAC length of cipher. \c 0, if cipher is not supported. */ -static inline int get_cipher_suite_mac_len(dtls_cipher_t cipher) +static inline uint8_t get_cipher_suite_mac_len(dtls_cipher_index_t cipher_index) { -#ifdef DTLS_ECC - if (cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) { - return 8; - } else if (cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM) { - return 16; - } -#endif /* DTLS_ECC */ -#ifdef DTLS_PSK - if (cipher == TLS_PSK_WITH_AES_128_CCM_8) { - return 8; - } else if (cipher == TLS_PSK_WITH_AES_128_CCM) { - return 16; - } -#endif /* DTLS_PSK */ - return 0; + assert(cipher_index < last_cipher_suite_param); + return cipher_suite_params[cipher_index].mac_length; } -/** returns true if the cipher matches TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM */ -static inline int is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(dtls_cipher_t cipher) +/** 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 - return cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 || cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM; + return DTLS_KEY_EXCHANGE_ECDHE_ECDSA == get_key_exchange_algorithm(cipher_index); #else - (void) cipher; + (void) cipher_index; return 0; #endif /* DTLS_ECC */ } -/** returns true if the cipher matches TLS_PSK_WITH_AES_128_CCM_8 or TLS_PSK_WITH_AES_128_CCM */ -static inline int is_tls_psk_with_aes_128_ccm_x(dtls_cipher_t cipher) +/** 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 - return cipher == TLS_PSK_WITH_AES_128_CCM_8 || cipher == TLS_PSK_WITH_AES_128_CCM; + return DTLS_KEY_EXCHANGE_PSK == get_key_exchange_algorithm(cipher_index); #else - (void) cipher; + (void) cipher_index; return 0; #endif /* DTLS_PSK */ } @@ -645,19 +692,18 @@ static inline int is_ecdsa_client_auth_supported(dtls_context_t *ctx) * TLS_NULL_WITH_NULL_NULL that we recognize. * * @param ctx The current DTLS context - * @param code The cipher suite identifier to check + * @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, */ static int -known_cipher(dtls_context_t *ctx, dtls_cipher_t code, int is_client) { - int psk; - int ecdsa; +known_cipher(dtls_context_t *ctx, dtls_cipher_index_t cipher_index, int is_client) { + const int psk = is_psk_supported(ctx); + const int ecdsa = is_ecdsa_supported(ctx, is_client); + const cipher_suite_key_exchange_algorithm_t key_exchange_algorithm = get_key_exchange_algorithm(cipher_index); - psk = is_psk_supported(ctx); - ecdsa = is_ecdsa_supported(ctx, is_client); - return (psk && is_tls_psk_with_aes_128_ccm_x(code)) || - (ecdsa && is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(code)); + return (psk && key_exchange_algorithm == DTLS_KEY_EXCHANGE_PSK) || + (ecdsa && key_exchange_algorithm == DTLS_KEY_EXCHANGE_ECDHE_ECDSA); } /** Dump out the cipher keys and IVs used for the symmetric cipher. */ @@ -764,77 +810,64 @@ calculate_key_block(dtls_context_t *ctx, } pre_master_secret = security->key_block; - - switch (handshake->cipher) { + switch (get_key_exchange_algorithm(handshake->cipher_index)) { + case DTLS_KEY_EXCHANGE_PSK: #ifdef DTLS_PSK - case TLS_PSK_WITH_AES_128_CCM: - case TLS_PSK_WITH_AES_128_CCM_8: { - unsigned char psk[DTLS_PSK_MAX_KEY_LEN]; - int len; - - len = CALL(ctx, get_psk_info, session, DTLS_PSK_KEY, - handshake->keyx.psk.identity, - handshake->keyx.psk.id_length, - psk, DTLS_PSK_MAX_KEY_LEN); - if (len < 0) { - dtls_crit("no psk key for session available\n"); - return len; - } - /* Temporarily use the key_block storage space for the pre master secret. */ - pre_master_len = dtls_psk_pre_master_secret(psk, len, - pre_master_secret, - MAX_KEYBLOCK_LENGTH); + { + unsigned char psk[DTLS_PSK_MAX_KEY_LEN]; + int len; + + len = CALL(ctx, get_psk_info, session, DTLS_PSK_KEY, + handshake->keyx.psk.identity, + handshake->keyx.psk.id_length, + psk, DTLS_PSK_MAX_KEY_LEN); + if (len < 0) { + dtls_crit("no psk key for session available\n"); + return len; + } + /* Temporarily use the key_block storage space for the pre master secret. */ + pre_master_len = dtls_psk_pre_master_secret(psk, len, + pre_master_secret, + MAX_KEYBLOCK_LENGTH); - dtls_debug_hexdump("psk", psk, len); + dtls_debug_hexdump("psk", psk, len); - memset(psk, 0, DTLS_PSK_MAX_KEY_LEN); - if (pre_master_len < 0) { - dtls_crit("the psk was too long, for the pre master secret\n"); - return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + memset(psk, 0, DTLS_PSK_MAX_KEY_LEN); + if (pre_master_len < 0) { + dtls_crit("the psk was too long, for the pre master secret\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + break; } - - break; - } +#else /* DTLS_PSK */ + dtls_crit("calculate_key_block: PSK not supported!\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); #endif /* DTLS_PSK */ + case DTLS_KEY_EXCHANGE_ECDHE_ECDSA: #ifdef DTLS_ECC - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: { - pre_master_len = dtls_ecdh_pre_master_secret(handshake->keyx.ecdsa.own_eph_priv, - handshake->keyx.ecdsa.other_eph_pub_x, - handshake->keyx.ecdsa.other_eph_pub_y, - sizeof(handshake->keyx.ecdsa.own_eph_priv), - pre_master_secret, - MAX_KEYBLOCK_LENGTH); - if (pre_master_len < 0) { - dtls_crit("the curve was too long, for the pre master secret\n"); - return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + { + pre_master_len = dtls_ecdh_pre_master_secret(handshake->keyx.ecdsa.own_eph_priv, + handshake->keyx.ecdsa.other_eph_pub_x, + handshake->keyx.ecdsa.other_eph_pub_y, + sizeof(handshake->keyx.ecdsa.own_eph_priv), + pre_master_secret, + MAX_KEYBLOCK_LENGTH); + if (pre_master_len < 0) { + dtls_crit("the curve was too long, for the pre master secret\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + break; } - break; - } +#else /* DTLS_ECC */ + dtls_crit("calculate_key_block: ECC not supported!\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); #endif /* DTLS_ECC */ - case TLS_NULL_WITH_NULL_NULL: - assert(!"calculate_key_block: tried to use NULL cipher\n"); + case DTLS_KEY_EXCHANGE_NONE: + assert(!"calculate_key_block: not supported key exchange algorithm\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY); + default: /* the key_exchange_algorithm is always from cipher_suite_params */ + assert(!"cipher_suite_params broken, unknown key exchange algorithm\n"); return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY); - - /* The following cases cover the enum symbols that are not - * included in this build. These must be kept just above the - * default case as they do nothing but fall through. - */ -#ifndef DTLS_PSK - case TLS_PSK_WITH_AES_128_CCM: - case TLS_PSK_WITH_AES_128_CCM_8: - /* fall through to default */ -#endif /* !DTLS_PSK */ - -#ifndef DTLS_ECC - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: - /* fall through to default */ -#endif /* !DTLS_ECC */ - - default: - dtls_crit("calculate_key_block: unknown cipher %04x\n", handshake->cipher); - return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); } dtls_debug_dump("client_random", handshake->tmp.random.client, DTLS_RANDOM_LENGTH); @@ -881,7 +914,7 @@ calculate_key_block(dtls_context_t *ctx, memcpy(handshake->tmp.master_secret, master_secret, DTLS_MASTER_SECRET_LENGTH); dtls_debug_keyblock(security); - security->cipher = handshake->cipher; + security->cipher_index = handshake->cipher_index; security->compression = handshake->compression; security->rseq = 0; @@ -1000,11 +1033,11 @@ 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; - dtls_handshake_parameters_t *handshake = peer->handshake_params; + const int ecdsa = is_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index); if (data_length < sizeof(uint16)) { /* no tls extensions specified */ - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher)) { + if (ecdsa) { goto error; } return 0; @@ -1074,7 +1107,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, dtls_info("skipped encrypt-then-mac extension\n"); break; case TLS_EXT_EXTENDED_MASTER_SECRET: - handshake->extended_master_secret = 1; + peer->handshake_params->extended_master_secret = 1; break; case TLS_EXT_SIG_HASH_ALGO: if (verify_ext_sig_hash_algo(data, j)) @@ -1087,7 +1120,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, data += j; data_length -= j; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher)) { + if (ecdsa) { if (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"); @@ -1174,8 +1207,8 @@ dtls_update_parameters(dtls_context_t *ctx, ok = 0; while ((i >= (int)sizeof(uint16)) && !ok) { - config->cipher = dtls_uint16_to_int(data); - ok = known_cipher(ctx, config->cipher, 0); + config->cipher_index = get_cipher_index(dtls_uint16_to_int(data)); + ok = known_cipher(ctx, config->cipher_index, 0); i -= sizeof(uint16); data += sizeof(uint16); } @@ -1185,7 +1218,7 @@ dtls_update_parameters(dtls_context_t *ctx, if (!ok) { /* reset config cipher to a well-defined value */ - config->cipher = TLS_NULL_WITH_NULL_NULL; + config->cipher_index = DTLS_CIPHER_INDEX_NULL; dtls_warn("No matching cipher found\n"); goto error; } @@ -1248,9 +1281,12 @@ check_client_keyexchange(dtls_context_t *ctx, dtls_handshake_parameters_t *handshake, uint8 *data, size_t length) { + const cipher_suite_key_exchange_algorithm_t key_exchange_algorithm = + get_key_exchange_algorithm(handshake->cipher_index); + (void) ctx; #ifdef DTLS_ECC - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher)) { + if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_ECDHE_ECDSA) { if (length < DTLS_HS_LENGTH + DTLS_CKXEC_LENGTH) { dtls_debug("The client key exchange is too short\n"); @@ -1280,7 +1316,7 @@ check_client_keyexchange(dtls_context_t *ctx, } #endif /* DTLS_ECC */ #ifdef DTLS_PSK - if (is_tls_psk_with_aes_128_ccm_x(handshake->cipher)) { + if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_PSK) { int id_length; if (length < DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN) { @@ -1444,7 +1480,7 @@ dtls_prepare_record(dtls_peer_t *peer, dtls_security_parameters_t *security, p = dtls_set_record_header(type, security->epoch, &(security->rseq), sendbuf); start = p; - if (security->cipher == TLS_NULL_WITH_NULL_NULL) { + if (security->cipher_index == DTLS_CIPHER_INDEX_NULL) { /* no cipher suite */ res = 0; @@ -1467,7 +1503,9 @@ dtls_prepare_record(dtls_peer_t *peer, dtls_security_parameters_t *security, #define A_DATA_LEN 13 unsigned char nonce[DTLS_CCM_BLOCKSIZE]; unsigned char A_DATA[A_DATA_LEN]; - uint8_t mac_len = get_cipher_suite_mac_len(security->cipher); + const uint8_t mac_len = get_cipher_suite_mac_len(security->cipher_index); + const cipher_suite_key_exchange_algorithm_t key_exchange_algorithm = + get_key_exchange_algorithm(security->cipher_index); /* For backwards-compatibility, dtls_encrypt_params is called with * M= and L=3. */ const dtls_ccm_params_t params = { nonce, mac_len, 3 }; @@ -1475,9 +1513,9 @@ dtls_prepare_record(dtls_peer_t *peer, dtls_security_parameters_t *security, if (mac_len == 0) { dtls_debug("dtls_prepare_record(): encrypt using unknown cipher\n"); } else { - if (is_tls_psk_with_aes_128_ccm_x(security->cipher)) { + if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_PSK) { dtls_debug("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_%d\n", mac_len); - } else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(security->cipher)) { + } else if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_ECDHE_ECDSA) { dtls_debug("dtls_prepare_record(): encrypt using TLS_ECDHE_ECDSA_WITH_AES_128_CCM_%d\n", mac_len); } } @@ -2182,7 +2220,7 @@ check_client_certificate_verify(dtls_context_t *ctx, dtls_hash_ctx hs_hash; unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE]; - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(config->cipher)); + assert(is_key_exchange_ecdhe_ecdsa(config->cipher_index)); data += DTLS_HS_LENGTH; data_length -= DTLS_HS_LENGTH; @@ -2229,11 +2267,10 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) * encoding function, so we do not need to guess.) */ uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 8 + 6 + 4]; uint8 *p; - int ecdsa; uint8 extension_size; - dtls_handshake_parameters_t *handshake = peer->handshake_params; - - ecdsa = is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(handshake->cipher); + dtls_handshake_parameters_t * const handshake = peer->handshake_params; + const dtls_cipher_t cipher_suite = get_cipher_suite(handshake->cipher_index); + const int ecdsa = is_key_exchange_ecdhe_ecdsa(handshake->cipher_index); extension_size = (handshake->extended_master_secret ? 4 : 0) + (ecdsa ? 5 + 5 + 6 : 0); @@ -2253,9 +2290,9 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) *p++ = 0; /* no session id */ - if (handshake->cipher != TLS_NULL_WITH_NULL_NULL) { + if (cipher_suite != TLS_NULL_WITH_NULL_NULL) { /* selected cipher suite */ - dtls_int_to_uint16(p, handshake->cipher); + dtls_int_to_uint16(p, cipher_suite); p += sizeof(uint16); /* selected compression method */ @@ -2559,6 +2596,7 @@ static int dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) { int res; + cipher_suite_key_exchange_algorithm_t key_exchange_algorithm; res = dtls_send_server_hello(ctx, peer); @@ -2566,9 +2604,10 @@ dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) dtls_debug("dtls_server_hello: cannot prepare ServerHello record\n"); return res; } + key_exchange_algorithm = get_key_exchange_algorithm(peer->handshake_params->cipher_index); #ifdef DTLS_ECC - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) { + if (DTLS_KEY_EXCHANGE_ECDHE_ECDSA == key_exchange_algorithm) { const dtls_ecdsa_key_t *ecdsa_key; res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key); @@ -2591,8 +2630,7 @@ dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) return res; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher) && - is_ecdsa_client_auth_supported(ctx)) { + if (is_ecdsa_client_auth_supported(ctx)) { res = dtls_send_server_certificate_request(ctx, peer); if (res < 0) { @@ -2604,7 +2642,7 @@ dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer) #endif /* DTLS_ECC */ #ifdef DTLS_PSK - if (is_tls_psk_with_aes_128_ccm_x(peer->handshake_params->cipher)) { + if (DTLS_KEY_EXCHANGE_PSK == key_exchange_algorithm) { unsigned char psk_hint[DTLS_PSK_MAX_CLIENT_IDENTITY_LEN]; int len; @@ -2652,16 +2690,18 @@ dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer) uint8 buf[DTLS_CKXEC_LENGTH]; uint8 *p; dtls_handshake_parameters_t *handshake = peer->handshake_params; + const cipher_suite_key_exchange_algorithm_t key_exchange_algorithm = + get_key_exchange_algorithm(handshake->cipher_index); int ret; p = buf; memset(buf, 0, sizeof(buf)); - switch (handshake->cipher) { + switch (key_exchange_algorithm) { + case DTLS_KEY_EXCHANGE_PSK: #ifdef DTLS_PSK - case TLS_PSK_WITH_AES_128_CCM: - case TLS_PSK_WITH_AES_128_CCM_8: { - int len; + { + int len; len = CALL(ctx, get_psk_info, &peer->session, DTLS_PSK_IDENTITY, handshake->keyx.psk.identity, handshake->keyx.psk.id_length, @@ -2689,55 +2729,45 @@ dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer) break; } +#else /* DTLS_PSK */ + dtls_crit("PSK not supported\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); #endif /* DTLS_PSK */ + case DTLS_KEY_EXCHANGE_ECDHE_ECDSA: #ifdef DTLS_ECC - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: { - uint8 *ephemeral_pub_x; - uint8 *ephemeral_pub_y; + { + uint8 *ephemeral_pub_x; + uint8 *ephemeral_pub_y; - dtls_int_to_uint8(p, 1 + 2 * DTLS_EC_KEY_SIZE); - p += sizeof(uint8); + dtls_int_to_uint8(p, 1 + 2 * DTLS_EC_KEY_SIZE); + p += sizeof(uint8); - /* This should be an uncompressed point, but I do not have access to the spec. */ - dtls_int_to_uint8(p, 4); - p += sizeof(uint8); + /* This should be an uncompressed point, but I do not have access to the spec. */ + dtls_int_to_uint8(p, 4); + p += sizeof(uint8); - ephemeral_pub_x = p; - p += DTLS_EC_KEY_SIZE; - ephemeral_pub_y = p; - p += DTLS_EC_KEY_SIZE; + ephemeral_pub_x = p; + p += DTLS_EC_KEY_SIZE; + ephemeral_pub_y = p; + p += DTLS_EC_KEY_SIZE; dtls_ecdsa_generate_key(peer->handshake_params->keyx.ecdsa.own_eph_priv, ephemeral_pub_x, ephemeral_pub_y, DTLS_EC_KEY_SIZE); - break; - } + break; + } +#else /* DTLS_ECC */ + dtls_crit("ECC not supported\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); #endif /* DTLS_ECC */ - case TLS_NULL_WITH_NULL_NULL: + case DTLS_KEY_EXCHANGE_NONE: assert(!"NULL cipher requested"); return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY); - /* The following cases cover the enum symbols that are not - * included in this build. These must be kept just above the - * default case as they do nothing but fall through. - */ -#ifndef DTLS_PSK - case TLS_PSK_WITH_AES_128_CCM: - case TLS_PSK_WITH_AES_128_CCM_8: - /* fall through to default */ -#endif /* !DTLS_PSK */ - -#ifndef DTLS_ECC - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM: - case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: - /* fall through to default */ -#endif /* !DTLS_ECC */ - default: - dtls_crit("cipher %04x not supported\n", handshake->cipher); + dtls_crit("key exchange algorithm %d not supported\n", key_exchange_algorithm); return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); } @@ -3058,8 +3088,8 @@ check_server_hello(dtls_context_t *ctx, /* 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 = dtls_uint16_to_int(data); - if (!known_cipher(ctx, handshake->cipher, 1)) { + handshake->cipher_index = get_cipher_index(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]); return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY); @@ -3117,7 +3147,7 @@ check_server_certificate(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(config->cipher)); + assert(is_key_exchange_ecdhe_ecdsa(config->cipher_index)); data += DTLS_HS_LENGTH; @@ -3168,7 +3198,7 @@ check_server_key_exchange_ecdsa(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(config->cipher)); + assert(is_key_exchange_ecdhe_ecdsa(config->cipher_index)); data += DTLS_HS_LENGTH; data_length -= DTLS_HS_LENGTH; @@ -3255,7 +3285,7 @@ check_server_key_exchange_psk(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_psk_with_aes_128_ccm_x(config->cipher)); + assert(is_key_exchange_psk(config->cipher_index)); data += DTLS_HS_LENGTH; @@ -3299,7 +3329,7 @@ check_certificate_request(dtls_context_t *ctx, update_hs_hash(peer, data, data_length); - assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)); + assert(is_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index)); data += DTLS_HS_LENGTH; @@ -3463,7 +3493,7 @@ decrypt_verify(dtls_peer_t *peer, uint8 *packet, size_t length, return -1; } - if (security->cipher == TLS_NULL_WITH_NULL_NULL) { + if (security->cipher_index == DTLS_CIPHER_INDEX_NULL) { /* no cipher suite selected */ return clen; } else { /* TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CCM, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM */ @@ -3474,7 +3504,7 @@ decrypt_verify(dtls_peer_t *peer, uint8 *packet, size_t length, #define A_DATA_LEN 13 unsigned char nonce[DTLS_CCM_BLOCKSIZE]; unsigned char A_DATA[A_DATA_LEN]; - uint8_t mac_len = get_cipher_suite_mac_len(security->cipher); + const uint8_t mac_len = get_cipher_suite_mac_len(security->cipher_index); /* For backwards-compatibility, dtls_encrypt_params is called with * M= and L=3. */ const dtls_ccm_params_t params = { nonce, mac_len, 3 }; @@ -3558,7 +3588,7 @@ handle_verified_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, if (err < 0) { return err; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher) && + if (is_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index) && is_ecdsa_client_auth_supported(ctx)) peer->state = DTLS_STATE_WAIT_CLIENTCERTIFICATE; else @@ -3573,6 +3603,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t int err = 0; const dtls_peer_type role = peer->role; const dtls_state_t state = peer->state; + const cipher_suite_key_exchange_algorithm_t key_exchange_algorithm = get_key_exchange_algorithm(peer->handshake_params->cipher_index); /* This will clear the retransmission buffer if we get an expected * handshake message. We have to make sure that no handshake message @@ -3616,7 +3647,8 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t dtls_warn("error in check_server_hello err: %i\n", err); return err; } - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) + /* check_server_hello sets the cipher_index */ + if (is_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index)) peer->state = DTLS_STATE_WAIT_SERVERCERTIFICATE; else { peer->optional_handshake_message = DTLS_HT_SERVER_KEY_EXCHANGE; @@ -3654,7 +3686,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t } #ifdef DTLS_ECC - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) { + if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_ECDHE_ECDSA) { if (state != DTLS_STATE_WAIT_SERVERKEYEXCHANGE) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } @@ -3663,7 +3695,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t } #endif /* DTLS_ECC */ #ifdef DTLS_PSK - if (is_tls_psk_with_aes_128_ccm_x(peer->handshake_params->cipher)) { + if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_PSK) { if (state != DTLS_STATE_WAIT_SERVERHELLODONE || peer->optional_handshake_message != DTLS_HT_SERVER_KEY_EXCHANGE) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } @@ -3702,7 +3734,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t if (state != DTLS_STATE_WAIT_SERVERHELLODONE || peer->optional_handshake_message != DTLS_HT_CERTIFICATE_REQUEST || - !is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher)) { + key_exchange_algorithm != DTLS_KEY_EXCHANGE_ECDHE_ECDSA) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } peer->optional_handshake_message = DTLS_HT_NO_OPTIONAL_MESSAGE; @@ -3778,8 +3810,7 @@ handle_handshake_msg(dtls_context_t *ctx, dtls_peer_t *peer, uint8 *data, size_t &peer->handshake_params->hs_state.hs_hash, sizeof(peer->handshake_params->hs_state.ext_hash)); - if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_x(peer->handshake_params->cipher) && - is_ecdsa_client_auth_supported(ctx)) + if (key_exchange_algorithm == DTLS_KEY_EXCHANGE_ECDHE_ECDSA && is_ecdsa_client_auth_supported(ctx)) peer->state = DTLS_STATE_WAIT_CERTIFICATEVERIFY; else peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC; From 426b601a0504e684d8ea89b70f02c5d861516bdb Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Wed, 8 Dec 2021 16:28:42 +0100 Subject: [PATCH 3/8] dtls.c: add callback for peer specific selection of cipher suites. Signed-off-by: Achim Kraus --- crypto.h | 1 + dtls.c | 138 ++++++++++++++++++++++++++++++++++++++++--------------- dtls.h | 12 +++++ 3 files changed, 115 insertions(+), 36 deletions(-) diff --git a/crypto.h b/crypto.h index 079ccea0..97f5a9ad 100644 --- a/crypto.h +++ b/crypto.h @@ -141,6 +141,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_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 fa9d2cd0..32977855 100644 --- a/dtls.c +++ b/dtls.c @@ -553,6 +553,25 @@ dtls_set_handshake_header(uint8 type, return buf; } +static const dtls_cipher_t default_cipher_suites[] = +#ifdef DTLS_DEFAULT_CIPHER_SUITES + DTLS_DEFAULT_CIPHER_SUITES ; +#else +{ +#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 + /** only one compression method is currently defined */ static uint8 compression_methods[] = { TLS_COMPRESSION_NULL @@ -586,11 +605,22 @@ 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); -static inline dtls_cipher_index_t get_cipher_index(dtls_cipher_t cipher) +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; +} + +static inline dtls_cipher_index_t get_cipher_index(const dtls_cipher_t* cipher_suites, dtls_cipher_t cipher) { for (int index = 0; index < last_cipher_suite_param ; ++index) { if (cipher_suite_params[index].cipher_suite == cipher) { - return index; + if (contains_cipher_suite(cipher_suites, cipher)) { + return index; + } } } return DTLS_CIPHER_INDEX_NULL; @@ -694,7 +724,7 @@ static inline int 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) { @@ -1205,9 +1235,16 @@ 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; + } + 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->cipher_suites, dtls_uint16_to_int(data)); ok = known_cipher(ctx, config->cipher_index, 0); i -= sizeof(uint16); data += sizeof(uint16); @@ -1219,7 +1256,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; } @@ -2856,21 +2893,21 @@ 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); - - 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"); + 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; } dtls_int_to_uint16(p, DTLS_VERSION); @@ -2901,22 +2938,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->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); + 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); @@ -2929,6 +2991,7 @@ 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 */ dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERTIFICATE_TYPE); @@ -3011,6 +3074,8 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, p += sizeof(uint8); } +#endif /* DTLS_ECC */ + /* extended master secret */ dtls_int_to_uint16(p, TLS_EXT_EXTENDED_MASTER_SECRET); p += sizeof(uint16); @@ -3085,15 +3150,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->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..c43f816b 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 list of cipher suites. + * + * @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. + */ + void (*get_cipher_suites)(struct dtls_context_t *ctx, session_t *session, const dtls_cipher_t **cipher_suites); + #ifdef DTLS_PSK /** * Called during handshake to get information related to the From 32fe1978fc8501fc545240b6b66c788d9a92d600 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Mon, 28 Nov 2022 16:01:12 +0100 Subject: [PATCH 4/8] Add cipher suite selection to test applications. Signed-off-by: Achim Kraus --- tests/CMakeLists.txt | 4 +- tests/dtls-client.c | 22 ++++++++-- tests/dtls-server.c | 22 ++++++++-- tests/dtls_ciphers_util.c | 92 +++++++++++++++++++++++++++++++++++++++ tests/dtls_ciphers_util.h | 26 +++++++++++ 5 files changed, 158 insertions(+), 8 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/dtls-client.c b/tests/dtls-client.c index 74baeed7..a7468f75 100644 --- a/tests/dtls-client.c +++ b/tests/dtls-client.c @@ -21,6 +21,7 @@ #include "global.h" #include "dtls_debug.h" +#include "dtls_ciphers_util.h" #include "dtls.h" #define DEFAULT_PORT 20220 @@ -214,6 +215,16 @@ send_to_peer(struct dtls_context_t *ctx, &session->addr.sa, session->size); } +static const dtls_cipher_t* ciphers = NULL; + +static void +get_cipher_suites(struct dtls_context_t *ctx, + session_t *session, const dtls_cipher_t **cipher_suites) { + (void) ctx; + (void) session; + *cipher_suites = ciphers; +} + static int dtls_handle_read(struct dtls_context_t *ctx) { int fd; @@ -308,9 +319,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] addr [port]\n" + "usage: %s [-i file] [-k file] [-o file] [-p port] [-v num] [-c cipher-suites] addr [port]\n" #else /* DTLS_PSK */ - "usage: %s [-o file] [-p port] [-v num] addr [port]\n" + "usage: %s [-o file] [-p port] [-v num] [-c cipher-suites] addr [port]\n" #endif /* DTLS_PSK */ #ifdef DTLS_PSK "\t-i file\t\tread PSK identity from file\n" @@ -320,11 +331,13 @@ usage( const char *program, const char *version) { "\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); + cipher_suites_usage(stderr, "\t"); } static dtls_handler_t cb = { .write = send_to_peer, .read = read_from_peer, + .get_cipher_suites = get_cipher_suites, .event = NULL, #ifdef DTLS_PSK .get_psk_info = get_psk_info, @@ -370,7 +383,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, "p:o:v:c:" PSK_OPTIONS)) != -1) { switch (opt) { #ifdef DTLS_PSK case 'i' : @@ -409,6 +422,9 @@ main(int argc, char **argv) { case 'v' : log_level = strtol(optarg, NULL, 10); break; + case 'c' : + ciphers = init_cipher_suites(optarg); + break; default: usage(argv[0], dtls_package_version()); exit(1); diff --git a/tests/dtls-server.c b/tests/dtls-server.c index 6e0d5894..78b07e90 100644 --- a/tests/dtls-server.c +++ b/tests/dtls-server.c @@ -14,8 +14,9 @@ #include #include "tinydtls.h" -#include "dtls.h" #include "dtls_debug.h" +#include "dtls_ciphers_util.h" +#include "dtls.h" #ifdef IS_WINDOWS #include @@ -161,6 +162,16 @@ send_to_peer(struct dtls_context_t *ctx, &session->addr.sa, session->size); } +static const dtls_cipher_t* ciphers = NULL; + +static void +get_cipher_suites(struct dtls_context_t *ctx, + session_t *session, const dtls_cipher_t **cipher_suites) { + (void) ctx; + (void) session; + *cipher_suites = ciphers; +} + static int dtls_handle_read(struct dtls_context_t *ctx) { int *fd; @@ -250,16 +261,18 @@ 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]\n" + "usage: %s [-A address] [-p port] [-v num] [-c cipher-suites]\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", 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, .event = NULL, #ifdef DTLS_PSK .get_psk_info = get_psk_info, @@ -294,7 +307,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:p:v:c:")) != -1) { switch (opt) { case 'A' : if (resolve_address(optarg, (struct sockaddr *)&listen_addr) < 0) { @@ -308,6 +321,9 @@ main(int argc, char **argv) { case 'v' : log_level = strtol(optarg, NULL, 10); break; + case 'c' : + ciphers = init_cipher_suites(optarg); + break; default: usage(argv[0], dtls_package_version()); exit(1); diff --git a/tests/dtls_ciphers_util.c b/tests/dtls_ciphers_util.c new file mode 100644 index 00000000..2fc749bd --- /dev/null +++ b/tests/dtls_ciphers_util.c @@ -0,0 +1,92 @@ +/******************************************************************************* + * + * 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 "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) { + for (int index=0; index < ARRAY_LENGTH - 1; ++index) { + size_t len = strlen(map[index].name); + 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 (int 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 ", head); +#if defined(DTLS_PSK) && defined(DTLS_ECC) + fprintf(file, "%s:%s\n", map[0].name, map[1].name); + fprintf(file, "%s\t\t :%s:%s)\n", head, map[2].name, map[3].name); +#elif defined(DTLS_PSK) || defined(DTLS_ECC) + fprintf(file, "%s:%s)\n", map[0].name, map[1].name); +#endif +} + 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_ */ From 5c6a0c545438a9172fd960e3f223010a8cce6c4a Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Mon, 28 Nov 2022 18:07:36 +0100 Subject: [PATCH 5/8] Fix tests makefile. The tests makefile only supports simple test programs. Use include instead of additional object files. Signed-off-by: Achim Kraus --- tests/CMakeLists.txt | 4 ++-- tests/dtls-client.c | 1 + tests/dtls-server.c | 3 +++ tests/dtls_ciphers_util.c | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5bf6f54a..06988ffd 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 dtls_ciphers_util.c) +add_executable(dtls-server dtls-server.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 dtls_ciphers_util.c) +add_executable(dtls-client dtls-client.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/dtls-client.c b/tests/dtls-client.c index a7468f75..68253212 100644 --- a/tests/dtls-client.c +++ b/tests/dtls-client.c @@ -562,3 +562,4 @@ main(int argc, char **argv) { exit(0); } +#include "dtls_ciphers_util.c" diff --git a/tests/dtls-server.c b/tests/dtls-server.c index 78b07e90..15246308 100644 --- a/tests/dtls-server.c +++ b/tests/dtls-server.c @@ -422,3 +422,6 @@ main(int argc, char **argv) { dtls_free_context(the_context); exit(0); } + +#include "dtls_ciphers_util.c" + diff --git a/tests/dtls_ciphers_util.c b/tests/dtls_ciphers_util.c index 2fc749bd..915e6969 100644 --- a/tests/dtls_ciphers_util.c +++ b/tests/dtls_ciphers_util.c @@ -41,7 +41,7 @@ static const struct cipher_entry map[] = { static dtls_cipher_t ciphers_table[ARRAY_LENGTH] = { TLS_NULL_WITH_NULL_NULL }; static dtls_cipher_t find_cipher_suite(const char *arg) { - for (int index=0; index < ARRAY_LENGTH - 1; ++index) { + for (size_t index=0; index < ARRAY_LENGTH - 1; ++index) { size_t len = strlen(map[index].name); if (strncmp(arg, map[index].name, len) == 0 && (arg[len] == 0 || arg[len] == SEP)) { return map[index].cipher; @@ -51,7 +51,7 @@ static dtls_cipher_t find_cipher_suite(const char *arg) { } static void add_cipher_suite(dtls_cipher_t cipher) { - for (int index=0; index < ARRAY_LENGTH - 1; ++index) { + for (size_t index=0; index < ARRAY_LENGTH - 1; ++index) { if (ciphers_table[index] == cipher) { return; } From c2c9d33c41f9652f0b242c83b3a42ae05d4d0f98 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Mon, 28 Nov 2022 17:16:43 +0100 Subject: [PATCH 6/8] dtls.c: add RFC5746 minimal version implementation. Supports RFC5746 minimal version without renegotiation. Signed-off-by: Achim Kraus --- crypto.h | 1 + dtls.c | 50 ++++++++++++++++++++++++++++++++++++++++++-------- global.h | 4 +++- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/crypto.h b/crypto.h index 97f5a9ad..a759cf66 100644 --- a/crypto.h +++ b/crypto.h @@ -145,6 +145,7 @@ typedef struct { 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; + unsigned int renegotiation_info:1; union { #ifdef DTLS_ECC dtls_handshake_parameters_ecdsa_t ecdsa; diff --git a/dtls.c b/dtls.c index 32977855..ef98ecbc 100644 --- a/dtls.c +++ b/dtls.c @@ -566,6 +566,8 @@ static const dtls_cipher_t default_cipher_suites[] = 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 @@ -1056,7 +1058,7 @@ 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; @@ -1107,7 +1109,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 { @@ -1117,7 +1119,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 { @@ -1143,6 +1145,15 @@ dtls_check_tls_extension(dtls_peer_t *peer, if (verify_ext_sig_hash_algo(data, j)) goto error; break; + 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; + } else { + dtls_warn("only empty renegotiation info is supported.\n"); + goto error; + } + break; default: dtls_warn("unsupported tls extension: %i\n", i); break; @@ -1151,7 +1162,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"); goto error; @@ -1166,7 +1177,7 @@ dtls_check_tls_extension(dtls_peer_t *peer, 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); @@ -1243,9 +1254,13 @@ dtls_update_parameters(dtls_context_t *ctx, } ok = 0; - while ((i >= (int)sizeof(uint16)) && !ok) { - config->cipher_index = get_cipher_index(config->cipher_suites, dtls_uint16_to_int(data)); - ok = known_cipher(ctx, config->cipher_index, 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)); + ok = known_cipher(ctx, config->cipher_index, 0); + } i -= sizeof(uint16); data += sizeof(uint16); } @@ -2310,6 +2325,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) const int ecdsa = is_key_exchange_ecdhe_ecdsa(handshake->cipher_index); extension_size = (handshake->extended_master_secret ? 4 : 0) + + (handshake->renegotiation_info ? 5 : 0) + (ecdsa ? 5 + 5 + 6 : 0); /* Handshake header */ @@ -2391,6 +2407,19 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) p += sizeof(uint16); } + if (handshake->renegotiation_info) { + /* RFC5746 minimal version, empty renegotiation info */ + dtls_int_to_uint16(p, TLS_EXT_RENEGOTIATION_INFO); + p += sizeof(uint16); + + /* length of this extension type */ + dtls_int_to_uint16(p, 1); + p += sizeof(uint16); + + /* empty renegotiation info */ + *p++ = 0; + } + assert((buf <= p) && ((unsigned int)(p - buf) <= sizeof(buf))); /* TODO use the same record sequence number as in the ClientHello, @@ -2964,6 +2993,11 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE); } + /* RFC5746 add RENEGOTIATION_INFO_SCSV */ + dtls_int_to_uint16(p, TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + p += sizeof(uint16); + cipher_suites_size += sizeof(uint16); + /* set size of known cipher suites */ dtls_int_to_uint16(p_cipher_suites_size, cipher_suites_size); diff --git a/global.h b/global.h index ffc12167..d915aed1 100644 --- a/global.h +++ b/global.h @@ -74,10 +74,11 @@ typedef unsigned char uint48[6]; /** Known cipher suites.*/ typedef enum { TLS_NULL_WITH_NULL_NULL = 0x0000, /**< NULL cipher */ + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, /**< see RFC 5746 */ TLS_PSK_WITH_AES_128_CCM = 0xC0A4, /**< see RFC 6655 */ TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8, /**< see RFC 6655 */ TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC, /**< see RFC 7251 */ - TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE /**< see RFC 7251 */ + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE, /**< see RFC 7251 */ } dtls_cipher_t; /** Known compression suites.*/ @@ -92,6 +93,7 @@ typedef enum { #define TLS_EXT_SERVER_CERTIFICATE_TYPE 20 /* see RFC 7250 */ #define TLS_EXT_ENCRYPT_THEN_MAC 22 /* see RFC 7366 */ #define TLS_EXT_EXTENDED_MASTER_SECRET 23 /* see RFC 7627 */ +#define TLS_EXT_RENEGOTIATION_INFO 65281 /* see RFC 5746 */ #define TLS_CERT_TYPE_RAW_PUBLIC_KEY 2 /* see RFC 7250 */ From 84f834a69eefefd4180edcda5a1b485cadb75b07 Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Mon, 5 Dec 2022 09:05:37 +0100 Subject: [PATCH 7/8] dtls.c: fix calculations of maximum message lengths. Add detailed documentation about the message length calculations. Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV to DTLS_CH_LENGTH_MAX. Remove eclipse_curves from ServerHello length. Signed-off-by: Achim Kraus --- dtls.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/dtls.c b/dtls.c index ef98ecbc..2a8ec032 100644 --- a/dtls.c +++ b/dtls.c @@ -119,11 +119,46 @@ memarray_t dtlscontext_storage; } #endif /* DTLS_PEERS_NOHASH */ +/* + * ClientHello: + * + * session_length := 1 byte + * session := 0 bytes + * cookie_length := 1 byte + * cookie := n bytes + * cipher_length := 2 bytes + * cipher suites (max) := 4 * 2 + 2 bytes + * compression_lenght := 1 byte + * compression := 1 byte + * extensions_length := 2 bytes => 18 + * + * 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 + * + * (The ClientHello uses TLS_EMPTY_RENEGOTIATION_INFO_SCSV + * instead of renegotiation info) + */ + +/* + * ServerHello: + * + * version := 2 bytes + * random := 32 bytes + * session_length := 1 byte + * session := 0 bytes + * cipher suites := 2 bytes + * compression := 1 byte + */ + #define DTLS_RH_LENGTH sizeof(dtls_record_header_t) #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 sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 16 + 26 + 12 +#define DTLS_CH_LENGTH_MAX DTLS_CH_LENGTH + DTLS_COOKIE_LENGTH_MAX + 18 + 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) @@ -2316,8 +2351,18 @@ 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 + * renegotiation info := 5 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; @@ -2359,7 +2404,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); @@ -2370,7 +2415,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); @@ -2381,7 +2426,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); @@ -2398,7 +2443,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); @@ -2408,7 +2453,7 @@ dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer) } if (handshake->renegotiation_info) { - /* RFC5746 minimal version, empty renegotiation info */ + /* RFC5746 minimal version, empty renegotiation info, 5 bytes */ dtls_int_to_uint16(p, TLS_EXT_RENEGOTIATION_INFO); p += sizeof(uint16); From c57c78c8f95cad70d9cf4cd648bb1ee003aa2c3b Mon Sep 17 00:00:00 2001 From: Achim Kraus Date: Fri, 2 Dec 2022 17:38:47 +0100 Subject: [PATCH 8/8] dtls.c: add dtls_user_parameters_t. Callback for user parameters. Includes cipher suites and flags to enforce security features. Cleanup test dtls-client. Signed-off-by: Achim Kraus --- crypto.h | 17 +++++++++- dtls.c | 75 ++++++++++++++++++++++++++------------------- dtls.h | 8 ++--- tests/dtls-client.c | 59 ++++++++++++++++++++++++++--------- tests/dtls-server.c | 44 +++++++++++++++++++++----- 5 files changed, 144 insertions(+), 59 deletions(-) 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 2a8ec032..4f62ee8f 100644 --- a/dtls.c +++ b/dtls.c @@ -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) @@ -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[] = { @@ -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 */ @@ -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)) @@ -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; @@ -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: @@ -1281,11 +1296,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; @@ -1293,7 +1306,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); @@ -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); @@ -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); @@ -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]); 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..6cc94073 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,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 @@ -319,9 +335,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 +345,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 +355,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 +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; @@ -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' : @@ -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); @@ -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) { @@ -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"); @@ -553,6 +583,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..fb619a3f 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,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 @@ -261,10 +283,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 +296,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 +331,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 +348,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);