Skip to content

Commit

Permalink
Add Data-less Zero-RTT support.
Browse files Browse the repository at this point in the history
This adds support on the server and client to accept data-less early
data. The server will still fail to parse early data with any
contents, so this should remain disabled.

BUG=76

Change-Id: Id85d192d8e0360b8de4b6971511b5e8a0e8012f7
Reviewed-on: https://boringssl-review.googlesource.com/12921
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
  • Loading branch information
dvorak42 authored and CQ bot account: commit-bot@chromium.org committed Mar 25, 2017
1 parent f35e838 commit 2d85062
Show file tree
Hide file tree
Showing 26 changed files with 852 additions and 54 deletions.
2 changes: 2 additions & 0 deletions crypto/err/ssl.errordata
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
SSL,277,ALPN_MISMATCH_ON_EARLY_DATA
SSL,100,APP_DATA_IN_HANDSHAKE
SSL,101,ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT
SSL,102,BAD_ALERT
Expand Down Expand Up @@ -204,5 +205,6 @@ SSL,244,WRONG_MESSAGE_TYPE
SSL,245,WRONG_SIGNATURE_TYPE
SSL,246,WRONG_SSL_VERSION
SSL,247,WRONG_VERSION_NUMBER
SSL,278,WRONG_VERSION_ON_EARLY_DATA
SSL,248,X509_LIB
SSL,249,X509_VERIFICATION_SETUP_PROBLEMS
6 changes: 6 additions & 0 deletions include/openssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3082,6 +3082,10 @@ OPENSSL_EXPORT int SSL_total_renegotiations(const SSL *ssl);
* interoperability failures until fully implemented. */
OPENSSL_EXPORT void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled);

/* SSL_early_data_accepted returns whether early data was accepted on the
* handshake performed by |ssl|. */
OPENSSL_EXPORT int SSL_early_data_accepted(const SSL *ssl);

/* SSL_MAX_CERT_LIST_DEFAULT is the default maximum length, in bytes, of a peer
* certificate chain. */
#define SSL_MAX_CERT_LIST_DEFAULT (1024 * 100)
Expand Down Expand Up @@ -4600,6 +4604,8 @@ BORINGSSL_MAKE_DELETER(SSL_SESSION, SSL_SESSION_free)
#define SSL_R_CERTIFICATE_AND_PRIVATE_KEY_MISMATCH 274
#define SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD 275
#define SSL_R_TICKET_ENCRYPTION_FAILED 276
#define SSL_R_ALPN_MISMATCH_ON_EARLY_DATA 277
#define SSL_R_WRONG_VERSION_ON_EARLY_DATA 278
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
Expand Down
1 change: 1 addition & 0 deletions ssl/dtls_method.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = {
dtls1_release_current_message,
dtls1_read_app_data,
dtls1_read_change_cipher_spec,
NULL,
dtls1_read_close_notify,
dtls1_write_app_data,
dtls1_dispatch_alert,
Expand Down
18 changes: 18 additions & 0 deletions ssl/handshake_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,18 @@ int ssl3_connect(SSL_HANDSHAKE *hs) {
}

if (!SSL_is_dtls(ssl) || ssl->d1->send_cookie) {
if (hs->early_data_offered) {
if (!tls13_init_early_key_schedule(hs) ||
!tls13_advance_key_schedule(hs, ssl->session->master_key,
ssl->session->master_key_length) ||
!tls13_derive_early_secrets(hs) ||
!tls13_set_traffic_key(ssl, evp_aead_seal,
hs->early_traffic_secret,
hs->hash_len)) {
ret = -1;
goto end;
}
}
hs->next_state = SSL3_ST_CR_SRVR_HELLO_A;
} else {
hs->next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
Expand Down Expand Up @@ -875,6 +887,12 @@ static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
return 1;
}

if (hs->early_data_offered) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_ON_EARLY_DATA);
al = SSL_AD_PROTOCOL_VERSION;
goto f_err;
}

ssl_clear_tls13_state(hs);

if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
Expand Down
21 changes: 21 additions & 0 deletions ssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,11 @@ int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
* It returns one on success and zero on error. */
int tls13_init_key_schedule(SSL_HANDSHAKE *hs);

/* tls13_init_early_key_schedule initializes the handshake hash and key
* derivation state from the resumption secret to derive the early secrets. It
* returns one on success and zero on error. */
int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs);

/* tls13_advance_key_schedule incorporates |in| into the key schedule with
* HKDF-Extract. It returns one on success and zero on error. */
int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
Expand All @@ -876,6 +881,10 @@ int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
const uint8_t *traffic_secret,
size_t traffic_secret_len);

/* tls13_derive_early_secrets derives the early traffic secret. It returns one
* on success and zero on error. */
int tls13_derive_early_secrets(SSL_HANDSHAKE *hs);

/* tls13_derive_handshake_secrets derives the handshake traffic secret. It
* returns one on success and zero on error. */
int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs);
Expand Down Expand Up @@ -930,6 +939,7 @@ enum ssl_hs_wait_t {
ssl_hs_channel_id_lookup,
ssl_hs_private_key_operation,
ssl_hs_pending_ticket,
ssl_hs_read_end_of_early_data,
};

