Added packet retransmission support with UDP transport.
[silc.git] / lib / silcske / silcske.c
index d4ad07ab0e1de381c0e3c7aed01638e300fb3af3..3276a58d9577d5deb37ca9605c5e0ed20617295c 100644 (file)
@@ -31,15 +31,37 @@ struct SilcSKECallbacksStruct {
   void *context;
 };
 
-
 /************************ Static utility functions **************************/
 
+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);
+
 SilcSKEKeyMaterial
 silc_ske_process_key_material(SilcSKE ske,
                              SilcUInt32 req_iv_len,
                              SilcUInt32 req_enc_key_len,
                              SilcUInt32 req_hmac_key_len);
-
+static SilcBool silc_ske_packet_send(SilcSKE ske,
+                                    SilcPacketType type,
+                                    SilcPacketFlags flags,
+                                    const unsigned char *data,
+                                    SilcUInt32 data_len);
 
 /* Packet callback */
 
@@ -387,8 +409,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 +467,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 +527,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);
@@ -799,6 +818,65 @@ 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) {
+    SILC_LOG_DEBUG(("Retry 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);
+}
+
+/* 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) {
+    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;
+    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));
+  }
+
+  return ret;
+}
+
 
 /******************************* Protocol API *******************************/
 
@@ -812,8 +890,13 @@ 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)
@@ -825,6 +908,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 +919,53 @@ 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->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 +995,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,9 +1026,9 @@ 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;
@@ -972,7 +1052,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;
@@ -980,6 +1060,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
 
   SILC_LOG_DEBUG(("Start"));
 
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
   if (ske->aborted) {
     /** Aborted */
     silc_packet_free(ske->packet);
@@ -987,6 +1070,19 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase1)
     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_initiator_failure);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_packet_free(ske->packet);
+    return SILC_FSM_WAIT;
+  }
+
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, packet_buf, &payload);
   if (status != SILC_SKE_STATUS_OK) {
@@ -1084,9 +1180,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)
@@ -1231,9 +1326,9 @@ 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;
@@ -1260,6 +1355,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3)
 
   SILC_LOG_DEBUG(("Start"));
 
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
   if (ske->aborted) {
     /** Aborted */
     silc_packet_free(ske->packet);
@@ -1267,6 +1365,19 @@ SILC_FSM_STATE(silc_ske_st_initiator_phase3)
     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_initiator_failure);
+    return SILC_FSM_CONTINUE;
+  }
+
+  if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_2) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_packet_free(ske->packet);
+    return SILC_FSM_WAIT;
+  }
+
   /* Decode the payload */
   status = silc_ske_payload_ke_decode(ske, packet_buf, &payload);
   if (status != SILC_SKE_STATUS_OK) {
@@ -1426,7 +1537,7 @@ 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;
@@ -1470,6 +1581,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_end)
 
   SILC_LOG_DEBUG(("Start"));
 
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
   if (ske->aborted) {
     /** Aborted */
     silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
@@ -1483,6 +1597,12 @@ SILC_FSM_STATE(silc_ske_st_initiator_end)
     return SILC_FSM_CONTINUE;
   }
 
+  if (ske->packet->type != SILC_PACKET_SUCCESS) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_packet_free(ske->packet);
+    return SILC_FSM_WAIT;
+  }
+
   SILC_LOG_DEBUG(("Key exchange completed successfully"));
 
   /* Call the completion callback */
@@ -1491,6 +1611,8 @@ SILC_FSM_STATE(silc_ske_st_initiator_end)
                              ske->rekey, ske->callbacks->context);
 
   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;
 }
@@ -1506,7 +1628,9 @@ 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;
 }
@@ -1528,13 +1652,16 @@ 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,
                              ske->callbacks->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;
 }
 
@@ -1552,6 +1679,9 @@ SILC_FSM_STATE(silc_ske_st_initiator_failure)
     ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
                              ske->callbacks->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;
 }
 
@@ -1561,7 +1691,10 @@ 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);
+
+  ske->running = FALSE;
+  if (ske->freed)
+    silc_ske_free(ske);
 }
 
 /* Starts the protocol as initiator */
@@ -1598,6 +1731,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;
@@ -1613,19 +1747,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)
@@ -1729,9 +1852,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);
@@ -1772,6 +1895,9 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2)
 
   SILC_LOG_DEBUG(("Start"));
 
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
   if (ske->aborted) {
     /** Aborted */
     silc_fsm_next(fsm, silc_ske_st_responder_aborted);
@@ -1784,6 +1910,12 @@ SILC_FSM_STATE(silc_ske_st_responder_phase2)
     return SILC_FSM_CONTINUE;
   }
 
+  if (ske->packet->type != SILC_PACKET_KEY_EXCHANGE_1) {
+    SILC_LOG_DEBUG(("Remote retransmitted an old packet"));
+    silc_packet_free(ske->packet);
+    return SILC_FSM_WAIT;
+  }
+
   /* Decode Key Exchange Payload */
   status = silc_ske_payload_ke_decode(ske, packet_buf, &recv_payload);
   if (status != SILC_SKE_STATUS_OK) {
@@ -2038,8 +2170,8 @@ 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);
@@ -2061,6 +2193,9 @@ SILC_FSM_STATE(silc_ske_st_responder_end)
   unsigned char tmp[4];
   SilcUInt32 hash_len, key_len, block_len;
 
+  ske->retry_timer = SILC_SKE_RETRY_MIN;
+  silc_schedule_task_del_by_context(ske->schedule, ske);
+
   if (ske->aborted) {
     /** Aborted */
     silc_fsm_next(fsm, silc_ske_st_responder_aborted);
@@ -2072,6 +2207,12 @@ SILC_FSM_STATE(silc_ske_st_responder_end)
     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_packet_free(ske->packet);
+    return SILC_FSM_WAIT;
+  }
   silc_packet_free(ske->packet);
 
   /* Process key material */
@@ -2089,9 +2230,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);
+  silc_schedule_task_del_by_context(ske->schedule, ske);
 
   /* Call the completion callback */
   if (ske->callbacks->completed)
@@ -2112,9 +2254,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;
 }
@@ -2139,6 +2282,7 @@ SILC_FSM_STATE(silc_ske_st_responder_failure)
 
   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;
 }
@@ -2157,9 +2301,9 @@ 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;
 }
@@ -2168,7 +2312,11 @@ SILC_FSM_STATE(silc_ske_st_responder_error)
 static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
                                        void *destructor_context)
 {
+  SilcSKE ske = fsm_context;
 
+  ske->running = FALSE;
+  if (ske->freed)
+    silc_ske_free(ske);
 }
 
 /* Starts the protocol as responder. */
@@ -2198,6 +2346,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;
@@ -2686,3 +2835,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;
+}