Merged from silc_1_0_branch.
[silc.git] / lib / silcske / silcske.c
index 1ca94b0f815b26579b04c3fd74c5304c62c5874a..2fd4e0107ea646bea7f2fe87dabc7d29830a80a6 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
-  silcske.c
+  silcske.c 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2000 Pekka Riikonen
+  Copyright (C) 2000 - 2002 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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 /* $Id$ */
 
 #include "silcincludes.h"
-#include "payload_internal.h"
+#include "silcske.h"
 #include "groups_internal.h"
 
+/* Static functions */
+static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n, 
+                                        SilcUInt32 len, 
+                                        SilcMPInt *rnd);
+static SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
+                                       unsigned char *return_hash,
+                                       SilcUInt32 *return_hash_len,
+                                       int initiator);
+
+/* Structure to hold all SKE callbacks. */
+struct SilcSKECallbacksStruct {
+  SilcSKESendPacketCb send_packet;
+  SilcSKECb payload_receive;
+  SilcSKEVerifyCb verify_key;
+  SilcSKECb proto_continue;
+  SilcSKECheckVersion check_version;
+  void *context;
+};
+
 /* Allocates new SKE object. */
 
-SilcSKE silc_ske_alloc()
+SilcSKE silc_ske_alloc(SilcRng rng, void *context)
 {
   SilcSKE ske;
 
   SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
 
   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;
 
   return ske;
 }
@@ -40,6 +64,13 @@ SilcSKE silc_ske_alloc()
 
 void silc_ske_free(SilcSKE ske)
 {
+  ske->users--;
+  if (ske->users > 0) {
+    SILC_LOG_DEBUG(("Key Exchange set to FREED status"));
+    ske->status = SILC_SKE_STATUS_FREED;
+    return;
+  }
+
   SILC_LOG_DEBUG(("Freeing Key Exchange object"));
 
   if (ske) {
@@ -47,57 +78,115 @@ void silc_ske_free(SilcSKE ske)
     if (ske->start_payload)
       silc_ske_payload_start_free(ske->start_payload);
 
-    /* Free KE1 payload */
+    /* Free KE payload */
     if (ske->ke1_payload)
-      silc_ske_payload_one_free(ske->ke1_payload);
-
-    /* Free KE2 payload */
+      silc_ske_payload_ke_free(ske->ke1_payload);
     if (ske->ke2_payload)
-      silc_ske_payload_two_free(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_free(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->pk)
-      silc_free(ske->pk);
-    /* XXX
-    silc_mp_clear(&ske->x);
-    silc_mp_clear(&ske->KEY);
-    */
-    if (ske->hash)
-      silc_free(ske->hash);
+    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);
   }
 }
 
+/* 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. */
+
+void silc_ske_set_callbacks(SilcSKE ske,
+                           SilcSKESendPacketCb send_packet,
+                           SilcSKECb payload_receive,
+                           SilcSKEVerifyCb verify_key,
+                           SilcSKECb proto_continue,
+                           SilcSKECheckVersion check_version,
+                           void *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;
+}
+
 /* 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 Paload which includes all our
+   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.
+   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,
-                                      SilcSKESendPacketCb send_packet,
-                                      void *context)
+                                      SilcSKEStartPayload *start_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
@@ -115,10 +204,12 @@ SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
   /* 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;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
@@ -131,9 +222,7 @@ SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
    sent in the silc_ske_initiator_start function. */
 
 SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske, 
-                                        SilcBuffer start_payload,
-                                        SilcSKECb callback,
-                                        void *context)
+                                        SilcBuffer start_payload)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcSKEStartPayload *payload;
@@ -144,16 +233,45 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
 
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, start_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK)
+  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);
+    return status;
+  }
+
+  /* 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;
+    }
+  }
+
+  /* Free our KE Start Payload context, we don't need it anymore. */
+  silc_ske_payload_start_free(ske->start_payload);
 
   /* 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_get_group_by_name(payload->ke_grp_list, &group);
+  status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
@@ -174,11 +292,17 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     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;
 
   /* Return the received payload by calling the callback function. */
-  if (callback)
-    (*callback)(ske, context);
+  if (ske->callbacks->payload_receive)
+    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
 
   return status;
 
@@ -186,7 +310,7 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
   if (payload)
     silc_ske_payload_start_free(payload);
 
-  silc_free(group);
+  silc_ske_group_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -194,167 +318,239 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
     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;
 
+  ske->status = status;
   return status;
 }
 
 /* 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. */
+   Key Exchange Payload. */
 
 SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
                                         SilcPublicKey public_key,
