summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2024-01-30 16:09:49 +0100
committerMichael Brown2024-01-30 17:16:31 +0100
commitb234226dbc4f348c7e4a5c61bdf7b0f8f0aef16c (patch)
tree8ce4a102d739c49850362f314ec0539fe9bde392
parent[tls] Split out Diffie-Hellman parameter signature verification (diff)
downloadipxe-b234226dbc4f348c7e4a5c61bdf7b0f8f0aef16c.tar.gz
ipxe-b234226dbc4f348c7e4a5c61bdf7b0f8f0aef16c.tar.xz
ipxe-b234226dbc4f348c7e4a5c61bdf7b0f8f0aef16c.zip
[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>
-rw-r--r--src/include/ipxe/tls.h24
-rw-r--r--src/net/tls.c169
2 files changed, 193 insertions, 0 deletions
diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h
index 30bb1c48..5c218f84 100644
--- a/src/include/ipxe/tls.h
+++ b/src/include/ipxe/tls.h
@@ -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 );
diff --git a/src/net/tls.c b/src/net/tls.c
index 66ab1e9c..58fad65e 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -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, \
@@ -1044,6 +1048,35 @@ tls_signature_hash_digest ( struct tls_signature_hash_id code ) {
/******************************************************************************
*
+ * 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
*
******************************************************************************
@@ -1166,11 +1199,21 @@ static int tls_client_hello ( struct tls_connection *tls,
} __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 ) );
}
@@ -1608,6 +1664,119 @@ struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = {
};
/**
+ * 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
*
* @v tls TLS connection