struct ssl_handshake_st {
Expand Down Expand Up @@ -957,6 +967,7 @@ struct ssl_handshake_st {

size_t hash_len;
uint8_t secret[EVP_MAX_MD_SIZE];
uint8_t early_traffic_secret[EVP_MAX_MD_SIZE];
uint8_t client_handshake_secret[EVP_MAX_MD_SIZE];
uint8_t server_handshake_secret[EVP_MAX_MD_SIZE];
uint8_t client_traffic_secret_0[EVP_MAX_MD_SIZE];
Expand Down Expand Up @@ -1100,6 +1111,9 @@ struct ssl_handshake_st {
* Start. The client may write data at this point. */
unsigned in_false_start:1;

/* early_data_offered is one if the client sent the early_data extension. */
unsigned early_data_offered:1;

/* next_proto_neg_seen is one of NPN was negotiated. */
unsigned next_proto_neg_seen:1;

Expand Down Expand Up @@ -1398,6 +1412,7 @@ struct ssl_protocol_method_st {
int (*read_app_data)(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
int peek);
int (*read_change_cipher_spec)(SSL *ssl);
int (*read_end_of_early_data)(SSL *ssl);
void (*read_close_notify)(SSL *ssl);
int (*write_app_data)(SSL *ssl, const uint8_t *buf, int len);
int (*dispatch_alert)(SSL *ssl);
Expand Down Expand Up @@ -1629,9 +1644,11 @@ typedef struct ssl3_state_st {
uint8_t write_traffic_secret[EVP_MAX_MD_SIZE];
uint8_t read_traffic_secret[EVP_MAX_MD_SIZE];
uint8_t exporter_secret[EVP_MAX_MD_SIZE];
uint8_t early_exporter_secret[EVP_MAX_MD_SIZE];
uint8_t write_traffic_secret_len;
uint8_t read_traffic_secret_len;
uint8_t exporter_secret_len;
uint8_t early_exporter_secret_len;

/* Connection binding to prevent renegotiation attacks */
uint8_t previous_client_finished[12];
Expand Down Expand Up @@ -1933,6 +1950,9 @@ struct ssl_st {
* hash of the peer's certificate and then discard it to save memory and
* session space. Only effective on the server side. */
unsigned retain_only_sha256_of_client_certs:1;

/* early_data_accepted is true if early data was accepted by the server. */
unsigned early_data_accepted:1;
};

/* From draft-ietf-tls-tls13-18, used in determining PSK modes. */
Expand Down Expand Up @@ -2049,6 +2069,7 @@ int ssl3_dispatch_alert(SSL *ssl);
int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
int peek);
int ssl3_read_change_cipher_spec(SSL *ssl);
int ssl3_read_end_of_early_data(SSL *ssl);
void ssl3_read_close_notify(SSL *ssl);
int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len);
int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len);
Expand Down
1 change: 1 addition & 0 deletions ssl/s3_both.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ void ssl_handshake_free(SSL_HANDSHAKE *hs) {
}

OPENSSL_cleanse(hs->secret, sizeof(hs->secret));
OPENSSL_cleanse(hs->early_traffic_secret, sizeof(hs->early_traffic_secret));
OPENSSL_cleanse(hs->client_handshake_secret,
sizeof(hs->client_handshake_secret));
OPENSSL_cleanse(hs->server_handshake_secret,
Expand Down
24 changes: 24 additions & 0 deletions ssl/s3_pkt.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,30 @@ int ssl3_read_change_cipher_spec(SSL *ssl) {
return 1;
}

int ssl3_read_end_of_early_data(SSL *ssl) {
SSL3_RECORD *rr = &ssl->s3->rrec;

if (rr->length == 0) {
int ret = ssl3_get_record(ssl);
if (ret <= 0) {
return ret;
}
}

if (rr->type != SSL3_RT_ALERT ||
rr->length != 2 ||
rr->data[0] != SSL3_AL_WARNING ||
rr->data[1] != TLS1_AD_END_OF_EARLY_DATA) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
return -1;
}

rr->length = 0;
ssl_read_buffer_discard(ssl);
return 1;
}

void ssl3_read_close_notify(SSL *ssl) {
/* Read records until an error or close_notify. */
while (ssl3_get_record(ssl) > 0) {
Expand Down
4 changes: 4 additions & 0 deletions ssl/ssl_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,10 @@ void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled) {
ctx->enable_early_data = !!enabled;
}

int SSL_early_data_accepted(const SSL *ssl) {
return ssl->early_data_accepted;
}

static int bio_retry_reason_to_error(int reason) {
switch (reason) {
case BIO_RR_CONNECT:
Expand Down
68 changes: 60 additions & 8 deletions ssl/t1_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -2086,11 +2086,30 @@ static int ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
* https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8 */

static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
/* TODO(svaldez): Support 0RTT. */
SSL *const ssl = hs->ssl;
uint16_t session_version;
if (ssl->session == NULL ||
!ssl->method->version_from_wire(&session_version,
ssl->session->ssl_version) ||
session_version < TLS1_3_VERSION ||
ssl->session->ticket_max_early_data == 0 ||
hs->received_hello_retry_request ||
!ssl->ctx->enable_early_data) {
return 1;
}

hs->early_data_offered = 1;

if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
!CBB_add_u16(out, 0) ||
!CBB_flush(out)) {
return 0;
}

return 1;
}

static int ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs,
static int ext_early_data_parse_serverhello(SSL_HANDSHAKE *hs,
uint8_t *out_alert, CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == NULL) {
Expand All @@ -2102,11 +2121,44 @@ static int ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs,
return 0;
}

/* Since we don't currently accept 0-RTT, we have to skip past any early data
* the client might have sent. */
if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
ssl->s3->skip_early_data = 1;
if (!ssl->s3->session_reused) {
*out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
return 0;
}

ssl->early_data_accepted = 1;
return 1;
}

static int ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs,
uint8_t *out_alert, CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == NULL ||
ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
return 1;
}

if (CBS_len(contents) != 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return 0;
}

hs->early_data_offered = 1;
return 1;
}