-                                        SilcSKESendPacketCb send_packet,
-                                        void *context)
+                                        SilcPrivateKey private_key,
+                                        SilcSKEPKType pk_type)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt x, e;
-  SilcSKEOnePayload *payload;
-  unsigned int pk_len;
+  SilcMPInt *x;
+  SilcSKEKEPayload *payload;
+  SilcUInt32 pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Create the random number x, 1 < x < q. */
-  silc_mp_init(&x);
+  x = silc_calloc(1, sizeof(*x));
+  if (!x){
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return ske->status;
+  }
+  silc_mp_init(x);
   status = 
-    silc_ske_create_rnd(ske, ske->prop->group->group_order,
+    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       &x);
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = status;
     return status;
   }
 
+  /* Encode the result to Key Exchange Payload. */
+
+  payload = silc_calloc(1, sizeof(*payload));
+  if (!payload) {
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return ske->status;
+  }
+  ske->ke1_payload = payload;
+
   SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
 
   /* Do the Diffie Hellman computation, e = g ^ x mod p */
-  silc_mp_init(&e);
-  silc_mp_powm(&e, &ske->prop->group->generator, &x, 
-              &ske->prop->group->group);
-  
-  /* Encode the result to Key Exchange 1 Payload. */
-  payload = silc_calloc(1, sizeof(*payload));
-  payload->e = e;
-  payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
-  payload->pk_len = pk_len;
-  payload->pk_type = SILC_SKE_PK_TYPE_SILC;
-  status = silc_ske_payload_one_encode(ske, payload, &payload_buf);
+  silc_mp_init(&payload->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 (!payload->pk_data) {
+      silc_mp_uninit(x);
+      silc_free(x);
+      silc_mp_uninit(&payload->x);
+      silc_free(payload);
+      ske->ke1_payload = NULL;
+      ske->status = SILC_SKE_STATUS_OK;
+      return ske->status;
+    }
+    payload->pk_len = pk_len;
+  }
+  payload->pk_type = 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 + 1];
+    SilcUInt32 hash_len, sign_len;
+
+    SILC_LOG_DEBUG(("We are doing mutual authentication"));
+    SILC_LOG_DEBUG(("Computing HASH_i value"));
+
+    /* Compute the hash value */
+    memset(hash, 0, sizeof(hash));
+    silc_ske_make_hash(ske, hash, &hash_len, TRUE);
+
+    SILC_LOG_DEBUG(("Signing HASH_i value"));
+    
+    /* Sign the hash value */
+    silc_pkcs_private_key_data_set(ske->prop->pkcs, 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)) {
+      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;
+    }
+    payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
+    memcpy(payload->sign_data, sign, sign_len);
+    memset(sign, 0, sizeof(sign));
+    payload->sign_len = sign_len;
+  }
+
+  status = silc_ske_payload_ke_encode(ske, payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
-    silc_mp_clear(&e);
+    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;
   }
 
-  ske->ke1_payload = payload;
   ske->x = x;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_1, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, 
+                                  SILC_PACKET_KEY_EXCHANGE_1, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
   return status;
 }
 
-/* Receives Key Exchange 2 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. */
+/* An initiator finish final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
-                                       SilcBuffer ke2_payload,
-                                       SilcSKEVerifyCb verify_key,
-                                       void *verify_context,
-                                       SilcSKECb callback,
-                                       void *context)
+static void silc_ske_initiator_finish_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKETwoPayload *payload;
-  SilcPublicKey public_key = NULL;
-  SilcInt KEY;
+  SilcSKEKEPayload *payload;
   unsigned char hash[32];
-  unsigned int hash_len;
+  SilcUInt32 hash_len;
+  SilcPublicKey public_key = NULL;
 
-  SILC_LOG_DEBUG(("Start"));
+  /* 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;
+  }
 
-  /* Decode the payload */
-  status = silc_ske_payload_two_decode(ske, ke2_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK)
-    return status;
-  ske->ke2_payload = payload;
+  /* 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;
 
-  SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
+  ske->users--;
+  payload = ske->ke2_payload;
 
-  /* Compute the shared secret key */
-  silc_mp_init(&KEY);
-  silc_mp_powm(&KEY, &payload->f, &ske->x, &ske->prop->group->group);
-  ske->KEY = KEY;
+  /* 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;
+  }
 
-  SILC_LOG_DEBUG(("Verifying public key"));
+  if (payload->pk_data) {
+    /* Decode the public key */
+    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;
+    }
 
-  if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
-                                  &public_key)) {
-    status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
-    goto err;
-  }
+    SILC_LOG_DEBUG(("Public key is authentic"));
 
-  if (verify_key) {
-    status = (*verify_key)(ske, payload->pk_data, payload->pk_len,
-                          payload->pk_type, verify_context);
+    /* Compute the hash value */
+    status = silc_ske_make_hash(ske, hash, &hash_len, FALSE);
     if (status != SILC_SKE_STATUS_OK)
       goto err;
-  }  
 
