Added SILC Server library.
[silc.git] / lib / silcske / silcske.c
index 4fd34f780c840b044f56b68d95f36f7f02c30774..c230e36b72ed4a7b820d58aa1296d2316c7228d2 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcske.c 
+  silcske.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 - 2002 Pekka Riikonen
+  Copyright (C) 2000 - 2005 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
 */
 /* $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);
+/************************** Types and definitions ***************************/
 
 /* Structure to hold all SKE callbacks. */
 struct SilcSKECallbacksStruct {
-  SilcSKESendPacketCb send_packet;
-  SilcSKECb payload_receive;
   SilcSKEVerifyCb verify_key;
-  SilcSKECb proto_continue;
-  SilcSKECheckVersion check_version;
+  SilcSKECompletionCb completed;
   void *context;
 };
 
-/* Allocates new SKE object. */
-
-SilcSKE silc_ske_alloc(SilcRng rng, void *context)
-{
-  SilcSKE ske;
 
-  SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
+/************************ Static utility functions **************************/
 
-  ske = silc_calloc(1, sizeof(*ske));
-  if (!ske)
-    return NULL;
-  ske->status = SILC_SKE_STATUS_OK;
-  ske->rng = rng;
-  ske->user_data = context;
-  ske->users = 1;
+SilcSKEKeyMaterial
+silc_ske_process_key_material_data(unsigned char *data,
+                                  SilcUInt32 data_len,
+                                  SilcUInt32 req_iv_len,
+                                  SilcUInt32 req_enc_key_len,
+                                  SilcUInt32 req_hmac_key_len,
+                                  SilcHash hash);
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+                             SilcUInt32 req_iv_len,
+                             SilcUInt32 req_enc_key_len,
+                             SilcUInt32 req_hmac_key_len);
 
-  return ske;
-}
 
-/* Free's SKE object. */
+/* Packet callback */
 
-void silc_ske_free(SilcSKE ske)
+static SilcBool silc_ske_packet_receive(SilcPacketEngine engine,
+                                       SilcPacketStream stream,
+                                       SilcPacket packet,
+                                       void *callback_context,
+                                       void *app_context)
 {
-  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"));
+  SilcSKE ske = callback_context;
+  ske->packet = packet;
+  silc_fsm_continue(&ske->fsm);
+  return TRUE;
+}
 
-  if (ske) {
-    /* Free start payload */
-    if (ske->start_payload)
-      silc_ske_payload_start_free(ske->start_payload);
+/* Packet stream callbacks */
+static SilcPacketCallbacks silc_ske_stream_cbs =
+{
+  silc_ske_packet_receive, NULL, NULL
+};
 
-    /* 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);
+/* Aborts SKE protocol */
 
-    /* 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);
-    silc_free(ske);
-  }
+static void silc_ske_abort(SilcAsyncOperation op, void *context)
+{
+  SilcSKE ske = context;
+  ske->aborted = TRUE;
 }
 
-/* Sets the callback functions for the SKE session. 
-
-   The `send_packet' callback is a function that sends the packet to
-   network. The SKE library will call it at any time packet needs to
-   be sent to the remote host. 
-
-   The `payload_receive' callback is called when the remote host's Key
-   Exchange Start Payload has been processed.  The payload is saved
-   to ske->start_payload if the application would need it.  The application
-   must also provide the payload to the next state of the SKE.
-
-   The `verify_key' callback is called to verify the received public key
-   or certificate.  The verification process is most likely asynchronous.
-   That is why the application must call the completion callback when the
-   verification process has been completed. The library then calls the user
-   callback (`proto_continue'), if it is provided to indicate that the SKE
-   protocol may continue. 
-   
-   The `proto_continue' callback is called to indicate that it is
-   safe to continue the execution of the SKE protocol after executing
-   an asynchronous operation, such as calling the `verify_key' callback
-   function, which is asynchronous. The application should check the
-   ske->status in this function to check whether it is Ok to continue
-   the execution of the protocol.
-
-   The `check_version' callback is called to verify the remote host's
-   version. The application may check its own version against the remote
-   host's version and determine whether supporting the remote host
-   is possible. 
-
-   The `context' is passed as argument to all of the above callback
-   functions. */
+/* Public key verification completion callback */
 
-void silc_ske_set_callbacks(SilcSKE ske,
-                           SilcSKESendPacketCb send_packet,
-                           SilcSKECb payload_receive,
-                           SilcSKEVerifyCb verify_key,
-                           SilcSKECb proto_continue,
-                           SilcSKECheckVersion check_version,
-                           void *context)
+static void silc_ske_pk_verified(SilcSKE ske, SilcSKEStatus status,
+                                void *completion_context)
 {
-  if (ske->callbacks)
-    silc_free(ske->callbacks);
-  ske->callbacks = silc_calloc(1, sizeof(*ske->callbacks));
-  if (!ske->callbacks)
-    return;
-  ske->callbacks->send_packet = send_packet;
-  ske->callbacks->payload_receive = payload_receive;
-  ske->callbacks->verify_key = verify_key;
-  ske->callbacks->proto_continue = proto_continue;
-  ske->callbacks->check_version = check_version;
-  ske->callbacks->context = context;
+  ske->status = status;
+  SILC_FSM_CALL_CONTINUE(&ske->fsm);
 }
 
-/* Starts the SILC Key Exchange protocol for initiator. The connection
-   to the remote end must be established before calling this function
-   and the connecting socket must be sent as argument. This function
-   creates the Key Exchange Start Payload which includes all our
-   configured security properties. This payload is then sent to the
-   remote end for further processing. This payload must be sent as
-   argument to the function, however, it must not be encoded
-   already, it is done by this function. The caller must not free
-   the `start_payload' since the SKE library will save it.
-
-   The packet sending is done by calling a callback function. Caller
-   must provide a routine to send the packet. */
-
-SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
-                                      SilcSocketConnection sock,
-                                      SilcSKEStartPayload *start_payload)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcBuffer payload_buf;
-
-  SILC_LOG_DEBUG(("Start"));
+/* Checks remote and local versions */
 
-  ske->sock = sock;
-  ske->rng = rng;
+static SilcSKEStatus silc_ske_check_version(SilcSKE ske)
+{
+  SilcUInt32 l_protocol_version = 0, r_protocol_version = 0;
 
-  /* Encode the payload */
-  status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
-  if (status != SILC_SKE_STATUS_OK)
-    return status;
+  if (!ske->remote_version || !ske->version)
+    return SILC_SKE_STATUS_BAD_VERSION;
 
-  /* 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(payload_buf);
-  ske->start_payload = start_payload;
+  if (!silc_parse_version_string(ske->remote_version, &r_protocol_version,
+                                NULL, NULL, NULL, NULL))
+    return SILC_SKE_STATUS_BAD_VERSION;
 
-  /* Send the packet. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, 
-                                  ske->callbacks->context);
+  if (!silc_parse_version_string(ske->version, &l_protocol_version,
+                                NULL, NULL, NULL, NULL))
+    return SILC_SKE_STATUS_BAD_VERSION;
 
-  silc_buffer_free(payload_buf);
+  /* If remote is too new, don't connect */
+  if (l_protocol_version < r_protocol_version)
+    return SILC_SKE_STATUS_BAD_VERSION;
 
-  return status;
+  return SILC_SKE_STATUS_OK;
 }
 
-/* Function called after ske_initiator_start fuction. This receives
-   the remote ends Key Exchange Start payload which includes the
-   security properties selected by the responder from our payload
-   sent in the silc_ske_initiator_start function. */
+/* Selects the supported security properties from the initiator's Key
+   Exchange Start Payload. */
 
-SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske, 
-                                        SilcBuffer start_payload)
+static SilcSKEStatus
+silc_ske_select_security_properties(SilcSKE ske,
+                                   SilcSKEStartPayload payload,
+                                   SilcSKEStartPayload remote_payload)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEStartPayload *payload;
-  SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group;
+  SilcSKEStatus status;
+  SilcSKEStartPayload rp;
+  char *cp;
+  int len;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Parsing KE Start Payload"));
 
-  /* Decode the payload */
-  status = silc_ske_payload_start_decode(ske, start_payload, &payload);
+  rp = remote_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) {
     ske->status = status;
-    silc_ske_payload_start_free(ske->start_payload);
     return status;
   }
 
