From: Matt Caswell Date: Tue, 21 Feb 2017 09:22:22 +0000 (+0000) Subject: Provide functions to write early data X-Git-Tag: OpenSSL_1_1_1-pre1~2172 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=49e7fe12eac1e84af726e0110ee29073699ed46b;p=openssl Provide functions to write early data We provide SSL_write_early() which *must* be called first on a connection (prior to any other IO function including SSL_connect()/SSL_do_handshake()). Also SSL_write_early_finish() which signals the end of early data. Reviewed-by: Rich Salz (Merged from https://github.com/openssl/openssl/pull/2737) --- diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 7f84e0a265..7d3ac4e253 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1025,6 +1025,7 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) # define SSL_AD_INTERNAL_ERROR TLS1_AD_INTERNAL_ERROR # define SSL_AD_USER_CANCELLED TLS1_AD_USER_CANCELLED # define SSL_AD_NO_RENEGOTIATION TLS1_AD_NO_RENEGOTIATION +# define SSL_AD_END_OF_EARLY_DATA TLS13_AD_END_OF_EARLY_DATA # define SSL_AD_MISSING_EXTENSION TLS13_AD_MISSING_EXTENSION # define SSL_AD_UNSUPPORTED_EXTENSION TLS1_AD_UNSUPPORTED_EXTENSION # define SSL_AD_CERTIFICATE_UNOBTAINABLE TLS1_AD_CERTIFICATE_UNOBTAINABLE @@ -1614,6 +1615,9 @@ __owur int SSL_peek(SSL *ssl, void *buf, int num); __owur int SSL_peek_ex(SSL *ssl, void *buf, size_t num, size_t *readbytes); __owur int SSL_write(SSL *ssl, const void *buf, int num); __owur int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written); +__owur int SSL_write_early(SSL *s, const void *buf, size_t num, + size_t *written); +__owur int SSL_write_early_finish(SSL *s); long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg); long SSL_callback_ctrl(SSL *, int, void (*)(void)); long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg); @@ -2290,6 +2294,8 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_VALIDATE_CT 400 # define SSL_F_SSL_VERIFY_CERT_CHAIN 207 # define SSL_F_SSL_WRITE 208 +# define SSL_F_SSL_WRITE_EARLY 526 +# define SSL_F_SSL_WRITE_EARLY_FINISH 527 # define SSL_F_SSL_WRITE_EX 433 # define SSL_F_SSL_WRITE_INTERNAL 524 # define SSL_F_STATE_MACHINE 353 @@ -2382,7 +2388,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_PARSE_CTOS_PSK 505 # define SSL_F_TLS_PARSE_CTOS_RENEGOTIATE 464 # define SSL_F_TLS_PARSE_CTOS_USE_SRTP 465 -# define SSL_F_TLS_PARSE_STOC_EARLY_DATA_INFO 520 +# define SSL_F_TLS_PARSE_STOC_EARLY_DATA_INFO 528 # define SSL_F_TLS_PARSE_STOC_KEY_SHARE 445 # define SSL_F_TLS_PARSE_STOC_PSK 502 # define SSL_F_TLS_PARSE_STOC_RENEGOTIATE 448 diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 4fb5a560ad..63e9ee35cb 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -104,6 +104,7 @@ extern "C" { # define TLS1_AD_USER_CANCELLED 90 # define TLS1_AD_NO_RENEGOTIATION 100 /* TLSv1.3 alerts */ +# define TLS13_AD_END_OF_EARLY_DATA 1 # define TLS13_AD_MISSING_EXTENSION 109 /* fatal */ /* codes 110-114 are from RFC3546 */ # define TLS1_AD_UNSUPPORTED_EXTENSION 110 diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index 5aea4b31bd..d7b98e055b 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -745,7 +745,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, } /* Explicit IV length, block ciphers appropriate version flag */ - if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s)) { + if (s->enc_write_ctx && SSL_USE_EXPLICIT_IV(s) && !SSL_TREAT_AS_TLS13(s)) { int mode = EVP_CIPHER_CTX_mode(s->enc_write_ctx); if (mode == EVP_CIPH_CBC_MODE) { /* TODO(size_t): Convert me */ @@ -764,7 +764,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, /* Clear our SSL3_RECORD structures */ memset(wr, 0, sizeof wr); for (j = 0; j < numpipes; j++) { - unsigned int version = SSL_IS_TLS13(s) ? TLS1_VERSION : s->version; + unsigned int version = SSL_TREAT_AS_TLS13(s) ? TLS1_VERSION : s->version; unsigned char *compressdata = NULL; size_t maxcomplen; unsigned int rectype; @@ -777,7 +777,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, * In TLSv1.3, once encrypting, we always use application data for the * record type */ - if (SSL_IS_TLS13(s) && s->enc_write_ctx != NULL) + if (SSL_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) rectype = SSL3_RT_APPLICATION_DATA; else rectype = type; @@ -835,7 +835,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, SSL3_RECORD_reset_input(&wr[j]); } - if (SSL_IS_TLS13(s) && s->enc_write_ctx != NULL) { + if (SSL_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) { if (!WPACKET_put_bytes_u8(thispkt, type)) { SSLerr(SSL_F_DO_SSL3_WRITE, ERR_R_INTERNAL_ERROR); goto err; @@ -887,8 +887,17 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, SSL3_RECORD_set_length(thiswr, len); } - if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1) - goto err; + if (s->early_data_state == SSL_EARLY_DATA_WRITING) { + /* + * We haven't actually negotiated the version yet, but we're trying to + * send early data - so we need to use the the tls13enc function. + */ + if (tls13_enc(s, wr, numpipes, 1) < 1) + goto err; + } else { + if (s->method->ssl3_enc->enc(s, wr, numpipes, 1) < 1) + goto err; + } for (j = 0; j < numpipes; j++) { size_t origlen; diff --git a/ssl/record/ssl3_record_tls13.c b/ssl/record/ssl3_record_tls13.c index d96a042ff9..87041df2c7 100644 --- a/ssl/record/ssl3_record_tls13.c +++ b/ssl/record/ssl3_record_tls13.c @@ -56,14 +56,18 @@ int tls13_enc(SSL *s, SSL3_RECORD *recs, size_t n_recs, int send) ivlen = EVP_CIPHER_CTX_iv_length(ctx); - /* - * To get here we must have selected a ciphersuite - otherwise ctx would - * be NULL - */ - assert(s->s3->tmp.new_cipher != NULL); - if (s->s3->tmp.new_cipher == NULL) - return -1; - alg_enc = s->s3->tmp.new_cipher->algorithm_enc; + if (s->early_data_state == SSL_EARLY_DATA_WRITING) { + alg_enc = s->session->cipher->algorithm_enc; + } else { + /* + * To get here we must have selected a ciphersuite - otherwise ctx would + * be NULL + */ + assert(s->s3->tmp.new_cipher != NULL); + if (s->s3->tmp.new_cipher == NULL) + return -1; + alg_enc = s->s3->tmp.new_cipher->algorithm_enc; + } if (alg_enc & SSL_AESCCM) { if (alg_enc & (SSL_AES128CCM8 | SSL_AES256CCM8)) diff --git a/ssl/s3_msg.c b/ssl/s3_msg.c index 743a02b8d1..7af2f99e05 100644 --- a/ssl/s3_msg.c +++ b/ssl/s3_msg.c @@ -63,7 +63,10 @@ int ssl3_do_change_cipher_spec(SSL *s) int ssl3_send_alert(SSL *s, int level, int desc) { /* Map tls/ssl alert value to correct one */ - desc = s->method->ssl3_enc->alert_value(desc); + if (SSL_TREAT_AS_TLS13(s)) + desc = tls13_alert_code(desc); + else + desc = s->method->ssl3_enc->alert_value(desc); if (s->version == SSL3_VERSION && desc == SSL_AD_PROTOCOL_VERSION) desc = SSL_AD_HANDSHAKE_FAILURE; /* SSL 3.0 does not have * protocol_version alerts */ diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 69d9adc9d7..41d4a69dd8 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -253,6 +253,8 @@ static ERR_STRING_DATA SSL_str_functs[] = { {ERR_FUNC(SSL_F_SSL_VALIDATE_CT), "ssl_validate_ct"}, {ERR_FUNC(SSL_F_SSL_VERIFY_CERT_CHAIN), "ssl_verify_cert_chain"}, {ERR_FUNC(SSL_F_SSL_WRITE), "SSL_write"}, + {ERR_FUNC(SSL_F_SSL_WRITE_EARLY), "SSL_write_early"}, + {ERR_FUNC(SSL_F_SSL_WRITE_EARLY_FINISH), "SSL_write_early_finish"}, {ERR_FUNC(SSL_F_SSL_WRITE_EX), "SSL_write_ex"}, {ERR_FUNC(SSL_F_SSL_WRITE_INTERNAL), "ssl_write_internal"}, {ERR_FUNC(SSL_F_STATE_MACHINE), "state_machine"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index d3dddafad7..8e28786447 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -1667,6 +1667,10 @@ int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written) return -1; } + if (s->early_data_state == SSL_EARLY_DATA_WRITE_RETRY + || s->early_data_state == SSL_EARLY_DATA_CONNECT_RETRY) + return 0; + if ((s->mode & SSL_MODE_ASYNC) && ASYNC_get_current_job() == NULL) { int ret; struct ssl_async_args args; @@ -1716,6 +1720,76 @@ int SSL_write_ex(SSL *s, const void *buf, size_t num, size_t *written) return ret; } +int SSL_write_early(SSL *s, const void *buf, size_t num, size_t *written) +{ + int ret; + + if (s->server) { + SSLerr(SSL_F_SSL_WRITE_EARLY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + /* + * TODO(TLS1.3): Somehow we need to check that we're not sending too much + * data + */ + + switch (s->early_data_state) { + case SSL_EARLY_DATA_NONE: + if (!SSL_in_before(s)) { + SSLerr(SSL_F_SSL_WRITE_EARLY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + /* fall through */ + + case SSL_EARLY_DATA_CONNECT_RETRY: + s->early_data_state = SSL_EARLY_DATA_CONNECTING; + ret = SSL_connect(s); + if (ret <= 0) { + /* NBIO or error */ + s->early_data_state = SSL_EARLY_DATA_CONNECT_RETRY; + return 0; + } + /* fall through */ + + case SSL_EARLY_DATA_WRITE_RETRY: + s->early_data_state = SSL_EARLY_DATA_WRITING; + ret = SSL_write_ex(s, buf, num, written); + s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY; + return ret; + + default: + SSLerr(SSL_F_SSL_WRITE_EARLY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } +} + +int SSL_write_early_finish(SSL *s) +{ + int ret; + + if (s->early_data_state != SSL_EARLY_DATA_WRITE_RETRY) { + SSLerr(SSL_F_SSL_WRITE_EARLY_FINISH, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + s->early_data_state = SSL_EARLY_DATA_WRITING; + ret = ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_END_OF_EARLY_DATA); + if (ret <= 0) { + s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY; + return 0; + } + s->early_data_state = SSL_EARLY_DATA_FINISHED_WRITING; + /* + * We set the enc_write_ctx back to NULL because we may end up writing + * in cleartext again if we get a HelloRetryRequest from the server. + */ + EVP_CIPHER_CTX_free(s->enc_write_ctx); + s->enc_write_ctx = NULL; + ossl_statem_set_in_init(s, 1); + return 1; +} + int SSL_shutdown(SSL *s) { /* @@ -3073,6 +3147,10 @@ int SSL_do_handshake(SSL *s) return -1; } + if (s->early_data_state == SSL_EARLY_DATA_WRITE_RETRY + || s->early_data_state == SSL_EARLY_DATA_CONNECT_RETRY) + return -1; + s->method->ssl_renegotiate_check(s, 0); if (SSL_in_init(s) || SSL_in_before(s)) { diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index a7fa0b52c2..86603a07c5 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -349,6 +349,9 @@ && (s)->method->version >= TLS1_3_VERSION \ && (s)->method->version != TLS_ANY_VERSION) +# define SSL_TREAT_AS_TLS13(s) \ + (SSL_IS_TLS13(s) || (s)->early_data_state == SSL_EARLY_DATA_WRITING) + # define SSL_IS_FIRST_HANDSHAKE(S) ((s)->s3->tmp.finish_md_len == 0) /* See if we need explicit IV */ @@ -609,6 +612,15 @@ typedef struct srp_ctx_st { # endif +typedef enum { + SSL_EARLY_DATA_NONE = 0, + SSL_EARLY_DATA_CONNECT_RETRY, + SSL_EARLY_DATA_CONNECTING, + SSL_EARLY_DATA_WRITE_RETRY, + SSL_EARLY_DATA_WRITING, + SSL_EARLY_DATA_FINISHED_WRITING +} SSL_EARLY_DATA_STATE; + #define MAX_COMPRESSIONS_SIZE 255 struct ssl_comp_st { @@ -976,6 +988,7 @@ struct ssl_st { int shutdown; /* where we are */ OSSL_STATEM statem; + SSL_EARLY_DATA_STATE early_data_state; BUF_MEM *init_buf; /* buffer used during init */ void *init_msg; /* pointer to handshake message body, set by * ssl3_get_message() */ diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c index a1c5a21522..10d794ede7 100644 --- a/ssl/statem/statem.c +++ b/ssl/statem/statem.c @@ -313,7 +313,9 @@ static int state_machine(SSL *s, int server) goto end; } - if (SSL_IS_FIRST_HANDSHAKE(s) || s->renegotiate) { + if ((SSL_IS_FIRST_HANDSHAKE(s) + && s->early_data_state != SSL_EARLY_DATA_FINISHED_WRITING) + || s->renegotiate) { if (!tls_setup_handshake(s)) { ossl_statem_set_error(s); goto end; diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index abddc0ace3..6507fc7d59 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -196,6 +196,11 @@ static int ossl_statem_client13_read_transition(SSL *s, int mt) break; case TLS_ST_OK: + if (s->early_data_state == SSL_EARLY_DATA_FINISHED_WRITING + && mt == SSL3_MT_SERVER_HELLO) { + st->hand_state = TLS_ST_CR_SRVR_HELLO; + return 1; + } if (mt == SSL3_MT_NEWSESSION_TICKET) { st->hand_state = TLS_ST_CR_SESSION_TICKET; return 1; @@ -382,7 +387,21 @@ int ossl_statem_client_read_transition(SSL *s, int mt) break; case TLS_ST_OK: - if (mt == SSL3_MT_HELLO_REQUEST) { + if (s->early_data_state == SSL_EARLY_DATA_FINISHED_WRITING) { + /* + * We've not actually selected TLSv1.3 yet, but we have sent early + * data. The only thing allowed now is a ServerHello or a + * HelloRetryRequest. + */ + if (mt == SSL3_MT_SERVER_HELLO) { + st->hand_state = TLS_ST_CR_SRVR_HELLO; + return 1; + } + if (mt == SSL3_MT_HELLO_RETRY_REQUEST) { + st->hand_state = TLS_ST_CR_HELLO_RETRY_REQUEST; + return 1; + } + } else if (mt == SSL3_MT_HELLO_REQUEST) { st->hand_state = TLS_ST_CR_HELLO_REQ; return 1; } @@ -485,6 +504,13 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s) return WRITE_TRAN_ERROR; case TLS_ST_OK: + if (s->early_data_state == SSL_EARLY_DATA_FINISHED_WRITING) { + /* + * We are assuming this is a TLSv1.3 connection, although we haven't + * actually selected a version yet. + */ + return WRITE_TRAN_FINISHED; + } if (!s->renegotiate) { /* * We haven't requested a renegotiation ourselves so we must have @@ -498,6 +524,15 @@ WRITE_TRAN ossl_statem_client_write_transition(SSL *s) return WRITE_TRAN_CONTINUE; case TLS_ST_CW_CLNT_HELLO: + if (s->early_data_state == SSL_EARLY_DATA_CONNECTING) { + /* + * We are assuming this is a TLSv1.3 connection, although we haven't + * actually selected a version yet. + */ + st->hand_state = TLS_ST_OK; + ossl_statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + } /* * No transition at the end of writing because we don't know what * we will be sent @@ -999,9 +1034,6 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt) } /* else use the pre-loaded session */ - /* This is a real handshake so make sure we clean it up at the end */ - s->statem.cleanuphand = 1; - p = s->s3->client_random; /* @@ -1185,6 +1217,12 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) SSL_COMP *comp; #endif + /* + * This is a real handshake so make sure we clean it up at the end. We set + * this here so that we are after any early_data + */ + s->statem.cleanuphand = 1; + if (!PACKET_get_net_2(pkt, &sversion)) { al = SSL_AD_DECODE_ERROR; SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH); diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c index 6faa4e0838..7f786e150e 100644 --- a/ssl/tls13_enc.c +++ b/ssl/tls13_enc.c @@ -500,7 +500,7 @@ int tls13_update_key(SSL *s, int send) int tls13_alert_code(int code) { - if (code == SSL_AD_MISSING_EXTENSION) + if (code == SSL_AD_MISSING_EXTENSION || code == SSL_AD_END_OF_EARLY_DATA) return code; return tls1_alert_code(code); diff --git a/util/libssl.num b/util/libssl.num index d3f7a4f596..52ae727b37 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -428,3 +428,5 @@ SSL_set_max_early_data 428 1_1_1 EXIST::FUNCTION: SSL_CTX_set_max_early_data 429 1_1_1 EXIST::FUNCTION: SSL_get_max_early_data 430 1_1_1 EXIST::FUNCTION: SSL_CTX_get_max_early_data 431 1_1_1 EXIST::FUNCTION: +SSL_write_early 432 1_1_1 EXIST::FUNCTION: +SSL_write_early_finish 433 1_1_1 EXIST::FUNCTION: