SilcChannelPrivateKey key,
SilcMessageFlags flags,
unsigned char *data,
- unsigned int data_len,
+ uint32 data_len,
int force_send)
{
int i;
SilcCipher cipher;
SilcHmac hmac;
unsigned char *id_string;
- unsigned int iv_len;
+ uint32 iv_len;
+ int block_len;
SILC_LOG_DEBUG(("Sending packet to channel"));
if (!cipher || !hmac)
return;
+ block_len = silc_cipher_get_block_len(cipher);
+
/* Generate IV */
iv_len = silc_cipher_get_block_len(cipher);
if (channel->iv[0] == '\0')
/* 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
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;
packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
packetdata.src_id_len +
- packetdata.dst_id_len));
+ packetdata.dst_id_len), block_len);
/* Prepare outgoing data buffer for packet sending */
silc_packet_send_prepare(sock,
silc_buffer_put(sock->outbuf, payload->data, payload->len);
/* Create the outgoing packet */
- silc_packet_assemble(&packetdata);
+ silc_packet_assemble(&packetdata, cipher);
/* Encrypt the header and padding of the packet. This is encrypted
with normal session key shared with our server. */
- silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN +
+ silc_packet_encrypt(cipher, hmac, conn->psn_send++,
+ sock->outbuf, SILC_PACKET_HEADER_LEN +
packetdata.src_id_len + packetdata.dst_id_len +
packetdata.padlen);
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. */
SilcChannelUser chu;
SilcIDCacheEntry id_cache = NULL;
SilcClientID *client_id = NULL;
- int found = FALSE;
+ bool found = FALSE;
unsigned char *message;
SILC_LOG_DEBUG(("Start"));
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;
/* Parse the channel message payload. This also decrypts the payload */
payload = silc_channel_message_payload_parse(buffer, channel->channel_key,
channel->hmac);
- if (!payload)
- goto out;
+
+ /* 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;
goto out;
}
- message = silc_channel_message_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) &&
+ chu->client->nickname) {
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,
+ client->ops->channel_message(client, conn, chu->client, channel,
silc_channel_message_get_flags(payload),
message);
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
receive Channel Key Payload and when we are processing JOIN command
reply. */
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;
/* 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 ? (char *)silc_hmac_get_name(channel->hmac) :
+ 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);
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;
}
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_hash_make(channel->hmac->hash, key, tmp_len, hash);
- silc_hmac_set_key(channel->hmac, hash, silc_hash_len(channel->hmac->hash));
+ silc_hmac_alloc(hmac, NULL, &channel->hmac);
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), key, tmp_len, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
memset(hash, 0, sizeof(hash));
out:
char *cipher,
char *hmac,
unsigned char *key,
- unsigned int key_len)
+ uint32 key_len)
{
SilcChannelPrivateKey entry;
unsigned char hash[32];
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;
/* Generate HMAC key from the channel key data and set it */
silc_hmac_alloc(hmac, NULL, &entry->hmac);
- 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));
+ silc_hash_make(silc_hmac_get_hash(entry->hmac), entry->key,
+ entry->key_len, hash);
+ silc_hmac_set_key(entry->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(entry->hmac)));
memset(hash, 0, sizeof(hash));
/* Add to the private keys list */
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;
/* Frees the SilcChannelPrivateKey array. */
void silc_client_free_channel_private_keys(SilcChannelPrivateKey *keys,
- unsigned int key_count)
+ uint32 key_count)
{
silc_free(keys);
}