-  /* Check that the cookie is returned unmodified */
-  if (memcmp(ske->start_payload->cookie, payload->cookie,
-            ske->start_payload->cookie_len)) {
-    SILC_LOG_ERROR(("Responder modified our cookie and it must not do it"));
-    ske->status = SILC_SKE_STATUS_INVALID_COOKIE;
-    silc_ske_payload_start_free(ske->start_payload);
+  /* 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) {
-      ske->status = status;
-      silc_ske_payload_start_free(ske->start_payload);
-      return status;
-    }
+  /* 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);
+  /* 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. The same data is returned to upper levels by calling
-     the callback function. */
-  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_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
+       SILC_LOG_DEBUG(("Found KE group `%s'", item));
 
-  if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
-    goto err;
-  }
+       payload->ke_grp_len = len;
+       payload->ke_grp_list = item;
+       break;
+      }
 
-  if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
-    goto err;
-  }
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
 
-  if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
-    status = SILC_SKE_STATUS_UNKNOWN_HMAC;
-    goto err;
-  }
+      if (item)
+       silc_free(item);
+    }
 
-  /* Save remote's KE Start Payload */
-  ske->start_payload = payload;
+    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 {
 
-  /* Return the received payload by calling the callback function. */
-  if (ske->callbacks->payload_receive)
-    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
+    if (!rp->ke_grp_len) {
+      SILC_LOG_DEBUG(("KE group not defined in payload"));
+      silc_free(payload);
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    }
 
-  return status;
+    SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
+    SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
 
- err:
-  if (payload)
-    silc_ske_payload_start_free(payload);
+    payload->ke_grp_len = rp->ke_grp_len;
+    payload->ke_grp_list = strdup(rp->ke_grp_list);
+  }
 
-  silc_ske_group_free(group);
+  /* Get supported PKCS algorithms */
+  cp = rp->pkcs_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
 
-  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;
+      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 (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+      SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
 
-  ske->status = status;
-  return status;
-}
+      if (silc_pkcs_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found PKCS alg `%s'", item));
 
-/* This function creates random number x, such that 1 < x < q and 
-   computes e = g ^ x mod p and sends the result to the remote end in 
-   Key Exchange Payload. */
+       payload->pkcs_alg_len = len;
+       payload->pkcs_alg_list = item;
+       break;
+      }
 
-SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
-                                        SilcPublicKey public_key,
-                                        SilcPrivateKey private_key,
-                                        SilcSKEPKType pk_type)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcBuffer payload_buf;
-  SilcMPInt *x;
-  SilcSKEKEPayload *payload;
-  SilcUInt32 pk_len;
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
 
-  SILC_LOG_DEBUG(("Start"));
+      if (item)
+       silc_free(item);
+    }
 
-  /* Create the random number x, 1 < x < q. */
-  x = silc_calloc(1, sizeof(*x));
-  if (!x){
-    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-    return ske->status;
-  }
-  silc_mp_init(x);
-  status = 
+    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 {
+
+    if (!rp->pkcs_alg_len) {
+      SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    }
+
+    SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
+    SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
+
+    payload->pkcs_alg_len = rp->pkcs_alg_len;
+    payload->pkcs_alg_list = strdup(rp->pkcs_alg_list);
+  }
+
+  /* Get supported encryption algorithms */
+  cp = rp->enc_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
+
+      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(("Proposed encryption alg `%s'", item));
+
+      if (silc_cipher_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
+
+       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);
+      return SILC_SKE_STATUS_UNKNOWN_CIPHER;
+    }
+  } else {
+
+    if (!rp->enc_alg_len) {
+      SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
+      silc_free(payload->ke_grp_list);
+      silc_free(payload->pkcs_alg_list);
+      silc_free(payload);
+      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    }
+
+    SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
+                   rp->enc_alg_list));
+
+    payload->enc_alg_len = rp->enc_alg_len;
+    payload->enc_alg_list = strdup(rp->enc_alg_list);
+  }
+
+  /* Get supported hash algorithms */
+  cp = rp->hash_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
+
+      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(("Proposed hash alg `%s'", item));
+
+      if (silc_hash_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+
+       payload->hash_alg_len = len;
+       payload->hash_alg_list = item;
+       break;
+      }
+
+      cp += len;
+      if (strlen(cp) == 0)
+       cp = NULL;
+      else
+       cp++;
+
+      if (item)
+       silc_free(item);
+    }
+
+    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);
+  }
+
+  /* Get supported HMACs */
+  cp = rp->hmac_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
+
+      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(("Proposed HMAC `%s'", item));
+
+      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);
+  }
+
+  /* Get supported compression algorithms */
+  cp = rp->comp_alg_list;
+  if (cp && strchr(cp, ',')) {
+    while(cp) {
+      char *item;
+
+      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(("Proposed Compression `%s'", item));
+
+#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
+
+      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;
+}
+
+/* Creates random number such that 1 < rnd < n and at most length
+   of len bits. The rnd sent as argument must be initialized. */
+
+static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
+                                        SilcUInt32 len,
+                                        SilcMPInt *rnd)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  unsigned char *string;
+  SilcUInt32 l;
+
+  if (!len)
+    return SILC_SKE_STATUS_ERROR;
+
+  SILC_LOG_DEBUG(("Creating random number"));
+
+  l = ((len - 1) / 8);
+
+  /* Get the random number as string */
+  string = silc_rng_get_rn_data(ske->rng, l);
+  if (!string)
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+
+  /* Decode the string into a MP integer */
+  silc_mp_bin2mp(string, l, rnd);
+  silc_mp_mod_2exp(rnd, rnd, len);
+
+  /* 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;
+
+  memset(string, 'F', l);
+  silc_free(string);
+
+  return status;
+}
+
+/* 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. */
+
+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_LOG_DEBUG(("Start"));
+
+  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);
+
+    /* 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;
+
+    /* 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;
+    }
+
+    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);
+
+    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;
+
+    /* 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);
+  }
+
+  /* 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 (initiator == FALSE) {
+    SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
+  } else {
+    SILC_LOG_HEXDUMP(("HASH_i"), return_hash, *return_hash_len);
+  }
+
+  silc_buffer_free(buf);
+
+  return status;
+}
+
+
+/******************************* Protocol API *******************************/
+
+/* Allocates new SKE object. */
+
+SilcSKE silc_ske_alloc(SilcRng rng, SilcSchedule schedule,
+                      SilcPublicKey public_key, SilcPrivateKey private_key,
+                      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->public_key = public_key;
+  ske->private_key = private_key;
+  ske->pk_type = SILC_SKE_PK_TYPE_SILC;
+
+  return ske;
+}
+
+/* Free's SKE object. */
+
+void silc_ske_free(SilcSKE ske)
+{
+  SILC_LOG_DEBUG(("Freeing Key Exchange object"));
+
+  if (ske) {
+    /* Free start payload */
+    if (ske->start_payload)
+      silc_ske_payload_start_free(ske->start_payload);
+
+    /* Free KE payload */
+    if (ske->ke1_payload)
+      silc_ske_payload_ke_free(ske->ke1_payload);
+    if (ske->ke2_payload)
+      silc_ske_payload_ke_free(ske->ke2_payload);
+    silc_free(ske->remote_version);
+
+    /* Free rest */
+    if (ske->prop) {
+      if (ske->prop->group)
+       silc_ske_group_free(ske->prop->group);
+      if (ske->prop->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 */
+
+void *silc_ske_get_context(SilcSKE ske)
+{
+  return ske->user_data;
+}
+
+/* Sets protocol callbacks */
+
+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;
+}
+
+
+/******************************** Initiator *********************************/
+
+/* Initiator state machine */
+SILC_FSM_STATE(silc_ske_st_initiator_start);
+SILC_FSM_STATE(silc_ske_st_initiator_phase1);
+SILC_FSM_STATE(silc_ske_st_initiator_phase2);
+SILC_FSM_STATE(silc_ske_st_initiator_phase3);
+SILC_FSM_STATE(silc_ske_st_initiator_phase4);
+SILC_FSM_STATE(silc_ske_st_initiator_end);
+SILC_FSM_STATE(silc_ske_st_initiator_aborted);
+SILC_FSM_STATE(silc_ske_st_initiator_error);
+
+/* Start protocol.  Send our proposal */
+
+SILC_FSM_STATE(silc_ske_st_initiator_start)
+{
+  SilcSKE ske = fsm_context;
+  SilcBuffer payload_buf;
+  SilcStatus status;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
+  }
+
+  /* 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;
+  }
+
+  /* Save the the payload buffer for future use. It is later used to
+     compute the HASH value. */
+  ske->start_payload_copy = payload_buf;
+
+  /* Send the packet */
+  /* XXX */
+
+  /** Wait for responder proposal */
+  SILC_LOG_DEBUG(("Waiting for reponder proposal"));
+  silc_fsm_next(ske, silc_ske_st_initiator_phase1);
+  return SILC_FSM_WAIT;
+}
+
+/* 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;
+  }
+
+  /* Decode the payload */
+  status = silc_ske_payload_start_decode(ske, packet_buf, &payload);
+  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;
+  }
+
+  /* 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;
+  }
+
+  /* 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;
+  }
+
+  /* Free our KE Start Payload context, we don't need it anymore. */
+  silc_ske_payload_start_free(ske->start_payload);
+  ske->start_payload = NULL;
+
+  /* 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;
+
+  prop->group = group;
+
+  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(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;
+  }
+
+  /* Save remote's KE Start Payload */
+  ske->start_payload = payload;
+
+  /** Send KE Payload */
+  silc_fsm_next(fsm, silc_ske_st_initiator_phase2);
+  return SILC_FSM_CONTINUE;
+
+ err:
+  if (payload)
+    silc_ske_payload_start_free(payload);
+
+  silc_ske_group_free(group);
+
+  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 (status == SILC_SKE_STATUS_OK)
+    status = SILC_SKE_STATUS_ERROR;
+
+  /** Error */
+  ske->status = status;
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
+  return SILC_FSM_CONTINUE;
+}
+
+/* Phase-2.  Send KE payload */
+
+SILC_FSM_STATE(silc_ske_st_initiator_phase2)
+{
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcBuffer payload_buf;
+  SilcMPInt *x;
+  SilcSKEKEPayload payload;
+  SilcUInt32 pk_len;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* 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;
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
 
   /* Encode the result to Key Exchange 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;
-    return ske->status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
   ske->ke1_payload = payload;
 
@@ -378,27 +979,31 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   /* 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, 
+  silc_mp_pow_mod(&payload->x, &ske->prop->group->generator, x,
                  &ske->prop->group->group);
 
   /* Get public key */
-  if (public_key) {
-    payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
+  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->status = SILC_SKE_STATUS_OK;
-      return ske->status;
+      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 = pk_type;
+  payload->pk_type = ske->pk_type;
 
   /* Compute signature data if we are doing mutual authentication */
-  if (private_key && ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) {
-    unsigned char hash[32], sign[2048];
+  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"));
@@ -409,96 +1014,167 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
     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, private_key->prv, 
-                                  private_key->prv_len);
+    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;
-      return ske->status;
+      silc_fsm_next(fsm, silc_ske_st_initiator_error);
+      return SILC_FSM_CONTINUE;
     }
-    payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
-    memcpy(payload->sign_data, sign, sign_len);
-    memset(sign, 0, sizeof(sign));
+    payload->sign_data = silc_memdup(sign, sign_len);
     payload->sign_len = sign_len;
+    memset(sign, 0, sizeof(sign));
   }
 
   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;
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_initiator_error);
+    return SILC_FSM_CONTINUE;
   }
 
   ske->x = x;
 
   /* Send the packet. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf, 
-                                  SILC_PACKET_KEY_EXCHANGE_1, 
-                                  ske->callbacks->context);
+  /* XXX */
 
   silc_buffer_free(payload_buf);
 
