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>
This commit is contained in:
parent
c44d2f4cb8
commit
9169c96458
@ -380,6 +380,8 @@ struct ssl_method_st
|
||||
* 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 @@ struct ssl_session_st
|
||||
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 @@ struct ssl_ctx_st
|
||||
/* 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_set_channel_id_cb(SSL_CTX *ctx, void (*channel_id_cb
|
||||
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 @@ struct ssl_st
|
||||
/* 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;
|
||||
|
@ -246,6 +246,9 @@ extern "C" {
|
||||
/* 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
|
||||
|
@ -119,12 +119,13 @@ typedef struct ssl_session_asn1_st
|
||||
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 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
|
||||
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 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
|
||||
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 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
|
||||
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 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp,
|
||||
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);
|
||||
}
|
||||
|
@ -404,6 +404,9 @@ SSL *SSL_new(SSL_CTX *ctx)
|
||||
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 @@ int SSL_get_servername_type(const SSL *s)
|
||||
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.
|
||||
|
@ -211,18 +211,7 @@ SSL_SESSION *SSL_SESSION_new(void)
|
||||
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 @@ void SSL_SESSION_free(SSL_SESSION *ss)
|
||||
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)
|
||||
|
29
ssl/t1_lib.c
29
ssl/t1_lib.c
@ -1109,6 +1109,16 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *buf, unsigned c
|
||||
}
|
||||
#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 @@ static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert)
|
||||
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))
|
||||
|
Loading…
Reference in New Issue
Block a user