Merged from silc_1_0_branch.
[silc.git] / lib / silcclient / client_prvmsg.c
index cc0155b225c833c65a068204096b06b806fbf731..36d7bbe43a7eaffd76fe308eb7980b50da6a9867 100644 (file)
@@ -1,16 +1,15 @@
 /*
 
 /*
 
-  client_prvmsg.c
+  client_prvmsg.c 
 
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
 
-  Copyright (C) 1997 - 2001 Pekka Riikonen
+  Copyright (C) 1997 - 2002 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
 
   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
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-  
+  the Free Software Foundation; version 2 of the License.
+
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
@@ -21,7 +20,8 @@
 /* This file includes the private message sending and receiving routines
    and private message key handling routines. */
 
 /* This file includes the private message sending and receiving routines
    and private message key handling routines. */
 
-#include "clientlibincludes.h"
+#include "silcincludes.h"
+#include "silcclient.h"
 #include "client_internal.h"
 
 /* Sends private message to remote client. If private message key has
 #include "client_internal.h"
 
 /* Sends private message to remote client. If private message key has
@@ -37,21 +37,29 @@ void silc_client_send_private_message(SilcClient client,
                                      SilcClientEntry client_entry,
                                      SilcMessageFlags flags,
                                      unsigned char *data, 
                                      SilcClientEntry client_entry,
                                      SilcMessageFlags flags,
                                      unsigned char *data, 
-                                     uint32 data_len, 
-                                     int force_send)
+                                     SilcUInt32 data_len, 
+                                     bool force_send)
 {
 {
-  SilcSocketConnection sock = conn->sock;
+  SilcSocketConnection sock;
   SilcBuffer buffer;
   SilcPacketContext packetdata;
   SilcBuffer buffer;
   SilcPacketContext packetdata;
+  const SilcBufferStruct packet;
   SilcCipher cipher;
   SilcHmac hmac;
   SilcCipher cipher;
   SilcHmac hmac;
+  int block_len;
 
 
+  assert(client && conn && client_entry);
+  sock = conn->sock;
   SILC_LOG_DEBUG(("Sending private message"));
 
   /* Encode private message payload */
   SILC_LOG_DEBUG(("Sending private message"));
 
   /* Encode private message payload */
-  buffer = silc_private_message_payload_encode(flags,
-                                              data_len, data,
-                                              client_entry->send_key);
+  buffer = silc_message_payload_encode(flags, data, data_len,
+                                      !client_entry->send_key ? FALSE :
+                                      !client_entry->generated,
+                                      TRUE, client_entry->send_key,
+                                      client_entry->hmac_send,
+                                      client->rng, NULL, client->private_key,
+                                      client->sha1hash);
 
   /* If we don't have private message specific key then private messages
      are just as any normal packet thus call normal packet sending.  If
 
   /* If we don't have private message specific key then private messages
      are just as any normal packet thus call normal packet sending.  If
@@ -67,51 +75,55 @@ void silc_client_send_private_message(SilcClient client,
   /* We have private message specific key */
 
   /* Get data used in the encryption */
   /* We have private message specific key */
 
   /* Get data used in the encryption */
-  cipher = client_entry->send_key;
-  hmac = conn->hmac_send;
+  cipher = conn->internal->send_key;
+  hmac = conn->internal->hmac_send;
+  block_len = silc_cipher_get_block_len(cipher);
 
   /* Set the packet context pointers. */
 
   /* Set the packet context pointers. */
+  data = buffer->data;
+  data_len = buffer->len;
   packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
   packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
   packetdata.src_id = conn->local_id_data;
   packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
   packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
   packetdata.src_id = conn->local_id_data;
-  packetdata.src_id_len = SILC_ID_CLIENT_LEN;
+  packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
   packetdata.src_id_type = SILC_ID_CLIENT;
   packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
   packetdata.src_id_type = SILC_ID_CLIENT;
   packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
