SKE: Verify initiator's public key always
[silc.git] / lib / silcske / silcske.c
index 7a864c26e017cf487362faea740c8baf4011196c..caf2579628bf9a1a426057a5179f2949dfa5d07e 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2006 Pekka Riikonen
+  Copyright (C) 2000 - 2008 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
@@ -31,22 +31,47 @@ struct SilcSKECallbacksStruct {
   void *context;
 };
 
-
 /************************ Static utility functions **************************/
 
-SilcSKEKeyMaterial
-silc_ske_process_key_material_data(unsigned char *data,
-                                  SilcUInt32 data_len,
-                                  SilcUInt32 req_iv_len,
-                                  SilcUInt32 req_enc_key_len,
-                                  SilcUInt32 req_hmac_key_len,
-                                  SilcHash hash);
+/* States */
+SILC_FSM_STATE(silc_ske_st_initiator_start);
+SILC_FSM_STATE(silc_ske_st_initiator_phase1);
+SILC_FSM_STATE(silc_ske_st_initiator_phase2);
+SILC_FSM_STATE(silc_ske_st_initiator_phase3);
+SILC_FSM_STATE(silc_ske_st_initiator_phase4);
+SILC_FSM_STATE(silc_ske_st_initiator_end);
+SILC_FSM_STATE(silc_ske_st_initiator_aborted);
+SILC_FSM_STATE(silc_ske_st_initiator_error);
+SILC_FSM_STATE(silc_ske_st_initiator_failure);
+SILC_FSM_STATE(silc_ske_st_responder_start);
+SILC_FSM_STATE(silc_ske_st_responder_phase1);
+SILC_FSM_STATE(silc_ske_st_responder_phase2);
+SILC_FSM_STATE(silc_ske_st_responder_phase4);
+SILC_FSM_STATE(silc_ske_st_responder_phase5);
+SILC_FSM_STATE(silc_ske_st_responder_end);
+SILC_FSM_STATE(silc_ske_st_responder_aborted);
+SILC_FSM_STATE(silc_ske_st_responder_failure);
+SILC_FSM_STATE(silc_ske_st_responder_error);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_done);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_end);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_wait);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_done);
+SILC_FSM_STATE(silc_ske_st_rekey_responder_end);
+SILC_TASK_CALLBACK(silc_ske_packet_send_retry);
+
 SilcSKEKeyMaterial
 silc_ske_process_key_material(SilcSKE ske,
                              SilcUInt32 req_iv_len,
                              SilcUInt32 req_enc_key_len,
-                             SilcUInt32 req_hmac_key_len);
-
+                             SilcUInt32 req_hmac_key_len,
+                             SilcSKERekeyMaterial *rekey);
+static SilcBool silc_ske_packet_send(SilcSKE ske,
+                                    SilcPacketType type,
+                                    SilcPacketFlags flags,
+                                    const unsigned char *data,
+                                    SilcUInt32 data_len);
 
 /* Packet callback */
 
@@ -57,8 +82,46 @@ static SilcBool silc_ske_packet_receive(SilcPacketEngine engine,
                                        void *app_context)
 {
   SilcSKE ske = callback_context;
+
+  /* Clear retransmission */
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  ske->retry_count = 0;
+  silc_schedule_task_del_by_callback(ske->schedule,
+                                    silc_ske_packet_send_retry);
+
+  /* Signal for new packet */
   ske->packet = packet;
-  silc_fsm_continue(&ske->fsm);
+
+  /* Check if we were aborted */
+  if (ske->aborted) {
+    silc_packet_free(packet);
+    ske->packet = NULL;
+
+    if (ske->responder)
+      silc_fsm_next(&ske->fsm, silc_ske_st_responder_aborted);
+    else
+      silc_fsm_next(&ske->fsm, silc_ske_st_initiator_aborted);
+
+    silc_fsm_continue_sync(&ske->fsm);
+    return TRUE;
+  }
+
+  /* See if received failure from remote */
+  if (packet->type == SILC_PACKET_FAILURE) {
+    if (ske->responder)
+      silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
+    else
+      silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+  }
+
+  /* Handle rekey and SUCCESS packets synchronously.  After SUCCESS packets
+     they keys are taken into use immediately, hence the synchronous
+     processing to get the keys in use as soon as possible. */
+  if (ske->rekeying || packet->type == SILC_PACKET_SUCCESS)
+    silc_fsm_continue_sync(&ske->fsm);
+  else
+    silc_fsm_continue(&ske->fsm);
+
   return TRUE;
 }
 
@@ -119,36 +182,31 @@ static void silc_ske_skr_callback(SilcSKR repository,
 
 static SilcSKEStatus silc_ske_check_version(SilcSKE ske)
 {
-  SilcUInt32 l_protocol_version = 0, r_protocol_version = 0;
+  SilcUInt32 r_software_version = 0;
+  char *r_software_string = NULL;
 
   if (!ske->remote_version || !ske->version)
     return SILC_SKE_STATUS_BAD_VERSION;
 
-  if (!silc_parse_version_string(ske->remote_version, &r_protocol_version,
-                                NULL, NULL, NULL, NULL))
-    return SILC_SKE_STATUS_BAD_VERSION;
-
-  if (!silc_parse_version_string(ske->version, &l_protocol_version,
-                                NULL, NULL, NULL, NULL))
-    return SILC_SKE_STATUS_BAD_VERSION;
-
-  /* If remote is too new, don't connect */
-  if (l_protocol_version < r_protocol_version)
+  if (!silc_parse_version_string(ske->remote_version, NULL, NULL,
+                                &r_software_version,
+                                &r_software_string, NULL))
     return SILC_SKE_STATUS_BAD_VERSION;
 
   return SILC_SKE_STATUS_OK;
 }
 
 /* Selects the supported security properties from the initiator's Key
-   Exchange Start Payload. */
+   Exchange Start Payload.  A responder function.  Saves our reply
+   start payload to ske->start_payload. */
 
 static SilcSKEStatus
 silc_ske_select_security_properties(SilcSKE ske,
-                                   SilcSKEStartPayload payload,
-                                   SilcSKEStartPayload remote_payload)
+                                   SilcSKEStartPayload remote_payload,
+                                   SilcSKESecurityProperties *prop)
 {
   SilcSKEStatus status;
-  SilcSKEStartPayload rp;
+  SilcSKEStartPayload rp, payload;
   char *cp;
   int len;
 
@@ -156,6 +214,40 @@ silc_ske_select_security_properties(SilcSKE ske,
 
   rp = remote_payload;
 
+  /* Check for mandatory fields */
+  if (!rp->ke_grp_len) {
+    SILC_LOG_DEBUG(("KE group not defined in payload"));
+    return SILC_SKE_STATUS_BAD_PAYLOAD;
+  }
+  if (!rp->pkcs_alg_len) {
+    SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
+    return SILC_SKE_STATUS_BAD_PAYLOAD;
+  }
+  if (!rp->enc_alg_len) {
+    SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
+    return SILC_SKE_STATUS_BAD_PAYLOAD;
+  }
+  if (!rp->hash_alg_len) {
+    SILC_LOG_DEBUG(("Hash alg not defined in payload"));
+    return SILC_SKE_STATUS_BAD_PAYLOAD;
+  }
+  if (!rp->hmac_alg_len) {
+    SILC_LOG_DEBUG(("HMAC not defined in payload"));
+    return SILC_SKE_STATUS_BAD_PAYLOAD;
+  }
+
+  /* Allocate security properties */
+  *prop = silc_calloc(1, sizeof(**prop));
+  if (!(*prop))
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+  /* Allocate our reply start payload */
+  payload = silc_calloc(1, sizeof(*payload));
+  if (!payload) {
+    silc_free(*prop);
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+  }
+
   /* Check version string */
   ske->remote_version = silc_memdup(rp->version, rp->version_len);
   status = silc_ske_check_version(ske);
@@ -165,7 +257,7 @@ silc_ske_select_security_properties(SilcSKE ske,
   }
 
   /* Flags are returned unchanged. */
-  payload->flags = rp->flags;
+  (*prop)->flags = payload->flags = rp->flags;
 
   /* Take cookie, we must return it to sender unmodified. */
   payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
@@ -178,8 +270,13 @@ silc_ske_select_security_properties(SilcSKE ske,
 
   /* In case IV included flag and session port is set the first 16-bits of
      cookie will include our session port. */
-  if (rp->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port)
+  if (rp->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port) {
+    /* Take remote port */
+    SILC_GET16_MSB((*prop)->remote_port, payload->cookie);
+
+    /* Put out port */
     SILC_PUT16_MSB(ske->session_port, payload->cookie);
+  }
 
   /* Put our version to our reply */
   payload->version = strdup(ske->version);
@@ -229,13 +326,6 @@ silc_ske_select_security_properties(SilcSKE ske,
       return SILC_SKE_STATUS_UNKNOWN_GROUP;
     }
   } else {
-
-    if (!rp->ke_grp_len) {
-      SILC_LOG_DEBUG(("KE group not defined in payload"));
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
-    }
-
     SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
     SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
 
@@ -243,6 +333,13 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->ke_grp_list = strdup(rp->ke_grp_list);
   }
 
+  /* Save group to security properties */
+  status = silc_ske_group_get_by_name(payload->ke_grp_list, &(*prop)->group);
+  if (status != SILC_SKE_STATUS_OK) {
+    silc_free(payload);
+    return SILC_SKE_STATUS_UNKNOWN_GROUP;
+  }
+
   /* Get supported PKCS algorithms */
   cp = rp->pkcs_alg_list;
   if (cp && strchr(cp, ',')) {
@@ -284,14 +381,6 @@ silc_ske_select_security_properties(SilcSKE ske,
       return SILC_SKE_STATUS_UNKNOWN_PKCS;
     }
   } else {
-
-    if (!rp->pkcs_alg_len) {
-      SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
-    }
-
     SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
     SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
 
@@ -341,15 +430,6 @@ silc_ske_select_security_properties(SilcSKE ske,
       return SILC_SKE_STATUS_UNKNOWN_CIPHER;
     }
   } else {
-
-    if (!rp->enc_alg_len) {
-      SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
-    }
-
     SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
                    rp->enc_alg_list));
 
@@ -357,6 +437,14 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->enc_alg_list = strdup(rp->enc_alg_list);
   }
 
