Changed SILC code to use new SRT and SCT APIs.
[silc.git] / lib / silccore / silcmessage.c
index f1fbe932e7809829167371c26025b2fac51b253b..b1e3894d9179ac104e253f0c1851803eb8dd78e4 100644 (file)
@@ -1,10 +1,10 @@
 /*
 
-  silcmessage.c 
+  silcmessage.c
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2007 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
    private messages. */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcmessage.h"
 
-/******************************************************************************
-
-                               Message Payload
-
-******************************************************************************/
+/*************************** Types and definitions **************************/
 
 /* Calculates padding length for message payload */
 #define SILC_MESSAGE_PAD(__payloadlen) (16 - ((__payloadlen) % 16))
 /* Header length plus maximum padding length */
 #define SILC_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_MESSAGE_DATALEN(data_len, header_len)             \
-  ((data_len + SILC_MESSAGE_HLEN + header_len) >               \
-   SILC_PACKET_MAX_LEN ?                                       \
-   data_len - ((data_len + SILC_MESSAGE_HLEN + header_len) -   \
-              SILC_PACKET_MAX_LEN) : data_len)
-
-/* Message Payload structure. Contents of this structure is parsed
-   from SILC packets. */
-struct SilcMessagePayloadStruct {
+/* Maximum message length */
+#define SILC_MESSAGE_MAX_LEN SILC_PACKET_MAX_LEN - SILC_MESSAGE_HLEN - 16
+
+/* Payload encoding context */
+typedef struct {
+  SilcBuffer buffer;
+  SilcBuffer sign;
+  SilcStack stack;
+  SilcMessagePayloadEncoded encoded;
+  void *context;
   SilcMessageFlags flags;
-  SilcUInt16 data_len;
-  SilcUInt16 pad_len;
-  SilcUInt16 iv_len;
-  unsigned char *data;
-  unsigned char *pad;
+  SilcPublicKey public_key;
+  SilcPrivateKey private_key;
+  SilcRng rng;
+  SilcHash hash;
+  SilcCipher cipher;
+  SilcHmac hmac;
   unsigned char *iv;
-  unsigned char *mac;
-  /*SilcMessageSignedPayload sig;*/
-};
+  unsigned char *pk;
+  SilcUInt16 pk_len;
+  SilcUInt16 iv_len;
+  SilcUInt16 payload_len;
+  SilcID sid;
+  SilcID rid;
+} SilcMessageEncode;
+
 
-/* Decrypts the Message Payload. The `data' is the actual Message Payload */
+/************************* Static utility functions *************************/
 
