Added SILC Server library.
[silc.git] / lib / silcske / silcske.c
index cb4713708ae0305eb360d7ec167310bee3d46f41..c230e36b72ed4a7b820d58aa1296d2316c7228d2 100644 (file)
 */
 /* $Id$ */
 
 */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcske.h"
 #include "groups_internal.h"
 
 #include "silcske.h"
 #include "groups_internal.h"
 
-/* Static functions */
-static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
-                                        SilcUInt32 len,
-                                        SilcMPInt *rnd);
-static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
-                                       unsigned char *return_hash,
-                                       SilcUInt32 *return_hash_len,
-                                       int initiator);
-static SilcSKEStatus
-silc_ske_select_security_properties(SilcSKE ske,
-                                   const char *version,
-                                   SilcSKEStartPayload payload,
-                                   SilcSKEStartPayload remote_payload);
+/************************** Types and definitions ***************************/
+
+/* Structure to hold all SKE callbacks. */
+struct SilcSKECallbacksStruct {
+  SilcSKEVerifyCb verify_key;
+  SilcSKECompletionCb completed;
+  void *context;
+};
+
+
+/************************ Static utility functions **************************/
+
 SilcSKEKeyMaterial
 silc_ske_process_key_material_data(unsigned char *data,
                                   SilcUInt32 data_len,
 SilcSKEKeyMaterial
 silc_ske_process_key_material_data(unsigned char *data,
                                   SilcUInt32 data_len,
@@ -47,142 +46,27 @@ silc_ske_process_key_material(SilcSKE ske,
                              SilcUInt32 req_iv_len,
                              SilcUInt32 req_enc_key_len,
                              SilcUInt32 req_hmac_key_len);
                              SilcUInt32 req_iv_len,
                              SilcUInt32 req_enc_key_len,
                              SilcUInt32 req_hmac_key_len);
-static void silc_ske_packet_receive(SilcPacketEngine engine,
-                                   SilcPacketStream stream,
-                                   SilcPacket packet,
-                                   void *callback_context,
-                                   void *app_context);
-static void silc_ske_packet_eos(SilcPacketEngine engine,
-                               SilcPacketStream stream,
-                               void *callback_context,
-                               void *app_context);
-static void silc_ske_packet_error(SilcPacketEngine engine,
-                                 SilcPacketStream stream,
-                                 SilcPacketError error,
-                                 void *callback_context,
-                                 void *app_context);
-
-/* Structure to hold all SKE callbacks. */
-struct SilcSKECallbacksStruct {
-  SilcSKEVerifyCb verify_key;
-  SilcSKECheckVersionCb check_version;
-  SilcSKECompletionCb completed;
-  void *context;
-};
-
-/* Packet stream callbacks */
-static SilcPacketCallbacks silc_ske_stream_cbs =
-{
-  silc_ske_packet_receive,
-  silc_ske_packet_eos,
-  silc_ske_packet_error
-};
-
-/* Allocates new SKE object. */
-
-SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule, void *context)
-{
-  SilcSKE ske;
-
-  SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
-
-  if (!rng || !schedule)
-    return NULL;
-
-  ske = silc_calloc(1, sizeof(*ske));
-  if (!ske)
-    return NULL;
-  ske->status = SILC_SKE_STATUS_OK;
-  ske->rng = rng;
-  ske->user_data = context;
-  ske->schedule = schedule;
-  ske->users = 1;
-
-  return ske;
-}
-
-/* Free's SKE object. */
-
-void silc_ske_free(SilcSKE ske)
-{
-  ske->users--;
-  if (ske->users > 0) {
-    SILC_LOG_DEBUG(("Key Exchange set to FREED status"));
-    ske->status = SILC_SKE_STATUS_FREED;
-    return;
-  }
-
-  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->pkcs)
-       silc_pkcs_free(ske->prop->pkcs);
-      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);
 
 
-    memset(ske, 'F', sizeof(*ske));
-    silc_free(ske);
-  }
-}
 
 
-/* Return user context */
+/* Packet callback */
 
 
-void *silc_ske_get_context(SilcSKE ske)
+static SilcBool silc_ske_packet_receive(SilcPacketEngine engine,
+                                       SilcPacketStream stream,
+                                       SilcPacket packet,
+                                       void *callback_context,
+                                       void *app_context)
 {
 {
-  return ske->user_data;
+  SilcSKE ske = callback_context;
+  ske->packet = packet;
+  silc_fsm_continue(&ske->fsm);
+  return TRUE;
 }
 
 }
 
-/* Sets protocol callbacks */
-
-void silc_ske_set_callbacks(SilcSKE ske,
-                           SilcSKEVerifyCb verify_key,
-                           SilcSKECheckVersionCb check_version,
-                           SilcSKECompletionCb completed,
-                           void *context)
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_ske_stream_cbs =
 {
 {
-  if (ske->callbacks)
-    silc_free(ske->callbacks);
-  ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
-  if (!ske->callbacks)
-    return;
-  ske->callbacks->verify_key = verify_key;
-  ske->callbacks->check_version = check_version;
-  ske->callbacks->completed = completed;
-  ske->callbacks->context = context;
-}
+  silc_ske_packet_receive, NULL, NULL
+};
 
 /* Aborts SKE protocol */
 
 
 /* Aborts SKE protocol */
 
@@ -197,716 +81,831 @@ static void silc_ske_abort(SilcAsyncOperation op, void *context)
 static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status,
                                 void *completion_context)
 {
 static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status,
                                 void *completion_context)
 {
+  ske->status = status;
   SILC_FSM_CALL_CONTINUE(&ske->fsm);
 }
 
   SILC_FSM_CALL_CONTINUE(&ske->fsm);
 }
 
-/* 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 */
+/* Checks remote and local versions */
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_start)
+static SilcSKEStatus silc_ske_check_version(SilcSKE ske)
 {
 {
-  SilcSKE ske = fsm_context;
-  SilcBuffer payload_buf;
-  SilcStatus status;
+  SilcUInt32 l_protocol_version = 0, r_protocol_version = 0;
 
 
-  SILC_LOG_DEBUG(("Start"));
-
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
-  }
+  if (!ske->remote_version || !ske->version)
+    return SILC_SKE_STATUS_BAD_VERSION;
 
 
-  /* Encode the payload */
-  status = silc_ske_payload_start_encode(ske, ske->start_payload,
-                                        &payload_buf);
-  if (status != SILC_SKE_STATUS_OK) {
-    /** Error encoding Start Payload */
-    ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
+  if (!silc_parse_version_string(ske->remote_version, &r_protocol_version,
+                                NULL, NULL, NULL, NULL))
+    return SILC_SKE_STATUS_BAD_VERSION;
 
 
-  /* Save the the payload buffer for future use. It is later used to
-     compute the HASH value. */
-  ske->start_payload_copy = payload_buf;
+  if (!silc_parse_version_string(ske->version, &l_protocol_version,
+                                NULL, NULL, NULL, NULL))
+    return SILC_SKE_STATUS_BAD_VERSION;
 
 
-  /* Send the packet */
-  /* XXX */
+  /* If remote is too new, don't connect */
+  if (l_protocol_version < r_protocol_version)
+    return SILC_SKE_STATUS_BAD_VERSION;
 
 
-  /** Wait for responder proposal */
-  SILC_LOG_DEBUG(("Waiting for reponder proposal"));
-  silc_fsm_next(ske, silc_ske_st_initiator_phase1);
-  return SILC_FSM_WAIT;
+  return SILC_SKE_STATUS_OK;
 }
 
 }
 
-/* Phase-1.  Receives responder's proposal */
+/* Selects the supported security properties from the initiator's Key
+   Exchange Start Payload. */
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_phase1)
+static SilcSKEStatus
+silc_ske_select_security_properties(SilcSKE ske,
+                                   SilcSKEStartPayload payload,
+                                   SilcSKEStartPayload remote_payload)
 {
 {
-  SilcSKE ske = fsm_context;
   SilcSKEStatus status;
   SilcSKEStatus status;
-  SilcSKEStartPayload payload;
-  SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group;
+  SilcSKEStartPayload rp;
+  char *cp;
+  int len;
 
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Parsing KE Start Payload"));
 
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
-  }
+  rp = remote_payload;
 
 
-  /* Decode the payload */
-  status = silc_ske_payload_start_decode(ske, ske->packet_buf, &payload);
+  /* Check version string */
+  ske->remote_version = silc_memdup(rp->version, rp->version_len);
+  status = silc_ske_check_version(ske);
   if (status != SILC_SKE_STATUS_OK) {
   if (status != SILC_SKE_STATUS_OK) {
-    /** Error decoding Start Payload */
     ske->status = status;
     ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
+    return status;
   }
 
   }
 
-  /* Check that the cookie is returned unmodified */
-  if (memcmp(ske->start_payload->cookie, payload->cookie,
-            ske->start_payload->cookie_len)) {
-    /** Invalid cookie */
-    SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
-    ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
+  /* Flags are returned unchanged. */
+  payload->flags = rp->flags;
+
+  /* Take cookie, we must return it to sender unmodified. */
+  payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
+  if (!payload->cookie) {
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return status;
   }
   }
+  payload->cookie_len = SILC_SKE_COOKIE_LEN;
+  memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN);
 
 
-  /* Check version string */
-  if (ske->callbacks->check_version) {
-    status = ske->callbacks->check_version(ske, payload->version,
-                                          payload->version_len,
-                                          ske->callbacks->context);
-    if (status != SILC_SKE_STATUS_OK) {
-      /** Version mismatch */
-      ske->status = status;
-      silc_fsm_next(fsm, silc_ske_st_initiator_error);
-      return SILC_FSM_CONTINUE;
-    }
+  /* Put our version to our reply */
+  payload->version = strdup(ske->version);
+  if (!payload->version) {
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return status;
   }
   }
+  payload->version_len = strlen(ske->version);
 
 
-  /* Free our KE Start Payload context, we don't need it anymore. */
-  silc_ske_payload_start_free(ske->start_payload);
-  ske->start_payload = NULL;
+  /* Get supported Key Exchange groups */
+  cp = rp->ke_grp_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
 
