Merged from silc_1_0_branch.
[silc.git] / lib / silcclient / client_prvmsg.c
index 2d065abe6406e005ff4f58c70cb8cd0c665cdffc..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
-  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
@@ -39,9 +38,9 @@ void silc_client_send_private_message(SilcClient client,
                                      SilcMessageFlags flags,
                                      unsigned char *data, 
                                      SilcUInt32 data_len, 
-                                     int force_send)
+                                     bool force_send)
 {
-  SilcSocketConnection sock = conn->sock;
+  SilcSocketConnection sock;
   SilcBuffer buffer;
   SilcPacketContext packetdata;
   const SilcBufferStruct packet;
@@ -49,13 +48,18 @@ void silc_client_send_private_message(SilcClient client,
   SilcHmac hmac;
   int block_len;
 
+  assert(client && conn && client_entry);
+  sock = conn->sock;
   SILC_LOG_DEBUG(("Sending private message"));
 
   /* Encode private message payload */
-  buffer = silc_private_message_payload_encode(flags,
-                                              data_len, data,
-                                              client_entry->send_key,
-                                              client->rng);
+  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
@@ -71,8 +75,8 @@ void silc_client_send_private_message(SilcClient client,
   /* We have private message specific key */
 
   /* Get data used in the encryption */
-  cipher = conn->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. */
@@ -91,9 +95,9 @@ void silc_client_send_private_message(SilcClient client,
                                 packetdata.dst_id_len);
   packetdata.truelen = data_len + SILC_PACKET_HEADER_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), block_len);
+  SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
+                     packetdata.src_id_len +
+                     packetdata.dst_id_len), block_len, packetdata.padlen);
 
   /* Create the outgoing packet */
   if (!silc_packet_assemble(&packetdata, client->rng, cipher, hmac, sock, 
@@ -103,8 +107,8 @@ void silc_client_send_private_message(SilcClient client,
   }
 
   /* Encrypt the header and padding of the packet. */
-  silc_packet_encrypt(cipher, hmac, conn->psn_send++,
-                     (SilcBuffer)&packet, 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);
 
@@ -113,6 +117,13 @@ void silc_client_send_private_message(SilcClient client,
 
   /* Now actually send the packet */
   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:
@@ -144,13 +155,14 @@ void silc_client_private_message(SilcClient client,
                                 SilcPacketContext *packet)
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcPrivateMessagePayload payload = NULL;
+  SilcMessagePayload payload = NULL;
   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;
@@ -180,29 +192,33 @@ void silc_client_private_message(SilcClient client,
   }
 
   cipher = remote_client->receive_key;
-  if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY && !cipher) {
+  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 */
-  payload = silc_private_message_payload_parse(packet->buffer->data,
-                                              packet->buffer->len, cipher);
+  payload = silc_message_payload_parse(packet->buffer->data,
+                                      packet->buffer->len, TRUE,
+                                      !remote_client->generated,
+                                      cipher, hmac);
   if (!payload) {
     silc_free(remote_id);
     return;
   }
 
-  flags = silc_private_message_get_flags(payload);
+  flags = silc_message_get_flags(payload);
 
   /* Pass the private message to application */
-  message = silc_private_message_get_message(payload, &message_len);
-  client->internal->ops->private_message(client, conn, remote_client, flags,
-                                        message, message_len);
+  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. */
-  if (conn->away && conn->away->away && !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
+  if (conn->internal->away && conn->internal->away->away &&
+      !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
     /* If it's me, ignore */
     if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
       goto out;
@@ -211,13 +227,13 @@ void silc_client_private_message(SilcClient client,
     silc_client_send_private_message(client, conn, remote_client,
                                     SILC_MESSAGE_FLAG_AUTOREPLY |
                                     SILC_MESSAGE_FLAG_NOREPLY,
-                                    conn->away->away,
-                                    strlen(conn->away->away), TRUE);
+                                    conn->internal->away->away,
+                                    strlen(conn->internal->away->away), TRUE);
   }
 
  out:
   if (payload)
-    silc_private_message_payload_free(payload);
+    silc_message_payload_free(payload);
   silc_free(remote_id);
 }
 
@@ -232,7 +248,7 @@ static void silc_client_private_message_key_cb(SilcClient client,
   SilcPacketContext *packet = (SilcPacketContext *)context;
   unsigned char *key;
   SilcUInt16 key_len;
-  unsigned char *cipher;
+  unsigned char *cipher = NULL, *hmac = NULL;
   int ret;
 
   if (!clients)
@@ -241,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),
-                            SILC_STR_UI16_STRING(&cipher),
+                            SILC_STR_UI16_STRING_ALLOC(&cipher),
+                            SILC_STR_UI16_STRING_ALLOC(&hmac),
                             SILC_STR_END);
   if (!ret)
     goto out;
@@ -251,7 +268,8 @@ 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],
-                                          cipher, key, key_len, FALSE, TRUE))
+                                          cipher, hmac, key, key_len,
+                                          FALSE, TRUE))
     goto out;
 
   /* Print some info for application */
