Protocol version 1.2 integrations
[silc.git] / lib / silccore / silcprivate.c
index 45e81d8544408dcb0fc5f9b2bf7fd417997f3c6e..b90d01096dcdb058380b017fd0d56598f309b503 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "silcincludes.h"
 #include "silcprivate.h"
+#include "silcprivate_i.h"
 
 /******************************************************************************
 
 
 ******************************************************************************/
 
-/* Private Message Payload structure. Contents of this structure is parsed
-   from SILC packets. */
-struct SilcPrivateMessagePayloadStruct {
-  unsigned short nickname_len;
-  unsigned char *nickname;
-  unsigned short flags;
-  unsigned short message_len;
-  unsigned char *message;
-};
-
 /* Parses private message payload returning new private mesage payload 
    structure. This also decrypts the message if the `cipher' is provided. */
 
 SilcPrivateMessagePayload 
-silc_private_message_payload_parse(SilcBuffer buffer, SilcCipher cipher)
+silc_private_message_payload_parse(unsigned char *payload,
+                                  SilcUInt32 payload_len,
+                                  SilcCipher cipher,
+                                  SilcHmac hmac)
 {
-  SilcPrivateMessagePayload new;
-  int ret;
+  SilcBufferStruct buffer;
+  SilcPrivateMessagePayload newp;
+  SilcUInt32 mac_len = 0, block_len, pad_len = 0;
+  unsigned char data[16], mac[32];
+  int len, totlen;
 
   SILC_LOG_DEBUG(("Parsing private message payload"));
 
+  silc_buffer_set(&buffer, payload, payload_len);
+
+  newp = silc_calloc(1, sizeof(*newp));
+  if (!newp)
+    return NULL;
+
   /* Decrypt the payload */
-  if (cipher)
-    silc_cipher_decrypt(cipher, buffer->data, buffer->data, 
-                       buffer->len, cipher->iv);
-
-  new = silc_calloc(1, sizeof(*new));
-
-  /* Parse the Private Message Payload. Ignore the padding. */
-  ret = silc_buffer_unformat(buffer,
-                            SILC_STR_UI16_NSTRING_ALLOC(&new->nickname, 
-                                                        &new->nickname_len),
-                            SILC_STR_UI_SHORT(&new->flags),
-                            SILC_STR_UI16_NSTRING_ALLOC(&new->message, 
-                                                        &new->message_len),
+  if (cipher) {
+    /* Decrypt first block. This is to get the true length of the data in
+       payload.  It is possible there is additional data after the message
+       payload with private messages. */
+    block_len = silc_cipher_get_block_len(cipher);
+    if (block_len > buffer.len)
+      goto err;
+    silc_cipher_decrypt(cipher, buffer.data, data, block_len,
+                       silc_cipher_get_iv(cipher));
+
+    /* Length of encrypted area */
+    SILC_GET16_MSB(newp->message_len, data + 2);
+    totlen = 4 + newp->message_len;
+    pad_len = SILC_PRIVATE_MESSAGE_PAD(4 + newp->message_len);
+    totlen += pad_len;
+
+    /* Sanity checks */
+    if (totlen > buffer.len || newp->message_len < 1 ||
+       newp->message_len > buffer.len - 4) {
+      SILC_LOG_DEBUG(("Incorrect private message payload in packet"));
+      goto err;
+    }
+
+    /* Compute MAC for integrity check from the cipher text */
+    if (hmac) {
+      SILC_LOG_DEBUG(("Checking private message MAC"));
+      silc_hmac_init(hmac);
+      silc_hmac_update(hmac, buffer.data, totlen);
+      silc_hmac_final(hmac, mac, &mac_len);
+      if (memcmp(mac, buffer.data + totlen, mac_len)) {
+       SILC_LOG_DEBUG(("Private message MAC does not match"));
+       goto err;
+      }
+      SILC_LOG_DEBUG(("MAC is Ok"));
+    }
+
+    /* Now decrypt rest of the data */
+    memcpy(buffer.data, data, block_len);
+    if (totlen - block_len > 0)
+      silc_cipher_decrypt(cipher, buffer.data + block_len,
+                         buffer.data + block_len, totlen - block_len,
+                         silc_cipher_get_iv(cipher));
+    memset(data, 0, sizeof(data));
+  }
+
+  /* Parse the Private Message Payload. */
+  len = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&newp->flags),
+                            SILC_STR_UI16_NSTRING_ALLOC(&newp->message, 
+                                                        &newp->message_len),
                             SILC_STR_END);
-  if (ret == -1)
+  if (len == -1 || newp->message_len < 1 ||
+      newp->message_len > buffer.len - 4) {
+    SILC_LOG_DEBUG(("Incorrect private message payload in packet"));
     goto err;
+  }
 