-  /* Take the selected security properties into use while doing
-     the key exchange.  This is used only while doing the key
-     exchange. */
-  ske->prop = prop = silc_calloc(1, sizeof(*prop));
-  if (!ske->prop)
-    goto err;
-  prop->flags = payload->flags;
-  status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      if (!item) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       return status;
+      }
+      memcpy(item, cp, len);
 
 
-  prop->group = group;
+      SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
 
 
-  if (silc_pkcs_alloc(payload->pkcs_alg_list, &prop->pkcs) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_PKCS;
-    goto err;
-  }
-  if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
-    goto err;
-  }
-  if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
-    goto err;
-  }
-  if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
-    goto err;
-  }
+      if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
+       SILC_LOG_DEBUG(("Found KE group `%s'", item));
 
 
-  /* Save remote's KE Start Payload */
-  ske->start_payload = payload;
+       payload->ke_grp_len = len;
+       payload->ke_grp_list = item;
+       break;
+      }
 
 
-  /** Send KE Payload */
-  silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
-  return SILC_FSM_CONTINUE;
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
 
 
- err:
-  if (payload)
-    silc_ske_payload_start_free(payload);
+      if (item)
+       silc_free(item);
+    }
 
 
-  silc_ske_group_free(group);
+    if (!payload->ke_grp_len && !payload->ke_grp_list) {
+      SILC_LOG_DEBUG(("Could not find supported KE group"));
+      silc_free(payload);
+      return SILC_SKE_STATUS_UNKNOWN_GROUP;
+    }
+  } else {
 
 
-  if (prop->pkcs)
-    silc_pkcs_free(prop->pkcs);
-  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);
-  ske->prop = NULL;
+    if (!rp->ke_grp_len) {
+      SILC_LOG_DEBUG(("KE group not defined in payload"));
+      silc_free(payload);
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    }
 
 
-  if (status == SILC_SKE_STATUS_OK)
-    status = SILC_SKE_STATUS_ERROR;
+    SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
+    SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
 
 
-  /** Error */
-  ske->status = status;
-  silc_fsm_next(fsm, silc_ske_st_initiator_error);
-  return SILC_FSM_CONTINUE;
-}
+    payload->ke_grp_len = rp->ke_grp_len;
+    payload->ke_grp_list = strdup(rp->ke_grp_list);
+  }
 
 
-/* Phase-2.  Send KE payload */
+  /* Get supported PKCS algorithms */
+  cp = rp->pkcs_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_phase2)
-{
-  SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcBuffer payload_buf;
-  SilcMPInt *x;
-  SilcSKEKEPayload payload;
-  SilcUInt32 pk_len;
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      if (!item) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       return status;
+      }
+      memcpy(item, cp, len);
 
 
-  SILC_LOG_DEBUG(("Start"));
+      SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
 
 
-  /* Create the random number x, 1 < x < q. */
-  x = silc_calloc(1, sizeof(*x));
-  if (!x){
-    /** Out of memory */
-    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
-  silc_mp_init(x);
-  status =
-    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
-                       silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       x);
-  if (status != SILC_SKE_STATUS_OK) {
-    /** Error generating random number */
-    silc_mp_uninit(x);
-    silc_free(x);
-    ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
+      if (silc_pkcs_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found PKCS alg `%s'", item));
 
 
-  /* Encode the result to Key Exchange Payload. */
+       payload->pkcs_alg_len = len;
+       payload->pkcs_alg_list = item;
+       break;
+      }
 
 
-  payload = silc_calloc(1, sizeof(*payload));
-  if (!payload) {
-    /** Out of memory */
-    silc_mp_uninit(x);
-    silc_free(x);
-    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
-  ske->ke1_payload = payload;
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
 
 
-  SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
+      if (item)
+       silc_free(item);
+    }
 
 
-  /* Do the Diffie Hellman computation, e = g ^ x mod p */
-  silc_mp_init(&payload->x);
-  silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x,
-                 &ske->prop->group->group);
+    if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) {
+      SILC_LOG_DEBUG(("Could not find supported PKCS alg"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_UNKNOWN_PKCS;
+    }
+  } else {
 
 
-  /* 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);
+    if (!rp->pkcs_alg_len) {
+      SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
+      silc_free(payload->ke_grp_list);
       silc_free(payload);
       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;
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
     }
     }
-    payload->pk_len = pk_len;
+
+    SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
+    SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
+
+    payload->pkcs_alg_len = rp->pkcs_alg_len;
+    payload->pkcs_alg_list = strdup(rp->pkcs_alg_list);
   }
   }
-  payload->pk_type = ske->pk_type;
 
 
-  /* Compute signature data if we are doing mutual authentication */
-  if (ske->private_key &&
-      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-    unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1];
-    SilcUInt32 hash_len, sign_len;
+  /* Get supported encryption algorithms */
+  cp = rp->enc_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
 
-    SILC_LOG_DEBUG(("We are doing mutual authentication"));
-    SILC_LOG_DEBUG(("Computing HASH_i value"));
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      if (!item) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       return status;
+      }
+      memcpy(item, cp, len);
 
 
-    /* Compute the hash value */
-    memset(hash, 0, sizeof(hash));
-    silc_ske_make_hash(ske, hash, &hash_len, TRUE);
+      SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item));
 
 
-    SILC_LOG_DEBUG(("Signing HASH_i value"));
+      if (silc_cipher_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
 
 
-    /* Sign the hash value */
-    silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
-                                  ske->private_key->prv_len);
-    if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
-       !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
-      /** Error computing signature */
-      silc_mp_uninit(x);
-      silc_free(x);
-      silc_mp_uninit(&payload->x);
-      silc_free(payload->pk_data);
+       payload->enc_alg_len = len;
+       payload->enc_alg_list = item;
+       break;
+      }
+
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
+
+      if (item)
+       silc_free(item);
+    }
+
+    if (!payload->enc_alg_len && !payload->enc_alg_list) {
+      SILC_LOG_DEBUG(("Could not find supported encryption alg"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload->pkcs_alg_list);
       silc_free(payload);
       silc_free(payload);
-      ske->ke1_payload = NULL;
-      ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
-      silc_fsm_next(fsm, silc_ske_st_initiator_error);
-      return SILC_FSM_CONTINUE;
+      return SILC_SKE_STATUS_UNKNOWN_CIPHER;
     }
     }
-    payload->sign_data = silc_memdup(sign, sign_len);
-    payload->sign_len = sign_len;
-    memset(sign, 0, sizeof(sign));
-  }
+  } else {
 
 
-  status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
-  if (status != SILC_SKE_STATUS_OK) {
-    /** Error encoding KE payload */
-    silc_mp_uninit(x);
-    silc_free(x);
-    silc_mp_uninit(&payload->x);
-    silc_free(payload->pk_data);
-    silc_free(payload->sign_data);
-    silc_free(payload);
-    ske->ke1_payload = NULL;
-    ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
+    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;
+    }
 
 
-  ske->x = x;
+    SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
+                   rp->enc_alg_list));
 
 
-  /* Send the packet. */
-  /* XXX */
+    payload->enc_alg_len = rp->enc_alg_len;
+    payload->enc_alg_list = strdup(rp->enc_alg_list);
+  }
 
 
-  silc_buffer_free(payload_buf);
+  /* Get supported hash algorithms */
+  cp = rp->hash_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
 
-  /** Waiting responder's KE payload */
-  silc_fsm_next(fsm, silc_ske_st_initiator_phase3);
-  return SILC_FSM_WAIT;
-}
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      if (!item) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       return status;
+      }
+      memcpy(item, cp, len);
 
 
-/* Phase-3.  Process responder's KE payload */
+      SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_phase3)
-{
-  SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcSKEKEPayload payload;
-  SilcMPInt *KEY;
+      if (silc_hash_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
 
 
-  SILC_LOG_DEBUG(("Start"));
+       payload->hash_alg_len = len;
+       payload->hash_alg_list = item;
+       break;
+      }
 
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
-  }
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
 
 
-  /* Decode the payload */
-  status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &payload);
-  if (status != SILC_SKE_STATUS_OK) {
-    /** Error decoding KE payload */
-    ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
-  ske->ke2_payload = payload;
+      if (item)
+       silc_free(item);
+    }
 
 
-  if (!payload->pk_data && ske->callbacks->verify_key) {
-    SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
-                   "even though we require it"));
-    ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
-    goto err;
+    if (!payload->hash_alg_len && !payload->hash_alg_list) {
+      SILC_LOG_DEBUG(("Could not find supported hash alg"));
+      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;
+    }
+  } 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));
+
+    payload->hash_alg_len = rp->hash_alg_len;
+    payload->hash_alg_list = strdup(rp->hash_alg_list);
   }
 
   }
 
-  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+  /* Get supported HMACs */
+  cp = rp->hmac_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
 
-  /* Compute the shared secret key */
-  KEY = silc_calloc(1, sizeof(*KEY));
-  silc_mp_init(KEY);
-  silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      if (!item) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       return status;
+      }
+      memcpy(item, cp, len);
 
 
-  if (payload->pk_data && ske->callbacks->verify_key) {
-    SILC_LOG_DEBUG(("Verifying public key"));
+      SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
 
 
-    /** Waiting public key verification */
-    silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
-    SILC_FSM_CALL(ske->callbacks->verify_key(ske, payload->pk_data,
-                                            payload->pk_len,
-                                            payload->pk_type,
-                                            ske->callbacks->context,
-                                            silc_ske_pk_verified, NULL));
-    /* NOT REACHED */
+      if (silc_hmac_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+
+       payload->hmac_alg_len = len;
+       payload->hmac_alg_list = item;
+       break;
+      }
+
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
+
+      if (item)
+       silc_free(item);
+    }
+
+    if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
+      SILC_LOG_DEBUG(("Could not find supported HMAC"));
+      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;
+    }
+  } 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));
+
+    payload->hmac_alg_len = rp->hmac_alg_len;
+    payload->hmac_alg_list = strdup(rp->hmac_alg_list);
   }
 
   }
 
-  /** Process key material */
-  silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
-  return SILC_FSM_CONTINUE;
+  /* Get supported compression algorithms */
+  cp = rp->comp_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
 
- err:
-  silc_ske_payload_ke_free(payload);
-  ske->ke2_payload = NULL;
+      len = strcspn(cp, ",");
+      item = silc_calloc(len + 1, sizeof(char));
+      if (!item) {
+       ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+       return status;
+      }
+      memcpy(item, cp, len);
 
 
-  silc_mp_uninit(ske->KEY);
-  silc_free(ske->KEY);
-  ske->KEY = NULL;
+      SILC_LOG_DEBUG(("Proposed Compression `%s'", item));
 
 
-  if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+#if 1
+      if (!strcmp(item, "none")) {
+       SILC_LOG_DEBUG(("Found Compression `%s'", item));
+       payload->comp_alg_len = len;
+       payload->comp_alg_list = item;
+       break;
+      }
+#else
+      if (silc_hmac_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found Compression `%s'", item));
+       payload->comp_alg_len = len;
+       payload->comp_alg_list = item;
+       break;
+      }
+#endif
 
 
-  /** Error */
-  ske->status = status;
-  silc_fsm_next(fsm, silc_ske_st_initiator_error);
-  return SILC_FSM_CONTINUE;
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
+
+      if (item)
+       silc_free(item);
+    }
+  }
+
+  payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
+    2 + payload->version_len +
+    2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len +
+    2 + payload->enc_alg_len + 2 + payload->hash_alg_len +
+    2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
+
+  return SILC_SKE_STATUS_OK;
 }
 
 }
 
-/* Process key material */
+/* Creates random number such that 1 < rnd < n and at most length
+   of len bits. The rnd sent as argument must be initialized. */
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_phase4)
+static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
+                                        SilcUInt32 len,
+                                        SilcMPInt *rnd)
 {
 {
-  SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcSKEKEPayload payload;
-  unsigned char hash[SILC_HASH_MAXLEN];
-  SilcUInt32 hash_len;
-  SilcPublicKey public_key = NULL;
-  int key_len, block_len;
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  unsigned char *string;
+  SilcUInt32 l;
 
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
-  }
+  if (!len)
+    return SILC_SKE_STATUS_ERROR;
 
 
-  /* Check result of public key verification */
-  if (ske->status != SILC_SKE_STATUS_OK) {
-    /** Public key not verified */
-    SILC_LOG_DEBUG(("Public key verification failed"));
-    silc_fsm_next(fsm, silc_ske_st_initiator_error);
-    return SILC_FSM_CONTINUE;
-  }
+  SILC_LOG_DEBUG(("Creating random number"));
 
 
-  payload = ske->ke2_payload;
+  l = ((len - 1) / 8);
 
 
-  if (payload->pk_data) {
-    /* Decode the public key */
-    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
-                                    &public_key)) {
-      SILC_LOG_ERROR(("Unsupported/malformed public key received"));
-      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-      goto err;
-    }
+  /* Get the random number as string */
+  string = silc_rng_get_rn_data(ske->rng, l);
+  if (!string)
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
 
-    SILC_LOG_DEBUG(("Public key is authentic"));
+  /* Decode the string into a MP integer */
+  silc_mp_bin2mp(string, l, rnd);
+  silc_mp_mod_2exp(rnd, rnd, len);
 
 
-    /* Compute the hash value */
-    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-    if (status != SILC_SKE_STATUS_OK)
-      goto err;
+  /* Checks */
+  if (silc_mp_cmp_ui(rnd, 1) < 0)
+    status = SILC_SKE_STATUS_ERROR;
+  if (silc_mp_cmp(rnd, n) >= 0)
+    status = SILC_SKE_STATUS_ERROR;
 
 
-    ske->hash = silc_memdup(hash, hash_len);
-    ske->hash_len = hash_len;
+  memset(string, 'F', l);
+  silc_free(string);
 
 
-    SILC_LOG_DEBUG(("Verifying signature (HASH)"));
+  return status;
+}
 
 
-    /* Verify signature */
-    silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
-    if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data,
-                        payload->sign_len, hash, hash_len) == FALSE) {
-      SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
-      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-      goto err;
-    }
+/* Creates a hash value HASH as defined in the SKE protocol. If the
+   `initiator' is TRUE then this function is used to create the HASH_i
+   hash value defined in the protocol. If it is FALSE then this is used
+   to create the HASH value defined by the protocol. */
 
 
-    SILC_LOG_DEBUG(("Signature is Ok"));
+static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
+                                       unsigned char *return_hash,
+                                       SilcUInt32 *return_hash_len,
+                                       int initiator)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcBuffer buf;
+  unsigned char *e, *f, *KEY;
+  SilcUInt32 e_len, f_len, KEY_len;
+  int ret;
 
 
-    silc_pkcs_public_key_free(public_key);
-    memset(hash, 'F', hash_len);
-  }
+  SILC_LOG_DEBUG(("Start"));
 
 
-  ske->status = SILC_SKE_STATUS_OK;
+  if (initiator == FALSE) {
+    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);
 
 
-  /* Process key material */
-  key_len = silc_cipher_get_key_len(ske->prop->cipher);
-  block_len = silc_cipher_get_key_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);
-  if (!ske->keymat) {
-    SILC_LOG_ERROR(("Error processing key material"));
-    status = SILC_SKE_STATUS_ERROR;
-    goto err;
-  }
+    /* Format the buffer used to compute the hash value */
+    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
+                                ske->ke2_payload->pk_len +
+                                ske->ke1_payload->pk_len +
+                                e_len + f_len + KEY_len);
+    if (!buf)
+      return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
 
