From e6d0a97c05d238c17eeae5116cb6e9c0fc9fdb56 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Aug 2026 10:37:11 +0100 Subject: [PATCH] [tls] Model classic RSA key transport as a key exchange algorithm Choose to model key transport as a key exchange algorithm that is incapable of generating public keys and where the public key size is zero (implying that the shared secret must be communicated via a means other than key exchange). Signed-off-by: Michael Brown --- src/crypto/tlsclassic.c | 108 ++++++++++++++++++++++++++++++++++++++ src/include/ipxe/crypto.h | 5 ++ src/include/ipxe/tls.h | 10 ++-- src/net/tls.c | 64 +++++++++------------- 4 files changed, 145 insertions(+), 42 deletions(-) create mode 100644 src/crypto/tlsclassic.c diff --git a/src/crypto/tlsclassic.c b/src/crypto/tlsclassic.c new file mode 100644 index 000000000..15a4f264e --- /dev/null +++ b/src/crypto/tlsclassic.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2026 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); + +/** @file + * + * Classic TLS static RSA pre-master secret + * + * With classic static RSA key transport, the client unilaterally + * constructs the shared pre-master secret and then encrypts it using + * the server's public key. + * + * We model key transport as a key exchange algorithm that is + * incapable of generating public keys and where the public key size + * is zero (implying that the shared secret must be communicated via a + * means other than key exchange). + * + * This RSA pre-master secret structure could in principle have been + * used with any public-key algorithm that supports encryption and + * decryption (rather than only signing and verification), but no + * non-RSA cipher suites were ever defined to use this exact same + * structure of the pre-master secret. + * + * Key transport provides no forward secrecy since a compromise of the + * server's long-term private key provides the ability to decrypt all + * pre-master secrets that were encrypted using that key. Almost all + * servers will prefer to use ephemeral key exhange (which does + * provide forward secrecy). We retain support for key transport only + * for the sake of backwards compatibility with older servers. + * + */ + +#include +#include +#include +#include +#include + +/** A classic pre-master private key */ +struct tls_classic_pre_master_private { + /** Random bytes */ + uint8_t random[46]; +} __attribute__ (( packed )); + +/** A classic pre-master shared secret */ +struct tls_classic_pre_master_shared { + /** Highest supported protocol version */ + uint16_t version; + /** Private key */ + struct tls_classic_pre_master_private private; +} __attribute__ (( packed )); + +/** + * Agree classic pre-master secret + * + * @v exchange Key exchange algorithm + * @v private Private key + * @v partner Partner public key + * @v shared Shared secret to fill in + * @ret rc Return status code + */ +static int +tls_classic_pre_master_agree ( struct exchange_algorithm *exchange __unused, + const void *private, + const void *partner __unused, void *shared ) { + struct tls_classic_pre_master_shared *premaster = shared; + + /* We model the classic pre-master secret as a key exchange + * algorithm in which we unilaterally construct the shared + * secret (with no partner public key input). + */ + premaster->version = htons ( TLS_VERSION_MAX ); + memcpy ( &premaster->private, private, sizeof ( premaster->private ) ); + + return 0; +} + +/** Classic pre-master secret key exchange algorithm */ +struct exchange_algorithm tls_classic_pre_master_algorithm = { + .name = "classic pre-master", + .privsize = sizeof ( struct tls_classic_pre_master_private ), + .pubsize = 0, + .sharedsize = sizeof ( struct tls_classic_pre_master_shared ), + .share = exchange_null_share, + .agree = tls_classic_pre_master_agree, +}; diff --git a/src/include/ipxe/crypto.h b/src/include/ipxe/crypto.h index 2049faaa0..6a813b090 100644 --- a/src/include/ipxe/crypto.h +++ b/src/include/ipxe/crypto.h @@ -405,6 +405,11 @@ exchange_agree ( struct exchange_algorithm *exchange, const void *private, return exchange->agree ( exchange, private, partner, shared ); } +static inline __attribute__ (( always_inline )) int +is_key_transport ( struct exchange_algorithm *exchange ) { + return ( exchange->pubsize == 0 ); +} + static inline __attribute__ (( always_inline )) int elliptic_is_infinity ( struct elliptic_curve *curve, const void *point ) { return curve->is_infinity ( curve, point ); diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 4cd322d62..8f6fee7fa 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -376,8 +376,8 @@ struct tls_key_schedule { * derivation. */ struct digest_algorithm *digest; - /** Named key exchange group */ - struct tls_named_group *group; + /** Key exchange algorithm */ + struct exchange_algorithm *exchange; /** Schedule holds secret key material * * This flag is set when shared secret key material is @@ -457,8 +457,10 @@ struct tls_key_schedule { void *dynamic; /** Handshake running transcript digest context */ void *handshake; - /** Key derivation function master secret */ + /** Key derivation function secret */ void *kdf; + /** Length of key derivation function secret */ + size_t kdfsize; /** Ephemeral master secret */ uint8_t ephemeral[SHA256_DIGEST_SIZE]; }; @@ -597,6 +599,8 @@ struct tls_connection { /** RX I/O buffer alignment */ #define TLS_RX_ALIGN 16 +extern struct exchange_algorithm tls_classic_pre_master_algorithm; + extern struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm; extern struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm; extern struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm; diff --git a/src/net/tls.c b/src/net/tls.c index f50d22591..0e88a8e11 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -550,6 +550,7 @@ static void tls_clear_digest ( struct tls_connection *tls ) { key->dynamic = NULL; key->handshake = NULL; key->kdf = NULL; + key->kdfsize = 0; /* Key schedule no longer contains any shared secret */ tls_clear_binding ( tls ); @@ -589,6 +590,7 @@ static int tls_set_digest ( struct tls_connection *tls, key->handshake = dynamic; dynamic += digest->ctxsize; key->kdf = dynamic; dynamic += kdfsize; assert ( ( key->dynamic + total ) == dynamic ); + key->kdfsize = kdfsize; /* Store digest algorithm */ key->digest = digest; @@ -802,8 +804,7 @@ static void tls_set_kdf_master ( struct tls_connection *tls, */ static int tls_share_ephemeral ( struct tls_connection *tls, void *public ) { struct tls_key_schedule *key = &tls->key; - struct tls_named_group *group = key->group; - struct exchange_algorithm *exchange = group->exchange; + struct exchange_algorithm *exchange = key->exchange; size_t privsize = exchange->privsize; struct { uint8_t private[privsize]; @@ -826,7 +827,7 @@ static int tls_share_ephemeral ( struct tls_connection *tls, void *public ) { } /** - * Agree ephemeral public key (i.e. pre-master secret) + * Agree ephemeral shared secret (i.e. pre-master secret) * * @v tls TLS connection * @v partner Partner public key @@ -838,8 +839,7 @@ static int tls_agree_ephemeral ( struct tls_connection *tls, const void *partner, size_t partner_len, int strip ) { struct tls_key_schedule *key = &tls->key; - struct tls_named_group *group = key->group; - struct exchange_algorithm *exchange = group->exchange; + struct exchange_algorithm *exchange = key->exchange; size_t privsize = exchange->privsize; size_t pubsize = exchange->pubsize; size_t sharedsize = exchange->sharedsize; @@ -1575,33 +1575,22 @@ static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { struct tls_key_schedule *key = &tls->key; struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey; struct x509_certificate *cert; - struct { - uint16_t version; - uint8_t random[46]; - } __attribute__ (( packed )) pre_master_secret; - struct asn1_cursor cursor = { - .data = &pre_master_secret, - .len = sizeof ( pre_master_secret ), - }; + struct asn1_cursor cursor; struct asn1_builder builder = { NULL, 0 }; int rc; + /* Select classic key transport algorithm */ + tls->key.exchange = &tls_classic_pre_master_algorithm; + assert ( is_key_transport ( tls->key.exchange ) ); + /* Generate pre-master secret */ - pre_master_secret.version = htons ( TLS_VERSION_MAX ); - tls_ephemeral_label ( tls, "classic pre-master", - &pre_master_secret.random, - sizeof ( pre_master_secret.random ) ); - tls_set_kdf_master ( tls, &pre_master_secret, - sizeof ( pre_master_secret ) ); + if ( ( rc = tls_agree_ephemeral ( tls, NULL, 0, 0 ) ) != 0 ) + goto err_agree; - /* Key derivation function secret has been overwritten with a - * value that was not derived from its previous value, and so - * is no longer bound to the server's identity. - */ - tls_clear_binding ( tls ); - - /* Key schedule now contains shared secret key material */ - key->keyed = 1; + /* Pre-master secret will be the current KDF secret */ + cursor.data = key->kdf; + cursor.len = tls->key.exchange->sharedsize; + assert ( cursor.len <= key->kdfsize ); /* Identify server certificate */ cert = x509_first ( tls->server.chain ); @@ -1658,6 +1647,7 @@ static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) { err_encrypt: zfree ( builder.data ); err_cert: + err_agree: return rc; } @@ -1679,7 +1669,6 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = { static int tls_new_server_key_exchange_dhe ( struct tls_connection *tls, const void *data, size_t len ) { struct tls_named_group *group; - struct exchange_algorithm *exchange; const struct { uint16_t len; uint8_t data[0]; @@ -1725,9 +1714,9 @@ static int tls_new_server_key_exchange_dhe ( struct tls_connection *tls, DBGC_HDA ( tls, 0, data, len ); return -ENOTSUP_GROUP; } - tls->key.group = group; - exchange = group->exchange; - DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name ); + tls->key.exchange = group->exchange; + DBGC ( tls, "TLS %p using named group %s\n", + tls, tls->key.exchange->name ); /* Generate pre-master secret */ if ( ( rc = tls_agree_ephemeral ( tls, dh_ys->data, @@ -1750,8 +1739,7 @@ static int tls_new_server_key_exchange_dhe ( struct tls_connection *tls, */ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) { struct tls_key_schedule *key = &tls->key; - struct tls_named_group *group = key->group; - struct exchange_algorithm *exchange = group->exchange; + struct exchange_algorithm *exchange = key->exchange; size_t pubsize = exchange->pubsize; struct { uint32_t type_length; @@ -1807,7 +1795,6 @@ struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = { static int tls_new_server_key_exchange_ecdhe ( struct tls_connection *tls, const void *data, size_t len ) { struct tls_named_group *group; - struct exchange_algorithm *exchange; const struct { uint8_t curve_type; uint16_t named_group; @@ -1841,9 +1828,9 @@ static int tls_new_server_key_exchange_ecdhe ( struct tls_connection *tls, DBGC_HDA ( tls, 0, data, len ); return -ENOTSUP_GROUP; } - tls->key.group = group; - exchange = group->exchange; - DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name ); + tls->key.exchange = group->exchange; + DBGC ( tls, "TLS %p using named group %s\n", + tls, tls->key.exchange->name ); /* Generate pre-master secret */ if ( ( rc = tls_agree_ephemeral ( tls, ecdh->public, @@ -1866,8 +1853,7 @@ static int tls_new_server_key_exchange_ecdhe ( struct tls_connection *tls, */ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) { struct tls_key_schedule *key = &tls->key; - struct tls_named_group *group = key->group; - struct exchange_algorithm *exchange = group->exchange; + struct exchange_algorithm *exchange = key->exchange; size_t pubsize = exchange->pubsize; struct { uint32_t type_length;