mirror of
https://github.com/ipxe/ipxe
synced 2026-10-07 15:05:42 +03:00
[tls] Handle TLS version 1.3 client Finished
The TLS version 1.3 client Finished is somewhat messy to handle: its verify_data must be calculated with the key schedule still holding the client handshake traffic secret, the application traffic secret must be calculated before the client Finished is added to the transcript digest, and the application traffic keys must be activated only after sending the client Finished. Since the action of sending the client Finished also adds the client Finished to the transcript digest, this necessitates an awkward sequence of events. (A cleaner protocol design might have chosen to derive the client application traffic secret from the transcript digest up to and including the client Finished.) Perform the various necessary contortions to construct the client Finished and to transition to using the application traffic secrets. With this commit, TLS version 1.3 is functional for the first time. It is not yet enabled by default, since there are still some missing features such as session resumption. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
+51
-8
@@ -773,6 +773,32 @@ static void tls_clear_cipher ( struct tls_connection *tls __unused,
|
||||
cipherspec->seq = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare cipher specification for a new traffic phase
|
||||
*
|
||||
* @v tls TLS connection
|
||||
* @v cipherspec TLS cipher specification
|
||||
* @v phase Traffic phase
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_prep_cipher ( struct tls_connection *tls,
|
||||
struct tls_cipherspec *cipherspec,
|
||||
const struct tls_phase *phase ) {
|
||||
const struct tls_endpoint *writer = cipherspec->writer;
|
||||
struct secure_pipe *pipe = cipherspec->pipe;
|
||||
int rc;
|
||||
|
||||
/* Generate traffic secret */
|
||||
if ( ( rc = tlskey_traffic ( &tls->key, writer, phase ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not generate %s %s traffic secret: "
|
||||
"%s\n", tls, tls_pipe_name ( tls, pipe ),
|
||||
writer->name, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change cipher specification
|
||||
*
|
||||
@@ -822,13 +848,10 @@ static int tls_change_cipher ( struct tls_connection *tls,
|
||||
/* Record cipher suite */
|
||||
cipherspec->suite = suite;
|
||||
|
||||
/* Generate traffic secret, if applicable */
|
||||
/* Prepare for new traffic phase, if applicable */
|
||||
if ( phase &&
|
||||
( ( rc = tlskey_traffic ( &tls->key, writer, phase ) ) != 0 ) ) {
|
||||
DBGC ( tls, "TLS %p could not generate %s %s traffic secret: "
|
||||
"%s\n", tls, tls_pipe_name ( tls, pipe ),
|
||||
writer->name, strerror ( rc ) );
|
||||
goto err_traffic;
|
||||
( ( rc = tls_prep_cipher ( tls, cipherspec, phase ) ) != 0 ) ) {
|
||||
goto err_prep;
|
||||
}
|
||||
|
||||
/* Generate cipher key material */
|
||||
@@ -858,7 +881,7 @@ static int tls_change_cipher ( struct tls_connection *tls,
|
||||
|
||||
err_channel:
|
||||
err_cipher:
|
||||
err_traffic:
|
||||
err_prep:
|
||||
tls_clear_cipher ( tls, cipherspec );
|
||||
err_alloc:
|
||||
err_null:
|
||||
@@ -2377,6 +2400,7 @@ static int tls_send_change_cipher ( struct tls_connection *tls ) {
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_send_finished ( struct tls_connection *tls ) {
|
||||
struct tls_cipherspec *cipherspec = &tls->tx.cipherspec;
|
||||
struct tls_cipher_suite *suite = tls->suite;
|
||||
size_t verify_len = suite->verify_len;
|
||||
struct {
|
||||
@@ -2400,10 +2424,29 @@ static int tls_send_finished ( struct tls_connection *tls ) {
|
||||
sizeof ( finished.type_length ) ) );
|
||||
memcpy ( finished.verify_data, tls->verify.client, verify_len );
|
||||
|
||||
/* TLS version 1.3 has an awkward design quirk in which the
|
||||
* application traffic secret must be generated using the
|
||||
* digest state prior to sending the client Finished, but the
|
||||
* corresponding application traffic keys must not be
|
||||
* activated until after sending the record.
|
||||
*/
|
||||
if ( tls_version ( tls, TLS_VERSION_TLS_1_3 ) &&
|
||||
( ( rc = tls_prep_cipher ( tls, cipherspec,
|
||||
&tls_application ) ) != 0 ) ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Transmit record */
|
||||
if ( ( rc = tls_send_handshake ( tls, &finished,
|
||||
sizeof ( finished ) ) ) != 0 )
|
||||
sizeof ( finished ) ) ) != 0 ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Change the (already prepared) cipher specification */
|
||||
if ( tls_version ( tls, TLS_VERSION_TLS_1_3 ) &&
|
||||
( ( rc = tls_change_cipher ( tls, cipherspec, NULL ) ) != 0 ) ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Mark client as finished */
|
||||
pending_put ( &tls->client.negotiation );
|
||||
|
||||
Reference in New Issue
Block a user