-  /* Send SUCCESS packet */
-  /* XXX */
+    /* Initiator is not required to send its public key */
+    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_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_END);
+    }
+    if (ret == -1) {
+      silc_buffer_free(buf);
+      memset(e, 0, e_len);
+      memset(f, 0, f_len);
+      memset(KEY, 0, KEY_len);
+      silc_free(e);
+      silc_free(f);
+      silc_free(KEY);
+      return SILC_SKE_STATUS_ERROR;
+    }
 
 
-  /** Waiting completion */
-  silc_fsm_next(fsm, silc_ske_st_initiator_end);
-  return SILC_FSM_WAIT;
+    memset(e, 0, e_len);
+    memset(f, 0, f_len);
+    memset(KEY, 0, KEY_len);
+    silc_free(e);
+    silc_free(f);
+    silc_free(KEY);
+  } else {
+    e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
 
 
- err:
-  memset(hash, 'F', sizeof(hash));
-  silc_ske_payload_ke_free(payload);
-  ske->ke2_payload = NULL;
+    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
+                                ske->ke1_payload->pk_len + e_len);
+    if (!buf)
+      return SILC_SKE_STATUS_OUT_OF_MEMORY;
 
 
-  silc_mp_uninit(ske->KEY);
-  silc_free(ske->KEY);
-  ske->KEY = NULL;
+    /* 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_END);
+    if (ret == -1) {
+      silc_buffer_free(buf);
+      memset(e, 0, e_len);
+      silc_free(e);
+      return SILC_SKE_STATUS_ERROR;
+    }
+
+    memset(e, 0, e_len);
+    silc_free(e);
+  }
 
 
-  if (public_key)
-    silc_pkcs_public_key_free(public_key);
+  /* Make the hash */
+  silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf),
+                return_hash);
+  *return_hash_len = silc_hash_len(ske->prop->hash);
 
 
-  if (ske->hash) {
-    memset(ske->hash, 'F', hash_len);
-    silc_free(ske->hash);
-    ske->hash = NULL;
+  if (initiator == FALSE) {
+    SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
+  } else {
+    SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len);
   }
 
   }
 
-  if (status == SILC_SKE_STATUS_OK)
-    status = SILC_SKE_STATUS_ERROR;
+  silc_buffer_free(buf);
 
 
-  /** Error */
-  ske->status = status;
-  silc_fsm_next(fsm, silc_ske_st_initiator_error);
-  return SILC_FSM_CONTINUE;
+  return status;
 }
 
 }
 
-/* Protocol completed */
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_end)
-{
-  SilcSKE ske = fsm_context;
+/******************************* Protocol API *******************************/
 
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
-    return SILC_FSM_CONTINUE;
-  }
+/* Allocates new SKE object. */
 
 
-  /* Call the completion callback */
-  if (ske->callbacks->completed)
-    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
+                      SilcPublicKey public_key, SilcPrivateKey private_key,
+                      void *context)
+{
+  SilcSKE ske;
 
 
-  return SILC_FSM_FINISH;
-}
+  SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
 
 
-/* Aborted by application */
+  if (!rng || !schedule)
+    return NULL;
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_aborted)
-{
+  ske = silc_calloc(1, sizeof(*ske));
+  if (!ske)
+    return NULL;
+  ske->status = SILC_SKE_STATUS_OK;
+  ske->rng = rng;
+  ske->user_data = context;
+  ske->schedule = schedule;
+  ske->public_key = public_key;
+  ske->private_key = private_key;
+  ske->pk_type = SILC_SKE_PK_TYPE_SILC;
 
 
-  return SILC_FSM_FINISH;
+  return ske;
 }
 
 }
 
-/* Error occurred */
+/* Free's SKE object. */
 
 
-SILC_FSM_STATE(silc_ske_st_initiator_error)
+void silc_ske_free(SilcSKE ske)
 {
 {
+  SILC_LOG_DEBUG(("Freeing Key Exchange object"));
 
 
-  return SILC_FSM_FINISH;
-}
+  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);
 
 
-static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
-                                       void *destructor_context)
-{
+    /* Free rest */
+    if (ske->prop) {
+      if (ske->prop->group)
+       silc_ske_group_free(ske->prop->group);
+      if (ske->prop->pkcs)
+       silc_pkcs_free(ske->prop->pkcs);
+      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);
 
 
+    memset(ske, 'F', sizeof(*ske));
+    silc_free(ske);
+  }
 }
 
 }
 
-/* Starts the protocol as initiator */
+/* Return user context */
 
 
-SilcAsyncOperation
-silc_ske_initiator_start(SilcSKE ske,
-                        SilcPacketStream stream,
-                        SilcSKEStartPayload start_payload)
+void *silc_ske_get_context(SilcSKE ske)
 {
 {
-  SILC_LOG_DEBUG(("Start SKE as initiator"));
-
-  if (!ske || !stream || !start_payload)
-    return NULL;
-
-  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))
-    return NULL;
+  return ske->user_data;
+}
 
 
-  ske->start_payload = start_payload;
+/* Sets protocol callbacks */
 
 
-  /* Link to packet stream to get key exchange packets */
-  ske->stream = stream;
-  silc_packet_stream_ref(ske->stream);
-  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+void silc_ske_set_callbacks(SilcSKE ske,
+                           SilcSKEVerifyCb verify_key,
+                           SilcSKECompletionCb completed,
+                           void *context)
+{
+  if (ske->callbacks)
+    silc_free(ske->callbacks);
+  ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
+  if (!ske->callbacks)
+    return;
+  ske->callbacks->verify_key = verify_key;
+  ske->callbacks->completed = completed;
+  ske->callbacks->context = context;
+}
 
 
-  /* Start SKE as initiator */
-  silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
 
 
-  return &ske->op;
-}
+/******************************** Initiator *********************************/
 
 
-/* Responder state machine */
-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_end);
-SILC_FSM_STATE(silc_ske_st_responder_aborted);
-SILC_FSM_STATE(silc_ske_st_responder_error);
+/* 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 as responder.  Decode initiator's start payload */
+/* Start protocol.  Send our proposal */
 
 
-SILC_FSM_STATE(silc_ske_st_responder_start)
+SILC_FSM_STATE(silc_ske_st_initiator_start)
 {
   SilcSKE ske = fsm_context;
 {
   SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcSKEStartPayload remote_payload = NULL, payload = NULL;
+  SilcBuffer payload_buf;
+  SilcStatus status;
 
   SILC_LOG_DEBUG(("Start"));
 
   if (ske->aborted) {
     /** Aborted */
 
   SILC_LOG_DEBUG(("Start"));
 
   if (ske->aborted) {
     /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
     return SILC_FSM_CONTINUE;
   }
 
     return SILC_FSM_CONTINUE;
   }
 
-  /* Decode the payload */
-  status = silc_ske_payload_start_decode(ske, ske->packet_buf,
-                                        &remote_payload);
+  /* Encode the payload */
+  status = silc_ske_payload_start_encode(ske, ske->start_payload,
+                                        &payload_buf);
   if (status != SILC_SKE_STATUS_OK) {
   if (status != SILC_SKE_STATUS_OK) {
-    /** Error decoding Start Payload */
+    /** Error encoding Start Payload */
     ske->status = status;
     ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
     return SILC_FSM_CONTINUE;
   }
 
     return SILC_FSM_CONTINUE;
   }
 
-  /* Take a copy of the payload buffer for future use. It is used to
+  /* Save the the payload buffer for future use. It is later used to
      compute the HASH value. */
      compute the HASH value. */
-  ske->start_payload_copy = silc_buffer_copy(ske->packet_buf);
+  ske->start_payload_copy = payload_buf;
 
 
-  /* Force the mutual authentication flag if we want to do it. */
-  if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-    SILC_LOG_DEBUG(("Force mutual authentication"));
-    remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
-  }
+  /* Send the packet */
+  /* XXX */
 
 
-  /* Force PFS flag if we require it */
-  if (ske->flags & SILC_SKE_SP_FLAG_PFS) {
-    SILC_LOG_DEBUG(("Force PFS"));
-    remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
-  }
+  /** Wait for responder proposal */
+  SILC_LOG_DEBUG(("Waiting for reponder proposal"));
+  silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+  return SILC_FSM_WAIT;
+}
 
 
-  /* Disable IV Included flag if requested */
-  if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED &&
-      !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
-    SILC_LOG_DEBUG(("We do not support IV Included flag"));
-    remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
+/* Phase-1.  Receives responder's proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase1)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEStartPayload payload;
+  SilcSKESecurityProperties prop;
+  SilcSKEDiffieHellmanGroup group;
+  SilcBuffer packet_buf = &ske->packet->buffer;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
   }
 
   }
 
-  /* Parse and select the security properties from the payload */
-  payload = silc_calloc(1, sizeof(*payload));
-  status = silc_ske_select_security_properties(ske, ske->version,
-                                              payload, remote_payload);
+  /* Decode the payload */
+  status = silc_ske_payload_start_decode(ske, packet_buf, &payload);
   if (status != SILC_SKE_STATUS_OK) {
   if (status != SILC_SKE_STATUS_OK) {
-    /** Error selecting proposal */
-    if (remote_payload)
-      silc_ske_payload_start_free(remote_payload);
-    silc_free(payload);
+    /** Error decoding Start Payload */
     ske->status = status;
     ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
     return SILC_FSM_CONTINUE;
   }
 
     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_phase1);
