Implement client side of TLS signed certificate stamps extension.

https://crbug.com/389420 and 3.3 in rfc6962.

Change-Id: Ib22bcd4e4bde5a314ed33e123e19a76cdb714da4
Reviewed-on: https://boringssl-review.googlesource.com/1491
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 3c64237..9402321 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -380,6 +380,8 @@
  *	Compression_meth [11]   EXPLICIT OCTET STRING, -- optional compression method
  *	SRP_username [ 12 ] EXPLICIT OCTET STRING -- optional SRP username
  *	Peer SHA256 [13]        EXPLICIT OCTET STRING, -- optional SHA256 hash of Peer certifiate
+ *	original handshake hash [14] EXPLICIT OCTET STRING, -- optional original handshake hash
+ *	tlsext_signed_cert_timestamp_list [15] EXPLICIT OCTET STRING, -- optional signed cert timestamp list extension
  *	}
  * Look in ssl/ssl_asn1.c for more details
  * I'm using EXPLICIT tags so I can read the damn things using asn1parse :-).
@@ -449,6 +451,8 @@
 	uint8_t *tlsext_tick;	/* Session ticket */
 	size_t tlsext_ticklen;		/* Session ticket length */
 	uint32_t tlsext_tick_lifetime_hint;	/* Session lifetime hint in seconds */
+	size_t tlsext_signed_cert_timestamp_list_length;
+	uint8_t *tlsext_signed_cert_timestamp_list; /* Server's list. */
 	char peer_sha256_valid;		/* Non-zero if peer_sha256 is valid */
 	unsigned char peer_sha256[SHA256_DIGEST_LENGTH];  /* SHA256 of peer certificate */
 
@@ -1023,6 +1027,8 @@
 	/* The client's Channel ID private key. */
 	EVP_PKEY *tlsext_channel_id_private;
 
+	/* If true, a client will request certificate timestamps. */
+	char signed_cert_timestamps_enabled;
 	};
 
 #endif
@@ -1087,6 +1093,28 @@
 OPENSSL_EXPORT void (*SSL_CTX_get_channel_id_cb(SSL_CTX *ctx))(SSL *ssl, EVP_PKEY **pkey);
 OPENSSL_EXPORT void SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, int (*app_gen_cookie_cb)(SSL *ssl, uint8_t *cookie, size_t *cookie_len));
 OPENSSL_EXPORT void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, int (*app_verify_cookie_cb)(SSL *ssl, const uint8_t *cookie, size_t cookie_len));