-  packetdata.dst_id_len = SILC_ID_CLIENT_LEN;
+  packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
   packetdata.dst_id_type = SILC_ID_CLIENT;
   packetdata.dst_id_type = SILC_ID_CLIENT;
-  packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN + 
+  data_len = SILC_PACKET_DATALEN(data_len, SILC_PACKET_HEADER_LEN +
+                                packetdata.src_id_len +
+                                packetdata.dst_id_len);
+  packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
     packetdata.src_id_len + packetdata.dst_id_len;
-  packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
-                                         packetdata.src_id_len +
-                                         packetdata.dst_id_len));
-
-  /* Prepare outgoing data buffer for packet sending */
-  silc_packet_send_prepare(sock, 
-                          SILC_PACKET_HEADER_LEN +
-                          packetdata.src_id_len + 
-                          packetdata.dst_id_len,
-                          packetdata.padlen,
-                          buffer->len);
-  
-  packetdata.buffer = sock->outbuf;
-
-  /* Put the actual encrypted message payload data into the buffer. */
-  silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
+  SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                     packetdata.src_id_len +
+                     packetdata.dst_id_len), block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
 
   /* Create the outgoing packet */
-  silc_packet_assemble(&packetdata);
+  if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock, 
+                            data, data_len, (const SilcBuffer)&packet)) {
+    SILC_LOG_ERROR(("Error assembling packet"));
+    goto out;
+  }
 
   /* Encrypt the header and padding of the packet. */
 
   /* Encrypt the header and padding of the packet. */
-  cipher = conn->send_key;
-  silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN + 
+  silc_packet_encrypt(cipher, hmac, conn->internal->psn_send++,
+                     (SilcBuffer)&packet, SILC_PACKET_HEADER_LEN +
                      packetdata.src_id_len + packetdata.dst_id_len +
                      packetdata.padlen);
 
                      packetdata.src_id_len + packetdata.dst_id_len +
                      packetdata.padlen);
 
-  SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
-                  sock->outbuf->data, sock->outbuf->len);
+  SILC_LOG_HEXDUMP(("Private message packet, len %d", packet.len),
+                  packet.data, packet.len);
 
   /* Now actually send the packet */
 
   /* Now actually send the packet */