-  return status;
+  /** 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 */
+  }
+
+  /** 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;
 }
 
-/* An initiator finish final callback that is called to indicate that
-   the SKE protocol may continue. */
+/* Process key material */
 
-static void silc_ske_initiator_finish_final(SilcSKE ske,
-                                           SilcSKEStatus status,
-                                           void *context)
+SILC_FSM_STATE(silc_ske_st_initiator_phase4)
 {
-  SilcSKEKEPayload *payload;
-  unsigned char hash[32];
+  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;
 
-  /* If the SKE was freed during the async call then free it really now,
-     otherwise just decrement the reference counter. */
-  if (ske->status == SILC_SKE_STATUS_FREED) {
-    silc_ske_free(ske);
-    return;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
   }
 
-  /* If the caller returns PENDING status SKE library will assume that
-     the caller will re-call this callback when it is not anymore in
-     PENDING status. */
-  if (status == SILC_SKE_STATUS_PENDING)
-    return;
+  /* 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;
+  }
 
-  ske->users--;
   payload = ske->ke2_payload;
 
-  /* If the status is an error then the public key that was verified
-     by the caller is not authentic. */
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    if (ske->callbacks->proto_continue)
-      ske->callbacks->proto_continue(ske, ske->callbacks->context);
-    return;
-  }
-
   if (payload->pk_data) {
     /* Decode the public key */
-    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
+    if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len,
                                     &public_key)) {
-      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
       SILC_LOG_ERROR(("Unsupported/malformed public key received"));
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
+      status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
+      goto err;
     }
 
     SILC_LOG_DEBUG(("Public key is authentic"));
@@ -508,15 +1184,14 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
     if (status != SILC_SKE_STATUS_OK)
       goto err;
 
-    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-    memcpy(ske->hash, hash, hash_len);
+    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, payload->sign_data, 
+    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;
@@ -524,18 +1199,31 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
     }
 
     SILC_LOG_DEBUG(("Signature is Ok"));
-    
+
     silc_pkcs_public_key_free(public_key);
     memset(hash, 'F', hash_len);
   }
 
   ske->status = SILC_SKE_STATUS_OK;
 
-  /* Call the callback. The caller may now continue the SKE protocol. */
-  if (ske->callbacks->proto_continue)
-    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+  /* 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;
+  }
+
+  /* Send SUCCESS packet */
+  /* XXX */
 
-  return;
+  /** Waiting completion */
+  silc_fsm_next(fsm, silc_ske_st_initiator_end);
+  return SILC_FSM_WAIT;
 
  err:
   memset(hash, 'F', sizeof(hash));
@@ -556,1220 +1244,896 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
   }
 
   if (status == SILC_SKE_STATUS_OK)
-    ske->status = SILC_SKE_STATUS_ERROR;
+    status = SILC_SKE_STATUS_ERROR;
 
+  /** Error */
   ske->status = status;
-
-  /* Call the callback. */
-  if (ske->callbacks->proto_continue)
-    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+  silc_fsm_next(fsm, silc_ske_st_initiator_error);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Receives Key Exchange Payload from responder consisting responders
-   public key, f, and signature. This function verifies the public key,
-   computes the secret shared key and verifies the signature. 
-
-   The `proto_continue' will be called to indicate that the caller may
-   continue with the SKE protocol.  The caller must not continue
-   before the SKE libary has called that callback.  If this function
-   returns an error the callback will not be called.  It is called
-   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
-   However, note that when the library calls the callback the ske->status
-   may be error.
-
-   This calls the `verify_key' callback to verify the received public
-   key or certificate. If the `verify_key' is provided then the remote
-   must send public key and it is considered to be an error if remote 
-   does not send its public key. If caller is performing a re-key with
-   SKE then the `verify_key' is usually not provided when it is not also
-   required for the remote to send its public key. */
-
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke_payload)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEKEPayload *payload;
-  SilcMPInt *KEY;
-
-  SILC_LOG_DEBUG(("Start"));
+/* Protocol completed */
 
-  /* Decode the payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    return status;
-  }
-  ske->ke2_payload = payload;
+SILC_FSM_STATE(silc_ske_st_initiator_end)
+{
+  SilcSKE ske = fsm_context;
 
-  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 (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_initiator_aborted);
+    return SILC_FSM_CONTINUE;
   }
 
-  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;
+  /* Call the completion callback */
+  if (ske->callbacks->completed)
+    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL, NULL);
 
-  if (payload->pk_data && ske->callbacks->verify_key) {
-    SILC_LOG_DEBUG(("Verifying public key"));
-    
-    ske->users++;
-    (*ske->callbacks->verify_key)(ske, payload->pk_data, payload->pk_len,
-                                payload->pk_type, ske->callbacks->context,
-                                silc_ske_initiator_finish_final, NULL);
-    
-    /* We will continue to the final state after the public key has
-       been verified by the caller. */
-    return SILC_SKE_STATUS_PENDING;
-  }
+  return SILC_FSM_FINISH;
+}
 
