mirror of
https://github.com/ipxe/ipxe
synced 2026-10-07 15:05:42 +03:00
[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:
@@ -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,
|
||||
};
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user