updates.
[silc.git] / lib / silcske / silcske.c
index 8d36c09653dcc1e5d58e4776bd37786ebc8fdba2..234d88d826aa8e9ce6d3cd55c82335e6a8bf84d0 100644 (file)
   GNU General Public License for more details.
 
 */
-/*
- * $Id$
- * $Log$
- * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
- *     Importet from internal CVS/Added Log headers.
- *
- *
- */
+/* $Id$ */
 
 #include "silcincludes.h"
 #include "payload_internal.h"
@@ -39,10 +32,7 @@ SilcSKE silc_ske_alloc()
   SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
 
   ske = silc_calloc(1, sizeof(*ske));
-  if (!ske) {
-    SILC_LOG_ERROR(("Could not allocate new SKE object"));
-    return NULL;
-  }
+  ske->status = SILC_SKE_STATUS_OK;
 
   return ske;
 }
@@ -82,8 +72,14 @@ void silc_ske_free(SilcSKE ske)
       silc_buffer_free(ske->start_payload_copy);
     if (ske->pk)
       silc_free(ske->pk);
-    silc_mp_clear(&ske->x);
-    silc_mp_clear(&ske->KEY);
+    if (ske->x) {
+      silc_mp_clear(ske->x);
+      silc_free(ske->x);
+    }
+    if (ske->KEY) {
+      silc_mp_clear(ske->KEY);
+      silc_free(ske->KEY);
+    }
     if (ske->hash)
       silc_free(ske->hash);
     silc_free(ske);
@@ -153,8 +149,10 @@ 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;
     return status;
+  }
 
   /* Take the selected security properties into use while doing
      the key exchange. This is used only while doing the key 
@@ -209,6 +207,7 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -217,24 +216,29 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
    Key Exchange 1 Payload. */
 
 SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
+                                        SilcPublicKey public_key,
                                         SilcSKESendPacketCb send_packet,
                                         void *context)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt x, e;
+  SilcInt *x, e;
   SilcSKEOnePayload *payload;
+  unsigned int pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* 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_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       &x);
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
+    silc_mp_clear(x);
+    silc_free(x);
+    ske->status = status;
     return status;
   }
 
@@ -242,17 +246,31 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   /* Do the Diffie Hellman computation, e = g ^ x mod p */
   silc_mp_init(&e);
-  silc_mp_powm(&e, &ske->prop->group->generator, &x, 
+  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);
+  if (!payload->pk_data) {
+    silc_mp_clear(x);
+    silc_free(x);
+    silc_mp_clear(&e);
+    silc_free(payload);
+    ske->status = SILC_SKE_STATUS_OK;
+    return ske->status;
+  }
+  payload->pk_len = pk_len;
+  payload->pk_type = SILC_SKE_PK_TYPE_SILC;
   status = silc_ske_payload_one_encode(ske, payload, &payload_buf);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
+    silc_mp_clear(x);
+    silc_free(x);
     silc_mp_clear(&e);
+    silc_free(payload->pk_data);
     silc_free(payload);
+    ske->status = status;
     return status;
   }
 
@@ -274,12 +292,15 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
 SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
                                        SilcBuffer ke2_payload,
+                                       SilcSKEVerifyCb verify_key,
+                                       void *verify_context,
                                        SilcSKECb callback,
                                        void *context)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcSKETwoPayload *payload;
-  SilcInt KEY;
+  SilcPublicKey public_key = NULL;
+  SilcInt *KEY;
   unsigned char hash[32];
   unsigned int hash_len;
 
@@ -287,25 +308,35 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
 
   /* Decode the payload */
   status = silc_ske_payload_two_decode(ske, ke2_payload, &payload);
-  if (status != SILC_SKE_STATUS_OK)
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
     return status;
+  }
   ske->ke2_payload = payload;
 
   SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
 
   /* Compute the shared secret key */