-  /* Continue to final state */
-  ske->users++;
-  silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
+/* Aborted by application */
 
-  return SILC_SKE_STATUS_OK;
+SILC_FSM_STATE(silc_ske_st_initiator_aborted)
+{
 
- err:
-  silc_ske_payload_ke_free(payload);
-  ske->ke2_payload = NULL;
+  return SILC_FSM_FINISH;
+}
 
-  silc_mp_uninit(ske->KEY);
-  silc_free(ske->KEY);
-  ske->KEY = NULL;
+/* Error occurred */
 
-  if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+SILC_FSM_STATE(silc_ske_st_initiator_error)
+{
 
-  ske->status = status;
-  return status;
+  return SILC_FSM_FINISH;
 }
 
-/* Starts Key Exchange protocol for responder. Responder receives
-   Key Exchange Start Payload from initiator consisting of all the
-   security properties the initiator supports. This function decodes
-   the payload and parses the payload further and selects the right 
-   security properties. */
-
-SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
-                                      SilcSocketConnection sock,
-                                      const char *version,
-                                      SilcBuffer start_payload,
-                                      SilcSKESecurityPropertyFlag flags)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
-
-  SILC_LOG_DEBUG(("Start"));
 
-  ske->sock = sock;
-  ske->rng = rng;
+static void silc_ske_initiator_finished(SilcFSM fsm, void *fsm_context,
+                                       void *destructor_context)
+{
 
-  /* Decode the payload */
-  status = silc_ske_payload_start_decode(ske, start_payload, &remote_payload);
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    return status;
-  }
+}
 
-  /* 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(start_payload);
+/* Starts the protocol as initiator */
 
-  /* Force the mutual authentication flag if we want to do it. */
-  if (flags & SILC_SKE_SP_FLAG_MUTUAL) {
-    SILC_LOG_DEBUG(("Force mutual authentication"));
-    remote_payload->flags |= SILC_SKE_SP_FLAG_MUTUAL;
-  }
+SilcAsyncOperation
+silc_ske_initiator(SilcSKE ske,
+                  SilcPacketStream stream,
+                  SilcSKEStartPayload start_payload)
+{
+  SILC_LOG_DEBUG(("Start SKE as initiator"));
 
-  /* Force PFS flag if we require it */
-  if (flags & SILC_SKE_SP_FLAG_PFS) {
-    SILC_LOG_DEBUG(("Force PFS"));
-    remote_payload->flags |= SILC_SKE_SP_FLAG_PFS;
-  }
+  if (!ske || !stream || !start_payload)
+    return NULL;
 
-  /* Disable IV Included flag if requested */
-  if (remote_payload->flags & SILC_SKE_SP_FLAG_IV_INCLUDED &&
-      !(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;
-  }
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
-  /* Parse and select the security properties from the payload */
-  payload = silc_calloc(1, sizeof(*payload));
-  status = silc_ske_select_security_properties(ske, version,
-                                              payload, remote_payload);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_initiator_finished, ske,
+                    ske->schedule))
+    return NULL;
 
-  ske->start_payload = payload;
+  ske->start_payload = start_payload;
 
-  /* Call the callback function. */
-  if (ske->callbacks->payload_receive)
-    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
+  /* 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);
 
-  silc_ske_payload_start_free(remote_payload);
+  /* Start SKE as initiator */
+  silc_fsm_start(&ske->fsm, silc_ske_st_initiator_start);
 
-  return status;
+  return &ske->op;
+}
 
- err:
-  if (remote_payload)
-    silc_ske_payload_start_free(remote_payload);
-  if (payload)
-    silc_free(payload);
 
-  if (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+/******************************** Responder *********************************/
 
-  ske->status = status;
-  return status;
-}
+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);
 
-/* The selected security properties from the initiator payload is now 
-   encoded into Key Exchange Start Payload and sent to the initiator. */
+/* Start protocol as responder.  Wait initiator's start payload */
 
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_start)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcBuffer payload_buf;
-  SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group = NULL;
+  SilcSKE ske = fsm_context;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Allocate security properties from the payload. These are allocated
-     only for this negotiation and will be free'd after KE is over. */
-  ske->prop = prop = silc_calloc(1, sizeof(*prop));
-  prop->flags = ske->start_payload->flags;
-  status = silc_ske_group_get_by_name(ske->start_payload->ke_grp_list, &group);
-  if (status != SILC_SKE_STATUS_OK)
-    goto err;
-
-  prop->group = group;
-
-  if (silc_pkcs_alloc(ske->start_payload->pkcs_alg_list, 
-                     &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;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
   }
 
-  /* 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. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, 
-                                  ske->callbacks->context);
-
-  silc_buffer_free(payload_buf);
-
-  return status;
-
- err:
-  if (group)
-    silc_ske_group_free(group);
-
-  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 (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+  /* Start timeout */
+  /* XXX */
 
-  ske->status = status;
-  return status;
+  /** Wait for initiator */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase1);
+  return SILC_FSM_WAIT;
 }
 
-/* An responder phase 2 final callback that is called to indicate that
-   the SKE protocol may continue. */
+/* Decode initiator's start payload */
 
-static void silc_ske_responder_phase2_final(SilcSKE ske,
-                                           SilcSKEStatus status,
-                                           void *context)
+SILC_FSM_STATE(silc_ske_st_responder_phase1)
 {
-  SilcSKEKEPayload *recv_payload, *send_payload;
-  SilcMPInt *x;
-
-  /* If the SKE was freed during the async call then free it really now,
-     otherwise just decrement the reference counter. */
-  if (ske->status == SILC_SKE_STATUS_FREED) {
-    silc_ske_free(ske);
-    return;
-  }
-
-  /* If the caller returns PENDING status SKE library will assume that
-     the caller will re-call this callback when it is not anymore in
-     PENDING status. */
-  if (status == SILC_SKE_STATUS_PENDING)
-    return;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEStartPayload remote_payload = NULL, payload = NULL;
+  SilcBuffer packet_buf = &ske->packet->buffer;
 
-  ske->users--;
-  recv_payload = ske->ke1_payload;
+  SILC_LOG_DEBUG(("Start"));
 
-  /* If the status is an error then the public key that was verified
-     by the caller is not authentic. */
-  if (status != SILC_SKE_STATUS_OK) {
-    ske->status = status;
-    if (ske->callbacks->proto_continue)
-      ske->callbacks->proto_continue(ske, ske->callbacks->context);
-    return;
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
   }
 
-  /* 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[32];
-    SilcUInt32 hash_len;
-
-    /* Decode the public key */
-    if (!silc_pkcs_public_key_decode(recv_payload->pk_data, 
-                                    recv_payload->pk_len, 
-                                    &public_key)) {
-      ske->status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-      SILC_LOG_ERROR(("Unsupported/malformed public key received"));
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
-    }
-
-    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) {
-      ske->status = status;
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
-    }
-
-    SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
-    
-    /* 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) {
-      SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
-      ske->status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-      if (ske->callbacks->proto_continue)
-       ske->callbacks->proto_continue(ske, ske->callbacks->context);
-      return;
-    }
-    
-    SILC_LOG_DEBUG(("Signature is Ok"));
-    
-    silc_pkcs_public_key_free(public_key);
-    memset(hash, 'F', hash_len);
+  /* 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;
   }
 
-  /* 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);
+  /* Decode the payload */
+  status = silc_ske_payload_start_decode(ske, packet_buf, &remote_payload);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_uninit(x);
-    silc_free(x);
+    /** Error decoding Start Payload */
+    silc_packet_free(ske->packet);
     ske->status = status;
-    if (ske->callbacks->proto_continue)
-      ske->callbacks->proto_continue(ske, ske->callbacks->context);
-    return;
+    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;
+  /* Take a copy of the payload buffer for future use. It is used to
+     compute the HASH value. */
+  ske->start_payload_copy = silc_buffer_copy(packet_buf);
 
-  SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
+  silc_packet_free(ske->packet);
 