+  /* Save selected cipher to security properties */
+  if (silc_cipher_alloc(payload->enc_alg_list, &(*prop)->cipher) == FALSE) {
+    silc_free(payload->ke_grp_list);
+    silc_free(payload->pkcs_alg_list);
+    silc_free(payload);
+    return SILC_SKE_STATUS_UNKNOWN_CIPHER;
+  }
+
   /* Get supported hash algorithms */
   cp = rp->hash_alg_list;
   if (cp && strchr(cp, ',')) {
@@ -400,16 +488,6 @@ silc_ske_select_security_properties(SilcSKE ske,
       return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
     }
   } else {
-
-    if (!rp->hash_alg_len) {
-      SILC_LOG_DEBUG(("Hash alg not defined in payload"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload->enc_alg_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
-    }
-
     SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
                    rp->hash_alg_list));
 
@@ -417,6 +495,15 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->hash_alg_list = strdup(rp->hash_alg_list);
   }
 
+  /* Save selected hash algorithm to security properties */
+  if (silc_hash_alloc(payload->hash_alg_list, &(*prop)->hash) == FALSE) {
+    silc_free(payload->ke_grp_list);
+    silc_free(payload->pkcs_alg_list);
+    silc_free(payload->enc_alg_list);
+    silc_free(payload);
+    return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
+  }
+
   /* Get supported HMACs */
   cp = rp->hmac_alg_list;
   if (cp && strchr(cp, ',')) {
@@ -461,17 +548,6 @@ silc_ske_select_security_properties(SilcSKE ske,
       return SILC_SKE_STATUS_UNKNOWN_HMAC;
     }
   } else {
-
-    if (!rp->hmac_alg_len) {
-      SILC_LOG_DEBUG(("HMAC not defined in payload"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload->enc_alg_list);
-      silc_free(payload->hash_alg_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
-    }
-
     SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
                    rp->hmac_alg_list));
 
@@ -479,6 +555,16 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->hmac_alg_list = strdup(rp->hmac_alg_list);
   }
 
+  /* Save selected HMACc to security properties */
+  if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &(*prop)->hmac) == FALSE) {
+    silc_free(payload->ke_grp_list);
+    silc_free(payload->pkcs_alg_list);
+    silc_free(payload->enc_alg_list);
+    silc_free(payload->hash_alg_list);
+    silc_free(payload);
+    return SILC_SKE_STATUS_UNKNOWN_HMAC;
+  }
+
   /* Get supported compression algorithms */
   cp = rp->comp_alg_list;
   if (cp && strchr(cp, ',')) {
@@ -528,6 +614,9 @@ silc_ske_select_security_properties(SilcSKE ske,
     2 + payload->enc_alg_len + 2 + payload->hash_alg_len +
     2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
 
+  /* Save our reply payload */
+  ske->start_payload = payload;
+
   return SILC_SKE_STATUS_OK;
 }
 
@@ -582,19 +671,23 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer buf;
-  unsigned char *e, *f, *KEY;
-  SilcUInt32 e_len, f_len, KEY_len;
+  unsigned char *e, *f, *KEY, *s_data;
+  SilcUInt32 e_len, f_len, KEY_len, s_len;
   int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
   if (initiator == FALSE) {
+    s_data = (ske->start_payload_copy ?
+            silc_buffer_data(ske->start_payload_copy) : NULL);
+    s_len = (ske->start_payload_copy ?
+            silc_buffer_len(ske->start_payload_copy) : 0);
     e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
     f = silc_mp_mp2bin(&ske->ke2_payload->x, 0, &f_len);
     KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
 
     /* Format the buffer used to compute the hash value */
-    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
+    buf = silc_buffer_alloc_size(s_len +
                                 ske->ke2_payload->pk_len +
                                 ske->ke1_payload->pk_len +
                                 e_len + f_len + KEY_len);
@@ -605,28 +698,24 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     if (!ske->ke1_payload->pk_data) {
       ret =
        silc_buffer_format(buf,
-                          SILC_STR_UI_XNSTRING(
-                                  ske->start_payload_copy->data,
-                                  silc_buffer_len(ske->start_payload_copy)),
-                          SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
-                                               ske->ke2_payload->pk_len),
-                          SILC_STR_UI_XNSTRING(e, e_len),
-                          SILC_STR_UI_XNSTRING(f, f_len),
-                          SILC_STR_UI_XNSTRING(KEY, KEY_len),
+                          SILC_STR_DATA(s_data, s_len),
+                          SILC_STR_DATA(ske->ke2_payload->pk_data,
+                                        ske->ke2_payload->pk_len),
+                          SILC_STR_DATA(e, e_len),
+                          SILC_STR_DATA(f, f_len),
+                          SILC_STR_DATA(KEY, KEY_len),
                           SILC_STR_END);
     } else {
       ret =
        silc_buffer_format(buf,
-                          SILC_STR_UI_XNSTRING(
-                                  ske->start_payload_copy->data,
-                                  silc_buffer_len(ske->start_payload_copy)),
-                          SILC_STR_UI_XNSTRING(ske->ke2_payload->pk_data,
-                                               ske->ke2_payload->pk_len),
-                          SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
-                                               ske->ke1_payload->pk_len),
-                          SILC_STR_UI_XNSTRING(e, e_len),
-                          SILC_STR_UI_XNSTRING(f, f_len),
-                          SILC_STR_UI_XNSTRING(KEY, KEY_len),
+                          SILC_STR_DATA(s_data, s_len),
+                          SILC_STR_DATA(ske->ke2_payload->pk_data,
+                                        ske->ke2_payload->pk_len),
+                          SILC_STR_DATA(ske->ke1_payload->pk_data,
+                                        ske->ke1_payload->pk_len),
+                          SILC_STR_DATA(e, e_len),
+                          SILC_STR_DATA(f, f_len),
+                          SILC_STR_DATA(KEY, KEY_len),
                           SILC_STR_END);
     }
     if (ret == -1) {
@@ -647,21 +736,23 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     silc_free(f);
     silc_free(KEY);
   } else {
+    s_data = (ske->start_payload_copy ?
+            silc_buffer_data(ske->start_payload_copy) : NULL);
+    s_len = (ske->start_payload_copy ?
+            silc_buffer_len(ske->start_payload_copy) : 0);
     e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
 
-    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
-                                ske->ke1_payload->pk_len + e_len);
+    buf = silc_buffer_alloc_size(s_len + ske->ke1_payload->pk_len + e_len);
     if (!buf)
       return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
     /* Format the buffer used to compute the hash value */
     ret =
       silc_buffer_format(buf,
-                        SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
-                                    silc_buffer_len(ske->start_payload_copy)),
-                        SILC_STR_UI_XNSTRING(ske->ke1_payload->pk_data,
-                                             ske->ke1_payload->pk_len),
-                        SILC_STR_UI_XNSTRING(e, e_len),
+                        SILC_STR_DATA(s_data, s_len),
+                        SILC_STR_DATA(ske->ke1_payload->pk_data,
+                                      ske->ke1_payload->pk_len),
+                        SILC_STR_DATA(e, e_len),
                         SILC_STR_END);
     if (ret == -1) {
       silc_buffer_free(buf);
@@ -692,6 +783,42 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   return status;
 }
 
+/* Generate rekey material */
+
+static SilcSKERekeyMaterial
+silc_ske_make_rekey_material(SilcSKE ske, SilcSKEKeyMaterial keymat)
+{
+  SilcSKERekeyMaterial rekey;
+  const char *hash;
+
+  /* Create rekey material */
+  rekey = silc_calloc(1, sizeof(*rekey));
+  if (!rekey)
+    return NULL;
+
+  if (ske->prop) {
+    if (ske->prop->group)
+      rekey->ske_group = silc_ske_group_get_number(ske->prop->group);
+    rekey->pfs = (ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? TRUE : FALSE);
+    hash = silc_hash_get_name(ske->prop->hash);
+    rekey->hash = silc_memdup(hash, strlen(hash));
+    if (!rekey->hash)
+      return NULL;
+  }
+
+  if (rekey->pfs == FALSE) {
+    rekey->send_enc_key = silc_memdup(keymat->send_enc_key,
+                                     keymat->enc_key_len / 8);
+    if (!rekey->send_enc_key) {
+      silc_free(rekey);
+      return NULL;
+    }
+    rekey->enc_key_len = keymat->enc_key_len;
+  }
+
+  return rekey;
+}
+
 /* Assembles security properties */
 
 static SilcSKEStartPayload
@@ -758,6 +885,120 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   return rp;
 }
 
+/* Packet retransmission callback. */
+
+SILC_TASK_CALLBACK(silc_ske_packet_send_retry)
+{
+  SilcSKE ske = context;
+
+  if (ske->retry_count++ >= SILC_SKE_RETRY_COUNT ||
+      ske->aborted) {
+    SILC_LOG_DEBUG(("Retransmission limit reached, packet was lost"));
+    ske->retry_count = 0;
+    ske->retry_timer = SILC_SKE_RETRY_MIN;
+    silc_free(ske->retrans.data);
+    ske->retrans.data = NULL;
+    ske->status = SILC_SKE_STATUS_TIMEOUT;
+    if (ske->responder)
+      silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
+    else
+      silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+    silc_fsm_continue_sync(&ske->fsm);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Retransmitting packet"));
+  silc_ske_packet_send(ske, ske->retrans.type, ske->retrans.flags,
+                      ske->retrans.data, ske->retrans.data_len);
+}
+
+/* Install retransmission timer */
+
+static void silc_ske_install_retransmission(SilcSKE ske)
+{
+  if (!silc_packet_stream_is_udp(ske->stream))
+    return;
+
+  if (ske->retrans.data) {
+    SILC_LOG_DEBUG(("Installing retransmission timer %d secs",
+                   ske->retry_timer));
+    silc_schedule_task_add_timeout(ske->schedule, silc_ske_packet_send_retry,
+                                  ske, ske->retry_timer, 0);
+  }
+  ske->retry_timer = ((ske->retry_timer * SILC_SKE_RETRY_MUL) +
+                     (silc_rng_get_rn16(ske->rng) % SILC_SKE_RETRY_RAND));
+}
+
+/* Sends SILC packet.  Handles retransmissions with UDP streams. */
+
+static SilcBool silc_ske_packet_send(SilcSKE ske,
+                                    SilcPacketType type,
+                                    SilcPacketFlags flags,
+                                    const unsigned char *data,
+                                    SilcUInt32 data_len)
+{
+  SilcBool ret;
+
+  /* Send the packet */
+  ret = silc_packet_send(ske->stream, type, flags, data, data_len);
+
+  if (silc_packet_stream_is_udp(ske->stream) &&
+      type != SILC_PACKET_FAILURE && type != SILC_PACKET_REKEY) {
+    silc_free(ske->retrans.data);
+    ske->retrans.type = type;
+    ske->retrans.flags = flags;
+    ske->retrans.data = silc_memdup(data, data_len);
+    ske->retrans.data_len = data_len;
+    silc_ske_install_retransmission(ske);
+  }
+
+  return ret;
+}
+
+/* Calls completion callback.  Completion is called always in this function
+   and must not be called anywhere else. */
+
+static void silc_ske_completion(SilcSKE ske)
+{
+  /* Call the completion callback */
+  if (!ske->freed && !ske->aborted && ske->callbacks->completed) {
+    if (ske->status != SILC_SKE_STATUS_OK)
+      ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
+                               ske->callbacks->context);
+    else
+      ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat,
+                               ske->rekey, ske->callbacks->context);
+  }
+}
+
+/* SKE FSM destructor. */
+
+static void silc_ske_finished(SilcFSM fsm, void *fsm_context,
+                             void *destructor_context)
+{
+  SilcSKE ske = fsm_context;
+  ske->running = FALSE;
+  if (ske->freed)
+    silc_ske_free(ske);
+}
+
+/* Key exchange timeout task callback */
+
+SILC_TASK_CALLBACK(silc_ske_timeout)
+{
+  SilcSKE ske = context;
+
+  SILC_LOG_DEBUG(("Timeout"));
+
+  ske->packet = NULL;
+  ske->status = SILC_SKE_STATUS_TIMEOUT;
+  if (ske->responder)
+    silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
+  else
+    silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+
+  silc_fsm_continue_sync(&ske->fsm);
+}
 
 /******************************* Protocol API *******************************/
 