-  silc_mp_init(&KEY);
-  silc_mp_powm(&KEY, &payload->f, &ske->x, &ske->prop->group->group);
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_powm(KEY, &payload->f, ske->x, &ske->prop->group->group);
   ske->KEY = KEY;
 
   SILC_LOG_DEBUG(("Verifying public key"));
 
-  /* Verify the public key */ /* XXX */
-  status = silc_ske_verify_public_key(ske, payload->pk_data, 
-                                     payload->pk_len);
-  if (status != SILC_SKE_STATUS_OK)
+  if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
+                                  &public_key)) {
+    status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
     goto err;
-  
+  }
+
+  if (verify_key) {
+    status = (*verify_key)(ske, payload->pk_data, payload->pk_len,
+                          payload->pk_type, verify_context);
+    if (status != SILC_SKE_STATUS_OK)
+      goto err;
+  }  
+
   SILC_LOG_DEBUG(("Public key is authentic"));
 
   /* Compute the hash value */
@@ -320,10 +351,10 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
   SILC_LOG_DEBUG(("Verifying signature"));
 
   /* Verify signature */
-  silc_pkcs_set_public_key(ske->prop->pkcs, payload->pk_data, payload->pk_len);
-  if (ske->prop->pkcs->pkcs->verify(ske->prop->pkcs->context,
-                                   payload->sign_data, payload->sign_len,
-                                   hash, hash_len) == FALSE) {
+  silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk, 
+                               public_key->pk_len);
+  if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, 
+                      payload->sign_len, hash, hash_len) == FALSE) {
 
     SILC_LOG_DEBUG(("Signature don't match"));
 
@@ -333,6 +364,7 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
 
   SILC_LOG_DEBUG(("Signature is Ok"));
 
+  silc_pkcs_public_key_free(public_key);
   memset(hash, 'F', hash_len);
 
   /* Call the callback. */
@@ -342,10 +374,16 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
   return status;
 
  err:
-  memset(hash, 'F', hash_len);
+  memset(hash, 'F', sizeof(hash));
   silc_ske_payload_two_free(payload);
+  ske->ke2_payload = NULL;
+
+  silc_mp_clear(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
 
-  silc_mp_clear(&ske->KEY);
+  if (public_key)
+    silc_pkcs_public_key_free(public_key);
 
   if (ske->hash) {
     memset(ske->hash, 'F', hash_len);
@@ -356,6 +394,7 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -367,6 +406,7 @@ SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
 
 SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
                                       SilcSocketConnection sock,
+                                      char *version,
                                       SilcBuffer start_payload,
                                       SilcSKECb callback,
                                       void *context)
@@ -381,8 +421,10 @@ 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. */
@@ -390,7 +432,8 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
 
   /* 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);
+  status = silc_ske_select_security_properties(ske, version,
+                                              payload, remote_payload);
   if (status != SILC_SKE_STATUS_OK)
     goto err;
 
@@ -411,6 +454,7 @@ 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;
 }
 
@@ -425,7 +469,7 @@ 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"));
 
@@ -471,7 +515,8 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
   return status;
 
  err:
-  silc_free(group);
+  if (group)
+    silc_free(group);
 
   if (prop->pkcs)
     silc_pkcs_free(prop->pkcs);
@@ -485,6 +530,7 @@ SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske,
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -504,23 +550,27 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcSKEOnePayload *one_payload;
   SilcSKETwoPayload *two_payload;
-  SilcInt x, f;
+  SilcInt *x, f;
 
   SILC_LOG_DEBUG(("Start"));
 
   /* Decode Key Exchange 1 Payload */
   status = silc_ske_payload_one_decode(ske, ke1_payload, &one_payload);
-  if (status != SILC_SKE_STATUS_OK)
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
     return status;
+  }
 
   /* 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_mp_sizeinbase(&ske->prop->group->group_order, 2),
-                       &x);
+                       x);
   if (status != SILC_SKE_STATUS_OK) {
-    silc_mp_clear(&x);
+    silc_mp_clear(x);
+    silc_free(x);
     return status;
   }
 
@@ -528,7 +578,7 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
 
   /* Do the Diffie Hellman computation, f = g ^ x mod p */
   silc_mp_init(&f);
-  silc_mp_powm(&f, &ske->prop->group->generator, &x, 
+  silc_mp_powm(&f, &ske->prop->group->generator, x, 
               &ske->prop->group->group);
   
   /* Save the results for later processing */
@@ -550,35 +600,43 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
    encodes Key Exchange 2 Payload and sends it to the other end. */
 
 SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
-                                       unsigned char *pk,
-                                       unsigned int pk_len,
-                                       unsigned char *prv,
-                                       unsigned int prv_len,
+                                       SilcPublicKey public_key,
+                                       SilcPrivateKey private_key,
                                        SilcSKEPKType pk_type,
                                        SilcSKESendPacketCb send_packet,
                                        void *context)
 {
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
-  SilcInt KEY;
-  unsigned char hash[32], sign[256];
-  unsigned int hash_len, sign_len;
+  SilcInt *KEY;
+  unsigned char hash[32], sign[256], *pk;
+  unsigned int hash_len, sign_len, pk_len;
 
   SILC_LOG_DEBUG(("Start"));
 
+  if (!public_key || !private_key) {
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
+
   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, 
+  KEY = silc_calloc(1, sizeof(*KEY));
+  silc_mp_init(KEY);
+  silc_mp_powm(KEY, &ske->ke1_payload->e, ske->x, 
               &ske->prop->group->group);
   ske->KEY = KEY;
 
   SILC_LOG_DEBUG(("Getting public key"));
 
   /* Get the public key */
-  ske->ke2_payload->pk_data = silc_calloc(pk_len, sizeof(unsigned char));
-  memcpy(ske->ke2_payload->pk_data, pk, pk_len);
+  pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+  if (!pk) {
+    status = SILC_SKE_STATUS_ERROR;
+    goto err;
+  }
+  ske->ke2_payload->pk_data = pk;
   ske->ke2_payload->pk_len = pk_len;
   ske->ke2_payload->pk_type = pk_type;
 
@@ -597,10 +655,9 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
   SILC_LOG_DEBUG(("Signing HASH value"));
 
   /* Sign the hash value */
-  silc_pkcs_set_private_key(ske->prop->pkcs, prv, prv_len);
-  ske->prop->pkcs->pkcs->sign(ske->prop->pkcs->context,
-                             hash, hash_len,
-                             sign, &sign_len);
+  silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv, 
+                                private_key->prv_len);
+  silc_pkcs_sign(ske->prop->pkcs, 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));
@@ -621,12 +678,15 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
   return status;
 
  err:
-  silc_mp_clear(&ske->KEY);
+  silc_mp_clear(ske->KEY);
+  silc_free(ske->KEY);
+  ske->KEY = NULL;
   silc_ske_payload_two_free(ske->ke2_payload);
 
   if (status == SILC_SKE_STATUS_OK)
     return SILC_SKE_STATUS_ERROR;
 
+  ske->status = status;
   return status;
 }
 
