Merged from silc_1_0_branch.
[silc.git] / lib / silcske / silcske.c
index 756d505d52f78f536c822ee02494d2839aa5c159..2fd4e0107ea646bea7f2fe87dabc7d29830a80a6 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2002 - 2002 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
@@ -50,6 +50,8 @@ SilcSKE silc_ske_alloc(SilcRng rng, void *context)
   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;
@@ -109,6 +111,8 @@ void silc_ske_free(SilcSKE ske)
     }
     silc_free(ske->hash);
     silc_free(ske->callbacks);
+
+    memset(ske, 'F', sizeof(*ske));
     silc_free(ske);
   }
 }
@@ -157,6 +161,8 @@ void silc_ske_set_callbacks(SilcSKE ske,
   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;
@@ -236,7 +242,7 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
   /* Check that the cookie is returned unmodified */
   if (memcmp(ske->start_payload->cookie, payload->cookie,
             ske->start_payload->cookie_len)) {
-    SILC_LOG_DEBUG(("Responder modified our cookie and it must not do it"));
+    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;
@@ -262,6 +268,8 @@ SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske,
      exchange. The same data is returned to upper levels by calling
      the callback function. */
   ske->prop = prop = silc_calloc(1, sizeof(*prop));
+  if (!ske->prop)
+    goto err;
   prop->flags = payload->flags;
   status = silc_ske_group_get_by_name(payload->ke_grp_list, &group);
   if (status != SILC_SKE_STATUS_OK)
@@ -341,6 +349,10 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   /* Create the random number x, 1 < x < q. */
   x = silc_calloc(1, sizeof(*x));
+  if (!x){
+    ske->status = SILC_SKE_STATUS_OUT_OF_MEMORY;
+    return ske->status;
+  }
   silc_mp_init(x);
   status = 
     silc_ske_create_rnd(ske, &ske->prop->group->group_order,
@@ -356,6 +368,12 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
   /* 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"));
@@ -373,6 +391,7 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
       silc_free(x);
       silc_mp_uninit(&payload->x);
       silc_free(payload);
+      ske->ke1_payload = NULL;
       ske->status = SILC_SKE_STATUS_OK;
       return ske->status;
     }
@@ -382,7 +401,7 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
 
   /* 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[1024];
+    unsigned char hash[32], sign[2048 + 1];
     SilcUInt32 hash_len, sign_len;
 
     SILC_LOG_DEBUG(("We are doing mutual authentication"));
@@ -397,7 +416,17 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
     /* Sign the hash value */
     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);
+    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));
@@ -410,7 +439,9 @@ SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
     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;
   }
@@ -470,6 +501,7 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
     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;
@@ -492,9 +524,7 @@ static void silc_ske_initiator_finish_final(SilcSKE ske,
     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_DEBUG(("Signature don't match"));
-      
+      SILC_LOG_ERROR(("Signature verification failed, incorrect signature"));
       status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
       goto err;
     }
@@ -669,6 +699,13 @@ SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
     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,
@@ -830,6 +867,7 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
                                     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;
@@ -852,9 +890,7 @@ static void silc_ske_responder_phase2_final(SilcSKE ske,
     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_DEBUG(("Signature don't match"));
-      
+      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);
@@ -945,7 +981,7 @@ SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
     SILC_LOG_DEBUG(("We are doing mutual authentication"));
     
     if (!recv_payload->pk_data && ske->callbacks->verify_key) {
-      SILC_LOG_DEBUG(("Remote end did not send its public key (or "
+      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;
@@ -986,7 +1022,7 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
   SilcSKEStatus status = SILC_SKE_STATUS_OK;
   SilcBuffer payload_buf;
   SilcMPInt *KEY;
-  unsigned char hash[32], sign[1024], *pk;
+  unsigned char hash[32], sign[2048 + 1], *pk;
   SilcUInt32 hash_len, sign_len, pk_len;
 
   SILC_LOG_DEBUG(("Start"));
@@ -1006,7 +1042,7 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
     /* Get the public key */
     pk = silc_pkcs_public_key_encode(public_key, &pk_len);
     if (!pk) {
-      status = SILC_SKE_STATUS_ERROR;
+      status = SILC_SKE_STATUS_OUT_OF_MEMORY;
       goto err;
     }
     ske->ke2_payload->pk_data = pk;
@@ -1029,7 +1065,11 @@ SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
     /* Sign the hash value */
     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);
+    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));
@@ -1076,8 +1116,9 @@ SilcSKEStatus silc_ske_end(SilcSKE ske)
 
   SILC_LOG_DEBUG(("Start"));
 
-  packet = silc_buffer_alloc(4);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  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);
@@ -1104,8 +1145,9 @@ SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status)
   if (status > SILC_SKE_STATUS_INVALID_COOKIE)
     status = SILC_SKE_STATUS_BAD_PAYLOAD;
 