-  return SILC_FSM_CONTINUE;
-}
-
-/* Phase-1.  Send Start Payload */
+  /* Check that the cookie is returned unmodified */
+  if (memcmp(ske->start_payload->cookie, payload->cookie,
+            ske->start_payload->cookie_len)) {
+    /** Invalid cookie */
+    SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
+    ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-SILC_FSM_STATE(silc_ske_st_responder_phase1)
-{
-  SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcBuffer payload_buf;
-  SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group = NULL;
+  /* Check version string */
+  ske->remote_version = silc_memdup(payload->version, payload->version_len);
+  status = silc_ske_check_version(ske);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Version mismatch */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-  SILC_LOG_DEBUG(("Start"));
+  /* Free our KE Start Payload context, we don't need it anymore. */
+  silc_ske_payload_start_free(ske->start_payload);
+  ske->start_payload = NULL;
 
 
-  /* Allocate security properties from the payload. These are allocated
-     only for this negotiation and will be free'd after KE is over. */
+  /* Take the selected security properties into use while doing
+     the key exchange.  This is used only while doing the key
+     exchange. */
   ske->prop = prop = silc_calloc(1, sizeof(*prop));
   ske->prop = prop = silc_calloc(1, sizeof(*prop));
-  prop->flags = ske->start_payload->flags;
-  status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list, &group);
+  if (!ske->prop)
+    goto err;
+  prop->flags = payload->flags;
+  status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   prop->group = group;
 
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   prop->group = group;
 
-  if (silc_pkcs_alloc(ske->start_payload->pkcs_alg_list,
+  if (silc_pkcs_alloc(payload->pkcs_alg_list, ske->pk_type,
                      &prop->pkcs) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
     goto err;
   }
                      &prop->pkcs) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
     goto err;
   }
-  if (silc_cipher_alloc(ske->start_payload->enc_alg_list,
-                       &prop->cipher) == FALSE) {
+  if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
     goto err;
   }
     status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
     goto err;
   }
-  if (silc_hash_alloc(ske->start_payload->hash_alg_list,
-                     &prop->hash) == FALSE) {
+  if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
     status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
     goto err;
   }
     status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
     goto err;
   }
-  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) {
     status = SILC_SKE_STATUS_UNKNOWN_HMAC;
     goto err;
   }
 
     status = SILC_SKE_STATUS_UNKNOWN_HMAC;
     goto err;
   }
 
-  /* Encode the payload */
-  status = silc_ske_payload_start_encode(ske, ske->start_payload,
-                                        &payload_buf);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
-
-  /* Send the packet. */
-  /* XXX */
-
-  silc_buffer_free(payload_buf);
+  /* Save remote's KE Start Payload */
+  ske->start_payload = payload;
 
 
-  /** Waiting initiator's KE payload */
-  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
-  return SILC_FSM_WAIT;
+  /** Send KE Payload */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+  return SILC_FSM_CONTINUE;
 
  err:
 
  err:
-  if (group)
-    silc_ske_group_free(group);
+  if (payload)
+    silc_ske_payload_start_free(payload);
+
+  silc_ske_group_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -924,84 +923,238 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1)
 
   /** Error */
   ske->status = status;
 
   /** Error */
   ske->status = status;
-  silc_fsm_next(fsm, silc_ske_st_responder_error);
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
   return SILC_FSM_CONTINUE;
 }
 
   return SILC_FSM_CONTINUE;
 }
 
-/* Phase-2.  Decode initiator's KE payload */
+/* Phase-2.  Send KE payload */
 
 
-SILC_FSM_STATE(silc_ske_st_responder_phase2)
+SILC_FSM_STATE(silc_ske_st_initiator_phase2)
 {
   SilcSKE ske = fsm_context;
   SilcSKEStatus status;
 {
   SilcSKE ske = fsm_context;
   SilcSKEStatus status;
-  SilcSKEKEPayload recv_payload;
+  SilcBuffer payload_buf;
+  SilcMPInt *x;
+  SilcSKEKEPayload payload;
+  SilcUInt32 pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
 
   SILC_LOG_DEBUG(("Start"));
 
-  if (ske->aborted) {
-    /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+  /* Create the random number x, 1 < x < q. */
+  x = silc_calloc(1, sizeof(*x));
+  if (!x){
+    /** Out of memory */
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
     return SILC_FSM_CONTINUE;
   }
     return SILC_FSM_CONTINUE;
   }
-
-  /* Decode Key Exchange Payload */
-  status = silc_ske_payload_ke_decode(ske, ske->packet_buf, &recv_payload);
+  silc_mp_init(x);
+  status =
+    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
+                       silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
   if (status != SILC_SKE_STATUS_OK) {
-    /** Error decoding KE payload */
+    /** Error generating random number */
+    silc_mp_uninit(x);
+    silc_free(x);
     ske->status = status;
     ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
     return SILC_FSM_CONTINUE;
   }
 
     return SILC_FSM_CONTINUE;
   }
 
-  ske->ke1_payload = recv_payload;
+  /* Encode the result to Key Exchange Payload. */
 
 
-  /* Verify the received public key and verify the signature if we are
-     doing mutual authentication. */
-  if (ske->start_payload &&
+  payload = silc_calloc(1, sizeof(*payload));
+  if (!payload) {
+    /** Out of memory */
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+  ske->ke1_payload = payload;
+
+  SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
+
+  /* Do the Diffie Hellman computation, e = g ^ x mod p */
+  silc_mp_init(&payload->x);
+  silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x,
+                 &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_type = ske->pk_type;
+
+  /* Compute signature data if we are doing mutual authentication */
+  if (ske->private_key &&
       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+    unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1];
+    SilcUInt32 hash_len, sign_len;
 
     SILC_LOG_DEBUG(("We are doing mutual authentication"));
 
     SILC_LOG_DEBUG(("We are doing mutual authentication"));
+    SILC_LOG_DEBUG(("Computing HASH_i value"));
 
 
-    if (!recv_payload->pk_data && ske->callbacks->verify_key) {
-      /** Public key not provided */
-      SILC_LOG_ERROR(("Remote end did not send its public key (or "
-                     "certificate), even though we require it"));
-      ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
-      silc_fsm_next(fsm, silc_ske_st_responder_error);
+    /* Compute the hash value */
+    memset(hash, 0, sizeof(hash));
+    silc_ske_make_hash(ske, hash, &hash_len, TRUE);
+
+    SILC_LOG_DEBUG(("Signing HASH_i value"));
+
+    /* Sign the hash value */
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
+                                  ske->private_key->prv_len);
+    if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
+       !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+      /** Error computing signature */
+      silc_mp_uninit(x);
+      silc_free(x);
+      silc_mp_uninit(&payload->x);
+      silc_free(payload->pk_data);
+      silc_free(payload);
+      ske->ke1_payload = NULL;
+      ske->status = SILC_SKE_STATUS_SIGNATURE_ERROR;
+      silc_fsm_next(fsm, silc_ske_st_initiator_error);
       return SILC_FSM_CONTINUE;
     }
       return SILC_FSM_CONTINUE;
     }
+    payload->sign_data = silc_memdup(sign, sign_len);
+    payload->sign_len = sign_len;
+    memset(sign, 0, sizeof(sign));
+  }
 
 
-    if (recv_payload->pk_data && ske->callbacks->verify_key) {
-      SILC_LOG_DEBUG(("Verifying public key"));
+  status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error encoding KE payload */
+    silc_mp_uninit(x);
+    silc_free(x);
+    silc_mp_uninit(&payload->x);
+    silc_free(payload->pk_data);
+    silc_free(payload->sign_data);
+    silc_free(payload);
+    ske->ke1_payload = NULL;
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-      /** Waiting public key verification */
-      silc_fsm_next(fsm, silc_ske_st_responder_phase3);
-      SILC_FSM_CALL(ske->callbacks->verify_key(ske, recv_payload->pk_data,
-                                              recv_payload->pk_len,
-                                              recv_payload->pk_type,
-                                              ske->callbacks->context,
-                                              silc_ske_pk_verified, NULL));
-      /* NOT REACHED */
-    }
+  ske->x = x;
+
+  /* Send the packet. */
+  /* XXX */
+
+  silc_buffer_free(payload_buf);
+
+  /** Waiting responder's KE payload */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase3);
+  return SILC_FSM_WAIT;
+}
+
+/* Phase-3.  Process responder's KE payload */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase3)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload payload;
+  SilcMPInt *KEY;
+  SilcBuffer packet_buf = &ske->packet->buffer;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Decode the payload */
+  status = silc_ske_payload_ke_decode(ske, packet_buf, &payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error decoding KE payload */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
+  ske->ke2_payload = payload;
+
+  if (!payload->pk_data && ske->callbacks->verify_key) {
+    SILC_LOG_DEBUG(("Remote end did not send its public key (or certificate), "
+                   "even though we require it"));
+    ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+    goto err;
+  }
+
+  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+
+  /* Compute the shared secret key */
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_pow_mod(KEY, &payload->x, ske->x, &ske->prop->group->group);
+  ske->KEY = KEY;
+
+  if (payload->pk_data && ske->callbacks->verify_key) {
+    SILC_LOG_DEBUG(("Verifying public key"));
+
+    /** Waiting public key verification */
+    silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+    SILC_FSM_CALL(ske->callbacks->verify_key(ske, payload->pk_data,
+                                            payload->pk_len,
+                                            payload->pk_type,
+                                            ske->callbacks->context,
+                                            silc_ske_pk_verified, NULL));
+    /* NOT REACHED */
   }
 
   }
 
-  /** Generate KE2 payload */
-  silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+  /** Process key material */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase4);
+  return SILC_FSM_CONTINUE;
+
+ err:
+  silc_ske_payload_ke_free(payload);
+  ske->ke2_payload = NULL;
+
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
+
+  if (status == SILC_SKE_STATUS_OK)
+    return SILC_SKE_STATUS_ERROR;
+
+  /** Error */
+  ske->status = status;
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
   return SILC_FSM_CONTINUE;
 }
 
   return SILC_FSM_CONTINUE;
 }
 