@@ -638,18 +698,22 @@ SilcSKEStatus silc_ske_end(SilcSKE ske,
                           SilcSKESendPacketCb send_packet,
                           void *context)
 {
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer packet;
 
   SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc(1);
-  packet->len = 0;
+  packet = silc_buffer_alloc(4);
+  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  silc_buffer_format(packet,
+                    SILC_STR_UI_SHORT(SILC_SKE_STATUS_OK),
+                    SILC_STR_END);
 
   if (send_packet)
     (*send_packet)(ske, packet, SILC_PACKET_SUCCESS, context);
 
-  return status;
+  silc_buffer_free(packet);
+
+  return SILC_SKE_STATUS_OK;
 }
 
 /* Aborts the Key Exchange protocol. This is called if error occurs
@@ -687,23 +751,29 @@ SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
 
 SilcSKEStatus 
 silc_ske_assemble_security_properties(SilcSKE ske,
+                                     unsigned char flags,
+                                     char *version,
                                      SilcSKEStartPayload **return_payload)
 {
   SilcSKEStartPayload *rp;
+  int i;
 
   SILC_LOG_DEBUG(("Assembling KE Start Payload"));
 
   rp = silc_calloc(1, sizeof(*rp));
 
-  /* XXX */
   /* Set flags */
-  rp->flags = 0;
+  rp->flags = flags;
 
