Add client side support for parsing Hello Retry Request

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2341)
This commit is contained in:
Matt Caswell 2017-02-01 13:31:27 +00:00
parent 7d061fced3
commit 3847d426e3
6 changed files with 204 additions and 54 deletions

View file

@ -881,7 +881,8 @@ typedef enum {
TLS_ST_CR_CERT_VRFY,
TLS_ST_SW_CERT_VRFY,
TLS_ST_CR_HELLO_REQ,
TLS_ST_SW_HELLO_RETRY_REQUEST
TLS_ST_SW_HELLO_RETRY_REQUEST,
TLS_ST_CR_HELLO_RETRY_REQUEST
} OSSL_HANDSHAKE_STATE;
/*
@ -2073,6 +2074,7 @@ int ERR_load_SSL_strings(void);
/* Function codes. */
# define SSL_F_ADD_CLIENT_KEY_SHARE_EXT 438
# define SSL_F_ADD_KEY_SHARE 512
# define SSL_F_CHECK_SUITEB_CIPHER_LIST 331
# define SSL_F_CT_MOVE_SCTS 345
# define SSL_F_CT_STRICT 349
@ -2355,6 +2357,7 @@ int ERR_load_SSL_strings(void);
# define SSL_F_TLS_PROCESS_ENCRYPTED_EXTENSIONS 444
# define SSL_F_TLS_PROCESS_FINISHED 364
# define SSL_F_TLS_PROCESS_HELLO_REQ 507
# define SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST 511
# define SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT 442
# define SSL_F_TLS_PROCESS_KEY_EXCHANGE 365
# define SSL_F_TLS_PROCESS_NEW_SESSION_TICKET 366

View file

@ -20,6 +20,7 @@
static ERR_STRING_DATA SSL_str_functs[] = {
{ERR_FUNC(SSL_F_ADD_CLIENT_KEY_SHARE_EXT), "add_client_key_share_ext"},
{ERR_FUNC(SSL_F_ADD_KEY_SHARE), "add_key_share"},
{ERR_FUNC(SSL_F_CHECK_SUITEB_CIPHER_LIST), "check_suiteb_cipher_list"},
{ERR_FUNC(SSL_F_CT_MOVE_SCTS), "ct_move_scts"},
{ERR_FUNC(SSL_F_CT_STRICT), "ct_strict"},
@ -412,6 +413,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
"tls_process_encrypted_extensions"},
{ERR_FUNC(SSL_F_TLS_PROCESS_FINISHED), "tls_process_finished"},
{ERR_FUNC(SSL_F_TLS_PROCESS_HELLO_REQ), "tls_process_hello_req"},
{ERR_FUNC(SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST),
"tls_process_hello_retry_request"},
{ERR_FUNC(SSL_F_TLS_PROCESS_INITIAL_SERVER_FLIGHT),
"tls_process_initial_server_flight"},
{ERR_FUNC(SSL_F_TLS_PROCESS_KEY_EXCHANGE), "tls_process_key_exchange"},

View file

