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..53547589 100644 --- a/crypto.h +++ b/crypto.h @@ -49,6 +49,14 @@ #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 + +/** Maximum number of cipher suites */ +#define DTLS_MAX_CIPHER_SUITES 4 + typedef enum { AES128=0 } dtls_crypto_alg; @@ -105,7 +113,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 */ @@ -122,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 { @@ -135,9 +156,11 @@ typedef struct { dtls_hs_state_t hs_state; /**< handshake protocol status */ dtls_compression_t compression; /**< compression method */ - dtls_cipher_t cipher; /**< cipher type */ + 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; + unsigned int renegotiation_info:1; union { #ifdef DTLS_ECC dtls_handshake_parameters_ecdsa_t ecdsa; diff --git a/dtls.c b/dtls.c index c60d3a81..4f62ee8f 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 + 12 + 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) @@ -553,30 +588,134 @@ dtls_set_handshake_header(uint8 type, return buf; } +static const dtls_user_parameters_t default_user_parameters = { + .cipher_suites = +#ifdef DTLS_DEFAULT_CIPHER_SUITES + DTLS_DEFAULT_CIPHER_SUITES , +#else /* DTLS_DEFAULT_CIPHER_SUITES */ + { +#ifdef DTLS_ECC + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM, +#endif /* DTLS_ECC */ +#ifdef DTLS_PSK + TLS_PSK_WITH_AES_128_CCM_8, + TLS_PSK_WITH_AES_128_CCM, +#endif /* DTLS_PSK */ + /* TLS_NULL_WITH_NULL_NULL must always be the last entry as it + * indicates the stop marker for the traversal of this table. */ + TLS_NULL_WITH_NULL_NULL + }, +#endif /* DTLS_DEFAULT_CIPHER_SUITES */ + .force_extended_master_secret = 1, + .force_renegotiation_info = 1, +}; + /** only one compression method is currently defined */ 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) +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 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) { + if (contains_cipher_suite(cipher_suites, 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_index index to cipher suite params + * \return MAC length of cipher. \c 0, if cipher is not supported. + */ +static inline uint8_t get_cipher_suite_mac_len(dtls_cipher_index_t cipher_index) +{ + assert(cipher_index < last_cipher_suite_param); + return cipher_suite_params[cipher_index].mac_length; +} + +/** 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; + 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 */ -static inline int is_tls_psk_with_aes_128_ccm_8(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) { - (void) cipher; - #ifdef DTLS_PSK - return cipher == TLS_PSK_WITH_AES_128_CCM_8; + return DTLS_KEY_EXCHANGE_PSK == get_key_exchange_algorithm(cipher_index); #else + (void) cipher_index; return 0; #endif /* DTLS_PSK */ } @@ -622,19 +761,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, + * @return @c 1 if @p code is recognized, */ static int -known_cipher(dtls_context_t *ctx, dtls_cipher_t code, int is_client) { - int psk; - int ecdsa; - - 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)); +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); + + 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. */ @@ -741,73 +879,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_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_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_8: - /* fall through to default */ -#endif /* !DTLS_PSK */ - -#ifndef DTLS_ECC - 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); @@ -854,7 +983,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; @@ -966,21 +1095,22 @@ static int verify_ext_sig_hash_algo(uint8 *data, size_t data_length) { */ static int dtls_check_tls_extension(dtls_peer_t *peer, - uint8 *data, size_t data_length, int client_hello) + uint8 *data, size_t data_length, int is_client_hello) { uint16_t i, j; int ext_elliptic_curve = 0; int ext_client_cert_type = 0; int ext_server_cert_type = 0; int ext_ec_point_formats = 0; - dtls_handshake_parameters_t *handshake = peer->handshake_params; + 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 (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) { + if (ecdsa) { goto error; } - return 0; + goto check_forced_extensions; } /* get the length of the tls extension list */ @@ -1017,7 +1147,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 { @@ -1027,7 +1157,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 { @@ -1047,12 +1177,21 @@ 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; + config->extended_master_secret = 1; break; case TLS_EXT_SIG_HASH_ALGO: 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) { + config->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; @@ -1060,22 +1199,35 @@ 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 (ecdsa) { + 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; + } + } else { + if (!ext_server_cert_type) { + dtls_warn("not all required tls extensions found in server hello\n"); + goto error; + } } } + +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: - 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); @@ -1144,10 +1296,19 @@ dtls_update_parameters(dtls_context_t *ctx, data += sizeof(uint16); data_length -= sizeof(uint16) + i; + config->user_parameters = default_user_parameters; + if (ctx->h->get_user_parameters != NULL) { + ctx->h->get_user_parameters(ctx, &peer->session, &config->user_parameters); + } + ok = 0; - while ((i >= (int)sizeof(uint16)) && !ok) { - config->cipher = dtls_uint16_to_int(data); - ok = known_cipher(ctx, config->cipher, 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->user_parameters.cipher_suites, dtls_uint16_to_int(data)); + ok = known_cipher(ctx, config->cipher_index, 0); + } i -= sizeof(uint16); data += sizeof(uint16); } @@ -1157,8 +1318,8 @@ dtls_update_parameters(dtls_context_t *ctx, if (!ok) { /* reset config cipher to a well-defined value */ - config->cipher = TLS_NULL_WITH_NULL_NULL; - dtls_warn("No matching cipher found\n"); + config->cipher_index = DTLS_CIPHER_INDEX_NULL; + dtls_warn("No matching cipher-suite found\n"); goto error; } @@ -1220,9 +1381,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_8(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"); @@ -1252,7 +1416,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 (key_exchange_algorithm == DTLS_KEY_EXCHANGE_PSK) { int id_length; if (length < DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN) { @@ -1416,7 +1580,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; @@ -1431,7 +1595,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 +1603,21 @@ 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]; + 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, 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 (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 (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); + } } /* set nonce @@ -2151,7 +2320,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_key_exchange_ecdhe_ecdsa(config->cipher_index)); data += DTLS_HS_LENGTH; data_length -= DTLS_HS_LENGTH; @@ -2195,16 +2364,26 @@ 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; - int ecdsa; uint8 extension_size; - dtls_handshake_parameters_t *handshake = peer->handshake_params; - - ecdsa = is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(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) + + (handshake->renegotiation_info ? 5 : 0) + (ecdsa ? 5 + 5 + 6 : 0); /* Handshake header */ @@ -2222,9 +2401,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 */ @@ -2238,7 +2417,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); @@ -2249,7 +2428,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); @@ -2260,7 +2439,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); @@ -2277,7 +2456,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); @@ -2286,6 +2465,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, 5 bytes */ + 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, @@ -2528,6 +2720,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); @@ -2535,9 +2728,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_8(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); @@ -2560,8 +2754,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) && - is_ecdsa_client_auth_supported(ctx)) { + if (is_ecdsa_client_auth_supported(ctx)) { res = dtls_send_server_certificate_request(ctx, peer); if (res < 0) { @@ -2573,7 +2766,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 (DTLS_KEY_EXCHANGE_PSK == key_exchange_algorithm) { unsigned char psk_hint[DTLS_PSK_MAX_CLIENT_IDENTITY_LEN]; int len; @@ -2621,15 +2814,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_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, @@ -2657,52 +2853,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_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_8: - /* fall through to default */ -#endif /* !DTLS_PSK */ - -#ifndef DTLS_ECC - 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); } @@ -2791,21 +2980,19 @@ static int dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, uint8 cookie[], size_t cookie_length) { uint8 buf[DTLS_CH_LENGTH_MAX]; - uint8 *p = buf; - uint8_t cipher_size; - uint8_t extension_size; - int psk; - int ecdsa; + uint8_t *p = buf; + uint8_t *p_cipher_suites_size = NULL; + uint8_t index = 0; + uint8_t cipher_suites_size = 0; + uint8_t extension_size = 4; /* extended master secret extension */ +#ifdef DTLS_ECC + uint8_t ecdsa = 0; +#endif dtls_handshake_parameters_t *handshake = peer->handshake_params; - psk = is_psk_supported(ctx); - ecdsa = is_ecdsa_supported(ctx, 1); - - cipher_size = 2 + ((ecdsa) ? 2 : 0) + ((psk) ? 2 : 0); - extension_size = 4 + ((ecdsa) ? 6 + 6 + 8 + 6 + 8: 0); - - if (cipher_size == 0) { - dtls_crit("no cipher callbacks implemented\n"); + handshake->user_parameters = default_user_parameters; + if (ctx->h->get_user_parameters != NULL) { + ctx->h->get_user_parameters(ctx, &peer->session, &(handshake->user_parameters)); } dtls_int_to_uint16(p, DTLS_VERSION); @@ -2836,18 +3023,52 @@ dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer, p += cookie_length; } + /* keep pointer to size of cipher suites */ + p_cipher_suites_size = p; + /* skip size of cipher suites field */ + p += sizeof(uint16); + /* add known cipher(s) */ - dtls_int_to_uint16(p, cipher_size - 2); + for (index = 0; handshake->user_parameters.cipher_suites[index] != TLS_NULL_WITH_NULL_NULL; ++index) { + dtls_cipher_t code = handshake->user_parameters.cipher_suites[index]; + dtls_cipher_index_t cipher_index = get_cipher_index(handshake->user_parameters.cipher_suites, code); + if (known_cipher(ctx, cipher_index, 1)) { + dtls_int_to_uint16(p, code); + p += sizeof(uint16); +#ifdef DTLS_ECC + ecdsa = ecdsa || is_key_exchange_ecdhe_ecdsa(cipher_index); +#endif /* DTLS_ECC */ + } + /* ignore not supported cipher-suite + credentials callback is missing */ + } + + 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); + } + + /* 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); +#ifdef DTLS_ECC if (ecdsa) { - dtls_int_to_uint16(p, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8); - p += sizeof(uint16); - } - if (psk) { - dtls_int_to_uint16(p, TLS_PSK_WITH_AES_128_CCM_8); - p += sizeof(uint16); + /* + * 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); @@ -2860,6 +3081,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); @@ -2942,6 +3164,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); @@ -3016,15 +3240,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 = dtls_uint16_to_int(data); - if (!known_cipher(ctx, handshake->cipher, 1)) { - dtls_alert("unsupported cipher 0x%02x 0x%02x\n", - data[0], data[1]); + /* Check if the cipher suite selected by the server + * is in our list of cipher suites. */ + handshake->cipher_index = get_cipher_index(handshake->user_parameters.cipher_suites, dtls_uint16_to_int(data)); + + if (!known_cipher(ctx, handshake->cipher_index, 1)) { + dtls_alert("unsupported cipher 0x%02x 0x%02x\n", data[0], data[1]); + handshake->cipher_index = DTLS_CIPHER_INDEX_NULL; return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY); } + data += sizeof(uint16); data_length -= sizeof(uint16); @@ -3078,7 +3303,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_key_exchange_ecdhe_ecdsa(config->cipher_index)); data += DTLS_HS_LENGTH; @@ -3129,7 +3354,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_key_exchange_ecdhe_ecdsa(config->cipher_index)); data += DTLS_HS_LENGTH; data_length -= DTLS_HS_LENGTH; @@ -3216,7 +3441,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_key_exchange_psk(config->cipher_index)); data += DTLS_HS_LENGTH; @@ -3260,7 +3485,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_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index)); data += DTLS_HS_LENGTH; @@ -3424,10 +3649,10 @@ 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 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 +3660,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]; + 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, 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 +3690,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 +3744,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_key_exchange_ecdhe_ecdsa(peer->handshake_params->cipher_index) && is_ecdsa_client_auth_supported(ctx)) peer->state = DTLS_STATE_WAIT_CLIENTCERTIFICATE; else @@ -3533,6 +3759,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 @@ -3576,7 +3803,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_8(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; @@ -3614,7 +3842,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 (key_exchange_algorithm == DTLS_KEY_EXCHANGE_ECDHE_ECDSA) { if (state != DTLS_STATE_WAIT_SERVERKEYEXCHANGE) { return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE); } @@ -3623,7 +3851,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 (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); } @@ -3662,7 +3890,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)) { + 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; @@ -3738,8 +3966,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) && - 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; diff --git a/dtls.h b/dtls.h index 82cae647..4a6528b5 100644 --- a/dtls.h +++ b/dtls.h @@ -119,6 +119,16 @@ 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 parameters The pointer to user parameters. + * The user parameters are initialized with the default values. + */ + void (*get_user_parameters)(struct dtls_context_t *ctx, session_t *session, dtls_user_parameters_t *parameters); + #ifdef DTLS_PSK /** * Called during handshake to get information related to the diff --git a/global.h b/global.h index 841392d9..d915aed1 100644 --- a/global.h +++ b/global.h @@ -74,8 +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_8 = 0xC0AE /**< see RFC 7251 */ + 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; /** Known compression suites.*/ @@ -90,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 */ diff --git a/tests/dtls-client.c b/tests/dtls-client.c index 74baeed7..6cc94073 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 @@ -188,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; } @@ -214,6 +215,32 @@ send_to_peer(struct dtls_context_t *ctx, &session->addr.sa, session->size); } +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_user_parameters(struct dtls_context_t *ctx, + session_t *session, dtls_user_parameters_t *user_parameters) { + (void) ctx; + (void) session; + user_parameters->force_extended_master_secret = force_extended_master_secret; + 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 dtls_handle_read(struct dtls_context_t *ctx) { int fd; @@ -308,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] 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] 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" @@ -318,13 +345,17 @@ 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"); } static dtls_handler_t cb = { .write = send_to_peer, .read = read_from_peer, + .get_user_parameters = get_user_parameters, .event = NULL, #ifdef DTLS_PSK .get_psk_info = get_psk_info, @@ -344,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; @@ -370,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:" PSK_OPTIONS)) != -1) { + while ((opt = getopt(argc, argv, "rep:o:v:c:" PSK_OPTIONS)) != -1) { switch (opt) { #ifdef DTLS_PSK case 'i' : @@ -409,6 +442,15 @@ main(int argc, char **argv) { case 'v' : log_level = strtol(optarg, NULL, 10); break; + 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); @@ -508,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) { @@ -517,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"); @@ -537,6 +583,7 @@ main(int argc, char **argv) { len = 0; } else { try_send(dtls_context, &dst, len, buf); + len = 0; } } } @@ -546,3 +593,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 6e0d5894..fb619a3f 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 @@ -136,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, @@ -148,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); } @@ -161,6 +168,32 @@ send_to_peer(struct dtls_context_t *ctx, &session->addr.sa, session->size); } +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_user_parameters(struct dtls_context_t *ctx, + session_t *session, dtls_user_parameters_t *user_parameters) { + (void) ctx; + (void) session; + user_parameters->force_extended_master_secret = force_extended_master_secret; + 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 dtls_handle_read(struct dtls_context_t *ctx) { int *fd; @@ -250,16 +283,20 @@ 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] [-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"); } static dtls_handler_t cb = { .write = send_to_peer, .read = read_from_peer, + .get_user_parameters = get_user_parameters, .event = NULL, #ifdef DTLS_PSK .get_psk_info = get_psk_info, @@ -294,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:")) != -1) { + while ((opt = getopt(argc, argv, "reA:p:v:c:")) != -1) { switch (opt) { case 'A' : if (resolve_address(optarg, (struct sockaddr *)&listen_addr) < 0) { @@ -308,6 +345,15 @@ main(int argc, char **argv) { case 'v' : log_level = strtol(optarg, NULL, 10); break; + 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); @@ -406,3 +452,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 new file mode 100644 index 00000000..915e6969 --- /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 (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; + } + } + return TLS_NULL_WITH_NULL_NULL; +} + +static void add_cipher_suite(dtls_cipher_t cipher) { + for (size_t index=0; index < ARRAY_LENGTH - 1; ++index) { + if (ciphers_table[index] == cipher) { + return; + } + if (ciphers_table[index] == TLS_NULL_WITH_NULL_NULL) { + ciphers_table[index] = cipher; + ciphers_table[index + 1] = TLS_NULL_WITH_NULL_NULL; + return; + } + } +} + +const dtls_cipher_t* +init_cipher_suites(const char* arg) { + while (arg) { + dtls_cipher_t cipher = find_cipher_suite(arg); + if (cipher != TLS_NULL_WITH_NULL_NULL) { + add_cipher_suite(cipher); + } + arg = strchr(arg, SEP); + if (arg) { + ++arg; + } + } + return ciphers_table; +} + +void +cipher_suites_usage(FILE* file, const char* head) { + fprintf(file, "%s-c ciphers\tlist of cipher-suites separated by ':'\n", head); + fprintf(file, "%s\t\t(default is ", 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_ */