-  /* XXX */
-  /* Cookie */
-  rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
+  /* 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;
-  memcpy(rp->cookie, "1234567890123456", 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();
@@ -727,6 +797,7 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   rp->comp_alg_len = 0;
 
   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;
@@ -741,9 +812,11 @@ silc_ske_assemble_security_properties(SilcSKE ske,
 
 SilcSKEStatus 
 silc_ske_select_security_properties(SilcSKE ske,
+                                   char *version,
                                    SilcSKEStartPayload *payload,
                                    SilcSKEStartPayload *remote_payload)
 {
+  SilcSKEStatus status;
   SilcSKEStartPayload *rp;
   char *cp;
   int len;
@@ -752,14 +825,25 @@ silc_ske_select_security_properties(SilcSKE ske,
 
   rp = remote_payload;
 
+  /* Check version string */
+  status = silc_ske_check_version(ske, rp->version, rp->version_len);
+  if (status != SILC_SKE_STATUS_OK) {
+    ske->status = status;
+    return status;
+  }
+
   /* Flags are returned unchanged. */
   payload->flags = rp->flags;
 
-  /* XXX Cookie check?? */
+  /* Take cookie */
   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);
 
+  /* Put our version to our reply */
+  payload->version = strdup(version);
+  payload->version_len = strlen(version);
+
   /* Get supported Key Exchange groups */
   cp = rp->ke_grp_list;
   if (cp && strchr(cp, ',')) {
@@ -1018,6 +1102,7 @@ silc_ske_select_security_properties(SilcSKE ske,
 #endif
 
   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;
@@ -1038,10 +1123,12 @@ SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n,
   SILC_LOG_DEBUG(("Creating random number"));
 
   /* Get the random number as string */
-  string = silc_rng_get_rn_string(ske->rng, (len / 8));
+  string = silc_rng_get_rn_data(ske->rng, (len / 8));
+  if (!string)
+    return SILC_SKE_STATUS_ERROR;
 
   /* Decode the string into a MP integer */
-  silc_mp_set_str(rnd, string, 16);
+  silc_mp_bin2mp(string, (len / 8), rnd);
   silc_mp_mod_2exp(rnd, rnd, len);
 
   /* Checks */
@@ -1057,17 +1144,6 @@ SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n,
   return status;
 }
 
-/* XXX TODO */
-
-SilcSKEStatus silc_ske_verify_public_key(SilcSKE ske, 
-                                        unsigned char *pubkey,
-                                        unsigned int pubkey_len)
-{
-  SilcSKEStatus status = SILC_SKE_STATUS_OK;
-
-  return status;
-}
-
 /* Creates a hash value HASH as defined in the SKE protocol. */
 
 SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
@@ -1078,38 +1154,37 @@ SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   SilcBuffer buf;
   unsigned char *e, *f, *KEY;
   unsigned int e_len, f_len, KEY_len;
+  int ret;
 
   SILC_LOG_DEBUG(("Start"));
 
-  e_len = silc_mp_sizeinbase(&ske->ke1_payload->e, 16);
-  e = silc_calloc(e_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(e, 16, &ske->ke1_payload->e);
-
-  f_len = silc_mp_sizeinbase(&ske->ke2_payload->f, 16);
-  f = silc_calloc(f_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(f, 16, &ske->ke2_payload->f);
-
-  KEY_len = silc_mp_sizeinbase(&ske->KEY, 16);
-  KEY = silc_calloc(KEY_len + 1, sizeof(unsigned char));
-  silc_mp_get_str(KEY, 16, &ske->KEY);
+  e = silc_mp_mp2bin(&ske->ke1_payload->e, 0, &e_len);
+  f = silc_mp_mp2bin(&ske->ke2_payload->f, 0, &f_len);
+  KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
 
   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));
 
   /* 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);
-
-#if 0
-  SILC_LOG_HEXDUMP(("Hash buffer"), buf->data, buf->len);
-#endif
+  ret = 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);
+  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;
+  }
 
   /* Make the hash */
   silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
@@ -1128,56 +1203,43 @@ SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   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 specifies. */
 
-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,
+                                  unsigned int data_len,
+                                  unsigned int req_iv_len,
+                                  unsigned int req_enc_key_len,
+                                  unsigned int req_hmac_key_len,
+                                  SilcHash hash,
+                                  SilcSKEKeyMaterial *key)
 {
-  int i, klen;
   SilcBuffer buf;
-  SilcInt tmp;
-  unsigned char *tmpbuf;
-  unsigned char hash[32];
-  unsigned int hash_len = ske->prop->hash->hash->hash_len;
+  unsigned char hashd[32];
+  unsigned int hash_len = req_hmac_key_len;
   unsigned int enc_key_len = req_enc_key_len / 8;
 
   SILC_LOG_DEBUG(("Start"));
 
-  silc_mp_init_set(&tmp, &ske->KEY);
-
-  klen = silc_mp_size(&tmp);
-
-  /* Format the KEY material into binary data */
-  tmpbuf = silc_calloc(klen, sizeof(unsigned char));
-  for (i = klen; i > 0; i--) {
-    tmpbuf[i - 1] = (unsigned char)(silc_mp_get_ui(&tmp) & 0xff);
-    silc_mp_fdiv_q_2exp(&tmp, &tmp, 8);
-  }
-
-  buf = silc_buffer_alloc(1 + klen + hash_len);
-
+  buf = silc_buffer_alloc(1 + data_len);
   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
   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
@@ -1193,30 +1255,31 @@ 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(data_len + hash_len);
+    silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
     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(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);
@@ -1234,10 +1297,10 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     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;
   }
 