-  silc_client_packet_send_real(client, sock, force_send, FALSE);
+  silc_client_packet_send_real(client, sock, force_send);
+
+  /* Check for mandatory rekey */
+  if (conn->internal->psn_send == SILC_CLIENT_REKEY_THRESHOLD)
+    silc_schedule_task_add(client->schedule, sock->sock,
+                          silc_client_rekey_callback, sock, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
   silc_free(packetdata.dst_id);
 
  out:
   silc_free(packetdata.dst_id);
 
  out:
@@ -121,11 +133,16 @@ void silc_client_send_private_message(SilcClient client,
 static void silc_client_private_message_cb(SilcClient client,
                                           SilcClientConnection conn,
                                           SilcClientEntry *clients,
 static void silc_client_private_message_cb(SilcClient client,
                                           SilcClientConnection conn,
                                           SilcClientEntry *clients,
-                                          uint32 clients_count,
+                                          SilcUInt32 clients_count,
                                           void *context)
 {
   SilcPacketContext *packet = (SilcPacketContext *)context;
 
                                           void *context)
 {
   SilcPacketContext *packet = (SilcPacketContext *)context;
 
+  if (!clients) {
+    silc_packet_context_free(packet);
+    return;
+  }
+
   silc_client_private_message(client, conn->sock, packet);
   silc_packet_context_free(packet);
 }
   silc_client_private_message(client, conn->sock, packet);
   silc_packet_context_free(packet);
 }
@@ -138,10 +155,14 @@ void silc_client_private_message(SilcClient client,
                                 SilcPacketContext *packet)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
                                 SilcPacketContext *packet)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcPrivateMessagePayload payload = NULL;
-  SilcIDCacheEntry id_cache;
+  SilcMessagePayload payload = NULL;
   SilcClientID *remote_id = NULL;
   SilcClientEntry remote_client;
   SilcClientID *remote_id = NULL;
   SilcClientEntry remote_client;
+  SilcMessageFlags flags;
+  unsigned char *message;
+  SilcUInt32 message_len;
+  SilcCipher cipher = NULL;
+  SilcHmac hmac = NULL;
 
   if (packet->src_id_type != SILC_ID_CLIENT)
     goto out;
 
   if (packet->src_id_type != SILC_ID_CLIENT)
     goto out;
@@ -152,50 +173,68 @@ void silc_client_private_message(SilcClient client,
     goto out;
 
   /* Check whether we know this client already */
     goto out;
 
   /* Check whether we know this client already */
-  if (!silc_idcache_find_by_id_one(conn->client_cache, remote_id,
-                                  SILC_ID_CLIENT, &id_cache)) {
+  remote_client = silc_client_get_client_by_id(client, conn, remote_id);
+  if (!remote_client || !remote_client->nickname) {
+    if (remote_client) {
+      if (remote_client->status & SILC_CLIENT_STATUS_RESOLVING) {
+       remote_client->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+       goto out;
+      }
+      remote_client->status |= SILC_CLIENT_STATUS_RESOLVING;
+      remote_client->resolve_cmd_ident = conn->cmd_ident + 1;
+    }
+
     /* Resolve the client info */
     /* Resolve the client info */
-    silc_client_get_client_by_id_resolve(client, conn, remote_id,
+    silc_client_get_client_by_id_resolve(client, conn, remote_id, NULL,
                                         silc_client_private_message_cb,
                                         silc_packet_context_dup(packet));
     return;
   }
 
                                         silc_client_private_message_cb,
                                         silc_packet_context_dup(packet));
     return;
   }
 
-  remote_client = (SilcClientEntry)id_cache->context;
+  cipher = remote_client->receive_key;
+  hmac = remote_client->hmac_receive;
+  if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY && !cipher && !hmac) {
+    silc_free(remote_id);
+    return;
+  }
 
   /* Parse the payload and decrypt it also if private message key is set */
 
   /* Parse the payload and decrypt it also if private message key is set */
-  payload = silc_private_message_payload_parse(packet->buffer,
-                                              remote_client->send_key);
+  payload = silc_message_payload_parse(packet->buffer->data,
+                                      packet->buffer->len, TRUE,
+                                      !remote_client->generated,
+                                      cipher, hmac);
   if (!payload) {
     silc_free(remote_id);
     return;
   }
 
   if (!payload) {
     silc_free(remote_id);
     return;
   }
 
+  flags = silc_message_get_flags(payload);
+
   /* Pass the private message to application */
   /* Pass the private message to application */
-  client->ops->private_message(client, conn, remote_client,
-                              silc_private_message_get_flags(payload),
-                              silc_private_message_get_message(payload, 
-                                                               NULL));
+  message = silc_message_get_data(payload, &message_len);
+  client->internal->ops->private_message(client, conn, remote_client, payload,
+                                        flags, message, message_len);
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
 
   /* See if we are away (gone). If we are away we will reply to the
      sender with the set away message. */
-  if (conn->away && conn->away->away) {
+  if (conn->internal->away && conn->internal->away->away &&
+      !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
     /* If it's me, ignore */
     /* If it's me, ignore */
-    if (!SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
+    if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
       goto out;
 
     /* Send the away message */
     silc_client_send_private_message(client, conn, remote_client,
       goto out;
 
     /* Send the away message */
     silc_client_send_private_message(client, conn, remote_client,
-                                    SILC_MESSAGE_FLAG_AUTOREPLY,
-                                    conn->away->away,
-                                    strlen(conn->away->away), TRUE);
+                                    SILC_MESSAGE_FLAG_AUTOREPLY |
+                                    SILC_MESSAGE_FLAG_NOREPLY,
+                                    conn->internal->away->away,
+                                    strlen(conn->internal->away->away), TRUE);
   }
 
  out:
   if (payload)
   }
 
  out:
   if (payload)
-    silc_private_message_payload_free(payload);
-  if (remote_id)
-    silc_free(remote_id);
+    silc_message_payload_free(payload);
+  silc_free(remote_id);
 }
 
 /* Function that actually employes the received private message key */
 }
 
 /* Function that actually employes the received private message key */
