commit ca77729f0395a16f08ff5d54968e05dbd84b331f Author: Adam Langley Date: Thu Nov 4 16:09:48 2010 -0400 snap_start.patch diff --git a/apps/s_server.c b/apps/s_server.c index c4e19c9..37db8f9 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -802,6 +802,7 @@ int MAIN(int argc, char *argv[]) tlsextctx tlsextcbp = {NULL, NULL, SSL_TLSEXT_ERR_ALERT_WARNING}; const char *next_proto_neg_in = NULL; tlsextnextprotoctx next_proto; + char snapstart = 0; #endif #if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) @@ -1105,6 +1106,10 @@ int MAIN(int argc, char *argv[]) if (--argc < 1) goto bad; next_proto_neg_in = *(++argv); } + else if (strcmp(*argv,"-snapstart") == 0) + { + snapstart = 1; + } #endif #ifndef OPENSSL_NO_JPAKE else if (strcmp(*argv,"-jpake") == 0) @@ -1389,6 +1394,11 @@ bad: } #endif + if (snapstart) + { + static const unsigned char orbit[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + SSL_CTX_set_snap_start_orbit(ctx, orbit); + } #ifndef OPENSSL_NO_DH if (!no_dhe) @@ -2031,6 +2041,7 @@ static int init_ssl_connection(SSL *con) unsigned next_proto_neg_len; #endif +again: if ((i=SSL_accept(con)) <= 0) { if (BIO_sock_should_retry(i)) @@ -2039,6 +2050,12 @@ static int init_ssl_connection(SSL *con) return(1); } + if (SSL_get_error(con, i) == SSL_ERROR_SERVER_RANDOM_VALIDATION_PENDING) + { + SSL_set_suggested_server_random_validity(con, 1); + goto again; + } + BIO_printf(bio_err,"ERROR\n"); verify_error=SSL_get_verify_result(con); if (verify_error != X509_V_OK) @@ -2224,6 +2241,9 @@ static int www_body(char *hostname, int s, unsigned char *context) case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_X509_LOOKUP: continue; + case SSL_ERROR_SERVER_RANDOM_VALIDATION_PENDING: + SSL_set_suggested_server_random_validity(con, 1); + continue; case SSL_ERROR_SYSCALL: case SSL_ERROR_SSL: case SSL_ERROR_ZERO_RETURN: diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c index 06e5466..e32f97d 100644 --- a/ssl/s3_enc.c +++ b/ssl/s3_enc.c @@ -111,6 +111,7 @@ #include #include "ssl_locl.h" +#include "fnv1a64.h" #include #include @@ -529,6 +530,11 @@ void ssl3_finish_mac(SSL *s, const unsigned char *buf, int len) { EVP_DigestUpdate(&(s->s3->finish_dgst1),buf,len); EVP_DigestUpdate(&(s->s3->finish_dgst2),buf,len); + if (s->s3->snap_start_requested) + { + /* Compute Fowler-Noll-Vo (FNV) hash for Snap Start handshake */ + fnv1a64_update((FNV1A64*) s->s3->response_hash, buf, len); + } } int ssl3_cert_verify_mac(SSL *s, EVP_MD_CTX *ctx, unsigned char *p) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 84bff8d..1058b4e 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -1701,6 +1701,12 @@ void ssl3_free(SSL *s) pq_64bit_free(&(s->s3->rrec.seq_num)); pq_64bit_free(&(s->s3->wrec.seq_num)); + if (s->s3->snap_start_client_hello.buf) + { + /* s->s3->snap_start_records, if set, uses the same buffer */ + OPENSSL_free(s->s3->snap_start_client_hello.buf); + } + OPENSSL_cleanse(s->s3,sizeof *s->s3); OPENSSL_free(s->s3); s->s3=NULL; diff --git a/ssl/s3_pkt.c b/ssl/s3_pkt.c index 6853058..61774b2 100644 --- a/ssl/s3_pkt.c +++ b/ssl/s3_pkt.c @@ -120,8 +120,51 @@ static int do_ssl3_write(SSL *s, int type, const unsigned char *buf, unsigned int len, int create_empty_fragment); static int ssl3_get_record(SSL *s); +/* ssl3_read_snap_start_n reads from the opportunistic records contained within + * a Snap Start extension. |s->packet| and |s->packet_length| are set to frame + * a record within this area. Partial records are not allowed. The Snap Start + * records are held in |s->s3->snap_start_records| and the |left| member must + * be non-zero on entry. + * + * If |extend| is true then we'll expand the currently framed record by |n| + * bytes, otherwise we frame a new record. */ +static int ssl3_read_snap_start_n(SSL *s, int n, int extend) + { + if (!extend) + { + s->packet = s->s3->snap_start_records.buf + s->s3->snap_start_records.offset; + s->packet_length = 0; + } + + if (s->s3->snap_start_records.left < n) + { + /* We aren't called unless .left is non-zero, therefore this + * means that we wanted to read more than we have. Since + * partial records aren't allowed, this is fatal. */ + SSLerr(SSL_F_SSL3_READ_SNAP_START_N,SSL_R_BAD_PACKET_LENGTH); + return -1; + } + + s->packet_length += n; + s->s3->snap_start_records.left -= n; + s->s3->snap_start_records.offset += n; + + return n; + } + int ssl3_read_n(SSL *s, int n, int max, int extend) { + if (s->s3->snap_start_records.left) + return ssl3_read_snap_start_n(s, n, extend); + else if (s->s3->snap_start_client_hello.buf && !extend) + { + /* If we started reading the opportunistic records then we know + * that we didn't enter recovery. Thus it's safe to free the + * copy of the ClientHello now because we'll not need it again. */ + OPENSSL_free(s->s3->snap_start_client_hello.buf); + s->s3->snap_start_client_hello.buf = NULL; + } + /* If extend == 0, obtain new n-byte packet; if extend == 1, increase * packet by another n bytes. * The packet will be in the sub-array of s->s3->rbuf.buf specified diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 8e0a504..315b8f3 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -144,6 +144,7 @@ #include static SSL_METHOD *ssl3_get_server_method(int ver); +static int ssl3_snap_start_evaluate_handshake(SSL* s); #ifndef OPENSSL_NO_ECDH static int nid2curve_id(int nid); #endif @@ -300,10 +301,36 @@ int ssl3_accept(SSL *s) case SSL3_ST_SW_SRVR_HELLO_A: case SSL3_ST_SW_SRVR_HELLO_B: ret=ssl3_send_server_hello(s); + if (ret == SERVER_RANDOM_VALIDATION_PENDING) + { + s->rwstate = SSL_SERVER_RANDOM_VALIDATE; + s->state = SSL3_ST_SW_SRVR_HELLO_A; + s->init_num = 0; + goto end; + } if (ret <= 0) goto end; #ifndef OPENSSL_NO_TLSEXT + if ((s->s3->tmp.new_cipher->algorithms & SSL_MKEY_MASK) != SSL_kRSA && + (s->s3->tmp.new_cipher->algorithms & SSL_MKEY_MASK) != SSL_kKRB5 && + (s->s3->tmp.new_cipher->algorithms & SSL_MKEY_MASK) != SSL_kDHr && + (s->s3->tmp.new_cipher->algorithms & SSL_MKEY_MASK) != SSL_kDHd && + (s->s3->tmp.new_cipher->algorithms & SSL_MKEY_MASK) != SSL_kECDH && + s->s3->snap_start_requested) + { + /* There's no point in carrying on with a Snap + * Start handshake if we're using a cipher + * suite which is going to send a + * ServerKeyExchange message. */ + ssl3_snap_start_reset_for_recovery(s); + s->state = SSL3_ST_SW_SRVR_HELLO_A; + break; + } + if (s->hit) { + if (ssl3_snap_start_evaluate_handshake(s)) + break; + if (s->tlsext_ticket_expected) s->state=SSL3_ST_SW_SESSION_TICKET_A; else @@ -440,8 +467,19 @@ int ssl3_accept(SSL *s) case SSL3_ST_SW_SRVR_DONE_B: ret=ssl3_send_server_done(s); if (ret <= 0) goto end; - s->s3->tmp.next_state=SSL3_ST_SR_CERT_A; - s->state=SSL3_ST_SW_FLUSH; + + if (s->s3->snap_start_requested) + { + if (ssl3_snap_start_evaluate_handshake(s)) + break; + s->state = SSL3_ST_SR_CERT_A; + } + else + { + s->s3->tmp.next_state=SSL3_ST_SR_CERT_A; + s->state=SSL3_ST_SW_FLUSH; + } + s->init_num=0; break; @@ -1152,11 +1190,19 @@ int ssl3_send_server_hello(SSL *s) if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { buf=(unsigned char *)s->init_buf->data; - p=s->s3->server_random; - Time=(unsigned long)time(NULL); /* Time */ - l2n(Time,p); - if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) - return -1; + if (!s->s3->snap_start_requested) + { + p=s->s3->server_random; + Time=(unsigned long)time(NULL); /* Time */ + l2n(Time,p); + if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0) + return -1; + } + else if (s->s3->server_random_suggestion_valid == 0) + { + return SERVER_RANDOM_VALIDATION_PENDING; + } + /* Do the message type and length last */ d=p= &(buf[4]); @@ -2952,3 +2998,55 @@ int ssl3_send_cert_status(SSL *s) return(ssl3_do_write(s,SSL3_RT_HANDSHAKE)); } #endif + +/* ssl3_snap_start_evaluate_handshake verifies the Snap Start prediction (if + * this is a Snap Start handshake). If it returns non-zero, then we are + * entering recovery and |s->state| has been set accordingly. */ +static int ssl3_snap_start_evaluate_handshake(SSL* s) + { + unsigned char digest[8]; + + if (!s->s3->snap_start_requested) + return 0; + + /* Drop the currently queued messages. Either we're entering recovery, + * in which case they're wrong, or we're doing snap start, in which + * case we don't want to send them. */ + if (!ssl_init_wbio_buffer(s, 1 /* push new BIO */)) + return -1; + + fnv1a64_final(digest, (FNV1A64*) s->s3->response_hash); + + /* Turn off FNV hashing of handshake messages. */ + s->s3->snap_start_requested = 0; + + if (memcmp(digest, s->s3->predicted_response_hash, sizeof(digest)) != 0) + { + /* The predicted handshake didn't match. */ + ssl3_snap_start_reset_for_recovery(s); + s->state = SSL3_ST_SW_SRVR_HELLO_A; + return 1; + } + + return 0; + } + +/* ssl3_snap_start_reset_for_recovery is called is called when a Snap Start + * handshake is impossible because either the application layer has rejected + * the client's suggested server random, or predicated_response_hash failed to + * match response_hash */ +int ssl3_snap_start_reset_for_recovery(SSL* s) + { + s->s3->snap_start_requested = 0; + s->s3->snap_start_records.left = 0; + s->init_num = 0; + + /* Reset the handshake hash and hash in the original ClientHello. */ + ssl3_init_finished_mac(s); + ssl3_finish_mac(s, s->s3->snap_start_client_hello.buf, s->s3->snap_start_client_hello.left); + + OPENSSL_free(s->s3->snap_start_client_hello.buf); + s->s3->snap_start_client_hello.buf = NULL; + + return 0; + } diff --git a/ssl/ssl.h b/ssl/ssl.h index dc8dff8..bbe2543 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -770,6 +770,11 @@ struct ssl_ctx_st X509_VERIFY_PARAM *param; + /* The configured Snap Start orbit value, if set. */ + char snap_start_orbit_valid; + unsigned char snap_start_orbit[8]; + + #if 0 int purpose; /* Purpose setting */ int trust; /* Trust setting */ @@ -876,10 +881,14 @@ void SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, int (*app_gen_cookie_cb)(SSL * void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, int (*app_verify_cookie_cb)(SSL *ssl, unsigned char *cookie, unsigned int cookie_len)); void SSL_CTX_set_next_protos_advertised_cb(SSL_CTX *s, int (*cb) (SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg), void *arg); void SSL_CTX_set_next_proto_select_cb(SSL_CTX *s, int (*cb) (SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg), void *arg); +void SSL_CTX_set_snap_start_orbit(SSL_CTX *s, const unsigned char orbit[8]); int SSL_select_next_proto(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, const unsigned char *client, unsigned int client_len); void SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, unsigned *len); +void SSL_get0_suggested_server_random(const SSL *s, const unsigned char **data, unsigned *len); +void SSL_set_suggested_server_random_validity(SSL *s, char is_valid); + #define OPENSSL_NPN_UNSUPPORTED 0 #define OPENSSL_NPN_NEGOTIATED 1 #define OPENSSL_NPN_NO_OVERLAP 2 @@ -888,12 +897,14 @@ void SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data, un #define SSL_WRITING 2 #define SSL_READING 3 #define SSL_X509_LOOKUP 4 +#define SSL_SERVER_RANDOM_VALIDATE 6 /* These will only be used when doing non-blocking IO */ #define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) #define SSL_want_read(s) (SSL_want(s) == SSL_READING) #define SSL_want_write(s) (SSL_want(s) == SSL_WRITING) #define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP) +#define SSL_want_server_random_validation(s) (SSL_want(s) == SSL_SERVER_RANDOM_VALIDATE) struct ssl_st { @@ -1255,6 +1266,7 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); #define SSL_ERROR_ZERO_RETURN 6 #define SSL_ERROR_WANT_CONNECT 7 #define SSL_ERROR_WANT_ACCEPT 8 +#define SSL_ERROR_SERVER_RANDOM_VALIDATION_PENDING 10 #define SSL_CTRL_NEED_TMP_RSA 1 #define SSL_CTRL_SET_TMP_RSA 2 @@ -1754,6 +1766,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_GET_SERVER_VERIFY 110 #define SSL_F_I2D_SSL_SESSION 111 #define SSL_F_READ_N 112 +#define SSL_F_SSL3_READ_SNAP_START_N 300 #define SSL_F_REQUEST_CERTIFICATE 113 #define SSL_F_SERVER_FINISH 239 #define SSL_F_SERVER_HELLO 114 @@ -1907,7 +1920,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_TLS1_ENC 210 #define SSL_F_TLS1_SETUP_KEY_BLOCK 211 #define SSL_F_WRITE_PENDING 212 -/* Next entry: 299 */ +/* Next entry: 300 */ /* Reason codes. */ #define SSL_R_APP_DATA_IN_HANDSHAKE 100 diff --git a/ssl/ssl3.h b/ssl/ssl3.h index 54b73b7..4a6e8cf 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -452,6 +452,48 @@ typedef struct ssl3_state_st unsigned char previous_server_finished[EVP_MAX_MD_SIZE]; unsigned char previous_server_finished_len; int send_connection_binding; /* TODOEKR */ + + /* Snap Start support (server-side only): + * + * Snap Start allows the client to 'suggest' the value of our random + * nonce. Assuming that we accept this suggestion, then the client can + * predict our exact reply and calculate a complete handshake based on + * that. These opportunistic handshake messages are embedded in the + * Snap Start extension, possibly including application data. + * + * (Note that if the handshake doesn't resume a session, the client + * couldn't hope to predict the exact server reply unless it uses the + * session ticket extension to suppress session ID generation.) + * + * All this allows for a TLS handshake that doesn't incur additional + * latency if the client side sends application data first. */ + + /* Set if the client presented a Snap Start extension (empty or + * otherwise and the SSL_CTX has a cell configured. Server side only. */ + int snap_start_ext_seen; + /* Set if the client-suggested a server random value (which is stored + * in |server_random|) */ + char snap_start_requested; + /* Set if the appplication has indicated that the client's + * server_random suggestion is acceptable (see + * SSL_set_suggested_server_random_validity). If so, a Snap Start + * handshake will be attempted. */ + char server_random_suggestion_valid; + /* Client's predicted response_hash from client snap start extension. + * Valid if |snap_start_requested| is set. */ + unsigned char predicted_response_hash[8]; + /* Actual server handshake message hash. A Snap Start handshake is + * possible only if predicated_response_hash matches this. */ + unsigned char response_hash[8]; + /* If we need to enter snap start recovery then we need to reset the + * Finished hash with a different value for the ClientHello. Thus, we + * need a copy of the whole ClientHello: */ + SSL3_BUFFER snap_start_client_hello; + /* A snap start ClientHello can contain records embedded in an + * extension. If we wish to read them then this points to the records + * within |snap_start_client_hello|. */ + SSL3_BUFFER snap_start_records; + } SSL3_STATE; diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index cfa70ec..88358fb 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2119,6 +2119,9 @@ int SSL_get_error(const SSL *s,int i) return(SSL_ERROR_SSL); } + if ((i < 0) && SSL_want_server_random_validation(s)) + return(SSL_ERROR_SERVER_RANDOM_VALIDATION_PENDING); + if ((i < 0) && SSL_want_read(s)) { bio=SSL_get_rbio(s); @@ -2876,6 +2879,61 @@ void SSL_CTX_set_next_proto_select_cb(SSL_CTX *ctx, int (*cb) (SSL *s, unsigned ctx->next_proto_select_cb = cb; ctx->next_proto_select_cb_arg = arg; } + +/* SSL_CTX_set_snap_start_orbit sets the orbit value which will be echoed back + * to the client and enables Snap Start for this context. + * + * An orbit value can be used to spatially partition the state needed to support + * Snap Start. See the comments above SSL_set_suggested_server_random_validity + * (below). */ +void SSL_CTX_set_snap_start_orbit(SSL_CTX *ctx, const unsigned char orbit[8]) + { + memcpy(ctx->snap_start_orbit, orbit, sizeof(ctx->snap_start_orbit)); + ctx->snap_start_orbit_valid = 1; + } + +/* Once SSL_accept has returned with SSL_SERVER_RANDOM_VALIDATE, then one can + * call this function in order to get the client's suggested server random + * value. */ +void SSL_get0_suggested_server_random(const SSL* s, const unsigned char **data, unsigned *length) + { + if (!s->s3->snap_start_requested) + { + *data = NULL; + *length = 0; + return; + } + *length = 32; + *data = s->s3->server_random; + } + +/* SSL_set_suggested_server_random_validity passes judgement on a + * client-suggested random value (obtained from + * SSL_get0_suggested_server_random). Rejecting the value triggers a recovery, + * while accepting the value /may/ result in a successful Snap Start, as long + * as the client predicted the handshake correctly. + * + * In order to accept a random value the user must ensure that it has NEVER + * been used before by this server, or any server configured with any of the + * same certificates. It may reject more if necessary. + * + * The first four bytes of the random value contain a timestamp (UNIX seconds + * since the epoch) which can be used to manage a time window. Additionally, + * the following eight bytes contain the orbit which which can also bound the + * state required if geographically separate servers share certificates. + * + * It's recommended that the time window have a maximum size, independent of + * the resources available, in order to prevent an attacker from arbitrarily + * delaying a Snap Start handshake. + */ +void SSL_set_suggested_server_random_validity(SSL *s, char is_valid) + { + if (is_valid) + s->s3->server_random_suggestion_valid = 1; + else + ssl3_snap_start_reset_for_recovery(s); + } + #endif int SSL_cutthrough_complete(const SSL *s) diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index a9183ff..639a185 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -392,6 +392,11 @@ #define CERT_PRIVATE_KEY 2 */ +/* This can be returned from ssl3_send_server_hello to indicate that an + * offline validation of a client-suggested server_random needs to be + * performed. */ +#define SERVER_RANDOM_VALIDATION_PENDING -(TLSEXT_TYPE_snap_start) + #ifndef OPENSSL_NO_EC /* From ECC-TLS draft, used in encoding the curve type in * ECParameters @@ -915,6 +920,7 @@ int ssl3_get_client_certificate(SSL *s); int ssl3_get_client_key_exchange(SSL *s); int ssl3_get_cert_verify(SSL *s); int ssl3_get_next_proto(SSL *s); +int ssl3_snap_start_reset_for_recovery(SSL* s); int dtls1_send_hello_request(SSL *s); int dtls1_send_server_hello(SSL *s); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index fd35b18..ce33f16 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -62,6 +62,7 @@ #include #include #include "ssl_locl.h" +#include "fnv1a64.h" const char tls1_version_str[]="TLSv1" OPENSSL_VERSION_PTEXT; @@ -368,6 +369,21 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha } } + if (s->s3->snap_start_ext_seen) + { + if ((long)(limit - ret - 14) < 0) return NULL; + s2n(TLSEXT_TYPE_snap_start,ret); + s2n(10,ret); /* extension length */ + memcpy(ret, s->ctx->snap_start_orbit, 8); + ret += 8; + /* This is the ciphersuite that we would pick in the event of a + * Snap Start handshake. (Maybe the server wants to do EDH + * unless the client is Snap Start capable). At the moment we + * don't have any logic to pick a different cipher suite so we + * repeat the choice from the ServerHello. */ + s2n(s->s3->tmp.new_cipher->id & 0xffff,ret); + } + if ((extdatalen = ret-p-2)== 0) return p; @@ -375,6 +391,174 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha return ret; } + +static int ssl_hash_snap_start_client_hello(SSL* s, + const char* data, + unsigned len, + unsigned ext_len) + { + /* We walk the ClientHello from the beginning, writing + * adjusted lengths into |b| and hashing as we go. + * + * The resulting ClientHello is going to be shorter by the length of + * this extension, which is |ext_len + 4| (two bytes for the type and two for + * the length). */ + + const unsigned char *p; + unsigned remaining; + unsigned char b[3], *c; + unsigned long l; + + p = (unsigned char*) data; + remaining = len; + /* Handshake header: type */ + if (!remaining) + return 0; + ssl3_finish_mac(s, p, 1); + p++; + remaining--; + /* Handshake header: length */ + if (remaining < 3) + return 0; + n2l3(p, l); + l -= ext_len + 4; + c = b; + l2n3(l, c); + ssl3_finish_mac(s, b, 3); + remaining -= 3; + /* ClientHello: version and random */ + if (remaining < 34) + return 0; + ssl3_finish_mac(s, p, 34); + p += 34; + remaining -= 34; + /* ClientHello: session id length */ + if (!remaining) + return 0; + l = *p; + ssl3_finish_mac(s, p, 1); + p++; + remaining--; + /* ClientHello: session id */ + if (remaining < l) + return 0; + ssl3_finish_mac(s, p, l); + p += l; + remaining -= l; + /* ClientHello: cipher suites length */ + if (remaining < 2) + return 0; + ssl3_finish_mac(s, p, 2); + n2s(p, l); + remaining -= 2; + /* ClientHello: cipher suites */ + if (remaining < l) + return 0; + ssl3_finish_mac(s, p, l); + p += l; + remaining -= l; + /* ClientHello: compression methods length */ + if (!remaining) + return 0; + l = *p; + ssl3_finish_mac(s, p, 1); + p++; + remaining--; + /* ClientHello: compression methods */ + if (remaining < l) + return 0; + ssl3_finish_mac(s, p, l); + p += l; + remaining -= l; + /* ClientHello: extensions length (must exist given that we're already + * parsing the extensions from it */ + if (remaining < 2) + return 0; + n2s(p, l); + remaining -= 2; + if (l != remaining || l < ext_len + 4) + return 0; + l -= ext_len + 4; + c = b; + s2n(l, c); + ssl3_finish_mac(s, b, 2); + + while (remaining) + { + unsigned long extension_type, extension_len; + if (remaining < 4) + return 0; + n2s(p, extension_type); + n2s(p, extension_len); + remaining -= 4; + if (remaining < extension_len) + return 0; + if (extension_type != TLSEXT_TYPE_snap_start) + ssl3_finish_mac(s, p - 4, extension_len + 4); + p += extension_len; + remaining -= extension_len; + } + + return 1; + } + +static char ssl_parse_snap_start_tlsext(SSL *s, const unsigned char *data, unsigned short len) + { + ptrdiff_t extension_offset = data - (unsigned char *) s->init_buf->data; + + if (len > 0 && len < 36) + return 0; + s->s3->snap_start_ext_seen = 1; + if (len == 0) + return 1; + + fnv1a64_init((FNV1A64*) s->s3->response_hash); + + /* We need to make a copy of the ClientHello because we'll be hashing a + * modified version. However, if we enter recovery then we need to hash + * the unchanged message. + * + * We are adding 4 bytes to the length here because we're including the + * handshake header. */ + s->s3->snap_start_client_hello.left = s->init_num + 4; + s->s3->snap_start_client_hello.offset = 0; + s->s3->snap_start_client_hello.buf = OPENSSL_malloc(s->init_num + 4); + if (!s->s3->snap_start_client_hello.buf) + { + /* If we're out of memory then we pretend that we + * didn't see the extension. */ + s->s3->snap_start_ext_seen = 0; + return 1; + } + + memcpy(s->s3->snap_start_client_hello.buf, s->init_buf->data, s->init_num + 4); + memcpy(s->s3->server_random, s->s3->client_random, 4); /* time */ + memcpy(s->s3->server_random + 4, data, 28); /* orbit and random bytes */ + memcpy(s->s3->predicted_response_hash, data + 28, 8); + + /* Point snap_start_records to within the copy of the ClientHello */ + s->s3->snap_start_records.offset = 0; + s->s3->snap_start_records.left = len - 36; + s->s3->snap_start_records.buf = s->s3->snap_start_client_hello.buf + extension_offset + 36; + + /* Reset the handshake hash */ + ssl3_init_finished_mac(s); + + /* Need to hash the ClientHello as if the snap start extension wasn't + * included. */ + if (!ssl_hash_snap_start_client_hello( + s, + s->init_buf->data, + s->init_num + 4 /* four bytes of handshake header */, + len)) + { + return 0; + } + + s->s3->snap_start_requested = 1; + return 1; + } + int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) { unsigned short type; @@ -627,6 +811,12 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in s->s3->next_proto_neg_seen = 1; } + else if (type == TLSEXT_TYPE_snap_start && s->ctx->snap_start_orbit_valid) + { + if (ssl_parse_snap_start_tlsext(s, data, size) == 0) + return 0; + } + /* session ticket processed earlier */ data+=size; diff --git a/ssl/tls1.h b/ssl/tls1.h index 71d76de..52ff325 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -120,6 +120,8 @@ extern "C" { /* This is not an IANA defined extension number */ #define TLSEXT_TYPE_next_proto_neg 13172 + /* http://tools.ietf.org/html/draft-agl-tls-snapstart-00 */ +#define TLSEXT_TYPE_snap_start 13174 /* NameType value from RFC 3546 */ #define TLSEXT_NAMETYPE_host_name 0