-/* Phase-3. Generate KE2 payload */
+/* Process key material */
 
 
-SILC_FSM_STATE(silc_ske_st_responder_phase3)
+SILC_FSM_STATE(silc_ske_st_initiator_phase4)
 {
   SilcSKE ske = fsm_context;
   SilcSKEStatus status;
 {
   SilcSKE ske = fsm_context;
   SilcSKEStatus status;
-  SilcSKEKEPayload recv_payload, send_payload;
-  SilcMPInt *x, *KEY;
+  SilcSKEKEPayload payload;
+  unsigned char hash[SILC_HASH_MAXLEN];
+  SilcUInt32 hash_len;
+  SilcPublicKey public_key = NULL;
+  int key_len, block_len;
 
   if (ske->aborted) {
     /** Aborted */
 
   if (ske->aborted) {
     /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
     return SILC_FSM_CONTINUE;
   }
 
     return SILC_FSM_CONTINUE;
   }
 
@@ -1013,49 +1166,36 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
     return SILC_FSM_CONTINUE;
   }
 
     return SILC_FSM_CONTINUE;
   }
 
-  recv_payload = ske->ke1_payload;
-
-  /* The public key verification was performed only if the Mutual
-     Authentication flag is set. */
-  if (ske->start_payload &&
-      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-    SilcPublicKey public_key = NULL;
-    unsigned char hash[SILC_HASH_MAXLEN];
-    SilcUInt32 hash_len;
+  payload = ske->ke2_payload;
 
 
+  if (payload->pk_data) {
     /* Decode the public key */
     /* Decode the public key */
-    if (!silc_pkcs_public_key_decode(recv_payload->pk_data,
-                                    recv_payload->pk_len,
+    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
                                     &public_key)) {
                                     &public_key)) {
-      /** Error decoding public key */
       SILC_LOG_ERROR(("Unsupported/malformed public key received"));
       SILC_LOG_ERROR(("Unsupported/malformed public key received"));
-      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-      silc_fsm_next(fsm, silc_ske_st_responder_error);
-      return SILC_FSM_CONTINUE;
+      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      goto err;
     }
 
     SILC_LOG_DEBUG(("Public key is authentic"));
 
     /* Compute the hash value */
     }
 
     SILC_LOG_DEBUG(("Public key is authentic"));
 
     /* Compute the hash value */
-    status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
-    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;
-    }
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
 
 
-    SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
+    ske->hash = silc_memdup(hash, hash_len);
+    ske->hash_len = hash_len;
+
+    SILC_LOG_DEBUG(("Verifying signature (HASH)"));
 
     /* Verify signature */
     silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
 
     /* Verify signature */
     silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
-    if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data,
-                        recv_payload->sign_len, hash, hash_len) == FALSE) {
-      /** Incorrect signature */
+    if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data,
+                        payload->sign_len, hash, hash_len) == FALSE) {
       SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
       SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
-      ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-      silc_fsm_next(fsm, silc_ske_st_responder_error);
-      return SILC_FSM_CONTINUE;
+      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      goto err;
     }
 
     SILC_LOG_DEBUG(("Signature is Ok"));
     }
 
     SILC_LOG_DEBUG(("Signature is Ok"));
@@ -1064,136 +1204,63 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
     memset(hash, 'F', hash_len);
   }
 
     memset(hash, 'F', hash_len);
   }
 
-  /* Create the random number x, 1 < x < q. */
-  x = silc_calloc(1, sizeof(*x));
-  silc_mp_init(x);
-  status =
-    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
-                       silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       x);
-  if (status != SILC_SKE_STATUS_OK) {
-    /** Error generating random number */
-    silc_mp_uninit(x);
-    silc_free(x);
-    ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_responder_error);
-    return SILC_FSM_CONTINUE;
-  }
-
-  /* Save the results for later processing */
-  send_payload = silc_calloc(1, sizeof(*send_payload));
-  ske->x = x;
-  ske->ke2_payload = send_payload;
-
-  SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
-
-  /* Do the Diffie Hellman computation, f = g ^ x mod p */
-  silc_mp_init(&send_payload->x);
-  silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x,
-                 &ske->prop->group->group);
-
-  SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
-
-  /* Compute the shared secret key */
-  KEY = silc_calloc(1, sizeof(*KEY));
-  silc_mp_init(KEY);
-  silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
-                 &ske->prop->group->group);
-  ske->KEY = KEY;
-
-  /** Send KE2 payload */
-  silc_fsm_next(fsm, silc_ske_st_responder_phase4);
-  return SILC_FSM_CONTINUE;
-}
-
-/* Phase-4.  Send KE2 payload */
-
-SILC_FSM_STATE(silc_ske_st_responder_phase4)
-{
-  SilcSKE ske = fsm_context;
-  SilcSKEStatus status;
-  SilcBuffer payload_buf;
-  unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk;
-  SilcUInt32 hash_len, sign_len, pk_len;
-
-  SILC_LOG_DEBUG(("Start"));
-
-  if (ske->public_key && ske->private_key) {
-    SILC_LOG_DEBUG(("Getting public key"));
+  ske->status = SILC_SKE_STATUS_OK;
 
 
-    /* Get the public key */
-    pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
-    if (!pk) {
-      /** Error encoding public key */
-      status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-      silc_fsm_next(fsm, silc_ske_st_responder_error);
-      return SILC_FSM_CONTINUE;
-    }
-    ske->ke2_payload->pk_data = pk;
-    ske->ke2_payload->pk_len = pk_len;
+  /* Process key material */
+  key_len = silc_cipher_get_key_len(ske->prop->cipher);
+  block_len = silc_cipher_get_key_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);
+  if (!ske->keymat) {
+    SILC_LOG_ERROR(("Error processing key material"));
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
 
 
-    SILC_LOG_DEBUG(("Computing HASH value"));
+  /* Send SUCCESS packet */
+  /* XXX */
 
 
-    /* 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;
-    }
+  /** Waiting completion */
+  silc_fsm_next(fsm, silc_ske_st_initiator_end);
+  return SILC_FSM_WAIT;
 
 
-    ske->hash = silc_memdup(hash, hash_len);
-    ske->hash_len = hash_len;
+ err:
+  memset(hash, 'F', sizeof(hash));
+  silc_ske_payload_ke_free(payload);
+  ske->ke2_payload = NULL;
 
 
-    SILC_LOG_DEBUG(("Signing HASH value"));
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
 
 
-    /* Sign the hash value */
-    silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
-                                  ske->private_key->prv_len);
-    if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
-       !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
-      /** Error computing signature */
-      status = SILC_SKE_STATUS_SIGNATURE_ERROR;
-      silc_fsm_next(fsm, silc_ske_st_responder_error);
-      return SILC_FSM_CONTINUE;
-    }
-    ske->ke2_payload->sign_data = silc_memdup(sign, sign_len);
-    ske->ke2_payload->sign_len = sign_len;
-    memset(sign, 0, sizeof(sign));
-  }
-  ske->ke2_payload->pk_type = ske->pk_type;
+  if (public_key)
+    silc_pkcs_public_key_free(public_key);
 
 
-  /* Encode the Key Exchange Payload */
-  status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
-                                     &payload_buf);
-  if (status != SILC_SKE_STATUS_OK) {
-    /** Error encoding KE payload */
-    ske->status = status;
-    silc_fsm_next(fsm, silc_ske_st_responder_error);
-    return SILC_FSM_CONTINUE;
+  if (ske->hash) {
+    memset(ske->hash, 'F', hash_len);
+    silc_free(ske->hash);
+    ske->hash = NULL;
   }
 
   }
 
-  /* Send the packet. */
-  /* XXX */
-
-  silc_buffer_free(payload_buf);
+  if (status == SILC_SKE_STATUS_OK)
+    status = SILC_SKE_STATUS_ERROR;
 
 
-  /** Waiting completion */
-  silc_fsm_next(fsm, silc_ske_st_responder_end);
-  return SILC_FSM_WAIT;
+  /** Error */
+  ske->status = status;
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
+  return SILC_FSM_CONTINUE;
 }
 
 /* Protocol completed */
 
 }
 
 /* Protocol completed */
 
-SILC_FSM_STATE(silc_ske_st_responder_end)
+SILC_FSM_STATE(silc_ske_st_initiator_end)
 {
   SilcSKE ske = fsm_context;
 
   if (ske->aborted) {
     /** Aborted */
 {
   SilcSKE ske = fsm_context;
 
   if (ske->aborted) {
     /** Aborted */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
     return SILC_FSM_CONTINUE;
   }
 
     return SILC_FSM_CONTINUE;
   }
 
@@ -1206,41 +1273,35 @@ SILC_FSM_STATE(silc_ske_st_responder_end)
 
 /* Aborted by application */
 
 
 /* Aborted by application */
 
-SILC_FSM_STATE(silc_ske_st_responder_aborted)
+SILC_FSM_STATE(silc_ske_st_initiator_aborted)
 {
 
 {
 
-  /* Send FAILURE */
-
   return SILC_FSM_FINISH;
 }
 
 /* Error occurred */
 
   return SILC_FSM_FINISH;
 }
 
 /* Error occurred */
 
-SILC_FSM_STATE(silc_ske_st_responder_error)
+SILC_FSM_STATE(silc_ske_st_initiator_error)
 {
 
 {
 
-  /* Send FAILURE */
-
   return SILC_FSM_FINISH;
 }
 
 
   return SILC_FSM_FINISH;
 }
 
 
-static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
+static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
                                        void *destructor_context)
 {
 
 }
 
                                        void *destructor_context)
 {
 
 }
 
-/* Starts the protocol as responder. */
+/* Starts the protocol as initiator */
 
 SilcAsyncOperation
 
 SilcAsyncOperation
-silc_ske_responder_start(SilcSKE ske,
-                        SilcPacketStream stream,
-                        const char *version,
-                        SilcBuffer start_payload,
-                        SilcSKESecurityPropertyFlag flags)
+silc_ske_initiator(SilcSKE ske,
+                  SilcPacketStream stream,
+                  SilcSKEStartPayload start_payload)
 {
 {
-  SILC_LOG_DEBUG(("Start SKE as responder"));
+  SILC_LOG_DEBUG(("Start SKE as initiator"));
 
   if (!ske || !stream || !start_payload)
     return NULL;
 
   if (!ske || !stream || !start_payload)
     return NULL;
@@ -1248,684 +1309,800 @@ silc_ske_responder_start(SilcSKE ske,
   if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
     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,
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske,
                     ske->schedule))
     return NULL;
 
                     ske->schedule))
     return NULL;
 
-  ske->packet_buf = start_payload;
-  ske->flags = flags;
-  ske->version = strdup(version);
+  ske->start_payload = start_payload;
+
+  /* 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_KEY_EXCHANGE,
+                         SILC_PACKET_KEY_EXCHANGE_2,
+                         SILC_PACKET_SUCCESS,
+                         SILC_PACKET_FAILURE, -1);
+
+  /* Start SKE as initiator */
+  silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+
+  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 */
 
 
-  /* Link to packet stream to get key exchange packets */
-  ske->stream = stream;
-  silc_packet_stream_ref(ske->stream);
-  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+SILC_FSM_STATE(silc_ske_st_responder_start)
+{
+  SilcSKE ske = fsm_context;
 
 
-  /* Start SKE as responder */
-  silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
+  SILC_LOG_DEBUG(("Start"));
 
 
-  return &ske->op;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* Start timeout */
+  /* XXX */
+
+  /** Wait for initiator */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase1);
+  return SILC_FSM_WAIT;
 }
 
 }
 
-SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+/* Decode initiator's start payload */
 
 
-SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
+SILC_FSM_STATE(silc_ske_st_responder_phase1)
 {
 {
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEStartPayload remote_payload = NULL, payload = NULL;
+  SilcBuffer packet_buf = &ske->packet->buffer;
 
 
-}
+  SILC_LOG_DEBUG(("Start"));
 
 
-/* Starts rekey protocol as initiator */
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-SilcAsyncOperation
-silc_ske_rekey_initiator_start(SilcSKE ske,
-                              SilcPacketStream stream,
-                              SilcSKERekeyMaterial rekey)
-{
-  SILC_LOG_DEBUG(("Start SKE rekey as initator"));
+  /* 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 || !stream || !rekey)
-    return NULL;
+  /* 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->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
-    return NULL;
+  /* 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);
 
 
-  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
-    return NULL;
+  silc_packet_free(ske->packet);
 
 
-  ske->rekey = rekey;
+  /* Force the mutual authentication flag if we want to do it. */
+  if (ske->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+    SILC_LOG_DEBUG(("Force mutual authentication"));
+    remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
+  }
 
 
-  /* Link to packet stream to get key exchange packets */
-  ske->stream = stream;
-  silc_packet_stream_ref(ske->stream);
-  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+  /* Force PFS flag if we require it */
+  if (ske->flags & SILC_SKE_SP_FLAG_PFS) {
+    SILC_LOG_DEBUG(("Force PFS"));
+    remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
+  }
 
 
-  /* Start SKE rekey as initiator */
-  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
+  /* Disable IV Included flag if requested */
+  if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED &&
+      !(ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)) {
+    SILC_LOG_DEBUG(("We do not support IV Included flag"));
+    remote_payload->flags &= ~SILC_SKE_SP_FLAG_IV_INCLUDED;
+  }
 
 
-  return &ske->op;
-}
+  /* 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);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error selecting proposal */
+    if (remote_payload)
+      silc_ske_payload_start_free(remote_payload);
+    silc_free(payload);
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
+  ske->start_payload = payload;
 
 
-SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
-{
+  silc_ske_payload_start_free(remote_payload);
 
 
+  /** Send proposal to initiator */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+  return SILC_FSM_CONTINUE;
 }
 
 }
 
-/* Starts rekey protocol as responder */
+/* Phase-2.  Send Start Payload */
 
 
-SilcAsyncOperation
-silc_ske_rekey_responder_start(SilcSKE ske,
-                              SilcPacketStream stream,
-                              SilcBuffer ke_payload,
-                              SilcSKERekeyMaterial rekey)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
 {
 {
-  SILC_LOG_DEBUG(("Start SKE rekey as responder"));
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcBuffer payload_buf;
+  SilcSKESecurityProperties prop;
+  SilcSKEDiffieHellmanGroup group = NULL;
 
 
-  if (!ske || !stream || !rekey)
-    return NULL;
-  if (rekey->pfs && !ke_payload)
-    return NULL;
+  SILC_LOG_DEBUG(("Start"));
 
 
-  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
-    return NULL;
+  /* 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;
 
 
-  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
-    return NULL;
+  prop->group = group;
 
 
-  ske->packet_buf = ke_payload;
-  ske->rekey = rekey;
+  /* 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_alloc(ske->start_payload->pkcs_alg_list,
+                     SILC_PKCS_SILC, &prop->pkcs) == FALSE) {
+    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;
+  }
 
 
-  /* Link to packet stream to get key exchange packets */
-  ske->stream = stream;
-  silc_packet_stream_ref(ske->stream);
-  silc_packet_stream_callbacks(ske->stream, &silc_ske_stream_cbs, ske);
+  /* Encode the payload */
+  status = silc_ske_payload_start_encode(ske, ske->start_payload,
+                                        &payload_buf);
+  if (status != SILC_SKE_STATUS_OK)
+    goto err;
 
 
-  /* Start SKE rekey as responder */
-  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start);
+  /* Send the packet. */
+  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0,
+                       payload_buf->data, silc_buffer_len(payload_buf)))
+    goto err;
 
 
-  return &ske->op;
-}
+  silc_buffer_free(payload_buf);
 
 
-/* Assembles security properties */
+  /** Waiting initiator's KE payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+  return SILC_FSM_WAIT;
 
 
-SilcSKEStartPayload
-silc_ske_assemble_security_properties(SilcSKE ske,
-                                     SilcSKESecurityPropertyFlag flags,
-                                     const char *version)
-{
-  SilcSKEStartPayload rp;
-  int i;
+ err:
+  if (group)
+    silc_ske_group_free(group);
 
 
-  SILC_LOG_DEBUG(("Assembling KE Start Payload"));
+  if (prop->pkcs)
+    silc_pkcs_free(prop->pkcs);
+  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);
+  ske->prop = NULL;
 
 
-  rp = silc_calloc(1, sizeof(*rp));
+  if (status == SILC_SKE_STATUS_OK)
+    status = SILC_SKE_STATUS_ERROR;
 
 
-  /* Set flags */
-  rp->flags = (unsigned char)flags;
+  /** Error */
+  ske->status = status;
+  silc_fsm_next(fsm, silc_ske_st_responder_error);
+  return SILC_FSM_CONTINUE;
+}
 
 
-  /* Set random cookie */
-  rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
-  for (i = 0; i < SILC_SKE_COOKIE_LEN; i++)
-    rp->cookie[i] = silc_rng_get_byte_fast(ske->rng);
-  rp->cookie_len = SILC_SKE_COOKIE_LEN;
+/* Phase-3.  Decode initiator's KE payload */
 
 
-  /* Put version */
-  rp->version = strdup(version);
-  rp->version_len = strlen(version);
+SILC_FSM_STATE(silc_ske_st_responder_phase3)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload recv_payload;
+  SilcBuffer packet_buf = &ske->packet->buffer;
 
 
-  /* Get supported Key Exhange groups */
-  rp->ke_grp_list = silc_ske_get_supported_groups();
-  rp->ke_grp_len = strlen(rp->ke_grp_list);
+  SILC_LOG_DEBUG(("Start"));
 
 
-  /* Get supported PKCS algorithms */
-  rp->pkcs_alg_list = silc_pkcs_get_supported();
-  rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-  /* Get supported encryption algorithms */
-  rp->enc_alg_list = silc_cipher_get_supported();
-  rp->enc_alg_len = strlen(rp->enc_alg_list);
+  /* 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;
+  }
 
 
-  /* Get supported hash algorithms */
-  rp->hash_alg_list = silc_hash_get_supported();
-  rp->hash_alg_len = strlen(rp->hash_alg_list);
+  /* Decode Key Exchange Payload */
+  status = silc_ske_payload_ke_decode(ske, packet_buf, &recv_payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error decoding KE payload */
+    silc_packet_free(ske->packet);
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-  /* Get supported HMACs */
-  rp->hmac_alg_list = silc_hmac_get_supported();
-  rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+  ske->ke1_payload = recv_payload;
 
 
-  /* XXX */
-  /* Get supported compression algorithms */
-  rp->comp_alg_list = strdup("none");
-  rp->comp_alg_len = strlen("none");
+  silc_packet_free(ske->packet);
+
+  /* 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) {
+      /** Public key not provided */
+      SILC_LOG_ERROR(("Remote end did not send its public key (or "
+                     "certificate), even though we require it"));
+      ske->status = SILC_SKE_STATUS_PUBLIC_KEY_NOT_PROVIDED;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
+    }
 
 
-  rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
-    2 + rp->version_len +
-    2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len +
-    2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
-    2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
+    if (recv_payload->pk_data && ske->callbacks->verify_key) {
+      SILC_LOG_DEBUG(("Verifying public key"));
 
 
-  return rp;
+      /** Waiting public key verification */
+      silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+      SILC_FSM_CALL(ske->callbacks->verify_key(ske, recv_payload->pk_data,
+                                              recv_payload->pk_len,
+                                              recv_payload->pk_type,
+                                              ske->callbacks->context,
+                                              silc_ske_pk_verified, NULL));
+      /* NOT REACHED */
+    }
+  }
+
+  /** Generate KE2 payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+  return SILC_FSM_CONTINUE;
 }
 
 }
 
-/* Selects the supported security properties from the remote end's Key
-   Exchange Start Payload. */
+/* Phase-4. Generate KE2 payload */
 
 
-static SilcSKEStatus
-silc_ske_select_security_properties(SilcSKE ske,
-                                   const char *version,
-                                   SilcSKEStartPayload payload,
-                                   SilcSKEStartPayload remote_payload)
+SILC_FSM_STATE(silc_ske_st_responder_phase4)
 {
 {
+  SilcSKE ske = fsm_context;
   SilcSKEStatus status;
   SilcSKEStatus status;
-  SilcSKEStartPayload rp;
-  char *cp;
-  int len;
+  SilcSKEKEPayload recv_payload, send_payload;
+  SilcMPInt *x, *KEY;
 
 
-  SILC_LOG_DEBUG(("Parsing KE Start Payload"));
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-  rp = remote_payload;
+  /* Check result of public key verification */
+  if (ske->status != SILC_SKE_STATUS_OK) {
+    /** Public key not verified */
+    SILC_LOG_DEBUG(("Public key verification failed"));
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-  /* Check version string */
-  if (ske->callbacks->check_version) {
-    status = ske->callbacks->check_version(ske, rp->version,
-                                          rp->version_len,
-                                          ske->callbacks->context);
+  recv_payload = ske->ke1_payload;
+
+  /* The public key verification was performed only if the Mutual
+     Authentication flag is set. */
+  if (ske->start_payload &&
+      ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
+    SilcPublicKey public_key = NULL;
+    unsigned char hash[SILC_HASH_MAXLEN];
+    SilcUInt32 hash_len;
+
+    /* Decode the public key */
+    if (!silc_pkcs_public_key_decode(recv_payload->pk_data,
+                                    recv_payload->pk_len,
+                                    &public_key)) {
+      /** Error decoding public key */
+      SILC_LOG_ERROR(("Unsupported/malformed public key received"));
+      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
+    }
+
+    SILC_LOG_DEBUG(("Public key is authentic"));
+
+    /* Compute the hash value */
+    status = silc_ske_make_hash(ske, hash, &hash_len, TRUE);
     if (status != SILC_SKE_STATUS_OK) {
     if (status != SILC_SKE_STATUS_OK) {
+      /** Error computing hash */
       ske->status = status;
       ske->status = status;
-      return status;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
     }
-  }
 
 
-  ske->remote_version = silc_memdup(rp->version, rp->version_len);
+    SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
 
 
-  /* Flags are returned unchanged. */
-  payload->flags = rp->flags;
+    /* Verify signature */
+    silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
+    if (silc_pkcs_verify(ske->prop->pkcs, recv_payload->sign_data,
+                        recv_payload->sign_len, hash, hash_len) == FALSE) {
+      /** Incorrect signature */
+      SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
+      ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
+    }
 
 
-  /* Take cookie, we must return it to sender unmodified. */
-  payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
-  payload->cookie_len = SILC_SKE_COOKIE_LEN;
-  memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN);
+    SILC_LOG_DEBUG(("Signature is Ok"));
 
 
-  /* Put our version to our reply */
-  payload->version = strdup(version);
-  payload->version_len = strlen(version);
+    silc_pkcs_public_key_free(public_key);
+    memset(hash, 'F', hash_len);
+  }
 
 
-  /* Get supported Key Exchange groups */
-  cp = rp->ke_grp_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+  /* Create the random number x, 1 < x < q. */
+  x = silc_calloc(1, sizeof(*x));
+  silc_mp_init(x);
+  status =
+    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
+                       silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
+                       x);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error generating random number */
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  /* Save the results for later processing */
+  send_payload = silc_calloc(1, sizeof(*send_payload));
+  ske->x = x;
+  ske->ke2_payload = send_payload;
 
 
-      SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
+  SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
 
 
-      if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
-       SILC_LOG_DEBUG(("Found KE group `%s'", item));
+  /* Do the Diffie Hellman computation, f = g ^ x mod p */
+  silc_mp_init(&send_payload->x);
+  silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x,
+                 &ske->prop->group->group);
 
 
-       payload->ke_grp_len = len;
-       payload->ke_grp_list = item;
-       break;
-      }
+  SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
 
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+  /* Compute the shared secret key */
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_pow_mod(KEY, &ske->ke1_payload->x, ske->x,
+                 &ske->prop->group->group);
+  ske->KEY = KEY;
 
 
-      if (item)
-       silc_free(item);
-    }
+  /** Send KE2 payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase5);
+  return SILC_FSM_CONTINUE;
+}
 
 
-    if (!payload->ke_grp_len && !payload->ke_grp_list) {
-      SILC_LOG_DEBUG(("Could not find supported KE group"));
-      silc_free(payload);
-      return SILC_SKE_STATUS_UNKNOWN_GROUP;
-    }
-  } else {
+/* Phase-5.  Send KE2 payload */
 
 
-    if (!rp->ke_grp_len) {
-      SILC_LOG_DEBUG(("KE group not defined in payload"));
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
+SILC_FSM_STATE(silc_ske_st_responder_phase5)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcBuffer payload_buf;
+  unsigned char hash[SILC_HASH_MAXLEN], sign[2048 + 1], *pk;
+  SilcUInt32 hash_len, sign_len, pk_len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->public_key && ske->private_key) {
+    SILC_LOG_DEBUG(("Getting public key"));
+
+    /* Get the public key */
+    pk = silc_pkcs_public_key_encode(ske->public_key, &pk_len);
+    if (!pk) {
+      /** Error encoding public key */
+      status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
     }
     }
