diff --git a/crypto/bytestring/bytestring_test.c b/crypto/bytestring/bytestring_test.c index 28dfd3a4..5ef3259f 100644 --- a/crypto/bytestring/bytestring_test.c +++ b/crypto/bytestring/bytestring_test.c @@ -222,6 +222,43 @@ static int test_get_asn1(void) { return 1; } +static int test_get_optional_asn1_bool(void) { + CBS data; + int val; + + static const uint8_t kTrue[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0xff}; + static const uint8_t kFalse[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x00}; + static const uint8_t kInvalid[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x01}; + + CBS_init(&data, NULL, 0); + val = 2; + if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 0) || + val != 0) { + return 0; + } + + CBS_init(&data, kTrue, sizeof(kTrue)); + val = 2; + if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 0) || + val != 1) { + return 0; + } + + CBS_init(&data, kFalse, sizeof(kFalse)); + val = 2; + if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 1) || + val != 0) { + return 0; + } + + CBS_init(&data, kInvalid, sizeof(kInvalid)); + if (CBS_get_optional_asn1_bool(&data, &val, 0x0a, 1)) { + return 0; + } + + return 1; +} + static int test_cbb_basic(void) { static const uint8_t kExpected[] = {1, 2, 3, 4, 5, 6, 7, 8}; uint8_t *buf; @@ -604,7 +641,8 @@ int main(void) { !test_cbb_prefixed() || !test_cbb_asn1() || !test_ber_convert() || - !test_asn1_uint64()) { + !test_asn1_uint64() || + !test_get_optional_asn1_bool()) { return 1; } diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c index ae661208..b4177162 100644 --- a/crypto/bytestring/cbs.c +++ b/crypto/bytestring/cbs.c @@ -356,3 +356,33 @@ int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, unsigned tag, } return 1; } + +int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag, + int default_value) { + CBS child, child2; + int present; + if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) { + return 0; + } + if (present) { + uint8_t boolean; + + if (!CBS_get_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || + CBS_len(&child2) != 1 || + CBS_len(&child) != 0) { + return 0; + } + + boolean = CBS_data(&child2)[0]; + if (boolean == 0) { + *out = 0; + } else if (boolean == 0xff) { + *out = 1; + } else { + return 0; + } + } else { + *out = default_value; + } + return 1; +} diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h index 9d2b4a90..d5e6cf92 100644 --- a/include/openssl/bytestring.h +++ b/include/openssl/bytestring.h @@ -193,6 +193,14 @@ OPENSSL_EXPORT int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, unsigned tag, uint64_t default_value); +/* CBS_get_optional_asn1_bool gets an optional, explicitly-tagged BOOLEAN from + * |cbs|. If present, it sets |*out| to either zero or one, based on the + * boolean. Otherwise, it sets |*out| to |default_value|. It returns one on + * success, whether or not the element was present, and zero on decode + * failure. */ +OPENSSL_EXPORT int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag, + int default_value); + /* CRYPTO ByteBuilder. * diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 10fdb1fb..858d2fda 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -432,6 +432,11 @@ struct ssl_session_st * resumption. */ unsigned char original_handshake_hash[EVP_MAX_MD_SIZE]; unsigned int original_handshake_hash_len; + + /* extended_master_secret is true if the master secret in this session + * was generated using EMS and thus isn't vulnerable to the Triple + * Handshake attack. */ + char extended_master_secret; }; #endif diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h index 2a201aa8..3d627638 100644 --- a/include/openssl/ssl3.h +++ b/include/openssl/ssl3.h @@ -485,6 +485,15 @@ typedef struct ssl3_state_st * this extension to the client. */ uint16_t *peer_ellipticcurvelist; size_t peer_ellipticcurvelist_length; + + /* extended_master_secret indicates whether the extended master + * secret computation is used in this handshake. Note that this + * is different from whether it was used for the current + * session. If this is a resumption handshake then EMS might be + * negotiated in the client and server hello messages, but it + * doesn't matter if the session that's being resumed didn't + * use it to create the master secret initially. */ + char extended_master_secret; } tmp; /* Connection binding to prevent renegotiation attacks */ diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 08ad8e89..d2682ddf 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -240,6 +240,9 @@ extern "C" { */ #define TLSEXT_TYPE_padding 21 +/* https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 */ +#define TLSEXT_TYPE_extended_master_secret 23 + /* ExtensionType value from RFC4507 */ #define TLSEXT_TYPE_session_ticket 35 @@ -704,6 +707,8 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB,(void (*)(void))cb) #define TLS_MD_IV_BLOCK_CONST_SIZE 8 #define TLS_MD_MASTER_SECRET_CONST "master secret" #define TLS_MD_MASTER_SECRET_CONST_SIZE 13 +#define TLS_MD_EXTENDED_MASTER_SECRET_CONST "extended master secret" +#define TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE 22 /* TLS Session Ticket extension struct */ diff --git a/ssl/d1_both.c b/ssl/d1_both.c index 2d944d84..8b225e58 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c @@ -229,7 +229,7 @@ dtls1_hm_fragment_free(hm_fragment *frag) } /* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC) */ -int dtls1_do_write(SSL *s, int type) +int dtls1_do_write(SSL *s, int type, enum should_add_to_finished_hash should_add_to_finished_hash) { int ret; int curr_mtu; @@ -365,7 +365,8 @@ int dtls1_do_write(SSL *s, int type) * message got sent. but why would this happen? */ assert(len == (unsigned int)ret); - if (type == SSL3_RT_HANDSHAKE && ! s->d1->retransmitting) + if (type == SSL3_RT_HANDSHAKE && !s->d1->retransmitting && + should_add_to_finished_hash == add_to_finished_hash) { /* should not be done for 'Hello Request's, but in that case * we'll ignore the result anyway */ @@ -967,7 +968,7 @@ int dtls1_send_change_cipher_spec(SSL *s, int a, int b) } /* SSL3_ST_CW_CHANGE_B */ - return(dtls1_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC)); + return(dtls1_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC, dont_add_to_finished_hash)); } int dtls1_read_failed(SSL *s, int code) @@ -1181,7 +1182,7 @@ dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, } ret = dtls1_do_write(s, frag->msg_header.is_ccs ? - SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE); + SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE, add_to_finished_hash); /* restore current state */ s->enc_write_ctx = saved_state.enc_write_ctx; diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index 96ce4960..650d8e78 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -74,8 +74,9 @@ static void get_current_time(OPENSSL_timeval *t); static OPENSSL_timeval* dtls1_get_timeout(SSL *s, OPENSSL_timeval* timeleft); static void dtls1_set_handshake_header(SSL *s, int type, unsigned long len); -static int dtls1_handshake_write(SSL *s); +static int dtls1_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash); int dtls1_listen(SSL *s, struct sockaddr *client); +static void dtls1_add_to_finished_hash(SSL *s); SSL3_ENC_METHOD DTLSv1_enc_data={ tls1_enc, @@ -93,7 +94,8 @@ SSL3_ENC_METHOD DTLSv1_enc_data={ SSL_ENC_FLAG_DTLS|SSL_ENC_FLAG_EXPLICIT_IV, DTLS1_HM_HEADER_LENGTH, dtls1_set_handshake_header, - dtls1_handshake_write + dtls1_handshake_write, + dtls1_add_to_finished_hash, }; SSL3_ENC_METHOD DTLSv1_2_enc_data={ @@ -113,7 +115,8 @@ SSL3_ENC_METHOD DTLSv1_2_enc_data={ |SSL_ENC_FLAG_SHA256_PRF|SSL_ENC_FLAG_TLS1_2_CIPHERS, DTLS1_HM_HEADER_LENGTH, dtls1_set_handshake_header, - dtls1_handshake_write + dtls1_handshake_write, + dtls1_add_to_finished_hash, }; int dtls1_new(SSL *s) @@ -502,7 +505,25 @@ static void dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) dtls1_buffer_message(s, 0); } -static int dtls1_handshake_write(SSL *s) +static int dtls1_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash) { - return dtls1_do_write(s, SSL3_RT_HANDSHAKE); + return dtls1_do_write(s, SSL3_RT_HANDSHAKE, should_add_to_finished_hash); + } + +static void dtls1_add_to_finished_hash(SSL *s) + { + uint8_t *record = (uint8_t *) &s->init_buf->data[s->init_off]; + const struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr; + uint8_t serialised_header[DTLS1_HM_HEADER_LENGTH]; + uint8_t *p = serialised_header; + + /* Construct the message header as if it were a single fragment. */ + *p++ = msg_hdr->type; + l2n3(msg_hdr->msg_len, p); + s2n (msg_hdr->seq, p); + l2n3(0, p); + l2n3(msg_hdr->msg_len, p); + ssl3_finish_mac(s, serialised_header, sizeof(serialised_header)); + ssl3_finish_mac(s, record + DTLS1_HM_HEADER_LENGTH, + s->init_num - DTLS1_HM_HEADER_LENGTH); } diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c index 79da484f..e1c56167 100644 --- a/ssl/d1_srvr.c +++ b/ssl/d1_srvr.c @@ -687,5 +687,5 @@ int dtls1_send_hello_verify_request(SSL *s) } /* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */ - return(dtls1_do_write(s,SSL3_RT_HANDSHAKE)); + return(dtls1_do_write(s,SSL3_RT_HANDSHAKE, add_to_finished_hash)); } diff --git a/ssl/s3_both.c b/ssl/s3_both.c index 6604fc7c..bbdeadbb 100644 --- a/ssl/s3_both.c +++ b/ssl/s3_both.c @@ -127,17 +127,19 @@ #include "ssl_locl.h" /* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC) */ -int ssl3_do_write(SSL *s, int type) +int ssl3_do_write(SSL *s, int type, enum should_add_to_finished_hash should_add_to_finished_hash) { int ret; ret=ssl3_write_bytes(s,type,&s->init_buf->data[s->init_off], s->init_num); if (ret < 0) return(-1); - if (type == SSL3_RT_HANDSHAKE) + if (type == SSL3_RT_HANDSHAKE && should_add_to_finished_hash == add_to_finished_hash) + { /* should not be done for 'Hello Request's, but in that case * we'll ignore the result anyway */ ssl3_finish_mac(s,(unsigned char *)&s->init_buf->data[s->init_off],ret); + } if (ret == s->init_num) { @@ -320,7 +322,7 @@ int ssl3_send_change_cipher_spec(SSL *s, int a, int b) } /* SSL3_ST_CW_CHANGE_B */ - return(ssl3_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC)); + return(ssl3_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC, dont_add_to_finished_hash)); } unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 6574f5a9..0321fd12 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -982,7 +982,7 @@ int ssl3_get_server_hello(SSL *s) /* Don't digest cached records if no sigalgs: we may need them for * client authentication. */ - if (!SSL_USE_SIGALGS(s) && !ssl3_digest_cached_records(s)) + if (!SSL_USE_SIGALGS(s) && !ssl3_digest_cached_records(s, free_handshake_buffer)) goto f_err; /* Only the NULL compression algorithm is supported. */ @@ -1637,7 +1637,7 @@ int ssl3_get_certificate_request(SSL *s) */ if (s->s3->handshake_buffer) { - if (!ssl3_digest_cached_records(s)) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) goto err; } return(1); @@ -1909,6 +1909,8 @@ int ssl3_send_client_key_exchange(SSL *s) BN_CTX * bn_ctx = NULL; unsigned int psk_len = 0; unsigned char psk[PSK_MAX_PSK_LEN]; + uint8_t *pms = NULL; + size_t pms_len = 0; if (s->state == SSL3_ST_CW_KEY_EXCH_A) { @@ -1921,9 +1923,6 @@ int ssl3_send_client_key_exchange(SSL *s) { char identity[PSK_MAX_IDENTITY_LEN + 1]; size_t identity_len; - unsigned char *t = NULL; - unsigned char pre_ms[PSK_MAX_PSK_LEN*2+4]; - unsigned int pre_ms_len = 0; int psk_err = 1; n = 0; @@ -1955,21 +1954,26 @@ int ssl3_send_client_key_exchange(SSL *s) if (!(alg_k & SSL_kEECDH)) { + uint8_t *t; + /* Create the shared secret now if we're not using ECDHE-PSK. * TODO(davidben): Refactor this logic similarly * to ssl3_get_client_key_exchange. */ - pre_ms_len = 2+psk_len+2+psk_len; - t = pre_ms; + pms_len = 2+psk_len+2+psk_len; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) + { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE); + goto psk_err; + } + + t = pms; s2n(psk_len, t); memset(t, 0, psk_len); - t+=psk_len; + t += psk_len; s2n(psk_len, t); memcpy(t, psk, psk_len); - s->session->master_key_length = - s->method->ssl3_enc->generate_master_secret(s, - s->session->master_key, - pre_ms, pre_ms_len); s2n(identity_len, p); memcpy(p, identity, identity_len); n = 2 + identity_len; @@ -1986,7 +1990,6 @@ int ssl3_send_client_key_exchange(SSL *s) psk_err = 0; psk_err: OPENSSL_cleanse(identity, sizeof(identity)); - OPENSSL_cleanse(pre_ms, sizeof(pre_ms)); if (psk_err != 0) { ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); @@ -1997,7 +2000,14 @@ int ssl3_send_client_key_exchange(SSL *s) if (alg_k & SSL_kRSA) { RSA *rsa; - unsigned char tmp_buf[SSL_MAX_MASTER_KEY_LENGTH]; + + pms_len = SSL_MAX_MASTER_KEY_LENGTH; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) + { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE); + goto err; + } if (s->session->sess_cert == NULL) { @@ -2022,19 +2032,19 @@ int ssl3_send_client_key_exchange(SSL *s) EVP_PKEY_free(pkey); } - tmp_buf[0]=s->client_version>>8; - tmp_buf[1]=s->client_version&0xff; - if (RAND_bytes(&(tmp_buf[2]),sizeof tmp_buf-2) <= 0) + pms[0]=s->client_version>>8; + pms[1]=s->client_version&0xff; + if (RAND_bytes(&pms[2],SSL_MAX_MASTER_KEY_LENGTH-2) <= 0) goto err; - s->session->master_key_length=sizeof tmp_buf; + s->session->master_key_length=SSL_MAX_MASTER_KEY_LENGTH; q=p; /* Fix buf for TLS and beyond */ if (s->version > SSL3_VERSION) p+=2; - n=RSA_public_encrypt(sizeof tmp_buf, - tmp_buf,p,rsa,RSA_PKCS1_PADDING); + n=RSA_public_encrypt(SSL_MAX_MASTER_KEY_LENGTH, + pms,p,rsa,RSA_PKCS1_PADDING); if (n <= 0) { OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, SSL_R_BAD_RSA_ENCRYPT); @@ -2043,7 +2053,7 @@ int ssl3_send_client_key_exchange(SSL *s) /* Log the premaster secret, if logging is enabled. */ if (!ssl_ctx_log_rsa_client_key_exchange(s->ctx, - p, n, tmp_buf, sizeof(tmp_buf))) + p, n, pms, SSL_MAX_MASTER_KEY_LENGTH)) { goto err; } @@ -2054,12 +2064,6 @@ int ssl3_send_client_key_exchange(SSL *s) s2n(n,q); n+=2; } - - s->session->master_key_length= - s->method->ssl3_enc->generate_master_secret(s, - s->session->master_key, - tmp_buf,sizeof tmp_buf); - OPENSSL_cleanse(tmp_buf,sizeof tmp_buf); } else if (alg_k & SSL_kEDH) { @@ -2093,23 +2097,23 @@ int ssl3_send_client_key_exchange(SSL *s) goto err; } - /* use the 'p' output buffer for the DH key, but - * make sure to clear it out afterwards */ + pms_len = DH_size(dh_clnt); + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) + { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE); + DH_free(dh_clnt); + goto err; + } - n=DH_compute_key(p,dh_srvr->pub_key,dh_clnt); + n=DH_compute_key(pms,dh_srvr->pub_key,dh_clnt); if (n <= 0) { OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); DH_free(dh_clnt); goto err; } - - /* generate master key from the result */ - s->session->master_key_length= - s->method->ssl3_enc->generate_master_secret(s, - s->session->master_key,p,n); - /* clean up */ - memset(p,0,n); + pms_len = n; /* send off the data */ n=BN_num_bytes(dh_clnt->pub_key); @@ -2118,8 +2122,6 @@ int ssl3_send_client_key_exchange(SSL *s) n+=2; DH_free(dh_clnt); - - /* perhaps clean things up a bit EAY EAY EAY EAY*/ } else if (alg_k & SSL_kEECDH) @@ -2127,9 +2129,6 @@ int ssl3_send_client_key_exchange(SSL *s) const EC_GROUP *srvr_group = NULL; EC_KEY *tkey; int field_size = 0; - unsigned char *pre_ms; - unsigned char *t; - unsigned int pre_ms_len; unsigned int i; if (s->session->sess_cert == NULL) @@ -2193,32 +2192,35 @@ int ssl3_send_client_key_exchange(SSL *s) /* ECDHE PSK ciphersuites from RFC 5489 */ if ((alg_a & SSL_aPSK) && psk_len != 0) { - pre_ms_len = 2+psk_len+2+n; - pre_ms = OPENSSL_malloc(pre_ms_len); - if (pre_ms == NULL) + uint8_t *t; + + pms_len = 2+psk_len+2+n; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) { OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE); goto err; } - memset(pre_ms, 0, pre_ms_len); - t = pre_ms; + + t = pms; + memset(t, 0, pms_len); s2n(psk_len, t); memcpy(t, psk, psk_len); t += psk_len; s2n(n, t); memcpy(t, p, n); - s->session->master_key_length = s->method->ssl3_enc \ - -> generate_master_secret(s, - s->session->master_key, pre_ms, pre_ms_len); - OPENSSL_cleanse(pre_ms, pre_ms_len); - OPENSSL_free(pre_ms); } if (!(alg_a & SSL_aPSK)) { /* generate master key from the result */ - s->session->master_key_length = s->method->ssl3_enc \ - -> generate_master_secret(s, - s->session->master_key, p, n); + pms_len = n; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) + { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE); + goto err; + } + memcpy(pms, p, n); } memset(p, 0, n); /* clean up */ @@ -2283,17 +2285,37 @@ int ssl3_send_client_key_exchange(SSL *s) ssl_set_handshake_header(s, SSL3_MT_CLIENT_KEY_EXCHANGE, n); s->state=SSL3_ST_CW_KEY_EXCH_B; + + /* The message must be added to the finished hash before + * calculating the master secret. */ + s->method->ssl3_enc->add_to_finished_hash(s); + + s->session->master_key_length = + s->method->ssl3_enc->generate_master_secret(s, + s->session->master_key, + pms, pms_len); + if (s->session->master_key_length == 0) + { + goto err; + } + s->session->extended_master_secret = s->s3->tmp.extended_master_secret; + OPENSSL_cleanse(pms, pms_len); + OPENSSL_free(pms); } /* SSL3_ST_CW_KEY_EXCH_B */ - return ssl_do_write(s); + /* The message has already been added to the finished hash. */ + return s->method->ssl3_enc->do_write(s, dont_add_to_finished_hash); + err: BN_CTX_free(bn_ctx); if (encodedPoint != NULL) OPENSSL_free(encodedPoint); if (clnt_ecdh != NULL) EC_KEY_free(clnt_ecdh); EVP_PKEY_free(srvr_pub_pkey); - return(-1); + if (pms) + OPENSSL_free(pms); + return -1; } int ssl3_send_cert_verify(SSL *s) @@ -2332,7 +2354,7 @@ int ssl3_send_cert_verify(SSL *s) goto err; /* The handshake buffer is no longer necessary. */ - if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s)) + if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer)) goto err; /* Sign the digest. */ @@ -2583,7 +2605,7 @@ int ssl3_send_next_proto(SSL *s) s->init_off = 0; } - return ssl3_do_write(s, SSL3_RT_HANDSHAKE); + return ssl3_do_write(s, SSL3_RT_HANDSHAKE, add_to_finished_hash); } @@ -2597,7 +2619,7 @@ int ssl3_send_channel_id(SSL *s) unsigned char *public_key = NULL, *derp, *der_sig = NULL; if (s->state != SSL3_ST_CW_CHANNEL_ID_A) - return ssl3_do_write(s, SSL3_RT_HANDSHAKE); + return ssl3_do_write(s, SSL3_RT_HANDSHAKE, add_to_finished_hash); if (!s->tlsext_channel_id_private && s->ctx->channel_id_cb) { @@ -2700,7 +2722,7 @@ int ssl3_send_channel_id(SSL *s) s->init_num = 4 + 2 + 2 + TLSEXT_CHANNEL_ID_SIZE; s->init_off = 0; - ret = ssl3_do_write(s, SSL3_RT_HANDSHAKE); + ret = ssl3_do_write(s, SSL3_RT_HANDSHAKE, add_to_finished_hash); err: EVP_MD_CTX_cleanup(&md_ctx); diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index b0ca5076..fc94a942 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -510,7 +510,7 @@ void ssl3_finish_mac(SSL *s, const unsigned char *buf, int len) } } -int ssl3_digest_cached_records(SSL *s) +int ssl3_digest_cached_records(SSL *s, enum should_free_handshake_buffer_t should_free_handshake_buffer) { int i; long mask; @@ -542,9 +542,13 @@ int ssl3_digest_cached_records(SSL *s) s->s3->handshake_dgst[i]=NULL; } } - /* Free handshake_buffer BIO */ - BIO_free(s->s3->handshake_buffer); - s->s3->handshake_buffer = NULL; + + if (should_free_handshake_buffer == free_handshake_buffer) + { + /* Free handshake_buffer BIO */ + BIO_free(s->s3->handshake_buffer); + s->s3->handshake_buffer = NULL; + } return 1; } @@ -581,7 +585,7 @@ static int ssl3_handshake_mac(SSL *s, int md_nid, EVP_MD_CTX ctx,*d=NULL; if (s->s3->handshake_buffer) - if (!ssl3_digest_cached_records(s)) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) return 0; /* Search for digest of specified type in the handshake_dgst diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 215b3f6d..b0861545 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -942,7 +942,8 @@ SSL3_ENC_METHOD SSLv3_enc_data={ 0, SSL3_HM_HEADER_LENGTH, ssl3_set_handshake_header, - ssl3_handshake_write + ssl3_handshake_write, + ssl3_add_to_finished_hash, }; int ssl3_num_ciphers(void) @@ -975,9 +976,14 @@ void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len) s->init_off = 0; } -int ssl3_handshake_write(SSL *s) +int ssl3_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash) { - return ssl3_do_write(s, SSL3_RT_HANDSHAKE); + return ssl3_do_write(s, SSL3_RT_HANDSHAKE, should_add_to_finished_hash); + } + +void ssl3_add_to_finished_hash(SSL *s) + { + ssl3_finish_mac(s, (uint8_t*) s->init_buf->data, s->init_num); } int ssl3_new(SSL *s) diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 0fac30ea..edcdc03b 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -444,7 +444,7 @@ int ssl3_accept(SSL *s) s->s3->tmp.cert_request=0; s->state=SSL3_ST_SW_SRVR_DONE_A; if (s->s3->handshake_buffer) - if (!ssl3_digest_cached_records(s)) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) return -1; } else @@ -1144,7 +1144,7 @@ int ssl3_get_client_hello(SSL *s) if (!SSL_USE_SIGALGS(s) || !(s->verify_mode & SSL_VERIFY_PEER)) { - if (!ssl3_digest_cached_records(s)) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) goto f_err; } @@ -2203,6 +2203,9 @@ int ssl3_get_client_key_exchange(SSL *s) s->session->master_key_length = s->method->ssl3_enc ->generate_master_secret(s, s->session->master_key, premaster_secret, premaster_secret_len); + if (s->session->master_key_length == 0) + goto err; + s->session->extended_master_secret = s->s3->tmp.extended_master_secret; OPENSSL_cleanse(premaster_secret, premaster_secret_len); OPENSSL_free(premaster_secret); @@ -2243,7 +2246,7 @@ int ssl3_get_cert_verify(SSL *s) * client certificate. */ if (peer == NULL) { - if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s)) + if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer)) return -1; return 1; } @@ -2284,7 +2287,7 @@ int ssl3_get_cert_verify(SSL *s) /* The handshake buffer is no longer necessary, and we may hash the * current message.*/ - if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s)) + if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer)) goto err; ssl3_hash_current_message(s); @@ -2453,7 +2456,7 @@ int ssl3_get_client_certificate(SSL *s) goto f_err; } /* No client certificate so digest cached records */ - if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s)) + if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer)) { al=SSL_AD_INTERNAL_ERROR; goto f_err; diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index 358f20f9..53dc9962 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c @@ -117,6 +117,7 @@ * -- contents of SCT extension * ocspResponse [16] OCTET STRING OPTIONAL, * -- stapled OCSP response from the server + * extendedMasterSecret [17] BOOLEAN OPTIONAL, * } * * Note: When the relevant features were #ifdef'd out, support for @@ -151,6 +152,8 @@ static const int kSignedCertTimestampListTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 15; static const int kOCSPResponseTag = CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 16; +static const int kExtendedMasterSecretTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17; int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp) { CBB cbb, session, child, child2; @@ -321,6 +324,15 @@ int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp) { } } + if (in->extended_master_secret) { + if (!CBB_add_asn1(&session, &child, kExtendedMasterSecretTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || + !CBB_add_u8(&child2, 0xff)) { + OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + } + if (!CBB_finish(&cbb, &out, &len)) { OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); goto err; @@ -399,7 +411,7 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { SSL_SESSION *ret = NULL; CBS cbs, session, cipher, session_id, master_key; CBS key_arg, peer, sid_ctx, peer_sha256, original_handshake_hash; - int has_key_arg, has_peer, has_peer_sha256; + int has_key_arg, has_peer, has_peer_sha256, extended_master_secret; uint64_t version, ssl_version; uint64_t session_time, timeout, verify_result, ticket_lifetime_hint; @@ -464,6 +476,13 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { kOCSPResponseTag)) { goto err; } + if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, + kExtendedMasterSecretTag, + 0 /* default to false */)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->extended_master_secret = extended_master_secret; /* Ignore |version|. The structure version number is ignored. */ diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 3e682a14..c214b914 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -568,6 +568,11 @@ struct tls_sigalgs_st #define FP_ICC (int (*)(const void *,const void *)) +enum should_add_to_finished_hash { + add_to_finished_hash, + dont_add_to_finished_hash, +}; + /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff * It is a bit of a mess of functions, but hell, think of it as * an opaque structure :-) */ @@ -597,7 +602,9 @@ typedef struct ssl3_enc_method /* Set the handshake header */ void (*set_handshake_header)(SSL *s, int type, unsigned long len); /* Write out handshake message */ - int (*do_write)(SSL *s); + int (*do_write)(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash); + /* Add the current handshake message to the finished hash. */ + void (*add_to_finished_hash)(SSL *s); } SSL3_ENC_METHOD; #define SSL_HM_HEADER_LENGTH(s) s->method->ssl3_enc->hhlen @@ -605,7 +612,7 @@ typedef struct ssl3_enc_method (((unsigned char *)s->init_buf->data) + s->method->ssl3_enc->hhlen) #define ssl_set_handshake_header(s, htype, len) \ s->method->ssl3_enc->set_handshake_header(s, htype, len) -#define ssl_do_write(s) s->method->ssl3_enc->do_write(s) +#define ssl_do_write(s) s->method->ssl3_enc->do_write(s, add_to_finished_hash) /* Values for enc_flags */ @@ -823,7 +830,7 @@ int ssl3_setup_key_block(SSL *s); int ssl3_send_change_cipher_spec(SSL *s,int state_a,int state_b); int ssl3_change_cipher_state(SSL *s,int which); void ssl3_cleanup_key_block(SSL *s); -int ssl3_do_write(SSL *s,int type); +int ssl3_do_write(SSL *s,int type, enum should_add_to_finished_hash should_add_to_finished_hash); int ssl3_send_alert(SSL *s,int level, int desc); int ssl3_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p, int len); @@ -865,7 +872,13 @@ int ssl3_setup_read_buffer(SSL *s); int ssl3_setup_write_buffer(SSL *s); int ssl3_release_read_buffer(SSL *s); int ssl3_release_write_buffer(SSL *s); -int ssl3_digest_cached_records(SSL *s); + +enum should_free_handshake_buffer_t { + free_handshake_buffer, + dont_free_handshake_buffer, +}; +int ssl3_digest_cached_records(SSL *s, enum should_free_handshake_buffer_t); + int ssl3_new(SSL *s); void ssl3_free(SSL *s); int ssl3_accept(SSL *s); @@ -885,13 +898,14 @@ void ssl3_record_sequence_update(unsigned char *seq); int ssl3_do_change_cipher_spec(SSL *ssl); void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len); -int ssl3_handshake_write(SSL *s); +int ssl3_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash); +void ssl3_add_to_finished_hash(SSL *s); int ssl23_read(SSL *s, void *buf, int len); int ssl23_peek(SSL *s, void *buf, int len); int ssl23_write(SSL *s, const void *buf, int len); -int dtls1_do_write(SSL *s,int type); +int dtls1_do_write(SSL *s,int type, enum should_add_to_finished_hash should_add_to_finished_hash); int ssl3_read_n(SSL *s, int n, int max, int extend); int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek); int ssl3_write_pending(SSL *s, int type, const unsigned char *buf, diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index dd00d0a8..0a4e0880 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -152,8 +152,6 @@ static int tls1_P_hash(const EVP_MD *md, const unsigned char *sec, const void *seed1, int seed1_len, const void *seed2, int seed2_len, const void *seed3, int seed3_len, - const void *seed4, int seed4_len, - const void *seed5, int seed5_len, unsigned char *out, int olen) { int chunk; @@ -182,10 +180,6 @@ static int tls1_P_hash(const EVP_MD *md, const unsigned char *sec, goto err; if (seed3 && !EVP_DigestSignUpdate(&ctx,seed3,seed3_len)) goto err; - if (seed4 && !EVP_DigestSignUpdate(&ctx,seed4,seed4_len)) - goto err; - if (seed5 && !EVP_DigestSignUpdate(&ctx,seed5,seed5_len)) - goto err; A1_len = EVP_MAX_MD_SIZE; if (!EVP_DigestSignFinal(&ctx,A1,&A1_len)) goto err; @@ -205,10 +199,6 @@ static int tls1_P_hash(const EVP_MD *md, const unsigned char *sec, goto err; if (seed3 && !EVP_DigestSignUpdate(&ctx,seed3,seed3_len)) goto err; - if (seed4 && !EVP_DigestSignUpdate(&ctx,seed4,seed4_len)) - goto err; - if (seed5 && !EVP_DigestSignUpdate(&ctx,seed5,seed5_len)) - goto err; if (olen > chunk) { @@ -246,8 +236,6 @@ static int tls1_PRF(long digest_mask, const void *seed1, int seed1_len, const void *seed2, int seed2_len, const void *seed3, int seed3_len, - const void *seed4, int seed4_len, - const void *seed5, int seed5_len, const unsigned char *sec, int slen, unsigned char *out1, unsigned char *out2, int olen) @@ -275,7 +263,7 @@ static int tls1_PRF(long digest_mask, goto err; } if (!tls1_P_hash(md ,S1,len+(slen&1), - seed1,seed1_len,seed2,seed2_len,seed3,seed3_len,seed4,seed4_len,seed5,seed5_len, + seed1,seed1_len,seed2,seed2_len,seed3,seed3_len, out2,olen)) goto err; S1+=len; @@ -298,7 +286,6 @@ static int tls1_generate_key_block(SSL *s, unsigned char *km, TLS_MD_KEY_EXPANSION_CONST,TLS_MD_KEY_EXPANSION_CONST_SIZE, s->s3->server_random,SSL3_RANDOM_SIZE, s->s3->client_random,SSL3_RANDOM_SIZE, - NULL,0,NULL,0, s->session->master_key,s->session->master_key_length, km,tmp,num); #ifdef KSSL_DEBUG @@ -1011,8 +998,8 @@ int tls1_cert_verify_mac(SSL *s, int md_nid, unsigned char *out) EVP_MD_CTX ctx, *d=NULL; int i; - if (s->s3->handshake_buffer) - if (!ssl3_digest_cached_records(s)) + if (s->s3->handshake_buffer) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) return 0; for (i=0;is3->handshake_buffer) - if (!ssl3_digest_cached_records(s)) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) return 0; digests_len = tls1_handshake_digest(s, buf, sizeof(buf)); @@ -1104,7 +1091,7 @@ int tls1_final_finish_mac(SSL *s, } if (!tls1_PRF(ssl_get_algorithm2(s), - str,slen, buf, digests_len, NULL,0, NULL,0, NULL,0, + str,slen, buf, digests_len, NULL,0, s->session->master_key,s->session->master_key_length, out,buf2,sizeof buf2)) err = 1; @@ -1212,22 +1199,57 @@ int tls1_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p, int len) { unsigned char buff[SSL_MAX_MASTER_KEY_LENGTH]; - const void *co = NULL, *so = NULL; - int col = 0, sol = 0; - #ifdef KSSL_DEBUG printf ("tls1_generate_master_secret(%p,%p, %p, %d)\n", s,out, p,len); #endif /* KSSL_DEBUG */ - tls1_PRF(ssl_get_algorithm2(s), - TLS_MD_MASTER_SECRET_CONST,TLS_MD_MASTER_SECRET_CONST_SIZE, - s->s3->client_random,SSL3_RANDOM_SIZE, - co, col, - s->s3->server_random,SSL3_RANDOM_SIZE, - so, sol, - p,len, - s->session->master_key,buff,sizeof buff); + if (s->s3->tmp.extended_master_secret) + { + uint8_t digests[2*EVP_MAX_MD_SIZE]; + int digests_len; + + if (s->s3->handshake_buffer) + { + /* The master secret is based on the handshake hash + * just after sending the ClientKeyExchange. However, + * we might have a client certificate to send, in which + * case we might need different hashes for the + * verification and thus still need the handshake + * buffer around. Keeping both a handshake buffer *and* + * running hashes isn't yet supported so, when it comes + * to calculating the Finished hash, we'll have to hash + * the handshake buffer again. */ + if (!ssl3_digest_cached_records(s, dont_free_handshake_buffer)) + return 0; + } + + digests_len = tls1_handshake_digest(s, digests, sizeof(digests)); + + if (digests_len == -1) + { + return 0; + } + + tls1_PRF(ssl_get_algorithm2(s), + TLS_MD_EXTENDED_MASTER_SECRET_CONST, + TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, + digests, digests_len, + NULL, 0, + p, len, + s->session->master_key, + buff, sizeof(buff)); + } + else + { + tls1_PRF(ssl_get_algorithm2(s), + TLS_MD_MASTER_SECRET_CONST,TLS_MD_MASTER_SECRET_CONST_SIZE, + s->s3->client_random,SSL3_RANDOM_SIZE, + s->s3->server_random,SSL3_RANDOM_SIZE, + p, len, + s->session->master_key,buff,sizeof buff); + } + #ifdef SSL_DEBUG fprintf(stderr, "Premaster Secret:\n"); BIO_dump_fp(stderr, (char *)p, len); @@ -1330,8 +1352,6 @@ int tls1_export_keying_material(SSL *s, unsigned char *out, size_t olen, val, vallen, NULL, 0, NULL, 0, - NULL, 0, - NULL, 0, s->session->master_key,s->session->master_key_length, out,buff,olen); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 4b13cfec..0972515f 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -140,7 +140,8 @@ SSL3_ENC_METHOD TLSv1_enc_data={ 0, SSL3_HM_HEADER_LENGTH, ssl3_set_handshake_header, - ssl3_handshake_write + ssl3_handshake_write, + ssl3_add_to_finished_hash, }; SSL3_ENC_METHOD TLSv1_1_enc_data={ @@ -159,7 +160,8 @@ SSL3_ENC_METHOD TLSv1_1_enc_data={ SSL_ENC_FLAG_EXPLICIT_IV, SSL3_HM_HEADER_LENGTH, ssl3_set_handshake_header, - ssl3_handshake_write + ssl3_handshake_write, + ssl3_add_to_finished_hash, }; SSL3_ENC_METHOD TLSv1_2_enc_data={ @@ -179,7 +181,8 @@ SSL3_ENC_METHOD TLSv1_2_enc_data={ |SSL_ENC_FLAG_TLS1_2_CIPHERS, SSL3_HM_HEADER_LENGTH, ssl3_set_handshake_header, - ssl3_handshake_write + ssl3_handshake_write, + ssl3_add_to_finished_hash, }; static int compare_uint16_t(const void *p1, const void *p2) @@ -978,6 +981,15 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *buf, unsigned c ret += el; } + /* Add extended master secret. */ + if (s->version != SSL3_VERSION) + { + if (limit - ret - 4 < 0) + return NULL; + s2n(TLSEXT_TYPE_extended_master_secret,ret); + s2n(0,ret); + } + if (!(SSL_get_options(s) & SSL_OP_NO_TICKET)) { int ticklen; @@ -1246,6 +1258,14 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf, unsigned c ret += el; } + if (s->s3->tmp.extended_master_secret) + { + if ((long)(limit - ret - 4) < 0) return NULL; + + s2n(TLSEXT_TYPE_extended_master_secret,ret); + s2n(0,ret); + } + if (using_ecc) { const unsigned char *plist; @@ -1423,6 +1443,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) s->should_ack_sni = 0; s->s3->next_proto_neg_seen = 0; s->s3->tmp.certificate_status_expected = 0; + s->s3->tmp.extended_master_secret = 0; if (s->s3->alpn_selected) { @@ -1782,6 +1803,18 @@ static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) if (!ssl_parse_clienthello_use_srtp_ext(s, &extension, out_alert)) return 0; } + + else if (type == TLSEXT_TYPE_extended_master_secret && + s->version != SSL3_VERSION) + { + if (CBS_len(&extension) != 0) + { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tmp.extended_master_secret = 1; + } } ri_check: @@ -1851,6 +1884,7 @@ static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) s->tlsext_ticket_expected = 0; s->s3->tmp.certificate_status_expected = 0; + s->s3->tmp.extended_master_secret = 0; if (s->s3->alpn_selected) { @@ -2086,6 +2120,20 @@ static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) if (!ssl_parse_serverhello_use_srtp_ext(s, &extension, out_alert)) return 0; } + + else if (type == TLSEXT_TYPE_extended_master_secret) + { + if (/* It is invalid for the server to select EMS and + SSLv3. */ + s->version == SSL3_VERSION || + CBS_len(&extension) != 0) + { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tmp.extended_master_secret = 1; + } } if (!s->hit && tlsext_servername == 1) @@ -2779,7 +2827,7 @@ tls1_channel_id_hash(EVP_MD_CTX *md, SSL *s) static const char kClientIDMagic[] = "TLS Channel ID signature"; if (s->s3->handshake_buffer) - if (!ssl3_digest_cached_records(s)) + if (!ssl3_digest_cached_records(s, free_handshake_buffer)) return 0; EVP_DigestUpdate(md, kClientIDMagic, sizeof(kClientIDMagic)); diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 6b27e261..d04c3c03 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc @@ -482,6 +482,13 @@ static int do_exchange(SSL_SESSION **out_session, } } + if (config->expect_extended_master_secret) { + if (!ssl->session->extended_master_secret) { + fprintf(stderr, "No EMS for session when expected"); + return 2; + } + } + if (config->write_different_record_sizes) { if (config->is_dtls) { fprintf(stderr, "write_different_record_sizes not supported for DTLS\n"); diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index 8b2c7503..935fd15f 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go @@ -71,16 +71,17 @@ const ( // TLS extension numbers const ( - extensionServerName uint16 = 0 - extensionStatusRequest uint16 = 5 - extensionSupportedCurves uint16 = 10 - extensionSupportedPoints uint16 = 11 - extensionSignatureAlgorithms uint16 = 13 - extensionALPN uint16 = 16 - extensionSessionTicket uint16 = 35 - extensionNextProtoNeg uint16 = 13172 // not IANA assigned - extensionRenegotiationInfo uint16 = 0xff01 - extensionChannelID uint16 = 30032 // not IANA assigned + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionSignatureAlgorithms uint16 = 13 + extensionALPN uint16 = 16 + extensionExtendedMasterSecret uint16 = 23 + extensionSessionTicket uint16 = 35 + extensionNextProtoNeg uint16 = 13172 // not IANA assigned + extensionRenegotiationInfo uint16 = 0xff01 + extensionChannelID uint16 = 30032 // not IANA assigned ) // TLS signaling cipher suite values @@ -189,12 +190,13 @@ const ( // ClientSessionState contains the state needed by clients to resume TLS // sessions. type ClientSessionState struct { - sessionTicket []uint8 // Encrypted ticket used for session resumption with server - vers uint16 // SSL/TLS version negotiated for the session - cipherSuite uint16 // Ciphersuite negotiated for the session - masterSecret []byte // MasterSecret generated by client on a full handshake - handshakeHash []byte // Handshake hash for Channel ID purposes. - serverCertificates []*x509.Certificate // Certificate chain presented by the server + sessionTicket []uint8 // Encrypted ticket used for session resumption with server + vers uint16 // SSL/TLS version negotiated for the session + cipherSuite uint16 // Ciphersuite negotiated for the session + masterSecret []byte // MasterSecret generated by client on a full handshake + handshakeHash []byte // Handshake hash for Channel ID purposes. + serverCertificates []*x509.Certificate // Certificate chain presented by the server + extendedMasterSecret bool // Whether an extended master secret was used to generate the session } // ClientSessionCache is a cache of ClientSessionState objects that can be used @@ -472,6 +474,14 @@ type ProtocolBugs struct { // OversizedSessionId causes the session id that is sent with a ticket // resumption attempt to be too large (33 bytes). OversizedSessionId bool + + // RequireExtendedMasterSecret, if true, requires that the peer support + // the extended master secret option. + RequireExtendedMasterSecret bool + + // NoExtendedMasterSecret causes the client and server to behave is if + // they didn't support an extended master secret. + NoExtendedMasterSecret bool } func (c *Config) serverInit() { diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index 9f0c328b..3ce6c764 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go @@ -29,16 +29,17 @@ type Conn struct { isClient bool // constant after handshake; protected by handshakeMutex - handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex - handshakeErr error // error resulting from handshake - vers uint16 // TLS version - haveVers bool // version has been negotiated - config *Config // configuration passed to constructor - handshakeComplete bool - didResume bool // whether this connection was a session resumption - cipherSuite uint16 - ocspResponse []byte // stapled OCSP response - peerCertificates []*x509.Certificate + handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex + handshakeErr error // error resulting from handshake + vers uint16 // TLS version + haveVers bool // version has been negotiated + config *Config // configuration passed to constructor + handshakeComplete bool + didResume bool // whether this connection was a session resumption + extendedMasterSecret bool // whether this session used an extended master secret + cipherSuite uint16 + ocspResponse []byte // stapled OCSP response + peerCertificates []*x509.Certificate // verifiedChains contains the certificate chains that we built, as // opposed to the ones presented by the server. verifiedChains [][]*x509.Certificate diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index f4cadc26..2f9fe12d 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go @@ -56,26 +56,31 @@ func (c *Conn) clientHandshake() error { } hello := &clientHelloMsg{ - isDTLS: c.isDTLS, - vers: c.config.maxVersion(), - compressionMethods: []uint8{compressionNone}, - random: make([]byte, 32), - ocspStapling: true, - serverName: c.config.ServerName, - supportedCurves: c.config.curvePreferences(), - supportedPoints: []uint8{pointFormatUncompressed}, - nextProtoNeg: len(c.config.NextProtos) > 0, - secureRenegotiation: true, - alpnProtocols: c.config.NextProtos, - duplicateExtension: c.config.Bugs.DuplicateExtension, - channelIDSupported: c.config.ChannelID != nil, - npnLast: c.config.Bugs.SwapNPNAndALPN, + isDTLS: c.isDTLS, + vers: c.config.maxVersion(), + compressionMethods: []uint8{compressionNone}, + random: make([]byte, 32), + ocspStapling: true, + serverName: c.config.ServerName, + supportedCurves: c.config.curvePreferences(), + supportedPoints: []uint8{pointFormatUncompressed}, + nextProtoNeg: len(c.config.NextProtos) > 0, + secureRenegotiation: true, + alpnProtocols: c.config.NextProtos, + duplicateExtension: c.config.Bugs.DuplicateExtension, + channelIDSupported: c.config.ChannelID != nil, + npnLast: c.config.Bugs.SwapNPNAndALPN, + extendedMasterSecret: c.config.maxVersion() >= VersionTLS10, } if c.config.Bugs.SendClientVersion != 0 { hello.vers = c.config.Bugs.SendClientVersion } + if c.config.Bugs.NoExtendedMasterSecret { + hello.extendedMasterSecret = false + } + possibleCipherSuites := c.config.cipherSuites() hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) @@ -503,7 +508,15 @@ func (hs *clientHandshakeState) doFullHandshake() error { c.writeRecord(recordTypeHandshake, ckx.marshal()) } - hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) + if hs.serverHello.extendedMasterSecret && c.vers >= VersionTLS10 { + hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash) + c.extendedMasterSecret = true + } else { + if c.config.Bugs.RequireExtendedMasterSecret { + return errors.New("tls: extended master secret required but not supported by peer") + } + hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) + } if chainToSend != nil { var signed []byte @@ -629,6 +642,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { // Restore masterSecret and peerCerts from previous state hs.masterSecret = hs.session.masterSecret c.peerCertificates = hs.session.serverCertificates + c.extendedMasterSecret = hs.session.extendedMasterSecret hs.finishedHash.discardHandshakeBuffer() return true, nil } diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 136360d0..1114a6fb 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go @@ -7,27 +7,28 @@ package main import "bytes" type clientHelloMsg struct { - raw []byte - isDTLS bool - vers uint16 - random []byte - sessionId []byte - cookie []byte - cipherSuites []uint16 - compressionMethods []uint8 - nextProtoNeg bool - serverName string - ocspStapling bool - supportedCurves []CurveID - supportedPoints []uint8 - ticketSupported bool - sessionTicket []uint8 - signatureAndHashes []signatureAndHash - secureRenegotiation bool - alpnProtocols []string - duplicateExtension bool - channelIDSupported bool - npnLast bool + raw []byte + isDTLS bool + vers uint16 + random []byte + sessionId []byte + cookie []byte + cipherSuites []uint16 + compressionMethods []uint8 + nextProtoNeg bool + serverName string + ocspStapling bool + supportedCurves []CurveID + supportedPoints []uint8 + ticketSupported bool + sessionTicket []uint8 + signatureAndHashes []signatureAndHash + secureRenegotiation bool + alpnProtocols []string + duplicateExtension bool + channelIDSupported bool + npnLast bool + extendedMasterSecret bool } func (m *clientHelloMsg) equal(i interface{}) bool { @@ -56,7 +57,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool { eqStrings(m.alpnProtocols, m1.alpnProtocols) && m.duplicateExtension == m1.duplicateExtension && m.channelIDSupported == m1.channelIDSupported && - m.npnLast == m1.npnLast + m.npnLast == m1.npnLast && + m.extendedMasterSecret == m1.extendedMasterSecret } func (m *clientHelloMsg) marshal() []byte { @@ -118,6 +120,9 @@ func (m *clientHelloMsg) marshal() []byte { } numExtensions++ } + if m.extendedMasterSecret { + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -319,6 +324,12 @@ func (m *clientHelloMsg) marshal() []byte { z[1] = 0xff z = z[4:] } + if m.extendedMasterSecret { + // https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 + z[0] = byte(extensionExtendedMasterSecret >> 8) + z[1] = byte(extensionExtendedMasterSecret & 0xff) + z = z[4:] + } m.raw = x @@ -385,6 +396,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.sessionTicket = nil m.signatureAndHashes = nil m.alpnProtocols = nil + m.extendedMasterSecret = false if len(data) == 0 { // ClientHello is optionally followed by extension data @@ -517,6 +529,11 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { return false } m.channelIDSupported = true + case extensionExtendedMasterSecret: + if length != 0 { + return false + } + m.extendedMasterSecret = true } data = data[length:] } @@ -525,21 +542,22 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } type serverHelloMsg struct { - raw []byte - isDTLS bool - vers uint16 - random []byte - sessionId []byte - cipherSuite uint16 - compressionMethod uint8 - nextProtoNeg bool - nextProtos []string - ocspStapling bool - ticketSupported bool - secureRenegotiation bool - alpnProtocol string - duplicateExtension bool - channelIDRequested bool + raw []byte + isDTLS bool + vers uint16 + random []byte + sessionId []byte + cipherSuite uint16 + compressionMethod uint8 + nextProtoNeg bool + nextProtos []string + ocspStapling bool + ticketSupported bool + secureRenegotiation bool + alpnProtocol string + duplicateExtension bool + channelIDRequested bool + extendedMasterSecret bool } func (m *serverHelloMsg) equal(i interface{}) bool { @@ -562,7 +580,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool { m.secureRenegotiation == m1.secureRenegotiation && m.alpnProtocol == m1.alpnProtocol && m.duplicateExtension == m1.duplicateExtension && - m.channelIDRequested == m1.channelIDRequested + m.channelIDRequested == m1.channelIDRequested && + m.extendedMasterSecret == m1.extendedMasterSecret } func (m *serverHelloMsg) marshal() []byte { @@ -606,6 +625,9 @@ func (m *serverHelloMsg) marshal() []byte { extensionsLength += 2 + 1 + alpnLen numExtensions++ } + if m.extendedMasterSecret { + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions @@ -699,6 +721,11 @@ func (m *serverHelloMsg) marshal() []byte { z[1] = 0xff z = z[4:] } + if m.extendedMasterSecret { + z[0] = byte(extensionExtendedMasterSecret >> 8) + z[1] = byte(extensionExtendedMasterSecret & 0xff) + z = z[4:] + } m.raw = x @@ -730,6 +757,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { m.ocspStapling = false m.ticketSupported = false m.alpnProtocol = "" + m.extendedMasterSecret = false if len(data) == 0 { // ServerHello is optionally followed by extension data @@ -805,6 +833,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { return false } m.channelIDRequested = true + case extensionExtendedMasterSecret: + if length != 0 { + return false + } + m.extendedMasterSecret = true } data = data[length:] } diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index 1eb3f110..645a67c0 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go @@ -237,6 +237,7 @@ Curves: hs.hello.nextProtos = config.NextProtos } } + hs.hello.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret if len(config.Certificates) == 0 { c.sendAlert(alertInternalError) @@ -373,6 +374,7 @@ func (hs *serverHandshakeState) doResumeHandshake() error { } hs.masterSecret = hs.sessionState.masterSecret + c.extendedMasterSecret = hs.sessionState.extendedMasterSecret return nil } @@ -387,6 +389,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled hs.hello.cipherSuite = hs.suite.id + c.extendedMasterSecret = hs.hello.extendedMasterSecret hs.finishedHash = newFinishedHash(c.vers, hs.suite) hs.writeClientHash(hs.clientHello.marshal()) @@ -502,7 +505,14 @@ func (hs *serverHandshakeState) doFullHandshake() error { c.sendAlert(alertHandshakeFailure) return err } - hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) + if c.extendedMasterSecret { + hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash) + } else { + if c.config.Bugs.RequireExtendedMasterSecret { + return errors.New("tls: extended master secret required but not supported by peer") + } + hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) + } // If we received a client cert in response to our certificate request message, // the client will send us a certificateVerifyMsg immediately after the diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go index 6d0db977..d45c080d 100644 --- a/ssl/test/runner/prf.go +++ b/ssl/test/runner/prf.go @@ -117,6 +117,7 @@ const ( ) var masterSecretLabel = []byte("master secret") +var extendedMasterSecretLabel = []byte("extended master secret") var keyExpansionLabel = []byte("key expansion") var clientFinishedLabel = []byte("client finished") var serverFinishedLabel = []byte("server finished") @@ -150,6 +151,15 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr return masterSecret } +// extendedMasterFromPreMasterSecret generates the master secret from the +// pre-master secret when the Triple Handshake fix is in effect. See +// https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 +func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, h finishedHash) []byte { + masterSecret := make([]byte, masterSecretLength) + prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, h.Sum()) + return masterSecret +} + // keysFromMasterSecret generates the connection keys from the master // secret, given the lengths of the MAC key, cipher key and IV, as defined in // RFC 2246, section 6.3. @@ -221,6 +231,16 @@ func (h *finishedHash) Write(msg []byte) (n int, err error) { return len(msg), nil } +func (h finishedHash) Sum() []byte { + if h.version >= VersionTLS12 { + return h.client.Sum(nil) + } + + out := make([]byte, 0, md5.Size+sha1.Size) + out = h.clientMD5.Sum(out) + return h.client.Sum(out) +} + // finishedSum30 calculates the contents of the verify_data member of a SSLv3 // Finished message given the MD5 and SHA1 hashes of a set of handshake // messages. @@ -264,15 +284,7 @@ func (h finishedHash) clientSum(masterSecret []byte) []byte { } out := make([]byte, finishedVerifyLength) - if h.version >= VersionTLS12 { - seed := h.client.Sum(nil) - h.prf(out, masterSecret, clientFinishedLabel, seed) - } else { - seed := make([]byte, 0, md5.Size+sha1.Size) - seed = h.clientMD5.Sum(seed) - seed = h.client.Sum(seed) - h.prf(out, masterSecret, clientFinishedLabel, seed) - } + h.prf(out, masterSecret, clientFinishedLabel, h.Sum()) return out } @@ -284,15 +296,7 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte { } out := make([]byte, finishedVerifyLength) - if h.version >= VersionTLS12 { - seed := h.server.Sum(nil) - h.prf(out, masterSecret, serverFinishedLabel, seed) - } else { - seed := make([]byte, 0, md5.Size+sha1.Size) - seed = h.serverMD5.Sum(seed) - seed = h.server.Sum(seed) - h.prf(out, masterSecret, serverFinishedLabel, seed) - } + h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) return out } @@ -334,14 +338,10 @@ func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash return digest[:], crypto.SHA256, nil } if signatureAndHash.signature == signatureECDSA { - digest := h.server.Sum(nil) - return digest, crypto.SHA1, nil + return h.server.Sum(nil), crypto.SHA1, nil } - digest := make([]byte, 0, 36) - digest = h.serverMD5.Sum(digest) - digest = h.server.Sum(digest) - return digest, crypto.MD5SHA1, nil + return h.Sum(), crypto.MD5SHA1, nil } // hashForChannelID returns the hash to be signed for TLS Channel diff --git a/ssl/test/runner/recordingconn.go b/ssl/test/runner/recordingconn.go new file mode 100644 index 00000000..a67fa48f --- /dev/null +++ b/ssl/test/runner/recordingconn.go @@ -0,0 +1,130 @@ +package main + +import ( + "bufio" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "strconv" + "strings" + "sync" +) + +// recordingConn is a net.Conn that records the traffic that passes through it. +// WriteTo can be used to produce output that can be later be loaded with +// ParseTestData. +type recordingConn struct { + net.Conn + sync.Mutex + flows [][]byte + reading bool +} + +func (r *recordingConn) Read(b []byte) (n int, err error) { + if n, err = r.Conn.Read(b); n == 0 { + return + } + b = b[:n] + + r.Lock() + defer r.Unlock() + + if l := len(r.flows); l == 0 || !r.reading { + buf := make([]byte, len(b)) + copy(buf, b) + r.flows = append(r.flows, buf) + } else { + r.flows[l-1] = append(r.flows[l-1], b[:n]...) + } + r.reading = true + return +} + +func (r *recordingConn) Write(b []byte) (n int, err error) { + if n, err = r.Conn.Write(b); n == 0 { + return + } + b = b[:n] + + r.Lock() + defer r.Unlock() + + if l := len(r.flows); l == 0 || r.reading { + buf := make([]byte, len(b)) + copy(buf, b) + r.flows = append(r.flows, buf) + } else { + r.flows[l-1] = append(r.flows[l-1], b[:n]...) + } + r.reading = false + return +} + +// WriteTo writes hex dumps to w that contains the recorded traffic. +func (r *recordingConn) WriteTo(w io.Writer) { + // TLS always starts with a client to server flow. + clientToServer := true + + for i, flow := range r.flows { + source, dest := "client", "server" + if !clientToServer { + source, dest = dest, source + } + fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest) + dumper := hex.Dumper(w) + dumper.Write(flow) + dumper.Close() + clientToServer = !clientToServer + } +} + +func parseTestData(r io.Reader) (flows [][]byte, err error) { + var currentFlow []byte + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + // If the line starts with ">>> " then it marks the beginning + // of a new flow. + if strings.HasPrefix(line, ">>> ") { + if len(currentFlow) > 0 || len(flows) > 0 { + flows = append(flows, currentFlow) + currentFlow = nil + } + continue + } + + // Otherwise the line is a line of hex dump that looks like: + // 00000170 fc f5 06 bf (...) |.....X{&?......!| + // (Some bytes have been omitted from the middle section.) + + if i := strings.IndexByte(line, ' '); i >= 0 { + line = line[i:] + } else { + return nil, errors.New("invalid test data") + } + + if i := strings.IndexByte(line, '|'); i >= 0 { + line = line[:i] + } else { + return nil, errors.New("invalid test data") + } + + hexBytes := strings.Fields(line) + for _, hexByte := range hexBytes { + val, err := strconv.ParseUint(hexByte, 16, 8) + if err != nil { + return nil, errors.New("invalid hex byte in test data: " + err.Error()) + } + currentFlow = append(currentFlow, byte(val)) + } + } + + if len(currentFlow) > 0 { + flows = append(flows, currentFlow) + } + + return flows, nil +} diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index b4c2e612..10f86c97 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -22,6 +22,8 @@ import ( ) var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind") +var useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb") +var flagDebug *bool = flag.Bool("debug", false, "Hexdump the contents of the connection") const ( rsaCertificateFile = "cert.pem" @@ -693,10 +695,11 @@ func runTest(test *testCase, buildDir string) error { var shim *exec.Cmd if *useValgrind { shim = valgrindOf(false, shim_path, flags...) + } else if *useGDB { + shim = gdbOf(shim_path, flags...) } else { shim = exec.Command(shim_path, flags...) } - // shim = gdbOf(shim_path, flags...) shim.ExtraFiles = []*os.File{shimEnd, shimEndResume} shim.Stdin = os.Stdin var stdoutBuf, stderrBuf bytes.Buffer @@ -717,8 +720,19 @@ func runTest(test *testCase, buildDir string) error { } } + var connDebug *recordingConn + if *flagDebug { + connDebug = &recordingConn{Conn: conn} + conn = connDebug + } + err := doExchange(test, &config, conn, test.messageLen, false /* not a resumption */) + + if *flagDebug { + connDebug.WriteTo(os.Stdout) + } + conn.Close() if err == nil && test.resumeSession { var resumeConfig Config @@ -1070,6 +1084,62 @@ func addClientAuthTests() { } } +func addExtendedMasterSecretTests() { + const expectEMSFlag = "-expect-extended-master-secret" + + for _, with := range []bool{false, true} { + prefix := "No" + var flags []string + if with { + prefix = "" + flags = []string{expectEMSFlag} + } + + for _, isClient := range []bool{false, true} { + suffix := "-Server" + testType := serverTest + if isClient { + suffix = "-Client" + testType = clientTest + } + + for _, ver := range tlsVersions { + test := testCase{ + testType: testType, + name: prefix + "ExtendedMasterSecret-" + ver.name + suffix, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: !with, + RequireExtendedMasterSecret: with, + }, + }, + flags: flags, + shouldFail: ver.version == VersionSSL30 && with, + } + if test.shouldFail { + test.expectedLocalError = "extended master secret required but not supported by peer" + } + testCases = append(testCases, test) + } + } + } + + // When a session is resumed, it should still be aware that its master + // secret was generated via EMS and thus it's safe to use tls-unique. + testCases = append(testCases, testCase{ + name: "ExtendedMasterSecret-Resume", + config: Config{ + Bugs: ProtocolBugs{ + RequireExtendedMasterSecret: true, + }, + }, + flags: []string{expectEMSFlag}, + resumeSession: true, + }) +} + // Adds tests that try to cover the range of the handshake state machine, under // various conditions. Some of these are redundant with other tests, but they // only cover the synchronous case. @@ -1568,7 +1638,7 @@ func addExtensionTests() { }, }, resumeSession: true, - shouldFail: true, + shouldFail: true, expectedError: ":DECODE_ERROR:", }) } @@ -1690,6 +1760,7 @@ func main() { addD5BugTests() addExtensionTests() addResumptionVersionTests() + addExtendedMasterSecretTests() for _, async := range []bool{false, true} { for _, splitHandshake := range []bool{false, true} { for _, protocol := range []protocol{tls, dtls} { diff --git a/ssl/test/runner/ticket.go b/ssl/test/runner/ticket.go index 74791d67..83558228 100644 --- a/ssl/test/runner/ticket.go +++ b/ssl/test/runner/ticket.go @@ -18,11 +18,12 @@ import ( // sessionState contains the information that is serialized into a session // ticket in order to later resume a connection. type sessionState struct { - vers uint16 - cipherSuite uint16 - masterSecret []byte - handshakeHash []byte - certificates [][]byte + vers uint16 + cipherSuite uint16 + masterSecret []byte + handshakeHash []byte + certificates [][]byte + extendedMasterSecret bool } func (s *sessionState) equal(i interface{}) bool { @@ -34,7 +35,8 @@ func (s *sessionState) equal(i interface{}) bool { if s.vers != s1.vers || s.cipherSuite != s1.cipherSuite || !bytes.Equal(s.masterSecret, s1.masterSecret) || - !bytes.Equal(s.handshakeHash, s1.handshakeHash) { + !bytes.Equal(s.handshakeHash, s1.handshakeHash) || + s.extendedMasterSecret != s1.extendedMasterSecret { return false } @@ -56,6 +58,7 @@ func (s *sessionState) marshal() []byte { for _, cert := range s.certificates { length += 4 + len(cert) } + length++ ret := make([]byte, length) x := ret @@ -88,6 +91,11 @@ func (s *sessionState) marshal() []byte { x = x[4+len(cert):] } + if s.extendedMasterSecret { + x[0] = 1 + } + x = x[1:] + return ret } @@ -144,6 +152,16 @@ func (s *sessionState) unmarshal(data []byte) bool { data = data[certLen:] } + if len(data) < 1 { + return false + } + + s.extendedMasterSecret = false + if data[0] == 1 { + s.extendedMasterSecret = true + } + data = data[1:] + if len(data) > 0 { return false } diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index 270fbfb5..737c78d5 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc @@ -57,6 +57,8 @@ const BoolFlag kBoolFlags[] = { { "-shim-writes-first", &TestConfig::shim_writes_first }, { "-tls-d5-bug", &TestConfig::tls_d5_bug }, { "-expect-session-miss", &TestConfig::expect_session_miss }, + { "-expect-extended-master-secret", + &TestConfig::expect_extended_master_secret }, }; const size_t kNumBoolFlags = sizeof(kBoolFlags) / sizeof(kBoolFlags[0]); @@ -105,7 +107,8 @@ TestConfig::TestConfig() cookie_exchange(false), shim_writes_first(false), tls_d5_bug(false), - expect_session_miss(false) { + expect_session_miss(false), + expect_extended_master_secret(false) { } bool ParseConfig(int argc, char **argv, TestConfig *out_config) { diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index acce5043..d7f1be88 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h @@ -53,6 +53,7 @@ struct TestConfig { std::string expected_advertised_alpn; std::string select_alpn; bool expect_session_miss; + bool expect_extended_master_secret; }; bool ParseConfig(int argc, char **argv, TestConfig *out_config);