Added SILC Server library.
[silc.git] / lib / silcske / silcske.c
index cb4713708ae0305eb360d7ec167310bee3d46f41..c230e36b72ed4a7b820d58aa1296d2316c7228d2 100644 (file)
 */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.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,
@@ -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);
-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 */
 
@@ -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)
 {
+  ske->status = status;
   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;
-  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) {
-    /** Error decoding Start Payload */
     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);
-      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);
-      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;
-  SilcSKEStatus status;
-  SilcSKEStartPayload remote_payload = NULL, payload = NULL;
+  SilcBuffer payload_buf;
+  SilcStatus status;
 
   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;
   }
 
-  /* 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) {
-    /** Error decoding Start Payload */
+    /** Error encoding Start Payload */
     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;
   }
 
-  /* 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. */
-  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) {
-    /** Error selecting proposal */
-    if (remote_payload)
-      silc_ske_payload_start_free(remote_payload);
-    silc_free(payload);
+    /** Error decoding Start Payload */
     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;
   }
 
-  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));
-  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 (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;
   }
-  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;
   }
-  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;
   }
-  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;
   }
 
-  /* 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:
-  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);
@@ -924,84 +923,238 @@ SILC_FSM_STATE(silc_ske_st_responder_phase1)
 
   /** 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;
 }
 
-/* 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;
-  SilcSKEKEPayload recv_payload;
+  SilcBuffer payload_buf;
+  SilcMPInt *x;
+  SilcSKEKEPayload payload;
+  SilcUInt32 pk_len;
 
   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;
   }
-
-  /* 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) {
-    /** Error decoding KE payload */
+    /** Error generating random number */
+    silc_mp_uninit(x);
+    silc_free(x);
     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;
   }
 
-  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) {
+    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(("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;
     }
+    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;
 }
 
-/* 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;
-  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 */
-    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
     return SILC_FSM_CONTINUE;
   }
 
@@ -1013,49 +1166,36 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
     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 */
-    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)) {
-      /** 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;
+      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      goto err;
     }
 
     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);
-    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"));
-      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"));
@@ -1064,136 +1204,63 @@ SILC_FSM_STATE(silc_ske_st_responder_phase3)
     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 */
 
-SILC_FSM_STATE(silc_ske_st_responder_end)
+SILC_FSM_STATE(silc_ske_st_initiator_end)
 {
   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;
   }
 
@@ -1206,41 +1273,35 @@ SILC_FSM_STATE(silc_ske_st_responder_end)
 
 /* 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 */
 
-SILC_FSM_STATE(silc_ske_st_responder_error)
+SILC_FSM_STATE(silc_ske_st_initiator_error)
 {
 
-  /* Send FAILURE */
-
   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)
 {
 
 }
 
-/* Starts the protocol as responder. */
+/* Starts the protocol as initiator */
 
 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;
@@ -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_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->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;
-  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) {
+      /** Error computing hash */
       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
@@ -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;
 
+  key = silc_calloc(1, sizeof(*key));
+  if (!key)
+    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)
 {
-  SilcSKEStatus status;
   SilcBuffer buf;
   unsigned char *tmpbuf;
   SilcUInt32 klen;
@@ -2190,6 +2370,81 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial 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 */
@@ -2207,9 +2462,7 @@ const char *silc_ske_status_string[] =
   "Invalid cookie",
 
   /* Other errors */
-  "Pending",
   "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",
@@ -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,
-                           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,