aboutsummaryrefslogtreecommitdiff
path: root/patches/0002-handshake_cutthrough.patch
blob: f4d6a8cd909281b72eef2689c7f9b61f7c6ba84e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
From 4c654523c703645f8b517389b6da537c5a9e5168 Mon Sep 17 00:00:00 2001
From: Adam Langley <agl@chromium.org>
Date: Thu, 24 Jan 2013 16:22:07 -0500
Subject: [PATCH] handshake_cutthrough

Enables SSL3+ clients to send application data immediately following the
Finished message even when negotiating full-handshakes.  With this
patch, clients can negotiate SSL connections in 1-RTT even when
performing full-handshakes.
---
 apps/s_client.c | 13 +++++++++++++
 ssl/s3_clnt.c   | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------
 ssl/s3_lib.c    | 15 ++++++++++++++-
 ssl/ssl.h       | 10 +++++++++-
 ssl/ssl3.h      |  1 +
 ssl/ssl_lib.c   | 42 ++++++++++++++++++++++++++++++++++++++++++
 ssl/ssl_locl.h  |  2 ++
 ssl/ssltest.c   | 12 ++++++++++++
 test/testssl    |  3 +++
 9 files changed, 144 insertions(+), 8 deletions(-)

diff --git a/apps/s_client.c b/apps/s_client.c
index 3ba6605..791e277 100644
--- a/apps/s_client.c
+++ b/apps/s_client.c
@@ -361,6 +361,7 @@ static void sc_usage(void)
 	BIO_printf(bio_err," -nextprotoneg arg - enable NPN extension, considering named protocols supported (comma-separated list)\n");
 # endif
 #endif
+	BIO_printf(bio_err," -cutthrough       - enable 1-RTT full-handshake for strong ciphers\n");
 	BIO_printf(bio_err," -legacy_renegotiation - enable use of legacy renegotiation (dangerous)\n");
 #ifndef OPENSSL_NO_SRTP
 	BIO_printf(bio_err," -use_srtp profiles - Offer SRTP key management with a colon-separated profile list\n");
@@ -577,6 +578,7 @@ int MAIN(int argc, char **argv)
 	EVP_PKEY *key = NULL;
 	char *CApath=NULL,*CAfile=NULL,*cipher=NULL;
 	int reconnect=0,badop=0,verify=SSL_VERIFY_NONE,bugs=0;
+	int cutthrough=0;
 	int crlf=0;
 	int write_tty,read_tty,write_ssl,read_ssl,tty_on,ssl_pending;
 	SSL_CTX *ctx=NULL;
@@ -883,6 +885,8 @@ int MAIN(int argc, char **argv)
 			}
 # endif
 #endif
+		else if (strcmp(*argv,"-cutthrough") == 0)
+			cutthrough=1;
 		else if (strcmp(*argv,"-serverpref") == 0)
 			off|=SSL_OP_CIPHER_SERVER_PREFERENCE;
 		else if (strcmp(*argv,"-legacy_renegotiation") == 0)
@@ -1158,6 +1162,15 @@ bad:
 		SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &next_proto);
 #endif
 
+	/* Enable handshake cutthrough for client connections using
+	 * strong ciphers. */
+	if (cutthrough)
+		{
+		int ssl_mode = SSL_CTX_get_mode(ctx);
+		ssl_mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;
+		SSL_CTX_set_mode(ctx, ssl_mode);
+		}
+
 	if (state) SSL_CTX_set_info_callback(ctx,apps_ssl_info_callback);
 	if (cipher != NULL)
 		if(!SSL_CTX_set_cipher_list(ctx,cipher)) {
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index a6b3c01..3d3fd64 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -215,6 +215,12 @@ int ssl3_connect(SSL *s)
 		}
 #endif
 
