Merged silc_1_0_branch to trunk.
[silc.git] / lib / silccore / silcpacket.c
index 89aae80b2edb362caa740e190fa2ce8e726f128a..167d7d17a61fcae170a8cf4b8171344b9d6a447a 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcpacket.c 
+  silcpacket.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2003 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
@@ -40,7 +40,7 @@
 int silc_packet_send(SilcSocketConnection sock, bool force_send)
 {
   SILC_LOG_DEBUG(("Sending packet to %s:%d [%s]", sock->hostname,
-                 sock->port,  
+                 sock->port,
                  (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                   sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                   sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
@@ -56,21 +56,21 @@ int silc_packet_send(SilcSocketConnection sock, bool force_send)
     ret = silc_socket_write(sock);
 
     if (ret == -1) {
-      SILC_LOG_ERROR(("Error sending packet, dropped: %s", 
+      SILC_LOG_ERROR(("Error sending packet, dropped: %s",
                       strerror(errno)));
     }
     if (ret != -2)
       return ret;
 
     SILC_LOG_DEBUG(("Could not force the send, packet put to queue"));
-  }  
+  }
 
   SILC_LOG_DEBUG(("Packet in queue"));
 
   return -2;
 }
 
-/* Encrypts a packet. This also creates HMAC of the packet before 
+/* Encrypts a packet. This also creates HMAC of the packet before
    encryption and adds the HMAC at the end of the buffer. This assumes
    that there is enough free space at the end of the buffer to add the
    computed HMAC. This is the normal way of encrypting packets, if some
@@ -80,34 +80,31 @@ int silc_packet_send(SilcSocketConnection sock, bool force_send)
 void silc_packet_encrypt(SilcCipher cipher, SilcHmac hmac, SilcUInt32 sequence,
                         SilcBuffer buffer, SilcUInt32 len)
 {
-  unsigned char mac[32];
-  SilcUInt32 mac_len;
 
-  /* Compute HMAC. This assumes that HMAC is created from the entire
+  /* Encrypt the data area of the packet. */
+  if (cipher) {
+    SILC_LOG_DEBUG(("Encrypting packet (%d), cipher %s, len %d",
+                   sequence, silc_cipher_get_name(cipher), len));
+    silc_cipher_encrypt(cipher, buffer->data, buffer->data, len, NULL);
+  }
+
+  /* Compute HMAC. This assumes that MAC is computed from the entire
      data area thus this uses the length found in buffer, not the length
      sent as argument. */
   if (hmac) {
-    unsigned char psn[4];
+    unsigned char mac[32], psn[4];
+    SilcUInt32 mac_len;
 
     silc_hmac_init(hmac);
     SILC_PUT32_MSB(sequence, psn);
     silc_hmac_update(hmac, psn, 4);
     silc_hmac_update(hmac, buffer->data, buffer->len);
     silc_hmac_final(hmac, mac, &mac_len);
-    silc_buffer_put_tail(buffer, mac, mac_len);
-    memset(mac, 0, sizeof(mac));
-  }
-
-  /* Encrypt the data area of the packet. */
-  if (cipher) {
-    SILC_LOG_DEBUG(("Encrypting packet, cipher %s, len %d", 
-                   cipher->cipher->name, len));
-    silc_cipher_encrypt(cipher, buffer->data, buffer->data, len, cipher->iv);
-  }
 
-  /* Pull the HMAC into the visible data area in the buffer */
-  if (hmac)
+    /* Put MAC and pull the it into the visible data area in the buffer */
+    silc_buffer_put_tail(buffer, mac, mac_len);
     silc_buffer_pull_tail(buffer, mac_len);
+  }
 }
 
 /* Assembles a new packet to be ready for send out. */
@@ -117,9 +114,9 @@ bool silc_packet_assemble(SilcPacketContext *packet, SilcRng rng,
                          SilcSocketConnection sock,
                          const unsigned char *data, SilcUInt32 data_len,
                          const SilcBuffer assembled_packet)
-{ 
-  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];   
-  int block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+{
+  unsigned char tmppad[SILC_PACKET_MAX_PADLEN];
+  unsigned int block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
   int i, ret;
 
   SILC_LOG_DEBUG(("Assembling outgoing packet"));
@@ -133,16 +130,17 @@ bool silc_packet_assemble(SilcPacketContext *packet, SilcRng rng,
   if (!packet->truelen) {
     data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
                                   packet->src_id_len + packet->dst_id_len);
-    packet->truelen = data_len + SILC_PACKET_HEADER_LEN + 
+    packet->truelen = data_len + SILC_PACKET_HEADER_LEN +
       packet->src_id_len + packet->dst_id_len;
   }
 
   /* Calculate the length of the padding. The padding is calculated from
      the data that will be encrypted. */
   if (!packet->padlen) {
-    packet->padlen = (packet->long_pad ?
-                     SILC_PACKET_PADLEN_MAX(packet->truelen) :
-                     SILC_PACKET_PADLEN(packet->truelen, block_len));
+    if (packet->long_pad)
+      SILC_PACKET_PADLEN_MAX(packet->truelen, block_len, packet->padlen);
+    else
+      SILC_PACKET_PADLEN(packet->truelen, block_len, packet->padlen);
   }
 
   /* Now prepare the outgoing data buffer for packet sending and start
@@ -203,10 +201,10 @@ bool silc_packet_send_prepare(SilcSocketConnection sock,
                              SilcUInt32 data_len,
                              SilcHmac hmac,
                              const SilcBuffer packet)
-{ 
-  int totlen;
+{
+  SilcUInt32 totlen;
   unsigned char *oldptr;
-  int mac_len = hmac ? silc_hmac_len(hmac) : 0;
+  unsigned int mac_len = hmac ? silc_hmac_len(hmac) : 0;
 
   if (!packet)
     return FALSE;
@@ -253,9 +251,14 @@ bool silc_packet_send_prepare(SilcSocketConnection sock,
 
 ******************************************************************************/
 
-static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac, 
-                              SilcUInt32 sequence, SilcBuffer buffer, 
+static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
+                              SilcUInt32 sequence, SilcBuffer buffer,
                               bool normal);
+static bool silc_packet_check_mac(SilcHmac hmac,
+                                 const unsigned char *data,
+                                 SilcUInt32 data_len,
+                                 const unsigned char *packet_mac,
+                                 SilcUInt32 sequence);
 
 /* Receives packet from network and reads the data into connection's
    incoming data buffer. If the data was read directly this returns the
@@ -269,7 +272,7 @@ int silc_packet_receive(SilcSocketConnection sock)
   int ret;
 
   SILC_LOG_DEBUG(("Receiving packet from %s:%d [%s]", sock->hostname,
-                 sock->port, 
+                 sock->port,
                  (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                   sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                   sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
@@ -286,8 +289,8 @@ int silc_packet_receive(SilcSocketConnection sock)
    If more than one packet was received this calls the parser multiple
    times.  The parser callback will get context SilcPacketParserContext
    that includes the packet and the `parser_context' sent to this
-   function. 
-   
+   function.
+
    The `local_is_router' indicates whether the caller is router server
    in which case the receiving process of a certain packet types may
    be special.  Normal server and client must set it to FALSE.  The
@@ -302,11 +305,15 @@ bool silc_packet_receive_process(SilcSocketConnection sock,
                                 void *parser_context)
 {
   SilcPacketParserContext *parse_ctx;
-  int packetlen, paddedlen, mac_len = 0;
+  SilcUInt16 packetlen;
+  SilcUInt32 paddedlen, mac_len = 0, block_len;
+  int ret;
   bool cont = TRUE;
+  unsigned char tmp[SILC_PACKET_MIN_HEADER_LEN], *header;
+  unsigned char iv[SILC_CIPHER_MAX_IV_SIZE];
 
   /* Do not process for disconnected connection */
-  if (SILC_IS_DISCONNECTED(sock))
+  if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock))
     return TRUE;
 
   if (sock->inbuf->len < SILC_PACKET_MIN_HEADER_LEN)