@@ -1251,30 +1314,31 @@ 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(data_len + hash_len);
+    silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
     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(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);
@@ -1292,23 +1356,82 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
     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));
+  memset(hashd, 0, sizeof(hashd));
   buf->data[0] = 4;
-  silc_hash_make(ske->prop->hash, buf->data, buf->len, hash);
+  silc_hash_make(hash, buf->data, buf->len, hashd);
   key->hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
-  memcpy(key->hmac_key, hash, req_hmac_key_len);
+  memcpy(key->hmac_key, hashd, req_hmac_key_len);
   key->hmac_key_len = req_hmac_key_len;
 
+  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, 
+                                           unsigned int req_iv_len,
+                                           unsigned int req_enc_key_len,
+                                           unsigned int req_hmac_key_len,
+                                           SilcSKEKeyMaterial *key)
+{
+  SilcSKEStatus status;
+  SilcBuffer buf;
+  unsigned char *tmpbuf;
+  int klen;
+
+  /* Encode KEY to binary data */
+  tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
+
+  buf = silc_buffer_alloc(klen + ske->hash_len);
+  silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+  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_free(buf);
 
-  return SILC_SKE_STATUS_OK;
+  return status;
+}
+
+/* Free key material structure */
+
+void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
+{
+  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->hmac_key) {
+    memset(key->hmac_key, 0, key->hmac_key_len);
+    silc_free(key->hmac_key);
+  }
+  silc_free(key);
 }