static int ext_early_data_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
if (!hs->ssl->early_data_accepted) {
return 1;
}

if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
!CBB_add_u16(out, 0) ||
!CBB_flush(out)) {
return 0;
}

return 1;
}

Expand Down Expand Up @@ -2597,9 +2649,9 @@ static const struct tls_extension kExtensions[] = {
TLSEXT_TYPE_early_data,
NULL,
ext_early_data_add_clienthello,
forbid_parse_serverhello,
ext_early_data_parse_serverhello,
ext_early_data_parse_clienthello,
dont_add_serverhello,
ext_early_data_add_serverhello,
},
{
TLSEXT_TYPE_supported_versions,
Expand Down
40 changes: 35 additions & 5 deletions ssl/test/bssl_shim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ struct TestState {
bssl::UniquePtr<SSL_SESSION> new_session;
bool ticket_decrypt_done = false;
bool alpn_select_done = false;
bool is_resume = false;
bool early_callback_ready = false;
};

Expand Down Expand Up @@ -764,6 +765,10 @@ static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen,

*out = (const uint8_t*)config->select_alpn.data();
*outlen = config->select_alpn.size();
if (GetTestState(ssl)->is_resume && config->select_resume_alpn.size() > 0) {
*out = (const uint8_t*)config->select_resume_alpn.data();
*outlen = config->select_resume_alpn.size();
}
return SSL_TLSEXT_ERR_OK;
}

Expand Down Expand Up @@ -1109,7 +1114,8 @@ static bssl::UniquePtr<SSL_CTX> SetupCtx(const TestConfig *config) {
NULL);
}

if (!config->select_alpn.empty() || config->decline_alpn) {
if (!config->select_alpn.empty() || !config->select_resume_alpn.empty() ||
config->decline_alpn) {
SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
}

Expand Down Expand Up @@ -1422,13 +1428,22 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
}
}

if (!config->expected_alpn.empty()) {
std::string expected_alpn = config->expected_alpn;
if (is_resume && !config->expected_resume_alpn.empty()) {
expected_alpn = config->expected_resume_alpn;
}
bool expect_no_alpn = (!is_resume && config->expect_no_alpn) ||
(is_resume && config->expect_no_resume_alpn);
if (expect_no_alpn) {
expected_alpn.clear();
}

if (!expected_alpn.empty() || expect_no_alpn) {
const uint8_t *alpn_proto;
unsigned alpn_proto_len;
SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len);
if (alpn_proto_len != config->expected_alpn.size() ||
OPENSSL_memcmp(alpn_proto, config->expected_alpn.data(),
alpn_proto_len) != 0) {
if (alpn_proto_len != expected_alpn.size() ||
OPENSSL_memcmp(alpn_proto, expected_alpn.data(), alpn_proto_len) != 0) {
fprintf(stderr, "negotiated alpn proto mismatch\n");
return false;
}
Expand Down Expand Up @@ -1540,6 +1555,15 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
return false;
}

if (is_resume) {
if ((config->expect_accept_early_data && !SSL_early_data_accepted(ssl)) ||
(config->expect_reject_early_data && SSL_early_data_accepted(ssl))) {
fprintf(stderr,
"Early data was%s accepted, but we expected the opposite\n",
SSL_early_data_accepted(ssl) ? "" : " not");
return false;
}
}

if (!config->psk.empty()) {
if (SSL_get_peer_cert_chain(ssl) != nullptr) {
Expand Down Expand Up @@ -1629,6 +1653,10 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
SSL_CTX *ssl_ctx, const TestConfig *config,
bool is_resume, SSL_SESSION *session) {
if (is_resume && config->enable_resume_early_data) {
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
}

bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
if (!ssl) {
return false;
Expand All @@ -1639,6 +1667,8 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
return false;
}

GetTestState(ssl.get())->is_resume = is_resume;

if (config->fallback_scsv &&
!SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
return false;
Expand Down
Loading

0 comments on commit 2d85062

Please sign in to comment.