@@ -323,79 +330,116 @@ bool silc_packet_receive_process(SilcSocketConnection sock,
       return TRUE;
     }
 
-    /* Decrypt first 16 bytes of the packet */
-    if (!SILC_IS_INBUF_PENDING(sock) && cipher)
-      silc_cipher_decrypt(cipher, sock->inbuf->data, sock->inbuf->data, 
-                         SILC_PACKET_MIN_HEADER_LEN, cipher->iv);
+    /* Decrypt first block of the packet to get the length field out */
+    if (cipher) {
+      block_len = silc_cipher_get_block_len(cipher);
+      memcpy(iv, silc_cipher_get_iv(cipher), block_len);
+      silc_cipher_decrypt(cipher, sock->inbuf->data, tmp, block_len, iv);
+      header = tmp;
+    } else {
+      block_len = SILC_PACKET_MIN_HEADER_LEN;
+      header = sock->inbuf->data;
+    }
 
     /* Get packet lenght and full packet length with padding */
-    SILC_PACKET_LENGTH(sock->inbuf, packetlen, paddedlen);
+    SILC_PACKET_LENGTH(header, packetlen, paddedlen);
 
     /* Sanity checks */
     if (packetlen < SILC_PACKET_MIN_LEN) {
-      SILC_LOG_DEBUG(("Received invalid packet, dropped"));
+      SILC_LOG_ERROR(("Received too short packet"));
+      memset(header, 0, sizeof(header));
       silc_buffer_clear(sock->inbuf);
       return FALSE;
     }
 
     if (sock->inbuf->len < paddedlen + mac_len) {
-      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest"
-                     "(%d < %d)", sock->inbuf->len, paddedlen + mac_len));
+      SILC_LOG_DEBUG(("Received partial packet, waiting for the rest "
+                     "(%d bytes)", paddedlen + mac_len - sock->inbuf->len));
       SILC_SET_INBUF_PENDING(sock);