-  /* 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);
-  
-  /* Call the callback. The caller may now continue with the SKE protocol. */
-  ske->status = SILC_SKE_STATUS_OK;
-  if (ske->callbacks->proto_continue)
-    ske->callbacks->proto_continue(ske, ske->callbacks->context);
-}
+  /* 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;
+  }
 
-/* This function receives the Key Exchange Payload from the initiator.
-   This also performs the mutual authentication if required. Then, this 
-   function first generated a random number x, such that 1 < x < q
-   and computes f = g ^ x mod p. This then puts the result f to a Key
-   Exchange Payload. 
-
-   The `proto_continue' will be called to indicate that the caller may
-   continue with the SKE protocol.  The caller must not continue
-   before the SKE libary has called that callback.  If this function
-   returns an error the callback will not be called.  It is called
-   if this function return SILC_SKE_STATUS_OK or SILC_SKE_STATUS_PENDING.
-   However, note that when the library calls the callback the ske->status
-   may be error.
-
-   This calls the `verify_key' callback to verify the received public
-   key or certificate if the Mutual Authentication flag is set. If the
-   `verify_key' is provided then the remote must send public key and it
-   is considered to be an error if remote does not send its public key. */
-
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke_payload)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEKEPayload *recv_payload;
+  /* 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;
+  }
 
-  SILC_LOG_DEBUG(("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;
+  }
 
-  /* Decode Key Exchange Payload */
-  status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
+  /* 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;
-    return status;
+    silc_fsm_next(fsm, silc_ske_st_responder_error);
+    return SILC_FSM_CONTINUE;
   }
 
-  ske->ke1_payload = recv_payload;
-
-  /* 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) {
-      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;
-      return status;
-    }
-
-    if (recv_payload->pk_data && ske->callbacks->verify_key) {
-      SILC_LOG_DEBUG(("Verifying public key"));
-
-      ske->users++;
-      (*ske->callbacks->verify_key)(ske, recv_payload->pk_data, 
-                                   recv_payload->pk_len,
-                                   recv_payload->pk_type, 
-                                   ske->callbacks->context,
-                                   silc_ske_responder_phase2_final, NULL);
-
-      /* We will continue to the final state after the public key has
-        been verified by the caller. */
-      return SILC_SKE_STATUS_PENDING;
-    }
-  }
+  ske->start_payload = payload;
 
-  /* Continue to final state */
-  ske->users++;
-  silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
+  silc_ske_payload_start_free(remote_payload);
 
-  return SILC_SKE_STATUS_OK;
+  /** Send proposal to initiator */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase2);
+  return SILC_FSM_CONTINUE;
 }
 
-/* This functions generates the secret key KEY = e ^ x mod p, and, a hash
-   value to be signed and sent to the other end. This then encodes Key
-   Exchange Payload and sends it to the other end. */
+/* Phase-2.  Send Start Payload */
 
-SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
-                                       SilcPublicKey public_key,
-                                       SilcPrivateKey private_key,
-                                       SilcSKEPKType pk_type)
+SILC_FSM_STATE(silc_ske_st_responder_phase2)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
   SilcBuffer payload_buf;
-  SilcMPInt *KEY;
-  unsigned char hash[32], sign[2048], *pk;
-  SilcUInt32 hash_len, sign_len, pk_len;
+  SilcSKESecurityProperties prop;
+  SilcSKEDiffieHellmanGroup group = NULL;
 
   SILC_LOG_DEBUG(("Start"));
 
-  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;
+  /* 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 (public_key && private_key) {
-    SILC_LOG_DEBUG(("Getting public key"));
-    
-    /* Get the public key */
-    pk = silc_pkcs_public_key_encode(public_key, &pk_len);
-    if (!pk) {
-      status = SILC_SKE_STATUS_OUT_OF_MEMORY;
-      goto err;
-    }
-    ske->ke2_payload->pk_data = pk;
-    ske->ke2_payload->pk_len = pk_len;
-    
-    SILC_LOG_DEBUG(("Computing HASH value"));
-    
-    /* Compute the hash value */
-    memset(hash, 0, sizeof(hash));
-    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
-    if (status != SILC_SKE_STATUS_OK)
-      goto err;
+  prop->group = group;
 
-    ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-    memcpy(ske->hash, hash, hash_len);
-    ske->hash_len = hash_len;
-    
-    SILC_LOG_DEBUG(("Signing HASH value"));
-    
-    /* Sign the hash value */
-    silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv, 
-                                  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)) {
-      status = SILC_SKE_STATUS_SIGNATURE_ERROR;
-      goto err;
-    }
-    ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
-    memcpy(ske->ke2_payload->sign_data, sign, sign_len);
-    memset(sign, 0, sizeof(sign));
-    ske->ke2_payload->sign_len = sign_len;
+  /* 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;
   }
-  ske->ke2_payload->pk_type = pk_type;
 
-  /* Encode the Key Exchange Payload */
-  status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
-                                     &payload_buf);
+  /* 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. */
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, payload_buf, 
-                                  SILC_PACKET_KEY_EXCHANGE_2,
-                                  ske->callbacks->context);
+  if (!silc_packet_send(ske->stream, SILC_PACKET_KEY_EXCHANGE, 0,
+                       payload_buf->data, silc_buffer_len(payload_buf)))
+    goto err;
 
   silc_buffer_free(payload_buf);
 
-  return status;
+  /** Waiting initiator's KE payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase3);
+  return SILC_FSM_WAIT;
 
  err:
-  silc_mp_uninit(ske->KEY);
-  silc_free(ske->KEY);
-  ske->KEY = NULL;
-  silc_ske_payload_ke_free(ske->ke2_payload);
+  if (group)
+    silc_ske_group_free(group);
+
+  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 (status == SILC_SKE_STATUS_OK)
-    return SILC_SKE_STATUS_ERROR;
+    status = SILC_SKE_STATUS_ERROR;
 
+  /** Error */
   ske->status = status;
-  return status;
+  silc_fsm_next(fsm, silc_ske_st_responder_error);
+  return SILC_FSM_CONTINUE;
 }
 
-/* The Key Exchange protocol is ended by calling this function. This
-   must not be called until the keys are processed like the protocol
-   defines. This function is for both initiator and responder. */
+/* Phase-3.  Decode initiator's KE payload */
 
-SilcSKEStatus silc_ske_end(SilcSKE ske)
+SILC_FSM_STATE(silc_ske_st_responder_phase3)
 {
-  SilcBuffer packet;
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload recv_payload;
+  SilcBuffer packet_buf = &ske->packet->buffer;
 
   SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc_size(4);
-  if (!packet)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
-  silc_buffer_format(packet,
-                    SILC_STR_UI_INT((SilcUInt32)SILC_SKE_STATUS_OK),
-                    SILC_STR_END);
-
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_SUCCESS, 
-                                  ske->callbacks->context);
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
-  silc_buffer_free(packet);
+  /* 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;
+  }
 
-  return SILC_SKE_STATUS_OK;
-}
+  /* 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;
+  }
 
-/* Aborts the Key Exchange protocol. This is called if error occurs
-   while performing the protocol. The status argument is the error
-   status and it is sent to the remote end. */
+  ske->ke1_payload = recv_payload;
 
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
-{
-  SilcBuffer packet;
+  silc_packet_free(ske->packet);
 
-  SILC_LOG_DEBUG(("Start"));
+  /* 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) {
 
-  if (status > SILC_SKE_STATUS_INVALID_COOKIE)
-    status = SILC_SKE_STATUS_BAD_PAYLOAD;
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
 
-  packet = silc_buffer_alloc_size(4);
-  if (!packet)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
-  silc_buffer_format(packet,
-                    SILC_STR_UI_INT((SilcUInt32)status),
-                    SILC_STR_END);
+    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;
+    }
 
-  if (ske->callbacks->send_packet)
-    (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_FAILURE, 
-                                  ske->callbacks->context);
+    if (recv_payload->pk_data && ske->callbacks->verify_key) {
+      SILC_LOG_DEBUG(("Verifying public key"));
 
-  silc_buffer_free(packet);
+      /** 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 */
+    }
+  }
 
-  return SILC_SKE_STATUS_OK;
+  /** Generate KE2 payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase4);
+  return SILC_FSM_CONTINUE;
 }
 
-/* Assembles security properties to Key Exchange Start Payload to be
-   sent to the remote end. This checks system wide (SILC system, that is)
-   settings and chooses from those. However, if other properties
-   should be used this function is easy to replace by another function,
-   as, this function is called by the caller of the protocol and not
-   by the protocol itself. */
+/* Phase-4. Generate KE2 payload */
 
