Added initiator rekey support.
[silc.git] / lib / silcske / silcske.c
index 021937c81df53d4da57d5d5bf850c3c4ed71d72c..4a871aa881671059854b029431bd61be04353e4c 100644 (file)
@@ -31,15 +31,42 @@ struct SilcSKECallbacksStruct {
   void *context;
 };
 
-
 /************************ Static utility functions **************************/
 
+/* 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);
+
 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 */
 
@@ -50,8 +77,43 @@ 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_context(ske->schedule, ske);
+
+  /* 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 synchronously */
+  if (ske->rekeying)
+    silc_fsm_continue_sync(&ske->fsm);
+  else
+    silc_fsm_continue(&ske->fsm);
+
   return TRUE;
 }
 
@@ -387,8 +449,7 @@ silc_ske_select_security_properties(SilcSKE ske,
   }
 
   /* Save selected cipher to security properties */
-  if (silc_cipher_alloc(payload->enc_alg_list,
-                       &(*prop)->cipher) == FALSE) {
+  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);
@@ -446,8 +507,7 @@ silc_ske_select_security_properties(SilcSKE ske,
   }
 
   /* Save selected hash algorithm to security properties */
-  if (silc_hash_alloc(ske->start_payload->hash_alg_list,
-                     &(*prop)->hash) == FALSE) {
+  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);
@@ -507,8 +567,7 @@ silc_ske_select_security_properties(SilcSKE ske,
   }
 
   /* Save selected HMACc to security properties */
-  if (silc_hmac_alloc(ske->start_payload->hmac_alg_list, NULL,
-                     &(*prop)->hmac) == FALSE) {
+  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);
@@ -733,6 +792,41 @@ 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) {
+    rekey->pfs = (ske->prop->flags & SILC_SKE_SP_FLAG_PFS ? TRUE : FALSE);
+    rekey->ske_group = silc_ske_group_get_number(ske->prop->group);
+    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);
+    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
@@ -799,6 +893,99 @@ 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;
+}
+
+/* SKE FSM destructor.  We call completion callback here.  All SKE
+   machines go here and call the completion.  Completion must not be called
+   from any other place. */
+
+static void silc_ske_finished(SilcFSM fsm, void *fsm_context,
+                             void *destructor_context)
+{
+  SilcSKE ske = fsm_context;
+
+  /* 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->running = FALSE;
+  if (ske->freed)
+    silc_ske_free(ske);
+}
 
 /******************************* Protocol API *******************************/
 
@@ -812,9 +999,14 @@ SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
 
   SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
 
-  if (!rng || !schedule || !public_key)
+  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;
@@ -825,6 +1017,7 @@ 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;
 
   return ske;
 }
@@ -835,46 +1028,55 @@ 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;
+    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);
+    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 */
@@ -904,17 +1106,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);
-SILC_FSM_STATE(silc_ske_st_initiator_failure);
-
 /* Start protocol.  Send our proposal */
 
 SILC_FSM_STATE(silc_ske_st_initiator_start)
@@ -946,10 +1137,11 @@ SILC_FSM_STATE(silc_ske_st_initiator_start)
   ske->start_payload_copy = payload_buf;
 
   /* Send the packet. */
-  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0,
-                       silc_buffer_data(payload_buf),
-                       silc_buffer_len(payload_buf))) {
+  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;
@@ -971,7 +1163,7 @@ 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;
@@ -979,11 +1171,12 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
+  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);
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
   /* Decode the payload */
@@ -991,6 +1184,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
   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;
@@ -1010,6 +1204,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
   }
 
   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
@@ -1083,9 +1278,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)
@@ -1176,8 +1370,7 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2)
   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;
 
@@ -1230,10 +1423,11 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase2)
   /* Check for backwards compatibility */
 
   /* Send the packet. */
-  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE_1, 0,
-                       silc_buffer_data(payload_buf),
-                       silc_buffer_len(payload_buf))) {
+  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;
@@ -1258,11 +1452,12 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
+  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);
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
   /* Decode the payload */
@@ -1270,11 +1465,13 @@ SILC_FSM_STATE(silc_ske_st_initiator_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_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)) {
@@ -1410,12 +1607,20 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4)
 
   ske->status = SILC_SKE_STATUS_OK;
 
+  /* 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;
@@ -1424,8 +1629,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase4)
 
   /* Send SUCCESS packet */
   SILC_PUT32_MSB((SilcUInt32)SILC_SKE_STATUS_OK, hash);
-  if (!silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, hash, 4)) {
+  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;
@@ -1467,27 +1673,20 @@ SILC_FSM_STATE(silc_ske_st_initiator_end)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
-  }
-
-  /* See if received failure from remote */
-  if (ske->packet->type == SILC_PACKET_FAILURE) {
+  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);
-    silc_fsm_next(fsm, silc_ske_st_initiator_failure);
-    return SILC_FSM_CONTINUE;
+    ske->packet = NULL;
+    return SILC_FSM_WAIT;
   }
 
   SILC_LOG_DEBUG(("Key exchange completed successfully"));
 
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat,
-                             ske->rekey, ske->user_data);
-
   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);
 
   return SILC_FSM_FINISH;
 }
@@ -1503,7 +1702,10 @@ SILC_FSM_STATE(silc_ske_st_initiator_aborted)
 
   /* Send FAILURE packet */
   SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, data);
