You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

780 lines
25 KiB

commit ca77729f0395a16f08ff5d54968e05dbd84b331f
Author: Adam Langley <agl@chromium.org>
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 <stdio.h>
#include "ssl_locl.h"
+#include "fnv1a64.h"
#include <openssl/evp.h>
#include <openssl/md5.h>
@@ -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 <openssl/md5.h>
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 <openssl/hmac.h>
#include <openssl/ocsp.h>
#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