summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2017-02-21 09:22:22 +0000
committerMatt Caswell <matt@openssl.org>2017-03-02 17:44:14 +0000
commit49e7fe12eac1e84af726e0110ee29073699ed46b (patch)
treef36e1908e9a4af56dc9fa7339b21d1200f52ed2b
parent5d5b3fba1fc15e3a63876aa9c8deae351369781b (diff)
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 <rsalz@openssl.org> (Merged from https://github.com/openssl/openssl/pull/2737)
-rw-r--r--include/openssl/ssl.h8
-rw-r--r--include/openssl/tls1.h1
-rw-r--r--ssl/record/rec_layer_s3.c21
-rw-r--r--ssl/record/ssl3_record_tls13.c20
-rw-r--r--ssl/s3_msg.c5
-rw-r--r--ssl/ssl_err.c2
-rw-r--r--ssl/ssl_lib.c78
-rw-r--r--ssl/ssl_locl.h13
-rw-r--r--ssl/statem/statem.c4
-rw-r--r--ssl/statem/statem_clnt.c46
-rw-r--r--ssl/tls13_enc.c2
-rw-r--r--util/libssl.num2
12 files changed, 180 insertions, 22 deletions
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: