]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Implemented EAP-TLS server functionality
authorMartin Willi <martin@revosec.ch>
Tue, 9 Feb 2010 17:19:25 +0000 (18:19 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Aug 2010 13:39:25 +0000 (15:39 +0200)
src/charon/plugins/eap_tls/eap_tls.c
src/charon/plugins/eap_tls/tls/tls.c
src/charon/plugins/eap_tls/tls/tls.h
src/charon/plugins/eap_tls/tls/tls_crypto.c
src/charon/plugins/eap_tls/tls/tls_crypto.h
src/charon/plugins/eap_tls/tls/tls_peer.c
src/charon/plugins/eap_tls/tls/tls_server.c

index fddd5073ec97048d20792d5e81328deb6d4e1be3..cf42949703228e2ffc2432954d18a6e548dbb73b 100644 (file)
@@ -335,9 +335,17 @@ METHOD(eap_method_t, process, status_t,
        if (!(pkt->flags & EAP_TLS_START))
        {
                if (data.len == sizeof(eap_tls_packet_t))
-               {       /* ACK to our fragment, send next */
-                       *out = read_buf(this, pkt->identifier);
-                       return NEED_MORE;
+               {
+                       if (this->output.len)
+                       {       /* ACK to our fragment, send next */
+                               *out = read_buf(this, pkt->identifier);
+                               return NEED_MORE;
+                       }
+                       if (this->tls->is_complete(this->tls))
+                       {
+                               return SUCCESS;
+                       }
+                       return FAILED;
                }
                if (!write_buf(this, pkt))
                {
index 39a46e2ceaf81a08204b202c485a49f8bf464bf1..ab03037fae01c9b7107ed4da26f62e7f7b2f38b7 100644 (file)
@@ -144,6 +144,12 @@ METHOD(tls_t, set_version, void,
        this->version = version;
 }
 
+METHOD(tls_t, is_complete, bool,
+       private_tls_t *this)
+{
+       return this->crypto->get_eap_msk(this->crypto).len != 0;
+}
+
 METHOD(tls_t, get_eap_msk, chunk_t,
        private_tls_t *this)
 {
@@ -179,6 +185,7 @@ tls_t *tls_create(bool is_server, identification_t *server,
                        .is_server = _is_server,
                        .get_version = _get_version,
                        .set_version = _set_version,
+                       .is_complete = _is_complete,
                        .get_eap_msk = _get_eap_msk,
                        .destroy = _destroy,
                },
index f40a59a027dd86509c653fef53dee6ba1ad17785..283f591e74e4ed5f1c57a19e0b42aad6a8f84c5e 100644 (file)
@@ -134,6 +134,13 @@ struct tls_t {
         */
        void (*set_version)(tls_t *this, tls_version_t version);
 
+       /**
+        * Check if TLS negotiation completed successfully.
+        *
+        * @return                      TRUE if TLS negotation and authentication complete
+        */
+       bool (*is_complete)(tls_t *this);
+
        /**
         * Get the MSK for EAP-TLS.
         *
index 789b942893cd839423474c394cd90c475eb04993..f8894629fb7196941d200ca8b9f5f30a103b79a9 100644 (file)
@@ -425,42 +425,81 @@ static bool hash_handshake(private_tls_crypto_t *this, chunk_t *hash)
 }
 
 METHOD(tls_crypto_t, sign_handshake, bool,
-       private_tls_crypto_t *this, private_key_t *key, chunk_t *sig)
+       private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer)
 {
+       chunk_t sig, hash;
+
        if (this->tls->get_version(this->tls) >= TLS_1_2)
        {
-               u_int16_t length;
-               u_int8_t hash_alg;
-               u_int8_t sig_alg;
+               /* TODO: use supported algorithms instead of fixed SHA1/RSA */
+               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, &sig))
+               {
+                       return FALSE;
+               }
+               writer->write_uint8(writer, 2);
+               writer->write_uint8(writer, 1);
+               writer->write_data16(writer, sig);
+               free(sig.ptr);
+       }
+       else
+       {
+               if (!hash_handshake(this, &hash))
+               {
+                       return FALSE;
+               }
+               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig))
+               {
+                       free(hash.ptr);
+                       return FALSE;
+               }
+               writer->write_data16(writer, sig);
+               free(hash.ptr);
+               free(sig.ptr);
+       }
+       return TRUE;
+}
+
+METHOD(tls_crypto_t, verify_handshake, bool,
+       private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader)
+{
+       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       {
+               u_int8_t hash, alg;
+               chunk_t sig;
 
-               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
+               if (!reader->read_uint8(reader, &hash) ||
+                       !reader->read_uint8(reader, &alg) ||
+                       !reader->read_data16(reader, &sig))
+               {
+                       DBG1(DBG_IKE, "received invalid Certificate Verify");
+                       return FALSE;
+               }
+               /* TODO: map received hash/sig alg to signature scheme */
+               if (hash != 2 || alg != 1 ||
+                       !key->verify(key, SIGN_RSA_EMSA_PKCS1_SHA1, this->handshake, sig))
                {
                        return FALSE;
                }
-               /* TODO: signature scheme to hashsign algorithm mapping */
-               hash_alg = 2; /* sha1 */
-               sig_alg = 1; /* RSA */
-               length = htons(sig->len);
-               *sig = chunk_cat("cccm", chunk_from_thing(hash_alg),
-                                       chunk_from_thing(sig_alg), chunk_from_thing(length), *sig);
        }
        else
        {
-               u_int16_t length;
-               chunk_t hash;
+               chunk_t sig, hash;
 
+               if (!reader->read_data16(reader, &sig))
+               {
+                       DBG1(DBG_IKE, "received invalid Certificate Verify");
+                       return FALSE;
+               }
                if (!hash_handshake(this, &hash))
                {
                        return FALSE;
                }
-               if (!key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
+               if (!key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig))
                {
                        free(hash.ptr);
                        return FALSE;
                }
                free(hash.ptr);
-               length = htons(sig->len);
-               *sig = chunk_cat("cm", chunk_from_thing(length), *sig);
        }
        return TRUE;
 }
@@ -635,6 +674,7 @@ tls_crypto_t *tls_crypto_create(tls_t *tls)
                        .set_protection = _set_protection,
                        .append_handshake = _append_handshake,
                        .sign_handshake = _sign_handshake,
+                       .verify_handshake = _verify_handshake,
                        .calculate_finished = _calculate_finished,
                        .derive_secrets = _derive_secrets,
                        .change_cipher = _change_cipher,
index 49bc09c9934ea00235c056683b6b082fa506b56d..69b8da7420abc608f87e0391726fcd37dc364a99 100644 (file)
@@ -116,10 +116,21 @@ struct tls_crypto_t {
         * Create a signature of the handshake data using a given private key.
         *
         * @param key                   private key to use for signature
-        * @param sig                   allocated signature
+        * @param writer                TLS writer to write signature to
         * @return                              TRUE if signature create successfully
         */
-       bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, chunk_t *sig);
+       bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key,
+                                                  tls_writer_t *writer);
+
+       /**
+        * Verify the signature over handshake data using a given public key.
+        *
+        * @param key                   public key to verify signature with
+        * @param reader                TLS reader to read signature from
+        * @return                              TRUE if signature valid
+        */
+       bool (*verify_handshake)(tls_crypto_t *this, public_key_t *key,
+                                                        tls_reader_t *reader);
 
        /**
         * Calculate the data of a TLS finished message.
index c52da6dfda469d64d1e91f89d39c597134b8a4ce..21bf77c3616a425f626aa42b8c1469dccb367ce0 100644 (file)
@@ -172,7 +172,7 @@ static status_t process_certificate(private_tls_peer_t *this,
                        if (first)
                        {
                                this->server_auth->add(this->server_auth,
-                                                                          AUTH_RULE_SUBJECT_CERT, cert);
+                                                                          AUTH_HELPER_SUBJECT_CERT, cert);
                                DBG1(DBG_IKE, "received TLS server certificate '%Y'",
                                         cert->get_subject(cert));
                                first = FALSE;
@@ -182,7 +182,7 @@ static status_t process_certificate(private_tls_peer_t *this,
                                DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'",
                                         cert->get_subject(cert));
                                this->server_auth->add(this->server_auth,
-                                                                          AUTH_RULE_IM_CERT, cert);
+                                                                          AUTH_HELPER_IM_CERT, cert);
                        }
                }
                else
@@ -486,6 +486,7 @@ static status_t send_key_exchange(private_tls_peer_t *this,
                DBG1(DBG_IKE, "encrypting TLS premaster secret failed");
                return FAILED;
        }
+
        public->destroy(public);
 
        writer->write_data16(writer, encrypted);
@@ -503,16 +504,12 @@ static status_t send_key_exchange(private_tls_peer_t *this,
 static status_t send_certificate_verify(private_tls_peer_t *this,
                                                        tls_handshake_type_t *type, tls_writer_t *writer)
 {
-       chunk_t signature;
-
        if (!this->private ||
-               !this->crypto->sign_handshake(this->crypto, this->private, &signature))
+               !this->crypto->sign_handshake(this->crypto, this->private, writer))
        {
                DBG1(DBG_IKE, "creating TLS Certificate Verify signature failed");
                return FAILED;
        }
-       writer->write_data(writer, signature);
-       free(signature.ptr);
 
        *type = TLS_CERTIFICATE_VERIFY;
        this->state = STATE_VERIFY_SENT;
index 8d5e016db67de34d55afa5c6d81f5d54e097ce38..ba873c847d74df78e9b31d4a47e19c3fcff2d297 100644 (file)
 
 #include "tls_server.h"
 
+#include <time.h>
+
 #include <daemon.h>
 
 typedef struct private_tls_server_t private_tls_server_t;
 
+
+typedef enum {
+       STATE_INIT,
+       STATE_HELLO_RECEIVED,
+       STATE_HELLO_SENT,
+       STATE_CERT_SENT,
+       STATE_CERTREQ_SENT,
+       STATE_HELLO_DONE,
+       STATE_CERT_RECEIVED,
+       STATE_KEY_EXCHANGE_RECEIVED,
+       STATE_CERT_VERIFY_RECEIVED,
+       STATE_CIPHERSPEC_CHANGED_IN,
+       STATE_FINISHED_RECEIVED,
+       STATE_CIPHERSPEC_CHANGED_OUT,
+       STATE_FINISHED_SENT,
+} server_state_t;
+
 /**
  * Private data of an tls_server_t object.
  */