+      memset(tmp, 0, sizeof(tmp));
       return TRUE;
     }
 
+    /* Check MAC of the packet */
+    if (!silc_packet_check_mac(hmac, sock->inbuf->data, paddedlen,
+                              sock->inbuf->data + paddedlen, sequence)) {
+      SILC_LOG_WARNING(("Packet MAC check failed %s:%d "
+                       "[%s type %d len %dB seq %d] [%s]",
+                       sock->hostname, sock->port,
+                       silc_get_packet_name(header[3]),
+                       header[3], paddedlen, sequence,
+                       (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                        sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                        sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                        "Router")));
+      memset(tmp, 0, sizeof(tmp));
+      silc_buffer_clear(sock->inbuf);
+      return FALSE;
+    }
+
     SILC_UNSET_INBUF_PENDING(sock);
     parse_ctx = silc_calloc(1, sizeof(*parse_ctx));
     if (!parse_ctx)
       return FALSE;
     parse_ctx->packet = silc_packet_context_alloc();
-    parse_ctx->packet->buffer = silc_buffer_alloc(paddedlen + mac_len);
-    parse_ctx->packet->type = sock->inbuf->data[3];
-    parse_ctx->packet->padlen = sock->inbuf->data[4];
+    parse_ctx->packet->buffer = silc_buffer_alloc_size(paddedlen);
+    parse_ctx->packet->type = (SilcPacketType)header[3];
+    parse_ctx->packet->padlen = (SilcUInt8)header[4];
     parse_ctx->packet->sequence = sequence++;
     parse_ctx->sock = sock;
     parse_ctx->context = parser_context;
 