-  SILC_LOG_DEBUG(("Public key is authentic"));
-
-  /* Compute the hash value */
-  status = silc_ske_make_hash(ske, hash, &hash_len);
-  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_len = hash_len;
 
-  ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
-  memcpy(ske->hash, hash, hash_len);
-  ske->hash_len = hash_len;
+    SILC_LOG_DEBUG(("Verifying signature (HASH)"));
 
-  SILC_LOG_DEBUG(("Verifying signature"));
-
-  /* Verify signature */
-  silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk, 
-                               public_key->pk_len);
-  if (ske->prop->pkcs->pkcs->verify(ske->prop->pkcs->context,
-                                   payload->sign_data, payload->sign_len,
-                                   hash, hash_len) == FALSE) {
-
-    SILC_LOG_DEBUG(("Signature don't match"));
+    /* Verify signature */
+    silc_pkcs_public_key_set(ske->prop->pkcs, public_key);
+    if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, 
+                        payload->sign_len, hash, hash_len) == FALSE) {
+      SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
+      status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
+      goto err;
+    }
 
-    status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
-    goto err;
+    SILC_LOG_DEBUG(("Signature is Ok"));
+    
+    silc_pkcs_public_key_free(public_key);
+    memset(hash, 'F', hash_len);
   }
 
-  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. */
-  if (callback)
-    (*callback)(ske, context);
+  /* Call the callback. The caller may now continue the SKE protocol. */
+  if (ske->callbacks->proto_continue)
+    ske->callbacks->proto_continue(ske, ske->callbacks->context);
 
-  return status;
+  return;
 
  err:
   memset(hash, 'F', sizeof(hash));
-  silc_ske_payload_two_free(payload);
+  silc_ske_payload_ke_free(payload);
   ske->ke2_payload = NULL;
 
-  silc_mp_clear(&ske->KEY);
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
 
   if (public_key)
     silc_pkcs_public_key_free(public_key);
@@ -365,9 +561,98 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
     ske->hash = NULL;
   }
 
+  if (status == SILC_SKE_STATUS_OK)
+    ske->status = SILC_SKE_STATUS_ERROR;
+
+  ske->status = status;
+
+  /* Call the callback. */
+  if (ske->callbacks->proto_continue)
+    ske->callbacks->proto_continue(ske, ske->callbacks->context);
+}
+
+/* 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"));
+
+  /* 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;
+
+  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"));
+    
+    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;
+  }
+
+  /* Continue to final state */
+  ske->users++;
+  silc_ske_initiator_finish_final(ske, SILC_SKE_STATUS_OK, NULL);
+
+  return SILC_SKE_STATUS_OK;
+
+ 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;
 
+  ske->status = status;
   return status;
 }
 
@@ -379,10 +664,9 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
 
 SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
                                       SilcSocketConnection sock,
-                                      char *version,
+                                      const char *version,
                                       SilcBuffer start_payload,
-                                      SilcSKECb callback,
-                                      void *context)
+                                      SilcSKESecurityPropertyFlag flags)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
@@ -394,13 +678,34 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
 
   /* Decode the payload */
   status = silc_ske_payload_start_decode(ske, start_payload, &remote_payload);
-  if (status != SILC_SKE_STATUS_OK)
+  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);
 
+  /* 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;
+  }
+
+  /* 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;
+  }
+
+  /* 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;
+  }
+
   /* Parse and select the security properties from the payload */
   payload = silc_calloc(1, sizeof(*payload));
   status = silc_ske_select_security_properties(ske, version,
@@ -411,8 +716,10 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
   ske->start_payload = payload;
 
   /* Call the callback function. */
-  if (callback)
-    (*callback)(ske, context);
+  if (ske->callbacks->payload_receive)
+    (*ske->callbacks->payload_receive)(ske, ske->callbacks->context);
+
+  silc_ske_payload_start_free(remote_payload);
 
   return status;
 
@@ -425,67 +732,74 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
 /* The selected security properties from the initiator payload is now 
    encoded into Key Exchange Start Payload and sent to the initiator. */
 
-SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske, 
-                                        SilcSKEStartPayload *start_payload,
-                                        SilcSKESendPacketCb send_packet,
-                                        void *context)
+SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
   SilcSKESecurityProperties prop;
-  SilcSKEDiffieHellmanGroup group;
+  SilcSKEDiffieHellmanGroup group = NULL;
 
   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 = start_payload->flags;