@@ -48,36 +67,510 @@ struct private_tls_server_t {
         * Peer identity
         */
        identification_t *peer;
+
+       /**
+        * State we are in
+        */
+       server_state_t state;
+
+       /**
+        * Hello random data selected by client
+        */
+       char client_random[32];
+
+       /**
+        * Hello random data selected by server
+        */
+       char server_random[32];
+
+       /**
+        * Auth helper for peer authentication
+        */
+       auth_cfg_t *peer_auth;
+
+       /**
+        * Auth helper for server authentication
+        */
+       auth_cfg_t *server_auth;
+
+       /**
+        * Peer private key
+        */
+       private_key_t *private;
+
+       /**
+        * Selected TLS cipher suite
+        */
+       tls_cipher_suite_t suite;
 };
 
+/**
+ * Process client hello message
+ */
+static status_t process_client_hello(private_tls_server_t *this,
+                                                                        tls_reader_t *reader)
+{
+       u_int16_t version;
+       chunk_t random, session, ciphers, compression, ext = chunk_empty;
+       tls_cipher_suite_t *suites;
+       int count, i;
+
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_CLIENT_HELLO, reader->peek(reader));
+
+       if (!reader->read_uint16(reader, &version) ||
+               !reader->read_data(reader, sizeof(this->client_random), &random) ||
+               !reader->read_data8(reader, &session) ||
+               !reader->read_data16(reader, &ciphers) ||
+               !reader->read_data8(reader, &compression) ||
+               (reader->remaining(reader) && !reader->read_data16(reader, &ext)))
+       {
+               DBG1(DBG_IKE, "received invalid ClientHello");
+               return FAILED;
+       }
+
+       memcpy(this->client_random, random.ptr, sizeof(this->client_random));
+
+       if (version < this->tls->get_version(this->tls))
+       {
+               this->tls->set_version(this->tls, version);
+       }
+       count = ciphers.len / sizeof(u_int16_t);
+       suites = alloca(count * sizeof(tls_cipher_suite_t));
+       for (i = 0; i < count; i++)
+       {
+               suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]);
+       }
+       this->suite = this->crypto->select_cipher_suite(this->crypto, suites, count);
+       if (!this->suite)
+       {
+               DBG1(DBG_IKE, "received cipher suite inacceptable");
+               return FAILED;
+       }
+       this->state = STATE_HELLO_RECEIVED;
+       return NEED_MORE;
+}
+
+/**
+ * Process certificate
+ */
+static status_t process_certificate(private_tls_server_t *this,
+                                                                       tls_reader_t *reader)
+{
+       certificate_t *cert;
+       tls_reader_t *certs;
+       chunk_t data;
+       bool first = TRUE;
+
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_CERTIFICATE, reader->peek(reader));
+
+       if (!reader->read_data24(reader, &data))
+       {
+               return FAILED;
+       }
+       certs = tls_reader_create(data);
+       while (certs->remaining(certs))
+       {
+               if (!certs->read_data24(certs, &data))
+               {
+                       certs->destroy(certs);
+                       return FAILED;
+               }
+               cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+                                                                 BUILD_BLOB_ASN1_DER, data, BUILD_END);
+               if (cert)
+               {
+                       if (first)
+                       {
+                               this->peer_auth->add(this->peer_auth,
+                                                                        AUTH_HELPER_SUBJECT_CERT, cert);
+                               DBG1(DBG_IKE, "received TLS peer certificate '%Y'",
+                                        cert->get_subject(cert));
+                               first = FALSE;
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "received TLS intermediate certificate '%Y'",
+                                        cert->get_subject(cert));
+                               this->peer_auth->add(this->peer_auth, AUTH_HELPER_IM_CERT, cert);
+                       }
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "parsing TLS certificate failed, skipped");
+               }
+       }
+       certs->destroy(certs);
+       this->state = STATE_CERT_RECEIVED;
+       return NEED_MORE;
+}
+
+/**
+ * Process Client Key Exchange
+ */
+static status_t process_key_exchange(private_tls_server_t *this,
+                                                                        tls_reader_t *reader)
+{
+       chunk_t encrypted, premaster;
+
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader));
+
+       if (!reader->read_data16(reader, &encrypted))
+       {
+               DBG1(DBG_IKE, "received invalid Client Key Exchange");
+               return FAILED;
+       }
+
+       if (!this->private ||
+               !this->private->decrypt(this->private, encrypted, &premaster))
+       {
+               DBG1(DBG_IKE, "decrypting Client Key Exchange data failed");
+               return FAILED;
+       }
+       this->crypto->derive_secrets(this->crypto, premaster,
+                                                                chunk_from_thing(this->client_random),
+                                                                chunk_from_thing(this->server_random));
+       chunk_clear(&premaster);
+
+       this->state = STATE_KEY_EXCHANGE_RECEIVED;
+       return NEED_MORE;
+}
+
+/**
+ * Process Certificate verify
+ */
+static status_t process_cert_verify(private_tls_server_t *this,
+                                                                       tls_reader_t *reader)
+{
+       bool verified = FALSE;
+       enumerator_t *enumerator;
+       public_key_t *public;
+       auth_cfg_t *auth;
+       tls_reader_t *sig;
+
+       enumerator = charon->credentials->create_public_enumerator(
+                                       charon->credentials, KEY_ANY, this->peer, this->peer_auth);
+       while (enumerator->enumerate(enumerator, &public, &auth))
+       {
+               sig = tls_reader_create(reader->peek(reader));
+               verified = this->crypto->verify_handshake(this->crypto, public, sig);
+               sig->destroy(sig);
+               if (verified)
+               {
+                       break;
+               }
+               DBG1(DBG_IKE, "signature verification failed, trying another key");
+       }
+       enumerator->destroy(enumerator);
+
+       if (!verified)
+       {
+               DBG1(DBG_IKE, "no trusted certificate found for '%Y' to verify TLS peer",
+                        this->peer);
+               return FAILED;
+       }
+
+       this->crypto->append_handshake(this->crypto,
+                                                                  TLS_CERTIFICATE_VERIFY, reader->peek(reader));
+       this->state = STATE_CERT_VERIFY_RECEIVED;
+       return NEED_MORE;
+}
+
+/**
+ * Process finished message
+ */
+static status_t process_finished(private_tls_server_t *this,
+                                                                tls_reader_t *reader)
+{
+       chunk_t received;
+       char buf[12];
+
+       if (!reader->read_data(reader, sizeof(buf), &received))
+       {
+               DBG1(DBG_IKE, "received client finished too short");
+               return FAILED;
+       }
+       if (!this->crypto->calculate_finished(this->crypto, "client finished", buf))
+       {
+               DBG1(DBG_IKE, "calculating client finished failed");
+               return FAILED;
+       }
+       if (!chunk_equals(received, chunk_from_thing(buf)))
+       {
+               DBG1(DBG_IKE, "received client finished invalid");
+               return FAILED;
+       }
+
+       this->crypto->append_handshake(this->crypto, TLS_FINISHED, received);
+       this->state = STATE_FINISHED_RECEIVED;
+       return NEED_MORE;
+}
 
 METHOD(tls_handshake_t, process, status_t,
        private_tls_server_t *this, tls_handshake_type_t type, tls_reader_t *reader)
 {
+       tls_handshake_type_t expected;
+
+       switch (this->state)
+       {
+               case STATE_INIT:
+                       if (type == TLS_CLIENT_HELLO)
+                       {
+                               return process_client_hello(this, reader);
+                       }
+                       expected = TLS_CLIENT_HELLO;
+                       break;
+               case STATE_HELLO_DONE:
+                       if (type == TLS_CERTIFICATE)
+                       {
+                               return process_certificate(this, reader);
+                       }
+                       expected = TLS_CERTIFICATE;
+                       break;
+               case STATE_CERT_RECEIVED:
+                       if (type == TLS_CLIENT_KEY_EXCHANGE)
+                       {
+                               return process_key_exchange(this, reader);
+                       }
+                       expected = TLS_CLIENT_KEY_EXCHANGE;
+                       break;
+               case STATE_KEY_EXCHANGE_RECEIVED:
+                       if (type == TLS_CERTIFICATE_VERIFY)
+                       {
+                               return process_cert_verify(this, reader);
+                       }
+                       expected = TLS_CERTIFICATE_VERIFY;
+                       break;
+               case STATE_CIPHERSPEC_CHANGED_IN:
+                       if (type == TLS_FINISHED)
+                       {
+                               return process_finished(this, reader);
+                       }
+                       expected = TLS_FINISHED;
+                       break;
+               default:
+                       DBG1(DBG_IKE, "TLS %N not expected in current state",
+                                tls_handshake_type_names, type);
+                       return FAILED;
+       }
+       DBG1(DBG_IKE, "TLS %N expected, but received %N",
+                tls_handshake_type_names, expected, tls_handshake_type_names, type);
+       return FAILED;
+}
+
+/**
+ * Send ServerHello message
+ */
+static status_t send_server_hello(private_tls_server_t *this,
+                                                       tls_handshake_type_t *type, tls_writer_t *writer)
+{
+       rng_t *rng;
+
+       htoun32(&this->server_random, time(NULL));
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng)
+       {
+               return FAILED;
+       }
+       rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4);
+       rng->destroy(rng);
+
+       writer->write_uint16(writer, this->tls->get_version(this->tls));
+       writer->write_data(writer, chunk_from_thing(this->server_random));
+       /* session identifier => none, we don't support session resumption */
+       writer->write_data8(writer, chunk_empty);
+       /* add selected suite */
+       writer->write_uint16(writer, this->suite);
+       /* NULL compression only */
+       writer->write_uint8(writer, 0);
+
+       *type = TLS_SERVER_HELLO;
+       this->state = STATE_HELLO_SENT;
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
+       return NEED_MORE;
+}
+
+/**
+ * Send Certificate
+ */
+static status_t send_certificate(private_tls_server_t *this,
+                                                       tls_handshake_type_t *type, tls_writer_t *writer)
+{
+       enumerator_t *enumerator;
+       certificate_t *cert;
+       auth_rule_t rule;
+       tls_writer_t *certs;
+       chunk_t data;
+
+       this->private = charon->credentials->get_private(charon->credentials,
+                                                                       KEY_ANY, this->server, this->server_auth);
+       if (!this->private)
+       {
+               DBG1(DBG_IKE, "no TLS server certificate found for '%Y'", this->server);
+               return FAILED;
+       }
+
+       /* generate certificate payload */
+       certs = tls_writer_create(256);
+       cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT);
+       if (cert)
+       {
+               DBG1(DBG_IKE, "sending TLS server certificate '%Y'",
+                        cert->get_subject(cert));
+               data = cert->get_encoding(cert);
+               certs->write_data24(certs, data);
+               free(data.ptr);
+       }
+       enumerator = this->server_auth->create_enumerator(this->server_auth);
+       while (enumerator->enumerate(enumerator, &rule, &cert))
+       {
+               if (rule == AUTH_RULE_IM_CERT)
+               {
+                       DBG1(DBG_IKE, "sending TLS intermediate certificate '%Y'",
+                                cert->get_subject(cert));
+                       data = cert->get_encoding(cert);
+                       certs->write_data24(certs, data);
+                       free(data.ptr);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       writer->write_data24(writer, certs->get_buf(certs));
+       certs->destroy(certs);
+
+       *type = TLS_CERTIFICATE;
+       this->state = STATE_CERT_SENT;
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
+       return NEED_MORE;
+}
+
+/**
+ * Send Certificate Request
+ */
+static status_t send_certificate_request(private_tls_server_t *this,
+                                                       tls_handshake_type_t *type, tls_writer_t *writer)
+{
+       tls_writer_t *authorities;
+       enumerator_t *enumerator;
+       certificate_t *cert;
+       identification_t *id;
+
+       /* currently only RSA signatures are supported */
+       writer->write_data8(writer, chunk_from_chars(1));
+       if (this->tls->get_version(this->tls) >= TLS_1_2)
+       {
+               /* enforce RSA with SHA1 signatures */
+               writer->write_data16(writer, chunk_from_chars(2, 1));
+       }
+
+       authorities = tls_writer_create(64);
+       enumerator = charon->credentials->create_cert_enumerator(
+                                               charon->credentials, CERT_X509, KEY_RSA, NULL, TRUE);
+       while (enumerator->enumerate(enumerator, &cert))
+       {
+               id = cert->get_subject(cert);
+               authorities->write_data16(authorities, id->get_encoding(id));
+       }
+       enumerator->destroy(enumerator);
+       writer->write_data16(writer, authorities->get_buf(authorities));
+       authorities->destroy(authorities);
+
+       *type = TLS_CERTIFICATE_REQUEST;
+       this->state = STATE_CERTREQ_SENT;
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
+       return NEED_MORE;
+}
+
+/**
+ * Send Hello Done
+ */
+static status_t send_hello_done(private_tls_server_t *this,
+                                                       tls_handshake_type_t *type, tls_writer_t *writer)
+{
+       *type = TLS_SERVER_HELLO_DONE;
+       this->state = STATE_HELLO_DONE;
+       this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer));
+       return NEED_MORE;
+}
+
+/**
+ * Send Finished
+ */
+static status_t send_finished(private_tls_server_t *this,
+                                                         tls_handshake_type_t *type, tls_writer_t *writer)
+{
+       char buf[12];
+
+       if (!this->crypto->calculate_finished(this->crypto, "server finished", buf))
+       {
+               DBG1(DBG_IKE, "calculating server finished data failed");
+               return FAILED;
+       }
+
+       writer->write_data(writer, chunk_from_thing(buf));
+
+       *type = TLS_FINISHED;
+       this->state = STATE_FINISHED_SENT;
+       this->crypto->derive_eap_msk(this->crypto,
+                                                                chunk_from_thing(this->client_random),
+                                                                chunk_from_thing(this->server_random));
        return NEED_MORE;
 }
 
 METHOD(tls_handshake_t, build, status_t,
        private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer)
 {
-       return INVALID_STATE;
+       switch (this->state)
+       {
+               case STATE_HELLO_RECEIVED:
+                       return send_server_hello(this, type, writer);
+               case STATE_HELLO_SENT:
+                       return send_certificate(this, type, writer);
+               case STATE_CERT_SENT:
+                       return send_certificate_request(this, type, writer);
+               case STATE_CERTREQ_SENT:
+                       return send_hello_done(this, type, writer);
+               case STATE_CIPHERSPEC_CHANGED_OUT:
+                       return send_finished(this, type, writer);
+               case STATE_FINISHED_SENT:
+                       return INVALID_STATE;
+               default:
+                       return INVALID_STATE;
+       }
 }
 
 METHOD(tls_handshake_t, cipherspec_changed, bool,
        private_tls_server_t *this)
 {
+       if (this->state == STATE_FINISHED_RECEIVED)
+       {
+               this->crypto->change_cipher(this->crypto, FALSE);
+               this->state = STATE_CIPHERSPEC_CHANGED_OUT;
+               return TRUE;
+       }
        return FALSE;
 }
 
 METHOD(tls_handshake_t, change_cipherspec, bool,
        private_tls_server_t *this)
 {
+       if (this->state == STATE_CERT_VERIFY_RECEIVED)
+       {
+               this->crypto->change_cipher(this->crypto, TRUE);
+               this->state = STATE_CIPHERSPEC_CHANGED_IN;
+               return TRUE;
+       }
        return FALSE;
 }
 
 METHOD(tls_handshake_t, destroy, void,
        private_tls_server_t *this)
 {
+       DESTROY_IF(this->private);
+       this->peer_auth->destroy(this->peer_auth);
+       this->server_auth->destroy(this->server_auth);
        free(this);
 }
 
@@ -101,6 +594,9 @@ tls_server_t *tls_server_create(tls_t *tls, tls_crypto_t *crypto,
                .crypto = crypto,
                .server = server,
                .peer = peer,
+               .state = STATE_INIT,
+               .peer_auth = auth_cfg_create(),
+               .server_auth = auth_cfg_create(),
        );
 
        return &this->public;