Protocol 1.2 integration continues.
[runtime.git] / lib / silccore / silcchannel.c
index d104cc182aaea154b5c2e438a10e66870ea07d32..491743c5a5c70f418a42b0edaa807fc86d361613 100644 (file)
 
 #include "silcincludes.h"
 #include "silcchannel.h"
-
-/******************************************************************************
-
-                              Channel Payload
-
-******************************************************************************/
-
-/* Channel Message Payload structure. Contents of this structure is parsed
-   from SILC packets. */
-struct SilcChannelPayloadStruct {
-  SilcUInt16 name_len;
-  unsigned char *channel_name;
-  SilcUInt16 id_len;
-  unsigned char *channel_id;
-  SilcUInt32 mode;
-};
+#include "silcchannel_i.h"
 
 /* Parses channel payload returning new channel payload structure. */
 
@@ -67,8 +52,9 @@ SilcChannelPayload silc_channel_payload_parse(const unsigned char *payload,
   if (ret == -1)
     goto err;
 
-  if ((newp->name_len < 1 || newp->name_len > buffer.len) ||
-      (newp->id_len < 1 || newp->id_len > buffer.len)) {
+  if ((newp->name_len < 1 || newp->name_len > buffer.len - 8) ||
+      (newp->id_len < 1 || newp->id_len > buffer.len - 8) ||
+      (newp->id_len + newp->name_len > buffer.len - 8)) {
     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
     goto err;
   }
@@ -230,30 +216,6 @@ SilcUInt32 silc_channel_get_mode(SilcChannelPayload payload)
 
 ******************************************************************************/
 
-/* Calculates padding length for message payload */
-#define SILC_CHANNEL_MESSAGE_PAD(__payloadlen) (16 - (__payloadlen) % 16)
-
-/* Header length plus maximum padding length */
-#define SILC_CHANNEL_MESSAGE_HLEN 6 + 16
-
-/* Returns the data length that fits to the packet.  If data length is too
-   big it will be truncated to fit to the payload. */
-#define SILC_CHANNEL_MESSAGE_DATALEN(data_len, header_len)             \
-  ((data_len + SILC_CHANNEL_MESSAGE_HLEN + header_len) >               \
-   SILC_PACKET_MAX_LEN ?                                               \
-   data_len - ((data_len + SILC_CHANNEL_MESSAGE_HLEN + header_len) -   \
-              SILC_PACKET_MAX_LEN) : data_len)
-
-/* Channel Message Payload structure. Contents of this structure is parsed
-   from SILC packets. */
-struct SilcChannelMessagePayloadStruct {
-  SilcMessageFlags flags;
-  SilcUInt16 data_len;
-  unsigned char *data;
-  unsigned char *mac;
-  unsigned char *iv;
-};
-
 /* Decrypts the channel message payload. First push the IV out of the
    packet. The IV is used in the decryption process. Then decrypt the
    message. After decyprtion, take the MAC from the decrypted packet, 
@@ -263,55 +225,38 @@ struct SilcChannelMessagePayloadStruct {
 bool silc_channel_message_payload_decrypt(unsigned char *data,
                                          size_t data_len,
                                          SilcCipher cipher,
-                                         SilcHmac hmac)
+                                         SilcHmac hmac,
+                                         bool check_mac)
 {
   SilcUInt32 iv_len, mac_len;
-  unsigned char *end, *mac, mac2[32];
-  unsigned char *dst, iv[SILC_CIPHER_MAX_IV_SIZE];
+  unsigned char *mac, mac2[32];
 
-  /* Push the IV out of the packet, and copy the IV since we do not want
-     to modify the original data buffer. */
-  end = data + data_len;
+  mac_len = silc_hmac_len(hmac);
   iv_len = silc_cipher_get_block_len(cipher);
-  memcpy(iv, end - iv_len, iv_len);
-
-  /* Allocate destination decryption buffer since we do not want to modify
-     the original data buffer, since we might want to call this function 
-     many times for same payload. */
-  if (hmac) {
-    dst = silc_calloc(data_len - iv_len, sizeof(*dst));
-    if (!dst)
-      return FALSE;
-  } else {
-    dst = data;
-  }
 
-  /* Decrypt the channel message */
-  silc_cipher_decrypt(cipher, data, dst, data_len - iv_len, iv);
+  if (data_len < mac_len)
+    return FALSE;
 
-  if (hmac) {
+  if (check_mac) {
     /* Take the MAC */
-    end = dst + (data_len - iv_len);
-    mac_len = silc_hmac_len(hmac);
-    mac = (end - mac_len);
+    mac = data + (data_len - mac_len);
 
     /* Check the MAC of the message */
-    SILC_LOG_DEBUG(("Checking channel message MACs"));
-    silc_hmac_make(hmac, dst, (data_len - iv_len - mac_len), mac2, &mac_len);
+    SILC_LOG_DEBUG(("Checking channel message MAC"));
+    silc_hmac_init(hmac);
+    silc_hmac_update(hmac, data, data_len - mac_len);
+    silc_hmac_final(hmac, mac2, &mac_len);
     if (memcmp(mac, mac2, mac_len)) {
-      SILC_LOG_DEBUG(("Channel message MACs does not match"));
-      silc_free(dst);
+      SILC_LOG_DEBUG(("Channel message MAC does not match"));
       return FALSE;
     }
     SILC_LOG_DEBUG(("MAC is Ok"));
-
-    /* Now copy the decrypted data into the buffer since it is verified
-       it decrypted correctly. */
-    memcpy(data, dst, data_len - iv_len);
-    memset(dst, 0, data_len - iv_len);
-    silc_free(dst);
   }
 
+  /* Decrypt the channel message */
+  silc_cipher_decrypt(cipher, data, data,
+                     data_len - iv_len - mac_len,
+                     data + (data_len - iv_len - mac_len));
   return TRUE;
 }
 
@@ -335,7 +280,7 @@ silc_channel_message_payload_parse(unsigned char *payload,
 
   /* Decrypt the payload */
   ret = silc_channel_message_payload_decrypt(buffer.data, buffer.len,
-                                            cipher, hmac);
+                                            cipher, hmac, TRUE);
   if (ret == FALSE)
     return NULL;
 
@@ -346,24 +291,28 @@ silc_channel_message_payload_parse(unsigned char *payload,
   if (!newp)
     return NULL;
 
-  /* Parse the Channel Message Payload. Ignore the padding. */
+  /* Parse the Channel Message Payload. */
   ret = silc_buffer_unformat(&buffer,
                             SILC_STR_UI_SHORT(&newp->flags),
                             SILC_STR_UI16_NSTRING_ALLOC(&newp->data, 
                                                         &newp->data_len),
-                            SILC_STR_UI16_NSTRING(NULL, NULL),
-                            SILC_STR_UI_XNSTRING(&newp->mac, mac_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->pad, 
+                                                        &newp->pad_len),
                             SILC_STR_UI_XNSTRING(&newp->iv, iv_len),
+                            SILC_STR_UI_XNSTRING(&newp->mac, mac_len),
                             SILC_STR_END);
   if (ret == -1)
     goto err;
 
-  if (newp->data_len > buffer.len) {
+  if ((newp->data_len > buffer.len - 6 - mac_len - iv_len) ||
+      (newp->pad_len + newp->data_len > buffer.len - 6 - mac_len - iv_len)) {
     SILC_LOG_ERROR(("Incorrect channel message payload in packet, "
                    "packet dropped"));
     goto err;
   }
 
+  newp->iv_len = iv_len;
+
   return newp;
 
  err:
@@ -371,12 +320,47 @@ silc_channel_message_payload_parse(unsigned char *payload,
   return NULL;
 }
 
+/* This function is used to encrypt the Channel Messsage Payload which is
+   the `data' and `data_len'.  This is used internally by the Channel Message
+   Payload encoding routines but application may call this too if needed. 
+   The `data_len' is the data lenght which is used to create MAC out of.
+   The `true_len' is the true length of `data' message payload and is used
+   assemble rest of the packet after MAC creation. The `true_len' length
+   packet will then be encrypted. */
+
+bool silc_channel_message_payload_encrypt(unsigned char *data,
+                                         SilcUInt32 data_len,
+                                         unsigned char *iv,
+                                         SilcUInt32 iv_len,
+                                         SilcCipher cipher,
+                                         SilcHmac hmac)
+{
+  unsigned char mac[32];
+  SilcUInt32 mac_len;
+  SilcBufferStruct buf;
+
+  /* Encrypt payload of the packet. This is encrypted with the channel key. */
+  silc_cipher_encrypt(cipher, data, data, data_len - iv_len, iv);
+
+  /* Compute the MAC of the encrypted channel message data */
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_final(hmac, mac, &mac_len);
+
+  /* Put rest of the data to the payload */
+  silc_buffer_set(&buf, data, data_len + mac_len);
+  silc_buffer_pull(&buf, data_len);
+  silc_buffer_put(&buf, mac, mac_len);
+
+  return TRUE;
+}
+
 /* Encodes channel message payload into a buffer and returns it. This is used 
    to add channel message payload into a packet. As the channel payload is
    encrypted separately from other parts of the packet padding must
    be applied to the payload. */
 
-SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags,
+SilcBuffer silc_channel_message_payload_encode(SilcMessageFlags flags,
                                               SilcUInt16 data_len,
                                               const unsigned char *data,
                                               SilcUInt16 iv_len,
@@ -389,7 +373,6 @@ SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags,
   SilcBuffer buffer;
   SilcUInt32 len, pad_len, mac_len;
   unsigned char pad[16];
-  unsigned char mac[32];
 
   SILC_LOG_DEBUG(("Encoding channel message payload"));
 
@@ -397,50 +380,42 @@ SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags,
      since it is not encrypted. */
   mac_len = silc_hmac_len(hmac);
   data_len = SILC_CHANNEL_MESSAGE_DATALEN(data_len, mac_len + iv_len);
-  len = 6 + data_len + mac_len;
+  len = 6 + data_len;
   pad_len = SILC_CHANNEL_MESSAGE_PAD(len);
 
   /* Allocate channel payload buffer */
-  len += pad_len + iv_len;
+  len += pad_len + iv_len + mac_len;
   buffer = silc_buffer_alloc(len);
   if (!buffer)
     return NULL;
 
   /* Generate padding */
   if (rng) {
-    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte(rng);
+    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte_fast(rng);
   } else {
-    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
+    for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte_fast();
   }
 
   /* Encode the Channel Message Payload */
-  silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
+  silc_buffer_pull_tail(buffer, 6 + data_len + pad_len + iv_len);
   silc_buffer_format(buffer, 
                     SILC_STR_UI_SHORT(flags),
                     SILC_STR_UI_SHORT(data_len),
                     SILC_STR_UI_XNSTRING(data, data_len),
                     SILC_STR_UI_SHORT(pad_len),
                     SILC_STR_UI_XNSTRING(pad, pad_len),
-                    SILC_STR_END);
-
-  /* Compute the MAC of the channel message data */
-  silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
-
-  /* Put rest of the data to the payload */
-  silc_buffer_pull_tail(buffer, mac_len + iv_len);
-  silc_buffer_pull(buffer, 6 + data_len + pad_len);
-  silc_buffer_format(buffer, 
-                    SILC_STR_UI_XNSTRING(mac, mac_len),
                     SILC_STR_UI_XNSTRING(iv, iv_len),
                     SILC_STR_END);
-  silc_buffer_push(buffer, 6 + data_len + pad_len);
-
-  /* Encrypt payload of the packet. This is encrypted with the channel key. */
-  silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
-                     buffer->len - iv_len, iv);
 
   memset(pad, 0, sizeof(pad));
-  memset(mac, 0, sizeof(mac));
+
+  if (!silc_channel_message_payload_encrypt(buffer->data, buffer->len,
+                                           iv, iv_len, cipher, hmac)) {
+    silc_buffer_free(buffer);
+    return NULL;
+  }
+
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
 
   return buffer;
 }
@@ -495,17 +470,6 @@ unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload)
 
 ******************************************************************************/
 
-/* Channel Key Payload structrue. Channel keys are parsed from SILC
-   packets into this structure. */
-struct SilcChannelKeyPayloadStruct {
-  SilcUInt16 id_len;
-  unsigned char *id;
-  SilcUInt16 cipher_len;
-  unsigned char *cipher;
-  SilcUInt16 key_len;
-  unsigned char *key;
-};
-
 /* Parses channel key payload returning new channel key payload structure */
 
 SilcChannelKeyPayload 
@@ -535,7 +499,8 @@ silc_channel_key_payload_parse(const unsigned char *payload,
   if (ret == -1)
     goto err;
 
-  if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1) {
+  if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1 ||
+      newp->id_len + newp->cipher_len + newp->key_len > buffer.len - 6) {
     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
     goto err;
   }