+	if (SSL_get_mode(s) & SSL_MODE_HANDSHAKE_CUTTHROUGH)
+		{
+		/* Send app data along with CCS/Finished */
+		s->s3->flags |= SSL3_FLAGS_DELAY_CLIENT_FINISHED;
+		}
+
 	for (;;)
 		{
 		state=s->state;
@@ -526,14 +532,32 @@ int ssl3_connect(SSL *s)
 				}
 			else
 				{
-#ifndef OPENSSL_NO_TLSEXT
-				/* Allow NewSessionTicket if ticket expected */
-				if (s->tlsext_ticket_expected)
-					s->s3->tmp.next_state=SSL3_ST_CR_SESSION_TICKET_A;
+				if ((SSL_get_mode(s) & SSL_MODE_HANDSHAKE_CUTTHROUGH)
+				    && ssl3_can_cutthrough(s)
+				    && s->s3->previous_server_finished_len == 0 /* no cutthrough on renegotiation (would complicate the state machine) */
+				   )
+					{
+					if (s->s3->flags & SSL3_FLAGS_DELAY_CLIENT_FINISHED)
+						{
+						s->state=SSL3_ST_CUTTHROUGH_COMPLETE;
+						s->s3->flags|=SSL3_FLAGS_POP_BUFFER;
+						s->s3->delay_buf_pop_ret=0;
+						}
+					else
+						{
+						s->s3->tmp.next_state=SSL3_ST_CUTTHROUGH_COMPLETE;
+						}
+					}
 				else
+					{
+#ifndef OPENSSL_NO_TLSEXT
+					/* Allow NewSessionTicket if ticket expected */
+					if (s->tlsext_ticket_expected)
+						s->s3->tmp.next_state=SSL3_ST_CR_SESSION_TICKET_A;
+					else
 #endif
-				
-				s->s3->tmp.next_state=SSL3_ST_CR_FINISHED_A;
+						s->s3->tmp.next_state=SSL3_ST_CR_FINISHED_A;
+					}
 				}
 			s->init_num=0;
 			break;
@@ -581,6 +605,24 @@ int ssl3_connect(SSL *s)
 			s->state=s->s3->tmp.next_state;
 			break;
 
