diff options
Diffstat (limited to 'src/libtls/tls_peer.c')
-rw-r--r-- | src/libtls/tls_peer.c | 169 |
1 files changed, 109 insertions, 60 deletions
diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c index 621f1729d..6091702cf 100644 --- a/src/libtls/tls_peer.c +++ b/src/libtls/tls_peer.c @@ -36,7 +36,7 @@ typedef enum { STATE_CIPHERSPEC_CHANGED_OUT, STATE_FINISHED_SENT, STATE_CIPHERSPEC_CHANGED_IN, - STATE_COMPLETE, + STATE_FINISHED_RECEIVED, } peer_state_t; /** @@ -110,6 +110,16 @@ struct private_tls_peer_t { diffie_hellman_t *dh; /** + * Resuming a session? + */ + bool resume; + + /** + * TLS session identifier + */ + chunk_t session; + + /** * List of server-supported hashsig algorithms */ chunk_t hashsig; @@ -124,12 +134,12 @@ struct private_tls_peer_t { * Process a server hello message */ static status_t process_server_hello(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { u_int8_t compression; u_int16_t version, cipher; chunk_t random, session, ext = chunk_empty; - tls_cipher_suite_t suite; + tls_cipher_suite_t suite = 0; this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO, reader->peek(reader)); @@ -155,16 +165,34 @@ static status_t process_server_hello(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); return NEED_MORE; } - suite = cipher; - if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) + + if (chunk_equals(this->session, session)) { - DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", - tls_cipher_suite_names, suite); - this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); - return NEED_MORE; + suite = this->crypto->resume_session(this->crypto, session, this->server, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + if (suite) + { + DBG1(DBG_TLS, "resumed %N using suite %N", + tls_version_names, version, tls_cipher_suite_names, suite); + this->resume = TRUE; + } + } + if (!suite) + { + suite = cipher; + if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) + { + DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", + tls_cipher_suite_names, suite); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + DBG1(DBG_TLS, "negotiated %N using suite %N", + tls_version_names, version, tls_cipher_suite_names, suite); + free(this->session.ptr); + this->session = chunk_clone(session); } - DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", - tls_version_names, version, tls_cipher_suite_names, suite); this->state = STATE_HELLO_RECEIVED; return NEED_MORE; } @@ -209,10 +237,10 @@ static bool check_certificate(private_tls_peer_t *this, certificate_t *cert) * Process a Certificate message */ static status_t process_certificate(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { certificate_t *cert; - tls_reader_t *certs; + bio_reader_t *certs; chunk_t data; bool first = TRUE; @@ -225,7 +253,7 @@ static status_t process_certificate(private_tls_peer_t *this, this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - certs = tls_reader_create(data); + certs = bio_reader_create(data); while (certs->remaining(certs)) { if (!certs->read_data24(certs, &data)) @@ -302,7 +330,7 @@ static public_key_t *find_public_key(private_tls_peer_t *this) * Process a Key Exchange message using MODP Diffie Hellman */ static status_t process_modp_key_exchange(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { chunk_t prime, generator, pub, chunk; public_key_t *public; @@ -379,7 +407,7 @@ static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this, * Process a Key Exchange message using EC Diffie Hellman */ static status_t process_ec_key_exchange(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { diffie_hellman_group_t group; public_key_t *public; @@ -466,7 +494,7 @@ static status_t process_ec_key_exchange(private_tls_peer_t *this, * Process a Server Key Exchange */ static status_t process_key_exchange(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { diffie_hellman_group_t group; @@ -491,10 +519,10 @@ static status_t process_key_exchange(private_tls_peer_t *this, /** * Process a Certificate Request message */ -static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) +static status_t process_certreq(private_tls_peer_t *this, bio_reader_t *reader) { chunk_t types, hashsig, data; - tls_reader_t *authorities; + bio_reader_t *authorities; identification_t *id; certificate_t *cert; @@ -529,7 +557,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); return NEED_MORE; } - authorities = tls_reader_create(data); + authorities = bio_reader_create(data); while (authorities->remaining(authorities)) { if (!authorities->read_data16(authorities, &data)) @@ -565,7 +593,7 @@ static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) * Process Hello Done message */ static status_t process_hello_done(private_tls_peer_t *this, - tls_reader_t *reader) + bio_reader_t *reader) { this->crypto->append_handshake(this->crypto, TLS_SERVER_HELLO_DONE, reader->peek(reader)); @@ -576,7 +604,7 @@ static status_t process_hello_done(private_tls_peer_t *this, /** * Process finished message */ -static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader) +static status_t process_finished(private_tls_peer_t *this, bio_reader_t *reader) { chunk_t received; char buf[12]; @@ -599,15 +627,14 @@ static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader) this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); return NEED_MORE; } - this->state = STATE_COMPLETE; - this->crypto->derive_eap_msk(this->crypto, - chunk_from_thing(this->client_random), - chunk_from_thing(this->server_random)); + this->state = STATE_FINISHED_RECEIVED; + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + return NEED_MORE; } METHOD(tls_handshake_t, process, status_t, - private_tls_peer_t *this, tls_handshake_type_t type, tls_reader_t *reader) + private_tls_peer_t *this, tls_handshake_type_t type, bio_reader_t *reader) { tls_handshake_type_t expected; @@ -670,10 +697,10 @@ METHOD(tls_handshake_t, process, status_t, * Send a client hello */ static status_t send_client_hello(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { tls_cipher_suite_t *suites; - tls_writer_t *extensions, *curves = NULL; + bio_writer_t *extensions, *curves = NULL; tls_version_t version; tls_named_curve_t curve; enumerator_t *enumerator; @@ -696,8 +723,9 @@ static status_t send_client_hello(private_tls_peer_t *this, writer->write_uint16(writer, version); writer->write_data(writer, chunk_from_thing(this->client_random)); - /* session identifier => none */ - writer->write_data8(writer, chunk_empty); + /* session identifier */ + this->session = this->crypto->get_session(this->crypto, this->server); + writer->write_data8(writer, this->session); /* add TLS cipher suites */ count = this->crypto->get_cipher_suites(this->crypto, &suites); @@ -711,7 +739,7 @@ static status_t send_client_hello(private_tls_peer_t *this, writer->write_uint8(writer, 1); writer->write_uint8(writer, 0); - extensions = tls_writer_create(32); + extensions = bio_writer_create(32); extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); this->crypto->get_signature_algorithms(this->crypto, extensions); @@ -723,7 +751,7 @@ static status_t send_client_hello(private_tls_peer_t *this, if (!curves) { extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES); - curves = tls_writer_create(16); + curves = bio_writer_create(16); } curves->write_uint16(curves, curve); } @@ -741,11 +769,11 @@ static status_t send_client_hello(private_tls_peer_t *this, } if (this->server->get_type(this->server) == ID_FQDN) { - tls_writer_t *names; + bio_writer_t *names; DBG2(DBG_TLS, "sending Server Name Indication for '%Y'", this->server); - names = tls_writer_create(8); + names = bio_writer_create(8); names->write_uint8(names, TLS_NAME_TYPE_HOST_NAME); names->write_data16(names, this->server->get_encoding(this->server)); names->wrap16(names); @@ -769,7 +797,7 @@ static status_t send_client_hello(private_tls_peer_t *this, static private_key_t *find_private_key(private_tls_peer_t *this) { private_key_t *key = NULL; - tls_reader_t *reader; + bio_reader_t *reader; key_type_t type; u_int8_t cert; @@ -777,7 +805,7 @@ static private_key_t *find_private_key(private_tls_peer_t *this) { return NULL; } - reader = tls_reader_create(this->cert_types); + reader = bio_reader_create(this->cert_types); while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) { switch (cert) @@ -806,12 +834,12 @@ static private_key_t *find_private_key(private_tls_peer_t *this) * Send Certificate */ static status_t send_certificate(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { enumerator_t *enumerator; certificate_t *cert; auth_rule_t rule; - tls_writer_t *certs; + bio_writer_t *certs; chunk_t data; this->private = find_private_key(this); @@ -823,7 +851,7 @@ static status_t send_certificate(private_tls_peer_t *this, } /* generate certificate payload */ - certs = tls_writer_create(256); + certs = bio_writer_create(256); if (this->peer) { cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); @@ -867,7 +895,7 @@ static status_t send_certificate(private_tls_peer_t *this, * Send client key exchange, using premaster encryption */ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { public_key_t *public; rng_t *rng; @@ -886,6 +914,7 @@ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, htoun16(premaster, TLS_1_2); this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), + this->session, this->server, chunk_from_thing(this->client_random), chunk_from_thing(this->server_random)); @@ -919,7 +948,7 @@ static status_t send_key_exchange_encrypt(private_tls_peer_t *this, * Send client key exchange, using DHE exchange */ static status_t send_key_exchange_dhe(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { chunk_t premaster, pub; @@ -930,6 +959,7 @@ static status_t send_key_exchange_dhe(private_tls_peer_t *this, return NEED_MORE; } this->crypto->derive_secrets(this->crypto, premaster, + this->session, this->server, chunk_from_thing(this->client_random), chunk_from_thing(this->server_random)); chunk_clear(&premaster); @@ -957,7 +987,7 @@ static status_t send_key_exchange_dhe(private_tls_peer_t *this, * Send client key exchange, depending on suite */ static status_t send_key_exchange(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { if (this->dh) { @@ -970,7 +1000,7 @@ static status_t send_key_exchange(private_tls_peer_t *this, * Send certificate verify */ static status_t send_certificate_verify(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { if (!this->private || !this->crypto->sign_handshake(this->crypto, this->private, @@ -991,7 +1021,7 @@ static status_t send_certificate_verify(private_tls_peer_t *this, * Send Finished */ static status_t send_finished(private_tls_peer_t *this, - tls_handshake_type_t *type, tls_writer_t *writer) + tls_handshake_type_t *type, bio_writer_t *writer) { char buf[12]; @@ -1011,7 +1041,7 @@ static status_t send_finished(private_tls_peer_t *this, } METHOD(tls_handshake_t, build, status_t, - private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer) + private_tls_peer_t *this, tls_handshake_type_t *type, bio_writer_t *writer) { switch (this->state) { @@ -1042,34 +1072,52 @@ METHOD(tls_handshake_t, build, status_t, } METHOD(tls_handshake_t, cipherspec_changed, bool, - private_tls_peer_t *this) + private_tls_peer_t *this, bool inbound) { - if ((this->peer && this->state == STATE_VERIFY_SENT) || - (!this->peer && this->state == STATE_KEY_EXCHANGE_SENT)) + if (inbound) { - this->crypto->change_cipher(this->crypto, FALSE); - this->state = STATE_CIPHERSPEC_CHANGED_OUT; - return TRUE; + if (this->resume) + { + return this->state == STATE_HELLO_RECEIVED; + } + return this->state == STATE_FINISHED_SENT; + } + else + { + if (this->resume) + { + return this->state == STATE_FINISHED_RECEIVED; + } + if (this->peer) + { + return this->state == STATE_VERIFY_SENT; + } + return this->state == STATE_KEY_EXCHANGE_SENT; } - return FALSE; } -METHOD(tls_handshake_t, change_cipherspec, bool, - private_tls_peer_t *this) +METHOD(tls_handshake_t, change_cipherspec, void, + private_tls_peer_t *this, bool inbound) { - if (this->state == STATE_FINISHED_SENT) + this->crypto->change_cipher(this->crypto, inbound); + if (inbound) { - this->crypto->change_cipher(this->crypto, TRUE); this->state = STATE_CIPHERSPEC_CHANGED_IN; - return TRUE; } - return FALSE; + else + { + this->state = STATE_CIPHERSPEC_CHANGED_OUT; + } } METHOD(tls_handshake_t, finished, bool, private_tls_peer_t *this) { - return this->state == STATE_COMPLETE; + if (this->resume) + { + return this->state == STATE_FINISHED_SENT; + } + return this->state == STATE_FINISHED_RECEIVED; } METHOD(tls_handshake_t, destroy, void, @@ -1081,6 +1129,7 @@ METHOD(tls_handshake_t, destroy, void, this->server_auth->destroy(this->server_auth); free(this->hashsig.ptr); free(this->cert_types.ptr); + free(this->session.ptr); free(this); } |