+    ske->ke2_payload->pk_data = pk;
+    ske->ke2_payload->pk_len = pk_len;
 
 
-    SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
-    SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
+    SILC_LOG_DEBUG(("Computing HASH value"));
 
 
-    payload->ke_grp_len = rp->ke_grp_len;
-    payload->ke_grp_list = strdup(rp->ke_grp_list);
-  }
+    /* 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;
+    }
 
 
-  /* Get supported PKCS algorithms */
-  cp = rp->pkcs_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+    ske->hash = silc_memdup(hash, hash_len);
+    ske->hash_len = hash_len;
 
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+    SILC_LOG_DEBUG(("Signing HASH value"));
 
 
-      SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
+    /* Sign the hash value */
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, ske->private_key->prv,
+                                  ske->private_key->prv_len);
+    if (silc_pkcs_get_key_len(ske->prop->pkcs) / 8 > sizeof(sign) - 1 ||
+       !silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len)) {
+      /** Error computing signature */
+      status = SILC_SKE_STATUS_SIGNATURE_ERROR;
+      silc_fsm_next(fsm, silc_ske_st_responder_error);
+      return SILC_FSM_CONTINUE;
+    }
+    ske->ke2_payload->sign_data = silc_memdup(sign, sign_len);
+    ske->ke2_payload->sign_len = sign_len;
+    memset(sign, 0, sizeof(sign));
+  }
+  ske->ke2_payload->pk_type = ske->pk_type;
 
 
-      if (silc_pkcs_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found PKCS alg `%s'", item));
+  /* Encode the Key Exchange Payload */
+  status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
+                                     &payload_buf);
+  if (status != SILC_SKE_STATUS_OK) {
+    /** Error encoding KE payload */
+    ske->status = status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-       payload->pkcs_alg_len = len;
-       payload->pkcs_alg_list = item;
-       break;
-      }
+  /* Send the packet. */
+  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE_2, 0,
+                       payload_buf->data, silc_buffer_len(payload_buf))) {
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+  silc_buffer_free(payload_buf);
 
 
-      if (item)
-       silc_free(item);
-    }
+  /** Waiting completion */
+  silc_fsm_next(fsm, silc_ske_st_responder_end);
+  return SILC_FSM_WAIT;
+}
 
 
-    if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) {
-      SILC_LOG_DEBUG(("Could not find supported PKCS alg"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_UNKNOWN_PKCS;
-    }
-  } else {
+/* Protocol completed */
 
 
-    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_FSM_STATE(silc_ske_st_responder_end)
+{
+  SilcSKE ske = fsm_context;
+  unsigned char tmp[4];
+  SilcUInt32 hash_len, key_len, block_len;
 
 
-    SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
-    SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-    payload->pkcs_alg_len = rp->pkcs_alg_len;
-    payload->pkcs_alg_list = strdup(rp->pkcs_alg_list);
+  /* 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;
   }
   }
+  silc_packet_free(ske->packet);
 
 
-  /* Get supported encryption algorithms */
-  cp = rp->enc_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+  /* Process key material */
+  key_len = silc_cipher_get_key_len(ske->prop->cipher);
+  block_len = silc_cipher_get_key_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);
+  if (!ske->keymat) {
+    /** Error processing key material */
+    ske->status = SILC_SKE_STATUS_ERROR;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
+  }
 
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  /* Send SUCCESS packet */
+  SILC_PUT32_MSB(SILC_SKE_STATUS_OK, tmp);
+  silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, tmp, 4);
 
 
-      SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item));
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
 
-      if (silc_cipher_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
+  /* Call the completion callback */
+  if (ske->callbacks->completed)
+    ske->callbacks->completed(ske, ske->status, ske->prop, ske->keymat,
+                             ske->rekey, ske->callbacks->context);
 
 
-       payload->enc_alg_len = len;
-       payload->enc_alg_list = item;
-       break;
-      }
+  return SILC_FSM_FINISH;
+}
 
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+/* Aborted by application */
 
 
-      if (item)
-       silc_free(item);
-    }
+SILC_FSM_STATE(silc_ske_st_responder_aborted)
+{
+  SilcSKE ske = fsm_context;
+  unsigned char tmp[4];
 
 
-    if (!payload->enc_alg_len && !payload->enc_alg_list) {
-      SILC_LOG_DEBUG(("Could not find supported encryption alg"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_UNKNOWN_CIPHER;
-    }
-  } else {
+  SILC_LOG_DEBUG(("Key exchange protocol aborted"));
 
 
-    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;
-    }
+  /* Send FAILURE packet */
+  SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, tmp);
+  silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4);
 
 
-    SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
-                   rp->enc_alg_list));
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
 
-    payload->enc_alg_len = rp->enc_alg_len;
-    payload->enc_alg_list = strdup(rp->enc_alg_list);
-  }
+  return SILC_FSM_FINISH;
+}
 
 
-  /* Get supported hash algorithms */
-  cp = rp->hash_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+/* Failure received from remote */
 
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+SILC_FSM_STATE(silc_ske_st_responder_failure)
+{
+  SilcSKE ske = fsm_context;
+  SilcUInt32 error = SILC_SKE_STATUS_ERROR;
 
 
-      SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+  SILC_LOG_DEBUG(("Key exchange protocol failed"));
 
 
-      if (silc_hash_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+  if (silc_buffer_len(&ske->packet->buffer) == 4)
+    SILC_GET32_MSB(error, ske->packet->buffer.data);
+  ske->status = error;
 
 
-       payload->hash_alg_len = len;
-       payload->hash_alg_list = item;
-       break;
-      }
+  /* Call the completion callback */
+  if (ske->callbacks->completed)
+    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
+                             ske->callbacks->context);
 
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+  silc_packet_free(ske->packet);
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
 
-      if (item)
-       silc_free(item);
-    }
+  return SILC_FSM_FINISH;
+}
 
 
-    if (!payload->hash_alg_len && !payload->hash_alg_list) {
-      SILC_LOG_DEBUG(("Could not find supported hash alg"));
-      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;
-    }
-  } else {
+/* Error occurred */
 
 
-    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_FSM_STATE(silc_ske_st_responder_error)
+{
+  SilcSKE ske = fsm_context;
+  unsigned char tmp[4];
 
 
-    SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
-                   rp->hash_alg_list));
+  SILC_LOG_DEBUG(("Error %d (%s) during key exchange protocol",
+                 ske->status, silc_ske_map_status(ske->status)));
 
 
-    payload->hash_alg_len = rp->hash_alg_len;
-    payload->hash_alg_list = strdup(rp->hash_alg_list);
-  }
+  /* Send FAILURE packet */
+  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);
 
 
-  /* Get supported HMACs */
-  cp = rp->hmac_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  return SILC_FSM_FINISH;
+}
 
 
-      SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
 
 
-      if (silc_hmac_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found HMAC `%s'", item));
+static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
+                                       void *destructor_context)
+{
 
 
-       payload->hmac_alg_len = len;
-       payload->hmac_alg_list = item;
-       break;
-      }
+}
 
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+/* Starts the protocol as responder. */
 
 
-      if (item)
-       silc_free(item);
-    }
+SilcAsyncOperation
+silc_ske_responder(SilcSKE ske,
+                  SilcPacketStream stream,
+                  const char *version,
+                  SilcSKESecurityPropertyFlag flags)
+{
+  SILC_LOG_DEBUG(("Start SKE as responder"));
 
 
-    if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
-      SILC_LOG_DEBUG(("Could not find supported HMAC"));
-      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;
-    }
-  } else {
+  if (!ske || !stream || !version) {
+    return NULL;
+  }
 
 
-    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;
-    }
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
 
