summaryrefslogtreecommitdiffstats
path: root/src/net
diff options
context:
space:
mode:
authorSimon Rettberg2022-10-21 17:22:09 +0200
committerSimon Rettberg2022-10-21 17:22:09 +0200
commit80109b804a700384799d54134ceebf4f40a8b7a3 (patch)
treebbdf311ed02dd2c5c1f0a8d1997519ed2c24d93e /src/net
parentMerge branch 'master' into openslx (diff)
parent[tls] Add support for Ephemeral Diffie-Hellman key exchange (diff)
downloadipxe-80109b804a700384799d54134ceebf4f40a8b7a3.tar.gz
ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.tar.xz
ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src/net')
-rw-r--r--src/net/80211/wpa_ccmp.c11
-rw-r--r--src/net/80211/wpa_tkip.c9
-rw-r--r--src/net/ipv6.c2
-rw-r--r--src/net/ndp.c36
-rw-r--r--src/net/pccrc.c9
-rw-r--r--src/net/tls.c420
6 files changed, 403 insertions, 84 deletions
diff --git a/src/net/80211/wpa_ccmp.c b/src/net/80211/wpa_ccmp.c
index a073c6a3c..0abd217e7 100644
--- a/src/net/80211/wpa_ccmp.c
+++ b/src/net/80211/wpa_ccmp.c
@@ -478,16 +478,15 @@ struct net80211_crypto ccmp_crypto __net80211_crypto = {
static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len,
void *mic )
{
- u8 sha1_ctx[SHA1_CTX_SIZE];
+ u8 ctx[SHA1_CTX_SIZE + SHA1_BLOCK_SIZE];
u8 kckb[16];
u8 hash[SHA1_DIGEST_SIZE];
- size_t kck_len = 16;
- memcpy ( kckb, kck, kck_len );
+ memcpy ( kckb, kck, sizeof ( kckb ) );
- hmac_init ( &sha1_algorithm, sha1_ctx, kckb, &kck_len );
- hmac_update ( &sha1_algorithm, sha1_ctx, msg, len );
- hmac_final ( &sha1_algorithm, sha1_ctx, kckb, &kck_len, hash );
+ hmac_init ( &sha1_algorithm, ctx, kckb, sizeof ( kckb ) );
+ hmac_update ( &sha1_algorithm, ctx, msg, len );
+ hmac_final ( &sha1_algorithm, ctx, hash );
memcpy ( mic, hash, 16 );
}
diff --git a/src/net/80211/wpa_tkip.c b/src/net/80211/wpa_tkip.c
index 3b1934b59..3bd651512 100644
--- a/src/net/80211/wpa_tkip.c
+++ b/src/net/80211/wpa_tkip.c
@@ -545,15 +545,14 @@ struct net80211_crypto tkip_crypto __net80211_crypto = {
static void tkip_kie_mic ( const void *kck, const void *msg, size_t len,
void *mic )
{
- uint8_t ctx[MD5_CTX_SIZE];
+ uint8_t ctx[MD5_CTX_SIZE + MD5_BLOCK_SIZE];
u8 kckb[16];
- size_t kck_len = 16;
- memcpy ( kckb, kck, kck_len );
+ memcpy ( kckb, kck, sizeof ( kckb ) );
- hmac_init ( &md5_algorithm, ctx, kckb, &kck_len );
+ hmac_init ( &md5_algorithm, ctx, kckb, sizeof ( kckb ) );
hmac_update ( &md5_algorithm, ctx, msg, len );
- hmac_final ( &md5_algorithm, ctx, kckb, &kck_len, mic );
+ hmac_final ( &md5_algorithm, ctx, mic );
}
/**
diff --git a/src/net/ipv6.c b/src/net/ipv6.c
index 4b2c33eb4..901203c40 100644
--- a/src/net/ipv6.c
+++ b/src/net/ipv6.c
@@ -251,7 +251,7 @@ int ipv6_add_miniroute ( struct net_device *netdev, struct in6_addr *address,
*prefix_mask = 0xff;
}
if ( remaining )
- *prefix_mask <<= ( 8 - remaining );
+ *prefix_mask = ( 0xff << ( 8 - remaining ) );
}
/* Add to start of routing table */
diff --git a/src/net/ndp.c b/src/net/ndp.c
index 75e531648..c8e8ebad3 100644
--- a/src/net/ndp.c
+++ b/src/net/ndp.c
@@ -789,29 +789,43 @@ static int ndp_prefix_fetch_ip6 ( struct settings *settings, void *data,
container_of ( ndpset->settings.parent, struct net_device,
settings.settings );
struct ndp_prefix_information_option *prefix = prefset->prefix;
- struct in6_addr ip6;
+ struct in6_addr *ip6 = &prefix->prefix;
+ struct in6_addr slaac;
int prefix_len;
+ int rc;
/* Skip dead prefixes */
if ( ! prefix->valid )
return -ENOENT;
/* Construct IPv6 address via SLAAC, if applicable */
- memcpy ( &ip6, &prefix->prefix, sizeof ( ip6 ) );
if ( prefix->flags & NDP_PREFIX_AUTONOMOUS ) {
- prefix_len = ipv6_eui64 ( &ip6, netdev );
- if ( prefix_len < 0 )
- return prefix_len;
- if ( prefix_len != prefix->prefix_len )
- return -EINVAL;
+ memcpy ( &slaac, ip6, sizeof ( slaac ) );
+ prefix_len = ipv6_eui64 ( &slaac, netdev );
+ if ( prefix_len == prefix->prefix_len ) {
+ /* Correctly configured prefix: use SLAAC address */
+ ip6 = &slaac;
+ } else if ( prefix_len < 0 ) {
+ /* Link layer does not support SLAAC */
+ rc = prefix_len;
+ DBGC ( netdev, "NDP %s does not support SLAAC: %s\n",
+ netdev->name, strerror ( rc ) );
+ } else {
+ /* Prefix length incorrect: assume a badly
+ * configured router and ignore SLAAC address.
+ */
+ DBGC ( netdev, "NDP %s ignoring misconfigured SLAAC "
+ "on prefix %s/%d\n", netdev->name,
+ inet6_ntoa ( ip6 ), prefix->prefix_len );
+ }
}
/* Fill in IPv6 address */
- if ( len > sizeof ( ip6 ) )
- len = sizeof ( ip6 );
- memcpy ( data, &ip6, len );
+ if ( len > sizeof ( *ip6 ) )
+ len = sizeof ( *ip6 );
+ memcpy ( data, ip6, len );
- return sizeof ( ip6 );
+ return sizeof ( *ip6 );
}
/**
diff --git a/src/net/pccrc.c b/src/net/pccrc.c
index 4cd82cd1c..a94bc0e11 100644
--- a/src/net/pccrc.c
+++ b/src/net/pccrc.c
@@ -104,9 +104,8 @@ static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment,
const void *hash, const void *secret ){
const struct peerdist_info *info = segment->info;
struct digest_algorithm *digest = info->digest;
- uint8_t ctx[digest->ctxsize];
+ uint8_t ctx[ hmac_ctxsize ( digest ) ];
size_t digestsize = info->digestsize;
- size_t secretsize = digestsize;
static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC;
/* Sanity check */
@@ -121,12 +120,10 @@ static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment,
memcpy ( segment->secret, secret, digestsize );
/* Calculate segment identifier */
- hmac_init ( digest, ctx, segment->secret, &secretsize );
- assert ( secretsize == digestsize );
+ hmac_init ( digest, ctx, segment->secret, digestsize );
hmac_update ( digest, ctx, segment->hash, digestsize );
hmac_update ( digest, ctx, magic, sizeof ( magic ) );
- hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id );
- assert ( secretsize == digestsize );
+ hmac_final ( digest, ctx, segment->id );
}
/******************************************************************************
diff --git a/src/net/tls.c b/src/net/tls.c
index 3c4144450..4aa4d9e29 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -49,6 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/rbg.h>
#include <ipxe/validator.h>
#include <ipxe/job.h>
+#include <ipxe/dhe.h>
#include <ipxe/tls.h>
#include <config/crypto.h>
@@ -109,6 +110,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EINVAL_TICKET \
__einfo_uniqify ( EINFO_EINVAL, 0x0e, \
"Invalid New Session Ticket record")
+#define EINVAL_KEY_EXCHANGE __einfo_error ( EINFO_EINVAL_KEY_EXCHANGE )
+#define EINFO_EINVAL_KEY_EXCHANGE \
+ __einfo_uniqify ( EINFO_EINVAL, 0x0f, \
+ "Invalid Server Key Exchange record" )
#define EIO_ALERT __einfo_error ( EINFO_EIO_ALERT )
#define EINFO_EIO_ALERT \
__einfo_uniqify ( EINFO_EIO, 0x01, \
@@ -177,6 +182,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EPERM_RENEG_VERIFY \
__einfo_uniqify ( EINFO_EPERM, 0x05, \
"Secure renegotiation verification failed" )
+#define EPERM_KEY_EXCHANGE __einfo_error ( EINFO_EPERM_KEY_EXCHANGE )
+#define EINFO_EPERM_KEY_EXCHANGE \
+ __einfo_uniqify ( EINFO_EPERM, 0x06, \
+ "ServerKeyExchange verification failed" )
#define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION )
#define EINFO_EPROTO_VERSION \
__einfo_uniqify ( EINFO_EPROTO, 0x01, \
@@ -377,6 +386,7 @@ static void free_tls ( struct refcnt *refcnt ) {
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
tls_clear_cipher ( tls, &tls->rx_cipherspec );
tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
+ free ( tls->server_key );
list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
@@ -458,17 +468,17 @@ static int tls_generate_random ( struct tls_connection *tls,
* Update HMAC with a list of ( data, len ) pairs
*
* @v digest Hash function to use
- * @v digest_ctx Digest context
+ * @v ctx HMAC context
* @v args ( data, len ) pairs of data, terminated by NULL
*/
static void tls_hmac_update_va ( struct digest_algorithm *digest,
- void *digest_ctx, va_list args ) {
+ void *ctx, va_list args ) {
void *data;
size_t len;
while ( ( data = va_arg ( args, void * ) ) ) {
len = va_arg ( args, size_t );
- hmac_update ( digest, digest_ctx, data, len );
+ hmac_update ( digest, ctx, data, len );
}
}
@@ -485,43 +495,38 @@ static void tls_hmac_update_va ( struct digest_algorithm *digest,
*/
static void tls_p_hash_va ( struct tls_connection *tls,
struct digest_algorithm *digest,
- void *secret, size_t secret_len,
+ const void *secret, size_t secret_len,
void *out, size_t out_len,
va_list seeds ) {
- uint8_t secret_copy[secret_len];
- uint8_t digest_ctx[digest->ctxsize];
- uint8_t digest_ctx_partial[digest->ctxsize];
+ uint8_t ctx[ hmac_ctxsize ( digest ) ];
+ uint8_t ctx_partial[ sizeof ( ctx ) ];
uint8_t a[digest->digestsize];
uint8_t out_tmp[digest->digestsize];
size_t frag_len = digest->digestsize;
va_list tmp;
- /* Copy the secret, in case HMAC modifies it */
- memcpy ( secret_copy, secret, secret_len );
- secret = secret_copy;
DBGC2 ( tls, "TLS %p %s secret:\n", tls, digest->name );
DBGC2_HD ( tls, secret, secret_len );
/* Calculate A(1) */
- hmac_init ( digest, digest_ctx, secret, &secret_len );
+ hmac_init ( digest, ctx, secret, secret_len );
va_copy ( tmp, seeds );
- tls_hmac_update_va ( digest, digest_ctx, tmp );
+ tls_hmac_update_va ( digest, ctx, tmp );
va_end ( tmp );
- hmac_final ( digest, digest_ctx, secret, &secret_len, a );
+ hmac_final ( digest, ctx, a );
DBGC2 ( tls, "TLS %p %s A(1):\n", tls, digest->name );
DBGC2_HD ( tls, &a, sizeof ( a ) );
/* Generate as much data as required */
while ( out_len ) {
/* Calculate output portion */
- hmac_init ( digest, digest_ctx, secret, &secret_len );
- hmac_update ( digest, digest_ctx, a, sizeof ( a ) );
- memcpy ( digest_ctx_partial, digest_ctx, digest->ctxsize );
+ hmac_init ( digest, ctx, secret, secret_len );
+ hmac_update ( digest, ctx, a, sizeof ( a ) );
+ memcpy ( ctx_partial, ctx, sizeof ( ctx_partial ) );
va_copy ( tmp, seeds );
- tls_hmac_update_va ( digest, digest_ctx, tmp );
+ tls_hmac_update_va ( digest, ctx, tmp );
va_end ( tmp );
- hmac_final ( digest, digest_ctx,
- secret, &secret_len, out_tmp );
+ hmac_final ( digest, ctx, out_tmp );
/* Copy output */
if ( frag_len > out_len )
@@ -531,8 +536,7 @@ static void tls_p_hash_va ( struct tls_connection *tls,
DBGC2_HD ( tls, out, frag_len );
/* Calculate A(i) */
- hmac_final ( digest, digest_ctx_partial,
- secret, &secret_len, a );
+ hmac_final ( digest, ctx_partial, a );
DBGC2 ( tls, "TLS %p %s A(n):\n", tls, digest->name );
DBGC2_HD ( tls, &a, sizeof ( a ) );
@@ -551,13 +555,13 @@ static void tls_p_hash_va ( struct tls_connection *tls,
* @v out_len Length of output buffer
* @v ... ( data, len ) pairs of seed data, terminated by NULL
*/
-static void tls_prf ( struct tls_connection *tls, void *secret,
+static void tls_prf ( struct tls_connection *tls, const void *secret,
size_t secret_len, void *out, size_t out_len, ... ) {
va_list seeds;
va_list tmp;
size_t subsecret_len;
- void *md5_secret;
- void *sha1_secret;
+ const void *md5_secret;
+ const void *sha1_secret;
uint8_t buf[out_len];
unsigned int i;
@@ -622,21 +626,23 @@ static void tls_prf ( struct tls_connection *tls, void *secret,
* Generate master secret
*
* @v tls TLS connection
+ * @v pre_master_secret Pre-master secret
+ * @v pre_master_secret_len Length of pre-master secret
*
- * The pre-master secret and the client and server random values must
- * already be known.
+ * The client and server random values must already be known.
*/
-static void tls_generate_master_secret ( struct tls_connection *tls ) {
+static void tls_generate_master_secret ( struct tls_connection *tls,
+ const void *pre_master_secret,
+ size_t pre_master_secret_len ) {
+
DBGC ( tls, "TLS %p pre-master-secret:\n", tls );
- DBGC_HD ( tls, &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ) );
+ DBGC_HD ( tls, pre_master_secret, pre_master_secret_len );
DBGC ( tls, "TLS %p client random bytes:\n", tls );
DBGC_HD ( tls, &tls->client_random, sizeof ( tls->client_random ) );
DBGC ( tls, "TLS %p server random bytes:\n", tls );
DBGC_HD ( tls, &tls->server_random, sizeof ( tls->server_random ) );
- tls_prf_label ( tls, &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ),
+ tls_prf_label ( tls, pre_master_secret, pre_master_secret_len,
&tls->master_secret, sizeof ( tls->master_secret ),
"master secret",
&tls->client_random, sizeof ( tls->client_random ),
@@ -737,6 +743,7 @@ static int tls_generate_keys ( struct tls_connection *tls ) {
/** Null cipher suite */
struct tls_cipher_suite tls_cipher_suite_null = {
+ .exchange = &tls_pubkey_exchange_algorithm,
.pubkey = &pubkey_null,
.cipher = &cipher_null,
.digest = &digest_null,
@@ -852,7 +859,8 @@ static int tls_select_cipher ( struct tls_connection *tls,
suite ) ) != 0 )
return rc;
- DBGC ( tls, "TLS %p selected %s-%s-%d-%s\n", tls, suite->pubkey->name,
+ DBGC ( tls, "TLS %p selected %s-%s-%s-%d-%s\n", tls,
+ suite->exchange->name, suite->pubkey->name,
suite->cipher->name, ( suite->key_len * 8 ),
suite->digest->name );
@@ -916,6 +924,44 @@ tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey,
return NULL;
}
+/**
+ * Find TLS signature algorithm
+ *
+ * @v code Signature and hash algorithm identifier
+ * @ret pubkey Public key algorithm, or NULL
+ */
+static struct pubkey_algorithm *
+tls_signature_hash_pubkey ( struct tls_signature_hash_id code ) {
+ struct tls_signature_hash_algorithm *sig_hash;
+
+ /* Identify signature and hash algorithm */
+ for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) {
+ if ( sig_hash->code.signature == code.signature )
+ return sig_hash->pubkey;
+ }
+
+ return NULL;
+}
+
+/**
+ * Find TLS hash algorithm
+ *
+ * @v code Signature and hash algorithm identifier
+ * @ret digest Digest algorithm, or NULL
+ */
+static struct digest_algorithm *
+tls_signature_hash_digest ( struct tls_signature_hash_id code ) {
+ struct tls_signature_hash_algorithm *sig_hash;
+
+ /* Identify signature and hash algorithm */
+ for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) {
+ if ( sig_hash->code.hash == code.hash )
+ return sig_hash->digest;
+ }
+
+ return NULL;
+}
+
/******************************************************************************
*
* Handshake verification
@@ -1208,16 +1254,20 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
}
/**
- * Transmit Client Key Exchange record
+ * Transmit Client Key Exchange record using public key exchange
*
* @v tls TLS connection
* @ret rc Return status code
*/
-static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
+static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) {
struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
size_t max_len = pubkey_max_len ( pubkey, cipherspec->pubkey_ctx );
struct {
+ uint16_t version;
+ uint8_t random[46];
+ } __attribute__ (( packed )) pre_master_secret;
+ struct {
uint32_t type_length;
uint16_t encrypted_pre_master_secret_len;
uint8_t encrypted_pre_master_secret[max_len];
@@ -1226,11 +1276,28 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
int len;
int rc;
+ /* Generate pre-master secret */
+ pre_master_secret.version = htons ( TLS_VERSION_MAX );
+ if ( ( rc = tls_generate_random ( tls, &pre_master_secret.random,
+ ( sizeof ( pre_master_secret.random ) ) ) ) != 0 ) {
+ return rc;
+ }
+
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, &pre_master_secret,
+ sizeof ( pre_master_secret ) );
+
+ /* Generate keys */
+ if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not generate keys: %s\n",
+ tls, strerror ( rc ) );
+ return rc;
+ }
+
/* Encrypt pre-master secret using server's public key */
memset ( &key_xchg, 0, sizeof ( key_xchg ) );
len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx,
- &tls->pre_master_secret,
- sizeof ( tls->pre_master_secret ),
+ &pre_master_secret, sizeof ( pre_master_secret ),
key_xchg.encrypted_pre_master_secret );
if ( len < 0 ) {
rc = len;
@@ -1251,6 +1318,225 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
( sizeof ( key_xchg ) - unused ) );
}
+/** Public key exchange algorithm */
+struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = {
+ .name = "pubkey",
+ .exchange = tls_send_client_key_exchange_pubkey,
+};
+
+/**
+ * Transmit Client Key Exchange record using DHE key exchange
+ *
+ * @v tls TLS connection
+ * @ret rc Return status code
+ */
+static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
+ struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ struct pubkey_algorithm *pubkey;
+ struct digest_algorithm *digest;
+ int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 );
+ uint8_t private[ sizeof ( tls->client_random.random ) ];
+ const struct {
+ uint16_t len;
+ uint8_t data[0];
+ } __attribute__ (( packed )) *dh_val[3];
+ const struct {
+ struct tls_signature_hash_id sig_hash[use_sig_hash];
+ uint16_t signature_len;
+ uint8_t signature[0];
+ } __attribute__ (( packed )) *sig;
+ const void *data;
+ size_t remaining;
+ size_t frag_len;
+ unsigned int i;
+ int rc;
+
+ /* Parse ServerKeyExchange */
+ data = tls->server_key;
+ remaining = tls->server_key_len;
+ for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){
+ dh_val[i] = data;
+ if ( ( sizeof ( *dh_val[i] ) > remaining ) ||
+ ( ntohs ( dh_val[i]->len ) > ( remaining -
+ sizeof ( *dh_val[i] ) ) )){
+ DBGC ( tls, "TLS %p received underlength "
+ "ServerKeyExchange\n", tls );
+ DBGC_HDA ( tls, 0, tls->server_key,
+ tls->server_key_len );
+ rc = -EINVAL_KEY_EXCHANGE;
+ goto err_header;
+ }
+ frag_len = ( sizeof ( *dh_val[i] ) + ntohs ( dh_val[i]->len ));
+ data += frag_len;
+ remaining -= frag_len;
+ }
+ sig = data;
+ if ( ( sizeof ( *sig ) > remaining ) ||
+ ( ntohs ( sig->signature_len ) > ( remaining -
+ sizeof ( *sig ) ) ) ) {
+ DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
+ tls );
+ DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ rc = -EINVAL_KEY_EXCHANGE;
+ goto err_header;
+ }
+
+ /* Identify signature and hash algorithm */
+ if ( use_sig_hash ) {
+ pubkey = tls_signature_hash_pubkey ( sig->sig_hash[0] );
+ digest = tls_signature_hash_digest ( sig->sig_hash[0] );
+ if ( ( ! pubkey ) || ( ! digest ) ) {
+ DBGC ( tls, "TLS %p ServerKeyExchange unsupported "
+ "signature and hash algorithm\n", tls );
+ rc = -ENOTSUP_SIG_HASH;
+ goto err_sig_hash;
+ }
+ if ( pubkey != cipherspec->suite->pubkey ) {
+ DBGC ( tls, "TLS %p ServerKeyExchange incorrect "
+ "signature algorithm %s (expected %s)\n", tls,
+ pubkey->name, cipherspec->suite->pubkey->name );
+ rc = -EPERM_KEY_EXCHANGE;
+ goto err_sig_hash;
+ }
+ } else {
+ pubkey = cipherspec->suite->pubkey;
+ digest = &md5_sha1_algorithm;
+ }
+
+ /* Verify signature */
+ {
+ const void *signature = sig->signature;
+ size_t signature_len = ntohs ( sig->signature_len );
+ uint8_t ctx[digest->ctxsize];
+ uint8_t hash[digest->digestsize];
+
+ /* Calculate digest */
+ digest_init ( digest, ctx );
+ digest_update ( digest, ctx, &tls->client_random,
+ sizeof ( tls->client_random ) );
+ digest_update ( digest, ctx, tls->server_random,
+ sizeof ( tls->server_random ) );
+ digest_update ( digest, ctx, tls->server_key,
+ ( tls->server_key_len - remaining ) );
+ digest_final ( digest, ctx, hash );
+
+ /* Verify signature */
+ if ( ( rc = pubkey_verify ( pubkey, cipherspec->pubkey_ctx,
+ digest, hash, signature,
+ signature_len ) ) != 0 ) {
+ DBGC ( tls, "TLS %p ServerKeyExchange failed "
+ "verification\n", tls );
+ DBGC_HDA ( tls, 0, tls->server_key,
+ tls->server_key_len );
+ rc = -EPERM_KEY_EXCHANGE;
+ goto err_verify;
+ }
+ }
+
+ /* Generate Diffie-Hellman private key */
+ if ( ( rc = tls_generate_random ( tls, private,
+ sizeof ( private ) ) ) != 0 ) {
+ goto err_random;
+ }
+
+ /* Construct pre-master secret and ClientKeyExchange record */
+ {
+ typeof ( dh_val[0] ) dh_p = dh_val[0];
+ typeof ( dh_val[1] ) dh_g = dh_val[1];
+ typeof ( dh_val[2] ) dh_ys = dh_val[2];
+ size_t len = ntohs ( dh_p->len );
+ struct {
+ uint32_t type_length;
+ uint16_t dh_xs_len;
+ uint8_t dh_xs[len];
+ } __attribute__ (( packed )) *key_xchg;
+ struct {
+ uint8_t pre_master_secret[len];
+ typeof ( *key_xchg ) key_xchg;
+ } *dynamic;
+ uint8_t *pre_master_secret;
+
+ /* Allocate space */
+ dynamic = malloc ( sizeof ( *dynamic ) );
+ if ( ! dynamic ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ pre_master_secret = dynamic->pre_master_secret;
+ key_xchg = &dynamic->key_xchg;
+ key_xchg->type_length =
+ ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
+ htonl ( sizeof ( *key_xchg ) -
+ sizeof ( key_xchg->type_length ) ) );
+ key_xchg->dh_xs_len = htons ( len );
+
+ /* Calculate pre-master secret and client public value */
+ if ( ( rc = dhe_key ( dh_p->data, len,
+ dh_g->data, ntohs ( dh_g->len ),
+ dh_ys->data, ntohs ( dh_ys->len ),
+ private, sizeof ( private ),
+ key_xchg->dh_xs,
+ pre_master_secret ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not calculate DHE key: %s\n",
+ tls, strerror ( rc ) );
+ goto err_dhe_key;
+ }
+
+ /* Strip leading zeroes from pre-master secret */
+ while ( len && ( ! *pre_master_secret ) ) {
+ pre_master_secret++;
+ len--;
+ }
+
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, pre_master_secret, len );
+
+ /* Generate keys */
+ if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not generate keys: %s\n",
+ tls, strerror ( rc ) );
+ goto err_generate_keys;
+ }
+
+ /* Transmit Client Key Exchange record */
+ if ( ( rc = tls_send_handshake ( tls, key_xchg,
+ sizeof ( *key_xchg ) ) ) !=0){
+ goto err_send_handshake;
+ }
+
+ err_send_handshake:
+ err_generate_keys:
+ err_dhe_key:
+ free ( dynamic );
+ }
+ err_alloc:
+ err_random:
+ err_verify:
+ err_sig_hash:
+ err_header:
+ return rc;
+}
+
+/** Ephemeral Diffie-Hellman key exchange algorithm */
+struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = {
+ .name = "dhe",
+ .exchange = tls_send_client_key_exchange_dhe,
+};
+
+/**
+ * Transmit Client Key Exchange record
+ *
+ * @v tls TLS connection
+ * @ret rc Return status code
+ */
+static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
+ struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ struct tls_cipher_suite *suite = cipherspec->suite;
+
+ /* Transmit Client Key Exchange record via key exchange algorithm */
+ return suite->exchange->exchange ( tls );
+}
+
/**
* Transmit Certificate Verify record
*
@@ -1628,7 +1914,7 @@ static int tls_new_server_hello ( struct tls_connection *tls,
if ( ( rc = tls_select_cipher ( tls, hello_b->cipher_suite ) ) != 0 )
return rc;
- /* Reuse or generate master secret */
+ /* Check session ID */
if ( hello_a->session_id_len &&
( hello_a->session_id_len == tls->session_id_len ) &&
( memcmp ( session_id, tls->session_id,
@@ -1637,12 +1923,11 @@ static int tls_new_server_hello ( struct tls_connection *tls,
/* Session ID match: reuse master secret */
DBGC ( tls, "TLS %p resuming session ID:\n", tls );
DBGC_HDA ( tls, 0, tls->session_id, tls->session_id_len );
+ if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
+ return rc;
} else {
- /* Generate new master secret */
- tls_generate_master_secret ( tls );
-
/* Record new session ID, if present */
if ( hello_a->session_id_len &&
( hello_a->session_id_len <= sizeof ( tls->session_id ))){
@@ -1655,10 +1940,6 @@ static int tls_new_server_hello ( struct tls_connection *tls,
}
}
- /* Generate keys */
- if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
- return rc;
-
/* Handle secure renegotiation */
if ( tls->secure_renegotiation ) {
@@ -1856,6 +2137,37 @@ static int tls_new_certificate ( struct tls_connection *tls,
}
/**
+ * Receive new Server Key Exchange handshake record
+ *
+ * @v tls TLS connection
+ * @v data Plaintext handshake record
+ * @v len Length of plaintext handshake record
+ * @ret rc Return status code
+ */
+static int tls_new_server_key_exchange ( struct tls_connection *tls,
+ const void *data, size_t len ) {
+
+ /* Free any existing server key exchange record */
+ free ( tls->server_key );
+ tls->server_key_len = 0;
+
+ /* Allocate copy of server key exchange record */
+ tls->server_key = malloc ( len );
+ if ( ! tls->server_key )
+ return -ENOMEM;
+
+ /* Store copy of server key exchange record for later
+ * processing. We cannot verify the signature at this point
+ * since the certificate validation will not yet have
+ * completed.
+ */
+ memcpy ( tls->server_key, data, len );
+ tls->server_key_len = len;
+
+ return 0;
+}
+
+/**
* Receive new Certificate Request handshake record
*
* @v tls TLS connection
@@ -2088,6 +2400,10 @@ static int tls_new_handshake ( struct tls_connection *tls,
case TLS_CERTIFICATE:
rc = tls_new_certificate ( tls, payload, payload_len );
break;
+ case TLS_SERVER_KEY_EXCHANGE:
+ rc = tls_new_server_key_exchange ( tls, payload,
+ payload_len );
+ break;
case TLS_CERTIFICATE_REQUEST:
rc = tls_new_certificate_request ( tls, payload,
payload_len );
@@ -2213,7 +2529,7 @@ static void tls_hmac_init ( struct tls_cipherspec *cipherspec, void *ctx,
uint64_t seq, struct tls_header *tlshdr ) {
struct digest_algorithm *digest = cipherspec->suite->digest;
- hmac_init ( digest, ctx, cipherspec->mac_secret, &digest->digestsize );
+ hmac_init ( digest, ctx, cipherspec->mac_secret, digest->digestsize );
seq = cpu_to_be64 ( seq );
hmac_update ( digest, ctx, &seq, sizeof ( seq ) );
hmac_update ( digest, ctx, tlshdr, sizeof ( *tlshdr ) );
@@ -2245,8 +2561,7 @@ static void tls_hmac_final ( struct tls_cipherspec *cipherspec, void *ctx,
void *hmac ) {
struct digest_algorithm *digest = cipherspec->suite->digest;
- hmac_final ( digest, ctx, cipherspec->mac_secret,
- &digest->digestsize, hmac );
+ hmac_final ( digest, ctx, hmac );
}
/**
@@ -2263,7 +2578,7 @@ static void tls_hmac ( struct tls_cipherspec *cipherspec,
uint64_t seq, struct tls_header *tlshdr,
const void *data, size_t len, void *hmac ) {
struct digest_algorithm *digest = cipherspec->suite->digest;
- uint8_t ctx[digest->ctxsize];
+ uint8_t ctx[ hmac_ctxsize ( digest ) ];
tls_hmac_init ( cipherspec, ctx, seq, tlshdr );
tls_hmac_update ( cipherspec, ctx, data, len );
@@ -2545,7 +2860,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
struct cipher_algorithm *cipher = cipherspec->suite->cipher;
struct digest_algorithm *digest = cipherspec->suite->digest;
- uint8_t ctx[digest->ctxsize];
+ uint8_t ctx[ hmac_ctxsize ( digest ) ];
uint8_t verify_mac[digest->digestsize];
struct io_buffer *iobuf;
void *mac;
@@ -3175,7 +3490,7 @@ int add_tls ( struct interface *xfer, const char *name,
&tls->refcnt );
tls->key = privkey_get ( key ? key : &private_key );
tls->root = x509_root_get ( root ? root : &root_certificates );
- tls->version = TLS_VERSION_TLS_1_2;
+ tls->version = TLS_VERSION_MAX;
tls_clear_cipher ( tls, &tls->tx_cipherspec );
tls_clear_cipher ( tls, &tls->tx_cipherspec_pending );
tls_clear_cipher ( tls, &tls->rx_cipherspec );
@@ -3188,11 +3503,6 @@ int add_tls ( struct interface *xfer, const char *name,
( sizeof ( tls->client_random.random ) ) ) ) != 0 ) {
goto err_random;
}
- tls->pre_master_secret.version = htons ( tls->version );
- if ( ( rc = tls_generate_random ( tls, &tls->pre_master_secret.random,
- ( sizeof ( tls->pre_master_secret.random ) ) ) ) != 0 ) {
- goto err_random;
- }
if ( ( rc = tls_session ( tls, name ) ) != 0 )
goto err_session;
list_add_tail ( &tls->list, &tls->session->conn );