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_handshake ( struct tls_connection *tls,
+ const void *data, size_t len );
static int tls_send_alert ( struct tls_connection *tls, unsigned int level,
unsigned int description );
static int tls_send_record ( struct tls_connection *tls, unsigned int type,
tls_clear_cipher ( tls, &tls->rx.cipherspec.active );
tls_clear_cipher ( tls, &tls->rx.cipherspec.pending );
tls_clear_digest ( tls );
- free ( tls->server.exchange );
list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) {
list_del ( &iobuf->list );
free_iob ( iobuf );
return NULL;
}
-/******************************************************************************
- *
- * Record handling
- *
- ******************************************************************************
- */
-
/**
- * Resume TX state machine
+ * Verify Diffie-Hellman parameter signature
*
* @v tls TLS connection
+ * @v data Server Key Exchange handshake record
+ * @v len Length of Server Key Exchange handshake record
+ * @v param_len Length of Diffie-Hellman parameters
+ * @ret rc Return status code
*/
-static void tls_tx_resume ( struct tls_connection *tls ) {
- process_add ( &tls->tx.process );
-}
+static int tls_verify_dh_params ( struct tls_connection *tls, const void *data,
+ size_t len, size_t param_len ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
+ struct tls_signature_hash_algorithm *sig_hash;
+ struct x509_certificate *cert;
+ struct pubkey_algorithm *pubkey;
+ struct digest_algorithm *digest;
+ int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 );
+ const struct {
+ uint16_t sig_hash[use_sig_hash];
+ uint16_t signature_len;
+ uint8_t signature[0];
+ } __attribute__ (( packed )) *sig;
+ struct asn1_cursor signature;
+ size_t remaining;
+ int rc;
-/**
- * Resume TX state machine for all connections within a session
- *
- * @v session TLS session
- */
-static void tls_tx_resume_all ( struct tls_session *session ) {
- struct tls_connection *tls;
+ /* Identify server certificate */
+ cert = x509_first ( tls->server.chain );
+ if ( ! cert ) {
+ DBGC ( tls, "TLS %p has no server certificate\n", tls );
+ return -ENOENT_CERT;
+ }
- list_for_each_entry ( tls, &session->conn, list )
- tls_tx_resume ( tls );
+ /* Signature follows parameters */
+ assert ( param_len <= len );
+ sig = ( data + param_len );
+ remaining = ( len - param_len );
+
+ /* Parse signature from ServerKeyExchange */
+ if ( ( sizeof ( *sig ) > remaining ) ||
+ ( ntohs ( sig->signature_len ) > ( remaining -
+ sizeof ( *sig ) ) ) ) {
+ DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
+ tls );
+ DBGC_HDA ( tls, 0, data, len );
+ return -EINVAL_KEY_EXCHANGE;
+ }
+ signature.data = sig->signature;
+ signature.len = ntohs ( sig->signature_len );
+
+ /* Identify signature and hash algorithm */
+ if ( use_sig_hash ) {
+ sig_hash = tls_find_signature_hash ( sig->sig_hash[0] );
+ if ( ! sig_hash ) {
+ DBGC ( tls, "TLS %p unsupported signature hash "
+ "%#04x\n", tls, sig->sig_hash[0] );
+ return -ENOTSUP_SIG_HASH;
+ }
+ pubkey = sig_hash->pubkey;
+ digest = sig_hash->digest;
+ DBGC ( tls, "TLS %p using signature hash %s-%s\n",
+ tls, pubkey->name, digest->name );
+ if ( sig_hash->algorithm !=
+ cert->subject.public_key.algorithm ) {
+ DBGC ( tls, "TLS %p cannot use %s public key\n", tls,
+ cert->subject.public_key.algorithm->name );
+ return -EPERM_KEY_EXCHANGE;
+ }
+ } else {
+ pubkey = cipherspec->suite->pubkey;
+ digest = &md5_sha1_algorithm;
+ }
+
+ /* Verify signature */
+ {
+ 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, data, param_len );
+ digest_final ( digest, ctx, hash );
+
+ /* Verify signature */
+ if ( ( rc = pubkey_verify ( pubkey,
+ &cert->subject.public_key.raw,
+ digest, hash,
+ &signature ) ) != 0 ) {
+ DBGC ( tls, "TLS %p ServerKeyExchange failed "
+ "verification\n", tls );
+ DBGC_HDA ( tls, 0, data, len );
+ return -EPERM_KEY_EXCHANGE;
+ }
+ }
+
+ /* The verified signature indicates the server's intention to
+ * delegate authority to the shared secret key material. The
+ * shared secret is therefore bound to the server's identity.
+ */
+ tls_set_binding ( tls, cert );
+
+ return 0;
}
/**
- * Restart negotiation
+ * Receive new Server Key Exchange record using public key transport
*
* @v tls TLS connection
+ * @v data Server Key Exchange handshake record
+ * @v len Length of Server Key Exchange handshake record
+ * @ret rc Return status code
*/
-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->server.validation ) );
-
- /* Reset ephemeral master secret */
- tls_regenerate_ephemeral_master ( tls );
+static int tls_new_server_key_exchange_pubkey ( struct tls_connection *tls,
+ const void *data, size_t len ){
- /* (Re)start negotiation */
- tls->tx.pending = TLS_TX_CLIENT_HELLO;
- tls_tx_resume ( tls );
- pending_get ( &tls->client.negotiation );
- pending_get ( &tls->server.negotiation );
+ /* Should never be received */
+ DBGC ( tls, "TLS %p received unexpected ServerKeyExchange:\n", tls );
+ DBGC_HDA ( tls, 0, data, len );
+ return -EPROTO;
}
/**
- * Transmit Handshake record
+ * Transmit Client Key Exchange record using public key exchange
*
* @v tls TLS connection
- * @v data Plaintext record
- * @v len Length of plaintext record
* @ret rc Return status code
*/
-static int tls_send_handshake ( struct tls_connection *tls,
- const void *data, size_t len ) {
+static int tls_send_client_key_exchange_pubkey ( struct tls_connection *tls ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
+ struct tls_key_schedule *key = &tls->key;
+ struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
+ struct x509_certificate *cert;
+ struct {
+ uint16_t version;
+ uint8_t random[46];
+ } __attribute__ (( packed )) pre_master_secret;
+ struct asn1_cursor cursor = {
+ .data = &pre_master_secret,
+ .len = sizeof ( pre_master_secret ),
+ };
+ struct asn1_builder builder = { NULL, 0 };
+ int rc;
- /* Send record */
- return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len );
+ /* Generate pre-master secret */
+ pre_master_secret.version = htons ( TLS_VERSION_MAX );
+ tls_ephemeral_label ( tls, "classic pre-master",
+ &pre_master_secret.random,
+ sizeof ( pre_master_secret.random ) );
+ tls_set_kdf_master ( tls, &pre_master_secret,
+ sizeof ( pre_master_secret ) );
+
+ /* Key derivation function secret has been overwritten with a
+ * value that was not derived from its previous value, and so
+ * is no longer bound to the server's identity.
+ */
+ tls_clear_binding ( tls );
+
+ /* Key schedule now contains shared secret key material */
+ key->keyed = 1;
+
+ /* Identify server certificate */
+ cert = x509_first ( tls->server.chain );
+ if ( ! cert ) {
+ DBGC ( tls, "TLS %p has no server certificate\n", tls );
+ rc = -ENOENT_CERT;
+ goto err_cert;
+ }
+
+ /* Encrypt pre-master secret using server's public key */
+ if ( ( rc = pubkey_encrypt ( pubkey, &cert->subject.public_key.raw,
+ &cursor, &builder ) ) != 0 ) {
+ DBGC ( tls, "TLS %p could not encrypt pre-master secret: %s\n",
+ tls, strerror ( 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;
+ }
+ }
+
+ /* Transmit Client Key Exchange record */
+ if ( ( rc = tls_send_handshake ( tls, builder.data,
+ builder.len ) ) != 0 ) {
+ goto err_send;
+ }
+
+ /* Shared secret has now been incorporated into the handshake
+ * digest. It can be decrypted only with access to the
+ * certificate's private key, and has thereby been bound to
+ * the server's identity.
+ */
+ tls_set_binding ( tls, cert );
+
+ err_send:
+ err_prepend:
+ err_encrypt:
+ free ( builder.data );
+ err_cert:
+ return rc;
}
+/** Public key exchange algorithm */
+struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = {
+ .name = "pubkey",
+ .server = tls_new_server_key_exchange_pubkey,
+ .client = tls_send_client_key_exchange_pubkey,
+};
+
/**
- * Digest or transmit Client Hello record
+ * Receive new Server Key Exchange record using DHE key exchange
*
* @v tls TLS connection
- * @v action Action to take on Client Hello record
+ * @v data Server Key Exchange handshake record
+ * @v len Length of Server Key Exchange handshake record
* @ret rc Return status code
*/
-static int tls_client_hello ( struct tls_connection *tls,
- int ( * action ) ( struct tls_connection *tls,
- const void *data,
- size_t len ) ) {
- struct tls_session *session = tls->session;
- size_t name_len = strlen ( session->name );
- struct {
- uint16_t type;
- uint16_t len;
- struct {
- uint16_t len;
- struct {
- uint8_t type;
- uint16_t len;
- uint8_t name[name_len];
- } __attribute__ (( packed )) list[1];
- } __attribute__ (( packed )) data;
- } __attribute__ (( packed )) *server_name_ext;
- struct {
- uint16_t type;
+static int tls_new_server_key_exchange_dhe ( struct tls_connection *tls,
+ const void *data, size_t len ) {
+ struct tls_named_group *group;
+ struct exchange_algorithm *exchange;
+ const struct {
uint16_t len;
- struct {
- uint8_t max;
- } __attribute__ (( packed )) data;
- } __attribute__ (( packed )) *max_fragment_length_ext;
- struct {
- uint16_t type;
- uint16_t len;
- struct {
- uint16_t len;
- uint16_t code[TLS_NUM_SIG_HASH_ALGORITHMS];
- } __attribute__ (( packed )) data;
- } __attribute__ (( packed )) *signature_algorithms_ext;
- struct {
- uint16_t type;
- uint16_t len;
- struct {
- uint8_t len;
- uint8_t data[ tls->secure_renegotiation ?
- sizeof ( tls->verify.client ) :0 ];
- } __attribute__ (( packed )) data;
- } __attribute__ (( packed )) *renegotiation_info_ext;
- struct {
- uint16_t type;
- uint16_t len;
- struct {
- uint8_t data[session->ticket_len];
- } __attribute__ (( packed )) data;
- } __attribute__ (( packed )) *session_ticket_ext;
- struct {
- uint16_t type;
- uint16_t len;
- struct {
- uint16_t len;
- uint16_t code[TLS_NUM_NAMED_GROUPS];
- } __attribute__ (( packed )) data;
- } __attribute__ (( packed )) *named_group_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_group_ext )
- named_group[TLS_NUM_NAMED_GROUPS ? 1 : 0];
- } __attribute__ (( packed )) *extensions;
- struct {
- uint32_t type_length;
- uint16_t version;
- uint8_t random[32];
- uint8_t session_id_len;
- uint8_t session_id[tls->session_id_len];
- uint16_t cipher_suite_len;
- uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
- uint8_t compression_methods_len;
- uint8_t compression_methods[1];
- uint16_t extensions_len;
- typeof ( *extensions ) extensions;
- } __attribute__ (( packed )) hello;
- struct tls_cipher_suite *suite;
- struct tls_signature_hash_algorithm *sighash;
- struct tls_named_group *group;
+ uint8_t data[0];
+ } __attribute__ (( packed )) *dh_val[3];
+ typeof ( dh_val[0] ) dh_p;
+ typeof ( dh_val[1] ) dh_g;
+ typeof ( dh_val[2] ) dh_ys;
+ const void *param;
+ size_t remaining;
+ size_t frag_len;
+ size_t param_len;
unsigned int i;
+ int rc;
- /* Construct record */
- memset ( &hello, 0, sizeof ( hello ) );
- hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
- htonl ( sizeof ( hello ) -
- sizeof ( hello.type_length ) ) );
- hello.version = htons ( TLS_VERSION_MAX );
- 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 ) );
- hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
- i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES )
- hello.cipher_suites[i++] = suite->code;
- hello.compression_methods_len = sizeof ( hello.compression_methods );
- hello.extensions_len = htons ( sizeof ( hello.extensions ) );
- extensions = &hello.extensions;
-
- /* Construct server name extension */
- server_name_ext = &extensions->server_name;
- server_name_ext->type = htons ( TLS_SERVER_NAME );
- server_name_ext->len = htons ( sizeof ( server_name_ext->data ) );
- server_name_ext->data.len
- = htons ( sizeof ( server_name_ext->data.list ) );
- server_name_ext->data.list[0].type = TLS_SERVER_NAME_HOST_NAME;
- server_name_ext->data.list[0].len
- = htons ( sizeof ( server_name_ext->data.list[0].name ) );
- memcpy ( server_name_ext->data.list[0].name, session->name,
- sizeof ( server_name_ext->data.list[0].name ) );
-
- /* Construct maximum fragment length extension */
- max_fragment_length_ext = &extensions->max_fragment_length;
- 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_VALUE;
-
- /* Construct supported signature algorithms extension */
- signature_algorithms_ext = &extensions->signature_algorithms;
- signature_algorithms_ext->type = htons ( TLS_SIGNATURE_ALGORITHMS );
- signature_algorithms_ext->len
- = htons ( sizeof ( signature_algorithms_ext->data ) );
- signature_algorithms_ext->data.len
- = htons ( sizeof ( signature_algorithms_ext->data.code ) );
- i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS )
- signature_algorithms_ext->data.code[i++] = sighash->code;
-
- /* Construct renegotiation information extension */
- renegotiation_info_ext = &extensions->renegotiation_info;
- renegotiation_info_ext->type = htons ( TLS_RENEGOTIATION_INFO );
- renegotiation_info_ext->len
- = htons ( sizeof ( renegotiation_info_ext->data ) );
- renegotiation_info_ext->data.len
- = sizeof ( renegotiation_info_ext->data.data );
- memcpy ( renegotiation_info_ext->data.data, tls->verify.client,
- sizeof ( renegotiation_info_ext->data.data ) );
-
- /* Construct session ticket extension */
- session_ticket_ext = &extensions->session_ticket;
- session_ticket_ext->type = htons ( TLS_SESSION_TICKET );
- session_ticket_ext->len
- = htons ( sizeof ( session_ticket_ext->data ) );
- 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 groups extension, if applicable */
- if ( sizeof ( extensions->named_group ) ) {
- named_group_ext = &extensions->named_group[0];
- named_group_ext->type = htons ( TLS_NAMED_GROUP );
- named_group_ext->len
- = htons ( sizeof ( named_group_ext->data ) );
- named_group_ext->data.len
- = htons ( sizeof ( named_group_ext->data.code ) );
- i = 0 ; for_each_table_entry ( group, TLS_NAMED_GROUPS ) {
- if ( group->code )
- named_group_ext->data.code[i++] = group->code;
+ /* Parse ServerKeyExchange */
+ param = data;
+ remaining = len;
+ for ( i = 0 ; i < ( sizeof ( dh_val ) / sizeof ( dh_val[0] ) ) ; i++ ){
+ dh_val[i] = param;
+ 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, data, len );
+ return -EINVAL_KEY_EXCHANGE;
}
- assert ( i == TLS_NUM_NAMED_GROUPS );
+ frag_len = ( sizeof ( *dh_val[i] ) + ntohs ( dh_val[i]->len ));
+ param += frag_len;
+ remaining -= frag_len;
}
+ param_len = ( len - remaining );
- return action ( tls, &hello, sizeof ( hello ) );
-}
+ /* Identify named group */
+ dh_p = dh_val[0];
+ dh_g = dh_val[1];
+ dh_ys = dh_val[2];
+ group = tls_find_param_group ( dh_p->data, ntohs ( dh_p->len ),
+ dh_g->data, ntohs ( dh_g->len ) );
+ if ( ! group ) {
+ DBGC ( tls, "TLS %p unsupported %d-bit group:\n",
+ tls, ( 8 * ntohs ( dh_p->len ) ) );
+ DBGC_HDA ( tls, 0, data, len );
+ return -ENOTSUP_GROUP;
+ }
+ tls->key.group = group;
+ exchange = group->exchange;
+ DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name );
-/**
- * Transmit Client Hello record
- *
- * @v tls TLS connection
- * @ret rc Return status code
- */
-static int tls_send_client_hello ( struct tls_connection *tls ) {
+ /* Generate pre-master secret */
+ if ( ( rc = tls_agree_ephemeral ( tls, dh_ys->data,
+ ntohs ( dh_ys->len ), 1 ) ) != 0 ) {
+ return rc;
+ }
- return tls_client_hello ( tls, tls_send_handshake );
+ /* Verify parameter signature */
+ if ( ( rc = tls_verify_dh_params ( tls, data, len, param_len ) ) != 0 )
+ return rc;
+
+ return 0;
}
/**
- * Transmit Certificate record
+ * Transmit Client Key Exchange record using DHE key exchange
*
* @v tls TLS connection
* @ret rc Return status code
*/
-static int tls_send_certificate ( struct tls_connection *tls ) {
- struct {
- tls24_t length;
- uint8_t data[0];
- } __attribute__ (( packed )) *certificate;
+static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
+ struct tls_key_schedule *key = &tls->key;
+ struct tls_named_group *group = key->group;
+ struct exchange_algorithm *exchange = group->exchange;
+ size_t pubsize = exchange->pubsize;
struct {
uint32_t type_length;
- tls24_t length;
- typeof ( *certificate ) certificates[0];
- } __attribute__ (( packed )) *certificates;
- struct x509_link *link;
- struct x509_certificate *cert;
- struct io_buffer *iobuf;
- size_t len;
+ uint16_t dh_xs_len;
+ uint8_t dh_xs[pubsize];
+ } __attribute__ (( packed )) *key_xchg;
+ int rc;
- /* Calculate length of client certificates */
- len = 0;
- 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",
- tls, x509_name ( cert ) );
+ /* Allocate space */
+ key_xchg = malloc ( sizeof ( *key_xchg ) );
+ if ( ! key_xchg ) {
+ rc = -ENOMEM;
+ goto err_alloc;
}
- /* Allocate storage for Certificate record (which may be too
- * large for the stack).
- */
- iobuf = tls_alloc_iob ( tls, ( sizeof ( *certificates ) + len ) );
- if ( ! iobuf )
- return -ENOMEM_CERTIFICATE;
+ /* 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->dh_xs_len = htons ( sizeof ( key_xchg->dh_xs ) );
+ if ( ( rc = tls_share_ephemeral ( tls, key_xchg->dh_xs ) ) != 0 )
+ goto err_share;
- /* 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 );
- 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 ( iob_put ( iobuf, cert->raw.len ), cert->raw.data,
- cert->raw.len );
+ /* Transmit Client Key Exchange record */
+ if ( ( rc = tls_send_handshake ( tls, key_xchg,
+ sizeof ( *key_xchg ) ) ) !=0 ) {
+ goto err_send_handshake;
}
- /* Transmit record */
- return tls_send_record ( tls, TLS_TYPE_HANDSHAKE,
- iob_disown ( iobuf ) );
+ err_send_handshake:
+ err_share:
+ free ( key_xchg );
+ err_alloc:
+ return rc;
}
-/**
- * Transmit Client Key Exchange record using public key exchange
+/** Ephemeral Diffie-Hellman key exchange algorithm */
+struct tls_key_exchange_algorithm tls_dhe_exchange_algorithm = {
+ .name = "dhe",
+ .server = tls_new_server_key_exchange_dhe,
+ .client = tls_send_client_key_exchange_dhe,
+};
+
+/**
+ * Receive new Server Key Exchange record using ECDHE key exchange
*
* @v tls TLS connection
+ * @v data Server Key Exchange handshake record
+ * @v len Length of Server Key Exchange handshake record
* @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_key_schedule *key = &tls->key;
- struct pubkey_algorithm *pubkey = cipherspec->suite->pubkey;
- struct x509_certificate *cert;
- struct {
- uint16_t version;
- uint8_t random[46];
- } __attribute__ (( packed )) pre_master_secret;
- struct asn1_cursor cursor = {
- .data = &pre_master_secret,
- .len = sizeof ( pre_master_secret ),
- };
- struct asn1_builder builder = { NULL, 0 };
+static int tls_new_server_key_exchange_ecdhe ( struct tls_connection *tls,
+ const void *data, size_t len ) {
+ struct tls_named_group *group;
+ struct exchange_algorithm *exchange;
+ const struct {
+ uint8_t curve_type;
+ uint16_t named_group;
+ uint8_t public_len;
+ uint8_t public[0];
+ } __attribute__ (( packed )) *ecdh = data;
+ size_t param_len;
int rc;
- /* Generate pre-master secret */
- pre_master_secret.version = htons ( TLS_VERSION_MAX );
- tls_ephemeral_label ( tls, "classic pre-master",
- &pre_master_secret.random,
- sizeof ( pre_master_secret.random ) );
- tls_set_kdf_master ( tls, &pre_master_secret,
- sizeof ( pre_master_secret ) );
-
- /* Key derivation function secret has been overwritten with a
- * value that was not derived from its previous value, and so
- * is no longer bound to the server's identity.
- */
- tls_clear_binding ( tls );
-
- /* Key schedule now contains shared secret key material */
- key->keyed = 1;
+ /* Parse ServerKeyExchange record */
+ if ( ( sizeof ( *ecdh ) > len ) ||
+ ( ecdh->public_len > ( len - sizeof ( *ecdh ) ) ) ) {
+ DBGC ( tls, "TLS %p received underlength ServerKeyExchange\n",
+ tls );
+ DBGC_HDA ( tls, 0, data, len );
+ return -EINVAL_KEY_EXCHANGE;
+ }
+ param_len = ( sizeof ( *ecdh ) + ecdh->public_len );
- /* Identify server certificate */
- cert = x509_first ( tls->server.chain );
- if ( ! cert ) {
- DBGC ( tls, "TLS %p has no server certificate\n", tls );
- rc = -ENOENT_CERT;
- goto err_cert;
+ /* Identify named group */
+ 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, data, len );
+ return -ENOTSUP_GROUP;
+ }
+ group = tls_find_named_group ( ecdh->named_group );
+ if ( ! group ) {
+ DBGC ( tls, "TLS %p unsupported named group %d\n",
+ tls, ntohs ( ecdh->named_group ) );
+ DBGC_HDA ( tls, 0, data, len );
+ return -ENOTSUP_GROUP;
}
+ tls->key.group = group;
+ exchange = group->exchange;
+ DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name );
- /* Encrypt pre-master secret using server's public key */
- if ( ( rc = pubkey_encrypt ( pubkey, &cert->subject.public_key.raw,
- &cursor, &builder ) ) != 0 ) {
- DBGC ( tls, "TLS %p could not encrypt pre-master secret: %s\n",
- tls, strerror ( rc ) );
- goto err_encrypt;
+ /* Generate pre-master secret */
+ if ( ( rc = tls_agree_ephemeral ( tls, ecdh->public,
+ ecdh->public_len, 0 ) ) != 0 ) {
+ return rc;
}
- /* Construct Client Key Exchange record */
- {
- struct {
- uint32_t type_length;
- uint16_t encrypted_pre_master_secret_len;
- } __attribute__ (( packed )) header;
+ /* Verify parameter signature */
+ if ( ( rc = tls_verify_dh_params ( tls, data, len, param_len ) ) != 0 )
+ return rc;
- 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 );
+ return 0;
+}
- 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;
- }
- }
+/**
+ * Transmit Client Key Exchange record using ECDHE key exchange
+ *
+ * @v tls TLS connection
+ * @ret rc Return status code
+ */
+static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
+ struct tls_key_schedule *key = &tls->key;
+ struct tls_named_group *group = key->group;
+ struct exchange_algorithm *exchange = group->exchange;
+ size_t pubsize = exchange->pubsize;
+ struct {
+ uint32_t type_length;
+ uint8_t public_len;
+ uint8_t public[pubsize];
+ } __attribute__ (( packed )) key_xchg;
+ int rc;
+
+ /* 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 = sizeof ( key_xchg.public );
+ if ( ( rc = tls_share_ephemeral ( tls, key_xchg.public ) ) != 0 )
+ return rc;
/* Transmit Client Key Exchange record */
- if ( ( rc = tls_send_handshake ( tls, builder.data,
- builder.len ) ) != 0 ) {
- goto err_send;
+ if ( ( rc = tls_send_handshake ( tls, &key_xchg,
+ sizeof ( key_xchg ) ) ) !=0 ) {
+ return rc;
}
- /* Shared secret has now been incorporated into the handshake
- * digest. It can be decrypted only with access to the
- * certificate's private key, and has thereby been bound to
- * the server's identity.
- */
- tls_set_binding ( tls, cert );
+ return 0;
+}
- /* Generate master secret */
- tls_generate_master_secret ( tls );
+/** Ephemeral Elliptic Curve Diffie-Hellman key exchange algorithm */
+struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = {
+ .name = "ecdhe",
+ .server = tls_new_server_key_exchange_ecdhe,
+ .client = tls_send_client_key_exchange_ecdhe,
+};
- err_send:
- err_prepend:
- err_encrypt:
- free ( builder.data );
- err_cert:
- return rc;
+/******************************************************************************
+ *
+ * Record handling
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Resume TX state machine
+ *
+ * @v tls TLS connection
+ */
+static void tls_tx_resume ( struct tls_connection *tls ) {
+ process_add ( &tls->tx.process );
}
-/** Public key exchange algorithm */
-struct tls_key_exchange_algorithm tls_pubkey_exchange_algorithm = {
- .name = "pubkey",
- .exchange = tls_send_client_key_exchange_pubkey,
-};
+/**
+ * Resume TX state machine for all connections within a session
+ *
+ * @v session TLS session
+ */
+static void tls_tx_resume_all ( struct tls_session *session ) {
+ struct tls_connection *tls;
+
+ list_for_each_entry ( tls, &session->conn, list )
+ tls_tx_resume ( tls );
+}
/**
- * Verify Diffie-Hellman parameter signature
+ * Restart negotiation
+ *
+ * @v tls TLS connection
+ */
+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->server.validation ) );
+
+ /* Reset ephemeral master secret */
+ tls_regenerate_ephemeral_master ( tls );
+
+ /* (Re)start negotiation */
+ tls->tx.pending = TLS_TX_CLIENT_HELLO;
+ tls_tx_resume ( tls );
+ pending_get ( &tls->client.negotiation );
+ pending_get ( &tls->server.negotiation );
+}
+
+/**
+ * Transmit Handshake record
*
* @v tls TLS connection
- * @v param_len Diffie-Hellman parameter length
+ * @v data Plaintext record
+ * @v len Length of plaintext record
* @ret rc Return status code
*/
-static int tls_verify_dh_params ( struct tls_connection *tls,
- size_t param_len ) {
- struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
- struct tls_signature_hash_algorithm *sig_hash;
- struct x509_certificate *cert;
- struct pubkey_algorithm *pubkey;
- struct digest_algorithm *digest;
- int use_sig_hash = tls_version ( tls, TLS_VERSION_TLS_1_2 );
- const struct {
- uint16_t sig_hash[use_sig_hash];
- uint16_t signature_len;
- uint8_t signature[0];
- } __attribute__ (( packed )) *sig;
- struct asn1_cursor signature;
- const void *data;
- size_t remaining;
- int rc;
+static int tls_send_handshake ( struct tls_connection *tls,
+ const void *data, size_t len ) {
- /* Identify server certificate */
- cert = x509_first ( tls->server.chain );
- if ( ! cert ) {
- DBGC ( tls, "TLS %p has no server certificate\n", tls );
- return -ENOENT_CERT;
- }
+ /* Send record */
+ return tls_send_plaintext ( tls, TLS_TYPE_HANDSHAKE, data, len );
+}
- /* Signature follows parameters */
- assert ( param_len <= tls->server.exchange_len );
- data = ( tls->server.exchange + param_len );
- remaining = ( tls->server.exchange_len - param_len );
+/**
+ * Digest or transmit Client Hello record
+ *
+ * @v tls TLS connection
+ * @v action Action to take on Client Hello record
+ * @ret rc Return status code
+ */
+static int tls_client_hello ( struct tls_connection *tls,
+ int ( * action ) ( struct tls_connection *tls,
+ const void *data,
+ size_t len ) ) {
+ struct tls_session *session = tls->session;
+ size_t name_len = strlen ( session->name );
+ struct {
+ uint16_t type;
+ uint16_t len;
+ struct {
+ uint16_t len;
+ struct {
+ uint8_t type;
+ uint16_t len;
+ uint8_t name[name_len];
+ } __attribute__ (( packed )) list[1];
+ } __attribute__ (( packed )) data;
+ } __attribute__ (( packed )) *server_name_ext;
+ struct {
+ uint16_t type;
+ uint16_t len;
+ struct {
+ uint8_t max;
+ } __attribute__ (( packed )) data;
+ } __attribute__ (( packed )) *max_fragment_length_ext;
+ struct {
+ uint16_t type;
+ uint16_t len;
+ struct {
+ uint16_t len;
+ uint16_t code[TLS_NUM_SIG_HASH_ALGORITHMS];
+ } __attribute__ (( packed )) data;
+ } __attribute__ (( packed )) *signature_algorithms_ext;
+ struct {
+ uint16_t type;
+ uint16_t len;
+ struct {
+ uint8_t len;
+ uint8_t data[ tls->secure_renegotiation ?
+ sizeof ( tls->verify.client ) :0 ];
+ } __attribute__ (( packed )) data;
+ } __attribute__ (( packed )) *renegotiation_info_ext;
+ struct {
+ uint16_t type;
+ uint16_t len;
+ struct {
+ uint8_t data[session->ticket_len];
+ } __attribute__ (( packed )) data;
+ } __attribute__ (( packed )) *session_ticket_ext;
+ struct {
+ uint16_t type;
+ uint16_t len;
+ struct {
+ uint16_t len;
+ uint16_t code[TLS_NUM_NAMED_GROUPS];
+ } __attribute__ (( packed )) data;
+ } __attribute__ (( packed )) *named_group_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_group_ext )
+ named_group[TLS_NUM_NAMED_GROUPS ? 1 : 0];
+ } __attribute__ (( packed )) *extensions;
+ struct {
+ uint32_t type_length;
+ uint16_t version;
+ uint8_t random[32];
+ uint8_t session_id_len;
+ uint8_t session_id[tls->session_id_len];
+ uint16_t cipher_suite_len;
+ uint16_t cipher_suites[TLS_NUM_CIPHER_SUITES];
+ uint8_t compression_methods_len;
+ uint8_t compression_methods[1];
+ uint16_t extensions_len;
+ typeof ( *extensions ) extensions;
+ } __attribute__ (( packed )) hello;
+ struct tls_cipher_suite *suite;
+ struct tls_signature_hash_algorithm *sighash;
+ struct tls_named_group *group;
+ unsigned int i;
- /* Parse signature from ServerKeyExchange */
- 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.exchange,
- tls->server.exchange_len );
- return -EINVAL_KEY_EXCHANGE;
- }
- signature.data = sig->signature;
- signature.len = ntohs ( sig->signature_len );
+ /* Construct record */
+ memset ( &hello, 0, sizeof ( hello ) );
+ hello.type_length = ( cpu_to_le32 ( TLS_CLIENT_HELLO ) |
+ htonl ( sizeof ( hello ) -
+ sizeof ( hello.type_length ) ) );
+ hello.version = htons ( TLS_VERSION_MAX );
+ 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 ) );
+ hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) );
+ i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES )
+ hello.cipher_suites[i++] = suite->code;
+ hello.compression_methods_len = sizeof ( hello.compression_methods );
+ hello.extensions_len = htons ( sizeof ( hello.extensions ) );
+ extensions = &hello.extensions;
- /* Identify signature and hash algorithm */
- if ( use_sig_hash ) {
- sig_hash = tls_find_signature_hash ( sig->sig_hash[0] );
- if ( ! sig_hash ) {
- DBGC ( tls, "TLS %p unsupported signature hash "
- "%#04x\n", tls, sig->sig_hash[0] );
- return -ENOTSUP_SIG_HASH;
- }
- pubkey = sig_hash->pubkey;
- digest = sig_hash->digest;
- DBGC ( tls, "TLS %p using signature hash %s-%s\n",
- tls, pubkey->name, digest->name );
- if ( sig_hash->algorithm !=
- cert->subject.public_key.algorithm ) {
- DBGC ( tls, "TLS %p cannot use %s public key\n", tls,
- cert->subject.public_key.algorithm->name );
- return -EPERM_KEY_EXCHANGE;
- }
- } else {
- pubkey = cipherspec->suite->pubkey;
- digest = &md5_sha1_algorithm;
- }
+ /* Construct server name extension */
+ server_name_ext = &extensions->server_name;
+ server_name_ext->type = htons ( TLS_SERVER_NAME );
+ server_name_ext->len = htons ( sizeof ( server_name_ext->data ) );
+ server_name_ext->data.len
+ = htons ( sizeof ( server_name_ext->data.list ) );
+ server_name_ext->data.list[0].type = TLS_SERVER_NAME_HOST_NAME;
+ server_name_ext->data.list[0].len
+ = htons ( sizeof ( server_name_ext->data.list[0].name ) );
+ memcpy ( server_name_ext->data.list[0].name, session->name,
+ sizeof ( server_name_ext->data.list[0].name ) );
+
+ /* Construct maximum fragment length extension */
+ max_fragment_length_ext = &extensions->max_fragment_length;
+ 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_VALUE;
- /* Verify signature */
- {
- uint8_t ctx[digest->ctxsize];
- uint8_t hash[digest->digestsize];
+ /* Construct supported signature algorithms extension */
+ signature_algorithms_ext = &extensions->signature_algorithms;
+ signature_algorithms_ext->type = htons ( TLS_SIGNATURE_ALGORITHMS );
+ signature_algorithms_ext->len
+ = htons ( sizeof ( signature_algorithms_ext->data ) );
+ signature_algorithms_ext->data.len
+ = htons ( sizeof ( signature_algorithms_ext->data.code ) );
+ i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS )
+ signature_algorithms_ext->data.code[i++] = sighash->code;
- /* 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.exchange, param_len );
- digest_final ( digest, ctx, hash );
+ /* Construct renegotiation information extension */
+ renegotiation_info_ext = &extensions->renegotiation_info;
+ renegotiation_info_ext->type = htons ( TLS_RENEGOTIATION_INFO );
+ renegotiation_info_ext->len
+ = htons ( sizeof ( renegotiation_info_ext->data ) );
+ renegotiation_info_ext->data.len
+ = sizeof ( renegotiation_info_ext->data.data );
+ memcpy ( renegotiation_info_ext->data.data, tls->verify.client,
+ sizeof ( renegotiation_info_ext->data.data ) );
- /* Verify signature */
- if ( ( rc = pubkey_verify ( pubkey,
- &cert->subject.public_key.raw,
- digest, hash,
- &signature ) ) != 0 ) {
- DBGC ( tls, "TLS %p ServerKeyExchange failed "
- "verification\n", tls );
- DBGC_HDA ( tls, 0, tls->server.exchange,
- tls->server.exchange_len );
- return -EPERM_KEY_EXCHANGE;
+ /* Construct session ticket extension */
+ session_ticket_ext = &extensions->session_ticket;
+ session_ticket_ext->type = htons ( TLS_SESSION_TICKET );
+ session_ticket_ext->len
+ = htons ( sizeof ( session_ticket_ext->data ) );
+ 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 groups extension, if applicable */
+ if ( sizeof ( extensions->named_group ) ) {
+ named_group_ext = &extensions->named_group[0];
+ named_group_ext->type = htons ( TLS_NAMED_GROUP );
+ named_group_ext->len
+ = htons ( sizeof ( named_group_ext->data ) );
+ named_group_ext->data.len
+ = htons ( sizeof ( named_group_ext->data.code ) );
+ i = 0 ; for_each_table_entry ( group, TLS_NAMED_GROUPS ) {
+ if ( group->code )
+ named_group_ext->data.code[i++] = group->code;
}
+ assert ( i == TLS_NUM_NAMED_GROUPS );
}
- /* The verified signature indicates the server's intention to
- * delegate authority to the shared secret key material. The
- * shared secret is therefore bound to the server's identity.
- */
- tls_set_binding ( tls, cert );
-
- return 0;
+ return action ( tls, &hello, sizeof ( hello ) );
}
/**
- * Transmit Client Key Exchange record using DHE key exchange
+ * Transmit Client Hello record
*
* @v tls TLS connection
* @ret rc Return status code
*/
-static int tls_send_client_key_exchange_dhe ( struct tls_connection *tls ) {
- struct tls_named_group *group;
- struct exchange_algorithm *exchange;
- const struct {
- uint16_t len;
- uint8_t data[0];
- } __attribute__ (( packed )) *dh_val[3];
- typeof ( dh_val[0] ) dh_p;
- typeof ( dh_val[1] ) dh_g;
- typeof ( dh_val[2] ) dh_ys;
- const void *data;
- size_t remaining;
- size_t frag_len;
- size_t param_len;
- unsigned int i;
- int rc;
-
- /* Parse ServerKeyExchange */
- 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 ) ||
- ( 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.exchange,
- tls->server.exchange_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;
- }
- param_len = ( tls->server.exchange_len - remaining );
-
- /* Identify named group */
- dh_p = dh_val[0];
- dh_g = dh_val[1];
- dh_ys = dh_val[2];
- group = tls_find_param_group ( dh_p->data, ntohs ( dh_p->len ),
- dh_g->data, ntohs ( dh_g->len ) );
- if ( ! group ) {
- DBGC ( tls, "TLS %p unsupported %d-bit group:\n",
- tls, ( 8 * ntohs ( dh_p->len ) ) );
- DBGC_HDA ( tls, 0, tls->server.exchange,
- tls->server.exchange_len );
- rc = -ENOTSUP_GROUP;
- goto err_group;
- }
- tls->key.group = group;
- exchange = group->exchange;
- DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name );
-
- /* Generate pre-master secret */
- if ( ( rc = tls_agree_ephemeral ( tls, dh_ys->data,
- ntohs ( dh_ys->len ), 1 ) ) != 0 ) {
- goto err_agree;
- }
-
- /* Verify parameter signature */
- if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 )
- goto err_verify;
-
- /* Construct and send ClientKeyExchange record */
- {
- size_t pubsize = exchange->pubsize;
- struct {
- uint32_t type_length;
- uint16_t dh_xs_len;
- uint8_t dh_xs[pubsize];
- } __attribute__ (( packed )) *key_xchg;
-
- /* Allocate space */
- key_xchg = malloc ( sizeof ( *key_xchg ) );
- if ( ! key_xchg ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
-
- /* 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->dh_xs_len = htons ( sizeof ( key_xchg->dh_xs ) );
- if ( ( rc = tls_share_ephemeral ( tls,
- key_xchg->dh_xs ) ) != 0 ) {
- goto err_share;
- }
-
- /* 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 );
+static int tls_send_client_hello ( struct tls_connection *tls ) {
- err_send_handshake:
- err_share:
- free ( key_xchg );
- }
- err_alloc:
- err_verify:
- err_agree:
- err_group:
- err_header:
- return rc;
+ return tls_client_hello ( tls, tls_send_handshake );
}
-/** 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 using ECDHE key exchange
+ * Transmit Certificate record
*
* @v tls TLS connection
* @ret rc Return status code
*/
-static int tls_send_client_key_exchange_ecdhe ( struct tls_connection *tls ) {
- struct tls_named_group *group;
- struct exchange_algorithm *exchange;
- const struct {
- uint8_t curve_type;
- uint16_t named_group;
- uint8_t public_len;
- uint8_t public[0];
- } __attribute__ (( packed )) *ecdh;
- size_t param_len;
- int rc;
-
- /* Parse ServerKeyExchange record */
- 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.exchange,
- tls->server.exchange_len );
- return -EINVAL_KEY_EXCHANGE;
- }
- param_len = ( sizeof ( *ecdh ) + ecdh->public_len );
-
- /* Identify named group */
- 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.exchange,
- tls->server.exchange_len );
- return -ENOTSUP_GROUP;
- }
- group = tls_find_named_group ( ecdh->named_group );
- if ( ! group ) {
- DBGC ( tls, "TLS %p unsupported named group %d\n",
- tls, ntohs ( ecdh->named_group ) );
- DBGC_HDA ( tls, 0, tls->server.exchange,
- tls->server.exchange_len );
- return -ENOTSUP_GROUP;
- }
- tls->key.group = group;
- exchange = group->exchange;
- DBGC ( tls, "TLS %p using named group %s\n", tls, exchange->name );
+static int tls_send_certificate ( struct tls_connection *tls ) {
+ struct {
+ tls24_t length;
+ uint8_t data[0];
+ } __attribute__ (( packed )) *certificate;
+ struct {
+ uint32_t type_length;
+ tls24_t length;
+ typeof ( *certificate ) certificates[0];
+ } __attribute__ (( packed )) *certificates;
+ struct x509_link *link;
+ struct x509_certificate *cert;
+ struct io_buffer *iobuf;
+ size_t len;
- /* Generate pre-master secret */
- if ( ( rc = tls_agree_ephemeral ( tls, ecdh->public,
- ecdh->public_len, 0 ) ) != 0 ) {
- return rc;
+ /* Calculate length of client certificates */
+ len = 0;
+ 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",
+ tls, x509_name ( cert ) );
}
- /* Verify parameter signature */
- if ( ( rc = tls_verify_dh_params ( tls, param_len ) ) != 0 )
- return rc;
-
- /* Construct and send ClientKeyExchange record */
- {
- size_t pubsize = exchange->pubsize;
- struct {
- uint32_t type_length;
- uint8_t public_len;
- uint8_t public[pubsize];
- } __attribute__ (( packed )) key_xchg;
-
- /* 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 = sizeof ( key_xchg.public );
- if ( ( rc = tls_share_ephemeral ( tls,
- key_xchg.public ) ) != 0 ) {
- return rc;
- }
-
- /* Transmit Client Key Exchange record */
- if ( ( rc = tls_send_handshake ( tls, &key_xchg,
- sizeof ( key_xchg ) ) ) !=0){
- return rc;
- }
+ /* Allocate storage for Certificate record (which may be too
+ * large for the stack).
+ */
+ iobuf = tls_alloc_iob ( tls, ( sizeof ( *certificates ) + len ) );
+ if ( ! iobuf )
+ return -ENOMEM_CERTIFICATE;
- /* Generate master secret */
- tls_generate_master_secret ( tls );
+ /* 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 );
+ 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 ( iob_put ( iobuf, cert->raw.len ), cert->raw.data,
+ cert->raw.len );
}
- return 0;
+ /* Transmit record */
+ return tls_send_record ( tls, TLS_TYPE_HANDSHAKE,
+ iob_disown ( iobuf ) );
}
-/** Ephemeral Elliptic Curve Diffie-Hellman key exchange algorithm */
-struct tls_key_exchange_algorithm tls_ecdhe_exchange_algorithm = {
- .name = "ecdhe",
- .exchange = tls_send_client_key_exchange_ecdhe,
-};
-
/**
* Transmit Client Key Exchange record
*
int rc;
/* Transmit Client Key Exchange record via key exchange algorithm */
- if ( ( rc = suite->exchange->exchange ( tls ) ) != 0 ) {
+ if ( ( rc = suite->exchange->client ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not exchange keys: %s\n",
tls, strerror ( rc ) );
return rc;
}
+ /* Generate master secret */
+ tls_generate_master_secret ( tls );
+
/* Generate keys from master secret */
if ( ( rc = tls_generate_keys ( tls ) ) != 0 ) {
DBGC ( tls, "TLS %p could not generate keys: %s\n",
*/
static int tls_new_server_key_exchange ( struct tls_connection *tls,
const void *data, size_t len ) {
+ struct tls_cipherspec *cipherspec = &tls->tx.cipherspec.pending;
+ struct tls_cipher_suite *suite = cipherspec->suite;
+ int rc;
- /* Free any existing server key exchange record */
- free ( tls->server.exchange );
- tls->server.exchange_len = 0;
-
- /* Allocate copy of server key exchange record */
- tls->server.exchange = malloc ( len );
- if ( ! tls->server.exchange )
- 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.exchange, data, len );
- tls->server.exchange_len = len;
+ /* Parse via key exchange algorithm */
+ if ( ( rc = suite->exchange->server ( tls, data, len ) ) != 0 )
+ return rc;
return 0;
}