@@ -774,6 +1015,11 @@ SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
   if (!rng || !schedule)
     return NULL;
 
+  if (!public_key) {
+    SILC_LOG_ERROR(("Public key must be given to silc_ske_alloc"));
+    return NULL;
+  }
+
   ske = silc_calloc(1, sizeof(*ske));
   if (!ske)
     return NULL;
@@ -784,6 +1030,8 @@ SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
   ske->schedule = schedule;
   ske->public_key = public_key;
   ske->private_key = private_key;
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  ske->refcnt = 1;
 
   return ske;
 }
@@ -794,46 +1042,72 @@ void silc_ske_free(SilcSKE ske)
 {
   SILC_LOG_DEBUG(("Freeing Key Exchange object"));
 
-  if (ske) {
-    /* Free start payload */
-    if (ske->start_payload)
-      silc_ske_payload_start_free(ske->start_payload);
-
-    /* Free KE payload */
-    if (ske->ke1_payload)
-      silc_ske_payload_ke_free(ske->ke1_payload);
-    if (ske->ke2_payload)
-      silc_ske_payload_ke_free(ske->ke2_payload);
-    silc_free(ske->remote_version);
-
-    /* Free rest */
-    if (ske->prop) {
-      if (ske->prop->group)
-       silc_ske_group_free(ske->prop->group);
-      if (ske->prop->cipher)
-       silc_cipher_free(ske->prop->cipher);
-      if (ske->prop->hash)
-       silc_hash_free(ske->prop->hash);
-      if (ske->prop->hmac)
-       silc_hmac_free(ske->prop->hmac);
-      silc_free(ske->prop);
-    }
-    if (ske->start_payload_copy)
-      silc_buffer_free(ske->start_payload_copy);
-    if (ske->x) {
-      silc_mp_uninit(ske->x);
-      silc_free(ske->x);
-    }
-    if (ske->KEY) {
-      silc_mp_uninit(ske->KEY);
-      silc_free(ske->KEY);
-    }
-    silc_free(ske->hash);
-    silc_free(ske->callbacks);
+  if (!ske)
+    return;
 
-    memset(ske, 'F', sizeof(*ske));
-    silc_free(ske);
+  if (ske->running) {
+    ske->freed = TRUE;
+
+    if (ske->aborted) {
+      /* If already aborted, destroy the session immediately */
+      ske->packet = NULL;
+      ske->status = SILC_SKE_STATUS_ERROR;
+      if (ske->responder)
+       silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
+      else
+       silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+      silc_fsm_continue_sync(&ske->fsm);
+    }
+    return;
   }
+
+  ske->refcnt--;
+  if (ske->refcnt > 0)
+    return;
+
+  /* Free start payload */
+  if (ske->start_payload)
+    silc_ske_payload_start_free(ske->start_payload);
+
+  /* Free KE payload */
+  if (ske->ke1_payload)
+    silc_ske_payload_ke_free(ske->ke1_payload);
+  if (ske->ke2_payload)
+    silc_ske_payload_ke_free(ske->ke2_payload);
+  silc_free(ske->remote_version);
+
+  /* Free rest */
+  if (ske->prop) {
+    if (ske->prop->group)
+      silc_ske_group_free(ske->prop->group);
+    if (ske->prop->cipher)
+      silc_cipher_free(ske->prop->cipher);
+    if (ske->prop->hash)
+      silc_hash_free(ske->prop->hash);
+    if (ske->prop->hmac)
+      silc_hmac_free(ske->prop->hmac);
+    if (ske->prop->public_key)
+      silc_pkcs_public_key_free(ske->prop->public_key);
+    silc_free(ske->prop);
+  }
+  if (ske->keymat)
+    silc_ske_free_key_material(ske->keymat);
+  if (ske->start_payload_copy)
+    silc_buffer_free(ske->start_payload_copy);
+  if (ske->x) {
+    silc_mp_uninit(ske->x);
+    silc_free(ske->x);
+  }
+  if (ske->KEY) {
+    silc_mp_uninit(ske->KEY);
+    silc_free(ske->KEY);
+  }
+  silc_free(ske->retrans.data);
+  silc_free(ske->hash);
+  silc_free(ske->callbacks);
+
+  memset(ske, 'F', sizeof(*ske));
+  silc_free(ske);
 }
 
 /* Return user context */
@@ -863,16 +1137,6 @@ void silc_ske_set_callbacks(SilcSKE ske,
 
 /******************************** Initiator *********************************/
 
-/* Initiator state machine */
-SILC_FSM_STATE(silc_ske_st_initiator_start);
-SILC_FSM_STATE(silc_ske_st_initiator_phase1);
-SILC_FSM_STATE(silc_ske_st_initiator_phase2);
-SILC_FSM_STATE(silc_ske_st_initiator_phase3);
-SILC_FSM_STATE(silc_ske_st_initiator_phase4);
-SILC_FSM_STATE(silc_ske_st_initiator_end);
-SILC_FSM_STATE(silc_ske_st_initiator_aborted);
-SILC_FSM_STATE(silc_ske_st_initiator_error);
-
 /* Start protocol.  Send our proposal */
 
 SILC_FSM_STATE(silc_ske_st_initiator_start)
@@ -903,12 +1167,24 @@ SILC_FSM_STATE(silc_ske_st_initiator_start)
      compute the HASH value. */
   ske->start_payload_copy = payload_buf;
 
-  /* Send the packet */
-  /* XXX */
+  /* Send the packet. */
+  if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE, 0,
+                           silc_buffer_data(payload_buf),
+                           silc_buffer_len(payload_buf))) {
+    /** Error sending packet */
+    SILC_LOG_DEBUG(("Error sending packet"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Add key exchange timeout */
+  silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+                                ske, ske->timeout, 0);
 
   /** Wait for responder proposal */
-  SILC_LOG_DEBUG(("Waiting for reponder proposal"));
-  silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+  SILC_LOG_DEBUG(("Waiting for responder proposal"));
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase1);
   return SILC_FSM_WAIT;
 }
 
@@ -920,32 +1196,57 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
   SilcSKEStatus status;
   SilcSKEStartPayload payload;
   SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group;
+  SilcSKEDiffieHellmanGroup group = NULL;
   SilcBuffer packet_buf = &ske->packet->buffer;
+  SilcUInt16 remote_port = 0;
+  SilcID id;
   int coff = 0;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
+  if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_ske_install_retransmission(ske);
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, packet_buf, &payload);
   if (status != SILC_SKE_STATUS_OK) {
     /** Error decoding Start Payload */
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
     ske->status = status;
     silc_fsm_next(fsm, silc_ske_st_initiator_error);
     return SILC_FSM_CONTINUE;
   }
 
+  /* Get remote ID and set it to stream */
+  if (ske->packet->src_id_len) {
+    silc_id_str2id(ske->packet->src_id, ske->packet->src_id_len,
+                  ske->packet->src_id_type,
+                  (ske->packet->src_id_type == SILC_ID_SERVER ?
+                   (void *)&id.u.server_id : (void *)&id.u.client_id),
+                  (ske->packet->src_id_type == SILC_ID_SERVER ?
+                   sizeof(id.u.server_id) : sizeof(id.u.client_id)));
+    silc_packet_set_ids(ske->stream, 0, NULL, ske->packet->src_id_type,
+                       (ske->packet->src_id_type == SILC_ID_SERVER ?
+                        (void *)&id.u.server_id : (void *)&id.u.client_id));
+  }
+
+  silc_packet_free(ske->packet);
+  ske->packet = NULL;
+
   /* Check that the cookie is returned unmodified.  In case IV included
      flag and session port has been set, the first two bytes of cookie
      are the session port and we ignore them in this check. */
-  if (payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port)
+  if (payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED && ske->session_port) {
+    /* Take remote port */
+    SILC_GET16_MSB(remote_port, ske->start_payload->cookie);
     coff = 2;
+  }
   if (memcmp(ske->start_payload->cookie + coff, payload->cookie + coff,
             SILC_SKE_COOKIE_LEN - coff)) {
     /** Invalid cookie */
@@ -981,6 +1282,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
     goto err;
 
   prop->group = group;
+  prop->remote_port = remote_port;
 
   if (silc_pkcs_find_algorithm(payload->pkcs_alg_list, NULL) == NULL) {
     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
@@ -1009,9 +1311,8 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
  err:
   if (payload)
     silc_ske_payload_start_free(payload);
-
-  silc_ske_group_free(group);
-
+  if (group)
+    silc_ske_group_free(group);
   if (prop->cipher)
     silc_cipher_free(prop->cipher);
   if (prop->hash)
@@ -1086,26 +1387,23 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2)
                  &ske->prop->group->group);
 
   /* Get public key */
-  if (ske->public_key) {
-    payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
-    if (!payload->pk_data) {
-      /** Error encoding public key */
-      silc_mp_uninit(x);
-      silc_free(x);
-      silc_mp_uninit(&payload->x);
-      silc_free(payload);
-      ske->ke1_payload = NULL;
-      ske->status = SILC_SKE_STATUS_ERROR;
-      silc_fsm_next(fsm, silc_ske_st_initiator_error);
-      return SILC_FSM_CONTINUE;
-    }
-    payload->pk_len = pk_len;
+  payload->pk_data = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
+  if (!payload->pk_data) {
+    /** Error encoding public key */
+    silc_mp_uninit(x);
+    silc_free(x);
+    silc_mp_uninit(&payload->x);
+    silc_free(payload);
+    ske->ke1_payload = NULL;
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
+  payload->pk_len = pk_len;
   payload->pk_type = silc_pkcs_get_type(ske->public_key);
 
   /* Compute signature data if we are doing mutual authentication */
-  if (ske->private_key &&
-      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+  if (ske->private_key && ske->prop->flags & SILC_SKE_SP_FLAG_MUTUAL) {
     unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1];
     SilcUInt32 hash_len, sign_len;
 
@@ -1120,7 +1418,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2)
 
     /* Sign the hash value */
     if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign,
-                       sizeof(sign) - 1, &sign_len, NULL)) {
+                       sizeof(sign) - 1, &sign_len, FALSE, ske->prop->hash)) {
       /** Error computing signature */
       silc_mp_uninit(x);
       silc_free(x);
@@ -1133,7 +1431,8 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2)
       return SILC_FSM_CONTINUE;
     }
     payload->sign_data = silc_memdup(sign, sign_len);
