Browse Source

[tls] Support stateless session resumption

Add support for RFC5077 session ticket extensions to allow for
stateless TLS session resumption.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 5 years ago
parent
commit
eaba1a22b8
2 changed files with 122 additions and 19 deletions
  1. 12
    0
      src/include/ipxe/tls.h
  2. 110
    19
      src/net/tls.c

+ 12
- 0
src/include/ipxe/tls.h View File

@@ -63,6 +63,7 @@ struct tls_header {
63 63
 #define TLS_HELLO_REQUEST 0
64 64
 #define TLS_CLIENT_HELLO 1
65 65
 #define TLS_SERVER_HELLO 2
66
+#define TLS_NEW_SESSION_TICKET 4
66 67
 #define TLS_CERTIFICATE 11
67 68
 #define TLS_SERVER_KEY_EXCHANGE 12
68 69
 #define TLS_CERTIFICATE_REQUEST 13
@@ -108,6 +109,9 @@ struct tls_header {
108 109
 /* TLS signature algorithms extension */
109 110
 #define TLS_SIGNATURE_ALGORITHMS 13
110 111
 
112
+/* TLS session ticket extension */
113
+#define TLS_SESSION_TICKET 35
114
+
111 115
 /* TLS renegotiation information extension */
112 116
 #define TLS_RENEGOTIATION_INFO 0xff01
113 117
 
@@ -255,6 +259,10 @@ struct tls_session {
255 259
 	uint8_t id[32];
256 260
 	/** Length of session ID */
257 261
 	size_t id_len;
262
+	/** Session ticket */
263
+	void *ticket;
264
+	/** Length of session ticket */
265
+	size_t ticket_len;
258 266
 	/** Master secret */
259 267
 	uint8_t master_secret[48];
260 268
 
@@ -275,6 +283,10 @@ struct tls_connection {
275 283
 	uint8_t session_id[32];
276 284
 	/** Length of session ID */
277 285
 	size_t session_id_len;
286
+	/** New session ticket */
287
+	void *new_session_ticket;
288
+	/** Length of new session ticket */
289
+	size_t new_session_ticket_len;
278 290
 
279 291
 	/** Plaintext stream */
280 292
 	struct interface plainstream;

+ 110
- 19
src/net/tls.c View File

@@ -102,6 +102,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
102 102
 #define EINFO_EINVAL_MAC						\
103 103
 	__einfo_uniqify ( EINFO_EINVAL, 0x0d,				\
104 104
 			  "Invalid MAC" )
105
+#define EINVAL_TICKET __einfo_error ( EINFO_EINVAL_TICKET )
106
+#define EINFO_EINVAL_TICKET						\
107
+	__einfo_uniqify ( EINFO_EINVAL, 0x0e,				\
108
+			  "Invalid New Session Ticket record")
105 109
 #define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT )
106 110
 #define EINFO_EIO_ALERT							\
107 111
 	__einfo_uniqify ( EINFO_EIO, 0x01,				\
@@ -326,6 +330,9 @@ static void free_tls_session ( struct refcnt *refcnt ) {
326 330
 	/* Remove from list of sessions */
327 331
 	list_del ( &session->list );
328 332
 
333
+	/* Free session ticket */
334
+	free ( session->ticket );
335
+
329 336
 	/* Free session */
330 337
 	free ( session );
331 338
 }
@@ -343,6 +350,7 @@ static void free_tls ( struct refcnt *refcnt ) {
343 350
 	struct io_buffer *tmp;
344 351
 
345 352
 	/* Free dynamically-allocated resources */
353
+	free ( tls->new_session_ticket );
346 354
 	tls_clear_cipher ( tls, &tls->tx_cipherspec );
347 355
 	tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
348 356
 	tls_clear_cipher ( tls, &tls->rx_cipherspec );
@@ -1007,7 +1015,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
1007 1015
 		uint16_t version;
1008 1016
 		uint8_t random[32];
1009 1017
 		uint8_t session_id_len;
1010
-		uint8_t session_id[session->id_len];
1018
+		uint8_t session_id[tls->session_id_len];
1011 1019
 		uint16_t cipher_suite_len;
1012 1020
 		uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
1013 1021
 		uint8_t compression_methods_len;
@@ -1043,18 +1051,17 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
1043 1051
 				uint8_t data[ tls->secure_renegotiation ?
1044 1052
 					      sizeof ( tls->verify.client ) :0];
1045 1053
 			} __attribute__ (( packed )) renegotiation_info;
1054
+			uint16_t session_ticket_type;
1055
+			uint16_t session_ticket_len;
1056
+			struct {
1057
+				uint8_t data[session->ticket_len];
1058
+			} __attribute__ (( packed )) session_ticket;
1046 1059
 		} __attribute__ (( packed )) extensions;
1047 1060
 	} __attribute__ (( packed )) hello;
1048 1061
 	struct tls_cipher_suite *suite;
1049 1062
 	struct tls_signature_hash_algorithm *sighash;
1050 1063
 	unsigned int i;
1051 1064
 
1052
-	/* Record requested session ID and associated master secret */
1053
-	memcpy ( tls->session_id, session->id, sizeof ( tls->session_id ) );
1054
-	tls->session_id_len = session->id_len;
1055
-	memcpy ( tls->master_secret, session->master_secret,
1056
-		 sizeof ( tls->master_secret ) );
1057
-
1058 1065
 	/* Construct record */
1059 1066
 	memset ( &hello, 0, sizeof ( hello ) );
1060 1067
 	hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
@@ -1102,6 +1109,11 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
1102 1109
 		= sizeof ( hello.extensions.renegotiation_info.data );
1103 1110
 	memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client,
1104 1111
 		 sizeof ( hello.extensions.renegotiation_info.data ) );
1112
+	hello.extensions.session_ticket_type = htons ( TLS_SESSION_TICKET );
1113
+	hello.extensions.session_ticket_len
1114
+		= htons ( sizeof ( hello.extensions.session_ticket ) );
1115
+	memcpy ( hello.extensions.session_ticket.data, session->ticket,
1116
+		 sizeof ( hello.extensions.session_ticket.data ) );
1105 1117
 
1106 1118
 	return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
1107 1119
 }
@@ -1631,6 +1643,57 @@ static int tls_new_server_hello ( struct tls_connection *tls,
1631 1643
 	return 0;
1632 1644
 }
1633 1645
 
1646
+/**
1647
+ * Receive New Session Ticket handshake record
1648
+ *
1649
+ * @v tls		TLS connection
1650
+ * @v data		Plaintext handshake record
1651
+ * @v len		Length of plaintext handshake record
1652
+ * @ret rc		Return status code
1653
+ */
1654
+static int tls_new_session_ticket ( struct tls_connection *tls,
1655
+				    const void *data, size_t len ) {
1656
+	const struct {
1657
+		uint32_t lifetime;
1658
+		uint16_t len;
1659
+		uint8_t ticket[0];
1660
+	} __attribute__ (( packed )) *new_session_ticket = data;
1661
+	size_t ticket_len;
1662
+
1663
+	/* Parse header */
1664
+	if ( sizeof ( *new_session_ticket ) > len ) {
1665
+		DBGC ( tls, "TLS %p received underlength New Session Ticket\n",
1666
+		       tls );
1667
+		DBGC_HD ( tls, data, len );
1668
+		return -EINVAL_TICKET;
1669
+	}
1670
+	ticket_len = ntohs ( new_session_ticket->len );
1671
+	if ( ticket_len > ( len - sizeof ( *new_session_ticket ) ) ) {
1672
+		DBGC ( tls, "TLS %p received overlength New Session Ticket\n",
1673
+		       tls );
1674
+		DBGC_HD ( tls, data, len );
1675
+		return -EINVAL_TICKET;
1676
+	}
1677
+
1678
+	/* Free any unapplied new session ticket */
1679
+	free ( tls->new_session_ticket );
1680
+	tls->new_session_ticket = NULL;
1681
+	tls->new_session_ticket_len = 0;
1682
+
1683
+	/* Record ticket */
1684
+	tls->new_session_ticket = malloc ( ticket_len );
1685
+	if ( ! tls->new_session_ticket )
1686
+		return -ENOMEM;
1687
+	memcpy ( tls->new_session_ticket, new_session_ticket->ticket,
1688
+		 ticket_len );
1689
+	tls->new_session_ticket_len = ticket_len;
1690
+	DBGC ( tls, "TLS %p new session ticket:\n", tls );
1691
+	DBGC_HDA ( tls, 0, tls->new_session_ticket,
1692
+		   tls->new_session_ticket_len );
1693
+
1694
+	return 0;
1695
+}
1696
+
1634 1697
 /**
1635 1698
  * Parse certificate chain
1636 1699
  *
@@ -1863,12 +1926,21 @@ static int tls_new_finished ( struct tls_connection *tls,
1863 1926
 		tls_tx_resume ( tls );
1864 1927
 	}
1865 1928
 
1866
-	/* Record session ID and master secret, if applicable */
1929
+	/* Record session ID, ticket, and master secret, if applicable */
1930
+	if ( tls->session_id_len || tls->new_session_ticket_len ) {
1931
+		memcpy ( session->master_secret, tls->master_secret,
1932
+			 sizeof ( session->master_secret ) );
1933
+	}
1867 1934
 	if ( tls->session_id_len ) {
1868 1935
 		session->id_len = tls->session_id_len;
1869 1936
 		memcpy ( session->id, tls->session_id, sizeof ( session->id ) );
1870
-		memcpy ( session->master_secret, tls->master_secret,
1871
-			 sizeof ( session->master_secret ) );
1937
+	}
1938
+	if ( tls->new_session_ticket_len ) {
1939
+		free ( session->ticket );
1940
+		session->ticket = tls->new_session_ticket;
1941
+		session->ticket_len = tls->new_session_ticket_len;
1942
+		tls->new_session_ticket = NULL;
1943
+		tls->new_session_ticket_len = 0;
1872 1944
 	}
1873 1945
 
1874 1946
 	/* Move to end of session's connection list and allow other
@@ -1933,6 +2005,10 @@ static int tls_new_handshake ( struct tls_connection *tls,
1933 2005
 		case TLS_SERVER_HELLO:
1934 2006
 			rc = tls_new_server_hello ( tls, payload, payload_len );
1935 2007
 			break;
2008
+		case TLS_NEW_SESSION_TICKET:
2009
+			rc = tls_new_session_ticket ( tls, payload,
2010
+						      payload_len );
2011
+			break;
1936 2012
 		case TLS_CERTIFICATE:
1937 2013
 			rc = tls_new_certificate ( tls, payload, payload_len );
1938 2014
 			break;
@@ -2804,16 +2880,31 @@ static void tls_tx_step ( struct tls_connection *tls ) {
2804 2880
 
2805 2881
 	/* Send first pending transmission */
2806 2882
 	if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) {
2807
-		/* Wait for session ID to become available unless we
2808
-		 * are the lead connection within the session.
2883
+		/* Serialise server negotiations within a session, to
2884
+		 * provide a consistent view of session IDs and
2885
+		 * session tickets.
2809 2886
 		 */
2810
-		if ( session->id_len == 0 ) {
2811
-			list_for_each_entry ( conn, &session->conn, list ) {
2812
-				if ( conn == tls )
2813
-					break;
2814
-				if ( is_pending ( &conn->server_negotiation ) )
2815
-					return;
2816
-			}
2887
+		list_for_each_entry ( conn, &session->conn, list ) {
2888
+			if ( conn == tls )
2889
+				break;
2890
+			if ( is_pending ( &conn->server_negotiation ) )
2891
+				return;
2892
+		}
2893
+		/* Record or generate session ID and associated master secret */
2894
+		if ( session->id_len ) {
2895
+			/* Attempt to resume an existing session */
2896
+			memcpy ( tls->session_id, session->id,
2897
+				 sizeof ( tls->session_id ) );
2898
+			tls->session_id_len = session->id_len;
2899
+			memcpy ( tls->master_secret, session->master_secret,
2900
+				 sizeof ( tls->master_secret ) );
2901
+		} else {
2902
+			/* No existing session: use a random session ID */
2903
+			assert ( sizeof ( tls->session_id ) ==
2904
+				 sizeof ( tls->client_random ) );
2905
+			memcpy ( tls->session_id, &tls->client_random,
2906
+				 sizeof ( tls->session_id ) );
2907
+			tls->session_id_len = sizeof ( tls->session_id );
2817 2908
 		}
2818 2909
 		/* Send Client Hello */
2819 2910
 		if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {

Loading…
Cancel
Save