+
+
+/* SSL_enable_signed_cert_timestamps causes |ssl| (which must be the client
+ * end of a connection) to request SCTs from the server.
+ * See https://tools.ietf.org/html/rfc6962.
+ * Returns 1 on success. */
+OPENSSL_EXPORT int SSL_enable_signed_cert_timestamps(SSL *ssl);
+
+/* SSL_CTX_enable_signed_cert_timestamps enables SCT requests on all
+ * client SSL objects created from |ctx|. */
+OPENSSL_EXPORT void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx);
+
+/* SSL_get0_signed_cert_timestamp_list sets |*out| and |*out_len| to point to
+ * |*out_len| bytes of SCT information from the server. This is only valid if
+ * |ssl| is a client. The SCT information is a SignedCertificateTimestampList
+ * (including the two leading length bytes).
+ * See https://tools.ietf.org/html/rfc6962#section-3.3
+ * If no SCT was received then |*out_len| will be zero on return.
+ *
+ * WARNING: the returned data is not guaranteed to be well formed. */
+OPENSSL_EXPORT void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, uint8_t **out, size_t *out_len);
+
 #ifndef OPENSSL_NO_NEXTPROTONEG
 OPENSSL_EXPORT void SSL_CTX_set_next_protos_advertised_cb(SSL_CTX *s,
 					   int (*cb) (SSL *ssl,
@@ -1392,6 +1420,9 @@
 	/* The client's Channel ID private key. */
 	EVP_PKEY *tlsext_channel_id_private;
 
+	/* Enable signed certificate time stamps. Currently client only. */
+	char signed_cert_timestamps_enabled;
+
 	/* For a client, this contains the list of supported protocols in wire
 	 * format. */
 	unsigned char* alpn_client_proto_list;
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index b075a20..76a1296 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -246,6 +246,9 @@
 /* ExtensionType value from RFC5746 */
 #define TLSEXT_TYPE_renegotiate                 0xff01
 
+/* ExtensionType value from RFC6962 */
+#define TLSEXT_TYPE_certificate_timestamp	18
+
 #ifndef OPENSSL_NO_NEXTPROTONEG
 /* This is not an IANA defined extension number */
 #define TLSEXT_TYPE_next_proto_neg		13172
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 9f372f9..05c6462 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -119,12 +119,13 @@
 	ASN1_OCTET_STRING psk_identity;
 	ASN1_OCTET_STRING peer_sha256;
 	ASN1_OCTET_STRING original_handshake_hash;
+	ASN1_OCTET_STRING tlsext_signed_cert_timestamp_list;
 	} SSL_SESSION_ASN1;
 
 int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
 	{
 #define LSIZE2 (sizeof(long)*2)
-	int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0,v13=0,v14=0;
+	int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0,v13=0,v14=0,v15=0;
 	unsigned char buf[4],ibuf1[LSIZE2],ibuf2[LSIZE2];
 	unsigned char ibuf3[LSIZE2],ibuf4[LSIZE2],ibuf5[LSIZE2];
 	int v6=0,v9=0,v10=0;
@@ -259,6 +260,15 @@
 		a.original_handshake_hash.data = in->original_handshake_hash;
 		}
 
+	if (in->tlsext_signed_cert_timestamp_list_length > 0)
+		{
+		a.tlsext_signed_cert_timestamp_list.length =
+				in->tlsext_signed_cert_timestamp_list_length;
+		a.tlsext_signed_cert_timestamp_list.type = V_ASN1_OCTET_STRING;
+		a.tlsext_signed_cert_timestamp_list.data =
+				in->tlsext_signed_cert_timestamp_list;
+		}
+
 	M_ASN1_I2D_len(&(a.version),		i2d_ASN1_INTEGER);
 	M_ASN1_I2D_len(&(a.ssl_version),	i2d_ASN1_INTEGER);
 	M_ASN1_I2D_len(&(a.cipher),		i2d_ASN1_OCTET_STRING);
@@ -290,6 +300,9 @@
 		M_ASN1_I2D_len_EXP_opt(&(a.peer_sha256),i2d_ASN1_OCTET_STRING,13,v13);
 	if (in->original_handshake_hash_len > 0)
 		M_ASN1_I2D_len_EXP_opt(&(a.original_handshake_hash),i2d_ASN1_OCTET_STRING,14,v14);
+	if (in->tlsext_signed_cert_timestamp_list_length > 0)
+		M_ASN1_I2D_len_EXP_opt(&(a.tlsext_signed_cert_timestamp_list),
+				i2d_ASN1_OCTET_STRING, 15, v15);
 
 	M_ASN1_I2D_seq_total();
 
@@ -324,6 +337,9 @@
 		M_ASN1_I2D_put_EXP_opt(&(a.peer_sha256),i2d_ASN1_OCTET_STRING,13,v13);
 	if (in->original_handshake_hash_len > 0)
 		M_ASN1_I2D_put_EXP_opt(&(a.original_handshake_hash),i2d_ASN1_OCTET_STRING,14,v14);
+	if (in->tlsext_signed_cert_timestamp_list_length > 0)
+		M_ASN1_I2D_put_EXP_opt(&(a.tlsext_signed_cert_timestamp_list),
+				i2d_ASN1_OCTET_STRING, 15, v15);
 
 	M_ASN1_I2D_finish();
 	}
@@ -572,5 +588,18 @@
 		os.data = NULL;
 		}
 
+	os.length = 0;
+	os.data = NULL;
+	M_ASN1_D2I_get_EXP_opt(osp, d2i_ASN1_OCTET_STRING, 15);
+	if (os.data)
+		{
+		if (ret->tlsext_signed_cert_timestamp_list)
+			OPENSSL_free(ret->tlsext_signed_cert_timestamp_list);
+		ret->tlsext_signed_cert_timestamp_list = os.data;
+		ret->tlsext_signed_cert_timestamp_list_length = os.length;
+		os.data = NULL;
+		}
+
+
 	M_ASN1_D2I_Finish(a,SSL_SESSION_free,SSL_F_D2I_SSL_SESSION);
 	}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 43ce97b..4722b3e 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -404,6 +404,9 @@
 	s->psk_client_callback=ctx->psk_client_callback;
 	s->psk_server_callback=ctx->psk_server_callback;
 