-SilcSKEStatus 
-silc_ske_assemble_security_properties(SilcSKE ske,
-                                     SilcSKESecurityPropertyFlag flags,
-                                     const char *version,
-                                     SilcSKEStartPayload **return_payload)
+SILC_FSM_STATE(silc_ske_st_responder_phase4)
 {
-  SilcSKEStartPayload *rp;
-  int i;
-
-  SILC_LOG_DEBUG(("Assembling KE Start Payload"));
-
-  rp = silc_calloc(1, sizeof(*rp));
-
-  /* Set flags */
-  rp->flags = (unsigned char)flags;
-
-  /* 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(ske->rng);
-  rp->cookie_len = SILC_SKE_COOKIE_LEN;
-
-  /* Put version */
-  rp->version = strdup(version);
-  rp->version_len = strlen(version);
-
-  /* Get supported Key Exhange groups */
-  rp->ke_grp_list = silc_ske_get_supported_groups();
-  rp->ke_grp_len = strlen(rp->ke_grp_list);
-
-  /* Get supported PKCS algorithms */
-  rp->pkcs_alg_list = silc_pkcs_get_supported();
-  rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
-
-  /* Get supported encryption algorithms */
-  rp->enc_alg_list = silc_cipher_get_supported();
-  rp->enc_alg_len = strlen(rp->enc_alg_list);
+  SilcSKE ske = fsm_context;
+  SilcSKEStatus status;
+  SilcSKEKEPayload recv_payload, send_payload;
+  SilcMPInt *x, *KEY;
 
-  /* Get supported hash algorithms */
-  rp->hash_alg_list = silc_hash_get_supported();
-  rp->hash_alg_len = strlen(rp->hash_alg_list);
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
-  /* Get supported HMACs */
-  rp->hmac_alg_list = silc_hmac_get_supported();
-  rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+  /* 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;
+  }
 
-  /* XXX */
-  /* Get supported compression algorithms */
-  rp->comp_alg_list = strdup("");
-  rp->comp_alg_len = 0;
+  recv_payload = ske->ke1_payload;
 
-  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;
+  /* 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;
 
-  *return_payload = rp;
+    /* 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;
+    }
 
-  return SILC_SKE_STATUS_OK;
-}
+    SILC_LOG_DEBUG(("Public key is authentic"));
 
-/* Selects the supported security properties from the remote end's Key 
-   Exchange Start Payload. */
+    /* 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;
+    }
 
-SilcSKEStatus 
-silc_ske_select_security_properties(SilcSKE ske,
-                                   const char *version,
-                                   SilcSKEStartPayload *payload,
-                                   SilcSKEStartPayload *remote_payload)
-{
-  SilcSKEStatus status;
-  SilcSKEStartPayload *rp;
-  char *cp;
-  int len;
+    SILC_LOG_DEBUG(("Verifying signature (HASH_i)"));
 
-  SILC_LOG_DEBUG(("Parsing KE Start Payload"));
+    /* 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;
+    }
 
-  rp = remote_payload;
+    SILC_LOG_DEBUG(("Signature is Ok"));
 
-  /* Check version string */
-  if (ske->callbacks->check_version) {
-    status = ske->callbacks->check_version(ske, rp->version, 
-                                          rp->version_len,
-                                          ske->callbacks->context);
-    if (status != SILC_SKE_STATUS_OK) {
-      ske->status = status;
-      return status;
-    }
+    silc_pkcs_public_key_free(public_key);
+    memset(hash, 'F', hash_len);
   }
 
-  ske->remote_version = silc_memdup(rp->version, rp->version_len);
-
-  /* Flags are returned unchanged. */
-  payload->flags = rp->flags;
+  /* 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;
+  }
 
-  /* 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);
+  /* Save the results for later processing */
+  send_payload = silc_calloc(1, sizeof(*send_payload));
+  ske->x = x;
+  ske->ke2_payload = send_payload;
 
-  /* Put our version to our reply */
-  payload->version = strdup(version);
-  payload->version_len = strlen(version);
+  SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
 
-  /* Get supported Key Exchange groups */
-  cp = rp->ke_grp_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *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);
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
 
-      SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
+  /* 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 (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
-       SILC_LOG_DEBUG(("Found KE group `%s'", item));
+  /** Send KE2 payload */
+  silc_fsm_next(fsm, silc_ske_st_responder_phase5);
+  return SILC_FSM_CONTINUE;
+}
 
-       payload->ke_grp_len = len;
-       payload->ke_grp_list = item;
-       break;
-      }
+/* Phase-5.  Send KE2 payload */
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+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;
 
-      if (item)
-       silc_free(item);
-    }
+  SILC_LOG_DEBUG(("Start"));
 
-    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 (ske->public_key && ske->private_key) {
+    SILC_LOG_DEBUG(("Getting public key"));
 
-    if (!rp->ke_grp_len) {
-      SILC_LOG_DEBUG(("KE group not defined in payload"));
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
+    /* 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));
-
-    payload->ke_grp_len = rp->ke_grp_len;
-    payload->ke_grp_list = strdup(rp->ke_grp_list);
-  }
+    SILC_LOG_DEBUG(("Computing HASH value"));
 
-  /* Get supported PKCS algorithms */
-  cp = rp->pkcs_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+    /* 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;
+    }
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+    ske->hash = silc_memdup(hash, hash_len);
+    ske->hash_len = hash_len;
 
-      SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
+    SILC_LOG_DEBUG(("Signing HASH value"));
 
-      if (silc_pkcs_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found 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;
 
-       payload->pkcs_alg_len = len;
-       payload->pkcs_alg_list = item;
-       break;
-      }
+  /* 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;
+  }
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+  /* 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;
+  }
 
-      if (item)
-       silc_free(item);
-    }
+  silc_buffer_free(payload_buf);
 
-    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 {
+  /** Waiting completion */
+  silc_fsm_next(fsm, silc_ske_st_responder_end);
+  return SILC_FSM_WAIT;
+}
 
-    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;
-    }
+/* Protocol completed */
 
-    SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
-    SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
+SILC_FSM_STATE(silc_ske_st_responder_end)
+{
+  SilcSKE ske = fsm_context;
+  unsigned char tmp[4];
+  SilcUInt32 hash_len, key_len, block_len;
+
+  if (ske->aborted) {
+    /** Aborted */
+    silc_fsm_next(fsm, silc_ske_st_responder_aborted);
+    return SILC_FSM_CONTINUE;
+  }
 
-    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);
+
+  /* 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;
   }
 
-  /* Get supported encryption algorithms */
-  cp = rp->enc_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+  /* Send SUCCESS packet */
+  SILC_PUT32_MSB(SILC_SKE_STATUS_OK, tmp);
+  silc_packet_send(ske->stream, SILC_PACKET_SUCCESS, 0, tmp, 4);
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
-      SILC_LOG_DEBUG(("Proposed 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);
 
-      if (silc_cipher_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
+  return SILC_FSM_FINISH;
+}
 
-       payload->enc_alg_len = len;
-       payload->enc_alg_list = item;
-       break;
-      }
+/* Aborted by application */
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+SILC_FSM_STATE(silc_ske_st_responder_aborted)
+{
+  SilcSKE ske = fsm_context;
+  unsigned char tmp[4];
 
-      if (item)
-       silc_free(item);
-    }
+  SILC_LOG_DEBUG(("Key exchange protocol aborted"));
 
-    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 {
+  /* Send FAILURE packet */
+  SILC_PUT32_MSB(SILC_SKE_STATUS_ERROR, tmp);
+  silc_packet_send(ske->stream, SILC_PACKET_FAILURE, 0, tmp, 4);
 
-    if (!rp->enc_alg_len) {
-      SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload);
-      return SILC_SKE_STATUS_BAD_PAYLOAD;
-    }
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
-    SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
-                   rp->enc_alg_list));
+  return SILC_FSM_FINISH;
+}
 
-    payload->enc_alg_len = rp->enc_alg_len;
-    payload->enc_alg_list = strdup(rp->enc_alg_list);
-  }
+/* Failure received from remote */
 