-    payload->sign_len = sign_len;
+    if (payload->sign_data)
+      payload->sign_len = sign_len;
     memset(sign, 0, sizeof(sign));
   }
 
@@ -1154,8 +1453,18 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2)
 
   ske->x = x;
 
+  /* Check for backwards compatibility */
+
   /* Send the packet. */
-  /* XXX */
+  if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE_1, 0,
+                           silc_buffer_data(payload_buf),
+                           silc_buffer_len(payload_buf))) {
+    /** Error sending packet */
+    SILC_LOG_DEBUG(("Error sending packet"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   silc_buffer_free(payload_buf);
 
@@ -1176,20 +1485,26 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
+  if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_ske_install_retransmission(ske);
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
   /* Decode the payload */
   status = silc_ske_payload_ke_decode(ske, packet_buf, &payload);
   if (status != SILC_SKE_STATUS_OK) {
     /** Error decoding KE payload */
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
     ske->status = status;
     silc_fsm_next(fsm, silc_ske_st_initiator_error);
     return SILC_FSM_CONTINUE;
   }
+  silc_packet_free(ske->packet);
+  ske->packet = NULL;
   ske->ke2_payload = payload;
 
   if (!payload->pk_data && (ske->callbacks->verify_key || ske->repository)) {
@@ -1236,10 +1551,11 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3)
       silc_skr_find_set_pkcs_type(find,
                                  silc_pkcs_get_type(ske->prop->public_key));
       silc_skr_find_set_public_key(find, ske->prop->public_key);
+      silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT);
 
       /* Find key from repository */
-      SILC_FSM_CALL(silc_skr_find(ske->repository, find,
-                                 silc_ske_skr_callback, ske));
+      SILC_FSM_CALL(silc_skr_find(ske->repository, silc_fsm_get_schedule(fsm),
+                                 find, silc_ske_skr_callback, ske));
     } else {
       /* Verify from application */
       SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key,
@@ -1297,40 +1613,46 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4)
 
   payload = ske->ke2_payload;
 
+  /* Compute the HASH value */
+  SILC_LOG_DEBUG(("Computing HASH value"));
+  status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+  if (status != SILC_SKE_STATUS_OK)
+    goto err;
+  ske->hash = silc_memdup(hash, hash_len);
+  ske->hash_len = hash_len;
+
   if (ske->prop->public_key) {
     SILC_LOG_DEBUG(("Public key is authentic"));
-
-    /* Compute the hash value */
-    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-    if (status != SILC_SKE_STATUS_OK)
-      goto err;
-
-    ske->hash = silc_memdup(hash, hash_len);
-    ske->hash_len = hash_len;
-
     SILC_LOG_DEBUG(("Verifying signature (HASH)"));
 
     /* Verify signature */
     if (!silc_pkcs_verify(ske->prop->public_key, payload->sign_data,
-                        payload->sign_len, hash, hash_len, NULL)) {
+                         payload->sign_len, hash, hash_len, NULL)) {
       SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
       status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
       goto err;
     }
 
     SILC_LOG_DEBUG(("Signature is Ok"));
-
     memset(hash, 'F', hash_len);
   }
 
   ske->status = SILC_SKE_STATUS_OK;
 
-  /* Process key material */
+  /* In case we are doing rekey move to finish it.  */
+  if (ske->rekey) {
+    /** Finish rekey */
+    silc_fsm_next(fsm, silc_ske_st_rekey_initiator_done);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Process key material */
   key_len = silc_cipher_get_key_len(ske->prop->cipher);
-  block_len = silc_cipher_get_key_len(ske->prop->cipher);
+  block_len = silc_cipher_get_block_len(ske->prop->cipher);
   hash_len = silc_hash_len(ske->prop->hash);
   ske->keymat = silc_ske_process_key_material(ske, block_len,
-                                             key_len, hash_len);
+                                             key_len, hash_len,
+                                             &ske->rekey);
   if (!ske->keymat) {
     SILC_LOG_ERROR(("Error processing key material"));
     status = SILC_SKE_STATUS_ERROR;
@@ -1338,7 +1660,14 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4)
   }
 
   /* Send SUCCESS packet */
-  /* XXX */
+  SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, hash);
+  if (!silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, hash, 4)) {
+    /** Error sending packet */
+    SILC_LOG_DEBUG(("Error sending packet"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
   /** Waiting completion */
   silc_fsm_next(fsm, silc_ske_st_initiator_end);
@@ -1374,15 +1703,25 @@ SILC_FSM_STATE(silc_ske_st_initiator_end)
 {
   SilcSKE ske = fsm_context;
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->packet->type != SILC_PACKET_SUCCESS) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_ske_install_retransmission(ske);
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
+  SILC_LOG_DEBUG(("Key exchange completed successfully"));
+
+  silc_packet_free(ske->packet);
+  ske->packet = NULL;
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
 
   return SILC_FSM_FINISH;
 }
@@ -1391,32 +1730,84 @@ SILC_FSM_STATE(silc_ske_st_initiator_end)
 
 SILC_FSM_STATE(silc_ske_st_initiator_aborted)
 {
+  SilcSKE ske = fsm_context;
+  unsigned char data[4];
+
+  SILC_LOG_DEBUG(("Aborted by caller"));
+
+  /* Send FAILURE packet */
+  SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, data);
+  silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, data, 4);
+
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
 
   return SILC_FSM_FINISH;
 }
 
-/* Error occurred */
+/* Error occurred.  Send error to remote host */
 
 SILC_FSM_STATE(silc_ske_st_initiator_error)
 {
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  unsigned char data[4];
+
+  SILC_LOG_DEBUG(("Error %s (%d) occurred during key exchange",
+                 silc_ske_map_status(ske->status), ske->status));
+
+  status = ske->status;
+  if (status > SILC_SKE_STATUS_INVALID_COOKIE)
+    status = SILC_SKE_STATUS_ERROR;
+
+  /* Send FAILURE packet */
+  SILC_PUT32_MSB((SilcUInt32)status, data);
+  silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, data, 4);
+
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
 
   return SILC_FSM_FINISH;
 }
 
+/* Failure received from remote */
 
-static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
-                                       void *destructor_context)
+SILC_FSM_STATE(silc_ske_st_initiator_failure)
 {
+  SilcSKE ske = fsm_context;
+  SilcUInt32 error = SILC_SKE_STATUS_ERROR;
+
+  if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) {
+    SILC_GET32_MSB(error, ske->packet->buffer.data);
+    ske->status = error;
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+  }
+
+  SILC_LOG_DEBUG(("Error %s (%d) received during key exchange",
+                 silc_ske_map_status(ske->status), ske->status));
 
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
+
+  return SILC_FSM_FINISH;
 }
 
 /* Starts the protocol as initiator */
 
-SilcAsyncOperation
-silc_ske_initiator(SilcSKE ske,
-                  SilcPacketStream stream,
-                  SilcSKEParams params,
-                  SilcSKEStartPayload start_payload)
+SilcAsyncOperation silc_ske_initiator(SilcSKE ske,
+                                     SilcPacketStream stream,
+                                     SilcSKEParams params,
+                                     SilcSKEStartPayload start_payload)
 {
   SILC_LOG_DEBUG(("Start SKE as initiator"));
 
@@ -1426,8 +1817,7 @@ silc_ske_initiator(SilcSKE ske,
   if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
     return NULL;
 
-  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske,
-                    ske->schedule))
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
     return NULL;
 
   if (params->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)
@@ -1442,7 +1832,10 @@ silc_ske_initiator(SilcSKE ske,
       return NULL;
   }
 
+  ske->timeout = params->timeout_secs ? params->timeout_secs : 30;
   ske->start_payload = start_payload;
+  ske->version = params->version;
+  ske->running = TRUE;
 
   /* Link to packet stream to get key exchange packets */
   ske->stream = stream;
@@ -1458,20 +1851,8 @@ silc_ske_initiator(SilcSKE ske,
   return &ske->op;
 }
 
-
 /******************************** Responder *********************************/
 
-SILC_FSM_STATE(silc_ske_st_responder_start);
-SILC_FSM_STATE(silc_ske_st_responder_phase1);
-SILC_FSM_STATE(silc_ske_st_responder_phase2);
-SILC_FSM_STATE(silc_ske_st_responder_phase3);
-SILC_FSM_STATE(silc_ske_st_responder_phase4);
-SILC_FSM_STATE(silc_ske_st_responder_phase5);
-SILC_FSM_STATE(silc_ske_st_responder_end);
-SILC_FSM_STATE(silc_ske_st_responder_aborted);
-SILC_FSM_STATE(silc_ske_st_responder_failure);
-SILC_FSM_STATE(silc_ske_st_responder_error);
-
 /* Start protocol as responder.  Wait initiator's start payload */
 
 SILC_FSM_STATE(silc_ske_st_responder_start)
@@ -1486,52 +1867,58 @@ SILC_FSM_STATE(silc_ske_st_responder_start)
     return SILC_FSM_CONTINUE;
   }
 
-  /* Start timeout */
-  /* XXX */
+  /* Add key exchange timeout */
+  silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+                                ske, ske->timeout, 0);
 
   /** Wait for initiator */
   silc_fsm_next(fsm, silc_ske_st_responder_phase1);
   return SILC_FSM_WAIT;
 }
 
-/* Decode initiator's start payload */
+/* Decode initiator's start payload.  Select the security properties from
+   the initiator's start payload and send our reply start payload back. */
 
 SILC_FSM_STATE(silc_ske_st_responder_phase1)
 {
   SilcSKE ske = fsm_context;
   SilcSKEStatus status;
-  SilcSKEStartPayload remote_payload = NULL, payload = NULL;
+  SilcSKEStartPayload remote_payload = NULL;
   SilcBuffer packet_buf = &ske->packet->buffer;
+  SilcID id;
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
-    return SILC_FSM_CONTINUE;
-  }
-
-  /* See if received failure from remote */
-  if (ske->packet->type == SILC_PACKET_FAILURE) {
-    silc_fsm_next(fsm, silc_ske_st_responder_failure);
-    return SILC_FSM_CONTINUE;
-  }
-
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, packet_buf, &remote_payload);
   if (status != SILC_SKE_STATUS_OK) {
     /** Error decoding Start Payload */
     silc_packet_free(ske->packet);
+    ske->packet = NULL;
     ske->status = status;
     silc_fsm_next(fsm, silc_ske_st_responder_error);
     return SILC_FSM_CONTINUE;
   }
 