-  packet = silc_buffer_alloc(4);
-  silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+  packet = silc_buffer_alloc_size(4);
+  if (!packet)
+    return SILC_SKE_STATUS_OUT_OF_MEMORY;
   silc_buffer_format(packet,
                     SILC_STR_UI_INT((SilcUInt32)status),
                     SILC_STR_END);
@@ -1145,7 +1187,7 @@ silc_ske_assemble_security_properties(SilcSKE ske,
   /* 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 */
@@ -1174,8 +1216,8 @@ silc_ske_assemble_security_properties(SilcSKE ske,
 
   /* XXX */
   /* Get supported compression algorithms */
-  rp->comp_alg_list = strdup("");
-  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 +
@@ -1501,9 +1543,8 @@ silc_ske_select_security_properties(SilcSKE ske,
     payload->hmac_alg_list = strdup(rp->hmac_alg_list);
   }
 
-#if 0
   /* Get supported compression algorithms */
-  cp = rp->hash_alg_list;
+  cp = rp->comp_alg_list;
   if (cp && strchr(cp, ',')) {
     while(cp) {
       char *item;
@@ -1512,15 +1553,23 @@ 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 Compression `%s'", item));
 
-      if (silc_hash_is_supported(item) == TRUE) {
-       SILC_LOG_DEBUG(("Found hash alg `%s'", item));
-
-       payload->hash_alg_len = len;
-       payload->hash_alg_list = item;
+#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)
@@ -1531,20 +1580,7 @@ silc_ske_select_security_properties(SilcSKE ske,
       if (item)
        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);
-      silc_free(payload->ke_grp_list);
-      silc_free(payload->pkcs_alg_list);
-      silc_free(payload->enc_alg_list);
-      silc_free(payload);
-      return;
-    }
-  } else {
-
   }
-#endif
 
   payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 
     2 + payload->version_len + 
@@ -1564,26 +1600,31 @@ static SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcMPInt *n,
 {
   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_ERROR;
+    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)
     status = SILC_SKE_STATUS_ERROR;
 
-  memset(string, 'F', (len / 8));
+  memset(string, 'F', l);
   silc_free(string);
 
   return status;
@@ -1613,11 +1654,12 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
     KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
     
     /* Format the buffer used to compute the hash value */
-    buf = silc_buffer_alloc(ske->start_payload_copy->len + 
-                           ske->ke2_payload->pk_len + 
-                           ske->ke1_payload->pk_len + 
-                           e_len + f_len + KEY_len);
-    silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+    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) {
@@ -1669,9 +1711,10 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
   } else {
     e = silc_mp_mp2bin(&ske->ke1_payload->x, 0, &e_len);
 
-    buf = silc_buffer_alloc(ske->start_payload_copy->len + 
-                           ske->ke1_payload->pk_len + e_len);
-    silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
+    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 = 
@@ -1695,7 +1738,7 @@ static SilcSKEStatus silc_ske_make_hash(SilcSKE ske,
 
   /* 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);
 
   if (initiator == FALSE) {
     SILC_LOG_HEXDUMP(("HASH"), return_hash, *return_hash_len);
@@ -1730,8 +1773,9 @@ silc_ske_process_key_material_data(unsigned char *data,
   if (!req_iv_len || !req_enc_key_len || !req_hmac_key_len)
     return SILC_SKE_STATUS_ERROR;
 
-  buf = silc_buffer_alloc(1 + data_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(data, data_len),
@@ -1768,8 +1812,9 @@ silc_ske_process_key_material_data(unsigned char *data,
     silc_hash_make(hash, buf->data, buf->len, k1);
     
     /* Take second round */
-    dist = silc_buffer_alloc(data_len + hash_len);
-    silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
+    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(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
@@ -1803,6 +1848,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     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 */
@@ -1828,8 +1874,9 @@ silc_ske_process_key_material_data(unsigned char *data,
     silc_hash_make(hash, buf->data, buf->len, k1);
     
     /* Take second round */
-    dist = silc_buffer_alloc(data_len + hash_len);
-    silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
+    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(data, data_len),
                       SILC_STR_UI_XNSTRING(k1, hash_len),
@@ -1863,6 +1910,7 @@ silc_ske_process_key_material_data(unsigned char *data,
     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 */
@@ -1887,6 +1935,7 @@ silc_ske_process_key_material_data(unsigned char *data,
   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;
@@ -1909,8 +1958,9 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
   /* 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));
+  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),
@@ -1924,6 +1974,7 @@ SilcSKEStatus silc_ske_process_key_material(SilcSKE ske,
 
   memset(tmpbuf, 0, klen);
   silc_free(tmpbuf);
+  silc_buffer_clear(buf);
   silc_buffer_free(buf);
 
   return status;
@@ -1981,7 +2032,8 @@ const char *silc_ske_status_string[] =
   "Key exchange protocol is not active",
   "Bad reserved field in packet",
   "Bad payload length in packet",
-  "Incorrect hash",
+  "Error computing signature",
+  "System out of memory",
 
   NULL
 };