updates.
[silc.git] / lib / silcclient / client_channel.c
index 81c9ca9a5b2f33030fb97f2dcba444f1eafb4291..88ccd4534aea5e2a6d50512ba67669c9672eb4b9 100644 (file)
@@ -36,8 +36,9 @@ void silc_client_send_channel_message(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcChannelEntry channel,
                                      SilcChannelPrivateKey key,
+                                     SilcMessageFlags flags,
                                      unsigned char *data, 
-                                     unsigned int data_len, 
+                                     uint32 data_len, 
                                      int force_send)
 {
   int i;
@@ -47,42 +48,44 @@ void silc_client_send_channel_message(SilcClient client,
   SilcCipher cipher;
   SilcHmac hmac;
   unsigned char *id_string;
-  unsigned int iv_len;
+  uint32 iv_len;
 
   SILC_LOG_DEBUG(("Sending packet to channel"));
 
-  if (!channel || !channel->hmac || 
-      (!channel->channel_key && !key && !channel->private_keys)) {
-    client->ops->say(client, conn, 
-                    "Cannot talk to channel: key does not exist");
-    return;
-  }
-
   /* Take the key to be used */
-  if (key) {
-    /* Use key application specified */
-    cipher = key->cipher;
-    hmac = key->hmac;
-  } else if (channel->curr_key) {
-    /* Use current private key */
-    cipher = channel->curr_key->cipher;
-    hmac = channel->curr_key->hmac;
-  } else if (!channel->curr_key && channel->private_keys) {
-    /* Use just some private key since we don't know what to use 
-       and private keys are set. */
-    silc_dlist_start(channel->private_keys);
-    key = silc_dlist_get(channel->private_keys);
-    cipher = key->cipher;
-    hmac = key->hmac;
-
-    /* Use this key as current private key */
-    channel->curr_key = key;
+  if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+    if (key) {
+      /* Use key application specified */
+      cipher = key->cipher;
+      hmac = key->hmac;
+    } else if (channel->curr_key) {
+      /* Use current private key */
+      cipher = channel->curr_key->cipher;
+      hmac = channel->curr_key->hmac;
+    } else if (!channel->curr_key && channel->private_keys) {
+      /* Use just some private key since we don't know what to use 
+        and private keys are set. */
+      silc_dlist_start(channel->private_keys);
+      key = silc_dlist_get(channel->private_keys);
+      cipher = key->cipher;
+      hmac = key->hmac;
+      
+      /* Use this key as current private key */
+      channel->curr_key = key;
+    } else {
+      /* Use normal channel key generated by the server */
+      cipher = channel->channel_key;
+      hmac = channel->hmac;
+    }
   } else {
     /* Use normal channel key generated by the server */
     cipher = channel->channel_key;
     hmac = channel->hmac;
   }
 
+  if (!cipher || !hmac)
+    return;
+
   /* Generate IV */
   iv_len = silc_cipher_get_block_len(cipher);
   if (channel->iv[0] == '\0')
@@ -92,13 +95,12 @@ void silc_client_send_channel_message(SilcClient client,
     silc_hash_make(client->md5hash, channel->iv, iv_len, channel->iv);
 
   /* Encode the channel payload. This also encrypts the message payload. */
-  payload = silc_channel_payload_encode(data_len, data, iv_len, 
-                                       channel->iv, cipher, hmac,
-                                       client->rng);
+  payload = silc_channel_message_payload_encode(flags, data_len, data, iv_len, 
+                                               channel->iv, cipher, hmac);
 
   /* Get data used in packet header encryption, keys and stuff. */
   cipher = conn->send_key;
-  hmac = conn->hmac;
+  hmac = conn->hmac_send;
   id_string = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
 
   /* Set the packet context pointers. The destination ID is always
@@ -107,10 +109,10 @@ void silc_client_send_channel_message(SilcClient client,
   packetdata.flags = 0;
   packetdata.type = SILC_PACKET_CHANNEL_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 = id_string;
-  packetdata.dst_id_len = SILC_ID_CHANNEL_LEN;
+  packetdata.dst_id_len = silc_id_get_len(channel->id, SILC_ID_CHANNEL);
   packetdata.dst_id_type = SILC_ID_CHANNEL;
   packetdata.truelen = payload->len + SILC_PACKET_HEADER_LEN + 
     packetdata.src_id_len + packetdata.dst_id_len;
@@ -149,6 +151,19 @@ void silc_client_send_channel_message(SilcClient client,
   silc_free(id_string);
 }
 
+static void silc_client_channel_message_cb(SilcClient client,
+                                          SilcClientConnection conn,
+                                          SilcClientEntry *clients,
+                                          uint32 clients_count,
+                                          void *context)
+{
+  SilcPacketContext *packet = (SilcPacketContext *)context;
+
+  if (clients)
+    silc_client_channel_message(client, conn->sock, packet);
+  silc_packet_context_free(packet);
+}
+
 /* Process received message to a channel (or from a channel, really). This
    decrypts the channel message with channel specific key and parses the
    channel payload. Finally it displays the message on the screen. */
@@ -159,13 +174,13 @@ void silc_client_channel_message(SilcClient client,
 {
   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
   SilcBuffer buffer = packet->buffer;
-  SilcChannelPayload payload = NULL;
+  SilcChannelMessagePayload payload = NULL;
   SilcChannelID *id = NULL;
   SilcChannelEntry channel;
   SilcChannelUser chu;
   SilcIDCacheEntry id_cache = NULL;
   SilcClientID *client_id = NULL;
-  int found = FALSE;
+  bool found = FALSE;
   unsigned char *message;
 
   SILC_LOG_DEBUG(("Start"));
@@ -183,8 +198,7 @@ void silc_client_channel_message(SilcClient client,
     goto out;
 
   /* Find the channel entry from channels on this connection */
-  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                  SILC_ID_CHANNEL, &id_cache))
+  if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id, &id_cache))
     goto out;
 
   channel = (SilcChannelEntry)id_cache->context;
@@ -192,41 +206,64 @@ void silc_client_channel_message(SilcClient client,
   /* If there is no channel private key then just decrypt the message 
      with the channel key. If private keys are set then just go through
      all private keys and check what decrypts correctly. */
-  if (!channel->private_keys) {
+  if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
     /* Parse the channel message payload. This also decrypts the payload */
-    payload = silc_channel_payload_parse(buffer, channel->channel_key,
-                                        channel->hmac);
-    if (!payload)
-      goto out;
-  } else {
+    payload = silc_channel_message_payload_parse(buffer, channel->channel_key,
+                                                channel->hmac);
+
+    /* If decryption failed and we have just performed channel key rekey
+       we will use the old key in decryption. If that fails too then we
+       cannot do more and will drop the packet. */
+    if (!payload) {
+      if (!channel->old_channel_key)
+       goto out;
+
+      payload = silc_channel_message_payload_parse(buffer, 
+                                                  channel->old_channel_key,
+                                                  channel->old_hmac);
+      if (!payload)
+       goto out;
+    }
+  } else if (channel->private_keys) {
     SilcChannelPrivateKey entry;
 
     silc_dlist_start(channel->private_keys);
     while ((entry = silc_dlist_get(channel->private_keys)) != SILC_LIST_END) {
       /* Parse the channel message payload. This also decrypts the payload */
-      payload = silc_channel_payload_parse(buffer, entry->cipher,
-                                          entry->hmac);
+      payload = silc_channel_message_payload_parse(buffer, entry->cipher,
+                                                  entry->hmac);
       if (payload)
        break;
     }
     if (entry == SILC_LIST_END)
       goto out;
+  } else {
+    goto out;
   }
 
-  message = silc_channel_get_data(payload, NULL);
-
   /* Find client entry */
   silc_list_start(channel->clients);
   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
-    if (!SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
+    if (SILC_ID_CLIENT_COMPARE(chu->client->id, client_id)) {
       found = TRUE;
       break;
     }
   }
 
+  if (!found) {
+    /* Resolve the client info */
+    silc_client_get_client_by_id_resolve(client, conn, client_id,
+                                        silc_client_channel_message_cb,
+                                        silc_packet_context_dup(packet));
+    goto out;
+  }
+
+  message = silc_channel_message_get_data(payload, NULL);
+
   /* Pass the message to application */
-  client->ops->channel_message(client, conn, found ? chu->client : NULL,
-                              channel, message);
+  client->ops->channel_message(client, conn, chu->client, channel,
+                              silc_channel_message_get_flags(payload),
+                              message);
 
  out:
   if (id)
@@ -234,7 +271,24 @@ void silc_client_channel_message(SilcClient client,
   if (client_id)
     silc_free(client_id);
   if (payload)
-    silc_channel_payload_free(payload);
+    silc_channel_message_payload_free(payload);
+}
+
+/* Timeout callback that is called after a short period of time after the
+   new channel key has been created. This removes the old channel key all
+   together. */
+
+SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
+{
+  SilcChannelEntry channel = (SilcChannelEntry)context;
+
+  if (channel->old_channel_key)
+    silc_cipher_free(channel->old_channel_key);
+  if (channel->old_hmac)
+    silc_hmac_free(channel->old_hmac);
+  channel->old_channel_key = NULL;
+  channel->old_hmac = NULL;
+  channel->rekey_task = NULL;
 }
 
 /* Saves channel key from encoded `key_payload'. This is used when we
@@ -245,8 +299,8 @@ void silc_client_save_channel_key(SilcClientConnection conn,
                                  SilcBuffer key_payload, 
                                  SilcChannelEntry channel)
 {
-  unsigned char *id_string, *key, *cipher, hash[32];
-  unsigned int tmp_len;
+  unsigned char *id_string, *key, *cipher, *hmac, hash[32];
+  uint32 tmp_len;
   SilcChannelID *id;
   SilcIDCacheEntry id_cache = NULL;
   SilcChannelKeyPayload payload;
@@ -269,14 +323,35 @@ void silc_client_save_channel_key(SilcClientConnection conn,
 
   /* Find channel. */
   if (!channel) {
-    if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)id,
-                                    SILC_ID_CHANNEL, &id_cache))
+    if (!silc_idcache_find_by_id_one(conn->channel_cache, 
+                                    (void *)id, &id_cache))
       goto out;
     
     /* Get channel entry */
     channel = (SilcChannelEntry)id_cache->context;
   }
 