-    silc_buffer_pull_tail(parse_ctx->packet->buffer, 
-                         SILC_BUFFER_END(parse_ctx->packet->buffer));
-    silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data, 
-                   paddedlen + mac_len);
-
-    SILC_LOG_HEXDUMP(("Incoming packet (%d) (%dB decrypted), len %d", 
-                     sequence - 1, SILC_PACKET_MIN_HEADER_LEN, 
-                     paddedlen + mac_len),
-                    sock->inbuf->data, paddedlen + mac_len);
-
     /* Check whether this is normal or special packet */
     if (local_is_router) {
-      if (sock->inbuf->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-         (sock->inbuf->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+      if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+         (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
        parse_ctx->normal = FALSE;
-      else if (sock->inbuf->data[3] != SILC_PACKET_CHANNEL_MESSAGE || 
-              (sock->inbuf->data[3] == SILC_PACKET_CHANNEL_MESSAGE &&
+      else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE ||
+              (header[3] == SILC_PACKET_CHANNEL_MESSAGE &&
                sock->type == SILC_SOCKET_TYPE_ROUTER))
        parse_ctx->normal = TRUE;
     } else {
-      if (sock->inbuf->data[3] == SILC_PACKET_PRIVATE_MESSAGE &&
-         (sock->inbuf->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
+      if (header[3] == SILC_PACKET_PRIVATE_MESSAGE &&
+         (header[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
        parse_ctx->normal = FALSE;
-      else if (sock->inbuf->data[3] != SILC_PACKET_CHANNEL_MESSAGE)
+      else if (header[3] != SILC_PACKET_CHANNEL_MESSAGE)
        parse_ctx->normal = TRUE;
     }
 
-    /* Decrypt rest of the packet */
-    if (cipher)
-      if (silc_packet_decrypt(cipher, hmac, parse_ctx->packet->sequence, 
-                             parse_ctx->packet->buffer, 
-                             parse_ctx->normal) == -1) {
-       SILC_LOG_WARNING(("Packet decryption failed %s:%d [%s]", 
+    SILC_LOG_HEXDUMP(("Incoming packet (%d) len %d",
+                     sequence - 1, paddedlen + mac_len),
+                    sock->inbuf->data, paddedlen + mac_len);
+
+    /* Put the decrypted part, and rest of the encrypted data, and decrypt */
+    silc_buffer_put(parse_ctx->packet->buffer, header, block_len);
+    silc_buffer_pull(parse_ctx->packet->buffer, block_len);
+    silc_buffer_put(parse_ctx->packet->buffer, sock->inbuf->data + block_len,
+                   paddedlen - block_len);
+    if (cipher) {
+      silc_cipher_set_iv(cipher, iv);
+      ret = silc_packet_decrypt(cipher, hmac, parse_ctx->packet->sequence,
+                               parse_ctx->packet->buffer,
+                               parse_ctx->normal);
+      if (ret < 0) {
+       SILC_LOG_WARNING(("Packet decryption failed %s:%d [%s] [%s]",
                          sock->hostname, sock->port,
+                         silc_get_packet_name(parse_ctx->packet->type),
                          (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
                           sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
                           sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
                           "Router")));
+       memset(tmp, 0, sizeof(tmp));
+       silc_packet_context_free(parse_ctx->packet);
+       silc_free(parse_ctx);
+       return FALSE;
       }
+    }
+    silc_buffer_push(parse_ctx->packet->buffer, block_len);
+
+    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d",
+                     parse_ctx->packet->buffer->len),
+                    parse_ctx->packet->buffer->data,
+                    parse_ctx->packet->buffer->len);
 
     /* Pull the packet from inbuf thus we'll get the next one
        in the inbuf. */
@@ -403,6 +447,14 @@ bool silc_packet_receive_process(SilcSocketConnection sock,
 
     /* Call the parser */
     cont = (*parser)(parse_ctx, parser_context);
+
+    /* See if socket disconnected while parsing the packet */
+    if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+      SILC_LOG_DEBUG(("Abandoning packet processing, socket disconnected"));
+      cont = FALSE;
+    }
+
+    memset(tmp, 0, sizeof(tmp));
   }
 
   if (cont == FALSE && sock->inbuf->len > 0)
@@ -413,171 +465,97 @@ bool silc_packet_receive_process(SilcSocketConnection sock,
   return TRUE;
 }
 
-/* Checks MAC in the packet. Returns TRUE if MAC is Ok. This is called
-   after packet has been totally decrypted and parsed. */
+/* Checks MAC in the packet. Returns TRUE if MAC is Ok. */
 
-static int silc_packet_check_mac(SilcHmac hmac, SilcBuffer buffer,
-                                SilcUInt32 sequence)
+static bool silc_packet_check_mac(SilcHmac hmac,
+                                 const unsigned char *data,
+                                 SilcUInt32 data_len,
+                                 const unsigned char *packet_mac,
+                                 SilcUInt32 sequence)
 {
   /* Check MAC */
   if (hmac) {
     unsigned char mac[32], psn[4];
     SilcUInt32 mac_len;
-    
+
     SILC_LOG_DEBUG(("Verifying MAC"));
 
     /* Compute HMAC of packet */
-
-    memset(mac, 0, sizeof(mac));
     silc_hmac_init(hmac);
     SILC_PUT32_MSB(sequence, psn);
     silc_hmac_update(hmac, psn, 4);
-    silc_hmac_update(hmac, buffer->data, buffer->len);
+    silc_hmac_update(hmac, data, data_len);
     silc_hmac_final(hmac, mac, &mac_len);
 
-    /* Compare the HMAC's (buffer->tail has the packet's HMAC) */
-    if (memcmp(buffer->tail, mac, mac_len)) {
+    /* Compare the MAC's */
+    if (memcmp(packet_mac, mac, mac_len)) {
       SILC_LOG_ERROR(("MAC failed"));
       return FALSE;
     }
-    
-    SILC_LOG_DEBUG(("MAC is Ok"));
-    memset(mac, 0, sizeof(mac));
-  }
-  
-  return TRUE;
-}
-
-/* Decrypts rest of the packet (after decrypting just the SILC header).
-   After calling this function the packet is ready to be parsed by calling 
-   silc_packet_parse. If everything goes without errors this returns TRUE,
-   if packet is malformed this returns FALSE. */
-
-static int silc_packet_decrypt_rest(SilcCipher cipher, SilcHmac hmac,
-                                   SilcBuffer buffer)
-{
-  if (cipher) {
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      if ((buffer->len - silc_hmac_len(hmac)) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, silc_hmac_len(hmac));
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-
-    SILC_LOG_DEBUG(("Decrypting rest of the packet"));
-
-    /* Decrypt rest of the packet */
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN);
-    silc_cipher_decrypt(cipher, buffer->data, buffer->data, buffer->len, 
-                       cipher->iv);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN);
-
-    SILC_LOG_HEXDUMP(("Fully decrypted packet, len %d", buffer->len),
-                    buffer->data, buffer->len);
-  }
-
-  return TRUE;
-}
 
-/* Decrypts rest of the SILC Packet header that has been decrypted partly
-   already. This decrypts the padding of the packet also. After calling 
-   this function the packet is ready to be parsed by calling function 
-   silc_packet_parse. This is used in special packet reception (protocol
-   defines the way of decrypting special packets). */
-
-static int silc_packet_decrypt_rest_special(SilcCipher cipher,
-                                           SilcHmac hmac,
-                                           SilcBuffer buffer)
-{
-  /* Decrypt rest of the header plus padding */
-  if (cipher) {
-    SilcUInt16 len;
-
-    /* Pull MAC from packet before decryption */
-    if (hmac) {
-      if ((buffer->len - silc_hmac_len(hmac)) > SILC_PACKET_MIN_LEN) {
-       silc_buffer_push_tail(buffer, silc_hmac_len(hmac));
-      } else {
-       SILC_LOG_DEBUG(("Bad MAC length in packet, packet dropped"));
-       return FALSE;
-      }
-    }
-  
-    SILC_LOG_DEBUG(("Decrypting rest of the header"));
-
-    /* padding length + src id len + dst id len + header length - 16
-       bytes already decrypted, gives the rest of the encrypted packet */
-    len = (((SilcUInt8)buffer->data[4] + (SilcUInt8)buffer->data[6] + 
-          (SilcUInt8)buffer->data[7] + SILC_PACKET_HEADER_LEN) -
-          SILC_PACKET_MIN_HEADER_LEN);
-
-    silc_buffer_pull(buffer, SILC_PACKET_MIN_HEADER_LEN);
-    if (len > buffer->len) {
-      SILC_LOG_DEBUG(("Garbage in header of packet, bad packet length, "
-                     "packet dropped"));
-      return FALSE;
-    }
-    silc_cipher_decrypt(cipher, buffer->data, buffer->data, len, cipher->iv);
-    silc_buffer_push(buffer, SILC_PACKET_MIN_HEADER_LEN);
-    SILC_LOG_HEXDUMP(("packet, len %d", buffer->len), 
-                    buffer->data, buffer->len);
+    SILC_LOG_DEBUG(("MAC is Ok"));
   }
 
   return TRUE;
 }
 
-/* Decrypts a packet. This assumes that typical SILC packet is the
-   packet to be decrypted and thus checks for normal and special SILC
-   packets and can handle both of them. This also computes and checks
-   the HMAC of the packet. If any other special or customized decryption
-   processing is required this function cannot be used. This returns
-   -1 on error, 0 when packet is normal packet and 1 when the packet
-   is special and requires special processing. 
-
-   The `check_packet' is a callback funtion that this function will 
-   call.  The callback relates to the checking whether the packet is
-   normal packet or special packet and how it should be processed.  If
-   the callback return TRUE the packet is normal and FALSE if the packet
-   is special and requires special procesing. */
+/* Decrypts SILC packet.  Handles both normal and special packet decryption.
+   Return 0 when packet is normal and 1 when it it special, -1 on error. */
 
 static int silc_packet_decrypt(SilcCipher cipher, SilcHmac hmac,
-                              SilcUInt32 sequence, SilcBuffer buffer, 
+                              SilcUInt32 sequence, SilcBuffer buffer,
                               bool normal)
 {
   /* If the packet type is not any special type lets decrypt rest
      of the packet here. */
   if (normal == TRUE) {
-    /* Normal packet, decrypt rest of the packet */
-    if (!silc_packet_decrypt_rest(cipher, hmac, buffer))
-      return -1;
-
-    /* Check MAC */
-    if (!silc_packet_check_mac(hmac, buffer, sequence))
-      return -1;
-
+    if (cipher) {
+      /* Decrypt rest of the packet */
+      SILC_LOG_DEBUG(("Decrypting the packet"));
+      if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data,
+                              buffer->len, NULL)) {
+       SILC_LOG_ERROR(("silc_cipher_decrypt failed"));
+       return -1;
+      }
+    }
     return 0;
-  } else {
-    /* Packet requires special handling, decrypt rest of the header.
-       This only decrypts. */
-    if (!silc_packet_decrypt_rest_special(cipher, hmac, buffer))
-      return -1;
 
-    /* Check MAC */
-    if (!silc_packet_check_mac(hmac, buffer, sequence))
-      return -1;
+  } else {
+    /* Decrypt rest of the header plus padding */
+    if (cipher) {
+      SilcUInt16 len;
+      SilcUInt32 block_len = silc_cipher_get_block_len(cipher);
+
+      SILC_LOG_DEBUG(("Decrypting the header"));
+
+      /* padding length + src id len + dst id len + header length - 16
+        bytes already decrypted, gives the rest of the encrypted packet */
+      silc_buffer_push(buffer, block_len);
+      len = (((SilcUInt8)buffer->data[4] + (SilcUInt8)buffer->data[6] +
+             (SilcUInt8)buffer->data[7] + SILC_PACKET_HEADER_LEN) -
+            block_len);
+      silc_buffer_pull(buffer, block_len);
+
+      if (len > buffer->len) {
+       SILC_LOG_ERROR(("Garbage in header of packet, bad packet length, "
+                       "packet dropped"));
+       return -1;
+      }
+      if (!silc_cipher_decrypt(cipher, buffer->data, buffer->data,
+                              len, NULL)) {
+       SILC_LOG_ERROR(("silc_cipher_decrypt failed"));
+       return -1;
+      }
+    }
 
     return 1;
   }
 }
 
 /* Parses the packet. This is called when a whole packet is ready to be
-   parsed. The buffer sent must be already decrypted before calling this 
-   function. The len argument must be the true length of the packet. This 
-   function returns the type of the packet. The data section of the 
+   parsed. The buffer sent must be already decrypted before calling this
+   function. The len argument must be the true length of the packet. This
+   function returns the type of the packet. The data section of the
    buffer is parsed, not head or tail sections. */
 
 SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
@@ -596,7 +574,7 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
   }
 
   /* Parse the buffer. This parses the SILC header of the packet. */
-  len = silc_buffer_unformat(buffer, 
+  len = silc_buffer_unformat(buffer,
                             SILC_STR_UI_SHORT(&ctx->truelen),
                             SILC_STR_UI_CHAR(&ctx->flags),
                             SILC_STR_UI_CHAR(&ctx->type),
@@ -617,7 +595,7 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
   }
 
   silc_buffer_pull(buffer, len);
-  ret = silc_buffer_unformat(buffer, 
+  ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
                                                        src_id_len),
                             SILC_STR_UI_CHAR(&dst_id_type),
@@ -629,7 +607,7 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
     return SILC_PACKET_NONE;
 
   if (src_id_type > SILC_ID_CHANNEL || dst_id_type > SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d",
+    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)",
                   src_id_type, dst_id_type));
     return SILC_PACKET_NONE;
   }
@@ -642,7 +620,7 @@ SilcPacketType silc_packet_parse(SilcPacketContext *ctx, SilcCipher cipher)
 
   silc_buffer_push(buffer, len);
 
-  SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
+  SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len),
                   ctx->buffer->data, ctx->buffer->len);
 
   /* Pull SILC header and padding from packet */
@@ -677,7 +655,7 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
   }
 
   /* Parse the buffer. This parses the SILC header of the packet. */
