This change implements support for the extended master secret. See https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 https://secure-resumption.com/ Change-Id: Ifc7327763149ab0894b4f1d48cdc35e0f1093b93 Reviewed-on: https://boringssl-review.googlesource.com/1930 Reviewed-by: David Benjamin <davidben@chromium.org> Reviewed-by: Adam Langley <agl@google.com>kris/onging/CECPQ3_patch15
@@ -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; | |||
} | |||
@@ -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; | |||
} |
@@ -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. | |||
* | |||
@@ -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 | |||
@@ -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 */ | |||
@@ -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 */ | |||
@@ -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; | |||
@@ -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); | |||
} |
@@ -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)); | |||
} |
@@ -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) | |||
@@ -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); | |||
@@ -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 | |||
@@ -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) | |||
@@ -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; | |||
@@ -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. */ | |||
@@ -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, | |||
@@ -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;i<SSL_MAX_DIGEST;i++) | |||
@@ -1093,7 +1080,7 @@ int tls1_final_finish_mac(SSL *s, | |||
int digests_len; | |||
if (s->s3->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); | |||
@@ -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)); | |||
@@ -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"); | |||
@@ -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() { | |||
@@ -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 | |||
@@ -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 | |||
} | |||
@@ -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:] | |||
} | |||
@@ -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 | |||
@@ -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 | |||
@@ -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 | |||
} |
@@ -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} { | |||
@@ -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 | |||
} | |||
@@ -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) { | |||
@@ -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); | |||