From: Michael Brown Date: Fri, 19 Jun 2026 17:09:56 +0000 (+0100) Subject: [tls] Centralise pseudorandom data generation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;ds=inline;p=thirdparty%2Fipxe.git [tls] Centralise pseudorandom data generation TLS version 1.3 has a formal key schedule based on HKDF, and requires the client to be able to recall ephemeral secrets at multiple points within the connection lifecycle. For example: the ephemeral private key for X25519 key exchange may be required when constructing ClientHello (for a TLS version 1.3 key share) or when constructing ClientKeyExchange (if subsequently falling back to use TLS version 1.2), and again when parsing a ServerHello key share or a ServerKeyExchange. Some ephemeral private keys may be large (e.g. for ffdhe4096). Avoid the need to store these large (and variably sized) private keys by instead instantiating a standalone HKDF instance that we seed with per-connection random data and subsequently use to generate ephemeral private keys on demand. (Note that this instance is unrelated to the HKDF instance defined in the formal key schedule for TLS: we are choosing to reuse HKDF for this purpose simply because supporting TLS version 1.3 will already require HKDF support to be present.) We use the key exchange algorithm name (e.g. "x25519") as additional information to ensure separation between keys used for different purposes. Since the initial random seed is generated afresh for each connection, and since there can meaningfully be only one ephemeral private key per key exchange algorithm per connection, this is sufficient to ensure separation. Having instantiated this HKDF, we then also use it to generate the client random bytes (with the label "client random"), to generate the random portion of the pre-master secret for classic RSA key exchange (with the label "classic pre-master"), and to generate the random portion of record IVs (using the authentication header structure, which is already guaranteed to be unique per record within a connection). Doing this allows us to eliminate all other calls to the RNG, and removes some potential failure paths. We reset the HKDF on a connection restart and on connection close, to preserve the property of forward secrecy. Signed-off-by: Michael Brown --- diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 18e644617..210444a38 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -17,6 +17,7 @@ FILE_SECBOOT ( PERMITTED ); #include #include #include +#include #include #include #include @@ -351,6 +352,15 @@ struct tls_session { struct list_head conn; }; +/** HKDF algorithm for ephemeral secrets */ +#define tls_ephemeral_algorithm sha256_algorithm + +/** TLS key schedule */ +struct tls_key_schedule { + /** Ephemeral secret pseudorandom key */ + uint8_t ephemeral[SHA256_DIGEST_SIZE]; +}; + /** TLS transmit state */ struct tls_tx { /** Cipher specifications */ @@ -455,6 +465,8 @@ struct tls_connection { /** Verification data */ struct tls_verify_data verify; + /** Key schedule */ + struct tls_key_schedule key; /** Transmit state */ struct tls_tx tx; /** Receive state */ diff --git a/src/net/tls.c b/src/net/tls.c index a8ca95162..097e72ee0 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -40,6 +40,7 @@ FILE_SECBOOT ( PERMITTED ); #include #include #include +#include #include #include #include @@ -196,6 +197,7 @@ FILE_SECBOOT ( PERMITTED ); /** List of TLS session */ static LIST_HEAD ( tls_sessions ); +static void tls_key_reset ( struct tls_connection *tls ); static void tls_tx_resume_all ( struct tls_session *session ); static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls, size_t len ); @@ -378,41 +380,101 @@ static void tls_close ( struct tls_connection *tls, int rc ) { list_del ( &tls->list ); INIT_LIST_HEAD ( &tls->list ); + /* Destroy ephemeral master key */ + tls_key_reset ( tls ); + /* Resume all other connections, in case we were the lead connection */ tls_tx_resume_all ( tls->session ); } /****************************************************************************** * - * Random number generation + * Key schedule * ****************************************************************************** */ /** - * Generate random data + * Initialise key schedule * * @v tls TLS connection - * @v data Buffer to fill - * @v len Length of buffer * @ret rc Return status code */ -static int tls_generate_random ( struct tls_connection *tls, - void *data, size_t len ) { +static int tls_key_init ( struct tls_connection *tls ) { + struct tls_key_schedule *key = &tls->key; + struct digest_algorithm *digest = &tls_ephemeral_algorithm; + static const char salt[16] = "ephemeral master"; int rc; /* Generate random bits with no additional input and without * prediction resistance */ - if ( ( rc = rbg_generate ( NULL, 0, 0, data, len ) ) != 0 ) { + if ( ( rc = rbg_generate ( NULL, 0, 0, key->ephemeral, + sizeof ( key->ephemeral ) ) ) != 0 ) { DBGC ( tls, "TLS %p could not generate random data: %s\n", tls, strerror ( rc ) ); return rc; } + /* Generate ephemeral master secret */ + hkdf_extract ( digest, salt, sizeof ( salt ), key->ephemeral, + sizeof ( key->ephemeral ), key->ephemeral ); + return 0; } +/** + * Generate ephemeral secret + * + * @v tls TLS connection + * @v info Additional information (or NULL) + * @v info_len Length of additional information + * @v out Ephemeral secret to fill in + * @v len Length of ephemeral secret + */ +static void tls_ephemeral ( struct tls_connection *tls, const void *info, + size_t info_len, void *out, size_t len ) { + struct tls_key_schedule *key = &tls->key; + struct digest_algorithm *digest = &tls_ephemeral_algorithm; + + /* Generate from ephemeral master secret and additional information */ + hkdf_expand ( digest, key->ephemeral, info, info_len, out, len ); +} + +/** + * Generate ephemeral secret from label + * + * @v tls TLS connection + * @v label Secret label + * @v out Ephemeral secret to fill in + * @v len Length of ephemeral secret + */ +static void tls_ephemeral_label ( struct tls_connection *tls, + const char *label, void *out, size_t len ) { + + /* Generate from ephemeral master secret and label */ + tls_ephemeral ( tls, label, strlen ( label ), out, len ); + DBGC2 ( tls, "TLS %p ephemeral %s:\n", tls, label ); + DBGC2_HDA ( tls, 0, out, len ); +} + +/** + * Reset key schedule + * + * @v tls TLS connection + */ +static void tls_key_reset ( struct tls_connection *tls ) { + struct tls_key_schedule *key = &tls->key; + + /* Derive a new ephemeral master secret */ + tls_ephemeral_label ( tls, "key reset", key->ephemeral, + sizeof ( key->ephemeral ) ); + + /* (Re)generate client random bytes */ + tls_ephemeral_label ( tls, "client random", &tls->client.random.random, + sizeof ( tls->client.random.random ) ); +} + /** * Update HMAC with a list of ( data, len ) pairs * @@ -1080,6 +1142,9 @@ static void tls_restart ( struct tls_connection *tls ) { assert ( ! is_pending ( &tls->server.negotiation ) ); assert ( ! is_pending ( &tls->server.validation ) ); + /* Reset key schedule */ + tls_key_reset ( tls ); + /* (Re)start negotiation */ tls->tx.pending = TLS_TX_CLIENT_HELLO; tls_tx_resume ( tls ); @@ -1376,10 +1441,9 @@ static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { /* Generate pre-master secret */ pre_master_secret.version = htons ( TLS_VERSION_MAX ); - if ( ( rc = tls_generate_random ( tls, &pre_master_secret.random, - ( sizeof ( pre_master_secret.random ) ) ) ) != 0 ) { - goto err_random; - } + tls_ephemeral_label ( tls, "classic pre-master", + &pre_master_secret.random, + sizeof ( pre_master_secret.random ) ); /* Encrypt pre-master secret using server's public key */ if ( ( rc = pubkey_encrypt ( pubkey, &tls->server.key, &cursor, @@ -1420,7 +1484,6 @@ static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { tls_generate_master_secret ( tls, &pre_master_secret, sizeof ( pre_master_secret ) ); - err_random: err_encrypt: err_prepend: err_send: @@ -1594,10 +1657,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name ); /* Generate Diffie-Hellman private key */ - if ( ( rc = tls_generate_random ( tls, private, - sizeof ( private ) ) ) != 0 ) { - goto err_random; - } + tls_ephemeral_label ( tls, exchange->name, private, + sizeof ( private ) ); /* Construct pre-master secret and ClientKeyExchange record */ { @@ -1659,7 +1720,6 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { free ( dynamic ); } err_alloc: - err_random: err_group: err_verify: err_header: @@ -1751,10 +1811,8 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { } __attribute__ (( packed )) key_xchg; /* Generate ephemeral private key */ - if ( ( rc = tls_generate_random ( tls, private, - sizeof ( private ) ) ) != 0){ - return rc; - } + tls_ephemeral_label ( tls, exchange->name, private, + sizeof ( private ) ); /* Generate Client Key Exchange record */ key_xchg.type_length = @@ -3083,12 +3141,16 @@ static int tls_send_record ( struct tls_connection *tls, unsigned int type, if ( record_len > TLS_TX_BUFSIZE ) record_len = TLS_TX_BUFSIZE; + /* Construct authentication header */ + authhdr.seq = cpu_to_be64 ( tls->tx.seq ); + authhdr.header.type = type; + authhdr.header.version = htons ( tls->version ); + authhdr.header.length = htons ( record_len ); + /* Construct and set initialisation vector */ memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) ); - if ( ( rc = tls_generate_random ( tls, iv.rec, - sizeof ( iv.rec ) ) ) != 0 ) { - goto err_random; - } + tls_ephemeral ( tls, &authhdr, sizeof ( authhdr ), iv.rec, + sizeof ( iv.rec ) ); if ( ( rc = cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) ) ) != 0 ) { DBGC ( tls, "TLS %p could not set TX IV: %s\n", @@ -3096,11 +3158,7 @@ static int tls_send_record ( struct tls_connection *tls, unsigned int type, goto err_setiv; } - /* Construct and process authentication data */ - authhdr.seq = cpu_to_be64 ( tls->tx.seq ); - authhdr.header.type = type; - authhdr.header.version = htons ( tls->version ); - authhdr.header.length = htons ( record_len ); + /* Process authentication data */ if ( suite->mac_len ) { tls_hmac ( cipherspec, &authhdr, plaintext, record_len, mac ); @@ -3176,7 +3234,6 @@ static int tls_send_record ( struct tls_connection *tls, unsigned int type, err_deliver: err_setiv: - err_random: free_iob ( iobuf ); return rc; } @@ -3992,10 +4049,8 @@ int add_tls ( struct interface *xfer, const char *name, iob_populate ( &tls->rx.iobuf, &tls->rx.header, 0, sizeof ( tls->rx.header ) ); INIT_LIST_HEAD ( &tls->rx.data ); - if ( ( rc = tls_generate_random ( tls, &tls->client.random.random, - ( sizeof ( tls->client.random.random ) ) ) ) != 0 ) { - goto err_random; - } + if ( ( rc = tls_key_init ( tls ) ) != 0 ) + goto err_key; if ( ( rc = tls_session ( tls, name ) ) != 0 ) goto err_session; list_add_tail ( &tls->list, &tls->session->conn ); @@ -4009,7 +4064,7 @@ int add_tls ( struct interface *xfer, const char *name, return 0; err_session: - err_random: + err_key: ref_put ( &tls->refcnt ); err_alloc: return rc;