-  if ((new->message_len < 1 || new->message_len > buffer->len) ||
-      (new->nickname_len < 1 || new->nickname_len > buffer->len)) {
-    SILC_LOG_ERROR(("Incorrect private message payload in packet, "
-                   "packet dropped"));
-    goto err;
+  /* Parse also padding and MAC */
+  if (cipher) {
+    silc_buffer_pull(&buffer, 4 + newp->message_len);
+    len = silc_buffer_unformat(&buffer,
+                              SILC_STR_UI_XNSTRING_ALLOC(&newp->pad,
+                                                         pad_len),
+                              SILC_STR_UI_XNSTRING_ALLOC(&newp->mac,
+                                                         mac_len),
+                              SILC_STR_END);
+    silc_buffer_push(&buffer, 4 + newp->message_len);
   }
 
-  return new;
+  return newp;
 
  err:
-  silc_private_message_payload_free(new);
+  silc_private_message_payload_free(newp);
   return NULL;
 }
 
@@ -86,39 +134,45 @@ silc_private_message_payload_parse(SilcBuffer buffer, SilcCipher cipher)
    the cipher is provided the packet is also encrypted here.  It is provided
    if the private message private keys are used. */
 
-SilcBuffer silc_private_message_payload_encode(unsigned int nickname_len,
-                                              unsigned char *nickname,
-                                              unsigned short flags,
-                                              unsigned short data_len,
-                                              unsigned char *data,
-                                              SilcCipher cipher)
+SilcBuffer silc_private_message_payload_encode(SilcUInt16 flags,
+                                              SilcUInt16 data_len,
+                                              const unsigned char *data,
+                                              SilcCipher cipher,
+                                              SilcHmac hmac,
+                                              SilcRng rng)
 {
   int i;
   SilcBuffer buffer;
-  unsigned int len, pad_len = 0;
-  unsigned char pad[SILC_PACKET_MAX_PADLEN];
+  SilcUInt32 len, pad_len = 0, mac_len = 0;
+  unsigned char pad[16], mac[32];
 
   SILC_LOG_DEBUG(("Encoding private message payload"));
 
-  len = 2 + nickname_len + 4 + data_len;
+  data_len = SILC_PRIVATE_MESSAGE_DATALEN(data_len);
+  len = 4 + data_len;
 
   if (cipher) {
     /* Calculate length of padding. */
-    pad_len = SILC_PACKET_PADLEN((len + 2));
+    pad_len = SILC_PRIVATE_MESSAGE_PAD(len);
     len += pad_len;
+    mac_len = hmac ? silc_hmac_len(hmac) : 0;
+    len += mac_len;
 
     /* 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();
+    }
   }
 
   /* Allocate private message payload buffer */
-  buffer = silc_buffer_alloc(len);
+  buffer = silc_buffer_alloc_size(len);
+  if (!buffer)
+    return NULL;
 
   /* Encode the Channel Message Payload */
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
   silc_buffer_format(buffer, 
-                    SILC_STR_UI_SHORT(nickname_len),
-                    SILC_STR_UI_XNSTRING(nickname, nickname_len),
                     SILC_STR_UI_SHORT(flags),
                     SILC_STR_UI_SHORT(data_len),
                     SILC_STR_UI_XNSTRING(data, data_len),
@@ -128,18 +182,26 @@ SilcBuffer silc_private_message_payload_encode(unsigned int nickname_len,
   if (cipher) {
     /* Encrypt payload of the packet. */
     silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
-                       buffer->len, cipher->iv);
+                       buffer->len - mac_len, silc_cipher_get_iv(cipher));
     memset(pad, 0, sizeof(pad));
+
+    /* Compute MAC from the ciphertext */
+    if (hmac) {
+      silc_hmac_init(hmac);
+      silc_hmac_update(hmac, buffer->data, buffer->len - mac_len);
+      silc_hmac_final(hmac, mac, &mac_len);
+      memcpy(buffer->data + (buffer->len - mac_len), mac, mac_len);
+      memset(mac, 0, sizeof(mac));
+    }
   }
 
   return buffer;
 }
 
-/* Free's Private Message Payload */
+/* Frees Private Message Payload */
 
 void silc_private_message_payload_free(SilcPrivateMessagePayload payload)
 {
-  silc_free(payload->nickname);
   if (payload->message) {
     memset(payload->message, 0, payload->message_len);
     silc_free(payload->message);
@@ -147,23 +209,19 @@ void silc_private_message_payload_free(SilcPrivateMessagePayload payload)
   silc_free(payload);
 }
 
-/* Return nickname */
+/* Return flags */
 
-unsigned char *
-silc_private_message_get_nickname(SilcPrivateMessagePayload payload,
-                                 unsigned int *nickname_len)
+SilcUInt16 
+silc_private_message_get_flags(SilcPrivateMessagePayload payload)
 {
-  if (nickname_len)
-    *nickname_len = payload->nickname_len;
-
-  return payload->nickname;
+  return payload->flags;
 }
 
 /* Return message */
 
 unsigned char *
 silc_private_message_get_message(SilcPrivateMessagePayload payload,
-                                unsigned int *message_len)
+                                SilcUInt32 *message_len)
 {
   if (message_len)
     *message_len = payload->message_len;
@@ -171,10 +229,10 @@ silc_private_message_get_message(SilcPrivateMessagePayload payload,
   return payload->message;
 }
 
-/* Return flags */
+/* Return MAC.  Caller knows its length */
 
-unsigned short 
-silc_private_message_get_flags(SilcPrivateMessagePayload payload)
+unsigned char *
+silc_private_message_get_mac(SilcPrivateMessagePayload payload)
 {
-  return payload->flags;
+  return payload->mac;
 }