-  status = silc_ske_get_group_by_name(start_payload->ke_grp_list, &group);
+  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(start_payload->pkcs_alg_list, 
+  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(start_payload->enc_alg_list, 
+  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(start_payload->hash_alg_list,
+  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;
+  }
+
   /* Encode the payload */
-  status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
+  status = silc_ske_payload_start_encode(ske, ske->start_payload, 
+                                        &payload_buf);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, context);
+  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:
-  silc_free(group);
+  if (group)
+    silc_ske_group_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -493,153 +807,302 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
     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;
 
+  ske->status = status;
   return status;
 }
 
-/* This function receives the Key Exchange 1 Payload from the initiator.
-   After processing the payload this then selects 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 2 Payload which is later processed
-   in ske_responder_finish function. The callback function should
-   not touch the payload (it should merely call the ske_responder_finish
-   function). */
+/* An responder phase 2 final callback that is called to indicate that
+   the SKE protocol may continue. */
 
-SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
-                                        SilcBuffer ke1_payload,
-                                        SilcSKECb callback,
-                                        void *context)
+static void silc_ske_responder_phase2_final(SilcSKE ske,
+                                           SilcSKEStatus status,
+                                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-  SilcSKEOnePayload *one_payload;
-  SilcSKETwoPayload *two_payload;
-  SilcInt x, f;
+  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;
+  }
 
-  SILC_LOG_DEBUG(("Start"));
+  /* 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;
 
-  /* Decode Key Exchange 1 Payload */
-  status = silc_ske_payload_one_decode(ske, ke1_payload, &one_payload);
-  if (status != SILC_SKE_STATUS_OK)
-    return status;
+  ske->users--;
+  recv_payload = ske->ke1_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;
+  }
+
+  /* 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);
+  }
 
   /* Create the random number x, 1 < x < q. */
-  silc_mp_init(&x);
+  x = silc_calloc(1, sizeof(*x));
+  silc_mp_init(x);
   status = 
-    silc_ske_create_rnd(ske, ske->prop->group->group_order,
+    silc_ske_create_rnd(ske, &ske->prop->group->group_order,
                        silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       &x);
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
-    return status;
+    silc_mp_uninit(x);
+    silc_free(x);
+    ske->status = status;
+    if (ske->callbacks->proto_continue)
+      ske->callbacks->proto_continue(ske, ske->callbacks->context);
+    return;
   }
 
+  /* Save the results for later processing */
+  send_payload = silc_calloc(1, sizeof(*send_payload));
+  ske->x = x;
+  ske->ke2_payload = send_payload;
+
   SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
 
   /* Do the Diffie Hellman computation, f = g ^ x mod p */
-  silc_mp_init(&f);
-  silc_mp_powm(&f, &ske->prop->group->generator, &x, 
-              &ske->prop->group->group);
+  silc_mp_init(&send_payload->x);
+  silc_mp_pow_mod(&send_payload->x, &ske->prop->group->generator, x, 
+                 &ske->prop->group->group);
   
-  /* Save the results for later processing */
-  two_payload = silc_calloc(1, sizeof(*two_payload));
-  two_payload->f = f;
-  ske->x = x;
-  ske->ke1_payload = one_payload;
-  ske->ke2_payload = two_payload;
+  /* 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);
+}
 
-  /* Call the callback. */
-  if (callback)
-    (*callback)(ske, context);
+/* 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. */
 
-  return status;
+SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
+                                        SilcBuffer ke_payload)
+{
+  SilcSKEStatus status = SILC_SKE_STATUS_OK;
+  SilcSKEKEPayload *recv_payload;
+
+  SILC_LOG_DEBUG(("Start"));
+
+  /* Decode Key Exchange Payload */
+  status = silc_ske_payload_ke_decode(ske, ke_payload, &recv_payload);
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    return status;
+  }
+
+  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;
+    }
+  }
+
+  /* Continue to final state */
+  ske->users++;
+  silc_ske_responder_phase2_final(ske, SILC_SKE_STATUS_OK, NULL);
+
+  return SILC_SKE_STATUS_OK;
 }
 
-/* This function computes the secret shared key KEY = e ^ x mod p, and, 
-   a hash value to be signed and sent to the other end. This then
-   encodes Key Exchange 2 Payload and sends it to the other end. */
+/* 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. */
 
 SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
                                        SilcPublicKey public_key,
                                        SilcPrivateKey private_key,
-                                       SilcSKEPKType pk_type,
-                                       SilcSKESendPacketCb send_packet,
-                                       void *context)
+                                       SilcSKEPKType pk_type)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt KEY;
-  unsigned char hash[32], sign[256], *pk;
-  unsigned int hash_len, sign_len, pk_len;
+  SilcMPInt *KEY;
+  unsigned char hash[32], sign[2048 + 1], *pk;
+  SilcUInt32 hash_len, sign_len, pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
   SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
 
   /* Compute the shared secret key */