-  silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, data, 4);
+  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);
 
   return SILC_FSM_FINISH;
 }
@@ -1525,11 +1727,10 @@ SILC_FSM_STATE(silc_ske_st_initiator_error)
 
   /* Send FAILURE packet */
   SILC_PUT32_MSB((SilcUInt32)status, data);
-  silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, data, 4);
+  silc_ske_packet_send(ske, SILC_PACKET_FAILURE, 0, data, 4);
 
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
 
   return SILC_FSM_FINISH;
 }
@@ -1539,24 +1740,22 @@ SILC_FSM_STATE(silc_ske_st_initiator_error)
 SILC_FSM_STATE(silc_ske_st_initiator_failure)
 {
   SilcSKE ske = fsm_context;
+  SilcUInt32 error = SILC_SKE_STATUS_ERROR;
 
   SILC_LOG_DEBUG(("Error %s (%d) received during key exchange",
                  silc_ske_map_status(ske->status), ske->status));
 
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
-
-  return SILC_FSM_FINISH;
-}
-
-/* FSM destructor */
+  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;
+  }
 
-static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
-                                       void *destructor_context)
-{
-  SilcSKE ske = fsm_context;
   silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
+  return SILC_FSM_FINISH;
 }
 
 /* Starts the protocol as initiator */
@@ -1575,8 +1774,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)
@@ -1593,6 +1791,7 @@ silc_ske_initiator(SilcSKE ske,
 
   ske->start_payload = start_payload;
   ske->version = params->version;
+  ske->running = TRUE;
 
   /* Link to packet stream to get key exchange packets */
   ske->stream = stream;
@@ -1608,19 +1807,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_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)
@@ -1655,25 +1843,12 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1)
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_packet_free(ske->packet);
-    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_packet_free(ske->packet);
-    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;
@@ -1684,6 +1859,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1)
   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) {
@@ -1724,9 +1900,9 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1)
     goto err;
 
   /* Send the packet. */
-  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0,
-                       silc_buffer_data(packet_buf),
-                       silc_buffer_len(packet_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(packet_buf);
@@ -1767,16 +1943,12 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2)
 
   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 */
@@ -1784,6 +1956,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2)
   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;
@@ -1792,6 +1965,7 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2)
   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. */
@@ -2033,8 +2207,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;
@@ -2055,25 +2230,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;
@@ -2083,14 +2256,10 @@ 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);
-
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat,
-                             ske->rekey, ske->callbacks->context);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
 
   return SILC_FSM_FINISH;
 }
@@ -2106,9 +2275,10 @@ 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);
 
   return SILC_FSM_FINISH;
 }
@@ -2122,17 +2292,15 @@ 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);
 
   return SILC_FSM_FINISH;
 }
@@ -2151,20 +2319,14 @@ 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)
-{
-
-}
-
 /* Starts the protocol as responder. */
 
 SilcAsyncOperation
@@ -2174,15 +2336,13 @@ silc_ske_responder(SilcSKE ske,
 {
   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;
@@ -2192,6 +2352,7 @@ silc_ske_responder(SilcSKE ske,
   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;
@@ -2207,10 +2368,199 @@ 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;
+  }
+
+  /* XXX timeout */
+
+  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;
+  }
+
+  /* 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);
+
+  if (!silc_hash_alloc(ske->rekey->hash, &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;
+  }
+  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;
+  ske->prop->hash = hash;
+
+  /* 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;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* 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_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;
+    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_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);
+  rekey->pfs = ske->rekey->pfs;
+  rekey->ske_group = ske->rekey->ske_group;
+  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);
+
   return SILC_FSM_FINISH;
 }
 
@@ -2229,13 +2579,22 @@ silc_ske_rekey_initiator(SilcSKE ske,
   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);
@@ -2243,6 +2602,8 @@ silc_ske_rekey_initiator(SilcSKE ske,
   return &ske->op;
 }
 
+/***************************** Responder Rekey ******************************/
+
 SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
 
 SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
@@ -2255,27 +2616,32 @@ SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
 SilcAsyncOperation
 silc_ske_rekey_responder(SilcSKE ske,
                         SilcPacketStream stream,
-                        SilcBuffer ke_payload,
                         SilcSKERekeyMaterial rekey)
 {
   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;
 
   /* 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);
@@ -2476,6 +2842,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;
 }
 
@@ -2486,7 +2854,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;
@@ -2515,6 +2884,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;
 }
 
@@ -2548,6 +2923,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);
+    silc_free(rekey->send_enc_key);
+  }
+  silc_free(rekey->hash);
+  silc_free(rekey);
+}
+
 /* Set keys into use */
 
 SilcBool silc_ske_set_keys(SilcSKE ske,
@@ -2585,27 +2974,39 @@ SilcBool silc_ske_set_keys(SilcSKE ske,
 
   /* 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);
+    if (ret_send_key) {
+      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);
+    }
+    if (ret_receive_key) {
+      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);
+    }
+    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 {
-    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);
+    if (ret_send_key) {
+      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);
+    }
+    if (ret_receive_key) {
+      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);
+    }
+    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);
   }
 
   /* Allocate hash */
@@ -2614,12 +3015,6 @@ SilcBool silc_ske_set_keys(SilcSKE ske,
       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" : ""));
-
   return TRUE;
 }
 
@@ -2686,3 +3081,10 @@ 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;
+}