+  hmac = channel->hmac ? channel->hmac->hmac->name : SILC_DEFAULT_HMAC;
+
+  /* Save the old key for a short period of time so that we can decrypt
+     channel message even after the rekey if some client would be sending
+     messages with the old key after the rekey. */
+  if (channel->old_channel_key)
+    silc_cipher_free(channel->old_channel_key);
+  if (channel->old_hmac)
+    silc_hmac_free(channel->old_hmac);
+  if (channel->rekey_task)
+    silc_schedule_task_del(conn->client->schedule, channel->rekey_task);
+  channel->old_channel_key = channel->channel_key;
+  channel->old_hmac = channel->hmac;
+  channel->rekey_task = 
+    silc_schedule_task_add(conn->client->schedule, 0,
+                          silc_client_save_channel_key_rekey, channel,
+                          10, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  /* Free the old channel key data */
+  silc_free(channel->key);
+
   /* Save the key */
   key = silc_channel_key_get_key(payload, &tmp_len);
   cipher = silc_channel_key_get_cipher(payload, NULL);
@@ -285,8 +360,9 @@ void silc_client_save_channel_key(SilcClientConnection conn,
   memcpy(channel->key, key, tmp_len);
 
   if (!silc_cipher_alloc(cipher, &channel->channel_key)) {
-    conn->client->ops->say(conn->client, conn,
-                    "Cannot talk to channel: unsupported cipher %s", cipher);
+    conn->client->ops->say(conn->client, conn, SILC_CLIENT_MESSAGE_AUDIT,
+                          "Cannot talk to channel: unsupported cipher %s", 
+                          cipher);
     goto out;
   }
 
@@ -294,15 +370,11 @@ void silc_client_save_channel_key(SilcClientConnection conn,
   silc_cipher_set_key(channel->channel_key, key, channel->key_len);
 
   /* Generate HMAC key from the channel key data and set it */
-  if (!channel->hmac)
-    silc_hmac_alloc("hmac-sha1-96", NULL, &channel->hmac);
+  silc_hmac_alloc(hmac, NULL, &channel->hmac);
   silc_hash_make(channel->hmac->hash, key, tmp_len, hash);
   silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
   memset(hash, 0, sizeof(hash));
 
-  /* Client is now joined to the channel */
-  channel->on_channel = TRUE;
-
  out:
   silc_free(id);
   silc_channel_key_payload_free(payload);
@@ -332,7 +404,7 @@ void silc_client_receive_channel_key(SilcClient client,
    several private keys per one channel. In this case only some of the
    clients on the channel may know the one key and only some the other key.
 
-   if `cipher' and/or `hmac' is NULL then default values will be used 
+   If `cipher' and/or `hmac' is NULL then default values will be used 
    (aes-256-cbc for cipher and hmac-sha1-96 for hmac).
 
    The private key for channel is optional. If it is not set then the
@@ -359,18 +431,19 @@ int silc_client_add_channel_private_key(SilcClient client,
                                        char *cipher,
                                        char *hmac,
                                        unsigned char *key,
-                                       unsigned int key_len)
+                                       uint32 key_len)
 {
   SilcChannelPrivateKey entry;
   unsigned char hash[32];
+  SilcSKEKeyMaterial *keymat;
 
   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
     return FALSE;
 
   if (!cipher)
-    cipher = "aes-256-cbc";
+    cipher = SILC_DEFAULT_CIPHER;
   if (!hmac)
-    hmac = "hmac-sha1-96";
+    hmac = SILC_DEFAULT_HMAC;
 
   if (!silc_cipher_is_supported(cipher))
     return FALSE;
@@ -378,6 +451,13 @@ int silc_client_add_channel_private_key(SilcClient client,
   if (!silc_hmac_is_supported(hmac))
     return FALSE;
 
+  /* Produce the key material */
+  keymat = silc_calloc(1, sizeof(*keymat));
+  if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16, 
+                                        client->md5hash, keymat) 
+      != SILC_SKE_STATUS_OK)
+    return FALSE;
+
   /* Remove the current key, if it exists. */
   if (channel->channel_key) {
     silc_cipher_free(channel->channel_key);
@@ -387,25 +467,27 @@ int silc_client_add_channel_private_key(SilcClient client,
     channel->key = NULL;
     channel->key_len = 0;
   }
-  if (channel->hmac)
+  if (channel->hmac) {
     silc_hmac_free(channel->hmac);
+    channel->hmac = NULL;
+  }
 
   if (!channel->private_keys)
     channel->private_keys = silc_dlist_init();
 
   /* Save the key */
   entry = silc_calloc(1, sizeof(*entry));
-  entry->key = silc_calloc(key_len, sizeof(*entry->key));
-  memcpy(entry->key, key, key_len);
-  entry->key_len = key_len;
+  entry->key = silc_calloc(keymat->enc_key_len / 8, sizeof(*entry->key));
+  memcpy(entry->key, keymat->send_enc_key, keymat->enc_key_len / 8);
+  entry->key_len = keymat->enc_key_len / 8;
 
   /* Allocate the cipher and set the key*/
   silc_cipher_alloc(cipher, &entry->cipher);
-  silc_cipher_set_key(entry->cipher, key, key_len * 8);
+  silc_cipher_set_key(entry->cipher, entry->key, keymat->enc_key_len);
 
   /* Generate HMAC key from the channel key data and set it */
   silc_hmac_alloc(hmac, NULL, &entry->hmac);
-  silc_hash_make(entry->hmac->hash, key, key_len, hash);
+  silc_hash_make(entry->hmac->hash, entry->key, entry->key_len, hash);
   silc_hmac_set_key(entry->hmac, hash, silc_hash_len(entry->hmac->hash));
   memset(hash, 0, sizeof(hash));
 
@@ -415,6 +497,9 @@ int silc_client_add_channel_private_key(SilcClient client,
   if (!channel->curr_key)
     channel->curr_key = entry;
 
+  /* Free the key material */
+  silc_ske_free_key_material(keymat);
+
   return TRUE;
 }
 
@@ -500,10 +585,10 @@ SilcChannelPrivateKey *
 silc_client_list_channel_private_keys(SilcClient client,
                                      SilcClientConnection conn,
                                      SilcChannelEntry channel,
-                                     unsigned int *key_count)
+                                     uint32 *key_count)
 {
   SilcChannelPrivateKey *keys = NULL, entry;
-  unsigned int count = 0;
+  uint32 count = 0;
 
   if (!channel->private_keys)
     return NULL;
@@ -524,7 +609,7 @@ silc_client_list_channel_private_keys(SilcClient client,
 /* Frees the SilcChannelPrivateKey array. */
 
 void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
-                                          unsigned int key_count)
+                                          uint32 key_count)
 {
   silc_free(keys);
 }