updates.
[silc.git] / lib / silccore / silcchannel.c
index 8fcc4c539be5cfbbb652a702f3f8fae962a9ee3f..2bd7eb6fce3351cb8fbee08cc26a0856ba4d7619 100644 (file)
@@ -230,14 +230,28 @@ 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;
+  SilcUInt16 pad_len;
+  unsigned char *pad;
   unsigned char *mac;
   unsigned char *iv;
 };
@@ -285,7 +299,10 @@ bool silc_channel_message_payload_decrypt(unsigned char *data,
 
     /* 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_hmac_init(hmac);
+    silc_hmac_update(hmac, dst, (data_len - iv_len - mac_len));
+    silc_hmac_update(hmac, data + (data_len - iv_len), iv_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);
@@ -334,12 +351,13 @@ 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_UI16_NSTRING_ALLOC(&newp->pad, 
+                                                        &newp->pad_len),
                             SILC_STR_UI_XNSTRING(&newp->mac, mac_len),
                             SILC_STR_UI_XNSTRING(&newp->iv, iv_len),
                             SILC_STR_END);
@@ -359,6 +377,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,
+                                         SilcUInt32 true_len,
+                                         unsigned char *iv,
+                                         SilcUInt32 iv_len,
+                                         SilcCipher cipher,
+                                         SilcHmac hmac)
+{
+  unsigned char mac[32];
+  SilcUInt32 mac_len;
+  SilcBufferStruct buf;
+
+  /* Compute the MAC of the channel message data */
+  silc_hmac_init(hmac);
+  silc_hmac_update(hmac, data, data_len);
+  silc_hmac_update(hmac, iv, iv_len);
+  silc_hmac_final(hmac, mac, &mac_len);
+
+  /* Put rest of the data to the payload */
+  silc_buffer_set(&buf, data, true_len);
+  silc_buffer_pull(&buf, data_len);
+  silc_buffer_format(&buf, 
+                    SILC_STR_UI_XNSTRING(mac, mac_len),
+                    SILC_STR_UI_XNSTRING(iv, iv_len),
+                    SILC_STR_END);
+
+  /* Encrypt payload of the packet. This is encrypted with the channel key. */
+  silc_cipher_encrypt(cipher, data, data, true_len - iv_len, iv);
+
+  memset(mac, 0, sizeof(mac));
+  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
@@ -370,19 +429,20 @@ SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags,
                                               SilcUInt16 iv_len,
                                               unsigned char *iv,
                                               SilcCipher cipher,
-                                              SilcHmac hmac)
+                                              SilcHmac hmac,
+                                              SilcRng rng)
 {
   int i;
   SilcBuffer buffer;
   SilcUInt32 len, pad_len, mac_len;
   unsigned char pad[16];
-  unsigned char mac[32];
 
   SILC_LOG_DEBUG(("Encoding channel message payload"));
 
   /* Calculate length of padding. IV is not included into the calculation
      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;
   pad_len = SILC_CHANNEL_MESSAGE_PAD(len);
 
@@ -393,7 +453,11 @@ SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags,
     return NULL;
 
   /* Generate padding */
-  for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte();
+  if (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_fast();
+  }
 
   /* Encode the Channel Message Payload */
   silc_buffer_pull_tail(buffer, 6 + data_len + pad_len);
@@ -405,24 +469,16 @@ SilcBuffer silc_channel_message_payload_encode(SilcUInt16 flags,
                     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);
+  memset(pad, 0, sizeof(pad));
 
-  /* 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);
+  if (!silc_channel_message_payload_encrypt(buffer->data, buffer->len,
+                                           buffer->truelen, iv, iv_len,
+                                           cipher, hmac)) {
+    silc_buffer_free(buffer);
+    return NULL;
+  }
 
-  memset(pad, 0, sizeof(pad));
-  memset(mac, 0, sizeof(mac));
+  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
 
   return buffer;
 }