+		case SSL3_ST_CUTTHROUGH_COMPLETE:
+#ifndef OPENSSL_NO_TLSEXT
+			/* Allow NewSessionTicket if ticket expected */
+			if (s->tlsext_ticket_expected)
+				s->state=SSL3_ST_CR_SESSION_TICKET_A;
+			else
+#endif
+				s->state=SSL3_ST_CR_FINISHED_A;
+
+			/* SSL_write() will take care of flushing buffered data if
+			 * DELAY_CLIENT_FINISHED is set.
+			 */
+			if (!(s->s3->flags & SSL3_FLAGS_DELAY_CLIENT_FINISHED))
+				ssl_free_wbio_buffer(s);
+			ret = 1;
+			goto end;
+			/* break; */
+
 		case SSL_ST_OK:
 			/* clean a few things up */
 			ssl3_cleanup_key_block(s);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index c4ef273..1865c70 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -4211,9 +4211,22 @@ int ssl3_write(SSL *s, const void *buf, int len)
 
 static int ssl3_read_internal(SSL *s, void *buf, int len, int peek)
 	{
-	int ret;
+	int n,ret;
 	
 	clear_sys_error();
+	if ((s->s3->flags & SSL3_FLAGS_POP_BUFFER) && (s->wbio == s->bbio))
+		{
+		/* Deal with an application that calls SSL_read() when handshake data
+		 * is yet to be written.
+		 */
+		if (BIO_wpending(s->wbio) > 0)
+			{
+			s->rwstate=SSL_WRITING;
+			n=BIO_flush(s->wbio);
+			if (n <= 0) return(n);
+			s->rwstate=SSL_NOTHING;
+			}
+		}
 	if (s->s3->renegotiate) ssl3_renegotiate_check(s);
 	s->s3->in_read_app_data=1;
 	ret=s->method->ssl_read_bytes(s,SSL3_RT_APPLICATION_DATA,buf,len,peek);
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 1f255c3..3e31fb5 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -644,6 +644,7 @@ struct ssl_session_st
  * TLS only.)  "Released" buffers are put onto a free-list in the context
  * or just freed (depending on the context's setting for freelist_max_len). */
 #define SSL_MODE_RELEASE_BUFFERS 0x00000010L
+
 /* Send the current time in the Random fields of the ClientHello and
  * ServerHello records for compatibility with hypothetical implementations
  * that require it.
@@ -651,6 +652,11 @@ struct ssl_session_st
 #define SSL_MODE_SEND_CLIENTHELLO_TIME 0x00000020L
 #define SSL_MODE_SEND_SERVERHELLO_TIME 0x00000040L
 
+/* When set, clients may send application data before receipt of CCS
+ * and Finished.  This mode enables full-handshakes to 'complete' in
+ * one RTT. */
+#define SSL_MODE_HANDSHAKE_CUTTHROUGH 0x00000200L
+
 /* Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value,
  * they cannot be used to clear bits. */
 
@@ -1424,10 +1430,12 @@ extern "C" {
 /* Is the SSL_connection established? */
 #define SSL_get_state(a)		SSL_state(a)
 #define SSL_is_init_finished(a)		(SSL_state(a) == SSL_ST_OK)
-#define SSL_in_init(a)			(SSL_state(a)&SSL_ST_INIT)
+#define SSL_in_init(a)			((SSL_state(a)&SSL_ST_INIT) && \
+					!SSL_cutthrough_complete(a))
 #define SSL_in_before(a)		(SSL_state(a)&SSL_ST_BEFORE)
 #define SSL_in_connect_init(a)		(SSL_state(a)&SSL_ST_CONNECT)
 #define SSL_in_accept_init(a)		(SSL_state(a)&SSL_ST_ACCEPT)
+int SSL_cutthrough_complete(const SSL *s);
 
 /* The following 2 states are kept in ssl->rstate when reads fail,
  * you should not need these */
diff --git a/ssl/ssl3.h b/ssl/ssl3.h
index cb8b249..9a61b71 100644
--- a/ssl/ssl3.h
+++ b/ssl/ssl3.h
@@ -556,6 +556,7 @@ typedef struct ssl3_state_st
 /*client */
 /* extra state */
 #define SSL3_ST_CW_FLUSH		(0x100|SSL_ST_CONNECT)
+#define SSL3_ST_CUTTHROUGH_COMPLETE	(0x101|SSL_ST_CONNECT)
 #ifndef OPENSSL_NO_SCTP
 #define DTLS1_SCTP_ST_CW_WRITE_SOCK			(0x310|SSL_ST_CONNECT)
 #define DTLS1_SCTP_ST_CR_READ_SOCK			(0x320|SSL_ST_CONNECT)
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 6dbc3c1..7892928 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -3225,6 +3225,48 @@ void SSL_set_msg_callback(SSL *ssl, void (*cb)(int write_p, int version, int con
 	SSL_callback_ctrl(ssl, SSL_CTRL_SET_MSG_CALLBACK, (void (*)(void))cb);
 	}
 
+int SSL_cutthrough_complete(const SSL *s)
+	{
+	return (!s->server &&                 /* cutthrough only applies to clients */
+		!s->hit &&                        /* full-handshake */
+		s->version >= SSL3_VERSION &&
+		s->s3->in_read_app_data == 0 &&   /* cutthrough only applies to write() */
+		(SSL_get_mode((SSL*)s) & SSL_MODE_HANDSHAKE_CUTTHROUGH) &&  /* cutthrough enabled */
+		ssl3_can_cutthrough(s) &&                                   /* cutthrough allowed */
+		s->s3->previous_server_finished_len == 0 &&                 /* not a renegotiation handshake */
+		(s->state == SSL3_ST_CR_SESSION_TICKET_A ||                 /* ready to write app-data*/
+			s->state == SSL3_ST_CR_FINISHED_A));
+	}
+
+int ssl3_can_cutthrough(const SSL *s)
+	{
+	const SSL_CIPHER *c;
+
+	/* require a strong enough cipher */
+	if (SSL_get_cipher_bits(s, NULL) < 128)
+		return 0;
+
+	/* require ALPN or NPN extension */
+	if (!s->s3->alpn_selected
+#ifndef OPENSSL_NO_NEXTPROTONEG
+		&& !s->s3->next_proto_neg_seen
+#endif
+	)
+		{
+		return 0;
+		}
+
+	/* require a forward-secret cipher */
+	c = SSL_get_current_cipher(s);
+	if (!c || (c->algorithm_mkey != SSL_kEDH &&
+			c->algorithm_mkey != SSL_kEECDH))
+		{
+		return 0;
+		}
+
+	return 1;
+	}
+
 /* Allocates new EVP_MD_CTX and sets pointer to it into given pointer
  * vairable, freeing  EVP_MD_CTX previously stored in that variable, if
  * any. If EVP_MD pointer is passed, initializes ctx with this md
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index e485907..3b1d644 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1126,6 +1126,8 @@ int tls12_get_sigid(const EVP_PKEY *pk);
 const EVP_MD *tls12_get_hash(unsigned char hash_alg);
 
 #endif
+
+int ssl3_can_cutthrough(const SSL *s);
 EVP_MD_CTX* ssl_replace_hash(EVP_MD_CTX **hash,const EVP_MD *md) ;
 void ssl_clear_hash_ctx(EVP_MD_CTX **hash);
 int ssl_add_serverhello_renegotiate_ext(SSL *s, unsigned char *p, int *len,
diff --git a/ssl/ssltest.c b/ssl/ssltest.c
index 4f80be8..28fa223 100644
--- a/ssl/ssltest.c
+++ b/ssl/ssltest.c
@@ -369,8 +369,9 @@ static void sv_usage(void)
 	               "                 (default is sect163r2).\n");
 #endif
 	fprintf(stderr," -test_cipherlist - Verifies the order of the ssl cipher lists.\n"
 		       "                    When this option is requested, the cipherlist\n"
 		       "                    tests are run instead of handshake tests.\n");
+	fprintf(stderr," -cutthrough      - enable 1-RTT full-handshake for strong ciphers\n");
 	}
 
 static void print_details(SSL *c_ssl, const char *prefix)
@@ -549,6 +550,7 @@ int main(int argc, char *argv[])
 #ifdef OPENSSL_FIPS
 	int fips_mode=0;
 #endif
+	int cutthrough = 0;
 
 	verbose = 0;
 	debug = 0;
@@ -765,6 +767,10 @@ int main(int argc, char *argv[])
 			{
 			test_cipherlist = 1;
 			}
+		else if (strcmp(*argv, "-cutthrough") == 0)
+			{
+			cutthrough = 1;
+			}
 		else
 			{
 			fprintf(stderr,"unknown option %s\n",*argv);
@@ -906,6 +912,12 @@ bad:
 		SSL_CTX_set_cipher_list(c_ctx,cipher);
 		SSL_CTX_set_cipher_list(s_ctx,cipher);
 		}
+	if (cutthrough)
+		{
+		int ssl_mode = SSL_CTX_get_mode(c_ctx);
+		ssl_mode |= SSL_MODE_HANDSHAKE_CUTTHROUGH;
+		SSL_CTX_set_mode(c_ctx, ssl_mode);
+		}
 
 #ifndef OPENSSL_NO_DH
 	if (!no_dhe)
diff --git a/test/testssl b/test/testssl
index 4e8542b..b5f90ba 100644
--- a/test/testssl
+++ b/test/testssl
@@ -70,6 +70,9 @@ $ssltest -client_auth $CA $extra || exit 1
 echo test sslv2/sslv3 with both client and server authentication
 $ssltest -server_auth -client_auth $CA $extra || exit 1
 
+echo test sslv2/sslv3 with both client and server authentication and handshake cutthrough
+$ssltest -server_auth -client_auth -cutthrough $CA $extra || exit 1
+
 echo test sslv2 via BIO pair
 $ssltest -bio_pair -ssl2 $extra || exit 1
 
-- 
1.9.1.423.g4596e3a