+	if (!s->server)
+		s->signed_cert_timestamps_enabled = s->ctx->signed_cert_timestamps_enabled;
+
 	return(s);
 err:
 	if (s != NULL)
@@ -1675,6 +1678,34 @@
 	return -1;
 	}
 
+void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx)
+	{
+	ctx->signed_cert_timestamps_enabled = 1;
+	}
+
+int SSL_enable_signed_cert_timestamps(SSL *ssl)
+	{
+	/* Currently not implemented server side */
+	if (ssl->server)
+		return 0;
+
+	ssl->signed_cert_timestamps_enabled = 1;
+	return 1;
+	}
+
+void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, uint8_t **out, size_t *out_len)
+	{
+	*out_len = 0;
+	*out = NULL;
+	if (ssl->server)
+		return;
+	SSL_SESSION *session = ssl->session;
+	if (!session || !session->tlsext_signed_cert_timestamp_list)
+		return;
+	*out = session->tlsext_signed_cert_timestamp_list;
+	*out_len = session->tlsext_signed_cert_timestamp_list_length;
+	}
+
 /* SSL_select_next_proto implements the standard protocol selection. It is
  * expected that this function is called from the callback set by
  * SSL_CTX_set_next_proto_select_cb.
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 8881042..56ba7c9 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -211,18 +211,7 @@
 	ss->references=1;
 	ss->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
 	ss->time=(unsigned long)time(NULL);
-	ss->prev=NULL;
-	ss->next=NULL;
-	ss->tlsext_hostname = NULL; 
-#ifndef OPENSSL_NO_EC
-	ss->tlsext_ecpointformatlist_length = 0;
-	ss->tlsext_ecpointformatlist = NULL;
-	ss->tlsext_ellipticcurvelist_length = 0;
-	ss->tlsext_ellipticcurvelist = NULL;
-#endif
 	CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data);
-	ss->psk_identity_hint=NULL;
-	ss->psk_identity=NULL;
 	return(ss);
 	}
 
@@ -727,6 +716,8 @@
 	ss->tlsext_ellipticcurvelist_length = 0;
 	if (ss->tlsext_ellipticcurvelist != NULL) OPENSSL_free(ss->tlsext_ellipticcurvelist);
 #endif /* OPENSSL_NO_EC */
+	if (ss->tlsext_signed_cert_timestamp_list != NULL)
+		OPENSSL_free(ss->tlsext_signed_cert_timestamp_list);
 	if (ss->psk_identity_hint != NULL)
 		OPENSSL_free(ss->psk_identity_hint);
 	if (ss->psk_identity != NULL)
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 855d1d0..d23b41e 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1109,6 +1109,16 @@
 		}
 #endif
 
+	if (s->signed_cert_timestamps_enabled && !s->s3->tmp.finish_md_len)
+		{
+		/* The client advertises an empty extension to indicate its support for
+		 * certificate timestamps. */
+		if (limit - ret - 4 < 0)
+			return NULL;
+		s2n(TLSEXT_TYPE_certificate_timestamp,ret);
+		s2n(0,ret);
+		}
+
 	if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len)
 		{
 		if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len)
@@ -2234,7 +2244,26 @@
 			s->s3->tlsext_channel_id_valid = 1;
 			s->s3->tlsext_channel_id_new = 1;
 			}
+		else if (type == TLSEXT_TYPE_certificate_timestamp)
+			{
+			if (CBS_len(&extension) == 0)
+				{
+				*out_alert = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
 
+			/* Session resumption uses the original session information. */
+			if (!s->hit)
+				{
+				if (!CBS_stow(&extension,
+					&s->session->tlsext_signed_cert_timestamp_list,
+					&s->session->tlsext_signed_cert_timestamp_list_length))
+					{
+					*out_alert = SSL_AD_INTERNAL_ERROR;
+					return 0;
+					}
+				}
+			}
 		else if (type == TLSEXT_TYPE_renegotiate)
 			{
 			if (!ssl_parse_serverhello_renegotiate_ext(s, &extension, out_alert))