@@ -203,13 +242,13 @@ void silc_client_private_message(SilcClient client,
 static void silc_client_private_message_key_cb(SilcClient client,
                                               SilcClientConnection conn,
                                               SilcClientEntry *clients,
 static void silc_client_private_message_key_cb(SilcClient client,
                                               SilcClientConnection conn,
                                               SilcClientEntry *clients,
-                                              uint32 clients_count,
+                                              SilcUInt32 clients_count,
                                               void *context)
 {
   SilcPacketContext *packet = (SilcPacketContext *)context;
   unsigned char *key;
                                               void *context)
 {
   SilcPacketContext *packet = (SilcPacketContext *)context;
   unsigned char *key;
-  uint16 key_len;
-  unsigned char *cipher;
+  SilcUInt16 key_len;
+  unsigned char *cipher = NULL, *hmac = NULL;
   int ret;
 
   if (!clients)
   int ret;
 
   if (!clients)
@@ -218,7 +257,8 @@ static void silc_client_private_message_key_cb(SilcClient client,
   /* Parse the private message key payload */
   ret = silc_buffer_unformat(packet->buffer,
                             SILC_STR_UI16_NSTRING(&key, &key_len),
   /* Parse the private message key payload */
   ret = silc_buffer_unformat(packet->buffer,
                             SILC_STR_UI16_NSTRING(&key, &key_len),
-                            SILC_STR_UI16_STRING(&cipher),
+                            SILC_STR_UI16_STRING_ALLOC(&cipher),
+                            SILC_STR_UI16_STRING_ALLOC(&hmac),
                             SILC_STR_END);
   if (!ret)
     goto out;
                             SILC_STR_END);
   if (!ret)
     goto out;
@@ -228,20 +268,24 @@ static void silc_client_private_message_key_cb(SilcClient client,
 
   /* Now take the key in use */
   if (!silc_client_add_private_message_key(client, conn, clients[0],
 
   /* Now take the key in use */
   if (!silc_client_add_private_message_key(client, conn, clients[0],
-                                          cipher, key, key_len, FALSE))
+                                          cipher, hmac, key, key_len,
+                                          FALSE, TRUE))
     goto out;
 
   /* Print some info for application */
     goto out;
 
   /* Print some info for application */
-  client->ops->say(client, conn, 
-                  "Received private message key from %s%s%s %s%s%s", 
-                  clients[0]->nickname,
-                  clients[0]->server ? "@" : "",
-                  clients[0]->server ? clients[0]->server : "",
-                  clients[0]->username ? "(" : "",
-                  clients[0]->username ? clients[0]->username : "",
-                  clients[0]->username ? ")" : "");
+  client->internal->ops->say(
+                    client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                    "Received private message key from %s%s%s %s%s%s", 
+                    clients[0]->nickname,
+                    clients[0]->server ? "@" : "",
+                    clients[0]->server ? clients[0]->server : "",
+                    clients[0]->username ? "(" : "",
+                    clients[0]->username ? clients[0]->username : "",
+                    clients[0]->username ? ")" : "");
 
  out:
 
  out:
+  silc_free(cipher);
+  silc_free(hmac);
   silc_packet_context_free(packet);
 }
 
   silc_packet_context_free(packet);
 }
 
@@ -263,6 +307,7 @@ void silc_client_private_message_key(SilcClient client,
     return;
 
   silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
     return;
 
   silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
+                                      NULL,
                                       silc_client_private_message_key_cb,
                                       silc_packet_context_dup(packet));
   silc_free(remote_id);
                                       silc_client_private_message_key_cb,
                                       silc_packet_context_dup(packet));
   silc_free(remote_id);
@@ -273,9 +318,14 @@ void silc_client_private_message_key(SilcClient client,
    indicated by the `client_entry'. If the `key' is NULL and the boolean
    value `generate_key' is TRUE the library will generate random key.
    The `key' maybe for example pre-shared-key, passphrase or similar.
    indicated by the `client_entry'. If the `key' is NULL and the boolean
    value `generate_key' is TRUE the library will generate random key.
    The `key' maybe for example pre-shared-key, passphrase or similar.
-   The `cipher' MAY be provided but SHOULD be NULL to assure that the
-   requirements of the SILC protocol are met. The API, however, allows
-   to allocate any cipher.
+   The `cipher' and `hmac' MAY be provided but SHOULD be NULL to assure
+   that the requirements of the SILC protocol are met. The API, however,
+   allows to allocate any cipher and HMAC.
+
+   If `responder' is TRUE then the sending and receiving keys will be
+   set according the client being the receiver of the private key.  If
+   FALSE the client is being the sender (or negotiator) of the private
+   key.
 
    It is not necessary to set key for normal private message usage. If the
    key is not set then the private messages are encrypted using normal
 
    It is not necessary to set key for normal private message usage. If the
    key is not set then the private messages are encrypted using normal
@@ -284,64 +334,89 @@ void silc_client_private_message_key(SilcClient client,
    Returns FALSE if the key is already set for the `client_entry', TRUE
    otherwise. */
 
    Returns FALSE if the key is already set for the `client_entry', TRUE
    otherwise. */
 
-int silc_client_add_private_message_key(SilcClient client,
-                                       SilcClientConnection conn,
-                                       SilcClientEntry client_entry,
-                                       char *cipher,
-                                       unsigned char *key,
-                                       uint32 key_len,
-                                       int generate_key)
+bool silc_client_add_private_message_key(SilcClient client,
+                                        SilcClientConnection conn,
+                                        SilcClientEntry client_entry,
+                                        const char *cipher,
+                                        const char *hmac,
+                                        unsigned char *key,
+                                        SilcUInt32 key_len,
+                                        bool generate_key,
+                                        bool responder)
 {
   unsigned char private_key[32];
 {
   unsigned char private_key[32];
-  uint32 len;
+  SilcUInt32 len;
   int i;
   SilcSKEKeyMaterial *keymat;
 
   int i;
   SilcSKEKeyMaterial *keymat;
 
-  assert(client_entry);
+  assert(client && client_entry);
 
   /* Return FALSE if key already set */
   if (client_entry->send_key && client_entry->receive_key)
     return FALSE;
 
   if (!cipher)
 
   /* Return FALSE if key already set */
   if (client_entry->send_key && client_entry->receive_key)
     return FALSE;
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
 
 
-  /* Check the requested cipher */
+  /* Check the requested cipher and HMAC */
   if (!silc_cipher_is_supported(cipher))
     return FALSE;
   if (!silc_cipher_is_supported(cipher))
     return FALSE;
+  if (!silc_hmac_is_supported(hmac))
+    return FALSE;
 
   /* Generate key if not provided */
   if (generate_key == TRUE) {
     len = 32;
 
   /* Generate key if not provided */
   if (generate_key == TRUE) {
     len = 32;
-    for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
+    for (i = 0; i < len; i++)
+      private_key[i] = silc_rng_get_byte_fast(client->rng);
     key = private_key;
     key_len = len;
     client_entry->generated = TRUE;
   }
 
   /* Save the key */
     key = private_key;
     key_len = len;
     client_entry->generated = TRUE;
   }
 
   /* Save the key */
-  client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
-  memcpy(client_entry->key, key, key_len);
+  client_entry->key = silc_memdup(key, key_len);
   client_entry->key_len = key_len;
 
   /* Produce the key material as the protocol defines */
   keymat = silc_calloc(1, sizeof(*keymat));
   if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
   client_entry->key_len = key_len;
 
   /* Produce the key material as the protocol defines */
   keymat = silc_calloc(1, sizeof(*keymat));
   if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
-                                        client->md5hash, keymat) 
+                                        client->sha1hash, keymat) 
       != SILC_SKE_STATUS_OK)
     return FALSE;
 
       != SILC_SKE_STATUS_OK)
     return FALSE;
 
-  /* Allocate the ciphers */
+  /* Allocate the cipher and HMAC */
   silc_cipher_alloc(cipher, &client_entry->send_key);
   silc_cipher_alloc(cipher, &client_entry->receive_key);
   silc_cipher_alloc(cipher, &client_entry->send_key);
   silc_cipher_alloc(cipher, &client_entry->receive_key);
+  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
+  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
 
   /* Set the keys */
 
   /* Set the keys */
-  silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
-                     keymat->enc_key_len);
-  silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
-  silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
-                     keymat->enc_key_len);
-  silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
+  if (responder == TRUE) {
+    silc_cipher_set_key(client_entry->send_key, keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, keymat->receive_iv);
+    silc_cipher_set_key(client_entry->receive_key, keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, keymat->send_iv);
+    silc_hmac_set_key(client_entry->hmac_send, keymat->receive_hmac_key,
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(client_entry->hmac_receive, keymat->send_hmac_key,
+                     keymat->hmac_key_len);
+  } else {
+    silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
+    silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
+                       keymat->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
+    silc_hmac_set_key(client_entry->hmac_send, keymat->send_hmac_key,
+                     keymat->hmac_key_len);
+    silc_hmac_set_key(client_entry->hmac_receive, keymat->receive_hmac_key,
+                     keymat->hmac_key_len);
+  }
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
 
   /* Free the key material */
   silc_ske_free_key_material(keymat);
@@ -352,39 +427,66 @@ int silc_client_add_private_message_key(SilcClient client,
 /* Same as above but takes the key material from the SKE key material
    structure. This structure is received if the application uses the
    silc_client_send_key_agreement to negotiate the key material. The
 /* Same as above but takes the key material from the SKE key material
    structure. This structure is received if the application uses the
    silc_client_send_key_agreement to negotiate the key material. The
-   `cipher' SHOULD be provided as it is negotiated also in the SKE
-   protocol. */
-
-int silc_client_add_private_message_key_ske(SilcClient client,
-                                           SilcClientConnection conn,
-                                           SilcClientEntry client_entry,
-                                           char *cipher,
-                                           SilcSKEKeyMaterial *key)
+   `cipher' and `hmac' SHOULD be provided as it is negotiated also in
+   the SKE protocol. */
+
+bool silc_client_add_private_message_key_ske(SilcClient client,
+                                            SilcClientConnection conn,
+                                            SilcClientEntry client_entry,
+                                            const char *cipher,
+                                            const char *hmac,
+                                            SilcSKEKeyMaterial *key,
+                                            bool responder)
 {
 {
-  assert(client_entry);
+  assert(client && client_entry);
 
   /* Return FALSE if key already set */
   if (client_entry->send_key && client_entry->receive_key)
     return FALSE;
 
   if (!cipher)
 
   /* Return FALSE if key already set */
   if (client_entry->send_key && client_entry->receive_key)
     return FALSE;
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
+  if (!hmac)
+    hmac = SILC_DEFAULT_HMAC;
 
 
-  /* Check the requested cipher */
+  /* Check the requested cipher and HMAC */
   if (!silc_cipher_is_supported(cipher))
     return FALSE;
   if (!silc_cipher_is_supported(cipher))
     return FALSE;
+  if (!silc_hmac_is_supported(hmac))
+    return FALSE;
+
+  client_entry->generated = TRUE;
 
 
-  /* Allocate the ciphers */
+  /* Allocate the cipher and HMAC */
   silc_cipher_alloc(cipher, &client_entry->send_key);
   silc_cipher_alloc(cipher, &client_entry->receive_key);
   silc_cipher_alloc(cipher, &client_entry->send_key);
   silc_cipher_alloc(cipher, &client_entry->receive_key);
+  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
+  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
 
   /* Set the keys */
 
   /* Set the keys */
-  silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
-                     key->enc_key_len);
-  silc_cipher_set_iv(client_entry->send_key, key->send_iv);
-  silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
-                     key->enc_key_len);
-  silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+  if (responder == TRUE) {
+    silc_cipher_set_key(client_entry->send_key, key->receive_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, key->receive_iv);
+    silc_cipher_set_key(client_entry->receive_key, key->send_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, key->send_iv);
+    silc_hmac_set_key(client_entry->hmac_send, key->receive_hmac_key,
+                     key->hmac_key_len);
+    silc_hmac_set_key(client_entry->hmac_receive, key->send_hmac_key,
+                     key->hmac_key_len);
+  } else {
+    silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->send_key, key->send_iv);
+    silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
+                       key->enc_key_len);
+    silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
+    silc_hmac_set_key(client_entry->hmac_send, key->send_hmac_key,
+                     key->hmac_key_len);
+    silc_hmac_set_key(client_entry->hmac_receive, key->receive_hmac_key,
+                     key->hmac_key_len);
+  }
 
   return TRUE;
 }
 
   return TRUE;
 }