-  silc_mp_init(&KEY);
-  silc_mp_powm(&KEY, &ske->ke1_payload->e, &ske->x, 
-              &ske->prop->group->group);
+  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;
 
-  SILC_LOG_DEBUG(("Getting public key"));
+  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;
 
-  /* Get the public key */
-  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
-  ske->ke2_payload->pk_data = pk;
-  ske->ke2_payload->pk_len = pk_len;
+    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;
+  }
   ske->ke2_payload->pk_type = pk_type;
 
-  SILC_LOG_DEBUG(("Computing HASH value"));
-
-  /* Compute the hash value */
-  memset(hash, 0, sizeof(hash));
-  status = silc_ske_make_hash(ske, hash, &hash_len);
-  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_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);
-  ske->prop->pkcs->pkcs->sign(ske->prop->pkcs->context,
-                             hash, hash_len,
-                             sign, &sign_len);
-  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;
-
-  /* Encode the Key Exchange 2 Payload */
-  status = silc_ske_payload_two_encode(ske, ske->ke2_payload,
-                                      &payload_buf);
+  /* Encode the Key Exchange Payload */
+  status = silc_ske_payload_ke_encode(ske, ske->ke2_payload,
+                                     &payload_buf);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
   /* Send the packet. */
-  if (send_packet)
-    (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_2, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, payload_buf, 
+                                  SILC_PACKET_KEY_EXCHANGE_2,
+                                  ske->callbacks->context);
 
   silc_buffer_free(payload_buf);
 
   return status;
 
  err:
-  silc_mp_clear(&ske->KEY);
-  silc_ske_payload_two_free(ske->ke2_payload);
+  silc_mp_uninit(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
+  silc_ske_payload_ke_free(ske->ke2_payload);
 
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -647,44 +1110,51 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
    must not be called until the keys are processed like the protocol
    defines. This function is for both initiator and responder. */
 
-SilcSKEStatus silc_ske_end(SilcSKE ske,
-                          SilcSKESendPacketCb send_packet,
-                          void *context)
+SilcSKEStatus silc_ske_end(SilcSKE ske)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer packet;
 
   SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc(1);
-  packet->len = 0;
+  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 (send_packet)
-    (*send_packet)(ske, packet, SILC_PACKET_SUCCESS, context);
+  silc_buffer_free(packet);
 
-  return status;
+  return SILC_SKE_STATUS_OK;
 }
 
 /* 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. */
 
-SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
-                            SilcSKESendPacketCb send_packet,
-                            void *context)
+SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
 {
   SilcBuffer packet;
 
   SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc(4);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  if (status > SILC_SKE_STATUS_INVALID_COOKIE)
+    status = SILC_SKE_STATUS_BAD_PAYLOAD;
+
+  packet = silc_buffer_alloc_size(4);
+  if (!packet)
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
   silc_buffer_format(packet,
-                    SILC_STR_UI_SHORT(status),
+                    SILC_STR_UI_INT((SilcUInt32)status),
                     SILC_STR_END);
 
-  if (send_packet)
-    (*send_packet)(ske, packet, SILC_PACKET_FAILURE, context);
+  if (ske->callbacks->send_packet)
+    (*ske->callbacks->send_packet)(ske, packet, SILC_PACKET_FAILURE, 
+                                  ske->callbacks->context);
 
   silc_buffer_free(packet);
 
@@ -700,8 +1170,8 @@ SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
 
 SilcSKEStatus 
 silc_ske_assemble_security_properties(SilcSKE ske,
-                                     unsigned char flags,
-                                     char *version,
+                                     SilcSKESecurityPropertyFlag flags,
+                                     const char *version,
                                      SilcSKEStartPayload **return_payload)
 {
   SilcSKEStartPayload *rp;
@@ -712,12 +1182,12 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   rp = silc_calloc(1, sizeof(*rp));
 
   /* Set flags */
-  rp->flags = 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[i] = silc_rng_get_byte_fast(ske->rng);
   rp->cookie_len = SILC_SKE_COOKIE_LEN;
 
   /* Put version */
@@ -740,16 +1210,20 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   rp->hash_alg_list = silc_hash_get_supported();
   rp->hash_alg_len = strlen(rp->hash_alg_list);
 
+  /* 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 = "";
-  rp->comp_alg_len = 0;
+  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->comp_alg_len;
+    2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
 
   *return_payload = rp;
 
@@ -761,10 +1235,11 @@ silc_ske_assemble_security_properties(SilcSKE ske,
 
 SilcSKEStatus 
 silc_ske_select_security_properties(SilcSKE ske,
-                                   char *version,
+                                   const char *version,
                                    SilcSKEStartPayload *payload,
                                    SilcSKEStartPayload *remote_payload)
 {
+  SilcSKEStatus status;
   SilcSKEStartPayload *rp;
   char *cp;
   int len;
@@ -773,16 +1248,27 @@ silc_ske_select_security_properties(SilcSKE ske,
 
   rp = remote_payload;
 
+  /* 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;
+    }
+  }
+
+  ske->remote_version = silc_memdup(rp->version, rp->version_len);
+
   /* Flags are returned unchanged. */
   payload->flags = rp->flags;
 
-  /* Take cookie */
+  /* 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);
 
-  /* XXX Do version check */
-
   /* Put our version to our reply */
   payload->version = strdup(version);
   payload->version_len = strlen(version);
@@ -799,7 +1285,7 @@ silc_ske_select_security_properties(SilcSKE ske,
 
       SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
 
-      if (silc_ske_get_group_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
+      if (silc_ske_group_get_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
        SILC_LOG_DEBUG(("Found KE group `%s'", item));
 
        payload->ke_grp_len = len;
@@ -999,9 +1485,8 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->hash_alg_list = strdup(rp->hash_alg_list);
   }
 
-#if 0
-  /* Get supported compression algorithms */
-  cp = rp->hash_alg_list;
+  /* Get supported HMACs */
+  cp = rp->hmac_alg_list;
   if (cp && strchr(cp, ',')) {
     while(cp) {
       char *item;
@@ -1010,13 +1495,13 @@ silc_ske_select_security_properties(SilcSKE ske,
       item = silc_calloc(len + 1, sizeof(char));
       memcpy(item, cp, len);
 
-      SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
+      SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
 
-      if (silc_hash_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
+      if (silc_hmac_is_supported(item) == TRUE) {
+       SILC_LOG_DEBUG(("Found HMAC `%s'", item));
 
-       payload->hash_alg_len = len;
-       payload->hash_alg_list = item;
+       payload->hmac_alg_len = len;
+       payload->hmac_alg_list = item;
        break;
       }
 
@@ -1030,25 +1515,78 @@ silc_ske_select_security_properties(SilcSKE ske,
        silc_free(item);
     }
 
-    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);
+    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;
+      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));
+      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->comp_alg_len;
+    2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
 
   return SILC_SKE_STATUS_OK;
 }
@@ -1056,127 +1594,204 @@ silc_ske_select_security_properties(SilcSKE ske,
 /* Creates random number such that 1 < rnd < n and at most length
    of len bits. The rnd sent as argument must be initialized. */
 
-SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n, 
-                                 unsigned int len, 
-                                 SilcInt *rnd)
+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, (len / 8));
+  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, (len / 8), rnd);
+  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)
+  if (silc_mp_cmp(rnd, n) >= 0)
     status = SILC_SKE_STATUS_ERROR;
 
-  memset(string, 'F', (len / 8));
+  memset(string, 'F', l);
   silc_free(string);
 
   return status;
 }
 
-/* Creates a hash value HASH as defined in the SKE protocol. */
+/* 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. */
 
-SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
-                                unsigned char *return_hash,
-                                unsigned int *return_hash_len)
+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;
-  unsigned int e_len, f_len, KEY_len;
+  SilcUInt32 e_len, f_len, KEY_len;
+  int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
-  e = silc_mp_mp2bin(&ske->ke1_payload->e, &e_len);
-  f = silc_mp_mp2bin(&ske->ke2_payload->f, &f_len);
-  KEY = silc_mp_mp2bin(&ske->KEY, &KEY_len);
+  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;
+
+    /* 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;
+    }
 
-  buf = silc_buffer_alloc(ske->start_payload_copy->len + 
-                         ske->pk_len + e_len + f_len + KEY_len);
-  silc_buffer_pull_tail(buf, SILC_BUFFER_END(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);
+  } else {
+    e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
 
-  /* Format the buffer used to compute the hash value */
-  silc_buffer_format(buf,
-                    SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
-                                         ske->start_payload_copy->len),
-                    SILC_STR_UI_XNSTRING(ske->pk, ske->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);
+    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;
+    }
 
-#if 0
-  SILC_LOG_HEXDUMP(("Hash buffer"), buf->data, buf->len);
-#endif
+    memset(e, 0, e_len);
+    silc_free(e);
+  }
 
   /* Make the hash */
   silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
-  *return_hash_len = ske->prop->hash->hash->hash_len;
+  *return_hash_len = silc_hash_len(ske->prop->hash);
 
-  SILC_LOG_HEXDUMP(("Hash"), return_hash, *return_hash_len);
+  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);
-  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 status;
 }
 
-/* Processes negotiated key material as protocol specifies. This returns
-   the actual keys to be used in the SILC. */
+/* Processes the provided key material `data' as the SILC protocol 
+   specification defines. */
 
-SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
-                                           unsigned int req_iv_len,
-                                           unsigned int req_enc_key_len,
-                                           unsigned int req_hmac_key_len,
-                                           SilcSKEKeyMaterial *key)
+SilcSKEStatus 
+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)
 {
-  int klen;
   SilcBuffer buf;
-  unsigned char *tmpbuf;
-  unsigned char hash[32];
-  unsigned int hash_len = ske->prop->hash->hash->hash_len;
-  unsigned int enc_key_len = req_enc_key_len / 8;
+  unsigned char hashd[32];
+  SilcUInt32 hash_len = req_hmac_key_len;
+  SilcUInt32 enc_key_len = req_enc_key_len / 8;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Encode KEY to binary data */
-  tmpbuf = silc_mp_mp2bin(&ske->KEY, &klen);
+  if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
+    return SILC_SKE_STATUS_ERROR;
 
-  buf = silc_buffer_alloc(1 + klen + hash_len);
-  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  buf = silc_buffer_alloc_size(1 + data_len);
+  if (!buf)
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
   silc_buffer_format(buf,
                     SILC_STR_UI_CHAR(0),
-                    SILC_STR_UI_XNSTRING(tmpbuf, klen),
-                    SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
+                    SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_END);
 
   /* Take IVs */
-  memset(hash, 0, sizeof(hash));
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 0;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
-  memcpy(key->send_iv, hash, req_iv_len);
-  memset(hash, 0, sizeof(hash));
+  memcpy(key->send_iv, hashd, req_iv_len);
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 1;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
-  memcpy(key->receive_iv, hash, req_iv_len);
+  memcpy(key->receive_iv, hashd, req_iv_len);
   key->iv_len = req_iv_len;
 
   /* Take the encryption keys. If requested key size is more than
@@ -1192,34 +1807,37 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     if (enc_key_len > (3 * hash_len))
       return SILC_SKE_STATUS_ERROR;
     
+    /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, k1);
+    silc_hash_make(hash, buf->data, buf->len, k1);
     
-    /* XXX */
-    dist = silc_buffer_alloc(hash_len * 3);
-    
-    silc_buffer_pull_tail(dist, klen + hash_len);
+    /* Take second round */
+    dist = silc_buffer_alloc_size(data_len + hash_len);
+    if (!dist)
+      return SILC_SKE_STATUS_OUT_OF_MEMORY;
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                      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(ske->prop->hash, dist->data, dist->len, k2);
-    
-    silc_buffer_pull(dist, klen + hash_len);
+    silc_hash_make(hash, dist->data, dist->len, k2);
+
+    /* Take third round */
+    dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull_tail(dist, hash_len);
+    silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
                       SILC_STR_END);
-    silc_buffer_push(dist, klen + hash_len);
-    
+    silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k3);
-    
+    silc_hash_make(hash, dist->data, dist->len, k3);
+
+    /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
     memcpy(dtmp, k1, hash_len);
     memcpy(dtmp + hash_len, k2, hash_len);
-    memcpy(dtmp + hash_len, k3, hash_len);
+    memcpy(dtmp + hash_len + hash_len, k3, hash_len);
 
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->send_enc_key, dtmp, enc_key_len);
@@ -1230,13 +1848,14 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     memset(k2, 0, sizeof(k2));
     memset(k3, 0, sizeof(k3));
     silc_free(dtmp);
+    silc_buffer_clear(dist);
     silc_buffer_free(dist);
   } else {
     /* Take normal hash as key */
-    memset(hash, 0, sizeof(hash));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+    memset(hashd, 0, sizeof(hashd));
+    silc_hash_make(hash, buf->data, buf->len, hashd);
     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
-    memcpy(key->send_enc_key, hash, enc_key_len);
+    memcpy(key->send_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
   }
 
@@ -1250,34 +1869,37 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     if (enc_key_len > (3 * hash_len))
       return SILC_SKE_STATUS_ERROR;
     
+    /* Take first round */
     memset(k1, 0, sizeof(k1));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, k1);
-    
-    /* XXX */
-    dist = silc_buffer_alloc(hash_len * 3);
+    silc_hash_make(hash, buf->data, buf->len, k1);
     
