[tls] Add support for Ephemeral Elliptic Curve Diffie-Hellman key exchange

Add support for the Ephemeral Elliptic Curve Diffie-Hellman (ECDHE)
key exchange algorithm.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
x25519
Michael Brown 2024-01-30 15:09:49 +00:00
parent 8e2469c861
commit b234226dbc
2 changed files with 193 additions and 0 deletions

View File

@ -119,6 +119,10 @@ struct tls_header {
#define TLS_MAX_FRAGMENT_LENGTH_2048 3
#define TLS_MAX_FRAGMENT_LENGTH_4096 4
/* TLS named curve extension */
#define TLS_NAMED_CURVE 10
#define TLS_NAMED_CURVE_X25519 29
/* TLS signature algorithms extension */
#define TLS_SIGNATURE_ALGORITHMS 13
@ -205,6 +209,25 @@ struct tls_cipher_suite {
#define __tls_cipher_suite( pref ) \
__table_entry ( TLS_CIPHER_SUITES, pref )
/** TLS named curved type */
#define TLS_NAMED_CURVE_TYPE 3
/** A TLS named curve */
struct tls_named_curve {
/** Elliptic curve */
struct elliptic_curve *curve;
/** Numeric code (in network-endian order) */
uint16_t code;
};
/** TLS named curve table */
#define TLS_NAMED_CURVES \
__table ( struct tls_named_curve, "tls_named_curves" )
/** Declare a TLS named curve */
#define __tls_named_curve( pref ) \
__table_entry ( TLS_NAMED_CURVES, pref )
/** A TLS cipher specification */
struct tls_cipherspec {
/** Cipher suite */
@ -425,6 +448,7 @@ struct tls_connection {
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;
extern int add_tls ( struct interface *xfer, const char *name,
struct x509_root *root, struct private_key *key );

View File

@ -158,6 +158,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_ENOTSUP_VERSION \
__einfo_uniqify ( EINFO_ENOTSUP, 0x04, \
"Unsupported protocol version" )
#define ENOTSUP_CURVE __einfo_error ( EINFO_ENOTSUP_CURVE )
#define EINFO_ENOTSUP_CURVE \
__einfo_uniqify ( EINFO_ENOTSUP, 0x05, \
"Unsupported elliptic curve" )
#define EPERM_ALERT __einfo_error ( EINFO_EPERM_ALERT )
#define EINFO_EPERM_ALERT \
__einfo_uniqify ( EINFO_EPERM, 0x01, \
@ -1042,6 +1046,35 @@ tls_signature_hash_digest ( struct tls_signature_hash_id code ) {
return NULL;
}
/******************************************************************************
*
* Ephemeral Elliptic Curve Diffie-Hellman key exchange
*
******************************************************************************
*/
/** Number of supported named curves */
#define TLS_NUM_NAMED_CURVES table_num_entries ( TLS_NAMED_CURVES )
/**
* Identify named curve
*
* @v named_curve Named curve specification
* @ret curve Named curve, or NULL
*/
static struct tls_named_curve *
tls_find_named_curve ( unsigned int named_curve ) {
struct tls_named_curve *curve;
/* Identify named curve */
for_each_table_entry ( curve, TLS_NAMED_CURVES ) {
if ( curve->code == named_curve )
return curve;
}
return NULL;
}
/******************************************************************************
*
* Record handling
@ -1165,12 +1198,22 @@ static int tls_client_hello ( struct tls_connection *tls,
uint8_t data[session->ticket_len];
} __attribute__ (( packed )) data;
} __attribute__ (( packed )) *session_ticket_ext;
struct {
uint16_t type;
uint16_t len;
struct {
uint16_t len;
uint16_t code[TLS_NUM_NAMED_CURVES];
} __attribute__ (( packed )) data;
} __attribute__ (( packed )) *named_curve_ext;
struct {
typeof ( *server_name_ext ) server_name;
typeof ( *max_fragment_length_ext ) max_fragment_length;
typeof ( *signature_algorithms_ext ) signature_algorithms;
typeof ( *renegotiation_info_ext ) renegotiation_info;
typeof ( *session_ticket_ext ) session_ticket;
typeof ( *named_curve_ext )
named_curve[TLS_NUM_NAMED_CURVES ? 1 : 0];
} __attribute__ (( packed )) *extensions;
struct {
uint32_t type_length;
@ -1187,6 +1230,7 @@ static int tls_client_hello ( struct tls_connection *tls,
} __attribute__ (( packed )) hello;
struct tls_cipher_suite *suite;
struct tls_signature_hash_algorithm *sighash;
struct tls_named_curve *curve;
unsigned int i;
/* Construct record */
@ -1253,6 +1297,18 @@ static int tls_client_hello ( struct tls_connection *tls,
memcpy ( session_ticket_ext->data.data, session->ticket,
sizeof ( session_ticket_ext->data.data ) );
/* Construct named curves extension, if applicable */
if ( sizeof ( extensions->named_curve ) ) {
named_curve_ext = &extensions->named_curve[0];
named_curve_ext->type = htons ( TLS_NAMED_CURVE );
named_curve_ext->len
= htons ( sizeof ( named_curve_ext->data ) );
named_curve_ext->data.len
= htons ( sizeof ( named_curve_ext->data.code ) );
i = 0 ; for_each_table_entry ( curve, TLS_NAMED_CURVES )
named_curve_ext->data.code[i++] = curve->code;
}
return action ( tls, &hello, sizeof ( hello ) );
}
@ -1607,6 +1663,119 @@ struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = {
.exchange = tls_send_client_key_exchange_dhe,
};
/**
* Transmit Client Key Exchange record using ECDHE key exchange
*
* @v tls TLS connection
* @ret rc Return status code
*/
static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
struct tls_named_curve *curve;
const struct {
uint8_t curve_type;
uint16_t named_curve;
uint8_t public_len;
uint8_t public[0];
} __attribute__ (( packed )) *ecdh;
size_t param_len;
int rc;
/* Parse ServerKeyExchange record */
ecdh = tls->server_key;
if ( ( sizeof ( *ecdh ) > tls->server_key_len ) ||
( ecdh->public_len > ( tls->server_key_len - sizeof ( *ecdh ) ))){
DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
tls );
DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
return -EINVAL_KEY_EXCHANGE;
}
param_len = ( sizeof ( *ecdh ) + ecdh->public_len );
/* Verify parameter signature */
if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 )
return rc;
/* Identify named curve */
if ( ecdh->curve_type != TLS_NAMED_CURVE_TYPE ) {
DBGC ( tls, "TLS %p unsupported curve type %d\n",
tls, ecdh->curve_type );
DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
return -ENOTSUP_CURVE;
}
curve = tls_find_named_curve ( ecdh->named_curve );
if ( ! curve ) {
DBGC ( tls, "TLS %p unsupported named curve %d\n",
tls, ntohs ( ecdh->named_curve ) );
DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
return -ENOTSUP_CURVE;
}
/* Check key length */
if ( ecdh->public_len != curve->curve->keysize ) {
DBGC ( tls, "TLS %p invalid %s key\n",
tls, curve->curve->name );
DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
return -EINVAL_KEY_EXCHANGE;
}
/* Construct pre-master secret and ClientKeyExchange record */
{
size_t len = curve->curve->keysize;
uint8_t private[len];
uint8_t pre_master_secret[len];
struct {
uint32_t type_length;
uint8_t public_len;
uint8_t public[len];
} __attribute__ (( packed )) key_xchg;
/* Generate ephemeral private key */
if ( ( rc = tls_generate_random ( tls, private,
sizeof ( private ) ) ) != 0){
return rc;
}
/* Calculate pre-master secret */
if ( ( rc = elliptic_multiply ( curve->curve,
ecdh->public, private,
pre_master_secret ) ) != 0 ) {
DBGC ( tls, "TLS %p could not exchange ECDHE key: %s\n",
tls, strerror ( rc ) );
return rc;
}
/* Generate master secret */
tls_generate_master_secret ( tls, pre_master_secret, len );
/* Generate Client Key Exchange record */
key_xchg.type_length =
( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
htonl ( sizeof ( key_xchg ) -
sizeof ( key_xchg.type_length ) ) );
key_xchg.public_len = len;
if ( ( rc = elliptic_multiply ( curve->curve, NULL, private,
key_xchg.public ) ) != 0 ) {
DBGC ( tls, "TLS %p could not generate ECDHE key: %s\n",
tls, strerror ( rc ) );
return rc;
}
/* Transmit Client Key Exchange record */
if ( ( rc = tls_send_handshake ( tls, &key_xchg,
sizeof ( key_xchg ) ) ) !=0){
return rc;
}
}
return 0;
}
/** Ephemeral Elliptic Curve Diffie-Hellman key exchange algorithm */
struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = {
.name = "ecdhe",
.exchange = tls_send_client_key_exchange_ecdhe,
};
/**
* Transmit Client Key Exchange record
*