Create Certificate messages in TLS1.3 format

Also updates TLSProxy to be able to understand the format and parse the
contained extensions.

Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2020)
This commit is contained in:
Matt Caswell 2016-12-02 09:14:15 +00:00
parent f97d4c3708
commit e96e0f8e42
12 changed files with 462 additions and 149 deletions

View file

@ -740,116 +740,6 @@ int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
return ret;
}
/* Add a certificate to the WPACKET */
static int ssl_add_cert_to_buf(WPACKET *pkt, X509 *x)
{
int len;
unsigned char *outbytes;
len = i2d_X509(x, NULL);
if (len < 0) {
SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_BUF_LIB);
return 0;
}
if (!WPACKET_sub_allocate_bytes_u24(pkt, len, &outbytes)
|| i2d_X509(x, &outbytes) != len) {
SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_INTERNAL_ERROR);
return 0;
}
return 1;
}
/* Add certificate chain to internal SSL BUF_MEM structure */
int ssl_add_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk)
{
int i, chain_count;
X509 *x;
STACK_OF(X509) *extra_certs;
STACK_OF(X509) *chain = NULL;
X509_STORE *chain_store;
if (cpk == NULL || cpk->x509 == NULL)
return 1;
x = cpk->x509;
/*
* If we have a certificate specific chain use it, else use parent ctx.
*/
if (cpk->chain)
extra_certs = cpk->chain;
else
extra_certs = s->ctx->extra_certs;
if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs)
chain_store = NULL;
else if (s->cert->chain_store)
chain_store = s->cert->chain_store;
else
chain_store = s->ctx->cert_store;
if (chain_store) {
X509_STORE_CTX *xs_ctx = X509_STORE_CTX_new();
if (xs_ctx == NULL) {
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_MALLOC_FAILURE);
return (0);
}
if (!X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)) {
X509_STORE_CTX_free(xs_ctx);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_X509_LIB);
return (0);
}
/*
* It is valid for the chain not to be complete (because normally we
* don't include the root cert in the chain). Therefore we deliberately
* ignore the error return from this call. We're not actually verifying
* the cert - we're just building as much of the chain as we can
*/
(void)X509_verify_cert(xs_ctx);
/* Don't leave errors in the queue */
ERR_clear_error();
chain = X509_STORE_CTX_get0_chain(xs_ctx);
i = ssl_security_cert_chain(s, chain, NULL, 0);
if (i != 1) {
#if 0
/* Dummy error calls so mkerr generates them */
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_EE_KEY_TOO_SMALL);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_KEY_TOO_SMALL);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_MD_TOO_WEAK);
#endif
X509_STORE_CTX_free(xs_ctx);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
return 0;
}
chain_count = sk_X509_num(chain);
for (i = 0; i < chain_count; i++) {
x = sk_X509_value(chain, i);
if (!ssl_add_cert_to_buf(pkt, x)) {
X509_STORE_CTX_free(xs_ctx);
return 0;
}
}
X509_STORE_CTX_free(xs_ctx);
} else {
i = ssl_security_cert_chain(s, extra_certs, x, 0);
if (i != 1) {
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
return 0;
}
if (!ssl_add_cert_to_buf(pkt, x))
return 0;
for (i = 0; i < sk_X509_num(extra_certs); i++) {
x = sk_X509_value(extra_certs, i);
if (!ssl_add_cert_to_buf(pkt, x))
return 0;
}
}
return 1;
}
/* Build a certificate chain for current certificate */
int ssl_build_cert_chain(SSL *s, SSL_CTX *ctx, int flags)
{

View file

@ -1896,7 +1896,6 @@ __owur X509 *ssl_cert_get0_next_certificate(CERT *c, int first);
void ssl_cert_set_cert_cb(CERT *c, int (*cb) (SSL *ssl, void *arg), void *arg);
__owur int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) *sk);
__owur int ssl_add_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk);
__owur int ssl_build_cert_chain(SSL *s, SSL_CTX *ctx, int flags);
__owur int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain,
int ref);
@ -1952,7 +1951,7 @@ __owur size_t ssl3_final_finish_mac(SSL *s, const char *sender, size_t slen,
__owur int ssl3_finish_mac(SSL *s, const unsigned char *buf, size_t len);
void ssl3_free_digest_list(SSL *s);
__owur unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt,
CERT_PKEY *cpk);
CERT_PKEY *cpk, int *al);
__owur const SSL_CIPHER *ssl3_choose_cipher(SSL *ssl,
STACK_OF(SSL_CIPHER) *clnt,
STACK_OF(SSL_CIPHER) *srvr);

