summaryrefslogtreecommitdiffstats
path: root/src/net/tls.c
diff options
context:
space:
mode:
authorSimon Rettberg2026-01-28 12:53:53 +0100
committerSimon Rettberg2026-01-28 12:53:53 +0100
commit8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch)
treea8b359e59196be5b2e3862bed189107f4bc9975f /src/net/tls.c
parentMerge branch 'master' into openslx (diff)
parent[prefix] Make unlzma.S compatible with 386 class CPUs (diff)
downloadipxe-openslx.tar.gz
ipxe-openslx.tar.xz
ipxe-openslx.zip
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/net/tls.c')
-rw-r--r--src/net/tls.c1044
1 files changed, 595 insertions, 449 deletions
diff --git a/src/net/tls.c b/src/net/tls.c
index 5f89be452..4f8ea2692 100644
--- a/src/net/tls.c
+++ b/src/net/tls.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
/**
* @file
@@ -50,6 +51,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/validator.h>
#include <ipxe/job.h>
#include <ipxe/dhe.h>
+#include <ipxe/ecdhe.h>
#include <ipxe/tls.h>
#include <config/crypto.h>
@@ -170,10 +172,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EPERM_VERIFY \
__einfo_uniqify ( EINFO_EPERM, 0x02, \
"Handshake verification failed" )
-#define EPERM_CLIENT_CERT __einfo_error ( EINFO_EPERM_CLIENT_CERT )
-#define EINFO_EPERM_CLIENT_CERT \
- __einfo_uniqify ( EINFO_EPERM, 0x03, \
- "No suitable client certificate available" )
#define EPERM_RENEG_INSECURE __einfo_error ( EINFO_EPERM_RENEG_INSECURE )
#define EINFO_EPERM_RENEG_INSECURE \
__einfo_uniqify ( EINFO_EPERM, 0x04, \
@@ -186,6 +184,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define EINFO_EPERM_KEY_EXCHANGE \
__einfo_uniqify ( EINFO_EPERM, 0x06, \
"ServerKeyExchange verification failed" )
+#define EPERM_EMS __einfo_error ( EINFO_EPERM_EMS )
+#define EINFO_EPERM_EMS \
+ __einfo_uniqify ( EINFO_EPERM, 0x07, \
+ "Extended master secret extension mismatch" )
#define EPROTO_VERSION __einfo_error ( EINFO_EPROTO_VERSION )
#define EINFO_EPROTO_VERSION \
__einfo_uniqify ( EINFO_EPROTO, 0x01, \
@@ -195,10 +197,15 @@ FILE_LICENCE ( GPL2_OR_LATER );
static LIST_HEAD ( tls_sessions );
static void tls_tx_resume_all ( struct tls_session *session );
+static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls,
+ size_t len );
+static int tls_send_record ( struct tls_connection *tls, unsigned int type,
+ struct io_buffer *iobuf );
static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
const void *data, size_t len );
static void tls_clear_cipher ( struct tls_connection *tls,
struct tls_cipherspec *cipherspec );
+static void tls_verify_handshake ( struct tls_connection *tls, void *out );
/******************************************************************************
*
@@ -251,8 +258,8 @@ static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) {
* @ret is_ready TLS connection is ready
*/
static int tls_ready ( struct tls_connection *tls ) {
- return ( ( ! is_pending ( &tls->client_negotiation ) ) &&
- ( ! is_pending ( &tls->server_negotiation ) ) );
+ return ( ( ! is_pending ( &tls->client.negotiation ) ) &&
+ ( ! is_pending ( &tls->server.negotiation ) ) );
}
/**
@@ -382,21 +389,21 @@ static void free_tls ( struct refcnt *refcnt ) {
/* Free dynamically-allocated resources */
free ( tls->new_session_ticket );
- tls_clear_cipher ( tls, &tls->tx_cipherspec );
- 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 );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.pending );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.pending );
+ free ( tls->server.exchange );
free ( tls->handshake_ctx );
- list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) {
+ list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
}
- free_iob ( tls->rx_handshake );
- x509_chain_put ( tls->certs );
- x509_chain_put ( tls->chain );
- x509_root_put ( tls->root );
- privkey_put ( tls->key );
+ free_iob ( tls->rx.handshake );
+ privkey_put ( tls->client.key );
+ x509_chain_put ( tls->client.chain );
+ x509_chain_put ( tls->server.chain );
+ x509_root_put ( tls->server.root );
/* Drop reference to session */
assert ( list_empty ( &tls->list ) );
@@ -415,17 +422,17 @@ static void free_tls ( struct refcnt *refcnt ) {
static void tls_close ( struct tls_connection *tls, int rc ) {
/* Remove pending operations, if applicable */
- pending_put ( &tls->client_negotiation );
- pending_put ( &tls->server_negotiation );
- pending_put ( &tls->validation );
+ pending_put ( &tls->client.negotiation );
+ pending_put ( &tls->server.negotiation );
+ pending_put ( &tls->server.validation );
/* Remove process */
- process_del ( &tls->process );
+ process_del ( &tls->tx.process );
/* Close all interfaces */
intf_shutdown ( &tls->cipherstream, rc );
intf_shutdown ( &tls->plainstream, rc );
- intf_shutdown ( &tls->validator, rc );
+ intf_shutdown ( &tls->server.validator, rc );
/* Remove from session */
list_del ( &tls->list );
@@ -636,21 +643,43 @@ static void tls_prf ( struct tls_connection *tls, const void *secret,
static void tls_generate_master_secret ( struct tls_connection *tls,
const void *pre_master_secret,
size_t pre_master_secret_len ) {
+ struct digest_algorithm *digest = tls->handshake_digest;
+ uint8_t digest_out[ digest->digestsize ];
+
+ /* Generate handshake digest */
+ tls_verify_handshake ( tls, digest_out );
- DBGC ( tls, "TLS %p pre-master-secret:\n", tls );
+ /* Show inputs */
+ DBGC ( tls, "TLS %p pre-master secret:\n", tls );
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_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, pre_master_secret, pre_master_secret_len,
- &tls->master_secret, sizeof ( tls->master_secret ),
- "master secret",
- &tls->client_random, sizeof ( tls->client_random ),
- &tls->server_random, sizeof ( tls->server_random ) );
+ DBGC_HD ( tls, &tls->server.random, sizeof ( tls->server.random ) );
+ DBGC ( tls, "TLS %p session hash:\n", tls );
+ DBGC_HD ( tls, digest_out, sizeof ( digest_out ) );
- DBGC ( tls, "TLS %p generated master secret:\n", tls );
+ /* Generate master secret */
+ if ( tls->extended_master_secret ) {
+ tls_prf_label ( tls, pre_master_secret, pre_master_secret_len,
+ &tls->master_secret,
+ sizeof ( tls->master_secret ),
+ "extended master secret",
+ digest_out, sizeof ( digest_out ) );
+ } else {
+ 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 ),
+ &tls->server.random,
+ sizeof ( tls->server.random ) );
+ }
+
+ /* Show output */
+ DBGC ( tls, "TLS %p generated %smaster secret:\n", tls,
+ ( tls->extended_master_secret ? "extended ": "" ) );
DBGC_HD ( tls, &tls->master_secret, sizeof ( tls->master_secret ) );
}
@@ -662,8 +691,8 @@ static void tls_generate_master_secret ( struct tls_connection *tls,
* The master secret must already be known.
*/
static int tls_generate_keys ( struct tls_connection *tls ) {
- struct tls_cipherspec *tx_cipherspec = &tls->tx_cipherspec_pending;
- struct tls_cipherspec *rx_cipherspec = &tls->rx_cipherspec_pending;
+ struct tls_cipherspec *tx_cipherspec = &tls->tx.cipherspec.pending;
+ struct tls_cipherspec *rx_cipherspec = &tls->rx.cipherspec.pending;
size_t hash_size = tx_cipherspec->suite->mac_len;
size_t key_size = tx_cipherspec->suite->key_len;
size_t iv_size = tx_cipherspec->suite->fixed_iv_len;
@@ -675,8 +704,8 @@ static int tls_generate_keys ( struct tls_connection *tls ) {
/* Generate key block */
tls_prf_label ( tls, &tls->master_secret, sizeof ( tls->master_secret ),
key_block, sizeof ( key_block ), "key expansion",
- &tls->server_random, sizeof ( tls->server_random ),
- &tls->client_random, sizeof ( tls->client_random ) );
+ &tls->server.random, sizeof ( tls->server.random ),
+ &tls->client.random, sizeof ( tls->client.random ) );
/* Split key block into portions */
key = key_block;
@@ -856,10 +885,6 @@ tls_find_cipher_suite ( unsigned int cipher_suite ) {
static void tls_clear_cipher ( struct tls_connection *tls __unused,
struct tls_cipherspec *cipherspec ) {
- if ( cipherspec->suite ) {
- pubkey_final ( cipherspec->suite->pubkey,
- cipherspec->pubkey_ctx );
- }
free ( cipherspec->dynamic );
memset ( cipherspec, 0, sizeof ( *cipherspec ) );
cipherspec->suite = &tls_cipher_suite_null;
@@ -876,7 +901,6 @@ static void tls_clear_cipher ( struct tls_connection *tls __unused,
static int tls_set_cipher ( struct tls_connection *tls,
struct tls_cipherspec *cipherspec,
struct tls_cipher_suite *suite ) {
- struct pubkey_algorithm *pubkey = suite->pubkey;
struct cipher_algorithm *cipher = suite->cipher;
size_t total;
void *dynamic;
@@ -885,8 +909,7 @@ static int tls_set_cipher ( struct tls_connection *tls,
tls_clear_cipher ( tls, cipherspec );
/* Allocate dynamic storage */
- total = ( pubkey->ctxsize + cipher->ctxsize + suite->mac_len +
- suite->fixed_iv_len );
+ total = ( cipher->ctxsize + suite->mac_len + suite->fixed_iv_len );
dynamic = zalloc ( total );
if ( ! dynamic ) {
DBGC ( tls, "TLS %p could not allocate %zd bytes for crypto "
@@ -896,7 +919,6 @@ static int tls_set_cipher ( struct tls_connection *tls,
/* Assign storage */
cipherspec->dynamic = dynamic;
- cipherspec->pubkey_ctx = dynamic; dynamic += pubkey->ctxsize;
cipherspec->cipher_ctx = dynamic; dynamic += cipher->ctxsize;
cipherspec->mac_secret = dynamic; dynamic += suite->mac_len;
cipherspec->fixed_iv = dynamic; dynamic += suite->fixed_iv_len;
@@ -936,10 +958,10 @@ static int tls_select_cipher ( struct tls_connection *tls,
return rc;
/* Set ciphers */
- if ( ( rc = tls_set_cipher ( tls, &tls->tx_cipherspec_pending,
+ if ( ( rc = tls_set_cipher ( tls, &tls->tx.cipherspec.pending,
suite ) ) != 0 )
return rc;
- if ( ( rc = tls_set_cipher ( tls, &tls->rx_cipherspec_pending,
+ if ( ( rc = tls_set_cipher ( tls, &tls->rx.cipherspec.pending,
suite ) ) != 0 )
return rc;
@@ -955,22 +977,20 @@ static int tls_select_cipher ( struct tls_connection *tls,
* Activate next cipher suite
*
* @v tls TLS connection
- * @v pending Pending cipher specification
- * @v active Active cipher specification to replace
+ * @v pair Cipher specification pair
* @ret rc Return status code
*/
static int tls_change_cipher ( struct tls_connection *tls,
- struct tls_cipherspec *pending,
- struct tls_cipherspec *active ) {
+ struct tls_cipherspec_pair *pair ) {
/* Sanity check */
- if ( pending->suite == &tls_cipher_suite_null ) {
+ if ( pair->pending.suite == &tls_cipher_suite_null ) {
DBGC ( tls, "TLS %p refusing to use null cipher\n", tls );
return -ENOTSUP_NULL;
}
- tls_clear_cipher ( tls, active );
- memswap ( active, pending, sizeof ( *active ) );
+ tls_clear_cipher ( tls, &pair->active );
+ memswap ( &pair->active, &pair->pending, sizeof ( pair->active ) );
return 0;
}
@@ -1088,7 +1108,7 @@ tls_find_named_curve ( unsigned int named_curve ) {
* @v tls TLS connection
*/
static void tls_tx_resume ( struct tls_connection *tls ) {
- process_add ( &tls->process );
+ process_add ( &tls->tx.process );
}
/**
@@ -1111,16 +1131,16 @@ static void tls_tx_resume_all ( struct tls_session *session ) {
static void tls_restart ( struct tls_connection *tls ) {
/* Sanity check */
- assert ( ! tls->tx_pending );
- assert ( ! is_pending ( &tls->client_negotiation ) );
- assert ( ! is_pending ( &tls->server_negotiation ) );
- assert ( ! is_pending ( &tls->validation ) );
+ assert ( ! tls->tx.pending );
+ assert ( ! is_pending ( &tls->client.negotiation ) );
+ assert ( ! is_pending ( &tls->server.negotiation ) );
+ assert ( ! is_pending ( &tls->server.validation ) );
/* (Re)start negotiation */
- tls->tx_pending = TLS_TX_CLIENT_HELLO;
+ tls->tx.pending = TLS_TX_CLIENT_HELLO;
tls_tx_resume ( tls );
- pending_get ( &tls->client_negotiation );
- pending_get ( &tls->server_negotiation );
+ pending_get ( &tls->client.negotiation );
+ pending_get ( &tls->server.negotiation );
}
/**
@@ -1134,9 +1154,6 @@ static void tls_restart ( struct tls_connection *tls ) {
static int tls_send_handshake ( struct tls_connection *tls,
const void *data, size_t len ) {
- /* Add to handshake digest */
- tls_add_handshake ( tls, data, len );
-
/* Send record */
return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len );
}
@@ -1207,11 +1224,16 @@ static int tls_client_hello ( struct tls_connection *tls,
} __attribute__ (( packed )) data;
} __attribute__ (( packed )) *named_curve_ext;
struct {
+ uint16_t type;
+ uint16_t len;
+ } __attribute__ (( packed )) *extended_master_secret_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 ( *extended_master_secret_ext ) extended_master_secret;
typeof ( *named_curve_ext )
named_curve[TLS_NUM_NAMED_CURVES ? 1 : 0];
} __attribute__ (( packed )) *extensions;
@@ -1239,7 +1261,7 @@ static int tls_client_hello ( struct tls_connection *tls,
htonl ( sizeof ( hello ) -
sizeof ( hello.type_length ) ) );
hello.version = htons ( TLS_VERSION_MAX );
- memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) );
+ memcpy ( &hello.random, &tls->client.random, sizeof ( hello.random ) );
hello.session_id_len = tls->session_id_len;
memcpy ( hello.session_id, tls->session_id,
sizeof ( hello.session_id ) );
@@ -1267,7 +1289,7 @@ static int tls_client_hello ( struct tls_connection *tls,
max_fragment_length_ext->type = htons ( TLS_MAX_FRAGMENT_LENGTH );
max_fragment_length_ext->len
= htons ( sizeof ( max_fragment_length_ext->data ) );
- max_fragment_length_ext->data.max = TLS_MAX_FRAGMENT_LENGTH_4096;
+ max_fragment_length_ext->data.max = TLS_MAX_FRAGMENT_LENGTH_VALUE;
/* Construct supported signature algorithms extension */
signature_algorithms_ext = &extensions->signature_algorithms;
@@ -1297,6 +1319,12 @@ static int tls_client_hello ( struct tls_connection *tls,
memcpy ( session_ticket_ext->data.data, session->ticket,
sizeof ( session_ticket_ext->data.data ) );
+ /* Construct extended master secret extension */
+ extended_master_secret_ext = &extensions->extended_master_secret;
+ extended_master_secret_ext->type
+ = htons ( TLS_EXTENDED_MASTER_SECRET );
+ extended_master_secret_ext->len = 0;
+
/* Construct named curves extension, if applicable */
if ( sizeof ( extensions->named_curve ) ) {
named_curve_ext = &extensions->named_curve[0];
@@ -1341,12 +1369,12 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
} __attribute__ (( packed )) *certificates;
struct x509_link *link;
struct x509_certificate *cert;
+ struct io_buffer *iobuf;
size_t len;
- int rc;
/* Calculate length of client certificates */
len = 0;
- list_for_each_entry ( link, &tls->certs->links, list ) {
+ list_for_each_entry ( link, &tls->client.chain->links, list ) {
cert = link->cert;
len += ( sizeof ( *certificate ) + cert->raw.len );
DBGC ( tls, "TLS %p sending client certificate %s\n",
@@ -1356,33 +1384,28 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
/* Allocate storage for Certificate record (which may be too
* large for the stack).
*/
- certificates = zalloc ( sizeof ( *certificates ) + len );
- if ( ! certificates )
+ iobuf = tls_alloc_iob ( tls, ( sizeof ( *certificates ) + len ) );
+ if ( ! iobuf )
return -ENOMEM_CERTIFICATE;
/* Populate record */
+ certificates = iob_put ( iobuf, sizeof ( *certificates ) );
certificates->type_length =
( cpu_to_le32 ( TLS_CERTIFICATE ) |
htonl ( sizeof ( *certificates ) + len -
sizeof ( certificates->type_length ) ) );
tls_set_uint24 ( &certificates->length, len );
- certificate = &certificates->certificates[0];
- list_for_each_entry ( link, &tls->certs->links, list ) {
+ list_for_each_entry ( link, &tls->client.chain->links, list ) {
cert = link->cert;
+ certificate = iob_put ( iobuf, sizeof ( *certificate ) );
tls_set_uint24 ( &certificate->length, cert->raw.len );
- memcpy ( certificate->data, cert->raw.data, cert->raw.len );
- certificate = ( ( ( void * ) certificate->data ) +
- cert->raw.len );
+ memcpy ( iob_put ( iobuf, cert->raw.len ), cert->raw.data,
+ cert->raw.len );
}
/* Transmit record */
- rc = tls_send_handshake ( tls, certificates,
- ( sizeof ( *certificates ) + len ) );
-
- /* Free record */
- free ( certificates );
-
- return rc;
+ return tls_send_record ( tls, TLS_TYPE_HANDSHAKE,
+ iob_disown ( iobuf ) );
}
/**
@@ -1392,55 +1415,71 @@ static int tls_send_certificate ( struct tls_connection *tls ) {
* @ret rc Return status code
*/
static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ 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];
- } __attribute__ (( packed )) key_xchg;
- size_t unused;
- int len;
+ struct asn1_cursor cursor = {
+ .data = &pre_master_secret,
+ .len = sizeof ( pre_master_secret ),
+ };
+ struct asn1_builder builder = { NULL, 0 };
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;
+ goto err_random;
}
- /* Generate master secret */
- tls_generate_master_secret ( tls, &pre_master_secret,
- sizeof ( pre_master_secret ) );
-
/* Encrypt pre-master secret using server's public key */
- memset ( &key_xchg, 0, sizeof ( key_xchg ) );
- len = pubkey_encrypt ( pubkey, cipherspec->pubkey_ctx,
- &pre_master_secret, sizeof ( pre_master_secret ),
- key_xchg.encrypted_pre_master_secret );
- if ( len < 0 ) {
- rc = len;
+ if ( ( rc = pubkey_encrypt ( pubkey, &tls->server.key, &cursor,
+ &builder ) ) != 0 ) {
DBGC ( tls, "TLS %p could not encrypt pre-master secret: %s\n",
tls, strerror ( rc ) );
- return rc;
+ goto err_encrypt;
+ }
+
+ /* Construct Client Key Exchange record */
+ {
+ struct {
+ uint32_t type_length;
+ uint16_t encrypted_pre_master_secret_len;
+ } __attribute__ (( packed )) header;
+
+ header.type_length =
+ ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
+ htonl ( builder.len + sizeof ( header ) -
+ sizeof ( header.type_length ) ) );
+ header.encrypted_pre_master_secret_len = htons ( builder.len );
+
+ if ( ( rc = asn1_prepend_raw ( &builder, &header,
+ sizeof ( header ) ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not construct Client Key "
+ "Exchange: %s\n", tls, strerror ( rc ) );
+ goto err_prepend;
+ }
}
- unused = ( max_len - len );
- key_xchg.type_length =
- ( cpu_to_le32 ( TLS_CLIENT_KEY_EXCHANGE ) |
- htonl ( sizeof ( key_xchg ) -
- sizeof ( key_xchg.type_length ) - unused ) );
- key_xchg.encrypted_pre_master_secret_len =
- htons ( sizeof ( key_xchg.encrypted_pre_master_secret ) -
- unused );
- return tls_send_handshake ( tls, &key_xchg,
- ( sizeof ( key_xchg ) - unused ) );
+ /* Transmit Client Key Exchange record */
+ if ( ( rc = tls_send_handshake ( tls, builder.data,
+ builder.len ) ) != 0 ) {
+ goto err_send;
+ }
+
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, &pre_master_secret,
+ sizeof ( pre_master_secret ) );
+
+ err_random:
+ err_encrypt:
+ err_prepend:
+ err_send:
+ free ( builder.data );
+ return rc;
}
/** Public key exchange algorithm */
@@ -1458,7 +1497,7 @@ struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = {
*/
static int tls_verify_dh_params ( struct tls_connection *tls,
size_t param_len ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
+ 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 );
@@ -1467,14 +1506,15 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
uint16_t signature_len;
uint8_t signature[0];
} __attribute__ (( packed )) *sig;
+ struct asn1_cursor signature;
const void *data;
size_t remaining;
int rc;
/* Signature follows parameters */
- assert ( param_len <= tls->server_key_len );
- data = ( tls->server_key + param_len );
- remaining = ( tls->server_key_len - param_len );
+ assert ( param_len <= tls->server.exchange_len );
+ data = ( tls->server.exchange + param_len );
+ remaining = ( tls->server.exchange_len - param_len );
/* Parse signature from ServerKeyExchange */
sig = data;
@@ -1483,9 +1523,12 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
sizeof ( *sig ) ) ) ) {
DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
tls );
- DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -EINVAL_KEY_EXCHANGE;
}
+ signature.data = sig->signature;
+ signature.len = ntohs ( sig->signature_len );
/* Identify signature and hash algorithm */
if ( use_sig_hash ) {
@@ -1509,28 +1552,25 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
/* 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, param_len );
+ 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.exchange, param_len );
digest_final ( digest, ctx, hash );
/* Verify signature */
- if ( ( rc = pubkey_verify ( pubkey, cipherspec->pubkey_ctx,
- digest, hash, signature,
- signature_len ) ) != 0 ) {
+ if ( ( rc = pubkey_verify ( pubkey, &tls->server.key, digest,
+ hash, &signature ) ) != 0 ) {
DBGC ( tls, "TLS %p ServerKeyExchange failed "
"verification\n", tls );
- DBGC_HDA ( tls, 0, tls->server_key,
- tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -EPERM_KEY_EXCHANGE;
}
}
@@ -1545,7 +1585,7 @@ static int tls_verify_dh_params ( struct tls_connection *tls,
* @ret rc Return status code
*/
static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
- uint8_t private[ sizeof ( tls->client_random.random ) ];
+ uint8_t private[ sizeof ( tls->client.random.random ) ];
const struct {
uint16_t len;
uint8_t data[0];
@@ -1558,8 +1598,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
int rc;
/* Parse ServerKeyExchange */
- data = tls->server_key;
- remaining = tls->server_key_len;
+ data = tls->server.exchange;
+ remaining = tls->server.exchange_len;
for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){
dh_val[i] = data;
if ( ( sizeof ( *dh_val[i] ) > remaining ) ||
@@ -1567,8 +1607,8 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
sizeof ( *dh_val[i] ) ) )){
DBGC ( tls, "TLS %p received underlength "
"ServerKeyExchange\n", tls );
- DBGC_HDA ( tls, 0, tls->server_key,
- tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
rc = -EINVAL_KEY_EXCHANGE;
goto err_header;
}
@@ -1576,7 +1616,7 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
data += frag_len;
remaining -= frag_len;
}
- param_len = ( tls->server_key_len - remaining );
+ param_len = ( tls->server.exchange_len - remaining );
/* Verify parameter signature */
if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 )
@@ -1637,15 +1677,15 @@ static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
len--;
}
- /* Generate master secret */
- tls_generate_master_secret ( tls, pre_master_secret, len );
-
/* Transmit Client Key Exchange record */
if ( ( rc = tls_send_handshake ( tls, key_xchg,
sizeof ( *key_xchg ) ) ) !=0){
goto err_send_handshake;
}
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, pre_master_secret, len );
+
err_send_handshake:
err_dhe_key:
free ( dynamic );
@@ -1678,15 +1718,20 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
uint8_t public[0];
} __attribute__ (( packed )) *ecdh;
size_t param_len;
+ size_t pointsize;
+ size_t keysize;
+ size_t offset;
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 ) ))){
+ ecdh = tls->server.exchange;
+ if ( ( sizeof ( *ecdh ) > tls->server.exchange_len ) ||
+ ( ecdh->public_len > ( tls->server.exchange_len -
+ sizeof ( *ecdh ) ) ) ) {
DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
tls );
- DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -EINVAL_KEY_EXCHANGE;
}
param_len = ( sizeof ( *ecdh ) + ecdh->public_len );
@@ -1699,34 +1744,49 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
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 );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_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 );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
return -ENOTSUP_CURVE;
}
+ DBGC ( tls, "TLS %p using named curve %s\n", tls, curve->curve->name );
+ pointsize = curve->curve->pointsize;
+ keysize = curve->curve->keysize;
+ offset = ( curve->format ? 1 : 0 );
/* Check key length */
- if ( ecdh->public_len != curve->curve->keysize ) {
+ if ( ecdh->public_len != ( offset + pointsize ) ) {
DBGC ( tls, "TLS %p invalid %s key\n",
tls, curve->curve->name );
- DBGC_HDA ( tls, 0, tls->server_key, tls->server_key_len );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_len );
+ return -EINVAL_KEY_EXCHANGE;
+ }
+
+ /* Check curve point format byte (if present) */
+ if ( curve->format && ( ecdh->public[0] != curve->format ) ) {
+ DBGC ( tls, "TLS %p invalid %s curve point format\n",
+ tls, curve->curve->name );
+ DBGC_HDA ( tls, 0, tls->server.exchange,
+ tls->server.exchange_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];
+ uint8_t private[keysize];
+ uint8_t pre_master_secret[pointsize];
struct {
uint32_t type_length;
uint8_t public_len;
- uint8_t public[len];
+ uint8_t public[ecdh->public_len];
} __attribute__ (( packed )) key_xchg;
/* Generate ephemeral private key */
@@ -1735,36 +1795,33 @@ static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
return rc;
}
- /* Calculate pre-master secret */
- if ( ( rc = elliptic_multiply ( curve->curve,
- ecdh->public, private,
- pre_master_secret ) ) != 0 ) {
+ /* Exchange keys */
+ if ( ( rc = ecdhe_key ( curve->curve, ( ecdh->public + offset ),
+ private, ( key_xchg.public + offset ),
+ 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;
- }
+ key_xchg.public_len = sizeof ( key_xchg.public );
+ if ( curve->format )
+ key_xchg.public[0] = curve->format;
/* Transmit Client Key Exchange record */
if ( ( rc = tls_send_handshake ( tls, &key_xchg,
sizeof ( key_xchg ) ) ) !=0){
return rc;
}
+
+ /* Generate master secret */
+ tls_generate_master_secret ( tls, pre_master_secret,
+ curve->pre_master_secret_len );
}
return 0;
@@ -1783,7 +1840,7 @@ struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = {
* @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_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
struct tls_cipher_suite *suite = cipherspec->suite;
int rc;
@@ -1812,24 +1869,17 @@ static int tls_send_client_key_exchange ( struct tls_connection *tls ) {
*/
static int tls_send_certificate_verify ( struct tls_connection *tls ) {
struct digest_algorithm *digest = tls->handshake_digest;
- struct x509_certificate *cert = x509_first ( tls->certs );
+ struct x509_certificate *cert = x509_first ( tls->client.chain );
struct pubkey_algorithm *pubkey = cert->signature_algorithm->pubkey;
- struct asn1_cursor *key = privkey_cursor ( tls->key );
+ struct asn1_cursor *key = privkey_cursor ( tls->client.key );
uint8_t digest_out[ digest->digestsize ];
- uint8_t ctx[ pubkey->ctxsize ];
struct tls_signature_hash_algorithm *sig_hash = NULL;
+ struct asn1_builder builder = { NULL, 0 };
int rc;
/* Generate digest to be signed */
tls_verify_handshake ( tls, digest_out );
- /* Initialise public-key algorithm */
- if ( ( rc = pubkey_init ( pubkey, ctx, key->data, key->len ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not initialise %s client private "
- "key: %s\n", tls, pubkey->name, strerror ( rc ) );
- goto err_pubkey_init;
- }
-
/* TLSv1.2 and later use explicit algorithm identifiers */
if ( tls_version ( tls, TLS_VERSION_TLS_1_2 ) ) {
sig_hash = tls_signature_hash_algorithm ( pubkey, digest );
@@ -1842,55 +1892,53 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) {
}
}
- /* Generate and transmit record */
+ /* Sign digest */
+ if ( ( rc = pubkey_sign ( pubkey, key, digest, digest_out,
+ &builder ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not sign %s digest using %s client "
+ "private key: %s\n", tls, digest->name, pubkey->name,
+ strerror ( rc ) );
+ goto err_pubkey_sign;
+ }
+
+ /* Construct Certificate Verify record */
{
- size_t max_len = pubkey_max_len ( pubkey, ctx );
int use_sig_hash = ( ( sig_hash == NULL ) ? 0 : 1 );
struct {
uint32_t type_length;
struct tls_signature_hash_id sig_hash[use_sig_hash];
uint16_t signature_len;
- uint8_t signature[max_len];
- } __attribute__ (( packed )) certificate_verify;
- size_t unused;
- int len;
-
- /* Sign digest */
- len = pubkey_sign ( pubkey, ctx, digest, digest_out,
- certificate_verify.signature );
- if ( len < 0 ) {
- rc = len;
- DBGC ( tls, "TLS %p could not sign %s digest using %s "
- "client private key: %s\n", tls, digest->name,
- pubkey->name, strerror ( rc ) );
- goto err_pubkey_sign;
- }
- unused = ( max_len - len );
-
- /* Construct Certificate Verify record */
- certificate_verify.type_length =
- ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) |
- htonl ( sizeof ( certificate_verify ) -
- sizeof ( certificate_verify.type_length ) -
- unused ) );
+ } __attribute__ (( packed )) header;
+
+ header.type_length = ( cpu_to_le32 ( TLS_CERTIFICATE_VERIFY ) |
+ htonl ( builder.len +
+ sizeof ( header ) -
+ sizeof ( header.type_length )));
if ( use_sig_hash ) {
- memcpy ( &certificate_verify.sig_hash[0],
- &sig_hash->code,
- sizeof ( certificate_verify.sig_hash[0] ) );
+ memcpy ( &header.sig_hash[0], &sig_hash->code,
+ sizeof ( header.sig_hash[0] ) );
}
- certificate_verify.signature_len =
- htons ( sizeof ( certificate_verify.signature ) -
- unused );
+ header.signature_len = htons ( builder.len );
- /* Transmit record */
- rc = tls_send_handshake ( tls, &certificate_verify,
- ( sizeof ( certificate_verify ) - unused ) );
+ if ( ( rc = asn1_prepend_raw ( &builder, &header,
+ sizeof ( header ) ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not construct Certificate "
+ "Verify: %s\n", tls, strerror ( rc ) );
+ goto err_prepend;
+ }
}
+ /* Transmit record */
+ if ( ( rc = tls_send_handshake ( tls, builder.data,
+ builder.len ) ) != 0 ) {
+ goto err_send;
+ }
+
+ err_send:
+ err_prepend:
err_pubkey_sign:
err_sig_hash:
- pubkey_final ( pubkey, ctx );
- err_pubkey_init:
+ free ( builder.data );
return rc;
}
@@ -1946,7 +1994,7 @@ static int tls_send_finished ( struct tls_connection *tls ) {
return rc;
/* Mark client as finished */
- pending_put ( &tls->client_negotiation );
+ pending_put ( &tls->client.negotiation );
return 0;
}
@@ -1976,13 +2024,12 @@ static int tls_new_change_cipher ( struct tls_connection *tls,
iob_pull ( iobuf, sizeof ( *change_cipher ) );
/* Change receive cipher spec */
- if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
- &tls->rx_cipherspec ) ) != 0 ) {
+ if ( ( rc = tls_change_cipher ( tls, &tls->rx.cipherspec ) ) != 0 ) {
DBGC ( tls, "TLS %p could not activate RX cipher: %s\n",
tls, strerror ( rc ) );
return rc;
}
- tls->rx_seq = ~( ( uint64_t ) 0 );
+ tls->rx.seq = ~( ( uint64_t ) 0 );
return 0;
}
@@ -2047,7 +2094,7 @@ static int tls_new_hello_request ( struct tls_connection *tls,
}
/* Fail unless server supports secure renegotiation */
- if ( ! tls->secure_renegotiation ) {
+ if ( ! ( tls->secure_renegotiation && tls->extended_master_secret ) ) {
DBGC ( tls, "TLS %p refusing to renegotiate insecurely\n",
tls );
return -EPERM_RENEG_INSECURE;
@@ -2094,6 +2141,9 @@ static int tls_new_server_hello ( struct tls_connection *tls,
uint8_t len;
uint8_t data[0];
} __attribute__ (( packed )) *reneg = NULL;
+ const struct {
+ uint8_t data[0];
+ } __attribute__ (( packed )) *ems = NULL;
uint16_t version;
size_t exts_len;
size_t ext_len;
@@ -2158,6 +2208,9 @@ static int tls_new_server_hello ( struct tls_connection *tls,
return -EINVAL_HELLO;
}
break;
+ case htons ( TLS_EXTENDED_MASTER_SECRET ) :
+ ems = ( ( void * ) ext->data );
+ break;
}
}
}
@@ -2188,8 +2241,11 @@ static int tls_new_server_hello ( struct tls_connection *tls,
return rc;
/* Copy out server random bytes */
- memcpy ( &tls->server_random, &hello_a->random,
- sizeof ( tls->server_random ) );
+ memcpy ( &tls->server.random, &hello_a->random,
+ sizeof ( tls->server.random ) );
+
+ /* Handle extended master secret */
+ tls->extended_master_secret = ( !! ems );
/* Check session ID */
if ( hello_a->session_id_len &&
@@ -2203,6 +2259,14 @@ static int tls_new_server_hello ( struct tls_connection *tls,
if ( ( rc = tls_generate_keys ( tls ) ) != 0 )
return rc;
+ /* Ensure master secret generation method matches */
+ if ( tls->extended_master_secret !=
+ tls->session->extended_master_secret ) {
+ DBGC ( tls, "TLS %p mismatched extended master secret "
+ "extension\n", tls );
+ return -EPERM_EMS;
+ }
+
} else {
/* Record new session ID, if present */
@@ -2309,12 +2373,13 @@ static int tls_parse_chain ( struct tls_connection *tls,
int rc;
/* Free any existing certificate chain */
- x509_chain_put ( tls->chain );
- tls->chain = NULL;
+ memset ( &tls->server.key, 0, sizeof ( tls->server.key ) );
+ x509_chain_put ( tls->server.chain );
+ tls->server.chain = NULL;
/* Create certificate chain */
- tls->chain = x509_alloc_chain();
- if ( ! tls->chain ) {
+ tls->server.chain = x509_alloc_chain();
+ if ( ! tls->server.chain ) {
rc = -ENOMEM_CHAIN;
goto err_alloc_chain;
}
@@ -2346,14 +2411,15 @@ static int tls_parse_chain ( struct tls_connection *tls,
record_len = ( sizeof ( *certificate ) + certificate_len );
/* Add certificate to chain */
- if ( ( rc = x509_append_raw ( tls->chain, certificate->data,
+ if ( ( rc = x509_append_raw ( tls->server.chain,
+ certificate->data,
certificate_len ) ) != 0 ) {
DBGC ( tls, "TLS %p could not append certificate: %s\n",
tls, strerror ( rc ) );
DBGC_HDA ( tls, 0, data, remaining );
goto err_parse;
}
- cert = x509_last ( tls->chain );
+ cert = x509_last ( tls->server.chain );
DBGC ( tls, "TLS %p found certificate %s\n",
tls, x509_name ( cert ) );
@@ -2367,8 +2433,9 @@ static int tls_parse_chain ( struct tls_connection *tls,
err_parse:
err_overlength:
err_underlength:
- x509_chain_put ( tls->chain );
- tls->chain = NULL;
+ memset ( &tls->server.key, 0, sizeof ( tls->server.key ) );
+ x509_chain_put ( tls->server.chain );
+ tls->server.chain = NULL;
err_alloc_chain:
return rc;
}
@@ -2425,12 +2492,12 @@ 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;
+ free ( tls->server.exchange );
+ tls->server.exchange_len = 0;
/* Allocate copy of server key exchange record */
- tls->server_key = malloc ( len );
- if ( ! tls->server_key )
+ tls->server.exchange = malloc ( len );
+ if ( ! tls->server.exchange )
return -ENOMEM;
/* Store copy of server key exchange record for later
@@ -2438,8 +2505,8 @@ static int tls_new_server_key_exchange ( struct tls_connection *tls,
* since the certificate validation will not yet have
* completed.
*/
- memcpy ( tls->server_key, data, len );
- tls->server_key_len = len;
+ memcpy ( tls->server.exchange, data, len );
+ tls->server.exchange_len = len;
return 0;
}
@@ -2463,48 +2530,44 @@ static int tls_new_certificate_request ( struct tls_connection *tls,
*/
/* Free any existing client certificate chain */
- x509_chain_put ( tls->certs );
- tls->certs = NULL;
-
- /* Determine client certificate to be sent */
- cert = certstore_find_key ( tls->key );
- if ( ! cert ) {
- DBGC ( tls, "TLS %p could not find certificate corresponding "
- "to private key\n", tls );
- rc = -EPERM_CLIENT_CERT;
- goto err_find;
- }
- x509_get ( cert );
- DBGC ( tls, "TLS %p selected client certificate %s\n",
- tls, x509_name ( cert ) );
+ x509_chain_put ( tls->client.chain );
+ tls->client.chain = NULL;
/* Create client certificate chain */
- tls->certs = x509_alloc_chain();
- if ( ! tls->certs ) {
+ tls->client.chain = x509_alloc_chain();
+ if ( ! tls->client.chain ) {
rc = -ENOMEM;
goto err_alloc;
}
- /* Append client certificate to chain */
- if ( ( rc = x509_append ( tls->certs, cert ) ) != 0 )
- goto err_append;
+ /* Determine client certificate to be sent, if any */
+ cert = x509_find_key ( NULL, tls->client.key );
+ if ( cert ) {
+ DBGC ( tls, "TLS %p selected client certificate %s\n",
+ tls, x509_name ( cert ) );
- /* Append any relevant issuer certificates */
- if ( ( rc = x509_auto_append ( tls->certs, &certstore ) ) != 0 )
- goto err_auto_append;
+ /* Append client certificate to chain */
+ if ( ( rc = x509_append ( tls->client.chain, cert ) ) != 0 )
+ goto err_append;
- /* Drop local reference to client certificate */
- x509_put ( cert );
+ /* Append any relevant issuer certificates */
+ if ( ( rc = x509_auto_append ( tls->client.chain,
+ &certstore ) ) != 0 )
+ goto err_auto_append;
+ } else {
+
+ /* Send an empty certificate chain */
+ DBGC ( tls, "TLS %p could not find certificate corresponding "
+ "to private key\n", tls );
+ }
return 0;
err_auto_append:
err_append:
- x509_chain_put ( tls->certs );
- tls->certs = NULL;
+ x509_chain_put ( tls->client.chain );
+ tls->client.chain = NULL;
err_alloc:
- x509_put ( cert );
- err_find:
return rc;
}
@@ -2532,13 +2595,14 @@ static int tls_new_server_hello_done ( struct tls_connection *tls,
}
/* Begin certificate validation */
- if ( ( rc = create_validator ( &tls->validator, tls->chain,
- tls->root ) ) != 0 ) {
+ if ( ( rc = create_validator ( &tls->server.validator,
+ tls->server.chain,
+ tls->server.root ) ) != 0 ) {
DBGC ( tls, "TLS %p could not start certificate validation: "
"%s\n", tls, strerror ( rc ) );
return rc;
}
- pending_get ( &tls->validation );
+ pending_get ( &tls->server.validation );
return 0;
}
@@ -2580,14 +2644,14 @@ static int tls_new_finished ( struct tls_connection *tls,
}
/* Mark server as finished */
- pending_put ( &tls->server_negotiation );
+ pending_put ( &tls->server.negotiation );
/* If we are resuming a session (i.e. if the server Finished
* arrives before the client Finished is sent), then schedule
* transmission of Change Cipher and Finished.
*/
- if ( is_pending ( &tls->client_negotiation ) ) {
- tls->tx_pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED );
+ if ( is_pending ( &tls->client.negotiation ) ) {
+ tls->tx.pending |= ( TLS_TX_CHANGE_CIPHER | TLS_TX_FINISHED );
tls_tx_resume ( tls );
}
@@ -2595,6 +2659,7 @@ static int tls_new_finished ( struct tls_connection *tls,
if ( tls->session_id_len || tls->new_session_ticket_len ) {
memcpy ( session->master_secret, tls->master_secret,
sizeof ( session->master_secret ) );
+ session->extended_master_secret = tls->extended_master_secret;
}
if ( tls->session_id_len ) {
session->id_len = tls->session_id_len;
@@ -2788,7 +2853,7 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type,
break;
case TLS_TYPE_HANDSHAKE:
handler = tls_new_handshake;
- iobuf = &tls->rx_handshake;
+ iobuf = &tls->rx.handshake;
break;
default:
DBGC ( tls, "TLS %p unknown record type %d\n", tls, type );
@@ -2925,143 +2990,228 @@ static void tls_hmac_list ( struct tls_cipherspec *cipherspec,
}
/**
- * Send plaintext record
+ * Calculate maximum additional length required for transmitted record(s)
+ *
+ * @v tls TLS connection
+ * @v len I/O buffer payload length
+ * @ret reserve Maximum additional length to reserve
+ */
+static size_t tls_iob_reserved ( struct tls_connection *tls, size_t len ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.active;
+ struct tls_cipher_suite *suite = cipherspec->suite;
+ struct cipher_algorithm *cipher = suite->cipher;
+ struct tls_header *tlshdr;
+ unsigned int count;
+ size_t each;
+
+ /* Calculate number of records (allowing for zero-length records) */
+ count = ( len ? ( ( len + TLS_TX_BUFSIZE - 1 ) / TLS_TX_BUFSIZE ) : 1 );
+
+ /* Calculate maximum additional length per record */
+ each = ( sizeof ( *tlshdr ) + suite->record_iv_len + suite->mac_len +
+ ( is_block_cipher ( cipher ) ? cipher->blocksize : 0 ) +
+ cipher->authsize );
+
+ /* Calculate maximum total additional length */
+ return ( count * each );
+}
+
+/**
+ * Allocate I/O buffer for transmitted record(s)
+ *
+ * @v tls TLS connection
+ * @v len I/O buffer payload length
+ * @ret iobuf I/O buffer
+ */
+static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls,
+ size_t len ) {
+ struct io_buffer *iobuf;
+ size_t reserve;
+
+ /* Calculate maximum additional length to reserve */
+ reserve = tls_iob_reserved ( tls, len );
+
+ /* Allocate I/O buffer */
+ iobuf = xfer_alloc_iob ( &tls->cipherstream, ( reserve + len ) );
+ if ( ! iobuf )
+ return NULL;
+
+ /* Reserve space */
+ iob_reserve ( iobuf, reserve );
+
+ return iobuf;
+}
+
+/**
+ * Send plaintext record(s)
*
* @v tls TLS connection
* @v type Record type
- * @v data Plaintext record
- * @v len Length of plaintext record
+ * @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
- const void *data, size_t len ) {
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec;
+static int tls_send_record ( struct tls_connection *tls, unsigned int type,
+ struct io_buffer *iobuf ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.active;
struct tls_cipher_suite *suite = cipherspec->suite;
struct cipher_algorithm *cipher = suite->cipher;
struct digest_algorithm *digest = suite->digest;
struct {
uint8_t fixed[suite->fixed_iv_len];
- uint8_t record[suite->record_iv_len];
+ uint8_t rec[suite->record_iv_len];
} __attribute__ (( packed )) iv;
struct tls_auth_header authhdr;
struct tls_header *tlshdr;
- void *plaintext;
- size_t plaintext_len;
- struct io_buffer *ciphertext;
- size_t ciphertext_len;
- size_t padding_len;
uint8_t mac[digest->digestsize];
- void *tmp;
+ const void *plaintext;
+ const void *encrypt;
+ void *ciphertext;
+ size_t record_len;
+ size_t encrypt_len;
+ size_t pad_len;
+ size_t len;
int rc;
- /* Construct initialisation vector */
- memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
- if ( ( rc = tls_generate_random ( tls, iv.record,
- sizeof ( iv.record ) ) ) != 0 ) {
- goto err_random;
- }
-
- /* Construct authentication data */
- authhdr.seq = cpu_to_be64 ( tls->tx_seq );
- authhdr.header.type = type;
- authhdr.header.version = htons ( tls->version );
- authhdr.header.length = htons ( len );
+ /* Record plaintext pointer and length */
+ plaintext = iobuf->data;
+ len = iob_len ( iobuf );
+
+ /* Add to handshake digest if applicable */
+ if ( type == TLS_TYPE_HANDSHAKE )
+ tls_add_handshake ( tls, plaintext, len );
+
+ /* Start constructing ciphertext at start of reserved space */
+ iob_push ( iobuf, tls_iob_reserved ( tls, len ) );
+ iob_unput ( iobuf, iob_len ( iobuf ) );
+
+ /* Construct records */
+ do {
+ /* Limit length of this record (may be zero) */
+ record_len = len;
+ if ( record_len > TLS_TX_BUFSIZE )
+ record_len = TLS_TX_BUFSIZE;
+
+ /* Construct and set initialisation vector */
+ memcpy ( iv.fixed, cipherspec->fixed_iv, sizeof ( iv.fixed ) );
+ if ( ( rc = tls_generate_random ( tls, iv.rec,
+ sizeof ( iv.rec ) ) ) != 0 ) {
+ goto err_random;
+ }
+ cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv,
+ sizeof ( iv ) );
+
+ /* Construct and process authentication data */
+ authhdr.seq = cpu_to_be64 ( tls->tx.seq );
+ authhdr.header.type = type;
+ authhdr.header.version = htons ( tls->version );
+ authhdr.header.length = htons ( record_len );
+ if ( suite->mac_len ) {
+ tls_hmac ( cipherspec, &authhdr, plaintext, record_len,
+ mac );
+ }
+ if ( is_auth_cipher ( cipher ) ) {
+ cipher_encrypt ( cipher, cipherspec->cipher_ctx,
+ &authhdr, NULL, sizeof ( authhdr ) );
+ }
- /* Calculate padding length */
- plaintext_len = ( len + suite->mac_len );
- if ( is_block_cipher ( cipher ) ) {
- padding_len = ( ( ( cipher->blocksize - 1 ) &
- -( plaintext_len + 1 ) ) + 1 );
- } else {
- padding_len = 0;
- }
- plaintext_len += padding_len;
+ /* Calculate encryption length */
+ encrypt_len = ( record_len + suite->mac_len );
+ if ( is_block_cipher ( cipher ) ) {
+ pad_len = ( ( ( cipher->blocksize - 1 ) &
+ -( encrypt_len + 1 ) ) + 1 );
+ } else {
+ pad_len = 0;
+ }
+ encrypt_len += pad_len;
+
+ /* Add record header */
+ tlshdr = iob_put ( iobuf, sizeof ( *tlshdr ) );
+ tlshdr->type = type;
+ tlshdr->version = htons ( tls->version );
+ tlshdr->length = htons ( sizeof ( iv.rec ) + encrypt_len +
+ cipher->authsize );
+
+ /* Add record initialisation vector, if applicable */
+ memcpy ( iob_put ( iobuf, sizeof ( iv.rec ) ), iv.rec,
+ sizeof ( iv.rec ) );
+
+ /* Copy plaintext data if necessary */
+ ciphertext = iob_put ( iobuf, record_len );
+ assert ( ciphertext <= plaintext );
+ if ( encrypt_len > record_len ) {
+ memmove ( ciphertext, plaintext, record_len );
+ encrypt = ciphertext;
+ } else {
+ encrypt = plaintext;
+ }
- /* Allocate plaintext */
- plaintext = malloc ( plaintext_len );
- if ( ! plaintext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "plaintext\n", tls, plaintext_len );
- rc = -ENOMEM_TX_PLAINTEXT;
- goto err_plaintext;
- }
+ /* Add MAC, if applicable */
+ memcpy ( iob_put ( iobuf, suite->mac_len ), mac,
+ suite->mac_len );
- /* Assemble plaintext */
- tmp = plaintext;
- memcpy ( tmp, data, len );
- tmp += len;
- if ( suite->mac_len )
- tls_hmac ( cipherspec, &authhdr, data, len, mac );
- memcpy ( tmp, mac, suite->mac_len );
- tmp += suite->mac_len;
- memset ( tmp, ( padding_len - 1 ), padding_len );
- tmp += padding_len;
- assert ( tmp == ( plaintext + plaintext_len ) );
- DBGC2 ( tls, "Sending plaintext data:\n" );
- DBGC2_HD ( tls, plaintext, plaintext_len );
+ /* Add padding, if applicable */
+ memset ( iob_put ( iobuf, pad_len ), ( pad_len - 1 ), pad_len );
- /* Set initialisation vector */
- cipher_setiv ( cipher, cipherspec->cipher_ctx, &iv, sizeof ( iv ) );
+ /* Encrypt data and append authentication tag */
+ DBGC2 ( tls, "Sending plaintext data:\n" );
+ DBGC2_HDA ( tls, 0, encrypt, encrypt_len );
+ cipher_encrypt ( cipher, cipherspec->cipher_ctx, encrypt,
+ ciphertext, encrypt_len );
+ cipher_auth ( cipher, cipherspec->cipher_ctx,
+ iob_put ( iobuf, cipher->authsize ) );
- /* Process authentication data, if applicable */
- if ( is_auth_cipher ( cipher ) ) {
- cipher_encrypt ( cipher, cipherspec->cipher_ctx, &authhdr,
- NULL, sizeof ( authhdr ) );
- }
+ /* Move to next record */
+ tls->tx.seq += 1;
+ plaintext += record_len;
+ len -= record_len;
- /* Allocate ciphertext */
- ciphertext_len = ( sizeof ( *tlshdr ) + sizeof ( iv.record ) +
- plaintext_len + cipher->authsize );
- ciphertext = xfer_alloc_iob ( &tls->cipherstream, ciphertext_len );
- if ( ! ciphertext ) {
- DBGC ( tls, "TLS %p could not allocate %zd bytes for "
- "ciphertext\n", tls, ciphertext_len );
- rc = -ENOMEM_TX_CIPHERTEXT;
- goto err_ciphertext;
- }
-
- /* Assemble ciphertext */
- tlshdr = iob_put ( ciphertext, sizeof ( *tlshdr ) );
- tlshdr->type = type;
- tlshdr->version = htons ( tls->version );
- tlshdr->length = htons ( ciphertext_len - sizeof ( *tlshdr ) );
- memcpy ( iob_put ( ciphertext, sizeof ( iv.record ) ), iv.record,
- sizeof ( iv.record ) );
- cipher_encrypt ( cipher, cipherspec->cipher_ctx, plaintext,
- iob_put ( ciphertext, plaintext_len ), plaintext_len );
- cipher_auth ( cipher, cipherspec->cipher_ctx,
- iob_put ( ciphertext, cipher->authsize ) );
- assert ( iob_len ( ciphertext ) == ciphertext_len );
-
- /* Free plaintext as soon as possible to conserve memory */
- free ( plaintext );
- plaintext = NULL;
+ } while ( len );
/* Send ciphertext */
if ( ( rc = xfer_deliver_iob ( &tls->cipherstream,
- iob_disown ( ciphertext ) ) ) != 0 ) {
+ iob_disown ( iobuf ) ) ) != 0 ) {
DBGC ( tls, "TLS %p could not deliver ciphertext: %s\n",
tls, strerror ( rc ) );
goto err_deliver;
}
- /* Update TX state machine to next record */
- tls->tx_seq += 1;
-
- assert ( plaintext == NULL );
- assert ( ciphertext == NULL );
+ assert ( iobuf == NULL );
return 0;
err_deliver:
- free_iob ( ciphertext );
- err_ciphertext:
- free ( plaintext );
- err_plaintext:
err_random:
+ free_iob ( iobuf );
return rc;
}
/**
+ * Send plaintext record
+ *
+ * @v tls TLS connection
+ * @v type Record type
+ * @v data Plaintext record
+ * @v len Length of plaintext record
+ * @ret rc Return status code
+ */
+static int tls_send_plaintext ( struct tls_connection *tls, unsigned int type,
+ const void *data, size_t len ) {
+ struct io_buffer *iobuf;
+ int rc;
+
+ /* Allocate I/O buffer */
+ iobuf = tls_alloc_iob ( tls, len );
+ if ( ! iobuf )
+ return -ENOMEM_TX_PLAINTEXT;
+ memcpy ( iob_put ( iobuf, len ), data, len );
+
+ /* Transmit I/O buffer */
+ if ( ( rc = tls_send_record ( tls, type, iob_disown ( iobuf ) ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
* Verify block padding
*
* @v tls TLS connection
@@ -3107,7 +3257,7 @@ static int tls_verify_padding ( struct tls_connection *tls,
static int tls_new_ciphertext ( struct tls_connection *tls,
struct tls_header *tlshdr,
struct list_head *rx_data ) {
- struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ struct tls_cipherspec *cipherspec = &tls->rx.cipherspec.active;
struct tls_cipher_suite *suite = cipherspec->suite;
struct cipher_algorithm *cipher = suite->cipher;
struct digest_algorithm *digest = suite->digest;
@@ -3156,7 +3306,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
auth = last->tail;
/* Construct authentication data */
- authhdr.seq = cpu_to_be64 ( tls->rx_seq );
+ authhdr.seq = cpu_to_be64 ( tls->rx.seq );
authhdr.header.type = tlshdr->type;
authhdr.header.version = tlshdr->version;
authhdr.header.length = htons ( len );
@@ -3172,7 +3322,7 @@ static int tls_new_ciphertext ( struct tls_connection *tls,
/* Decrypt the received data */
check_len = 0;
- list_for_each_entry ( iobuf, &tls->rx_data, list ) {
+ list_for_each_entry ( iobuf, &tls->rx.data, list ) {
cipher_decrypt ( cipher, cipherspec->cipher_ctx,
iobuf->data, iobuf->data, iob_len ( iobuf ) );
check_len += iob_len ( iobuf );
@@ -3278,8 +3428,9 @@ static int tls_plainstream_deliver ( struct tls_connection *tls,
goto done;
}
- if ( ( rc = tls_send_plaintext ( tls, TLS_TYPE_DATA, iobuf->data,
- iob_len ( iobuf ) ) ) != 0 )
+ /* Send data record */
+ if ( ( rc = tls_send_record ( tls, TLS_TYPE_DATA,
+ iob_disown ( iobuf ) ) ) != 0 )
goto done;
done:
@@ -3298,8 +3449,8 @@ static int tls_progress ( struct tls_connection *tls,
struct job_progress *progress ) {
/* Return cipherstream or validator progress as applicable */
- if ( is_pending ( &tls->validation ) ) {
- return job_progress ( &tls->validator, progress );
+ if ( is_pending ( &tls->server.validation ) ) {
+ return job_progress ( &tls->server.validator, progress );
} else {
return job_progress ( &tls->cipherstream, progress );
}
@@ -3307,6 +3458,7 @@ static int tls_progress ( struct tls_connection *tls,
/** TLS plaintext stream interface operations */
static struct interface_operation tls_plainstream_ops[] = {
+ INTF_OP ( xfer_alloc_iob, struct tls_connection *, tls_alloc_iob ),
INTF_OP ( xfer_deliver, struct tls_connection *,
tls_plainstream_deliver ),
INTF_OP ( xfer_window, struct tls_connection *,
@@ -3334,10 +3486,10 @@ static struct interface_descriptor tls_plainstream_desc =
* @ret rc Returned status code
*/
static int tls_newdata_process_header ( struct tls_connection *tls ) {
- struct tls_cipherspec *cipherspec = &tls->rx_cipherspec;
+ struct tls_cipherspec *cipherspec = &tls->rx.cipherspec.active;
struct cipher_algorithm *cipher = cipherspec->suite->cipher;
size_t iv_len = cipherspec->suite->record_iv_len;
- size_t data_len = ntohs ( tls->rx_header.length );
+ size_t data_len = ntohs ( tls->rx.header.length );
size_t remaining = data_len;
size_t frag_len;
size_t reserve;
@@ -3353,7 +3505,7 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) {
remaining += reserve;
/* Allocate data buffers now that we know the length */
- assert ( list_empty ( &tls->rx_data ) );
+ assert ( list_empty ( &tls->rx.data ) );
while ( remaining ) {
/* Calculate fragment length. Ensure that no block is
@@ -3394,16 +3546,16 @@ static int tls_newdata_process_header ( struct tls_connection *tls ) {
reserve = 0;
/* Add I/O buffer to list */
- list_add_tail ( &iobuf->list, &tls->rx_data );
+ list_add_tail ( &iobuf->list, &tls->rx.data );
}
/* Move to data state */
- tls->rx_state = TLS_RX_DATA;
+ tls->rx.state = TLS_RX_DATA;
return 0;
err:
- list_for_each_entry_safe ( iobuf, tmp, &tls->rx_data, list ) {
+ list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
}
@@ -3421,27 +3573,27 @@ static int tls_newdata_process_data ( struct tls_connection *tls ) {
int rc;
/* Move current buffer to end of list */
- iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list );
+ iobuf = list_first_entry ( &tls->rx.data, struct io_buffer, list );
list_del ( &iobuf->list );
- list_add_tail ( &iobuf->list, &tls->rx_data );
+ list_add_tail ( &iobuf->list, &tls->rx.data );
/* Continue receiving data if any space remains */
- iobuf = list_first_entry ( &tls->rx_data, struct io_buffer, list );
+ iobuf = list_first_entry ( &tls->rx.data, struct io_buffer, list );
if ( iob_tailroom ( iobuf ) )
return 0;
/* Process record */
- if ( ( rc = tls_new_ciphertext ( tls, &tls->rx_header,
- &tls->rx_data ) ) != 0 )
+ if ( ( rc = tls_new_ciphertext ( tls, &tls->rx.header,
+ &tls->rx.data ) ) != 0 )
return rc;
/* Increment RX sequence number */
- tls->rx_seq += 1;
+ tls->rx.seq += 1;
/* Return to header state */
- assert ( list_empty ( &tls->rx_data ) );
- tls->rx_state = TLS_RX_HEADER;
- iob_unput ( &tls->rx_header_iobuf, sizeof ( tls->rx_header ) );
+ assert ( list_empty ( &tls->rx.data ) );
+ tls->rx.state = TLS_RX_HEADER;
+ iob_unput ( &tls->rx.iobuf, sizeof ( tls->rx.header ) );
return 0;
}
@@ -3480,13 +3632,13 @@ static int tls_cipherstream_deliver ( struct tls_connection *tls,
while ( iob_len ( iobuf ) ) {
/* Select buffer according to current state */
- switch ( tls->rx_state ) {
+ switch ( tls->rx.state ) {
case TLS_RX_HEADER:
- dest = &tls->rx_header_iobuf;
+ dest = &tls->rx.iobuf;
process = tls_newdata_process_header;
break;
case TLS_RX_DATA:
- dest = list_first_entry ( &tls->rx_data,
+ dest = list_first_entry ( &tls->rx.data,
struct io_buffer, list );
assert ( dest != NULL );
process = tls_newdata_process_data;
@@ -3550,15 +3702,13 @@ static struct interface_descriptor tls_cipherstream_desc =
*/
static void tls_validator_done ( struct tls_connection *tls, int rc ) {
struct tls_session *session = tls->session;
- struct tls_cipherspec *cipherspec = &tls->tx_cipherspec_pending;
- struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
struct x509_certificate *cert;
/* Mark validation as complete */
- pending_put ( &tls->validation );
+ pending_put ( &tls->server.validation );
/* Close validator interface */
- intf_restart ( &tls->validator, rc );
+ intf_restart ( &tls->server.validator, rc );
/* Check for validation failure */
if ( rc != 0 ) {
@@ -3569,7 +3719,7 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) {
DBGC ( tls, "TLS %p certificate validation succeeded\n", tls );
/* Extract first certificate */
- cert = x509_first ( tls->chain );
+ cert = x509_first ( tls->server.chain );
assert ( cert != NULL );
/* Verify server name */
@@ -3579,22 +3729,18 @@ static void tls_validator_done ( struct tls_connection *tls, int rc ) {
goto err;
}
- /* Initialise public key algorithm */
- if ( ( rc = pubkey_init ( pubkey, cipherspec->pubkey_ctx,
- cert->subject.public_key.raw.data,
- cert->subject.public_key.raw.len ) ) != 0 ) {
- DBGC ( tls, "TLS %p cannot initialise public key: %s\n",
- tls, strerror ( rc ) );
- goto err;
- }
+ /* Extract the now trusted server public key */
+ memcpy ( &tls->server.key, &cert->subject.public_key.raw,
+ sizeof ( tls->server.key ) );
- /* Schedule Client Key Exchange, Change Cipher, and Finished */
- tls->tx_pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
+ /* Schedule transmission of applicable handshake messages */
+ tls->tx.pending |= ( TLS_TX_CLIENT_KEY_EXCHANGE |
TLS_TX_CHANGE_CIPHER |
TLS_TX_FINISHED );
- if ( tls->certs ) {
- tls->tx_pending |= ( TLS_TX_CERTIFICATE |
- TLS_TX_CERTIFICATE_VERIFY );
+ if ( tls->client.chain ) {
+ tls->tx.pending |= TLS_TX_CERTIFICATE;
+ if ( ! list_empty ( &tls->client.chain->links ) )
+ tls->tx.pending |= TLS_TX_CERTIFICATE_VERIFY;
}
tls_tx_resume ( tls );
@@ -3612,7 +3758,8 @@ static struct interface_operation tls_validator_ops[] = {
/** TLS certificate validator interface descriptor */
static struct interface_descriptor tls_validator_desc =
- INTF_DESC ( struct tls_connection, validator, tls_validator_ops );
+ INTF_DESC ( struct tls_connection, server.validator,
+ tls_validator_ops );
/******************************************************************************
*
@@ -3636,7 +3783,7 @@ static void tls_tx_step ( struct tls_connection *tls ) {
return;
/* Send first pending transmission */
- if ( tls->tx_pending & TLS_TX_CLIENT_HELLO ) {
+ if ( tls->tx.pending & TLS_TX_CLIENT_HELLO ) {
/* Serialise server negotiations within a session, to
* provide a consistent view of session IDs and
* session tickets.
@@ -3644,7 +3791,7 @@ static void tls_tx_step ( struct tls_connection *tls ) {
list_for_each_entry ( conn, &session->conn, list ) {
if ( conn == tls )
break;
- if ( is_pending ( &conn->server_negotiation ) )
+ if ( is_pending ( &conn->server.negotiation ) )
return;
}
/* Record or generate session ID and associated master secret */
@@ -3658,8 +3805,8 @@ static void tls_tx_step ( struct tls_connection *tls ) {
} else {
/* No existing session: use a random session ID */
assert ( sizeof ( tls->session_id ) ==
- sizeof ( tls->client_random ) );
- memcpy ( tls->session_id, &tls->client_random,
+ sizeof ( tls->client.random ) );
+ memcpy ( tls->session_id, &tls->client.random,
sizeof ( tls->session_id ) );
tls->session_id_len = sizeof ( tls->session_id );
}
@@ -3669,32 +3816,32 @@ static void tls_tx_step ( struct tls_connection *tls ) {
tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CLIENT_HELLO;
- } else if ( tls->tx_pending & TLS_TX_CERTIFICATE ) {
+ tls->tx.pending &= ~TLS_TX_CLIENT_HELLO;
+ } else if ( tls->tx.pending & TLS_TX_CERTIFICATE ) {
/* Send Certificate */
if ( ( rc = tls_send_certificate ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p cold not send Certificate: %s\n",
tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CERTIFICATE;
- } else if ( tls->tx_pending & TLS_TX_CLIENT_KEY_EXCHANGE ) {
+ tls->tx.pending &= ~TLS_TX_CERTIFICATE;
+ } else if ( tls->tx.pending & TLS_TX_CLIENT_KEY_EXCHANGE ) {
/* Send Client Key Exchange */
if ( ( rc = tls_send_client_key_exchange ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Client Key "
"Exchange: %s\n", tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE;
- } else if ( tls->tx_pending & TLS_TX_CERTIFICATE_VERIFY ) {
+ tls->tx.pending &= ~TLS_TX_CLIENT_KEY_EXCHANGE;
+ } else if ( tls->tx.pending & TLS_TX_CERTIFICATE_VERIFY ) {
/* Send Certificate Verify */
if ( ( rc = tls_send_certificate_verify ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Certificate "
"Verify: %s\n", tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_CERTIFICATE_VERIFY;
- } else if ( tls->tx_pending & TLS_TX_CHANGE_CIPHER ) {
+ tls->tx.pending &= ~TLS_TX_CERTIFICATE_VERIFY;
+ } else if ( tls->tx.pending & TLS_TX_CHANGE_CIPHER ) {
/* Send Change Cipher, and then change the cipher in use */
if ( ( rc = tls_send_change_cipher ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Change Cipher: "
@@ -3702,28 +3849,27 @@ static void tls_tx_step ( struct tls_connection *tls ) {
goto err;
}
if ( ( rc = tls_change_cipher ( tls,
- &tls->tx_cipherspec_pending,
- &tls->tx_cipherspec )) != 0 ){
+ &tls->tx.cipherspec ) ) != 0 ){
DBGC ( tls, "TLS %p could not activate TX cipher: "
"%s\n", tls, strerror ( rc ) );
goto err;
}
- tls->tx_seq = 0;
- tls->tx_pending &= ~TLS_TX_CHANGE_CIPHER;
- } else if ( tls->tx_pending & TLS_TX_FINISHED ) {
+ tls->tx.seq = 0;
+ tls->tx.pending &= ~TLS_TX_CHANGE_CIPHER;
+ } else if ( tls->tx.pending & TLS_TX_FINISHED ) {
/* Send Finished */
if ( ( rc = tls_send_finished ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not send Finished: %s\n",
tls, strerror ( rc ) );
goto err;
}
- tls->tx_pending &= ~TLS_TX_FINISHED;
+ tls->tx.pending &= ~TLS_TX_FINISHED;
}
/* Reschedule process if pending transmissions remain,
* otherwise send notification of a window change.
*/
- if ( tls->tx_pending ) {
+ if ( tls->tx.pending ) {
tls_tx_resume ( tls );
} else {
xfer_window_changed ( &tls->plainstream );
@@ -3737,7 +3883,7 @@ static void tls_tx_step ( struct tls_connection *tls ) {
/** TLS TX process descriptor */
static struct process_descriptor tls_process_desc =
- PROC_DESC_ONCE ( struct tls_connection, process, tls_tx_step );
+ PROC_DESC_ONCE ( struct tls_connection, tx.process, tls_tx_step );
/******************************************************************************
*
@@ -3761,8 +3907,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) {
/* Find existing matching session, if any */
list_for_each_entry ( session, &tls_sessions, list ) {
if ( ( strcmp ( name, session->name ) == 0 ) &&
- ( tls->root == session->root ) &&
- ( tls->key == session->key ) ) {
+ ( tls->server.root == session->root ) &&
+ ( tls->client.key == session->key ) ) {
ref_get ( &session->refcnt );
tls->session = session;
DBGC ( tls, "TLS %p joining session %s\n", tls, name );
@@ -3781,8 +3927,8 @@ static int tls_session ( struct tls_connection *tls, const char *name ) {
name_copy = ( ( ( void * ) session ) + sizeof ( *session ) );
strcpy ( name_copy, name );
session->name = name_copy;
- session->root = x509_root_get ( tls->root );
- session->key = privkey_get ( tls->key );
+ session->root = x509_root_get ( tls->server.root );
+ session->key = privkey_get ( tls->client.key );
INIT_LIST_HEAD ( &session->conn );
list_add ( &session->list, &tls_sessions );
@@ -3829,23 +3975,23 @@ int add_tls ( struct interface *xfer, const char *name,
INIT_LIST_HEAD ( &tls->list );
intf_init ( &tls->plainstream, &tls_plainstream_desc, &tls->refcnt );
intf_init ( &tls->cipherstream, &tls_cipherstream_desc, &tls->refcnt );
- intf_init ( &tls->validator, &tls_validator_desc, &tls->refcnt );
- process_init_stopped ( &tls->process, &tls_process_desc,
+ intf_init ( &tls->server.validator, &tls_validator_desc, &tls->refcnt );
+ process_init_stopped ( &tls->tx.process, &tls_process_desc,
&tls->refcnt );
- tls->key = privkey_get ( key ? key : &private_key );
- tls->root = x509_root_get ( root ? root : &root_certificates );
+ tls->client.key = privkey_get ( key ? key : &private_key );
+ tls->server.root = x509_root_get ( root ? root : &root_certificates );
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 );
- tls_clear_cipher ( tls, &tls->rx_cipherspec_pending );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->tx.cipherspec.pending );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.active );
+ tls_clear_cipher ( tls, &tls->rx.cipherspec.pending );
tls_clear_handshake ( tls );
- tls->client_random.gmt_unix_time = time ( NULL );
- iob_populate ( &tls->rx_header_iobuf, &tls->rx_header, 0,
- sizeof ( tls->rx_header ) );
- INIT_LIST_HEAD ( &tls->rx_data );
- if ( ( rc = tls_generate_random ( tls, &tls->client_random.random,
- ( sizeof ( tls->client_random.random ) ) ) ) != 0 ) {
+ tls->client.random.gmt_unix_time = time ( NULL );
+ iob_populate ( &tls->rx.iobuf, &tls->rx.header, 0,
+ sizeof ( tls->rx.header ) );
+ INIT_LIST_HEAD ( &tls->rx.data );
+ if ( ( rc = tls_generate_random ( tls, &tls->client.random.random,
+ ( sizeof ( tls->client.random.random ) ) ) ) != 0 ) {
goto err_random;
}
if ( ( rc = tls_session ( tls, name ) ) != 0 )