-  /* Get supported hash algorithms */
-  cp = rp->hash_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+SILC_FSM_STATE(silc_ske_st_responder_failure)
+{
+  SilcSKE ske = fsm_context;
+  SilcUInt32 error = SILC_SKE_STATUS_ERROR;
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+  SILC_LOG_DEBUG(("Key exchange protocol failed"));
 
-      SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+  if (silc_buffer_len(&ske->packet->buffer) == 4)
+    SILC_GET32_MSB(error, ske->packet->buffer.data);
+  ske->status = error;
 
-      if (silc_hash_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+  /* Call the completion callback */
+  if (ske->callbacks->completed)
+    ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
+                             ske->callbacks->context);
 
-       payload->hash_alg_len = len;
-       payload->hash_alg_list = item;
-       break;
-      }
+  silc_packet_free(ske->packet);
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+  return SILC_FSM_FINISH;
+}
 
-      if (item)
-       silc_free(item);
-    }
+/* Error occurred */
 
-    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 {
+SILC_FSM_STATE(silc_ske_st_responder_error)
+{
+  SilcSKE ske = fsm_context;
+  unsigned char tmp[4];
 
-    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(("Error %d (%s) during key exchange protocol",
+                 ske->status, silc_ske_map_status(ske->status)));
 
-    SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
-                   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);
 
-    payload->hash_alg_len = rp->hash_alg_len;
-    payload->hash_alg_list = strdup(rp->hash_alg_list);
-  }
+  silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
 
-  /* Get supported HMACs */
-  cp = rp->hmac_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+  return SILC_FSM_FINISH;
+}
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
 
-      SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
+static void silc_ske_responder_finished(SilcFSM fsm, void *fsm_context,
+                                       void *destructor_context)
+{
 
-      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;
-      }
+/* Starts the protocol as responder. */
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+SilcAsyncOperation
+silc_ske_responder(SilcSKE ske,
+                  SilcPacketStream stream,
+                  const char *version,
+                  SilcSKESecurityPropertyFlag flags)
+{
+  SILC_LOG_DEBUG(("Start SKE as responder"));
+
+  if (!ske || !stream || !version) {
+    return NULL;
+  }
+
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
-      if (item)
-       silc_free(item);
-    }
+  if (!silc_fsm_init(&ske->fsm, ske, silc_ske_responder_finished, ske,
+                    ske->schedule))
+    return NULL;
 
-    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 {
+  ske->flags = flags;
+  ske->version = strdup(version);
+  if (!ske->version)
+    return NULL;
+  ske->responder = TRUE;
 
-    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;
-    }
+  /* 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);
 
-    SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
-                   rp->hmac_alg_list));
+  /* Start SKE as responder */
+  silc_fsm_start(&ske->fsm, silc_ske_st_responder_start);
 
-    payload->hmac_alg_len = rp->hmac_alg_len;
-    payload->hmac_alg_list = strdup(rp->hmac_alg_list);
-  }
+  return &ske->op;
+}
 
-#if 0
-  /* Get supported compression algorithms */
-  cp = rp->hash_alg_list;
-  if (cp && strchr(cp, ',')) {
-    while(cp) {
-      char *item;
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start);
 
-      len = strcspn(cp, ",");
-      item = silc_calloc(len + 1, sizeof(char));
-      memcpy(item, cp, len);
+SILC_FSM_STATE(silc_ske_st_rekey_initiator_start)
+{
+  return SILC_FSM_FINISH;
+}
 
-      SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+/* Starts rekey protocol as initiator */
 
-      if (silc_hash_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+SilcAsyncOperation
+silc_ske_rekey_initiator(SilcSKE ske,
+                        SilcPacketStream stream,
+                        SilcSKERekeyMaterial rekey)
+{
+  SILC_LOG_DEBUG(("Start SKE rekey as initator"));
 
-       payload->hash_alg_len = len;
-       payload->hash_alg_list = item;
-       break;
-      }
+  if (!ske || !stream || !rekey)
+    return NULL;
 
-      cp += len;
-      if (strlen(cp) == 0)
-       cp = NULL;
-      else
-       cp++;
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
-      if (item)
-       silc_free(item);
-    }
+  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+    return NULL;
 
-    if (!payload->hash_alg_len && !payload->hash_alg_list) {
-      SILC_LOG_DEBUG(("Could not find supported hash alg"));
-      silc_ske_abort(ske, SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION);
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload->enc_alg_list);
-      silc_free(payload);
-      return;
-    }
-  } else {
+  ske->rekey = rekey;
 
-  }
-#endif
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
 
-  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;
+  /* Start SKE rekey as initiator */
+  silc_fsm_start(&ske->fsm, silc_ske_st_rekey_initiator_start);
 
-  return SILC_SKE_STATUS_OK;
+  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;
+  return SILC_FSM_FINISH;
+}
 
-  SILC_LOG_DEBUG(("Creating random number"));
+/* Starts rekey protocol as responder */
 
-  /* Get the random number as string */
-  string = silc_rng_get_rn_data(ske->rng, ((len - 1) / 8));
-  if (!string)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+SilcAsyncOperation
+silc_ske_rekey_responder(SilcSKE ske,
+                        SilcPacketStream stream,
+                        SilcBuffer ke_payload,
+                        SilcSKERekeyMaterial rekey)
+{
+  SILC_LOG_DEBUG(("Start SKE rekey as responder"));
 
-  /* Decode the string into a MP integer */
-  silc_mp_bin2mp(string, ((len - 1) / 8), rnd);
-  silc_mp_mod_2exp(rnd, rnd, len);
+  if (!ske || !stream || !rekey)
+    return NULL;
+  if (rekey->pfs && !ke_payload)
+    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;
+  if (!silc_async_init(&ske->op, silc_ske_abort, NULL, ske))
+    return NULL;
 
-  memset(string, 'F', (len / 8));
-  silc_free(string);
+  if (!silc_fsm_init(&ske->fsm, ske, NULL, NULL, ske->schedule))
+    return NULL;
 
-  return status;
+  //  ske->packet_buf = ke_payload;
+  ske->rekey = rekey;
+
+  /* Link to packet stream to get key exchange packets */
+  ske->stream = stream;
+
+  /* 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);
-    
-    /* Format the buffer used to compute the hash value */
-    buf = silc_buffer_alloc_size(ske->start_payload_copy->len + 
-                                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;
+  rp = silc_calloc(1, sizeof(*rp));
 
-    /* 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,
-                                               ske->start_payload_copy->
-                                               len),
-                          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,
-                                               ske->start_payload_copy->
-                                               len),
-                          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 flags */
+  rp->flags = (unsigned char)flags;
 
-    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);
+  /* 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;
 
-    buf = silc_buffer_alloc_size(ske->start_payload_copy->len + 
-                                 ske->ke1_payload->pk_len + e_len);
-    if (!buf)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
-    
-    /* Format the buffer used to compute the hash value */
-    ret = 
-      silc_buffer_format(buf,
-                        SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
-                                             ske->start_payload_copy->len),
-                        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;
-    }
+  /* Put version */
+  rp->version = strdup(version);
+  rp->version_len = strlen(version);
 
-    memset(e, 0, e_len);
-    silc_free(e);
-  }
+  /* Get supported Key Exhange groups */
+  rp->ke_grp_list = silc_ske_get_supported_groups();
+  rp->ke_grp_len = strlen(rp->ke_grp_list);
 
-  /* Make the hash */
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
-  *return_hash_len = silc_hash_len(ske->prop->hash);
+  /* Get supported PKCS algorithms */
+  rp->pkcs_alg_list = silc_pkcs_get_supported();
+  rp->pkcs_alg_len = strlen(rp->pkcs_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 encryption algorithms */
+  rp->enc_alg_list = silc_cipher_get_supported();
+  rp->enc_alg_len = strlen(rp->enc_alg_list);
 
-  silc_buffer_free(buf);
+  /* Get supported hash algorithms */
+  rp->hash_alg_list = silc_hash_get_supported();
+  rp->hash_alg_len = strlen(rp->hash_alg_list);
 
-  return status;
+  /* Get supported HMACs */
+  rp->hmac_alg_list = silc_hmac_get_supported();
+  rp->hmac_alg_len = strlen(rp->hmac_alg_list);
+
+  /* XXX */
+  /* Get supported compression algorithms */
+  rp->comp_alg_list = strdup("none");
+  rp->comp_alg_len = strlen("none");
+
+  rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN +
+    2 + rp->version_len +
+    2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len +
+    2 + rp->enc_alg_len + 2 + rp->hash_alg_len +
+    2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
+
+  return rp;
 }
 