-  len = silc_buffer_unformat(buffer, 
+  len = silc_buffer_unformat(buffer,
                             SILC_STR_UI_SHORT(&ctx->truelen),
                             SILC_STR_UI_CHAR(&ctx->flags),
                             SILC_STR_UI_CHAR(&ctx->type),
@@ -700,7 +678,7 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
   }
 
   silc_buffer_pull(buffer, len);
-  ret = silc_buffer_unformat(buffer, 
+  ret = silc_buffer_unformat(buffer,
                             SILC_STR_UI_XNSTRING_ALLOC(&ctx->src_id,
                                                        src_id_len),
                             SILC_STR_UI_CHAR(&dst_id_type),
@@ -714,7 +692,7 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
   }
 
   if (src_id_type > SILC_ID_CHANNEL || dst_id_type > SILC_ID_CHANNEL) {
-    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d",
+    SILC_LOG_ERROR(("Bad ID types in packet (%d and %d)",
                   src_id_type, dst_id_type));
     return SILC_PACKET_NONE;
   }
@@ -727,7 +705,7 @@ SilcPacketType silc_packet_parse_special(SilcPacketContext *ctx,
 
   silc_buffer_push(buffer, len);
 
-  SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len), 
+  SILC_LOG_HEXDUMP(("parsed packet, len %d", ctx->buffer->len),
                   ctx->buffer->data, ctx->buffer->len);
 
   /* Pull SILC header and padding from packet */