Added SILC Server library.
[silc.git] / lib / silcclient / protocol.c
index da7bc07fb352423627398550db7c670bd609cb86..c16d9aa3545570e952a347d05f395b51358f7c85 100644 (file)
@@ -2,27 +2,24 @@
 
   protocol.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2004 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
 */
-/*
- * Client side of the protocols.
- */
 /* $Id$ */
 
-#include "clientlibincludes.h"
+#include "silc.h"
+#include "silcclient.h"
 #include "client_internal.h"
 
 SILC_TASK_CALLBACK(silc_client_protocol_connection_auth);
@@ -41,7 +38,7 @@ void silc_client_protocol_ke_send_packet(SilcSKE ske,
                                         void *context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
+  SilcClientKEInternalContext *ctx =
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
 
@@ -58,15 +55,15 @@ typedef struct {
   void *completion_context;
 } *VerifyKeyContext;
 
-static void silc_client_verify_key_cb(bool success, void *context)
+static void silc_client_verify_key_cb(SilcBool success, void *context)
 {
   VerifyKeyContext verify = (VerifyKeyContext)context;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Call the completion callback back to the SKE */
-  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK : 
-                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY, 
+  verify->completion(verify->ske, success ? SILC_SKE_STATUS_OK :
+                    SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY,
                     verify->completion_context);
 
   silc_free(verify);
@@ -77,14 +74,14 @@ static void silc_client_verify_key_cb(bool success, void *context)
 
 void silc_client_protocol_ke_verify_key(SilcSKE ske,
                                        unsigned char *pk_data,
-                                       uint32 pk_len,
+                                       SilcUInt32 pk_len,
                                        SilcSKEPKType pk_type,
                                        void *context,
                                        SilcSKEVerifyCbCompletion completion,
                                        void *completion_context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
+  SilcClientKEInternalContext *ctx =
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   VerifyKeyContext verify;
@@ -97,7 +94,7 @@ void silc_client_protocol_ke_verify_key(SilcSKE ske,
   verify->completion_context = completion_context;
 
   /* Verify public key from user. */
-  client->internal->ops->verify_public_key(client, ctx->sock->user_data, 
+  client->internal->ops->verify_public_key(client, ctx->sock->user_data,
                                           ctx->sock->type,
                                           pk_data, pk_len, pk_type,
                                           silc_client_verify_key_cb, verify);
@@ -113,119 +110,101 @@ void silc_client_protocol_ke_set_keys(SilcSKE ske,
                                      SilcHash hash,
                                      SilcHmac hmac,
                                      SilcSKEDiffieHellmanGroup group,
-                                     bool is_responder)
+                                     SilcBool is_responder)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+  const char *cname = silc_cipher_get_name(cipher);
 
   SILC_LOG_DEBUG(("Setting new keys into use"));
 
   /* Allocate cipher to be used in the communication */
-  silc_cipher_alloc(cipher->cipher->name, &conn->send_key);
-  silc_cipher_alloc(cipher->cipher->name, &conn->receive_key);
-  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, &conn->hmac_send);
-  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL, &conn->hmac_receive);
+  silc_cipher_alloc((char *)cname, &conn->internal->send_key);
+  silc_cipher_alloc((char *)cname, &conn->internal->receive_key);
+  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL,
+                 &conn->internal->hmac_send);
+  silc_hmac_alloc((char *)silc_hmac_get_name(hmac), NULL,
+                 &conn->internal->hmac_receive);
 
   if (is_responder == TRUE) {
-    silc_cipher_set_key(conn->send_key, keymat->receive_enc_key, 
+    silc_cipher_set_key(conn->internal->send_key, keymat->receive_enc_key,
                        keymat->enc_key_len);
-    silc_cipher_set_iv(conn->send_key, keymat->receive_iv);
-    silc_cipher_set_key(conn->receive_key, keymat->send_enc_key, 
+    silc_cipher_set_iv(conn->internal->send_key, keymat->receive_iv);
+    silc_cipher_set_key(conn->internal->receive_key, keymat->send_enc_key,
                        keymat->enc_key_len);
-    silc_cipher_set_iv(conn->receive_key, keymat->send_iv);
-    silc_hmac_set_key(conn->hmac_send, keymat->receive_hmac_key, 
+    silc_cipher_set_iv(conn->internal->receive_key, keymat->send_iv);
+    silc_hmac_set_key(conn->internal->hmac_send, keymat->receive_hmac_key,
                      keymat->hmac_key_len);
-    silc_hmac_set_key(conn->hmac_receive, keymat->send_hmac_key, 
+    silc_hmac_set_key(conn->internal->hmac_receive, keymat->send_hmac_key,
                      keymat->hmac_key_len);
   } else {
-    silc_cipher_set_key(conn->send_key, keymat->send_enc_key, 
+    silc_cipher_set_key(conn->internal->send_key, keymat->send_enc_key,
                        keymat->enc_key_len);
-    silc_cipher_set_iv(conn->send_key, keymat->send_iv);
-    silc_cipher_set_key(conn->receive_key, keymat->receive_enc_key, 
+    silc_cipher_set_iv(conn->internal->send_key, keymat->send_iv);
+    silc_cipher_set_key(conn->internal->receive_key, keymat->receive_enc_key,
                        keymat->enc_key_len);
-    silc_cipher_set_iv(conn->receive_key, keymat->receive_iv);
-    silc_hmac_set_key(conn->hmac_send, keymat->send_hmac_key, 
+    silc_cipher_set_iv(conn->internal->receive_key, keymat->receive_iv);
+    silc_hmac_set_key(conn->internal->hmac_send, keymat->send_hmac_key,
                      keymat->hmac_key_len);
-    silc_hmac_set_key(conn->hmac_receive, keymat->receive_hmac_key, 
+    silc_hmac_set_key(conn->internal->hmac_receive, keymat->receive_hmac_key,
                      keymat->hmac_key_len);
   }
 
   /* Rekey stuff */
-  conn->rekey = silc_calloc(1, sizeof(*conn->rekey));
-  conn->rekey->send_enc_key = silc_memdup(keymat->send_enc_key, 
-                                         keymat->enc_key_len / 8);
-  conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+  conn->internal->rekey = silc_calloc(1, sizeof(*conn->internal->rekey));
+  conn->internal->rekey->send_enc_key = silc_memdup(keymat->send_enc_key,
+                                                   keymat->enc_key_len / 8);
+  conn->internal->rekey->enc_key_len = keymat->enc_key_len / 8;
 
   if (ske->start_payload->flags & SILC_SKE_SP_FLAG_PFS)
-    conn->rekey->pfs = TRUE;
-  conn->rekey->ske_group = silc_ske_group_get_number(group);
+    conn->internal->rekey->pfs = TRUE;
+  conn->internal->rekey->ske_group = silc_ske_group_get_number(group);
 
   /* Save the HASH function */
-  silc_hash_alloc(hash->hash->name, &conn->hash);
+  silc_hash_alloc(silc_hash_get_name(hash), &conn->internal->hash);
 }
 
 /* Checks the version string of the server. */
 
 SilcSKEStatus silc_ske_check_version(SilcSKE ske, unsigned char *version,
-                                    uint32 len, void *context)
+                                    SilcUInt32 len, void *context)
 {
   SilcClientConnection conn = (SilcClientConnection)ske->sock->user_data;
   SilcClient client = (SilcClient)ske->user_data;
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  char *cp;
-  int maj = 0, min = 0, build = 0, maj2 = 0, min2 = 0, build2 = 0;
-
-  /* Check for initial version string */
-  if (!strstr(version, "SILC-1.0-"))
-    status = SILC_SKE_STATUS_BAD_VERSION;
-
-  /* Check software version */
+  SilcUInt32 l_protocol_version = 0, r_protocol_version = 0;
 
-  cp = version + 9;
-  if (!cp)
-    status = SILC_SKE_STATUS_BAD_VERSION;
-
-  maj = atoi(cp);
-  cp = strchr(cp, '.');
-  if (cp) {
-    min = atoi(cp + 1);
-    cp++;
-  }
-  cp = strchr(cp, '.');
-  if (cp)
-    build = atoi(cp + 1);
-
-  cp = client->internal->silc_client_version + 9;
-  if (!cp)
-    status = SILC_SKE_STATUS_BAD_VERSION;
-
-  maj2 = atoi(cp);
-  cp = strchr(cp, '.');
-  if (cp) {
-    min2 = atoi(cp + 1);
-    cp++;
+  if (!silc_parse_version_string(version, &r_protocol_version, NULL, NULL,
+                                NULL, NULL)) {
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                              "We don't support server version `%s'",
+                              version);
+    return SILC_SKE_STATUS_BAD_VERSION;
   }
-  cp = strchr(cp, '.');
-  if (cp)
-    build2 = atoi(cp + 1);
-
-  if (maj != maj2)
-    status = SILC_SKE_STATUS_BAD_VERSION;
 
-  /* XXX backward support for 0.6.1 */
-  if (maj == 0 && min == 6 && build < 2)
-    ske->backward_version = 1;
+  if (!silc_parse_version_string(client->internal->silc_client_version,
+                                &l_protocol_version, NULL, NULL,
+                                NULL, NULL)) {
+    client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                              "We don't support server version `%s'",
+                              version);
+    return SILC_SKE_STATUS_BAD_VERSION;
+  }
 
-  if (status != SILC_SKE_STATUS_OK)
+  /* If remote is too new, don't connect */
+  if (l_protocol_version < r_protocol_version) {
     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
-                              "We don't support server version `%s'", 
+                              "We don't support server version `%s'",
                               version);
+    return SILC_SKE_STATUS_BAD_VERSION;
+  }
+
+  ske->sock->version = r_protocol_version;
 
-  return status;
+  return SILC_SKE_STATUS_OK;
 }
 
 /* Callback that is called by the SKE to indicate that it is safe to
-   continue the execution of the protocol. Is given as argument to the 
-   silc_ske_initiator_finish or silc_ske_responder_phase_2 functions. 
+   continue the execution of the protocol. Is given as argument to the
+   silc_ske_initiator_finish or silc_ske_responder_phase_2 functions.
    This is called due to the fact that the public key verification
    process is asynchronous and we must not continue the protocl until
    the public key has been verified and this callback is called. */
@@ -234,7 +213,7 @@ static void silc_client_protocol_ke_continue(SilcSKE ske,
                                             void *context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
+  SilcClientKEInternalContext *ctx =
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = ctx->sock->user_data;
@@ -243,7 +222,7 @@ static void silc_client_protocol_ke_continue(SilcSKE ske,
 
   if (ske->status != SILC_SKE_STATUS_OK) {
     /* Call failure client operation */
-    client->internal->ops->failure(client, conn, protocol, 
+    client->internal->ops->failure(client, conn, protocol,
                                   (void *)ske->status);
     protocol->state = SILC_PROTOCOL_STATE_ERROR;
     silc_protocol_execute(protocol, client->schedule, 0, 0);
@@ -261,12 +240,12 @@ static void silc_client_protocol_ke_continue(SilcSKE ske,
     protocol->state = SILC_PROTOCOL_STATE_END;
   }
 
-  /* Advance protocol state and call the next state if we are responder. 
+  /* Advance protocol state and call the next state if we are responder.
      This happens when this callback was sent to silc_ske_responder_phase_2
      function. */
   if (ctx->responder == TRUE) {
     protocol->state++;
-    silc_protocol_execute(protocol, client->schedule, 0, 100000);
+    silc_protocol_execute(protocol, client->schedule, 0, 1);
   }
 }
 
@@ -276,7 +255,7 @@ static void silc_client_protocol_ke_continue(SilcSKE ske,
 SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientKEInternalContext *ctx = 
+  SilcClientKEInternalContext *ctx =
     (SilcClientKEInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = ctx->sock->user_data;
@@ -301,13 +280,21 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       silc_ske_set_callbacks(ske, ctx->send_packet, NULL,
                             ctx->verify,
                             silc_client_protocol_ke_continue,
-                            silc_ske_check_version, 
+                            silc_ske_check_version,
                             context);
-      
+
       if (ctx->responder == TRUE) {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Start the key exchange by processing the received security
           properties packet from initiator. */
-       status = 
+       status =
          silc_ske_responder_start(ske, ctx->rng, ctx->sock,
                                   client->internal->silc_client_version,
                                   ctx->packet->buffer, TRUE);
@@ -316,7 +303,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
 
        /* Assemble security properties. */
        silc_ske_assemble_security_properties(
-                                 ske, SILC_SKE_SP_FLAG_MUTUAL, 
+                                 ske, SILC_SKE_SP_FLAG_MUTUAL,
                                  client->internal->silc_client_version,
                                  &start_payload);
 
@@ -344,18 +331,26 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       /* Advance protocol state and call the next state if we are responder */
       protocol->state++;
       if (ctx->responder == TRUE)
-       silc_protocol_execute(protocol, client->schedule, 0, 100000);
+       silc_protocol_execute(protocol, client->schedule, 0, 1);
     }
     break;
   case 2:
     {
-      /* 
-       * Phase 1 
+      /*
+       * Phase 1
        */
       if (ctx->responder == TRUE) {
        /* Sends the selected security properties to the initiator. */
        status = silc_ske_responder_phase_1(ctx->ske);
       } else {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Call Phase-1 function. This processes the Key Exchange Start
           paylaod reply we just got from the responder. The callback
           function will receive the processed payload where we will
@@ -377,15 +372,23 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       /* Advance protocol state and call next state if we are initiator */
       protocol->state++;
       if (ctx->responder == FALSE)
-       silc_protocol_execute(protocol, client->schedule, 0, 100000);
+       silc_protocol_execute(protocol, client->schedule, 0, 1);
     }
     break;
   case 3:
     {
-      /* 
-       * Phase 2 
+      /*
+       * Phase 2
        */
       if (ctx->responder == TRUE) {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Process the received Key Exchange 1 Payload packet from
           the initiator. This also creates our parts of the Diffie
           Hellman algorithm. The silc_client_protocol_ke_continue will
@@ -420,20 +423,28 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
     break;
   case 4:
     {
-      /* 
+      /*
        * Finish protocol
        */
       if (ctx->responder == TRUE) {
        /* This creates the key exchange material and sends our
           public parts to the initiator inside Key Exchange 2 Payload. */
-       status = 
-         silc_ske_responder_finish(ctx->ske, 
+       status =
+         silc_ske_responder_finish(ctx->ske,
                                    client->public_key, client->private_key,
                                    SILC_SKE_PK_TYPE_SILC);
 
        /* End the protocol on the next round */
        protocol->state = SILC_PROTOCOL_STATE_END;
       } else {
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error (type %d) during Key Exchange protocol",
+                           status));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 0);
+         return;
+       }
+
        /* Finish the protocol. This verifies the Key Exchange 2 payload
           sent by responder. The silc_client_protocol_ke_continue will
           be called after the public key has been verified. */
@@ -447,7 +458,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       if (status != SILC_SKE_STATUS_OK) {
         if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) {
           client->internal->ops->say(
-                            client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                            client, conn, SILC_CLIENT_MESSAGE_AUDIT,
                             "Received unsupported server %s public key",
                             ctx->sock->hostname);
         } else {
@@ -465,12 +476,12 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
 
   case SILC_PROTOCOL_STATE_END:
     {
-      /* 
+      /*
        * End protocol
        */
       SilcSKEKeyMaterial *keymat;
       int key_len = silc_cipher_get_key_len(ctx->ske->prop->cipher);
-      int hash_len = ctx->ske->prop->hash->hash->hash_len;
+      int hash_len = silc_hash_len(ctx->ske->prop->hash);
 
       /* Process the key material */
       keymat = silc_calloc(1, sizeof(*keymat));
@@ -489,7 +500,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
       if (ctx->responder == TRUE)
        silc_ske_end(ctx->ske);
 
-      /* Unregister the timeout task since the protocol has ended. 
+      /* Unregister the timeout task since the protocol has ended.
         This was the timeout task to be executed if the protocol is
         not completed fast enough. */
       if (ctx->timeout_task)
@@ -507,7 +518,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
     /*
      * Error during protocol
      */
-    
+
     /* Send abort notification */
     silc_ske_abort(ctx->ske, ctx->ske->status);
 
@@ -523,7 +534,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_key_exchange)
      * Received failure from remote.
      */
 
-    /* Unregister the timeout task since the protocol has ended. 
+    /* Unregister the timeout task since the protocol has ended.
        This was the timeout task to be executed if the protocol is
        not completed fast enough. */
     if (ctx->timeout_task)
@@ -548,7 +559,7 @@ static int
 silc_client_get_public_key_auth(SilcClient client,
                                SilcClientConnection conn,
                                unsigned char *auth_data,
-                               uint32 *auth_data_len,
+                               SilcUInt32 *auth_data_len,
                                SilcSKE ske)
 {
   int len;
@@ -569,7 +580,7 @@ silc_client_get_public_key_auth(SilcClient client,
                                          ske->start_payload_copy->len),
                     SILC_STR_END);
 
-  if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data, 
+  if (silc_pkcs_sign_with_hash(pkcs, ske->prop->hash, auth->data,
                               auth->len, auth_data, auth_data_len)) {
     silc_buffer_free(auth);
     return TRUE;
@@ -582,18 +593,30 @@ silc_client_get_public_key_auth(SilcClient client,
 /* Continues the connection authentication protocol. This funtion may
    be called directly or used as SilcAskPassphrase callback. */
 
-static void 
+static void
 silc_client_conn_auth_continue(unsigned char *auth_data,
-                              uint32 auth_data_len, void *context)
+                              SilcUInt32 auth_data_len, void *context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx = 
+  SilcClientConnAuthInternalContext *ctx =
     (SilcClientConnAuthInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcBuffer packet;
   int payload_len = 0;
-
-  SILC_LOG_DEBUG(("Start"));
+  unsigned char *autf8 = NULL;
+
+  SILC_LOG_DEBUG(("Sending authentication to server"));
+
+  /* Passphrase must be UTF-8 encoded, if it isn't encode it */
+  if (ctx->auth_meth == SILC_AUTH_PASSWORD &&
+      !silc_utf8_valid(auth_data, auth_data_len)) {
+    payload_len = silc_utf8_encoded_len(auth_data, auth_data_len,
+                                       SILC_STRING_ASCII);
+    autf8 = silc_calloc(payload_len, sizeof(*autf8));
+    auth_data_len = silc_utf8_encode(auth_data, auth_data_len,
+                                    SILC_STRING_ASCII, autf8, payload_len);
+    auth_data = autf8;
+  }
 
   payload_len = 4 + auth_data_len;
   packet = silc_buffer_alloc(payload_len);
@@ -610,15 +633,16 @@ silc_client_conn_auth_continue(unsigned char *auth_data,
                          NULL, 0, NULL, NULL,
                          packet->data, packet->len, TRUE);
   silc_buffer_free(packet);
-      
+  silc_free(autf8);
+
   /* Next state is end of protocol */
   protocol->state = SILC_PROTOCOL_STATE_END;
 }
-                                                   
+
 SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientConnAuthInternalContext *ctx = 
+  SilcClientConnAuthInternalContext *ctx =
     (SilcClientConnAuthInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = ctx->sock->user_data;
@@ -631,13 +655,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
   switch(protocol->state) {
   case SILC_PROTOCOL_STATE_START:
     {
-      /* 
+      /*
        * Start protocol. We send authentication data to the server
        * to be authenticated.
        */
       unsigned char *auth_data = NULL;
-      uint32 auth_data_len = 0;
-      unsigned char sign[1024];
+      SilcUInt32 auth_data_len = 0;
+      unsigned char sign[2048 + 1];
 
       switch(ctx->auth_meth) {
       case SILC_AUTH_NONE:
@@ -665,14 +689,14 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
       case SILC_AUTH_PUBLIC_KEY:
        if (!ctx->auth_data) {
          /* Public key authentication */
-         silc_client_get_public_key_auth(client, conn, sign, &auth_data_len, 
+         silc_client_get_public_key_auth(client, conn, sign, &auth_data_len,
                                          ctx->ske);
          auth_data = sign;
        } else {
          auth_data = ctx->auth_data;
          auth_data_len = ctx->auth_data_len;
        }
-       
+
        break;
       }
 
@@ -683,7 +707,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
 
   case SILC_PROTOCOL_STATE_END:
     {
-      /* 
+      /*
        * End protocol. Nothing special to be done here.
        */
 
@@ -697,7 +721,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
 
   case SILC_PROTOCOL_STATE_ERROR:
     {
-      /* 
+      /*
        * Error. Send notify to remote.
        */
       unsigned char error[4];
@@ -739,77 +763,78 @@ SILC_TASK_CALLBACK(silc_client_protocol_connection_auth)
 
 /* Actually takes the new keys into use. */
 
-static void 
+static void
 silc_client_protocol_rekey_validate(SilcClient client,
                                    SilcClientRekeyInternalContext *ctx,
                                    SilcSocketConnection sock,
                                    SilcSKEKeyMaterial *keymat,
-                                   bool send)
+                                   SilcBool send)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
 
   if (ctx->responder == TRUE) {
     if (send) {
-      silc_cipher_set_key(conn->send_key, keymat->receive_enc_key, 
+      silc_cipher_set_key(conn->internal->send_key, keymat->receive_enc_key,
                          keymat->enc_key_len);
-      silc_cipher_set_iv(conn->send_key, keymat->receive_iv);
-      silc_hmac_set_key(conn->hmac_send, keymat->receive_hmac_key, 
+      silc_cipher_set_iv(conn->internal->send_key, keymat->receive_iv);
+      silc_hmac_set_key(conn->internal->hmac_send, keymat->receive_hmac_key,
                        keymat->hmac_key_len);
     } else {
-      silc_cipher_set_key(conn->receive_key, keymat->send_enc_key, 
+      silc_cipher_set_key(conn->internal->receive_key, keymat->send_enc_key,
                          keymat->enc_key_len);
-      silc_cipher_set_iv(conn->receive_key, keymat->send_iv);
-      silc_hmac_set_key(conn->hmac_receive, keymat->send_hmac_key, 
+      silc_cipher_set_iv(conn->internal->receive_key, keymat->send_iv);
+      silc_hmac_set_key(conn->internal->hmac_receive, keymat->send_hmac_key,
                        keymat->hmac_key_len);
     }
   } else {
     if (send) {
-      silc_cipher_set_key(conn->send_key, keymat->send_enc_key, 
+      silc_cipher_set_key(conn->internal->send_key, keymat->send_enc_key,
                          keymat->enc_key_len);
-      silc_cipher_set_iv(conn->send_key, keymat->send_iv);
-      silc_hmac_set_key(conn->hmac_send, keymat->send_hmac_key, 
+      silc_cipher_set_iv(conn->internal->send_key, keymat->send_iv);
+      silc_hmac_set_key(conn->internal->hmac_send, keymat->send_hmac_key,
                        keymat->hmac_key_len);
     } else {
-      silc_cipher_set_key(conn->receive_key, keymat->receive_enc_key, 
-                         keymat->enc_key_len);
-      silc_cipher_set_iv(conn->receive_key, keymat->receive_iv);
-      silc_hmac_set_key(conn->hmac_receive, keymat->receive_hmac_key, 
-                       keymat->hmac_key_len);
+      silc_cipher_set_key(conn->internal->receive_key,
+                         keymat->receive_enc_key, keymat->enc_key_len);
+      silc_cipher_set_iv(conn->internal->receive_key, keymat->receive_iv);
+      silc_hmac_set_key(conn->internal->hmac_receive,
+                       keymat->receive_hmac_key, keymat->hmac_key_len);
     }
   }
 
   /* Save the current sending encryption key */
   if (!send) {
-    memset(conn->rekey->send_enc_key, 0, conn->rekey->enc_key_len);
-    silc_free(conn->rekey->send_enc_key);
-    conn->rekey->send_enc_key = silc_memdup(keymat->send_enc_key,
-                                           keymat->enc_key_len / 8);
-    conn->rekey->enc_key_len = keymat->enc_key_len / 8;
+    memset(conn->internal->rekey->send_enc_key, 0,
+          conn->internal->rekey->enc_key_len);
+    silc_free(conn->internal->rekey->send_enc_key);
+    conn->internal->rekey->send_enc_key = silc_memdup(keymat->send_enc_key,
+                                                     keymat->enc_key_len / 8);
+    conn->internal->rekey->enc_key_len = keymat->enc_key_len / 8;
   }
 }
 
 /* This function actually re-generates (when not using PFS) the keys and
    takes them into use. */
 
-static void 
+static void
 silc_client_protocol_rekey_generate(SilcClient client,
                                    SilcClientRekeyInternalContext *ctx,
-                                   bool send)
+                                   SilcBool send)
 {
   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
   SilcSKEKeyMaterial *keymat;
-  uint32 key_len = silc_cipher_get_key_len(conn->send_key);
-  uint32 hash_len = conn->hash->hash->hash_len;
+  SilcUInt32 key_len = silc_cipher_get_key_len(conn->internal->send_key);
+  SilcUInt32 hash_len = silc_hash_len(conn->internal->hash);
 
   SILC_LOG_DEBUG(("Generating new %s session keys (no PFS)",
                  send ? "sending" : "receiving"));
 
   /* Generate the new key */
   keymat = silc_calloc(1, sizeof(*keymat));
-  silc_ske_process_key_material_data(conn->rekey->send_enc_key,
-                                    conn->rekey->enc_key_len,
-                                    16, key_len, hash_len, 
-                                    conn->hash, keymat);
+  silc_ske_process_key_material_data(conn->internal->rekey->send_enc_key,
+                                    conn->internal->rekey->enc_key_len,
+                                    16, key_len, hash_len,
+                                    conn->internal->hash, keymat);
 
   /* Set the keys into use */
   silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat, send);
@@ -820,17 +845,17 @@ silc_client_protocol_rekey_generate(SilcClient client,
 /* This function actually re-generates (with PFS) the keys and
    takes them into use. */
 
-static void 
+static void
 silc_client_protocol_rekey_generate_pfs(SilcClient client,
                                        SilcClientRekeyInternalContext *ctx,
-                                       bool send)
+                                       SilcBool send)
 {
   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
   SilcSKEKeyMaterial *keymat;
-  uint32 key_len = silc_cipher_get_key_len(conn->send_key);
-  uint32 hash_len = conn->hash->hash->hash_len;
+  SilcUInt32 key_len = silc_cipher_get_key_len(conn->internal->send_key);
+  SilcUInt32 hash_len = silc_hash_len(conn->internal->hash);
   unsigned char *tmpbuf;
-  uint32 klen;
+  SilcUInt32 klen;
 
   SILC_LOG_DEBUG(("Generating new %s session keys (with PFS)",
                  send ? "sending" : "receiving"));
@@ -840,8 +865,8 @@ silc_client_protocol_rekey_generate_pfs(SilcClient client,
 
   /* Generate the new key */
   keymat = silc_calloc(1, sizeof(*keymat));
-  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len, 
-                                    conn->hash, keymat);
+  silc_ske_process_key_material_data(tmpbuf, klen, 16, key_len, hash_len,
+                                    conn->internal->hash, keymat);
 
   /* Set the keys into use */
   silc_client_protocol_rekey_validate(client, ctx, ctx->sock, keymat, send);
@@ -854,14 +879,14 @@ silc_client_protocol_rekey_generate_pfs(SilcClient client,
 /* Packet sending callback. This function is provided as packet sending
    routine to the Key Exchange functions. */
 
-static void 
+static void
 silc_client_protocol_rekey_send_packet(SilcSKE ske,
                                       SilcBuffer packet,
                                       SilcPacketType type,
                                       void *context)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientRekeyInternalContext *ctx = 
+  SilcClientRekeyInternalContext *ctx =
     (SilcClientRekeyInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
 
@@ -875,7 +900,7 @@ silc_client_protocol_rekey_send_packet(SilcSKE ske,
 SILC_TASK_CALLBACK(silc_client_protocol_rekey)
 {
   SilcProtocol protocol = (SilcProtocol)context;
-  SilcClientRekeyInternalContext *ctx = 
+  SilcClientRekeyInternalContext *ctx =
     (SilcClientRekeyInternalContext *)protocol->context;
   SilcClient client = (SilcClient)ctx->client;
   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
@@ -891,7 +916,7 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
   switch(protocol->state) {
   case SILC_PROTOCOL_STATE_START:
     {
-      /* 
+      /*
        * Start protocol.
        */
 
@@ -901,11 +926,18 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
         */
 
        if (ctx->pfs == TRUE) {
-         /* 
+         /*
           * Use Perfect Forward Secrecy, ie. negotiate the key material
           * using the SKE protocol.
           */
 
+         if (!ctx->packet) {
+           SILC_LOG_WARNING(("Error during Re-key"));
+           protocol->state = SILC_PROTOCOL_STATE_ERROR;
+           silc_protocol_execute(protocol, client->schedule, 0, 300000);
+           return;
+         }
+
          if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
            /* Error in protocol */
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -914,19 +946,19 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
 
          ctx->ske = silc_ske_alloc(client->rng, client);
          ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
-         silc_ske_group_get_by_number(conn->rekey->ske_group,
+         silc_ske_group_get_by_number(conn->internal->rekey->ske_group,
                                       &ctx->ske->prop->group);
 
-         silc_ske_set_callbacks(ctx->ske, 
+         silc_ske_set_callbacks(ctx->ske,
                                 silc_client_protocol_rekey_send_packet,
                                 NULL,  NULL, NULL, silc_ske_check_version,
                                 context);
-      
+
          status = silc_ske_responder_phase_2(ctx->ske, ctx->packet->buffer);
          if (status != SILC_SKE_STATUS_OK) {
            SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
                              status));
-           
+
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
            silc_protocol_execute(protocol, client->schedule, 0, 300000);
            return;
@@ -941,48 +973,50 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
           */
 
          /* Send the REKEY_DONE to indicate we will take new keys into use */
-         silc_client_packet_send(client, ctx->sock, 
-                                 SILC_PACKET_REKEY_DONE, 
+         silc_client_packet_queue_purge(client, ctx->sock);
+         silc_client_packet_send(client, ctx->sock,
+                                 SILC_PACKET_REKEY_DONE,
                                  NULL, 0, NULL, NULL, NULL, 0, FALSE);
 
          /* After we send REKEY_DONE we must set the sending encryption
             key to the new key since all packets after this packet must
             encrypted with the new key. */
          silc_client_protocol_rekey_generate(client, ctx, TRUE);
+         silc_client_packet_queue_purge(client, ctx->sock);
 
          /* The protocol ends in next stage. */
          protocol->state = SILC_PROTOCOL_STATE_END;
        }
-      
+
       } else {
        /*
         * We are the initiator of this protocol
         */
 
        /* Start the re-key by sending the REKEY packet */
-       silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY, 
+       silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY,
                                NULL, 0, NULL, NULL, NULL, 0, FALSE);
 
        if (ctx->pfs == TRUE) {
-         /* 
+         /*
           * Use Perfect Forward Secrecy, ie. negotiate the key material
           * using the SKE protocol.
           */
          ctx->ske = silc_ske_alloc(client->rng, client);
          ctx->ske->prop = silc_calloc(1, sizeof(*ctx->ske->prop));
-         silc_ske_group_get_by_number(conn->rekey->ske_group,
+         silc_ske_group_get_by_number(conn->internal->rekey->ske_group,
                                       &ctx->ske->prop->group);
 
-         silc_ske_set_callbacks(ctx->ske, 
+         silc_ske_set_callbacks(ctx->ske,
                                 silc_client_protocol_rekey_send_packet,
                                 NULL,  NULL, NULL, silc_ske_check_version,
                                 context);
-      
+
          status =  silc_ske_initiator_phase_2(ctx->ske, NULL, NULL, 0);
          if (status != SILC_SKE_STATUS_OK) {
            SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
                              status));
-           
+
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
            silc_protocol_execute(protocol, client->schedule, 0, 300000);
            return;
@@ -995,16 +1029,18 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
           * Do normal and simple re-key.
           */
 
-         /* Send the REKEY_DONE to indicate we will take new keys into use 
-            now. */ 
-         silc_client_packet_send(client, ctx->sock, 
-                                 SILC_PACKET_REKEY_DONE, 
+         /* Send the REKEY_DONE to indicate we will take new keys into use
+            now. */
+         silc_client_packet_queue_purge(client, ctx->sock);
+         silc_client_packet_send(client, ctx->sock,
+                                 SILC_PACKET_REKEY_DONE,
                                  NULL, 0, NULL, NULL, NULL, 0, FALSE);
 
          /* After we send REKEY_DONE we must set the sending encryption
             key to the new key since all packets after this packet must
             encrypted with the new key. */
          silc_client_protocol_rekey_generate(client, ctx, TRUE);
+         silc_client_packet_queue_purge(client, ctx->sock);
 
          /* The protocol ends in next stage. */
          protocol->state = SILC_PROTOCOL_STATE_END;
@@ -1023,13 +1059,13 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
         * Send our KE packe to the initiator now that we've processed
         * the initiator's KE packet.
         */
-       status = silc_ske_responder_finish(ctx->ske, NULL, NULL, 
+       status = silc_ske_responder_finish(ctx->ske, NULL, NULL,
                                           SILC_SKE_PK_TYPE_SILC);
 
          if (status != SILC_SKE_STATUS_OK) {
            SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
                              status));
-           
+
            protocol->state = SILC_PROTOCOL_STATE_ERROR;
            silc_protocol_execute(protocol, client->schedule, 0, 300000);
            return;
@@ -1041,17 +1077,24 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
        /*
         * The packet type must be KE packet
         */
+       if (!ctx->packet) {
+         SILC_LOG_WARNING(("Error during Re-key"));
+         protocol->state = SILC_PROTOCOL_STATE_ERROR;
+         silc_protocol_execute(protocol, client->schedule, 0, 300000);
+         return;
+       }
+
        if (ctx->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
          /* Error in protocol */
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
          silc_protocol_execute(protocol, client->schedule, 0, 300000);
        }
-       
+
        status = silc_ske_initiator_finish(ctx->ske, ctx->packet->buffer);
        if (status != SILC_SKE_STATUS_OK) {
          SILC_LOG_WARNING(("Error (type %d) during Re-key (PFS)",
                            status));
-         
+
          protocol->state = SILC_PROTOCOL_STATE_ERROR;
          silc_protocol_execute(protocol, client->schedule, 0, 300000);
          return;
@@ -1059,25 +1102,34 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
       }
     }
 
-    /* Send the REKEY_DONE to indicate we will take new keys into use 
-       now. */ 
-    silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY_DONE, 
+    /* Send the REKEY_DONE to indicate we will take new keys into use
+       now. */
+    silc_client_packet_queue_purge(client, ctx->sock);
+    silc_client_packet_send(client, ctx->sock, SILC_PACKET_REKEY_DONE,
                            NULL, 0, NULL, NULL, NULL, 0, FALSE);
-    
+
     /* After we send REKEY_DONE we must set the sending encryption
        key to the new key since all packets after this packet must
        encrypted with the new key. */
     silc_client_protocol_rekey_generate_pfs(client, ctx, TRUE);
+    silc_client_packet_queue_purge(client, ctx->sock);
 
     /* The protocol ends in next stage. */
     protocol->state = SILC_PROTOCOL_STATE_END;
     break;
 
   case SILC_PROTOCOL_STATE_END:
-    /* 
+    /*
      * End protocol
      */
 
+    if (!ctx->packet) {
+      SILC_LOG_WARNING(("Error during Re-key"));
+      protocol->state = SILC_PROTOCOL_STATE_ERROR;
+      silc_protocol_execute(protocol, client->schedule, 0, 300000);
+      return;
+    }
+
     if (ctx->packet->type != SILC_PACKET_REKEY_DONE) {
       /* Error in protocol */
       protocol->state = SILC_PROTOCOL_STATE_ERROR;
@@ -1086,7 +1138,11 @@ SILC_TASK_CALLBACK(silc_client_protocol_rekey)
 
     /* We received the REKEY_DONE packet and all packets after this is
        encrypted with the new key so set the decryption key to the new key */
-    silc_client_protocol_rekey_generate(client, ctx, FALSE);
+    if (ctx->pfs == TRUE)
+      silc_client_protocol_rekey_generate_pfs(client, ctx, FALSE);
+    else
+      silc_client_protocol_rekey_generate(client, ctx, FALSE);
+    silc_client_packet_queue_purge(client, ctx->sock);
 
     /* Protocol has ended, call the final callback */
     if (protocol->final_callback)