+  /* Get remote ID and set it to stream */
+  if (ske->packet->src_id_len) {
+    silc_id_str2id(ske->packet->src_id, ske->packet->src_id_len,
+                  ske->packet->src_id_type,
+                  (ske->packet->src_id_type == SILC_ID_SERVER ?
+                   (void *)&id.u.server_id : (void *)&id.u.client_id),
+                  (ske->packet->src_id_type == SILC_ID_SERVER ?
+                   sizeof(id.u.server_id) : sizeof(id.u.client_id)));
+    silc_packet_set_ids(ske->stream, 0, NULL, ske->packet->src_id_type,
+                       (ske->packet->src_id_type == SILC_ID_SERVER ?
+                        (void *)&id.u.server_id : (void *)&id.u.client_id));
+  }
+
   /* Take a copy of the payload buffer for future use. It is used to
      compute the HASH value. */
   ske->start_payload_copy = silc_buffer_copy(packet_buf);
 
   silc_packet_free(ske->packet);
+  ske->packet = NULL;
 
   /* Force the mutual authentication flag if we want to do it. */
   if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) {
@@ -1552,106 +1939,47 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1)
     remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
   }
 
-  /* Parse and select the security properties from the payload */
-  payload = silc_calloc(1, sizeof(*payload));
-  status = silc_ske_select_security_properties(ske, payload, remote_payload);
+  /* Check and select security properties */
+  status = silc_ske_select_security_properties(ske, remote_payload,
+                                              &ske->prop);
   if (status != SILC_SKE_STATUS_OK) {
     /** Error selecting proposal */
-    if (remote_payload)
-      silc_ske_payload_start_free(remote_payload);
-    silc_free(payload);
+    silc_ske_payload_start_free(remote_payload);
     ske->status = status;
     silc_fsm_next(fsm, silc_ske_st_responder_error);
     return SILC_FSM_CONTINUE;
   }
 
-  ske->start_payload = payload;
-
   silc_ske_payload_start_free(remote_payload);
 
-  /** Send proposal to initiator */
-  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
-  return SILC_FSM_CONTINUE;
-}
-
-/* Phase-2.  Send Start Payload */
-
-SILC_FSM_STATE(silc_ske_st_responder_phase2)
-{
-  SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcBuffer payload_buf;
-  SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Allocate security properties from the payload. These are allocated
-     only for this negotiation and will be free'd after KE is over. */
-  ske->prop = prop = silc_calloc(1, sizeof(*prop));
-  if (!ske->prop) {
-    status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-    goto err;
-  }
-  prop->flags = ske->start_payload->flags;
-  status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list,
-                                     &group);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
-
-  prop->group = group;
-
-  /* XXX these shouldn't be allocated before we know the remote's
-     public key type.  It's unnecessary to allocate these because the
-     select_security_properties has succeeded already. */
-  if (!silc_pkcs_find_algorithm(ske->start_payload->pkcs_alg_list, NULL)) {
-    status = SILC_SKE_STATUS_UNKNOWN_PKCS;
-    goto err;
-  }
-  if (silc_cipher_alloc(ske->start_payload->enc_alg_list,
-                       &prop->cipher) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
-    goto err;
-  }
-  if (silc_hash_alloc(ske->start_payload->hash_alg_list,
-                     &prop->hash) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
-    goto err;
-  }
-  if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
-                     &prop->hmac) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
-    goto err;
-  }
-
-  /* Encode the payload */
+  /* Encode our reply payload to send the selected security properties */
   status = silc_ske_payload_start_encode(ske, ske->start_payload,
-                                        &payload_buf);
+                                        &packet_buf);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   /* Send the packet. */
-  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0,
-                       payload_buf->data, silc_buffer_len(payload_buf)))
+  if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE, 0,
+                           silc_buffer_data(packet_buf),
+                           silc_buffer_len(packet_buf)))
     goto err;
 
-  silc_buffer_free(payload_buf);
+  silc_buffer_free(packet_buf);
 
   /** Waiting initiator's KE payload */
-  silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
   return SILC_FSM_WAIT;
 
  err:
-  if (group)
-    silc_ske_group_free(group);
-
-  if (prop->cipher)
-    silc_cipher_free(prop->cipher);
-  if (prop->hash)
-    silc_hash_free(prop->hash);
-  if (prop->hmac)
-    silc_hmac_free(prop->hmac);
-  silc_free(prop);
+  if (ske->prop->group)
+    silc_ske_group_free(ske->prop->group);
+  if (ske->prop->cipher)
+    silc_cipher_free(ske->prop->cipher);
+  if (ske->prop->hash)
+    silc_hash_free(ske->prop->hash);
+  if (ske->prop->hmac)
+    silc_hmac_free(ske->prop->hmac);
+  silc_free(ske->prop);
   ske->prop = NULL;
 
   if (status == SILC_SKE_STATUS_OK)
@@ -1663,9 +1991,9 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2)
   return SILC_FSM_CONTINUE;
 }
 
-/* Phase-3.  Decode initiator's KE payload */
+/* Phase-2.  Decode initiator's KE payload */
 
-SILC_FSM_STATE(silc_ske_st_responder_phase3)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
 {
   SilcSKE ske = fsm_context;
   SilcSKEStatus status;
@@ -1674,16 +2002,12 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
-    return SILC_FSM_CONTINUE;
-  }
-
-  /* See if received failure from remote */
-  if (ske->packet->type == SILC_PACKET_FAILURE) {
-    silc_fsm_next(fsm, silc_ske_st_responder_failure);
-    return SILC_FSM_CONTINUE;
+  if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_ske_install_retransmission(ske);
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
   /* Decode Key Exchange Payload */
@@ -1691,6 +2015,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
   if (status != SILC_SKE_STATUS_OK) {
     /** Error decoding KE payload */
     silc_packet_free(ske->packet);
+    ske->packet = NULL;
     ske->status = status;
     silc_fsm_next(fsm, silc_ske_st_responder_error);
     return SILC_FSM_CONTINUE;
@@ -1699,16 +2024,11 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
   ske->ke1_payload = recv_payload;
 
   silc_packet_free(ske->packet);
+  ske->packet = NULL;
 
-  /* Verify the received public key and verify the signature if we are
-     doing mutual authentication. */
-  if (ske->start_payload &&
-      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-
-    SILC_LOG_DEBUG(("We are doing mutual authentication"));
-
-    if (!recv_payload->pk_data && (ske->callbacks->verify_key ||
-                                  ske->repository)) {
+  /* Verify public key, except in rekey, when it is not sent */
+  if (!ske->rekey) {
+    if (!recv_payload->pk_data) {
       /** Public key not provided */
       SILC_LOG_ERROR(("Remote end did not send its public key (or "
                      "certificate), even though we require it"));
@@ -1718,8 +2038,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
     }
 
     /* Decode the remote's public key */
-    if (recv_payload->pk_data &&
-       !silc_pkcs_public_key_alloc(recv_payload->pk_type,
+    if (!silc_pkcs_public_key_alloc(recv_payload->pk_type,
                                    recv_payload->pk_data,
                                    recv_payload->pk_len,
                                    &ske->prop->public_key)) {
@@ -1730,37 +2049,36 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
       return SILC_FSM_CONTINUE;
     }
 
-    if (ske->prop->public_key && (ske->callbacks->verify_key ||
-                                 ske->repository)) {
-      SILC_LOG_DEBUG(("Verifying public key"));
+    SILC_LOG_DEBUG(("Verifying public key"));
 
-      /** Waiting public key verification */
-      silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+    /** Waiting public key verification */
+    silc_fsm_next(fsm, silc_ske_st_responder_phase4);
 
-      /* If repository is provided, verify the key from there. */
-      if (ske->repository) {
-       SilcSKRFind find;
+    /* If repository is provided, verify the key from there. */
+    if (ske->repository) {
+      SilcSKRFind find;
 
-       find = silc_skr_find_alloc();
-       if (!find) {
-         ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-         silc_fsm_next(fsm, silc_ske_st_responder_error);
-         return SILC_FSM_CONTINUE;
-       }
-       silc_skr_find_set_pkcs_type(find,
-                                   silc_pkcs_get_type(ske->prop->public_key));
-       silc_skr_find_set_public_key(find, ske->prop->public_key);
+      find = silc_skr_find_alloc();
+      if (!find) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       silc_fsm_next(fsm, silc_ske_st_responder_error);
+       return SILC_FSM_CONTINUE;
+      }
+      silc_skr_find_set_pkcs_type(find,
+                                 silc_pkcs_get_type(ske->prop->public_key));
+      silc_skr_find_set_public_key(find, ske->prop->public_key);
+      silc_skr_find_set_usage(find, SILC_SKR_USAGE_KEY_AGREEMENT);
 
-       /* Find key from repository */
-       SILC_FSM_CALL(silc_skr_find(ske->repository, find,
-                                   silc_ske_skr_callback, ske));
-      } else {
-       /* Verify from application */
+      /* Find key from repository */
+      SILC_FSM_CALL(silc_skr_find(ske->repository,
+                                 silc_fsm_get_schedule(fsm), find,
+                                 silc_ske_skr_callback, ske));
+    } else {
+      /* Verify from application */
+      if (ske->callbacks->verify_key)
        SILC_FSM_CALL(ske->callbacks->verify_key(ske, ske->prop->public_key,
                                                 ske->callbacks->context,
                                                 silc_ske_pk_verified, NULL));
-      }
-      /* NOT REACHED */
     }
   }
 
@@ -1801,7 +2119,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase4)
     unsigned char hash[SILC_HASH_MAXLEN];
     SilcUInt32 hash_len;
 
-    SILC_LOG_DEBUG(("Public key is authentic"));
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
 
     /* Compute the hash value */
     status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
@@ -1896,27 +2214,28 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5)
     }
     ske->ke2_payload->pk_data = pk;
     ske->ke2_payload->pk_len = pk_len;
+  }
 
-    SILC_LOG_DEBUG(("Computing HASH value"));
+  SILC_LOG_DEBUG(("Computing HASH value"));
 
-    /* Compute the hash value */
-    memset(hash, 0, sizeof(hash));
-    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-    if (status != SILC_SKE_STATUS_OK) {
-      /** Error computing hash */
-      ske->status = status;
-      silc_fsm_next(fsm, silc_ske_st_responder_error);
-      return SILC_FSM_CONTINUE;
-    }
-
-    ske->hash = silc_memdup(hash, hash_len);
-    ske->hash_len = hash_len;
+  /* Compute the hash value */
+  memset(hash, 0, sizeof(hash));
+  status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error computing hash */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+  ske->hash = silc_memdup(hash, hash_len);
+  ske->hash_len = hash_len;
 
+  if (ske->public_key && ske->private_key) {
     SILC_LOG_DEBUG(("Signing HASH value"));
 
     /* Sign the hash value */
     if (!silc_pkcs_sign(ske->private_key, hash, hash_len, sign,
-                       sizeof(sign) - 1, &sign_len, NULL)) {
+                       sizeof(sign) - 1, &sign_len, FALSE, ske->prop->hash)) {
       /** Error computing signature */
       status = SILC_SKE_STATUS_SIGNATURE_ERROR;
       silc_fsm_next(fsm, silc_ske_st_responder_error);
@@ -1939,8 +2258,9 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5)
   }
 
   /* Send the packet. */
-  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE_2, 0,
-                       payload_buf->data, silc_buffer_len(payload_buf))) {
+  if (!silc_ske_packet_send(ske, SILC_PACKET_KEY_EXCHANGE_2, 0,
+                           payload_buf->data, silc_buffer_len(payload_buf))) {
+    SILC_LOG_DEBUG(("Error sending packet"));
     ske->status = SILC_SKE_STATUS_ERROR;
     silc_fsm_next(fsm, silc_ske_st_responder_error);
     return SILC_FSM_CONTINUE;
@@ -1948,6 +2268,13 @@ SILC_FSM_STATE(silc_ske_st_responder_phase5)
 
   silc_buffer_free(payload_buf);
 
+  /* In case we are doing rekey move to finish it. */
+  if (ske->rekey) {
+    /** Finish rekey */
+    silc_fsm_next(fsm, silc_ske_st_rekey_responder_done);
+    return SILC_FSM_CONTINUE;
+  }
+
   /** Waiting completion */
   silc_fsm_next(fsm, silc_ske_st_responder_end);
   return SILC_FSM_WAIT;
@@ -1961,25 +2288,23 @@ SILC_FSM_STATE(silc_ske_st_responder_end)
   unsigned char tmp[4];
   SilcUInt32 hash_len, key_len, block_len;
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
-    return SILC_FSM_CONTINUE;
-  }
-
-  /* Check the result of the protocol */
-  if (ske->packet->type == SILC_PACKET_FAILURE) {
-    silc_fsm_next(fsm, silc_ske_st_responder_failure);
-    return SILC_FSM_CONTINUE;
+  if (ske->packet->type != SILC_PACKET_SUCCESS) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_ske_install_retransmission(ske);
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
   silc_packet_free(ske->packet);
+  ske->packet = NULL;
 
   /* Process key material */
   key_len = silc_cipher_get_key_len(ske->prop->cipher);
-  block_len = silc_cipher_get_key_len(ske->prop->cipher);
+  block_len = silc_cipher_get_block_len(ske->prop->cipher);
   hash_len = silc_hash_len(ske->prop->hash);
   ske->keymat = silc_ske_process_key_material(ske, block_len,
-                                             key_len, hash_len);
+                                             key_len, hash_len,
+                                             &ske->rekey);
   if (!ske->keymat) {
     /** Error processing key material */
     ske->status = SILC_SKE_STATUS_ERROR;
@@ -1989,14 +2314,13 @@ SILC_FSM_STATE(silc_ske_st_responder_end)
 
   /* Send SUCCESS packet */
   SILC_PUT32_MSB(SILC_SKE_STATUS_OK, tmp);
-  silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, tmp, 4);
+  silc_ske_packet_send(ske, SILC_PACKET_SUCCESS, 0, tmp, 4);
 
   silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
 
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat,
-                             ske->rekey, ske->callbacks->context);
+  /* Call completion */
+  silc_ske_completion(ske);
 
   return SILC_FSM_FINISH;
 }