@ -528,12 +528,57 @@ int tls_construct_ctos_psk_kex_modes(SSL *s, WPACKET *pkt, unsigned int context,
return 1;
}
#ifndef OPENSSL_NO_TLS1_3
static int add_key_share(SSL *s, WPACKET *pkt, unsigned int curve_id)
{
unsigned char *encodedPoint = NULL;
EVP_PKEY *key_share_key = NULL;
size_t encodedlen;
key_share_key = ssl_generate_pkey_curve(curve_id);
if (key_share_key == NULL) {
SSLerr(SSL_F_ADD_KEY_SHARE, ERR_R_EVP_LIB);
return 0;
}
/* Encode the public key. */
encodedlen = EVP_PKEY_get1_tls_encodedpoint(key_share_key,
&encodedPoint);
if (encodedlen == 0) {
SSLerr(SSL_F_ADD_KEY_SHARE, ERR_R_EC_LIB);
EVP_PKEY_free(key_share_key);
return 0;
}
/* Create KeyShareEntry */
if (!WPACKET_put_bytes_u16(pkt, curve_id)
|| !WPACKET_sub_memcpy_u16(pkt, encodedPoint, encodedlen)) {
SSLerr(SSL_F_ADD_KEY_SHARE, ERR_R_INTERNAL_ERROR);
EVP_PKEY_free(key_share_key);
OPENSSL_free(encodedPoint);
return 0;
}
/*
* TODO(TLS1.3): When changing to send more than one key_share we're
* going to need to be able to save more than one EVP_PKEY. For now
* we reuse the existing tmp.pkey
*/
s->s3->tmp.pkey = key_share_key;
s->s3->group_id = curve_id;
OPENSSL_free(encodedPoint);
return 1;
}
#endif
int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, unsigned int context,
X509 *x, size_t chainidx, int *al)
{
#ifndef OPENSSL_NO_TLS1_3
size_t i, sharessent = 0, num_curves = 0;
size_t i, num_curves = 0;
const unsigned char *pcurves = NULL;
unsigned int curve_id = 0;
/* key_share extension */
if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share)
@ -551,62 +596,37 @@ int tls_construct_ctos_key_share(SSL *s, WPACKET *pkt, unsigned int context,
return 0;
}
if (s->s3->tmp.pkey != NULL) {
/* Shouldn't happen! */
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, ERR_R_INTERNAL_ERROR);
return 0;
}
/*
* TODO(TLS1.3): Make the number of key_shares sent configurable. For
* now, just send one
*/
for (i = 0; i < num_curves && sharessent < 1; i++, pcurves += 2) {
unsigned char *encodedPoint = NULL;
unsigned int curve_id = 0;
EVP_PKEY *key_share_key = NULL;
size_t encodedlen;
if (s->s3->group_id != 0) {
curve_id = s->s3->group_id;
} else {
for (i = 0; i < num_curves; i++, pcurves += 2) {
if (!tls_curve_allowed(s, pcurves, SSL_SECOP_CURVE_SUPPORTED))
continue;
if (!tls_curve_allowed(s, pcurves, SSL_SECOP_CURVE_SUPPORTED))
continue;
if (s->s3->tmp.pkey != NULL) {
/* Shouldn't happen! */
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, ERR_R_INTERNAL_ERROR);
return 0;
curve_id = (pcurves[0] << 8) | pcurves[1];
break;
}
/* Generate a key for this key_share */
curve_id = (pcurves[0] << 8) | pcurves[1];
key_share_key = ssl_generate_pkey_curve(curve_id);
if (key_share_key == NULL) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, ERR_R_EVP_LIB);
return 0;
}
/* Encode the public key. */
encodedlen = EVP_PKEY_get1_tls_encodedpoint(key_share_key,
&encodedPoint);
if (encodedlen == 0) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, ERR_R_EC_LIB);
EVP_PKEY_free(key_share_key);
return 0;
}
/* Create KeyShareEntry */
if (!WPACKET_put_bytes_u16(pkt, curve_id)
|| !WPACKET_sub_memcpy_u16(pkt, encodedPoint, encodedlen)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, ERR_R_INTERNAL_ERROR);
EVP_PKEY_free(key_share_key);
OPENSSL_free(encodedPoint);
return 0;
}
/*
* TODO(TLS1.3): When changing to send more than one key_share we're
* going to need to be able to save more than one EVP_PKEY. For now
* we reuse the existing tmp.pkey
*/
s->s3->group_id = curve_id;
s->s3->tmp.pkey = key_share_key;
sharessent++;
OPENSSL_free(encodedPoint);
}
if (curve_id == 0) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, SSL_R_NO_SUITABLE_KEY_SHARE);
return 0;
}
if (!add_key_share(s, pkt, curve_id))
return 0;
if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) {
SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, ERR_R_INTERNAL_ERROR);
return 0;
@ -1188,6 +1208,49 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
return 0;
}
if ((context & EXT_TLS1_3_HELLO_RETRY_REQUEST) != 0) {
unsigned const char *pcurves = NULL;
size_t i, num_curves;
if (PACKET_remaining(pkt) != 0) {
*al = SSL_AD_HANDSHAKE_FAILURE;
SSLerr(SSL_F_TLS_PARSE_STOC_KEY_SHARE, SSL_R_LENGTH_MISMATCH);
return 0;
}
/*
* It is an error if the HelloRetryRequest wants a key_share that we
* already sent in the first ClientHello
*/
if (group_id == s->s3->group_id) {
*al = SSL_AD_ILLEGAL_PARAMETER;
SSLerr(SSL_F_TLS_PARSE_STOC_KEY_SHARE, SSL_R_BAD_KEY_SHARE);
return 0;
}
/* Validate the selected group is one we support */
pcurves = s->ext.supportedgroups;
if (!tls1_get_curvelist(s, 0, &pcurves, &num_curves)) {
SSLerr(SSL_F_TLS_PARSE_STOC_KEY_SHARE, ERR_R_INTERNAL_ERROR);
return 0;
}
for (i = 0; i < num_curves; i++, pcurves += 2) {
if (group_id == (unsigned int)((pcurves[0] << 8) | pcurves[1]))
break;
}
if (i >= num_curves
|| !tls_curve_allowed(s, pcurves, SSL_SECOP_CURVE_SUPPORTED)) {
*al = SSL_AD_ILLEGAL_PARAMETER;
SSLerr(SSL_F_TLS_PARSE_STOC_KEY_SHARE, SSL_R_BAD_KEY_SHARE);
return 0;
}
s->s3->group_id = group_id;
EVP_PKEY_free(s->s3->tmp.pkey);
s->s3->tmp.pkey = NULL;
return 1;
}
if (group_id != s->s3->group_id) {
/*
* This isn't for the group that we sent in the original

View file

@ -60,6 +60,7 @@
#include <openssl/bn.h>
#include <openssl/engine.h>
static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt);
static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt);
static ossl_inline int cert_req_allowed(SSL *s);
@ -137,6 +138,17 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt)
default:
break;
case TLS_ST_CW_CLNT_HELLO:
/*
* This must a ClientHello following a HelloRetryRequest, so the only
* thing we can get now is a ServerHello.
*/
if (mt == SSL3_MT_SERVER_HELLO) {
st->hand_state = TLS_ST_CR_SRVR_HELLO;
return 1;
}
break;
case TLS_ST_CR_SRVR_HELLO:
if (mt == SSL3_MT_ENCRYPTED_EXTENSIONS) {
st->hand_state = TLS_ST_CR_ENCRYPTED_EXTENSIONS;
@ -210,8 +222,8 @@ int ossl_statem_client_read_transition(SSL *s, int mt)
int ske_expected;
/*
* Note that after a ClientHello we don't know what version we are going
* to negotiate yet, so we don't take this branch until later
* Note that after writing the first ClientHello we don't know what version
* we are going to negotiate yet, so we don't take this branch until later.
*/
if (SSL_IS_TLS13(s)) {
if (!ossl_statem_client13_read_transition(s, mt))
@ -234,6 +246,11 @@ int ossl_statem_client_read_transition(SSL *s, int mt)
st->hand_state = DTLS_ST_CR_HELLO_VERIFY_REQUEST;
return 1;
}
} else {
if (mt == SSL3_MT_HELLO_RETRY_REQUEST) {
st->hand_state = TLS_ST_CR_HELLO_RETRY_REQUEST;
return 1;
}
}
break;
@ -390,15 +407,23 @@ static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s)
*/
/*
* Note: There are no cases for TLS_ST_BEFORE or TLS_ST_CW_CLNT_HELLO,
* because we haven't negotiated TLSv1.3 yet at that point. They are
* handled by ossl_statem_client_write_transition().
* Note: There are no cases for TLS_ST_BEFORE because we haven't negotiated
* TLSv1.3 yet at that point. They are handled by
* ossl_statem_client_write_transition().
*/
switch (st->hand_state) {
default:
/* Shouldn't happen */
return WRITE_TRAN_ERROR;
case TLS_ST_CW_CLNT_HELLO:
/* We only hit this in the case of HelloRetryRequest */
return WRITE_TRAN_FINISHED;
case TLS_ST_CR_HELLO_RETRY_REQUEST:
st->hand_state = TLS_ST_CW_CLNT_HELLO;
return WRITE_TRAN_CONTINUE;
case TLS_ST_CR_FINISHED:
st->hand_state = (s->s3->tmp.cert_req != 0) ? TLS_ST_CW_CERT
: TLS_ST_CW_FINISHED;
@ -779,6 +804,9 @@ size_t ossl_statem_client_max_message_size(SSL *s)
case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
return HELLO_VERIFY_REQUEST_MAX_LENGTH;
case TLS_ST_CR_HELLO_RETRY_REQUEST:
return HELLO_RETRY_REQUEST_MAX_LENGTH;
case TLS_ST_CR_CERT:
return s->max_cert_list;
@ -836,6 +864,9 @@ MSG_PROCESS_RETURN ossl_statem_client_process_message(SSL *s, PACKET *pkt)
case DTLS_ST_CR_HELLO_VERIFY_REQUEST:
return dtls_process_hello_verify(s, pkt);
case TLS_ST_CR_HELLO_RETRY_REQUEST:
return tls_process_hello_retry_request(s, pkt);
case TLS_ST_CR_CERT:
return tls_process_server_certificate(s, pkt);
@ -1432,6 +1463,52 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
return MSG_PROCESS_ERROR;
}
static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
{
unsigned int sversion;
int protverr;
RAW_EXTENSION *extensions = NULL;
int al;
PACKET extpkt;
if (!PACKET_get_net_2(pkt, &sversion)) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST, SSL_R_LENGTH_MISMATCH);
goto f_err;
}
s->hello_retry_request = 1;
/* This will fail if it doesn't choose TLSv1.3+ */
protverr = ssl_choose_client_version(s, sversion);
if (protverr != 0) {
al = SSL_AD_PROTOCOL_VERSION;
SSLerr(SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST, protverr);
goto f_err;
}
if (!PACKET_as_length_prefixed_2(pkt, &extpkt)) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST, SSL_R_BAD_LENGTH);
goto f_err;
}
if (!tls_collect_extensions(s, &extpkt, EXT_TLS1_3_HELLO_RETRY_REQUEST,
&extensions, &al)
|| !tls_parse_all_extensions(s, EXT_TLS1_3_HELLO_RETRY_REQUEST,
extensions, NULL, 0, &al))
goto f_err;
OPENSSL_free(extensions);
return MSG_PROCESS_FINISHED_READING;
f_err:
ssl3_send_alert(s, SSL3_AL_FATAL, al);
ossl_statem_set_error(s);
OPENSSL_free(extensions);
return MSG_PROCESS_ERROR;
}
MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
{
int al, i, ret = MSG_PROCESS_ERROR, exp_idx;

View file

@ -1595,6 +1595,9 @@ int ssl_choose_client_version(SSL *s, int version)
continue;
if (vent->cmeth == NULL)
break;
if (s->hello_retry_request && version != TLS1_3_VERSION)
return SSL_R_WRONG_SSL_VERSION;
method = vent->cmeth();
err = ssl_method_error(s, method);
if (err != 0)

View file

@ -19,6 +19,7 @@
/* The spec allows for a longer length than this, but we limit it */
#define HELLO_VERIFY_REQUEST_MAX_LENGTH 258
#define SERVER_HELLO_MAX_LENGTH 20000
#define HELLO_RETRY_REQUEST_MAX_LENGTH 20000
#define ENCRYPTED_EXTENSIONS_MAX_LENGTH 20000
#define SERVER_KEY_EXCH_MAX_LENGTH 102400
#define SERVER_HELLO_DONE_MAX_LENGTH 0