-    SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
-                   rp->hmac_alg_list));
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske,
+                    ske->schedule))
+    return NULL;
 
 
-    payload->hmac_alg_len = rp->hmac_alg_len;
-    payload->hmac_alg_list = strdup(rp->hmac_alg_list);
-  }
+  ske->flags = flags;
+  ske->version = strdup(version);
+  if (!ske->version)
+    return NULL;
+  ske->responder = TRUE;
 
 
-  /* Get supported compression algorithms */
-  cp = rp->comp_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+  /* 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_KEY_EXCHANGE,
+                         SILC_PACKET_KEY_EXCHANGE_1,
+                         SILC_PACKET_SUCCESS,
+                         SILC_PACKET_FAILURE, -1);
 
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  /* Start SKE as responder */
+  silc_fsm_start(&ske->fsm, silc_ske_st_responder_start);
 
 
-      SILC_LOG_DEBUG(("Proposed Compression `%s'", item));
+  return &ske->op;
+}
 
 
-#if 1
-      if (!strcmp(item, "none")) {
-       SILC_LOG_DEBUG(("Found Compression `%s'", item));
-       payload->comp_alg_len = len;
-       payload->comp_alg_list = item;
-       break;
-      }
-#else
-      if (silc_hmac_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found Compression `%s'", item));
-       payload->comp_alg_len = len;
-       payload->comp_alg_list = item;
-       break;
-      }
-#endif
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
+
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
+{
+  return SILC_FSM_FINISH;
+}
+
+/* Starts rekey protocol as initiator */
 
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+SilcAsyncOperation
+silc_ske_rekey_initiator(SilcSKE ske,
+                        SilcPacketStream stream,
+                        SilcSKERekeyMaterial rekey)
+{
+  SILC_LOG_DEBUG(("Start SKE rekey as initator"));
 
 
-      if (item)
-       silc_free(item);
-    }
-  }
+  if (!ske || !stream || !rekey)
+    return NULL;
 
 
-  payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
-    2 + payload->version_len +
-    2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len +
-    2 + payload->enc_alg_len + 2 + payload->hash_alg_len +
-    2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
 
-  return SILC_SKE_STATUS_OK;
+  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+    return NULL;
+
+  ske->rekey = rekey;
+
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
+
+  /* Start SKE rekey as initiator */
+  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
+
+  return &ske->op;
 }
 
 }
 
-/* Creates random number such that 1 < rnd < n and at most length
-   of len bits. The rnd sent as argument must be initialized. */
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start);
 
 
-static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
-                                        SilcUInt32 len,
-                                        SilcMPInt *rnd)
+SILC_FSM_STATE(silc_ske_st_rekey_responder_start)
 {
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  unsigned char *string;
-  SilcUInt32 l;
+  return SILC_FSM_FINISH;
+}
 
 
-  if (!len)
-    return SILC_SKE_STATUS_ERROR;
+/* Starts rekey protocol as responder */
 
 
-  SILC_LOG_DEBUG(("Creating random number"));
+SilcAsyncOperation
+silc_ske_rekey_responder(SilcSKE ske,
+                        SilcPacketStream stream,
+                        SilcBuffer ke_payload,
+                        SilcSKERekeyMaterial rekey)
+{
+  SILC_LOG_DEBUG(("Start SKE rekey as responder"));
 
 
-  l = ((len - 1) / 8);
+  if (!ske || !stream || !rekey)
+    return NULL;
+  if (rekey->pfs && !ke_payload)
+    return NULL;
 
 
-  /* Get the random number as string */
-  string = silc_rng_get_rn_data(ske->rng, l);
-  if (!string)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
 
-  /* Decode the string into a MP integer */
-  silc_mp_bin2mp(string, l, rnd);
-  silc_mp_mod_2exp(rnd, rnd, len);
+  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+    return NULL;
 
 
-  /* Checks */
-  if (silc_mp_cmp_ui(rnd, 1) < 0)
-    status = SILC_SKE_STATUS_ERROR;
-  if (silc_mp_cmp(rnd, n) >= 0)
-    status = SILC_SKE_STATUS_ERROR;
+  //  ske->packet_buf = ke_payload;
+  ske->rekey = rekey;
 
 
-  memset(string, 'F', l);
-  silc_free(string);
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
 
 
-  return status;
+  /* Start SKE rekey as responder */
+  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_responder_start);
+
+  return &ske->op;
 }
 
 }
 
-/* Creates a hash value HASH as defined in the SKE protocol. If the
-   `initiator' is TRUE then this function is used to create the HASH_i
-   hash value defined in the protocol. If it is FALSE then this is used
-   to create the HASH value defined by the protocol. */
+/* Assembles security properties */
 
 
-static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
-                                       unsigned char *return_hash,
-                                       SilcUInt32 *return_hash_len,
-                                       int initiator)
+SilcSKEStartPayload
+silc_ske_assemble_security_properties(SilcSKE ske,
+                                     SilcSKESecurityPropertyFlag flags,
+                                     const char *version)
 {
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcBuffer buf;
-  unsigned char *e, *f, *KEY;
-  SilcUInt32 e_len, f_len, KEY_len;
-  int ret;
+  SilcSKEStartPayload rp;
+  int i;
 
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Assembling KE Start Payload"));
 
 
-  if (initiator == FALSE) {
-    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);
+  rp = silc_calloc(1, sizeof(*rp));
 
 
-    /* Format the buffer used to compute the hash value */
-    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
-                                ske->ke2_payload->pk_len +
-                                ske->ke1_payload->pk_len +
-                                e_len + f_len + KEY_len);
-    if (!buf)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
+  /* Set flags */
+  rp->flags = (unsigned char)flags;
 
 
-    /* Initiator is not required to send its public key */
-    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_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_END);
-    }
-    if (ret == -1) {
-      silc_buffer_free(buf);
-      memset(e, 0, e_len);
-      memset(f, 0, f_len);
-      memset(KEY, 0, KEY_len);
-      silc_free(e);
-      silc_free(f);
-      silc_free(KEY);
-      return SILC_SKE_STATUS_ERROR;
-    }
+  /* Set random cookie */
+  rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
+  for (i = 0; i < SILC_SKE_COOKIE_LEN; i++)
+    rp->cookie[i] = silc_rng_get_byte_fast(ske->rng);
+  rp->cookie_len = SILC_SKE_COOKIE_LEN;
 
 
-    memset(e, 0, e_len);
-    memset(f, 0, f_len);
-    memset(KEY, 0, KEY_len);
-    silc_free(e);
-    silc_free(f);
-    silc_free(KEY);
-  } else {
-    e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
+  /* Put version */
+  rp->version = strdup(version);
+  rp->version_len = strlen(version);
 
 
-    buf = silc_buffer_alloc_size(silc_buffer_len(ske->start_payload_copy) +
-                                ske->ke1_payload->pk_len + e_len);
-    if (!buf)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
+  /* Get supported Key Exhange groups */
+  rp->ke_grp_list = silc_ske_get_supported_groups();
+  rp->ke_grp_len = strlen(rp->ke_grp_list);
 
 
-    /* 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_END);
-    if (ret == -1) {
-      silc_buffer_free(buf);
-      memset(e, 0, e_len);
-      silc_free(e);
-      return SILC_SKE_STATUS_ERROR;
-    }
+  /* Get supported PKCS algorithms */
+  rp->pkcs_alg_list = silc_pkcs_get_supported();
+  rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
 
 
-    memset(e, 0, e_len);
-    silc_free(e);
-  }
+  /* Get supported encryption algorithms */
+  rp->enc_alg_list = silc_cipher_get_supported();
+  rp->enc_alg_len = strlen(rp->enc_alg_list);
 
 
-  /* Make the hash */
-  silc_hash_make(ske->prop->hash, buf->data, silc_buffer_len(buf),
-                return_hash);
-  *return_hash_len = silc_hash_len(ske->prop->hash);
+  /* Get supported hash algorithms */
+  rp->hash_alg_list = silc_hash_get_supported();
+  rp->hash_alg_len = strlen(rp->hash_alg_list);
 
 
-  if (initiator == FALSE) {
-    SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
-  } else {
-    SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len);
-  }
+  /* Get supported HMACs */
+  rp->hmac_alg_list = silc_hmac_get_supported();
+  rp->hmac_alg_len = strlen(rp->hmac_alg_list);
 
 
-  silc_buffer_free(buf);
+  /* XXX */
+  /* Get supported compression algorithms */
+  rp->comp_alg_list = strdup("none");
+  rp->comp_alg_len = strlen("none");
 
 
-  return status;
+  rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
+    2 + rp->version_len +
+    2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len +
+    2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
+    2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
+
+  return rp;
 }
 
 /* Processes the provided key material `data' as the SILC protocol
 }
 
 /* Processes the provided key material `data' as the SILC protocol
@@ -1950,6 +2127,10 @@ silc_ske_process_key_material_data(unsigned char *data,
   if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
     return NULL;
 
   if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
     return NULL;
 
+  key = silc_calloc(1, sizeof(*key));
+  if (!key)
+    return NULL;
+
   buf = silc_buffer_alloc_size(1 + data_len);
   if (!buf)
     return NULL;
   buf = silc_buffer_alloc_size(1 + data_len);
   if (!buf)
     return NULL;
@@ -2129,7 +2310,6 @@ silc_ske_process_key_material(SilcSKE ske,
                              SilcUInt32 req_enc_key_len,
                              SilcUInt32 req_hmac_key_len)
 {
                              SilcUInt32 req_enc_key_len,
                              SilcUInt32 req_hmac_key_len)
 {
-  SilcSKEStatus status;
   SilcBuffer buf;
   unsigned char *tmpbuf;
   SilcUInt32 klen;
   SilcBuffer buf;
   unsigned char *tmpbuf;
   SilcUInt32 klen;
@@ -2190,6 +2370,81 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial key)
   silc_free(key);
 }
 
   silc_free(key);
 }
 
+/* Set keys into use */
+
+SilcBool silc_ske_set_keys(SilcSKE ske,
+                          SilcSKEKeyMaterial keymat,
+                          SilcSKESecurityProperties prop,
+                          SilcCipher *ret_send_key,
+                          SilcCipher *ret_receive_key,
+                          SilcHmac *ret_hmac_send,
+                          SilcHmac *ret_hmac_receive,
+                          SilcHash *ret_hash)
+{
+  /* Allocate ciphers to be used in the communication */
+  if (ret_send_key) {
+    if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher),
+                          ret_send_key))
+      return FALSE;
+  }
+  if (ret_receive_key) {
+    if (!silc_cipher_alloc((char *)silc_cipher_get_name(prop->cipher),
+                          ret_receive_key))
+      return FALSE;
+  }
+
+  /* Allocate HMACs */
+  if (ret_hmac_send) {
+    if (!silc_hmac_alloc((char *)silc_hmac_get_name(prop->hmac), NULL,
+                        ret_hmac_send))
+      return FALSE;
+  }
+  if (ret_hmac_receive) {
+    if (!silc_hmac_alloc((char *)silc_hmac_get_name(prop->hmac), NULL,
+                        ret_hmac_receive))
+      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" : ""));
+
+  return TRUE;
+}
+
 const char *silc_ske_status_string[] =
 {
   /* Official */
 const char *silc_ske_status_string[] =
 {
   /* Official */
@@ -2207,9 +2462,7 @@ const char *silc_ske_status_string[] =
   "Invalid cookie",
 
   /* Other errors */
   "Invalid cookie",
 
   /* Other errors */
-  "Pending",
   "Remote did not provide public key",
   "Remote did not provide public key",
-  "Key exchange protocol is not active",
   "Bad reserved field in packet",
   "Bad payload length in packet",
   "Error computing signature",
   "Bad reserved field in packet",
   "Bad payload length in packet",
   "Error computing signature",
@@ -2235,11 +2488,11 @@ const char *silc_ske_map_status(SilcSKEStatus status)
 /* Parses remote host's version string. */
 
 SilcBool silc_ske_parse_version(SilcSKE ske,
 /* Parses remote host's version string. */
 
 SilcBool silc_ske_parse_version(SilcSKE ske,
-                           SilcUInt32 *protocol_version,
-                           char **protocol_version_string,
-                           SilcUInt32 *software_version,
-                           char **software_version_string,
-                           char **vendor_version)
+                               SilcUInt32 *protocol_version,
+                               char **protocol_version_string,
+                               SilcUInt32 *software_version,
+                               char **software_version_string,
+                               char **vendor_version)
 {
   return silc_parse_version_string(ske->remote_version,
                                   protocol_version,
 {
   return silc_parse_version_string(ske->remote_version,
                                   protocol_version,