View file

@ -252,6 +252,10 @@ int tls_construct_ctos_status_request(SSL *s, WPACKET *pkt, X509 *x,
{
int i;
/* This extension isn't defined for client Certificates */
if (x != NULL)
return 1;
if (s->tlsext_status_type != TLSEXT_STATUSTYPE_ocsp)
return 1;
@ -418,6 +422,10 @@ int tls_construct_ctos_sct(SSL *s, WPACKET *pkt, X509 *x, size_t chain, int *al)
if (s->ct_validation_callback == NULL)
return 1;
/* Not defined for client Certificates */
if (x != NULL)
return 1;
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_signed_certificate_timestamp)
|| !WPACKET_put_bytes_u16(pkt, 0)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_SCT, ERR_R_INTERNAL_ERROR);

View file

@ -224,6 +224,10 @@ int tls_parse_ctos_status_request(SSL *s, PACKET *pkt, X509 *x, size_t chain,
{
PACKET responder_id_list, exts;
/* Not defined if we get one of these in a client Certificate */
if (x != NULL)
return 1;
if (!PACKET_get_1(pkt, (unsigned int *)&s->tlsext_status_type)) {
*al = SSL_AD_DECODE_ERROR;
return 0;
@ -752,6 +756,9 @@ int tls_construct_stoc_status_request(SSL *s, WPACKET *pkt, X509 *x,
if (!s->tlsext_status_expected)
return 1;
if (SSL_IS_TLS13(s) && chain != 0)
return 1;
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_status_request)
|| !WPACKET_put_bytes_u16(pkt, 0)) {
SSLerr(SSL_F_TLS_CONSTRUCT_STOC_STATUS_REQUEST, ERR_R_INTERNAL_ERROR);

View file

@ -1371,19 +1371,23 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
const unsigned char *certstart, *certbytes;
STACK_OF(X509) *sk = NULL;
EVP_PKEY *pkey = NULL;
size_t chain;
unsigned int context = 0;
if ((sk = sk_X509_new_null()) == NULL) {
SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_MALLOC_FAILURE);
goto err;
}
if (!PACKET_get_net_3(pkt, &cert_list_len)
|| PACKET_remaining(pkt) != cert_list_len) {
if ((SSL_IS_TLS13(s) && !PACKET_get_1(pkt, &context))
|| context != 0
|| !PACKET_get_net_3(pkt, &cert_list_len)
|| PACKET_remaining(pkt) != cert_list_len) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_LENGTH_MISMATCH);
goto f_err;
}
while (PACKET_remaining(pkt)) {
for (chain = 0; PACKET_remaining(pkt); chain++) {
if (!PACKET_get_net_3(pkt, &cert_len)
|| !PACKET_get_bytes(pkt, &certbytes, cert_len)) {
al = SSL_AD_DECODE_ERROR;
@ -1405,6 +1409,23 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
SSL_R_CERT_LENGTH_MISMATCH);
goto f_err;
}
if (SSL_IS_TLS13(s)) {
RAW_EXTENSION *rawexts = NULL;
PACKET extensions;
if (!PACKET_get_length_prefixed_2(pkt, &extensions)) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_BAD_LENGTH);
goto f_err;
}
if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
&rawexts, &al)
|| !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
rawexts, x, chain, &al))
goto f_err;
}
if (!sk_X509_push(sk, x)) {
SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, ERR_R_MALLOC_FAILURE);
goto err;
@ -2986,11 +3007,19 @@ WORK_STATE tls_prepare_client_certificate(SSL *s, WORK_STATE wst)
int tls_construct_client_certificate(SSL *s, WPACKET *pkt)
{
if (!ssl3_output_cert_chain(s, pkt,
int al;
/*
* TODO(TLS1.3): For now we must put an empty context. Needs to be filled in
* later
*/
if ((SSL_IS_TLS13(s) && !WPACKET_put_bytes_u8(pkt, 0))
|| !ssl3_output_cert_chain(s, pkt,
(s->s3->tmp.cert_req == 2) ? NULL
: s->cert->key)) {
: s->cert->key,
&al)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
ssl3_send_alert(s, SSL3_AL_FATAL, al);
return 0;
}
@ -3108,18 +3137,9 @@ static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt)
goto err;
}
/*
* TODO(TLS1.3): For now we are processing Encrypted Extensions and
* Certificate extensions as part of this one message. Later we need to
* split out the Certificate extensions into the Certificate message
*/
if (!tls_collect_extensions(s, &extensions,
EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| EXT_TLS1_3_CERTIFICATE,
if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
&rawexts, &al)
|| !tls_parse_all_extensions(s,
EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| EXT_TLS1_3_CERTIFICATE,
|| !tls_parse_all_extensions(s, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
rawexts, NULL, 0, &al))
goto err;

View file

@ -308,12 +308,139 @@ int tls_construct_change_cipher_spec(SSL *s, WPACKET *pkt)
return 1;
}
unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk)
/* Add a certificate to the WPACKET */
static int ssl_add_cert_to_wpacket(SSL *s, WPACKET *pkt, X509 *x, int chain,
int *al)
{
int len;
unsigned char *outbytes;
len = i2d_X509(x, NULL);
if (len < 0) {
SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_BUF_LIB);
*al = SSL_AD_INTERNAL_ERROR;
return 0;
}
if (!WPACKET_sub_allocate_bytes_u24(pkt, len, &outbytes)
|| i2d_X509(x, &outbytes) != len) {
SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_INTERNAL_ERROR);
*al = SSL_AD_INTERNAL_ERROR;
return 0;
}
if (SSL_IS_TLS13(s)
&& !tls_construct_extensions(s, pkt, EXT_TLS1_3_CERTIFICATE, x,
chain, al))
return 0;
return 1;
}
/* Add certificate chain to provided WPACKET */
static int ssl_add_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk, int *al)
{
int i, chain_count;
X509 *x;
STACK_OF(X509) *extra_certs;
STACK_OF(X509) *chain = NULL;
X509_STORE *chain_store;
int tmpal = SSL_AD_INTERNAL_ERROR;
if (cpk == NULL || cpk->x509 == NULL)
return 1;
x = cpk->x509;
/*
* If we have a certificate specific chain use it, else use parent ctx.
*/
if (cpk->chain)
extra_certs = cpk->chain;
else
extra_certs = s->ctx->extra_certs;
if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs)
chain_store = NULL;
else if (s->cert->chain_store)
chain_store = s->cert->chain_store;
else
chain_store = s->ctx->cert_store;
if (chain_store) {
X509_STORE_CTX *xs_ctx = X509_STORE_CTX_new();
if (xs_ctx == NULL) {
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_MALLOC_FAILURE);
goto err;
}
if (!X509_STORE_CTX_init(xs_ctx, chain_store, x, NULL)) {
X509_STORE_CTX_free(xs_ctx);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_X509_LIB);
goto err;
}
/*
* It is valid for the chain not to be complete (because normally we
* don't include the root cert in the chain). Therefore we deliberately
* ignore the error return from this call. We're not actually verifying
* the cert - we're just building as much of the chain as we can
*/
(void)X509_verify_cert(xs_ctx);
/* Don't leave errors in the queue */
ERR_clear_error();
chain = X509_STORE_CTX_get0_chain(xs_ctx);
i = ssl_security_cert_chain(s, chain, NULL, 0);
if (i != 1) {
#if 0
/* Dummy error calls so mkerr generates them */
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_EE_KEY_TOO_SMALL);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_KEY_TOO_SMALL);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, SSL_R_CA_MD_TOO_WEAK);
#endif
X509_STORE_CTX_free(xs_ctx);
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
goto err;
}
chain_count = sk_X509_num(chain);
for (i = 0; i < chain_count; i++) {
x = sk_X509_value(chain, i);
if (!ssl_add_cert_to_wpacket(s, pkt, x, i, &tmpal)) {
X509_STORE_CTX_free(xs_ctx);
goto err;
}
}
X509_STORE_CTX_free(xs_ctx);
} else {
i = ssl_security_cert_chain(s, extra_certs, x, 0);
if (i != 1) {
SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
goto err;
}
if (!ssl_add_cert_to_wpacket(s, pkt, x, 0, &tmpal))
goto err;
for (i = 0; i < sk_X509_num(extra_certs); i++) {
x = sk_X509_value(extra_certs, i);
if (!ssl_add_cert_to_wpacket(s, pkt, x, i + 1, &tmpal))
goto err;
}
}
return 1;
err:
*al = tmpal;
return 0;
}
unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk,
int *al)
{
int tmpal = SSL_AD_INTERNAL_ERROR;
if (!WPACKET_start_sub_packet_u24(pkt)
|| !ssl_add_cert_chain(s, pkt, cpk)
|| !ssl_add_cert_chain(s, pkt, cpk, &tmpal)
|| !WPACKET_close(pkt)) {
SSLerr(SSL_F_SSL3_OUTPUT_CERT_CHAIN, ERR_R_INTERNAL_ERROR);
*al = tmpal;
return 0;
}
return 1;

View file

@ -3143,22 +3143,25 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
unsigned long l, llen;
const unsigned char *certstart, *certbytes;
STACK_OF(X509) *sk = NULL;
PACKET spkt;
PACKET spkt, context;
size_t chain;
if ((sk = sk_X509_new_null()) == NULL) {
SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, ERR_R_MALLOC_FAILURE);
goto f_err;
}
if (!PACKET_get_net_3(pkt, &llen)
|| !PACKET_get_sub_packet(pkt, &spkt, llen)
|| PACKET_remaining(pkt) != 0) {
/* TODO(TLS1.3): For now we ignore the context. We need to verify this */
if ((SSL_IS_TLS13(s) && !PACKET_get_length_prefixed_1(pkt, &context))
|| !PACKET_get_net_3(pkt, &llen)
|| !PACKET_get_sub_packet(pkt, &spkt, llen)
|| PACKET_remaining(pkt) != 0) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, SSL_R_LENGTH_MISMATCH);
goto f_err;
}
while (PACKET_remaining(&spkt) > 0) {
for (chain = 0; PACKET_remaining(&spkt) > 0; chain++) {
if (!PACKET_get_net_3(&spkt, &l)
|| !PACKET_get_bytes(&spkt, &certbytes, l)) {
al = SSL_AD_DECODE_ERROR;
@ -3179,6 +3182,23 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
SSL_R_CERT_LENGTH_MISMATCH);
goto f_err;
}
if (SSL_IS_TLS13(s)) {
RAW_EXTENSION *rawexts = NULL;
PACKET extensions;
if (!PACKET_get_length_prefixed_2(&spkt, &extensions)) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, SSL_R_BAD_LENGTH);
goto f_err;
}
if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
&rawexts, &al)
|| !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
rawexts, x, chain, &al))
goto f_err;
}
if (!sk_X509_push(sk, x)) {
SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, ERR_R_MALLOC_FAILURE);
goto f_err;
@ -3266,6 +3286,7 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
int tls_construct_server_certificate(SSL *s, WPACKET *pkt)
{
CERT_PKEY *cpk;
int al = SSL_AD_INTERNAL_ERROR;
cpk = ssl_get_server_send_pkey(s);
if (cpk == NULL) {
@ -3273,8 +3294,14 @@ int tls_construct_server_certificate(SSL *s, WPACKET *pkt)
return 0;
}
if (!ssl3_output_cert_chain(s, pkt, cpk)) {
/*
* In TLSv1.3 the certificate chain is always preceded by a 0 length context
* for the server Certificate message
*/
if ((SSL_IS_TLS13(s) && !WPACKET_put_bytes_u8(pkt, 0))
|| !ssl3_output_cert_chain(s, pkt, cpk, &al)) {
SSLerr(SSL_F_TLS_CONSTRUCT_SERVER_CERTIFICATE, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(s, SSL3_AL_FATAL, al);
return 0;
}
@ -3492,13 +3519,7 @@ static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt)
{
int al;
/*
* TODO(TLS1.3): For now we send certificate extensions in with the
* encrypted extensions. Later we need to move these to the certificate
* message.
*/
if (!tls_construct_extensions(s, pkt, EXT_TLS1_3_ENCRYPTED_EXTENSIONS
| EXT_TLS1_3_CERTIFICATE,
if (!tls_construct_extensions(s, pkt, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
NULL, 0, &al)) {
ssl3_send_alert(s, SSL3_AL_FATAL, al);
SSLerr(SSL_F_TLS_CONSTRUCT_ENCRYPTED_EXTENSIONS, ERR_R_INTERNAL_ERROR);

View file

@ -87,10 +87,12 @@ $ENV{CTLOG_FILE} = srctop_file("test", "ct", "log_list.conf");
[TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_SERVER_NAME,
checkhandshake::SERVER_NAME_SRV_EXTENSION],
[TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_STATUS_REQUEST,
checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
[TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS, TLSProxy::Message::EXT_ALPN,
checkhandshake::ALPN_SRV_EXTENSION],
[TLSProxy::Message::MT_CERTIFICATE, TLSProxy::Message::EXT_STATUS_REQUEST,
checkhandshake::STATUS_REQUEST_SRV_EXTENSION],
[0,0,0]
);

View file

@ -73,8 +73,14 @@ sub checkhandshake($$$$)
if (($handtype & RENEG_HANDSHAKE) != 0) {
$numtests += $#extensions + 2;
}
#In TLS1.3 there are 3 messages with extensions (and no renegotiations)
$numtests += 1 if ($proxy->is_tls13());
#In TLS1.3 there are 4 messages with extensions (i.e. 2 extra) and no
#renegotiations: 1 ClientHello, 1 ServerHello, 1 EncryptedExtensions,
#1 Certificate
$numtests += 2 if ($proxy->is_tls13());
#Except in Client auth where we have an extra Certificate message, and
#one extension gets checked twice (once in each Certificate message)
$numtests += 2 if ($proxy->is_tls13()
&& ($handtype & CLIENT_AUTH_HANDSHAKE) != 0);
plan tests => $numtests;
@ -101,7 +107,11 @@ sub checkhandshake($$$$)
next if ($message->mt() != TLSProxy::Message::MT_CLIENT_HELLO
&& $message->mt() != TLSProxy::Message::MT_SERVER_HELLO
&& $message->mt() !=
TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS);
TLSProxy::Message::MT_ENCRYPTED_EXTENSIONS
&& $message->mt() != TLSProxy::Message::MT_CERTIFICATE);
next if $message->mt() == TLSProxy::Message::MT_CERTIFICATE
&& !TLSProxy::Proxy::is_tls13();
if ($message->mt() == TLSProxy::Message::MT_CLIENT_HELLO) {
#Add renegotiate extension we will expect if renegotiating

View file

@ -0,0 +1,219 @@
# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html
use strict;
package TLSProxy::Certificate;
use vars '@ISA';
push @ISA, 'TLSProxy::Message';
sub new
{
my $class = shift;
my ($server,
$data,
$records,
$startoffset,
$message_frag_lens) = @_;
my $self = $class->SUPER::new(
$server,
TLSProxy::Message::MT_CERTIFICATE,
$data,
$records,
$startoffset,
$message_frag_lens);
$self->{first_certificate} = "";
$self->{extension_data} = "";
$self->{remaining_certdata} = "";
return $self;
}
sub parse
{
my $self = shift;
if (TLSProxy::Proxy->is_tls13()) {
my $context_len = unpack('C', $self->data);
my $context = substr($self->data, 1, $context_len);
my $remdata = substr($self->data, 1 + $context_len);
my ($hicertlistlen, $certlistlen) = unpack('Cn', $remdata);
$certlistlen += ($hicertlistlen << 16);
$remdata = substr($remdata, 3);
die "Invalid Certificate List length"
if length($remdata) != $certlistlen;
my ($hicertlen, $certlen) = unpack('Cn', $remdata);
$certlen += ($hicertlen << 16);
die "Certificate too long" if ($certlen + 3) > $certlistlen;
$remdata = substr($remdata, 3);
my $certdata = substr($remdata, 0, $certlen);
$remdata = substr($remdata, $certlen);
my $extensions_len = unpack('n', $remdata);
$remdata = substr($remdata, 2);
die "Extensions too long"
if ($certlen + 3 + $extensions_len + 2) > $certlistlen;
my $extension_data = "";
if ($extensions_len != 0) {
$extension_data = substr($remdata, 0, $extensions_len);
if (length($extension_data) != $extensions_len) {
die "Invalid extension length\n";
}
}
my %extensions = ();
while (length($extension_data) >= 4) {
my ($type, $size) = unpack("nn", $extension_data);
my $extdata = substr($extension_data, 4, $size);
$extension_data = substr($extension_data, 4 + $size);
$extensions{$type} = $extdata;
}
$remdata = substr($remdata, $extensions_len);
$self->context($context);
$self->first_certificate($certdata);
$self->extension_data(\%extensions);
$self->remaining_certdata($remdata);
print " Context:".$context."\n";
print " Certificate List Len:".$certlistlen."\n";
print " Certificate Len:".$certlen."\n";
print " Extensions Len:".$extensions_len."\n";
} else {
my ($hicertlistlen, $certlistlen) = unpack('Cn', $self->data);
$certlistlen += ($hicertlistlen << 16);
my $remdata = substr($self->data, 3);
die "Invalid Certificate List length"
if length($remdata) != $certlistlen;
my ($hicertlen, $certlen) = unpack('Cn', $remdata);
$certlen += ($hicertlen << 16);
die "Certificate too long" if ($certlen + 3) > $certlistlen;
$remdata = substr($remdata, 3);
my $certdata = substr($remdata, 0, $certlen);
$remdata = substr($remdata, $certlen);
$self->first_certificate($certdata);
$self->remaining_certdata($remdata);
print " Certificate List Len:".$certlistlen."\n";
print " Certificate Len:".$certlen."\n";
}
}
#Reconstruct the on-the-wire message data following changes
sub set_message_contents
{
my $self = shift;
my $data;
my $extensions = "";
if (TLSProxy::Proxy->is_tls13()) {
foreach my $key (keys %{$self->extension_data}) {
my $extdata = ${$self->extension_data}{$key};
$extensions .= pack("n", $key);
$extensions .= pack("n", length($extdata));
$extensions .= $extdata;
if ($key == TLSProxy::Message::EXT_DUPLICATE_EXTENSION) {
$extensions .= pack("n", $key);
$extensions .= pack("n", length($extdata));
$extensions .= $extdata;
}
}
$data = pack('C', length($self->context()));
$data .= $self->context;
my $certlen = length($self->first_certificate);
my $certlistlen = $certlen + length($extensions)
+ length($self->remaining_certdata);
my $hi = $certlistlen >> 16;
$certlistlen = $certlistlen & 0xffff;
$data .= pack('Cn', $hi, $certlistlen);
$hi = $certlen >> 16;
$certlen = $certlen & 0xffff;
$data .= pack('Cn', $hi, $certlen);
$data .= pack('n', length($extensions));
$data .= $extensions;
$data .= $self->remaining_certdata();
$self->data($data);
} else {
my $certlen = length($self->first_certificate);
my $certlistlen = $certlen + length($self->remaining_certdata);
my $hi = $certlistlen >> 16;
$certlistlen = $certlistlen & 0xffff;
$data .= pack('Cn', $hi, $certlistlen);
$hi = $certlen >> 16;
$certlen = $certlen & 0xffff;
$data .= pack('Cn', $hi, $certlen);
$data .= $self->remaining_certdata();
$self->data($data);
}
}
#Read/write accessors
sub context
{
my $self = shift;
if (@_) {
$self->{context} = shift;
}
return $self->{context};
}
sub first_certificate
{
my $self = shift;
if (@_) {
$self->{first_certificate} = shift;
}
return $self->{first_certificate};
}
sub remaining_certdata
{
my $self = shift;
if (@_) {
$self->{remaining_certdata} = shift;
}
return $self->{remaining_certdata};
}
sub extension_data
{
my $self = shift;
if (@_) {
$self->{extension_data} = shift;
}
return $self->{extension_data};
}
sub set_extension
{
my ($self, $ext_type, $ext_data) = @_;
$self->{extension_data}{$ext_type} = $ext_data;
}
sub delete_extension
{
my ($self, $ext_type) = @_;
delete $self->{extension_data}{$ext_type};
}
1;

View file

@ -268,6 +268,15 @@ sub create_message
[@message_frag_lens]
);
$message->parse();
} elsif ($mt == MT_CERTIFICATE) {
$message = TLSProxy::Certificate->new(
$server,
$data,
[@message_rec_list],
$startoffset,
[@message_frag_lens]
);
$message->parse();
} elsif ($mt == MT_SERVER_KEY_EXCHANGE) {
$message = TLSProxy::ServerKeyExchange->new(
$server,

View file

@ -18,6 +18,7 @@ use TLSProxy::Message;
use TLSProxy::ClientHello;
use TLSProxy::ServerHello;
use TLSProxy::EncryptedExtensions;
use TLSProxy::Certificate;
use TLSProxy::ServerKeyExchange;
use TLSProxy::NewSessionTicket;