diff options
| author | Simon Rettberg | 2022-10-21 17:22:09 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2022-10-21 17:22:09 +0200 |
| commit | 80109b804a700384799d54134ceebf4f40a8b7a3 (patch) | |
| tree | bbdf311ed02dd2c5c1f0a8d1997519ed2c24d93e /src/net | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [tls] Add support for Ephemeral Diffie-Hellman key exchange (diff) | |
| download | ipxe-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.c | 11 | ||||
| -rw-r--r-- | src/net/80211/wpa_tkip.c | 9 | ||||
| -rw-r--r-- | src/net/ipv6.c | 2 | ||||
| -rw-r--r-- | src/net/ndp.c | 36 | ||||
| -rw-r--r-- | src/net/pccrc.c | 9 | ||||
| -rw-r--r-- | src/net/tls.c | 420 |
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 ); |