-/* Processes the provided key material `data' as the SILC protocol 
+/* Processes the provided key material `data' as the SILC protocol
    specification defines. */
 
-SilcSKEStatus 
+SilcSKEKeyMaterial
 silc_ske_process_key_material_data(unsigned char *data,
                                   SilcUInt32 data_len,
                                   SilcUInt32 req_iv_len,
                                   SilcUInt32 req_enc_key_len,
                                   SilcUInt32 req_hmac_key_len,
-                                  SilcHash hash,
-                                  SilcSKEKeyMaterial *key)
+                                  SilcHash hash)
 {
   SilcBuffer buf;
-  unsigned char hashd[32];
+  unsigned char hashd[SILC_HASH_MAXLEN];
   SilcUInt32 hash_len = req_hmac_key_len;
   SilcUInt32 enc_key_len = req_enc_key_len / 8;
+  SilcSKEKeyMaterial key;
 
   SILC_LOG_DEBUG(("Start"));
 
   if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
-    return SILC_SKE_STATUS_ERROR;
+    return NULL;
+
+  key = silc_calloc(1, sizeof(*key));
+  if (!key)
+    return NULL;
 
   buf = silc_buffer_alloc_size(1 + data_len);
   if (!buf)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return NULL;
   silc_buffer_format(buf,
                     SILC_STR_UI_CHAR(0),
                     SILC_STR_UI_XNSTRING(data, data_len),
@@ -1778,12 +2142,12 @@ silc_ske_process_key_material_data(unsigned char *data,
   /* Take IVs */
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 0;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
   memcpy(key->send_iv, hashd, req_iv_len);
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 1;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
   memcpy(key->receive_iv, hashd, req_iv_len);
   key->iv_len = req_iv_len;
@@ -1794,27 +2158,28 @@ silc_ske_process_key_material_data(unsigned char *data,
   buf->data[0] = 2;
   if (enc_key_len > hash_len) {
     SilcBuffer dist;
-    unsigned char k1[32], k2[32], k3[32];
+    unsigned char k1[SILC_HASH_MAXLEN], k2[SILC_HASH_MAXLEN],
+       k3[SILC_HASH_MAXLEN];
     unsigned char *dtmp;
-    
+
     /* XXX */
     if (enc_key_len > (3 * hash_len))
-      return SILC_SKE_STATUS_ERROR;
-    
+      return NULL;
+
     /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(hash, buf->data, buf->len, k1);
-    
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
+
     /* Take second round */
     dist = silc_buffer_alloc_size(data_len + hash_len);
     if (!dist)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
+      return NULL;
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
                       SILC_STR_END);
     memset(k2, 0, sizeof(k2));
-    silc_hash_make(hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
 
     /* Take third round */
     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
@@ -1825,7 +2190,7 @@ silc_ske_process_key_material_data(unsigned char *data,
                       SILC_STR_END);
     silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(hash, dist->data, dist->len, k3);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
 
     /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
@@ -1847,7 +2212,7 @@ silc_ske_process_key_material_data(unsigned char *data,
   } else {
     /* Take normal hash as key */
     memset(hashd, 0, sizeof(hashd));
-    silc_hash_make(hash, buf->data, buf->len, hashd);
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->send_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
@@ -1856,28 +2221,29 @@ silc_ske_process_key_material_data(unsigned char *data,
   buf->data[0] = 3;
   if (enc_key_len > hash_len) {
     SilcBuffer dist;
-    unsigned char k1[32], k2[32], k3[32];
+    unsigned char k1[SILC_HASH_MAXLEN], k2[SILC_HASH_MAXLEN],
+       k3[SILC_HASH_MAXLEN];
     unsigned char *dtmp;
-    
+
     /* XXX */
     if (enc_key_len > (3 * hash_len))
-      return SILC_SKE_STATUS_ERROR;
-    
+      return NULL;
+
     /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(hash, buf->data, buf->len, k1);
-    
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), k1);
+
     /* Take second round */
     dist = silc_buffer_alloc_size(data_len + hash_len);
     if (!dist)
-      return SILC_SKE_STATUS_OUT_OF_MEMORY;
+      return NULL;
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
                       SILC_STR_END);
     memset(k2, 0, sizeof(k2));
-    silc_hash_make(hash, dist->data, dist->len, k2);
-    
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k2);
+
     /* Take third round */
     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
     silc_buffer_pull_tail(dist, hash_len);
@@ -1887,7 +2253,7 @@ silc_ske_process_key_material_data(unsigned char *data,
                       SILC_STR_END);
     silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(hash, dist->data, dist->len, k3);
+    silc_hash_make(hash, dist->data, silc_buffer_len(dist), k3);
 
     /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
@@ -1909,7 +2275,7 @@ silc_ske_process_key_material_data(unsigned char *data,
   } else {
     /* Take normal hash as key */
     memset(hashd, 0, sizeof(hashd));
-    silc_hash_make(hash, buf->data, buf->len, hashd);
+    silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->receive_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
@@ -1918,12 +2284,12 @@ silc_ske_process_key_material_data(unsigned char *data,
   /* Take HMAC keys */
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 4;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->send_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
   memcpy(key->send_hmac_key, hashd, req_hmac_key_len);
   memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 5;
-  silc_hash_make(hash, buf->data, buf->len, hashd);
+  silc_hash_make(hash, buf->data, silc_buffer_len(buf), hashd);
   key->receive_hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
   memcpy(key->receive_hmac_key, hashd, req_hmac_key_len);
   key->hmac_key_len = req_hmac_key_len;
@@ -1932,51 +2298,51 @@ silc_ske_process_key_material_data(unsigned char *data,
   silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
-  return SILC_SKE_STATUS_OK;
+  return key;
 }
 
 /* Processes negotiated key material as protocol specifies. This returns
    the actual keys to be used in the SILC. */
 
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
-                                           SilcUInt32 req_iv_len,
-                                           SilcUInt32 req_enc_key_len,
-                                           SilcUInt32 req_hmac_key_len,
-                                           SilcSKEKeyMaterial *key)
+SilcSKEKeyMaterial
+silc_ske_process_key_material(SilcSKE ske,
+                             SilcUInt32 req_iv_len,
+                             SilcUInt32 req_enc_key_len,
+                             SilcUInt32 req_hmac_key_len)
 {
-  SilcSKEStatus status;
   SilcBuffer buf;
   unsigned char *tmpbuf;
   SilcUInt32 klen;
+  SilcSKEKeyMaterial key;
 
   /* Encode KEY to binary data */
   tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
 
   buf = silc_buffer_alloc_size(klen + ske->hash_len);
   if (!buf)
-    return SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return NULL;
   silc_buffer_format(buf,
                     SILC_STR_UI_XNSTRING(tmpbuf, klen),
                     SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
                     SILC_STR_END);
 
   /* Process the key material */
-  status = silc_ske_process_key_material_data(buf->data, buf->len,
-                                             req_iv_len, req_enc_key_len,
-                                             req_hmac_key_len, 
-                                             ske->prop->hash, key);
+  key = silc_ske_process_key_material_data(buf->data, silc_buffer_len(buf),
+                                          req_iv_len, req_enc_key_len,
+                                          req_hmac_key_len,
+                                          ske->prop->hash);
 
   memset(tmpbuf, 0, klen);
   silc_free(tmpbuf);
   silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
-  return status;
+  return key;
 }
 
 /* Free key material structure */
 
-void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+void silc_ske_free_key_material(SilcSKEKeyMaterial key)
 {
   if (!key)
     return;
@@ -2004,7 +2370,82 @@ void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
   silc_free(key);
 }
 
-const char *silc_ske_status_string[] = 
+/* 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 */
   "Ok",
@@ -2021,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",
@@ -2048,16 +2487,16 @@ const char *silc_ske_map_status(SilcSKEStatus status)
 
 /* Parses remote host's version string. */
 
-bool silc_ske_parse_version(SilcSKE ske, 
-                           SilcUInt32 *protocol_version,
-                           char **protocol_version_string,
-                           SilcUInt32 *software_version, 
-                           char **software_version_string,
-                           char **vendor_version)
+SilcBool silc_ske_parse_version(SilcSKE ske,
+                               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, 
-                                  protocol_version_string, 
+                                  protocol_version,
+                                  protocol_version_string,
                                   software_version,
                                   software_version_string,
                                   vendor_version);