-bool silc_message_payload_decrypt(unsigned char *data,
-                                 size_t data_len,
-                                 bool private_message,
-                                 bool static_key,
+static void
+silc_message_payload_encode_final(SilcBuffer buffer,
+                                 SilcMessageFlags flags,
                                  SilcCipher cipher,
                                  SilcHmac hmac,
-                                 bool check_mac)
+                                 unsigned char *iv,
+                                 SilcUInt32 iv_len,
+                                 SilcUInt32 payload_len,
+                                 SilcID *sender_id,
+                                 SilcID *receiver_id,
+                                 SilcStack stack,
+                                 SilcBuffer signature,
+                                 SilcMessagePayloadEncoded encoded,
+                                 void *context);
+
+/* Returns the data length that fits to the packet.  If data length is too
+   big it will be truncated to fit to the payload. */
+
+static inline
+SilcUInt32 silc_message_payload_datalen(SilcUInt32 data_len,
+                                       SilcUInt32 header_len,
+                                       SilcUInt32 flags,
+                                       SilcPublicKey public_key,
+                                       SilcPrivateKey private_key)
 {
-  SilcUInt32 mac_len = 0, iv_len = 0;
-  unsigned char *mac, mac2[32];
+  SilcUInt32 pklen = (flags & SILC_MESSAGE_FLAG_SIGNED && public_key ?
+                     silc_pkcs_public_key_get_len(public_key) : 0);
+  SilcUInt32 prlen = (flags & SILC_MESSAGE_FLAG_SIGNED ?
+                     silc_pkcs_private_key_get_len(private_key) / 8 : 0);
+  SilcUInt32 dlen = data_len + SILC_MESSAGE_HLEN + header_len + pklen + prlen;
+
+  if (silc_unlikely(dlen > SILC_MESSAGE_MAX_LEN))
+    data_len -= (dlen - SILC_MESSAGE_MAX_LEN);
+
+  return data_len;
+}
+
+/* Free signed payload */
+
+static void silc_message_signed_payload_free(SilcMessageSignedPayload sig)
+{
+  if (sig->sign_data) {
+    memset(sig->sign_data, 0, sig->sign_len);
+    silc_free(sig->sign_data);
+  }
+  silc_free(sig->pk_data);
+}
+
+/* Parses the SILC_MESSAGE_FLAG_SIGNED Payload */
+
+static SilcBool
+silc_message_signed_payload_parse(const unsigned char *data,
+                                 SilcUInt32 data_len,
+                                 SilcMessageSignedPayload sig)
+{
+  SilcBufferStruct buffer;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing SILC_MESSAGE_FLAG_SIGNED Payload"));
+
+  SILC_LOG_HEXDUMP(("sig payload"), (unsigned char *)data, data_len);
+
+  silc_buffer_set(&buffer, (unsigned char *)data, data_len);
+
+  /* Parse the payload */
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_SHORT(&sig->pk_len),
+                            SILC_STR_UI_SHORT(&sig->pk_type),
+                            SILC_STR_END);
+  if (ret == -1 || sig->pk_len > data_len - 4) {
+    SILC_LOG_DEBUG(("Malformed public key in SILC_MESSAGE_FLAG_SIGNED "
+                   "Payload"));
+    return FALSE;
+  }
+
+  silc_buffer_pull(&buffer, 4);
+  ret = silc_buffer_unformat(&buffer,
+                            SILC_STR_UI_XNSTRING_ALLOC(&sig->pk_data,
+                                                       sig->pk_len),
+                            SILC_STR_UI16_NSTRING_ALLOC(&sig->sign_data,
+                                                        &sig->sign_len),
+                            SILC_STR_END);
+  if (ret == -1 || sig->sign_len > silc_buffer_len(&buffer) -
+      sig->pk_len - 2) {
+    silc_message_signed_payload_free(sig);
+    SILC_LOG_DEBUG(("Malformed SILC_MESSAGE_FLAG_SIGNED Payload"));
+    return FALSE;
+  }
+  silc_buffer_push(&buffer, 4);
+
+  /* Signature must be provided */
+  if (sig->sign_len < 1)  {
+    SILC_LOG_DEBUG(("Malformed signature in SILC_MESSAGE_SIGNED_PAYLOAD "
+                   "Payload"));
+    silc_message_signed_payload_free(sig);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Encodes the data to be signed to SILC_MESSAGE_FLAG_SIGNED Payload */
+
+static SilcBuffer
+silc_message_signed_encode_data(SilcStack stack,
+                               const unsigned char *message_payload,
+                               SilcUInt32 message_payload_len,
+                               unsigned char *pk,
+                               SilcUInt32 pk_len, SilcUInt32 pk_type)
+{
+  SilcBuffer sign;
+
+  sign = silc_buffer_salloc_size(stack, message_payload_len + 4 + pk_len);
+  if (!sign)
+    return NULL;
+
+  silc_buffer_sformat(stack, sign,
+                     SILC_STR_DATA(message_payload, message_payload_len),
+                     SILC_STR_UI_SHORT(pk_len),
+                     SILC_STR_UI_SHORT(pk_type),
+                     SILC_STR_END);
+
+  if (pk && pk_len) {
+    silc_buffer_pull(sign, message_payload_len + 4);
+    silc_buffer_sformat(stack, sign,
+                       SILC_STR_UI_XNSTRING(pk, pk_len),
+                       SILC_STR_END);
+    silc_buffer_push(sign, message_payload_len + 4);
+  }
+
+  return sign;
+}
+
+/* Signature callback */
+
+void silc_message_signed_payload_encode_cb(SilcBool success,
+                                          const unsigned char *signature,
+                                          SilcUInt32 signature_len,
+                                          void *context)
+{
+  SilcMessageEncode *e = context;
+  SilcPKCSType pk_type;
+  SilcStack stack = e->stack;
+  SilcBuffer buffer, payload;
+  SilcMessageFlags flags;
+  SilcCipher cipher;
+  SilcHmac hmac;
+  unsigned char *iv;
+  SilcUInt32 iv_len, payload_len;
+  SilcID *sid, *rid;
+  SilcMessagePayloadEncoded encoded;
+  void *encoded_context;
+
+  if (!success) {
+    e->encoded(NULL, e->context);
+    silc_buffer_sfree(stack, e->sign);
+    silc_sfree(stack, e->pk);
+    silc_sfree(stack, e);
+    silc_stack_free(stack);
+    return;
+  }
+
+  pk_type = silc_pkcs_get_type(e->private_key);
+
+  /* Encode the SILC_MESSAGE_FLAG_SIGNED Payload */
+  buffer = silc_buffer_salloc_size(stack, 4 + e->pk_len + 2 + signature_len);
+  if (!buffer) {
+    e->encoded(NULL, e->context);
+    silc_buffer_sfree(stack, e->sign);
+    silc_sfree(stack, e->pk);
+    silc_sfree(stack, e);
+    silc_stack_free(stack);
+    return;
+  }
+
+  silc_buffer_sformat(stack, buffer,
+                     SILC_STR_UI_SHORT(e->pk_len),
+                     SILC_STR_UI_SHORT(pk_type),
+                     SILC_STR_END);
+
+  if (e->pk_len && e->pk) {
+    silc_buffer_pull(buffer, 4);
+    silc_buffer_sformat(stack, buffer,
+                       SILC_STR_DATA(e->pk, e->pk_len),
+                       SILC_STR_END);
+    silc_buffer_push(buffer, 4);
+  }
+
+  silc_buffer_pull(buffer, 4 + e->pk_len);
+  silc_buffer_sformat(stack, buffer,
+                     SILC_STR_UI_SHORT(signature_len),
+                     SILC_STR_DATA(signature, signature_len),
+                     SILC_STR_END);
+  silc_buffer_push(buffer, 4 + e->pk_len);
+
+  SILC_LOG_HEXDUMP(("SIG payload"), buffer->data, silc_buffer_len(buffer));
+
+  payload = e->buffer;
+  flags = e->flags;
+  cipher = e->cipher;
+  hmac = e->hmac;
+  iv = e->iv;
+  iv_len = e->iv_len;
+  payload_len = e->payload_len;
+  sid = &e->sid;
+  rid = &e->rid;
+  encoded = e->encoded;
+  encoded_context = e->context;
+
+  silc_sfree(stack, e->pk);
+  silc_buffer_sfree(stack, e->sign);
+  silc_sfree(stack, e);
+
+  /* Finalize message payload encoding */
+  silc_message_payload_encode_final(payload, flags, cipher, hmac,
+                                   iv, iv_len, payload_len,
+                                   sid, rid, stack, buffer,
+                                   encoded, encoded_context);
+}
+
+/* Encodes the SILC_MESSAGE_FLAG_SIGNED Payload and computes the digital
+   signature. */
+
+static SilcAsyncOperation
+silc_message_signed_payload_encode(SilcBuffer payload,
+                                  SilcMessageEncode *e)
+{
+  SilcAsyncOperation op;
+  SilcBuffer sign;
+  unsigned char *pk = NULL;
+  SilcUInt32 pk_len = 0;
+  SilcUInt16 pk_type;
+  SilcStack stack = e->stack;
+  SilcRng rng = e->rng;
+  SilcHash hash = e->hash;
+  SilcPublicKey public_key = e->public_key;
+  SilcPrivateKey private_key = e->private_key;
+  unsigned char *message_payload;
+  SilcUInt16 message_payload_len;
+
+  message_payload = payload->head;
+  message_payload_len = silc_buffer_headlen(payload);
+
+  if (!message_payload || !message_payload_len || !private_key || !hash) {
+    e->encoded(NULL, e->context);
+    silc_sfree(stack, e);
+    silc_stack_free(stack);
+    return NULL;
+  }
+
+  if (public_key) {
+    e->pk = pk = silc_pkcs_public_key_encode(stack, public_key, &pk_len);
+    if (!pk) {
+      e->encoded(NULL, e->context);
+      silc_sfree(stack, e);
+      silc_stack_free(stack);
+      return NULL;
+    }
+    e->pk_len = pk_len;
+  }
+  pk_type = silc_pkcs_get_type(private_key);
+
+  /* Encode the data to be signed */
+  e->sign = sign = silc_message_signed_encode_data(stack, message_payload,
+                                                  message_payload_len,
+                                                  pk, pk_len, pk_type);
+  if (!sign) {
+    e->encoded(NULL, e->context);
+    silc_sfree(stack, pk);
+    silc_sfree(stack, e);
+    silc_stack_free(stack);
+    return NULL;
+  }
+
+  /* Compute signature */
+  op = silc_pkcs_sign_async(private_key, sign->data, silc_buffer_len(sign),
+                           TRUE, hash, rng,
+                           silc_message_signed_payload_encode_cb, e);
+
+  return op;
+}
+
+/***************************** Payload parsing ******************************/
+
+/* Decrypts the Message Payload. The `data' is the actual Message Payload. */
+
+SilcBool silc_message_payload_decrypt(unsigned char *data,
+                                     size_t data_len,
+                                     SilcBool private_message,
+                                     SilcBool static_key,
+                                     SilcCipher cipher,
+                                     SilcHmac hmac,
+                                     unsigned char *sender_id,
+                                     SilcUInt32 sender_id_len,
+                                     unsigned char *receiver_id,
+                                     SilcUInt32 receiver_id_len,
+                                     SilcBool check_mac)
+{
+  SilcUInt32 mac_len, iv_len = 0, block_len;
+  SilcUInt16 len, totlen;
+  unsigned char mac[32], *ivp;
 
   mac_len = silc_hmac_len(hmac);
+  block_len = silc_cipher_get_block_len(cipher);
 
-  /* IV is present for channel messages and private messages when static
-     key (pre-shared key) is used. */
+  /* IV is present for all channel messages, and private messages when
+     static key (pre-shared key) is used. */
   if (!private_message || (private_message && static_key))
-    iv_len = silc_cipher_get_block_len(cipher);
+    iv_len = block_len;
 
-  if (data_len < mac_len)
+  if (silc_unlikely(data_len < (mac_len + iv_len + block_len)))
     return FALSE;
 
-  if (check_mac) {
-    /* Take the MAC */
-    mac = data + (data_len - mac_len);
-
+  if (silc_likely(check_mac)) {
     /* Check the MAC of the message */
     SILC_LOG_DEBUG(("Checking 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(("Message MAC does not match"));
-      return FALSE;
+    silc_hmac_update(hmac, sender_id, sender_id_len);
+    silc_hmac_update(hmac, receiver_id, receiver_id_len);
+    silc_hmac_final(hmac, mac, &mac_len);
+    if (silc_unlikely(memcmp(data + (data_len - mac_len), mac, mac_len))) {
+      /* Check for old style (version 1.2) message MAC.  Remove this check
+        at some point. */
+      silc_hmac_init(hmac);
+      silc_hmac_update(hmac, data, data_len - mac_len);
+      silc_hmac_final(hmac, mac, &mac_len);
+      if (silc_unlikely(memcmp(data + (data_len - mac_len), mac, mac_len))) {
+       SILC_LOG_DEBUG(("Message MAC does not match"));
+       return FALSE;
+      }
     }
     SILC_LOG_DEBUG(("MAC is Ok"));
   }
 
-  /* Decrypt the message */
-  silc_cipher_decrypt(cipher, data, data, data_len - iv_len - mac_len,
-                     (iv_len ? data + (data_len - iv_len - mac_len) :
-                      silc_cipher_get_iv(cipher)));
+  /* Decrypt first only one block to get the header and then rest of
+     the data.  This is done because there might be unencrypted data at
+     the end and we don't know the encrypted length yet. */
+
+  /* Get pointer to the IV */
+  ivp = (iv_len ? data + (data_len - iv_len - mac_len) :
+        silc_cipher_get_iv(cipher));
+
+  /* Decrypt block */
+  if (silc_unlikely(!silc_cipher_decrypt(cipher, data, data, block_len,
+                                        ivp))) {
+    SILC_ASSERT(FALSE);
+    return FALSE;
+  }
+
+  /* Get the payload length and decrypt rest */
+  totlen = 2;
+  SILC_GET16_MSB(len, data + totlen);
+  totlen += 2 + len;
+  if (silc_unlikely(totlen + iv_len + mac_len + 2 > data_len))
+    return FALSE;
+  totlen += 2;
+  if (totlen >= block_len)
+    if (silc_unlikely(!silc_cipher_decrypt(cipher, data + block_len,
+                                          data + block_len,
+                                          (totlen - block_len) +
+                                          SILC_MESSAGE_PAD(totlen), ivp))) {
+      SILC_ASSERT(FALSE);
+      return FALSE;
+    }
+
   return TRUE;
 }
 
 /* Parses Message Payload returning new payload structure.  This also
    decrypts it and checks the MAC. */
 
-SilcMessagePayload 
+SilcMessagePayload
 silc_message_payload_parse(unsigned char *payload,
                           SilcUInt32 payload_len,
-                          bool private_message,
-                          bool static_key,
+                          SilcBool private_message,
+                          SilcBool static_key,
                           SilcCipher cipher,
-                          SilcHmac hmac)
+                          SilcHmac hmac,
+                          unsigned char *sender_id,
+                          SilcUInt32 sender_id_len,
+                          unsigned char *receiver_id,
+                          SilcUInt32 receiver_id_len,
+                          SilcStack stack,
+                          SilcBool no_allocation,
+                          SilcMessagePayload message)
 {
   SilcBufferStruct buffer;
-  SilcMessagePayload newp;
+  SilcMessagePayload newp = NULL;
   int ret;
   SilcUInt32 mac_len = 0, iv_len = 0;
 
@@ -124,108 +457,252 @@ silc_message_payload_parse(unsigned char *payload,
   silc_buffer_set(&buffer, payload, payload_len);
 
   /* Decrypt the payload */
-  if (cipher) {
-    ret = silc_message_payload_decrypt(buffer.data, buffer.len,
+  if (silc_likely(cipher)) {
+    ret = silc_message_payload_decrypt(buffer.data, silc_buffer_len(&buffer),
                                       private_message, static_key,
-                                      cipher, hmac, TRUE);
-    if (ret == FALSE)
+                                      cipher, hmac, sender_id,
+                                      sender_id_len, receiver_id,
+                                      receiver_id_len, TRUE);
+    if (silc_unlikely(ret == FALSE))
       return NULL;
   }
 
-  if (hmac)
+  if (silc_likely(hmac))
     mac_len = silc_hmac_len(hmac);
 
-  /* IV is present for channel messages and private messages when static
-     key (pre-shared key) is used. */
+  /* IV is present for all channel messages, and private messages when
+     static key (pre-shared key) is used. */
   if (cipher && (!private_message || (private_message && static_key)))
     iv_len = silc_cipher_get_block_len(cipher);
 
-  newp = silc_calloc(1, sizeof(*newp));
-  if (!newp)
-    return NULL;
+  if (!message) {
+    newp = message = silc_calloc(1, sizeof(*newp));
+    if (silc_unlikely(!newp))
+      return NULL;
+  }
+  memset(message, 0, sizeof(*message));
+  message->allocated = (stack || no_allocation ? FALSE : TRUE);
 
   /* Parse the 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_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)
+  if (!no_allocation)
+    ret = silc_buffer_sunformat(stack, &buffer,
+                         SILC_STR_UI_SHORT(&message->flags),
+                         SILC_STR_UI16_NSTRING_ALLOC(&message->data,
+                                                     &message->data_len),
+                         SILC_STR_UI16_NSTRING_ALLOC(&message->pad,
+                                                     &message->pad_len),
+                         SILC_STR_END);
+  else
+    ret = silc_buffer_unformat(&buffer,
+                              SILC_STR_UI_SHORT(&message->flags),
+                              SILC_STR_UI16_NSTRING(&message->data,
+                                                    &message->data_len),
+                              SILC_STR_UI16_NSTRING(&message->pad,
+                                                    &message->pad_len),
+                              SILC_STR_END);
+  if (silc_unlikely(ret == -1))
     goto err;
 
-  if ((newp->data_len > buffer.len - 6 - mac_len - iv_len) ||
-      (newp->pad_len + newp->data_len > buffer.len - 6 - mac_len - iv_len)) {
+  if (silc_unlikely((message->data_len > silc_buffer_len(&buffer) -
+                    6 - mac_len - iv_len) ||
+                   (message->pad_len + message->data_len >
+                    silc_buffer_len(&buffer) - 6 - mac_len - iv_len))) {
     SILC_LOG_ERROR(("Incorrect Message Payload in packet"));
     goto err;
   }
 
-  newp->iv_len = iv_len;
+  /* Parse Signed Message Payload if provided */
+  if (message->flags & SILC_MESSAGE_FLAG_SIGNED &&
+      message->data_len + message->pad_len + 6 + mac_len +
+      iv_len < silc_buffer_len(&buffer)) {
+    if (!silc_message_signed_payload_parse(buffer.data + 6 +
+                                          message->data_len +
+                                          message->pad_len,
+                                          silc_buffer_len(&buffer) -
+                                          iv_len - mac_len - 6 -
+                                          message->data_len -
+                                          message->pad_len,
+                                          &message->sig))
+      goto err;
+  }
+
+  /* Parse MAC from the payload */
+  if (mac_len)
+    message->mac = buffer.data + (silc_buffer_len(&buffer) - mac_len);
 
   return newp;
 
  err:
-  silc_message_payload_free(newp);
+  if (newp)
+    silc_message_payload_free(newp);
   return NULL;
 }
 
+
+/***************************** Payload encoding *****************************/
+
 /* This function is used to encrypt the Messsage Payload which is
    the `data' and `data_len'.  This is used internally by the 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. */
-
-bool silc_message_payload_encrypt(unsigned char *data,
-                                 SilcUInt32 data_len,
-                                 unsigned char *iv,
-                                 SilcUInt32 iv_len,
-                                 SilcCipher cipher,
-                                 SilcHmac hmac)
+   Payload encoding routines but application may call this too if needed.
+   The `true_len' is the data length which is used to create MAC out of. */
+
+SilcBool silc_message_payload_encrypt(unsigned char *data,
+                                     SilcUInt32 data_len,
+                                     SilcUInt32 true_len,
+                                     unsigned char *iv,
+                                     SilcID *sender_id,
+                                     SilcID *receiver_id,
+                                     SilcCipher cipher,
+                                     SilcHmac hmac)
 {
-  unsigned char mac[32];
-  SilcUInt32 mac_len;
-  SilcBufferStruct buf;
+  unsigned char sid[32], rid[32];
+  SilcUInt32 sid_len = 0, rid_len = 0;
 
-  /* Encrypt payload of the packet. If the IV is added to packet do
-     not encrypt that. */
-  silc_cipher_encrypt(cipher, data, data, data_len - iv_len,
-                     iv_len ? iv : silc_cipher_get_iv(cipher));
+  /* Encrypt payload of the packet */
+  if (silc_unlikely(!silc_cipher_encrypt(cipher, data, data, data_len, iv)))
+    return FALSE;
+
+  /* Encode IDs */
+  silc_id_id2str(&sender_id->u.client_id, SILC_ID_CLIENT, sid, sizeof(sid),
+                &sid_len);
+  if (receiver_id->type == SILC_ID_CLIENT)
+    silc_id_id2str(&receiver_id->u.client_id, SILC_ID_CLIENT, rid,
+                  sizeof(rid), &rid_len);
+  else if (receiver_id->type == SILC_ID_CHANNEL)
+    silc_id_id2str(&receiver_id->u.channel_id, SILC_ID_CHANNEL, rid,
+                  sizeof(rid), &rid_len);
 
   /* Compute the MAC of the encrypted 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);
+  silc_hmac_update(hmac, data, true_len);
+  silc_hmac_update(hmac, sid, sid_len);
+  silc_hmac_update(hmac, rid, rid_len);
+  silc_hmac_final(hmac, data + true_len, NULL);
 
   return TRUE;
 }
 
+/* Encrypt message payload */
+
+static int silc_message_payload_encode_encrypt(SilcStack stack,
+                                              SilcBuffer buffer,
+                                              void *value, void *context)
+{
+  SilcMessageEncode *e = context;
+  SilcUInt32 mac_len;
+
+  if (!e->cipher || !e->hmac)
+    return 0;
+
+  mac_len = silc_hmac_len(e->hmac);
+  if (silc_unlikely(!silc_buffer_enlarge(buffer, mac_len)))
+    return -1;
+
+  if (silc_unlikely(!silc_message_payload_encrypt(buffer->head,
+                                                 e->payload_len,
+                                                 silc_buffer_headlen(buffer),
+                                                 e->iv, &e->sid, &e->rid,
+                                                 e->cipher, e->hmac)))
+    return -1;
+
+  return mac_len;
+}
+
+/* Finalize message payload encoding */
+
+static void
+silc_message_payload_encode_final(SilcBuffer buffer,
+                                 SilcMessageFlags flags,
+                                 SilcCipher cipher,
+                                 SilcHmac hmac,
+                                 unsigned char *iv,
+                                 SilcUInt32 iv_len,
+                                 SilcUInt32 payload_len,
+                                 SilcID *sender_id,
+                                 SilcID *receiver_id,
+                                 SilcStack stack,
+                                 SilcBuffer signature,
+                                 SilcMessagePayloadEncoded encoded,
+                                 void *context)
+{
+  SilcMessageEncode e;
+
+  e.flags = flags;
+  e.cipher = cipher;
+  e.hmac = hmac;
+  e.sid = *sender_id;
+  e.rid = *receiver_id;
+  e.iv = iv;
+  e.payload_len = payload_len;
+
+  /* Encrypt */
+  if (silc_buffer_format(buffer,
+                        SILC_STR_DATA(signature ?
+                                      silc_buffer_data(signature) : NULL,
+                                      signature ?
+                                      silc_buffer_len(signature) : 0),
+                        SILC_STR_DATA(iv, iv_len),
+                        SILC_STR_FUNC(silc_message_payload_encode_encrypt,
+                                      NULL, &e),
+                        SILC_STR_END) < 0) {
+    silc_buffer_sfree(stack, buffer);
+    encoded(NULL, context);
+    return;
+  }
+
+  /* Deliver message payload */
+  silc_buffer_start(buffer);
+  encoded(buffer, context);
+
+  silc_buffer_sfree(stack, buffer);
+  silc_buffer_sfree(stack, signature);
+  silc_stack_free(stack);
+}
+
 /* Encodes Message Payload into a buffer and returns it. */
 
-SilcBuffer silc_message_payload_encode(SilcMessageFlags flags,
-                                      const unsigned char *data,
-                                      SilcUInt32 data_len,
-                                      bool generate_iv,
-                                      bool private_message,
-                                      SilcCipher cipher,
-                                      SilcHmac hmac,
-                                      SilcRng rng)
+SilcAsyncOperation
+silc_message_payload_encode(SilcMessageFlags flags,
+                           const unsigned char *data,
+                           SilcUInt32 data_len,
+                           SilcBool generate_iv,
+                           SilcBool private_message,
+                           SilcCipher cipher,
+                           SilcHmac hmac,
+                           SilcRng rng,
+                           SilcPublicKey public_key,
+                           SilcPrivateKey private_key,
+                           SilcHash hash,
+                           SilcID *sender_id,
+                           SilcID *receiver_id,
+                           SilcStack stack,
+                           SilcMessagePayloadEncoded encoded,
+                           void *context)
 {
-  int i;
-  SilcBuffer buffer;
-  SilcUInt32 len, pad_len = 0, mac_len = 0, iv_len = 0;
+  SilcUInt32 pad_len = 0, mac_len = 0, iv_len = 0;
   unsigned char pad[16], iv[SILC_CIPHER_MAX_IV_SIZE];
+  SilcBuffer buffer;
+  int i;
 
   SILC_LOG_DEBUG(("Encoding Message Payload"));
 
-  if (!data_len)
+  if (silc_unlikely(!data_len)) {
+    encoded(NULL, context);
+    return NULL;
+  }
+  if (silc_unlikely(!private_message && (!cipher || !hmac))) {
+    encoded(NULL, context);
     return NULL;
+  }
+
+  stack = silc_stack_alloc(0, stack ? stack : silc_crypto_stack());
+
+  buffer = silc_buffer_salloc(stack, 0);
+  if (silc_unlikely(!buffer)) {
+    encoded(NULL, context);
+    silc_stack_free(stack);
+    return NULL;
+  }
 
   /* For channel messages IV is always generated */
   if (!private_message && !generate_iv)
@@ -243,18 +720,12 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags,
 
   if (hmac)
     mac_len = silc_hmac_len(hmac);
-  data_len = SILC_MESSAGE_DATALEN(data_len, mac_len + iv_len);
+  data_len = silc_message_payload_datalen(data_len, mac_len + iv_len, flags,
+                                         public_key, private_key);
 
   /* Calculate length of padding. IV is not included into the calculation
      since it is not encrypted. */
-  len = 6 + data_len;
-  pad_len = SILC_MESSAGE_PAD(len);
-
-  /* Allocate payload buffer */
-  len += pad_len + iv_len + mac_len;
-  buffer = silc_buffer_alloc(len);
-  if (!buffer)
-    return NULL;
+  pad_len = SILC_MESSAGE_PAD(6 + data_len);
 
   /* Generate padding */
   if (cipher) {
@@ -266,41 +737,73 @@ SilcBuffer silc_message_payload_encode(SilcMessageFlags flags,
   }
 
   /* Encode the Message Payload */
-  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_UI_XNSTRING(iv, iv_len),
-                    SILC_STR_END);
-
-  memset(pad, 0, sizeof(pad));
-
-  /* Now encrypt the Message Payload */
-  if (cipher) {
-    if (!silc_message_payload_encrypt(buffer->data, buffer->len,
-                                     iv, iv_len, cipher, hmac)) {
-      silc_buffer_free(buffer);
+  if (silc_buffer_format(buffer,
+                        SILC_STR_ADVANCE,
+                        SILC_STR_UI_SHORT(flags),
+                        SILC_STR_UI_SHORT(data_len),
+                        SILC_STR_DATA(data, data_len),
+                        SILC_STR_UI_SHORT(pad_len),
+                        SILC_STR_DATA(pad, pad_len),
+                        SILC_STR_END) < 0) {
+    silc_buffer_sfree(stack, buffer);
+    encoded(NULL, context);
+    silc_stack_free(stack);
+    return NULL;
+  }
+
+  if (flags & SILC_MESSAGE_FLAG_SIGNED) {
+    SilcMessageEncode *e = silc_scalloc(stack, 1, sizeof(*e));
+    if (!e) {
+      silc_buffer_sfree(stack, buffer);
+      encoded(NULL, context);
+      silc_stack_free(stack);
       return NULL;
     }
+
+    e->stack = stack;
+    e->buffer = buffer;
+    e->flags = flags;
+    e->public_key = public_key;
+    e->private_key = private_key;
+    e->rng = rng;
+    e->hash = hash;
+    e->cipher = cipher;
+    e->hmac = hmac;
+    e->sid = *sender_id;
+    e->rid = *receiver_id;
+    e->iv = iv_len ? iv : NULL;
+    e->iv_len = iv_len;
+    e->payload_len = 6 + data_len + pad_len;
+    e->encoded = encoded;
+    e->context = context;
+
+    /* Compute signature */
+    return silc_message_signed_payload_encode(buffer, e);
   }
-  silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
 
-  return buffer;
+  /* Finalize */
+  silc_message_payload_encode_final(buffer, flags, cipher, hmac,
+                                   iv_len ? iv : NULL, iv_len,
+                                   6 + data_len + pad_len,
+                                   sender_id, receiver_id, stack, NULL,
+                                   encoded, context);
+  return NULL;
 }
 
 /* Free's Message Payload */
 
 void silc_message_payload_free(SilcMessagePayload payload)
 {
+  silc_message_signed_payload_free(&payload->sig);
   if (payload->data) {
     memset(payload->data, 0, payload->data_len);
-    silc_free(payload->data);
+    if (payload->allocated)
+      silc_free(payload->data);
+  }
+  if (payload->allocated) {
+    silc_free(payload->pad);
+    silc_free(payload);
   }
-  silc_free(payload->pad);
-  silc_free(payload);
 }
 
 /* Return flags */
@@ -327,299 +830,85 @@ unsigned char *silc_message_get_mac(SilcMessagePayload payload)
   return payload->mac;
 }
 
-/* Return IV. The caller knows the length of the IV */
-
-unsigned char *silc_message_get_iv(SilcMessagePayload payload)
-{
-  return payload->iv;
-}
-
-/******************************************************************************
-
-                     SILC_MESSAGE_FLAG_SIGNED Payload
-
-******************************************************************************/
-
-/* The SILC_MESSAGE_FLAG_SIGNED Payload */
-struct SilcMessageSignedPayloadStruct {
-  SilcUInt16 pk_len;
-  SilcUInt16 pk_type;
-  SilcUInt16 sign_len;
-  unsigned char *pk_data;
-  unsigned char *sign_data;
-};
-
-/* Encodes the data to be signed to SILC_MESSAGE_FLAG_SIGNED Payload */
-
-static SilcBuffer
-silc_message_signed_encode_data(const unsigned char *message_payload,
-                               SilcUInt32 message_payload_len,
-                               unsigned char *pk,
-                               SilcUInt32 pk_len, SilcUInt32 pk_type)
-{
-  SilcBuffer sign;
-
-  sign = silc_buffer_alloc_size(message_payload_len + 4 + pk_len);
-  if (!sign)
-    return NULL;
-
-  silc_buffer_format(sign,
-                    SILC_STR_UI_XNSTRING(message_payload,
-                                         message_payload_len),
-                    SILC_STR_UI_SHORT(pk_len),
-                    SILC_STR_UI_SHORT(pk_type),
-                    SILC_STR_END);
-
-  if (pk && pk_len) {
-    silc_buffer_pull(sign, message_payload_len + 4);
-    silc_buffer_format(sign,
-                      SILC_STR_UI_XNSTRING(pk, pk_len),
-                      SILC_STR_END);
-    silc_buffer_push(sign, message_payload_len + 4);
-  }
-
-  return sign;
-}
-
-/* Parses the SILC_MESSAGE_FLAG_SIGNED Payload */
-
-SilcMessageSignedPayload
-silc_message_signed_payload_parse(const unsigned char *data,
-                                 SilcUInt32 data_len)
-{
-  SilcMessageSignedPayload sig;
-  SilcBufferStruct buffer;
-  int ret;
-
-  SILC_LOG_DEBUG(("Parsing SILC_MESSAGE_FLAG_SIGNED Payload"));
-
-  silc_buffer_set(&buffer, (unsigned char *)data, data_len);
-  sig = silc_calloc(1, sizeof(*sig));
-  if (!sig)
-    return NULL;
-
-  /* Parse the payload */
-  ret = silc_buffer_unformat(&buffer,
-                            SILC_STR_UI_SHORT(&sig->pk_len),
-                            SILC_STR_UI_SHORT(&sig->pk_type),
-                            SILC_STR_END);
-  if (ret == -1 || sig->pk_len > data_len - 4) {
-    silc_message_signed_payload_free(sig);
-    return NULL;
-  }
-
-  silc_buffer_pull(&buffer, 4);
-  ret = silc_buffer_unformat(&buffer,
-                            SILC_STR_UI_XNSTRING_ALLOC(&sig->pk_data,
-                                                       sig->pk_len),
-                            SILC_STR_UI16_NSTRING_ALLOC(&sig->sign_data,
-                                                        &sig->sign_len),
-                            SILC_STR_END);
-  if (ret == -1) {
-    silc_message_signed_payload_free(sig);
-    return NULL;
-  }
-  silc_buffer_push(&buffer, 4);
-
-  /* Signature must be provided */
-  if (sig->sign_len < 1)  {
-    silc_message_signed_payload_free(sig);
-    return NULL;
-  }
-
-  return sig;
-}
-
-/* Encodes the SILC_MESSAGE_FLAG_SIGNED Payload and computes the digital
-   signature. */
+/* Verify the signature in SILC_MESSAGE_FLAG_SIGNED Payload */
 
-SilcBuffer
-silc_message_signed_payload_encode(const unsigned char *message_payload,
-                                  SilcUInt32 message_payload_len,
-                                  SilcPublicKey public_key,
-                                  SilcPrivateKey private_key,
-                                  SilcHash hash,
-                                  bool include_public_key)
+SilcAsyncOperation
+silc_message_signed_verify(SilcMessagePayload message,
+                          SilcPublicKey remote_public_key,
+                          SilcHash hash,
+                          SilcAuthResultCb result,
+                          void *context)
 {
-  SilcBuffer buffer, sign;
-  SilcPKCS pkcs;
-  unsigned char auth_data[2048];
-  SilcUInt32 auth_len;
-  unsigned char *pk = NULL;
-  SilcUInt32 pk_len = 0;
-  SilcUInt16 pk_type;
-
-  if (!message_payload || !message_payload_len || !private_key || !hash)
-    return NULL;
-  if (include_public_key && !public_key)
-    return NULL;
-
-  if (include_public_key)
-    pk = silc_pkcs_public_key_encode(public_key, &pk_len);
-
-  /* Now we support only SILC style public key */
-  pk_type = SILC_SKE_PK_TYPE_SILC;
-
-  /* Encode the data to be signed */
-  sign = silc_message_signed_encode_data(message_payload,
-                                        message_payload_len,
-                                        pk, pk_len, pk_type);
-  if (!sign) {
-    silc_free(pk);
+  SilcAsyncOperation op;
+  SilcBuffer sign, tmp;
+  SilcStack stack = NULL;
+  SilcMessageSignedPayload sig = &message->sig;
+
+  if (!(message->flags & SILC_MESSAGE_FLAG_SIGNED) ||
+      !sig->sign_len || !remote_public_key || !hash) {
+    result(FALSE, context);
     return NULL;
   }
 
-  /* Sign the buffer */
-
-  /* Allocate PKCS object */
-  if (!silc_pkcs_alloc(private_key->name, &pkcs)) {
-    silc_buffer_clear(sign);
-    silc_buffer_free(sign);
-    silc_free(pk);
-    return NULL;
-  }
-  silc_pkcs_private_key_set(pkcs, private_key);
-
-  /* Compute the hash and the signature. */
-  if (silc_pkcs_get_key_len(pkcs) / 8 > sizeof(auth_data) - 1 ||
-      !silc_pkcs_sign_with_hash(pkcs, hash, sign->data, sign->len, auth_data,
-                               &auth_len)) {
-    silc_buffer_clear(sign);
-    silc_buffer_free(sign);
-    silc_pkcs_free(pkcs);
-    silc_free(pk);
-    return NULL;
-  }
-
-  /* Encode the SILC_MESSAGE_FLAG_SIGNED Payload */
-
-  buffer = silc_buffer_alloc_size(4 + pk_len + 2 + auth_len);
-  if (!buffer) {
-    silc_buffer_clear(sign);
-    silc_buffer_free(sign);
-    silc_pkcs_free(pkcs);
-    memset(auth_data, 0, sizeof(auth_data));
-    silc_free(pk);
-    return NULL;
-  }
-
-  silc_buffer_format(sign,
-                    SILC_STR_UI_SHORT(pk_len),
-                    SILC_STR_UI_SHORT(pk_type),
-                    SILC_STR_END);
-
-  if (pk_len && pk) {
-    silc_buffer_pull(sign, 4);
-    silc_buffer_format(sign,
-                      SILC_STR_UI_XNSTRING(pk, pk_len),
-                      SILC_STR_END);
-    silc_buffer_push(sign, 4);
-  }
-
-  silc_buffer_pull(sign, 4 + pk_len);
-  silc_buffer_format(sign,
-                    SILC_STR_UI_SHORT(auth_len),
-                    SILC_STR_UI_XNSTRING(auth_data, auth_len),
-                    SILC_STR_END);
-  silc_buffer_push(sign, 4 + pk_len);
-
-  memset(auth_data, 0, sizeof(auth_data));
-  silc_pkcs_free(pkcs);
-  silc_buffer_clear(sign);
-  silc_buffer_free(sign);
-  silc_free(pk);
-
-  return buffer;
-}
-
-/* Free the payload */
-
-void silc_message_signed_payload_free(SilcMessageSignedPayload sig)
-{
-  if (sig) {
-    memset(sig->sign_data, 0, sig->sign_len);
-    silc_free(sig->sign_data);
-    silc_free(sig->pk_data);
-    silc_free(sig);
-  }
-}
-
-/* Verify the signature in SILC_MESSAGE_FLAG_SIGNED Payload */
-
-int silc_message_signed_verify(SilcMessageSignedPayload sig,
-                              SilcMessagePayload message,
-                              SilcPublicKey remote_public_key,
-                              SilcHash hash)
-{
-  int ret = SILC_AUTH_FAILED;
-  SilcBuffer sign;
-  SilcPKCS pkcs;
-  SilcBuffer tmp;
-  
-  if (!sig || !remote_public_key || !hash)
-    return ret;
+  if (silc_crypto_stack())
+    stack = silc_stack_alloc(0, silc_crypto_stack());
 
   /* Generate the signature verification data, the Message Payload */
-  tmp = silc_buffer_alloc_size(6 + message->data_len + message->pad_len +
-                              message->iv_len);
-  silc_buffer_format(tmp,
-                    SILC_STR_UI_SHORT(message->flags),
-                    SILC_STR_UI_SHORT(message->data_len),
-                    SILC_STR_UI_XNSTRING(message->data, message->data_len),
-                    SILC_STR_UI_SHORT(message->pad_len),
-                    SILC_STR_UI_XNSTRING(message->pad, message->pad_len),
-                    SILC_STR_UI_XNSTRING(message->iv, message->iv_len),
-                    SILC_STR_END);
-  sign = silc_message_signed_encode_data(tmp->data, tmp->len,
+  tmp = silc_buffer_salloc_size(stack,
+                               6 + message->data_len + message->pad_len);
+  silc_buffer_sformat(stack, tmp,
+                     SILC_STR_UI_SHORT(message->flags),
+                     SILC_STR_UI_SHORT(message->data_len),
+                     SILC_STR_DATA(message->data, message->data_len),
+                     SILC_STR_UI_SHORT(message->pad_len),
+                     SILC_STR_DATA(message->pad, message->pad_len),
+                     SILC_STR_END);
+  sign = silc_message_signed_encode_data(stack, tmp->data, silc_buffer_len(tmp),
                                         sig->pk_data, sig->pk_len,
                                         sig->pk_type);
   silc_buffer_clear(tmp);
-  silc_buffer_free(tmp);
-  
-  if (!sign)
-    return ret;
-  
-  /* Allocate PKCS object */
-  if (!silc_pkcs_alloc(remote_public_key->name, &pkcs)) {
-    silc_buffer_clear(sign);
-    silc_buffer_free(sign);
-    return ret;
-  }
-  silc_pkcs_public_key_set(pkcs, remote_public_key);
+  silc_buffer_sfree(stack, tmp);
 
-  /* Verify the authentication data */
-  if (!silc_pkcs_verify_with_hash(pkcs, hash, sig->sign_data,
-                                 sig->sign_len,
-                                 sign->data, sign->len)) {
-
-    silc_buffer_clear(sign);
-    silc_buffer_free(sign);
-    silc_pkcs_free(pkcs);
-    SILC_LOG_DEBUG(("Signature verification failed"));
-    return ret;
+  if (!sign) {
+    result(FALSE, context);
+    silc_stack_free(stack);
+    return NULL;
   }
 
-  ret = SILC_AUTH_OK;
+  /* Verify the authentication data */
+  op = silc_pkcs_verify_async(remote_public_key, sig->sign_data,
+                             sig->sign_len,
+                             silc_buffer_data(sign), silc_buffer_len(sign),
+                             TRUE, hash, result, context);
 
   silc_buffer_clear(sign);
-  silc_buffer_free(sign);
-  silc_pkcs_free(pkcs);
-
-  SILC_LOG_DEBUG(("Signature verification successful"));
+  silc_buffer_sfree(stack, sign);
+  silc_stack_free(stack);
 
-  return ret;
+  return op;
 }
 
 /* Return the public key from the payload */
 
 SilcPublicKey
-silc_message_signed_get_public_key(SilcMessageSignedPayload sig)
+silc_message_signed_get_public_key(SilcMessagePayload payload,
+                                  const unsigned char **pk_data,
+                                  SilcUInt32 *pk_data_len)
 {
   SilcPublicKey pk;
+  SilcMessageSignedPayload sig = &payload->sig;
 
-  if (!sig->pk_data || !silc_pkcs_public_key_decode(sig->pk_data,
-                                                   sig->pk_len, &pk))
+  if (!sig->pk_data)
     return NULL;
 
+  if (!silc_pkcs_public_key_alloc(sig->pk_type, sig->pk_data,
+                                 sig->pk_len, &pk))
+    return NULL;
+
+  if (pk_data)
+    *pk_data = sig->pk_data;
+  if (pk_data_len)
+    *pk_data_len = sig->pk_len;
+
   return pk;
 }