mirror of
https://github.com/ipxe/ipxe
synced 2025-12-17 10:01:03 +03:00
[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>
This commit is contained in:
@@ -63,6 +63,7 @@ struct tls_header {
|
|||||||
#define TLS_HELLO_REQUEST 0
|
#define TLS_HELLO_REQUEST 0
|
||||||
#define TLS_CLIENT_HELLO 1
|
#define TLS_CLIENT_HELLO 1
|
||||||
#define TLS_SERVER_HELLO 2
|
#define TLS_SERVER_HELLO 2
|
||||||
|
#define TLS_NEW_SESSION_TICKET 4
|
||||||
#define TLS_CERTIFICATE 11
|
#define TLS_CERTIFICATE 11
|
||||||
#define TLS_SERVER_KEY_EXCHANGE 12
|
#define TLS_SERVER_KEY_EXCHANGE 12
|
||||||
#define TLS_CERTIFICATE_REQUEST 13
|
#define TLS_CERTIFICATE_REQUEST 13
|
||||||
@@ -108,6 +109,9 @@ struct tls_header {
|
|||||||
/* TLS signature algorithms extension */
|
/* TLS signature algorithms extension */
|
||||||
#define TLS_SIGNATURE_ALGORITHMS 13
|
#define TLS_SIGNATURE_ALGORITHMS 13
|
||||||
|
|
||||||
|
/* TLS session ticket extension */
|
||||||
|
#define TLS_SESSION_TICKET 35
|
||||||
|
|
||||||
/* TLS renegotiation information extension */
|
/* TLS renegotiation information extension */
|
||||||
#define TLS_RENEGOTIATION_INFO 0xff01
|
#define TLS_RENEGOTIATION_INFO 0xff01
|
||||||
|
|
||||||
@@ -255,6 +259,10 @@ struct tls_session {
|
|||||||
uint8_t id[32];
|
uint8_t id[32];
|
||||||
/** Length of session ID */
|
/** Length of session ID */
|
||||||
size_t id_len;
|
size_t id_len;
|
||||||
|
/** Session ticket */
|
||||||
|
void *ticket;
|
||||||
|
/** Length of session ticket */
|
||||||
|
size_t ticket_len;
|
||||||
/** Master secret */
|
/** Master secret */
|
||||||
uint8_t master_secret[48];
|
uint8_t master_secret[48];
|
||||||
|
|
||||||
@@ -275,6 +283,10 @@ struct tls_connection {
|
|||||||
uint8_t session_id[32];
|
uint8_t session_id[32];
|
||||||
/** Length of session ID */
|
/** Length of session ID */
|
||||||
size_t session_id_len;
|
size_t session_id_len;
|
||||||
|
/** New session ticket */
|
||||||
|
void *new_session_ticket;
|
||||||
|
/** Length of new session ticket */
|
||||||
|
size_t new_session_ticket_len;
|
||||||
|
|
||||||
/** Plaintext stream */
|
/** Plaintext stream */
|
||||||
struct interface plainstream;
|
struct interface plainstream;
|
||||||
|
|||||||
117
src/net/tls.c
117
src/net/tls.c
@@ -102,6 +102,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
|||||||
#define EINFO_EINVAL_MAC \
|
#define EINFO_EINVAL_MAC \
|
||||||
__einfo_uniqify ( EINFO_EINVAL, 0x0d, \
|
__einfo_uniqify ( EINFO_EINVAL, 0x0d, \
|
||||||
"Invalid MAC" )
|
"Invalid MAC" )
|
||||||
|
#define EINVAL_TICKET __einfo_error ( EINFO_EINVAL_TICKET )
|
||||||
|
#define EINFO_EINVAL_TICKET \
|
||||||
|
__einfo_uniqify ( EINFO_EINVAL, 0x0e, \
|
||||||
|
"Invalid New Session Ticket record")
|
||||||
#define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT )
|
#define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT )
|
||||||
#define EINFO_EIO_ALERT \
|
#define EINFO_EIO_ALERT \
|
||||||
__einfo_uniqify ( EINFO_EIO, 0x01, \
|
__einfo_uniqify ( EINFO_EIO, 0x01, \
|
||||||
@@ -326,6 +330,9 @@ static void free_tls_session ( struct refcnt *refcnt ) {
|
|||||||
/* Remove from list of sessions */
|
/* Remove from list of sessions */
|
||||||
list_del ( &session->list );
|
list_del ( &session->list );
|
||||||
|
|
||||||
|
/* Free session ticket */
|
||||||
|
free ( session->ticket );
|
||||||
|
|
||||||
/* Free session */
|
/* Free session */
|
||||||
free ( session );
|
free ( session );
|
||||||
}
|
}
|
||||||
@@ -343,6 +350,7 @@ static void free_tls ( struct refcnt *refcnt ) {
|
|||||||
struct io_buffer *tmp;
|
struct io_buffer *tmp;
|
||||||
|
|
||||||
/* Free dynamically-allocated resources */
|
/* Free dynamically-allocated resources */
|
||||||
|
free ( tls->new_session_ticket );
|
||||||
tls_clear_cipher ( tls, &tls->tx_cipherspec );
|
tls_clear_cipher ( tls, &tls->tx_cipherspec );
|
||||||
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
|
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
|
||||||
tls_clear_cipher ( tls, &tls->rx_cipherspec );
|
tls_clear_cipher ( tls, &tls->rx_cipherspec );
|
||||||
@@ -1007,7 +1015,7 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
|
|||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint8_t random[32];
|
uint8_t random[32];
|
||||||
uint8_t session_id_len;
|
uint8_t session_id_len;
|
||||||
uint8_t session_id[session->id_len];
|
uint8_t session_id[tls->session_id_len];
|
||||||
uint16_t cipher_suite_len;
|
uint16_t cipher_suite_len;
|
||||||
uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
|
uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
|
||||||
uint8_t compression_methods_len;
|
uint8_t compression_methods_len;
|
||||||
@@ -1043,18 +1051,17 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
|
|||||||
uint8_t data[ tls->secure_renegotiation ?
|
uint8_t data[ tls->secure_renegotiation ?
|
||||||
sizeof ( tls->verify.client ) :0];
|
sizeof ( tls->verify.client ) :0];
|
||||||
} __attribute__ (( packed )) renegotiation_info;
|
} __attribute__ (( packed )) renegotiation_info;
|
||||||
|
uint16_t session_ticket_type;
|
||||||
|
uint16_t session_ticket_len;
|
||||||
|
struct {
|
||||||
|
uint8_t data[session->ticket_len];
|
||||||
|
} __attribute__ (( packed )) session_ticket;
|
||||||
} __attribute__ (( packed )) extensions;
|
} __attribute__ (( packed )) extensions;
|
||||||
} __attribute__ (( packed )) hello;
|
} __attribute__ (( packed )) hello;
|
||||||
struct tls_cipher_suite *suite;
|
struct tls_cipher_suite *suite;
|
||||||
struct tls_signature_hash_algorithm *sighash;
|
struct tls_signature_hash_algorithm *sighash;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/* Record requested session ID and associated master secret */
|
|
||||||
memcpy ( tls->session_id, session->id, sizeof ( tls->session_id ) );
|
|
||||||
tls->session_id_len = session->id_len;
|
|
||||||
memcpy ( tls->master_secret, session->master_secret,
|
|
||||||
sizeof ( tls->master_secret ) );
|
|
||||||
|
|
||||||
/* Construct record */
|
/* Construct record */
|
||||||
memset ( &hello, 0, sizeof ( hello ) );
|
memset ( &hello, 0, sizeof ( hello ) );
|
||||||
hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
|
hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
|
||||||
@@ -1102,6 +1109,11 @@ static int tls_send_client_hello ( struct tls_connection *tls ) {
|
|||||||
= sizeof ( hello.extensions.renegotiation_info.data );
|
= sizeof ( hello.extensions.renegotiation_info.data );
|
||||||
memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client,
|
memcpy ( hello.extensions.renegotiation_info.data, tls->verify.client,
|
||||||
sizeof ( hello.extensions.renegotiation_info.data ) );
|
sizeof ( hello.extensions.renegotiation_info.data ) );
|
||||||
|
hello.extensions.session_ticket_type = htons ( TLS_SESSION_TICKET );
|
||||||
|
hello.extensions.session_ticket_len
|
||||||
|
= htons ( sizeof ( hello.extensions.session_ticket ) );
|
||||||
|
memcpy ( hello.extensions.session_ticket.data, session->ticket,
|
||||||
|
sizeof ( hello.extensions.session_ticket.data ) );
|
||||||
|
|
||||||
return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
|
return tls_send_handshake ( tls, &hello, sizeof ( hello ) );
|
||||||
}
|
}
|
||||||
@@ -1631,6 +1643,57 @@ static int tls_new_server_hello ( struct tls_connection *tls,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive New Session Ticket handshake record
|
||||||
|
*
|
||||||
|
* @v tls TLS connection
|
||||||
|
* @v data Plaintext handshake record
|
||||||
|
* @v len Length of plaintext handshake record
|
||||||
|
* @ret rc Return status code
|
||||||
|
*/
|
||||||
|
static int tls_new_session_ticket ( struct tls_connection *tls,
|
||||||
|
const void *data, size_t len ) {
|
||||||
|
const struct {
|
||||||
|
uint32_t lifetime;
|
||||||
|
uint16_t len;
|
||||||
|
uint8_t ticket[0];
|
||||||
|
} __attribute__ (( packed )) *new_session_ticket = data;
|
||||||
|
size_t ticket_len;
|
||||||
|
|
||||||
|
/* Parse header */
|
||||||
|
if ( sizeof ( *new_session_ticket ) > len ) {
|
||||||
|
DBGC ( tls, "TLS %p received underlength New Session Ticket\n",
|
||||||
|
tls );
|
||||||
|
DBGC_HD ( tls, data, len );
|
||||||
|
return -EINVAL_TICKET;
|
||||||
|
}
|
||||||
|
ticket_len = ntohs ( new_session_ticket->len );
|
||||||
|
if ( ticket_len > ( len - sizeof ( *new_session_ticket ) ) ) {
|
||||||
|
DBGC ( tls, "TLS %p received overlength New Session Ticket\n",
|
||||||
|
tls );
|
||||||
|
DBGC_HD ( tls, data, len );
|
||||||
|
return -EINVAL_TICKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free any unapplied new session ticket */
|
||||||
|
free ( tls->new_session_ticket );
|
||||||
|
tls->new_session_ticket = NULL;
|
||||||
|
tls->new_session_ticket_len = 0;
|
||||||
|
|
||||||
|
/* Record ticket */
|
||||||
|
tls->new_session_ticket = malloc ( ticket_len );
|
||||||
|
if ( ! tls->new_session_ticket )
|
||||||
|
return -ENOMEM;
|
||||||
|
memcpy ( tls->new_session_ticket, new_session_ticket->ticket,
|
||||||
|
ticket_len );
|
||||||
|
tls->new_session_ticket_len = ticket_len;
|
||||||
|
DBGC ( tls, "TLS %p new session ticket:\n", tls );
|
||||||
|
DBGC_HDA ( tls, 0, tls->new_session_ticket,
|
||||||
|
tls->new_session_ticket_len );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse certificate chain
|
* Parse certificate chain
|
||||||
*
|
*
|
||||||
@@ -1863,12 +1926,21 @@ static int tls_new_finished ( struct tls_connection *tls,
|
|||||||
tls_tx_resume ( tls );
|
tls_tx_resume ( tls );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record session ID and master secret, if applicable */
|
/* Record session ID, ticket, and master secret, if applicable */
|
||||||
|
if ( tls->session_id_len || tls->new_session_ticket_len ) {
|
||||||
|
memcpy ( session->master_secret, tls->master_secret,
|
||||||
|
sizeof ( session->master_secret ) );
|
||||||
|
}
|
||||||
if ( tls->session_id_len ) {
|
if ( tls->session_id_len ) {
|
||||||
session->id_len = tls->session_id_len;
|
session->id_len = tls->session_id_len;
|
||||||
memcpy ( session->id, tls->session_id, sizeof ( session->id ) );
|
memcpy ( session->id, tls->session_id, sizeof ( session->id ) );
|
||||||
memcpy ( session->master_secret, tls->master_secret,
|
}
|
||||||
sizeof ( session->master_secret ) );
|
if ( tls->new_session_ticket_len ) {
|
||||||
|
free ( session->ticket );
|
||||||
|
session->ticket = tls->new_session_ticket;
|
||||||
|
session->ticket_len = tls->new_session_ticket_len;
|
||||||
|
tls->new_session_ticket = NULL;
|
||||||
|
tls->new_session_ticket_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move to end of session's connection list and allow other
|
/* Move to end of session's connection list and allow other
|
||||||
@@ -1933,6 +2005,10 @@ static int tls_new_handshake ( struct tls_connection *tls,
|
|||||||
case TLS_SERVER_HELLO:
|
case TLS_SERVER_HELLO:
|
||||||
rc = tls_new_server_hello ( tls, payload, payload_len );
|
rc = tls_new_server_hello ( tls, payload, payload_len );
|
||||||
break;
|
break;
|
||||||
|
case TLS_NEW_SESSION_TICKET:
|
||||||
|
rc = tls_new_session_ticket ( tls, payload,
|
||||||
|
payload_len );
|
||||||
|
break;
|
||||||
case TLS_CERTIFICATE:
|
case TLS_CERTIFICATE:
|
||||||
rc = tls_new_certificate ( tls, payload, payload_len );
|
rc = tls_new_certificate ( tls, payload, payload_len );
|
||||||
break;
|
break;
|
||||||
@@ -2804,16 +2880,31 @@ static void tls_tx_step ( struct tls_connection *tls ) {
|
|||||||
|
|
||||||
/* Send first pending transmission */
|
/* Send first pending transmission */
|
||||||
if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) {
|
if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) {
|
||||||
/* Wait for session ID to become available unless we
|
/* Serialise server negotiations within a session, to
|
||||||
* are the lead connection within the session.
|
* provide a consistent view of session IDs and
|
||||||
|
* session tickets.
|
||||||
*/
|
*/
|
||||||
if ( session->id_len == 0 ) {
|
|
||||||
list_for_each_entry ( conn, &session->conn, list ) {
|
list_for_each_entry ( conn, &session->conn, list ) {
|
||||||
if ( conn == tls )
|
if ( conn == tls )
|
||||||
break;
|
break;
|
||||||
if ( is_pending ( &conn->server_negotiation ) )
|
if ( is_pending ( &conn->server_negotiation ) )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* Record or generate session ID and associated master secret */
|
||||||
|
if ( session->id_len ) {
|
||||||
|
/* Attempt to resume an existing session */
|
||||||
|
memcpy ( tls->session_id, session->id,
|
||||||
|
sizeof ( tls->session_id ) );
|
||||||
|
tls->session_id_len = session->id_len;
|
||||||
|
memcpy ( tls->master_secret, session->master_secret,
|
||||||
|
sizeof ( tls->master_secret ) );
|
||||||
|
} else {
|
||||||
|
/* No existing session: use a random session ID */
|
||||||
|
assert ( sizeof ( tls->session_id ) ==
|
||||||
|
sizeof ( tls->client_random ) );
|
||||||
|
memcpy ( tls->session_id, &tls->client_random,
|
||||||
|
sizeof ( tls->session_id ) );
|
||||||
|
tls->session_id_len = sizeof ( tls->session_id );
|
||||||
}
|
}
|
||||||
/* Send Client Hello */
|
/* Send Client Hello */
|
||||||
if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {
|
if ( ( rc = tls_send_client_hello ( tls ) ) != 0 ) {
|
||||||
|
|||||||
Reference in New Issue
Block a user