@@ -399,21 +501,28 @@ int silc_client_add_private_message_key_ske(SilcClient client,
    through the SILC network. The packet is protected using normal session
    keys. */
 
    through the SILC network. The packet is protected using normal session
    keys. */
 
-int silc_client_send_private_message_key(SilcClient client,
+bool silc_client_send_private_message_key(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcClientEntry client_entry,
                                         SilcClientConnection conn,
                                         SilcClientEntry client_entry,
-                                        int force_send)
+                                        bool force_send)
 {
 {
-  SilcSocketConnection sock = conn->sock;
+  SilcSocketConnection sock;
   SilcBuffer buffer;
   SilcBuffer buffer;
-  int cipher_len;
+  int cipher_len, hmac_len;
+  const char *cipher, *hmac;
+
+  assert(client && conn && client_entry);
 
 
+  sock = conn->sock;
   if (!client_entry->send_key || !client_entry->key)
     return FALSE;
 
   SILC_LOG_DEBUG(("Sending private message key"));
 
   if (!client_entry->send_key || !client_entry->key)
     return FALSE;
 
   SILC_LOG_DEBUG(("Sending private message key"));
 
-  cipher_len = strlen(client_entry->send_key->cipher->name);
+  cipher = silc_cipher_get_name(client_entry->send_key);
+  cipher_len = strlen(cipher);
+  hmac = silc_hmac_get_name(client_entry->hmac_send);
+  hmac_len = strlen(hmac);
 
   /* Create private message key payload */
   buffer = silc_buffer_alloc(2 + client_entry->key_len);
 
   /* Create private message key payload */
   buffer = silc_buffer_alloc(2 + client_entry->key_len);
@@ -423,8 +532,11 @@ int silc_client_send_private_message_key(SilcClient client,
                     SILC_STR_UI_XNSTRING(client_entry->key, 
                                          client_entry->key_len),
                     SILC_STR_UI_SHORT(cipher_len),
                     SILC_STR_UI_XNSTRING(client_entry->key, 
                                          client_entry->key_len),
                     SILC_STR_UI_SHORT(cipher_len),
-                    SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
+                    SILC_STR_UI_XNSTRING(cipher,
                                          cipher_len),
                                          cipher_len),
+                    SILC_STR_UI_SHORT(hmac_len),
+                    SILC_STR_UI_XNSTRING(hmac,
+                                         hmac_len),
                     SILC_STR_END);
 
   /* Send the packet */
                     SILC_STR_END);
 
   /* Send the packet */
@@ -440,11 +552,11 @@ int silc_client_send_private_message_key(SilcClient client,
    after this to protect the private messages with the remote `client_entry'
    client. Returns FALSE on error, TRUE otherwise. */
 
    after this to protect the private messages with the remote `client_entry'
    client. Returns FALSE on error, TRUE otherwise. */
 
-int silc_client_del_private_message_key(SilcClient client,
+bool silc_client_del_private_message_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcClientEntry client_entry)
 {
                                        SilcClientConnection conn,
                                        SilcClientEntry client_entry)
 {
-  assert(client_entry);
+  assert(client && client_entry);
 
   if (!client_entry->send_key && !client_entry->receive_key)
     return FALSE;
 
   if (!client_entry->send_key && !client_entry->receive_key)
     return FALSE;
@@ -475,16 +587,17 @@ int silc_client_del_private_message_key(SilcClient client,
 SilcPrivateMessageKeys
 silc_client_list_private_message_keys(SilcClient client,
                                      SilcClientConnection conn,
 SilcPrivateMessageKeys
 silc_client_list_private_message_keys(SilcClient client,
                                      SilcClientConnection conn,
-                                     uint32 *key_count)
+                                     SilcUInt32 *key_count)
 {
   SilcPrivateMessageKeys keys;
 {
   SilcPrivateMessageKeys keys;
-  uint32 count = 0;
+  SilcUInt32 count = 0;
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list;
   SilcClientEntry entry;
 
   SilcIDCacheEntry id_cache;
   SilcIDCacheList list;
   SilcClientEntry entry;
 
-  if (!silc_idcache_find_by_id(conn->client_cache, SILC_ID_CACHE_ANY, 
-                              SILC_ID_CLIENT, &list))
+  assert(client && conn);
+
+  if (!silc_idcache_get_all(conn->internal->client_cache, &list))
     return NULL;
 
   if (!silc_idcache_list_count(list)) {
     return NULL;
 
   if (!silc_idcache_list_count(list)) {
@@ -500,7 +613,7 @@ silc_client_list_private_message_keys(SilcClient client,
 
     if (entry->send_key) {
       keys[count].client_entry = entry;
 
     if (entry->send_key) {
       keys[count].client_entry = entry;
-      keys[count].cipher = entry->send_key->cipher->name;
+      keys[count].cipher = (char *)silc_cipher_get_name(entry->send_key);
       keys[count].key = entry->generated == FALSE ? entry->key : NULL;
       keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
       count++;
       keys[count].key = entry->generated == FALSE ? entry->key : NULL;
       keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
       count++;
@@ -520,7 +633,36 @@ silc_client_list_private_message_keys(SilcClient client,
    silc_client_list_private_message_keys. */
 
 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
    silc_client_list_private_message_keys. */
 
 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
-                                          uint32 key_count)
+                                          SilcUInt32 key_count)
 {
   silc_free(keys);
 }
 {
   silc_free(keys);
 }
+
+/* Sets away `message'.  The away message may be set when the client's
+   mode is changed to SILC_UMODE_GONE and the client whishes to reply
+   to anyone who sends private message.  The `message' will be sent
+   automatically back to the the client who send private message.  If
+   away message is already set this replaces the old message with the
+   new one.  If `message' is NULL the old away message is removed. 
+   The sender may freely free the memory of the `message'. */
+
+void silc_client_set_away_message(SilcClient client,
+                                 SilcClientConnection conn,
+                                 char *message)
+{
+  assert(client && conn);
+
+  if (!message && conn->internal->away) {
+    silc_free(conn->internal->away->away);
+    silc_free(conn->internal->away);
+    conn->internal->away = NULL;
+  }
+
+  if (message) {
+    if (!conn->internal->away)
+      conn->internal->away = silc_calloc(1, sizeof(*conn->internal->away));
+    if (conn->internal->away->away)
+      silc_free(conn->internal->away->away);
+    conn->internal->away->away = strdup(message);
+  }
+}