Extended master secret support.

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>
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 2d944d8..8b225e5 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -229,7 +229,7 @@
 	}
 
 /* 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 @@
 			 * 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 @@
 		}
 
 	/* 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 @@
 	}
 	
 	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 96ce496..650d8e7 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 @@
 	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 @@
 		|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 @@
 	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 79da484..e1c5616 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -687,5 +687,5 @@
 		}
 
 	/* 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 6604fc7..bbdeadb 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 @@
 		}
 
 	/* 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 6574f5a..0321fd1 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -982,7 +982,7 @@
 	/* 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 @@
 		 */
 		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 @@
 	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 @@
 			{
 			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 @@
 
 			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 @@
 			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 @@
 		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 @@
 				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 @@
 
 			/* 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 @@
 				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 @@
 				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 @@
 			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 @@
 			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 @@
 			/* 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 @@
 
 		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 @@
 			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 @@
 		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 @@
 	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 @@
 	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 b0ca507..fc94a94 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -510,7 +510,7 @@
 		}	
 	}
 
-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 @@
 			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 @@
 	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 215b3f6..b086154 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -942,7 +942,8 @@
 	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 @@
 	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 0fac30e..edcdc03 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -444,7 +444,7 @@
 				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 @@
 
 	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 @@
 	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 @@
 	 * 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 @@
 
 	/* 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 @@
 			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 358f20f..53dc996 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 @@
     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 @@
     }
   }
 
+  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 *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 @@
           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 3e682a1..c214b91 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -568,6 +568,11 @@
 
 #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 @@
 	/* 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 @@
 	(((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_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_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 @@
 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 dd00d0a..0a4e088 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -152,8 +152,6 @@
 			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 @@
 		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 @@
 			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 @@
 		     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 @@
 				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 @@
 		 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 @@
 	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 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 @@
 		}
 		
 	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 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 @@
 		      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 4b13cfe..0972515 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -140,7 +140,8 @@
 	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 @@
 	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 @@
 		|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 @@
           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 @@
           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 @@
 	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 @@
 			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 @@
 
 	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 @@
                         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 @@
 	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 6b27e26..d04c3c0 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -482,6 +482,13 @@
     }
   }
 
+  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 8b2c750..935fd15 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -71,16 +71,17 @@
 
 // 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 @@
 // 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 @@
 	// 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 9f0c328..3ce6c76 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -29,16 +29,17 @@
 	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 f4cadc2..2f9fe12 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -56,26 +56,31 @@
 	}
 
 	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 @@
 		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 @@
 		// 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 136360d..1114a6f 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -7,27 +7,28 @@
 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 @@
 		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 @@
 		}
 		numExtensions++
 	}
+	if m.extendedMasterSecret {
+		numExtensions++
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -319,6 +324,12 @@
 		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 @@
 	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 @@
 				return false
 			}
 			m.channelIDSupported = true
+		case extensionExtendedMasterSecret:
+			if length != 0 {
+				return false
+			}
+			m.extendedMasterSecret = true
 		}
 		data = data[length:]
 	}
@@ -525,21 +542,22 @@
 }
 
 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 @@
 		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 @@
 		extensionsLength += 2 + 1 + alpnLen
 		numExtensions++
 	}
+	if m.extendedMasterSecret {
+		numExtensions++
+	}
 
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
@@ -699,6 +721,11 @@
 		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 @@
 	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 @@
 				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 1eb3f11..645a67c 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -237,6 +237,7 @@
 			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 @@
 	}
 
 	hs.masterSecret = hs.sessionState.masterSecret
+	c.extendedMasterSecret = hs.sessionState.extendedMasterSecret
 
 	return nil
 }
@@ -387,6 +389,7 @@
 
 	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 @@
 		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 6d0db97..d45c080 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -117,6 +117,7 @@
 )
 
 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 @@
 	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 @@
 	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 @@
 	}
 
 	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 @@
 	}
 
 	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 @@
 		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 0000000..a67fa48
--- /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 b4c2e61..10f86c9 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -22,6 +22,8 @@
 )
 
 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 @@
 	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 @@
 		}
 	}
 
+	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 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 @@
 			},
 		},
 		resumeSession: true,
-		shouldFail: true,
+		shouldFail:    true,
 		expectedError: ":DECODE_ERROR:",
 	})
 }
@@ -1690,6 +1760,7 @@
 	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 74791d6..8355822 100644
--- a/ssl/test/runner/ticket.go
+++ b/ssl/test/runner/ticket.go
@@ -18,11 +18,12 @@
 // 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 @@
 	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 @@
 	for _, cert := range s.certificates {
 		length += 4 + len(cert)
 	}
+	length++
 
 	ret := make([]byte, length)
 	x := ret
@@ -88,6 +91,11 @@
 		x = x[4+len(cert):]
 	}
 
+	if s.extendedMasterSecret {
+		x[0] = 1
+	}
+	x = x[1:]
+
 	return ret
 }
 
@@ -144,6 +152,16 @@
 		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 270fbfb..737c78d 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -57,6 +57,8 @@
   { "-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 @@
       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 acce504..d7f1be8 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -53,6 +53,7 @@
   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);