[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2026-08-11 11:09:33 +01:00
parent c10c815181
commit e6d0a97c05
4 changed files with 145 additions and 42 deletions
+108
View File
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2026 Michael Brown <mbrown@fensystems.co.uk>.
*
* 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 <string.h>
#include <byteswap.h>
#include <ipxe/tls.h>
#include <ipxe/crypto.h>
#include <config/crypto.h>
/** 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,
};
+5
View File
@@ -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 );
+7 -3
View File
@@ -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;
+25 -39
View File
@@ -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;