@@ -2012,9 +2336,13 @@ SILC_FSM_STATE(silc_ske_st_responder_aborted)
 
   /* Send FAILURE packet */
   SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, tmp);
-  silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4);
+  silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, tmp, 4);
 
   silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
 
   return SILC_FSM_FINISH;
 }
@@ -2028,17 +2356,18 @@ SILC_FSM_STATE(silc_ske_st_responder_failure)
 
   SILC_LOG_DEBUG(("Key exchange protocol failed"));
 
-  if (silc_buffer_len(&ske->packet->buffer) == 4)
+  if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) {
     SILC_GET32_MSB(error, ske->packet->buffer.data);
-  ske->status = error;
-
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
-                             ske->callbacks->context);
+    ske->status = error;
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+  }
 
-  silc_packet_free(ske->packet);
   silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
 
   return SILC_FSM_FINISH;
 }
@@ -2057,47 +2386,43 @@ SILC_FSM_STATE(silc_ske_st_responder_error)
   if (ske->status > SILC_SKE_STATUS_INVALID_COOKIE)
     ske->status = SILC_SKE_STATUS_BAD_PAYLOAD;
   SILC_PUT32_MSB(ske->status, tmp);
-  silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4);
+  silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, tmp, 4);
 
   silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
 
-  return SILC_FSM_FINISH;
-}
-
-
-static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
-                                       void *destructor_context)
-{
+  /* Call completion */
+  silc_ske_completion(ske);
 
+  return SILC_FSM_FINISH;
 }
 
 /* Starts the protocol as responder. */
 
-SilcAsyncOperation
-silc_ske_responder(SilcSKE ske,
-                  SilcPacketStream stream,
-                  SilcSKEParams params)
+SilcAsyncOperation silc_ske_responder(SilcSKE ske,
+                                     SilcPacketStream stream,
+                                     SilcSKEParams params)
 {
   SILC_LOG_DEBUG(("Start SKE as responder"));
 
-  if (!ske || !stream || !params || !params->version) {
+  if (!ske || !stream || !params || !params->version)
     return NULL;
-  }
 
   if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
     return NULL;
 
-  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske,
-                    ske->schedule))
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
     return NULL;
 
   ske->responder = TRUE;
   ske->flags = params->flags;
+  ske->timeout = params->timeout_secs ? params->timeout_secs : 30;
   if (ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)
     ske->session_port = params->session_port;
   ske->version = strdup(params->version);
   if (!ske->version)
     return NULL;
+  ske->running = TRUE;
 
   /* Link to packet stream to get key exchange packets */
   ske->stream = stream;
@@ -2113,10 +2438,222 @@ silc_ske_responder(SilcSKE ske,
   return &ske->op;
 }
 
-SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+/***************************** Initiator Rekey ******************************/
+
+/* Start rekey */
 
 SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
 {
+  SilcSKE ske = fsm_context;
+  SilcStatus status;
+
+  SILC_LOG_DEBUG(("Start rekey (%s)", ske->rekey->pfs ? "PFS" : "No PFS"));
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Add rekey exchange timeout */
+  silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+                                ske, 30, 0);
+
+  ske->prop = silc_calloc(1, sizeof(*ske->prop));
+  if (!ske->prop) {
+    /** No memory */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (!silc_hash_alloc(ske->rekey->hash, &ske->prop->hash)) {
+    /** Cannot allocate hash */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Send REKEY packet to start rekey protocol */
+  if (!silc_ske_packet_send(ske, SILC_PACKET_REKEY, 0, NULL, 0)) {
+    /** Error sending packet */
+    SILC_LOG_DEBUG(("Error sending packet"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* If doing rekey without PFS, move directly to the end of the protocol. */
+  if (!ske->rekey->pfs) {
+    /** Rekey without PFS */
+    silc_fsm_next(fsm, silc_ske_st_rekey_initiator_done);
+    return SILC_FSM_CONTINUE;
+  }
+
+  status = silc_ske_group_get_by_number(ske->rekey->ske_group,
+                                       &ske->prop->group);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Unknown group */
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Rekey with PFS */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Sends REKEY_DONE packet to finish the protocol. */
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_done)
+{
+  SilcSKE ske = fsm_context;
+  SilcCipher send_key;
+  SilcHmac hmac_send;
+  SilcHash hash;
+  SilcUInt32 key_len, block_len, hash_len, x_len;
+  unsigned char *pfsbuf;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  silc_packet_get_keys(ske->stream, &send_key, NULL, &hmac_send, NULL);
+  key_len = silc_cipher_get_key_len(send_key);
+  block_len = silc_cipher_get_block_len(send_key);
+  hash = ske->prop->hash;
+  hash_len = silc_hash_len(hash);
+
+  /* Process key material */
+  if (ske->rekey->pfs) {
+    /* PFS */
+    pfsbuf = silc_mp_mp2bin(ske->KEY, 0, &x_len);
+    if (pfsbuf) {
+      ske->keymat = silc_ske_process_key_material_data(pfsbuf, x_len,
+                                                      block_len, key_len,
+                                                      hash_len, hash);
+      memset(pfsbuf, 0, x_len);
+      silc_free(pfsbuf);
+    }
+  } else {
+    /* No PFS */
+    ske->keymat =
+      silc_ske_process_key_material_data(ske->rekey->send_enc_key,
+                                        ske->rekey->enc_key_len / 8,
+                                        block_len, key_len,
+                                        hash_len, hash);
+  }
+
+  if (!ske->keymat) {
+    SILC_LOG_ERROR(("Error processing key material"));
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  ske->prop->cipher = send_key;
+  ske->prop->hmac = hmac_send;
+
+  /* Get sending keys */
+  if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, &send_key, NULL,
+                        &hmac_send, NULL, NULL)) {
+    /** Cannot get keys */
+    ske->status = SILC_SKE_STATUS_ERROR;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  ske->prop->cipher = NULL;
+  ske->prop->hmac = NULL;
+
+  /* Set the new keys into use.  This will also send REKEY_DONE packet.  Any
+     packet sent after this call will be protected with the new keys. */
+  if (!silc_packet_set_keys(ske->stream, send_key, NULL, hmac_send, NULL,
+                           TRUE)) {
+    /** Cannot set keys */
+    SILC_LOG_DEBUG(("Cannot set new keys, error sending REKEY_DONE"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_cipher_free(send_key);
+    silc_hmac_free(hmac_send);
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Wait for REKEY_DONE */
+  silc_fsm_next(fsm, silc_ske_st_rekey_initiator_end);
+  return SILC_FSM_WAIT;
+}
+
+/* Rekey protocol end */
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_end)
+{
+  SilcSKE ske = fsm_context;
+  SilcCipher receive_key;
+  SilcHmac hmac_receive;
+  SilcSKERekeyMaterial rekey;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->packet->type != SILC_PACKET_REKEY_DONE) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
+  }
+
+  silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive);
+  ske->prop->cipher = receive_key;
+  ske->prop->hmac = hmac_receive;
+
+  /* Get receiving keys */
+  if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, NULL, &receive_key,
+                        NULL, &hmac_receive, NULL)) {
+    /** Cannot get keys */
+    ske->status = SILC_SKE_STATUS_ERROR;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Set new receiving keys into use.  All packets received after this will
+     be decrypted with the new keys. */
+  if (!silc_packet_set_keys(ske->stream, NULL, receive_key, NULL,
+                           hmac_receive, FALSE)) {
+    /** Cannot set keys */
+    SILC_LOG_DEBUG(("Cannot set new keys"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_cipher_free(receive_key);
+    silc_hmac_free(hmac_receive);
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  SILC_LOG_DEBUG(("Rekey completed successfully"));
+
+  /* Generate new rekey material */
+  rekey = silc_ske_make_rekey_material(ske, ske->keymat);
+  if (!rekey) {
+    /** No memory */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+  rekey->pfs = ske->rekey->pfs;
+  ske->rekey = rekey;
+
+  ske->prop->cipher = NULL;
+  ske->prop->hmac = NULL;
+  silc_packet_free(ske->packet);
+  ske->packet = NULL;
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
+
   return SILC_FSM_FINISH;
 }
 
@@ -2129,19 +2666,31 @@ silc_ske_rekey_initiator(SilcSKE ske,
 {
   SILC_LOG_DEBUG(("Start SKE rekey as initator"));
 
-  if (!ske || !stream || !rekey)
+  if (!ske || !stream || !rekey) {
+    SILC_LOG_ERROR(("Missing arguments to silc_ske_rekey_initiator"));
+    SILC_ASSERT(rekey);
     return NULL;
+  }
 
   if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
     return NULL;
 
-  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
     return NULL;
 
   ske->rekey = rekey;
+  ske->responder = FALSE;
+  ske->running = TRUE;
+  ske->rekeying = TRUE;
 
   /* Link to packet stream to get key exchange packets */
   ske->stream = stream;
+  silc_packet_stream_link(ske->stream, &silc_ske_stream_cbs, ske, 1000000,
+                         SILC_PACKET_REKEY,
+                         SILC_PACKET_REKEY_DONE,
+                         SILC_PACKET_KEY_EXCHANGE_2,
+                         SILC_PACKET_SUCCESS,
+                         SILC_PACKET_FAILURE, -1);
 
   /* Start SKE rekey as initiator */
   silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
@@ -2149,10 +2698,241 @@ silc_ske_rekey_initiator(SilcSKE ske,
   return &ske->op;
 }
 
-SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
+/***************************** Responder Rekey ******************************/
+
+/* Wait for initiator's packet */
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_wait)
+{
+  SilcSKE ske = fsm_context;
+
+  SILC_LOG_DEBUG(("Start rekey (%s)", ske->rekey->pfs ? "PFS" : "No PFS"));
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Add rekey exchange timeout */
+  silc_schedule_task_add_timeout(ske->schedule, silc_ske_timeout,
+                                ske, 30, 0);
+
+  silc_fsm_next(fsm, silc_ske_st_rekey_responder_start);
+
+  /* If REKEY packet already received process it directly */
+  if (ske->packet && ske->packet->type == SILC_PACKET_REKEY)
+    return SILC_FSM_CONTINUE;
+
+  /* Wait for REKEY */
+  return SILC_FSM_WAIT;
+}
+
+/* Process initiator's REKEY packet */
 
 SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
 {
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->packet->type != SILC_PACKET_REKEY) {
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  ske->prop = silc_calloc(1, sizeof(*ske->prop));
+  if (!ske->prop) {
+    /** No memory */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (!silc_hash_alloc(ske->rekey->hash, &ske->prop->hash)) {
+    /** Cannot allocate hash */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* If doing rekey without PFS, move directly to the end of the protocol. */
+  if (!ske->rekey->pfs) {
+    /** Rekey without PFS */
+    silc_fsm_next(fsm, silc_ske_st_rekey_responder_done);
+    return SILC_FSM_CONTINUE;
+  }
+
+  status = silc_ske_group_get_by_number(ske->rekey->ske_group,
+                                       &ske->prop->group);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Unknown group */
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Rekey with PFS */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+  return SILC_FSM_WAIT;
+}
+
+/* Sends REKEY_DONE packet to finish the protocol. */
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_done)
+{
+  SilcSKE ske = fsm_context;
+  SilcCipher send_key;
+  SilcHmac hmac_send;
+  SilcHash hash;
+  SilcUInt32 key_len, block_len, hash_len, x_len;
+  unsigned char *pfsbuf;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  silc_packet_get_keys(ske->stream, &send_key, NULL, &hmac_send, NULL);
+  key_len = silc_cipher_get_key_len(send_key);
+  block_len = silc_cipher_get_block_len(send_key);
+  hash = ske->prop->hash;
+  hash_len = silc_hash_len(hash);
+
+  /* Process key material */
+  if (ske->rekey->pfs) {
+    /* PFS */
+    pfsbuf = silc_mp_mp2bin(ske->KEY, 0, &x_len);
+    if (pfsbuf) {
+      ske->keymat = silc_ske_process_key_material_data(pfsbuf, x_len,
+                                                      block_len, key_len,
+                                                      hash_len, hash);
+      memset(pfsbuf, 0, x_len);
+      silc_free(pfsbuf);
+    }
+  } else {
+    /* No PFS */
+    ske->keymat =
+      silc_ske_process_key_material_data(ske->rekey->send_enc_key,
+                                        ske->rekey->enc_key_len / 8,
+                                        block_len, key_len,
+                                        hash_len, hash);
+  }
+
+  if (!ske->keymat) {
+    SILC_LOG_ERROR(("Error processing key material"));
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  ske->prop->cipher = send_key;
+  ske->prop->hmac = hmac_send;
+
+  /* Get sending keys */
+  if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, &send_key, NULL,
+                        &hmac_send, NULL, NULL)) {
+    /** Cannot get keys */
+    ske->status = SILC_SKE_STATUS_ERROR;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  ske->prop->cipher = NULL;
+  ske->prop->hmac = NULL;
+
+  /* Set the new keys into use.  This will also send REKEY_DONE packet.  Any
+     packet sent after this call will be protected with the new keys. */
+  if (!silc_packet_set_keys(ske->stream, send_key, NULL, hmac_send, NULL,
+                           TRUE)) {
+    /** Cannot set keys */
+    SILC_LOG_DEBUG(("Cannot set new keys, error sending REKEY_DONE"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_cipher_free(send_key);
+    silc_hmac_free(hmac_send);
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /** Wait for REKEY_DONE */
+  silc_fsm_next(fsm, silc_ske_st_rekey_responder_end);
+  return SILC_FSM_WAIT;
+}
+
+/* Rekey protocol end */
+
+SILC_FSM_STATE(silc_ske_st_rekey_responder_end)
+{
+  SilcSKE ske = fsm_context;
+  SilcCipher receive_key;
+  SilcHmac hmac_receive;
+  SilcSKERekeyMaterial rekey;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->packet->type != SILC_PACKET_REKEY_DONE) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_packet_free(ske->packet);
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
+  }
+
+  silc_packet_get_keys(ske->stream, NULL, &receive_key, NULL, &hmac_receive);
+  ske->prop->cipher = receive_key;
+  ske->prop->hmac = hmac_receive;
+
+  /* Get receiving keys */
+  if (!silc_ske_set_keys(ske, ske->keymat, ske->prop, NULL, &receive_key,
+                        NULL, &hmac_receive, NULL)) {
+    /** Cannot get keys */
+    ske->status = SILC_SKE_STATUS_ERROR;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Set new receiving keys into use.  All packets received after this will
+     be decrypted with the new keys. */
+  if (!silc_packet_set_keys(ske->stream, NULL, receive_key, NULL,
+                           hmac_receive, FALSE)) {
+    /** Cannot set keys */
+    SILC_LOG_DEBUG(("Cannot set new keys"));
+    ske->status = SILC_SKE_STATUS_ERROR;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_cipher_free(receive_key);
+    silc_hmac_free(hmac_receive);
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  SILC_LOG_DEBUG(("Rekey completed successfully"));
+
+  /* Generate new rekey material */
+  rekey = silc_ske_make_rekey_material(ske, ske->keymat);
+  if (!rekey) {
+    /** No memory */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    ske->prop->cipher = NULL;
+    ske->prop->hmac = NULL;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
+  rekey->pfs = ske->rekey->pfs;
+  ske->rekey = rekey;
+
+  ske->prop->cipher = NULL;
+  ske->prop->hmac = NULL;
+  silc_packet_free(ske->packet);
+  ske->packet = NULL;
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  /* Call completion */
+  silc_ske_completion(ske);
+
   return SILC_FSM_FINISH;
 }
 
@@ -2161,30 +2941,37 @@ SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
 SilcAsyncOperation
 silc_ske_rekey_responder(SilcSKE ske,
                         SilcPacketStream stream,
-                        SilcBuffer ke_payload,
-                        SilcSKERekeyMaterial rekey)
+                        SilcSKERekeyMaterial rekey,
+                        SilcPacket packet)
 {
   SILC_LOG_DEBUG(("Start SKE rekey as responder"));
 
   if (!ske || !stream || !rekey)
     return NULL;
-  if (rekey->pfs && !ke_payload)
-    return NULL;
 
   if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
     return NULL;
 
-  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_finished, ske, ske->schedule))
     return NULL;
 
-  //  ske->packet_buf = ke_payload;
   ske->rekey = rekey;
+  ske->responder = TRUE;
+  ske->running = TRUE;
+  ske->rekeying = TRUE;
+  ske->packet = packet;
 
   /* Link to packet stream to get key exchange packets */
   ske->stream = stream;
+  silc_packet_stream_link(ske->stream, &silc_ske_stream_cbs, ske, 1000000,
+                         SILC_PACKET_REKEY,
+                         SILC_PACKET_REKEY_DONE,
+                         SILC_PACKET_KEY_EXCHANGE_1,
+                         SILC_PACKET_SUCCESS,
+                         SILC_PACKET_FAILURE, -1);
 
   /* Start SKE rekey as responder */
-  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start);
+  silc_fsm_start_sync(&ske->fsm, silc_ske_st_rekey_responder_wait);
 
   return &ske->op;
 }
@@ -2220,7 +3007,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     return NULL;
   silc_buffer_format(buf,
                     SILC_STR_UI_CHAR(0),
-                    SILC_STR_UI_XNSTRING(data, data_len),
+                    SILC_STR_DATA(data, data_len),
                     SILC_STR_END);
 
   /* Take IVs */
@@ -2259,8 +3046,8 @@ silc_ske_process_key_material_data(unsigned char *data,
     if (!dist)
       return NULL;
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(data, data_len),
-                      SILC_STR_UI_XNSTRING(k1, hash_len),
+                      SILC_STR_DATA(data, data_len),
+                      SILC_STR_DATA(k1, hash_len),
                       SILC_STR_END);
     memset(k2, 0, sizeof(k2));
     silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
@@ -2270,7 +3057,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     silc_buffer_pull_tail(dist, hash_len);
     silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(k2, hash_len),
+                      SILC_STR_DATA(k2, hash_len),
                       SILC_STR_END);
     silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
@@ -2322,8 +3109,8 @@ silc_ske_process_key_material_data(unsigned char *data,
     if (!dist)
       return NULL;
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(data, data_len),
-                      SILC_STR_UI_XNSTRING(k1, hash_len),
+                      SILC_STR_DATA(data, data_len),
+                      SILC_STR_DATA(k1, hash_len),
                       SILC_STR_END);
     memset(k2, 0, sizeof(k2));
     silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
@@ -2333,7 +3120,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     silc_buffer_pull_tail(dist, hash_len);
     silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(k2, hash_len),
+                      SILC_STR_DATA(k2, hash_len),
                       SILC_STR_END);
     silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
@@ -2382,6 +3169,8 @@ silc_ske_process_key_material_data(unsigned char *data,
   silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
+  SILC_LOG_HEXDUMP(("enc"), key->send_enc_key, key->enc_key_len / 8);
+
   return key;
 }
 
@@ -2392,7 +3181,8 @@ SilcSKEKeyMaterial
 silc_ske_process_key_material(SilcSKE ske,
                              SilcUInt32 req_iv_len,
                              SilcUInt32 req_enc_key_len,
-                             SilcUInt32 req_hmac_key_len)
+                             SilcUInt32 req_hmac_key_len,
+                             SilcSKERekeyMaterial *rekey)
 {
   SilcBuffer buf;
   unsigned char *tmpbuf;
@@ -2406,8 +3196,8 @@ silc_ske_process_key_material(SilcSKE ske,
   if (!buf)
     return NULL;
   silc_buffer_format(buf,
-                    SILC_STR_UI_XNSTRING(tmpbuf, klen),
-                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_DATA(tmpbuf, klen),
+                    SILC_STR_DATA(ske->hash, ske->hash_len),
                     SILC_STR_END);
 
   /* Process the key material */
@@ -2421,6 +3211,12 @@ silc_ske_process_key_material(SilcSKE ske,
   silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
+  if (rekey) {
+    *rekey = silc_ske_make_rekey_material(ske, key);
+    if (!(*rekey))
+      return NULL;
+  }
+
   return key;
 }
 
@@ -2454,6 +3250,20 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial key)
   silc_free(key);
 }
 
+/* Free rekey material */
+
+void silc_ske_free_rekey_material(SilcSKERekeyMaterial rekey)
+{
+  if (!rekey)
+    return;
+  if (rekey->send_enc_key) {
+    memset(rekey->send_enc_key, 0, rekey->enc_key_len / 8);
+    silc_free(rekey->send_enc_key);
+  }
+  silc_free(rekey->hash);
+  silc_free(rekey);
+}
+
 /* Set keys into use */
 
 SilcBool silc_ske_set_keys(SilcSKE ske,
@@ -2465,6 +3275,9 @@ SilcBool silc_ske_set_keys(SilcSKE ske,
                           SilcHmac *ret_hmac_receive,
                           SilcHash *ret_hash)
 {
+  unsigned char iv[SILC_HASH_MAXLEN];
+  SilcBool iv_included = (prop->flags & SILC_SKE_SP_FLAG_IV_INCLUDED);
+
   /* Allocate ciphers to be used in the communication */
   if (ret_send_key) {
     if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher),
@@ -2489,42 +3302,134 @@ SilcBool silc_ske_set_keys(SilcSKE ske,
       return FALSE;
   }
 
-  /* Set key material */
-  if (ske->responder) {
-    silc_cipher_set_key(*ret_send_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(*ret_send_key, keymat->receive_iv);
-    silc_cipher_set_key(*ret_receive_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(*ret_receive_key, keymat->send_iv);
-    silc_hmac_set_key(*ret_hmac_send, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(*ret_hmac_receive, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-  } else {
-    silc_cipher_set_key(*ret_send_key, keymat->send_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(*ret_send_key, keymat->send_iv);
-    silc_cipher_set_key(*ret_receive_key, keymat->receive_enc_key,
-                       keymat->enc_key_len);
-    silc_cipher_set_iv(*ret_receive_key, keymat->receive_iv);
-    silc_hmac_set_key(*ret_hmac_send, keymat->send_hmac_key,
-                     keymat->hmac_key_len);
-    silc_hmac_set_key(*ret_hmac_receive, keymat->receive_hmac_key,
-                     keymat->hmac_key_len);
-  }
-
   /* Allocate hash */
   if (ret_hash) {
     if (!silc_hash_alloc(silc_hash_get_name(prop->hash), ret_hash))
       return FALSE;
   }
 
-  SILC_LOG_INFO(("Security properties: %s %s %s %s",
-                ret_send_key ? silc_cipher_get_name(*ret_send_key) : "??",
-                ret_hmac_send ? silc_hmac_get_name(*ret_hmac_send) : "??",
-                ret_hash ? silc_hash_get_name(*ret_hash) : "??",
-                ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? "PFS" : ""));
+  /* Set key material */
+  memset(iv, 0, sizeof(iv));
+  if (ske->responder) {
+    if (ret_send_key) {
+      silc_cipher_set_key(*ret_send_key, keymat->receive_enc_key,
+                         keymat->enc_key_len, TRUE);
+
+      if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) {
+       /* Counter mode */
+       if (!ske->rekeying) {
+         /* Set IV. */
+         memcpy(iv, ske->hash, 4);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->receive_iv, 8);
+       } else {
+         /* Rekey, recompute the truncated hash value. */
+         silc_hash_make(prop->hash, keymat->receive_iv, 8, iv);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->receive_iv, 8);
+         else
+           memset(iv + 4, 0, 12);
+       }
+
+        silc_cipher_set_iv(*ret_send_key, iv);
+      } else {
+       /* Other modes */
+       silc_cipher_set_iv(*ret_send_key, keymat->receive_iv);
+      }
+    }
+    if (ret_receive_key) {
+      silc_cipher_set_key(*ret_receive_key, keymat->send_enc_key,
+                         keymat->enc_key_len, FALSE);
+
+      if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) {
+       /* Counter mode */
+       if (!ske->rekeying) {
+         /* Set IV. */
+         memcpy(iv, ske->hash, 4);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->send_iv, 8);
+       } else {
+         /* Rekey, recompute the truncated hash value. */
+         silc_hash_make(prop->hash, keymat->send_iv, 8, iv);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->send_iv, 8);
+         else
+           memset(iv + 4, 0, 12);
+       }
+
+        silc_cipher_set_iv(*ret_receive_key, iv);
+      } else {
+       /* Other modes */
+       silc_cipher_set_iv(*ret_receive_key, keymat->send_iv);
+      }
+    }
+    if (ret_hmac_send)
+      silc_hmac_set_key(*ret_hmac_send, keymat->receive_hmac_key,
+                       keymat->hmac_key_len);
+    if (ret_hmac_receive)
+      silc_hmac_set_key(*ret_hmac_receive, keymat->send_hmac_key,
+                       keymat->hmac_key_len);
+  } else {
+    if (ret_send_key) {
+      silc_cipher_set_key(*ret_send_key, keymat->send_enc_key,
+                         keymat->enc_key_len, TRUE);
+
+      if (silc_cipher_get_mode(*ret_send_key) == SILC_CIPHER_MODE_CTR) {
+       /* Counter mode */
+       if (!ske->rekeying) {
+         /* Set IV. */
+         memcpy(iv, ske->hash, 4);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->send_iv, 8);
+       } else {
+         /* Rekey, recompute the truncated hash value. */
+         silc_hash_make(prop->hash, keymat->send_iv, 8, iv);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->send_iv, 8);
+         else
+           memset(iv + 4, 0, 12);
+       }
+
+       silc_cipher_set_iv(*ret_send_key, iv);
+      } else {
+       /* Other modes */
+       silc_cipher_set_iv(*ret_send_key, keymat->send_iv);
+      }
+    }
+    if (ret_receive_key) {
+      silc_cipher_set_key(*ret_receive_key, keymat->receive_enc_key,
+                         keymat->enc_key_len, FALSE);
+
+      if (silc_cipher_get_mode(*ret_receive_key) == SILC_CIPHER_MODE_CTR) {
+       /* Counter mode */
+       if (!ske->rekeying) {
+         /* Set IV.  If IV Included flag was negotiated we only set the
+            truncated hash value. */
+         memcpy(iv, ske->hash, 4);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->receive_iv, 8);
+       } else {
+         /* Rekey, recompute the truncated hash value. */
+         silc_hash_make(prop->hash, keymat->receive_iv, 8, iv);
+         if (!iv_included)
+           memcpy(iv + 4, keymat->receive_iv, 8);
+         else
+           memset(iv + 4, 0, 12);
+       }
+
+       silc_cipher_set_iv(*ret_receive_key, iv);
+      } else {
+       /* Other modes */
+       silc_cipher_set_iv(*ret_receive_key, keymat->receive_iv);
+      }
+    }
+    if (ret_hmac_send)
+      silc_hmac_set_key(*ret_hmac_send, keymat->send_hmac_key,
+                       keymat->hmac_key_len);
+    if (ret_hmac_receive)
+      silc_hmac_set_key(*ret_hmac_receive, keymat->receive_hmac_key,
+                       keymat->hmac_key_len);
+  }
 
   return TRUE;
 }
@@ -2533,7 +3438,7 @@ const char *silc_ske_status_string[] =
 {
   /* Official */
   "Ok",
-  "Unkown error occurred",
+  "Unexpected error occurred",
   "Bad payload in packet",
   "Unsupported group",
   "Unsupported cipher",
@@ -2551,6 +3456,7 @@ const char *silc_ske_status_string[] =
   "Bad payload length in packet",
   "Error computing signature",
   "System out of memory",
+  "Key exchange timeout",
 
   NULL
 };
@@ -2585,3 +3491,17 @@ SilcBool silc_ske_parse_version(SilcSKE ske,
                                   software_version_string,
                                   vendor_version);
 }
+
+/* Get security properties */
+
+SilcSKESecurityProperties silc_ske_get_security_properties(SilcSKE ske)
+{
+  return ske->prop;
+}
+
+/* Get key material */
+
+SilcSKEKeyMaterial silc_ske_get_key_material(SilcSKE ske)
+{
+  return ske->keymat;
+}