@@ -266,6 +284,8 @@ static void silc_client_private_message_key_cb(SilcClient client,
                     clients[0]->username ? ")" : "");
 
  out:
+  silc_free(cipher);
+  silc_free(hmac);
   silc_packet_context_free(packet);
 }
 
@@ -298,9 +318,9 @@ 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.
-   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
@@ -314,21 +334,22 @@ void silc_client_private_message_key(SilcClient client,
    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,
-                                       SilcUInt32 key_len,
-                                       bool generate_key,
-                                       bool responder)
+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];
   SilcUInt32 len;
   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)
@@ -336,15 +357,20 @@ int silc_client_add_private_message_key(SilcClient client,
 
   if (!cipher)
     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_hmac_is_supported(hmac))
+    return FALSE;
 
   /* 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;
@@ -357,13 +383,15 @@ int silc_client_add_private_message_key(SilcClient client,
   /* 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;
 
-  /* 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_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
+  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
 
   /* Set the keys */
   if (responder == TRUE) {
@@ -373,6 +401,10 @@ int silc_client_add_private_message_key(SilcClient client,
     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);
@@ -380,6 +412,10 @@ int silc_client_add_private_message_key(SilcClient client,
     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 */
@@ -391,17 +427,18 @@ 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
-   `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,
-                                           bool responder)
+   `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)
@@ -409,14 +446,22 @@ int silc_client_add_private_message_key_ske(SilcClient client,
 
   if (!cipher)
     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_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_hmac_alloc(hmac, NULL, &client_entry->hmac_send);
+  silc_hmac_alloc(hmac, NULL, &client_entry->hmac_receive);
 
   /* Set the keys */
   if (responder == TRUE) {
@@ -426,6 +471,10 @@ int silc_client_add_private_message_key_ske(SilcClient client,
     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);
@@ -433,6 +482,10 @@ int silc_client_add_private_message_key_ske(SilcClient client,
     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;
@@ -448,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. */
 
-int silc_client_send_private_message_key(SilcClient client,
+bool silc_client_send_private_message_key(SilcClient client,
                                         SilcClientConnection conn,
                                         SilcClientEntry client_entry,
-                                        int force_send)
+                                        bool force_send)
 {
-  SilcSocketConnection sock = conn->sock;
+  SilcSocketConnection sock;
   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"));
 
-  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);
@@ -472,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->send_key->cipher->name,
+                    SILC_STR_UI_XNSTRING(cipher,
                                          cipher_len),
+                    SILC_STR_UI_SHORT(hmac_len),
+                    SILC_STR_UI_XNSTRING(hmac,
+                                         hmac_len),
                     SILC_STR_END);
 
   /* Send the packet */
@@ -489,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. */
 
-int silc_client_del_private_message_key(SilcClient client,
+bool silc_client_del_private_message_key(SilcClient client,
                                        SilcClientConnection conn,
                                        SilcClientEntry client_entry)
 {
-  assert(client_entry);
+  assert(client && client_entry);
 
   if (!client_entry->send_key && !client_entry->receive_key)
     return FALSE;
@@ -532,7 +595,9 @@ silc_client_list_private_message_keys(SilcClient client,
   SilcIDCacheList list;
   SilcClientEntry entry;
 
-  if (!silc_idcache_get_all(conn->client_cache, &list))
+  assert(client && conn);
+
+  if (!silc_idcache_get_all(conn->internal->client_cache, &list))
     return NULL;
 
   if (!silc_idcache_list_count(list)) {
@@ -548,7 +613,7 @@ silc_client_list_private_message_keys(SilcClient client,
 
     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++;
@@ -585,17 +650,19 @@ void silc_client_set_away_message(SilcClient client,
                                  SilcClientConnection conn,
                                  char *message)
 {
-  if (!message && conn->away) {
-    silc_free(conn->away->away);
-    silc_free(conn->away);
-    conn->away = NULL;
+  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->away)
-      conn->away = silc_calloc(1, sizeof(*conn->away));
-    if (conn->away->away)
-      silc_free(conn->away->away);
-    conn->away->away = strdup(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);
   }
 }