-    silc_buffer_pull_tail(dist, klen + hash_len);
+    /* Take second round */
+    dist = silc_buffer_alloc_size(data_len + hash_len);
+    if (!dist)
+      return SILC_SKE_STATUS_OUT_OF_MEMORY;
     silc_buffer_format(dist,
-                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
+                      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(ske->prop->hash, dist->data, dist->len, k2);
+    silc_hash_make(hash, dist->data, dist->len, k2);
     
-    silc_buffer_pull(dist, klen + hash_len);
+    /* Take third round */
+    dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
+    silc_buffer_pull_tail(dist, hash_len);
+    silc_buffer_pull(dist, data_len + hash_len);
     silc_buffer_format(dist,
                       SILC_STR_UI_XNSTRING(k2, hash_len),
                       SILC_STR_END);
-    silc_buffer_push(dist, klen + hash_len);
-    
+    silc_buffer_push(dist, data_len + hash_len);
     memset(k3, 0, sizeof(k3));
-    silc_hash_make(ske->prop->hash, dist->data, dist->len, k3);
-    
+    silc_hash_make(hash, dist->data, dist->len, k3);
+
+    /* Then, save the keys */
     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
     memcpy(dtmp, k1, hash_len);
     memcpy(dtmp + hash_len, k2, hash_len);
-    memcpy(dtmp + hash_len, k3, hash_len);
+    memcpy(dtmp + hash_len + hash_len, k3, hash_len);
 
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
     memcpy(key->receive_enc_key, dtmp, enc_key_len);
@@ -1288,26 +1910,161 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     memset(k2, 0, sizeof(k2));
     memset(k3, 0, sizeof(k3));
     silc_free(dtmp);
+    silc_buffer_clear(dist);
     silc_buffer_free(dist);
   } else {
     /* Take normal hash as key */
-    memset(hash, 0, sizeof(hash));
-    silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+    memset(hashd, 0, sizeof(hashd));
+    silc_hash_make(hash, buf->data, buf->len, hashd);
     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
-    memcpy(key->receive_enc_key, hash, enc_key_len);
+    memcpy(key->receive_enc_key, hashd, enc_key_len);
     key->enc_key_len = req_enc_key_len;
   }
 
-  /* Take HMAC key */
-  memset(hash, 0, sizeof(hash));
+  /* Take HMAC keys */
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 4;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
-  key->hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
-  memcpy(key->hmac_key, hash, req_hmac_key_len);
+  silc_hash_make(hash, buf->data, buf->len, 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);
+  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;
+  memset(hashd, 0, sizeof(hashd));
+
+  silc_buffer_clear(buf);
+  silc_buffer_free(buf);
+
+  return SILC_SKE_STATUS_OK;
+}
+
+/* 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)
+{
+  SilcSKEStatus status;
+  SilcBuffer buf;
+  unsigned char *tmpbuf;
+  SilcUInt32 klen;
+
+  /* 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;
+  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);
 
   memset(tmpbuf, 0, klen);
   silc_free(tmpbuf);
+  silc_buffer_clear(buf);
+  silc_buffer_free(buf);
 
-  return SILC_SKE_STATUS_OK;
+  return status;
+}
+
+/* Free key material structure */
+
+void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+{
+  if (!key)
+    return;
+
+  if (key->send_iv)
+    silc_free(key->send_iv);
+  if (key->receive_iv)
+    silc_free(key->receive_iv);
+  if (key->send_enc_key) {
+    memset(key->send_enc_key, 0, key->enc_key_len / 8);
+    silc_free(key->send_enc_key);
+  }
+  if (key->receive_enc_key) {
+    memset(key->receive_enc_key, 0, key->enc_key_len / 8);
+    silc_free(key->receive_enc_key);
+  }
+  if (key->send_hmac_key) {
+    memset(key->send_hmac_key, 0, key->hmac_key_len);
+    silc_free(key->send_hmac_key);
+  }
+  if (key->receive_hmac_key) {
+    memset(key->receive_hmac_key, 0, key->hmac_key_len);
+    silc_free(key->receive_hmac_key);
+  }
+  silc_free(key);
+}
+
+const char *silc_ske_status_string[] = 
+{
+  /* Official */
+  "Ok",
+  "Unkown error occurred",
+  "Bad payload in packet",
+  "Unsupported group",
+  "Unsupported cipher",
+  "Unsupported PKCS",
+  "Unsupported hash function",
+  "Unsupported HMAC",
+  "Unsupported public key (or certificate)",
+  "Incorrect signature",
+  "Bad or unsupported version",
+  "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",
+  "System out of memory",
+
+  NULL
+};
+
+/* Maps status to readable string and returns the string. If string is not
+   found and empty character string ("") is returned. */
+
+const char *silc_ske_map_status(SilcSKEStatus status)
+{
+  int i;
+
+  for (i = 0; silc_ske_status_string[i]; i++)
+    if (status == i)
+      return silc_ske_status_string[i];
+
+  return "";
+}
+
+/* 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)
+{
+  return silc_parse_version_string(ske->remote_version,
+                                  protocol_version, 
+                                  protocol_version_string, 
+                                  software_version,
+                                  software_version_string,
+                                  vendor_version);
 }