5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Channel Message Send ****************************/
27 /* Sends channel message to `channel'. */
29 SilcBool silc_client_send_channel_message(SilcClient client,
30 SilcClientConnection conn,
31 SilcChannelEntry channel,
32 SilcChannelPrivateKey key,
33 SilcMessageFlags flags,
44 SILC_LOG_DEBUG(("Sending channel message"));
46 if (silc_unlikely(!client || !conn || !channel))
48 if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
50 if (silc_unlikely(conn->internal->disconnected))
53 chu = silc_client_on_channel(channel, conn->local_entry);
54 if (silc_unlikely(!chu)) {
55 client->internal->ops->say(conn->client, conn,
56 SILC_CLIENT_MESSAGE_AUDIT,
57 "Cannot talk to channel: not joined");
61 /* Check if it is allowed to send messages to this channel by us. */
62 if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS &&
65 if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
66 chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
67 !(chu->mode & SILC_CHANNEL_UMODE_CHANFO)))
69 if (silc_unlikely(chu->mode & SILC_CHANNEL_UMODE_QUIET))
72 /* Take the key to be used */
73 if (channel->internal.private_keys) {
75 /* Use key application specified */
78 } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
79 channel->internal.curr_key) {
80 /* Use current private key */
81 cipher = channel->internal.curr_key->cipher;
82 hmac = channel->internal.curr_key->hmac;
83 } else if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY &&
84 !channel->internal.curr_key &&
85 channel->internal.private_keys) {
86 /* Use just some private key since we don't know what to use
87 and private keys are set. */
88 silc_dlist_start(channel->internal.private_keys);
89 key = silc_dlist_get(channel->internal.private_keys);
93 /* Use this key as current private key */
94 channel->internal.curr_key = key;
96 /* Use normal channel key generated by the server */
97 cipher = channel->internal.send_key;
98 hmac = channel->internal.hmac;
101 /* Use normal channel key generated by the server */
102 cipher = channel->internal.send_key;
103 hmac = channel->internal.hmac;
106 if (silc_unlikely(!cipher || !hmac)) {
107 SILC_LOG_ERROR(("No cipher and HMAC for channel"));
111 /* Encode the message payload. This also encrypts the message payload. */
112 buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
113 cipher, hmac, client->rng, NULL,
114 conn->private_key, hash, NULL);
115 if (silc_unlikely(!buffer)) {
116 SILC_LOG_ERROR(("Error encoding channel message"));
120 /* Send the channel message */
121 ret = silc_packet_send_ext(conn->stream, SILC_PACKET_CHANNEL_MESSAGE, 0,
122 0, NULL, SILC_ID_CHANNEL, &channel->id,
123 silc_buffer_datalen(buffer), NULL, NULL);
125 silc_buffer_free(buffer);
129 /************************* Channel Message Receive **************************/
131 /* Client resolving callback. Continues with the channel message processing */
133 static void silc_client_channel_message_resolved(SilcClient client,
134 SilcClientConnection conn,
139 /* If no client found, ignore the channel message, a silent error */
141 silc_fsm_next(context, silc_client_channel_message_error);
143 /* Continue processing the channel message packet */
144 SILC_FSM_CALL_CONTINUE(context);
147 /* Process received channel message */
149 SILC_FSM_STATE(silc_client_channel_message)
151 SilcClientConnection conn = fsm_context;
152 SilcClient client = conn->client;
153 SilcPacket packet = state_context;
154 SilcBuffer buffer = &packet->buffer;
155 SilcMessagePayload payload = NULL;
156 SilcChannelEntry channel;
157 SilcClientEntry client_entry;
158 SilcClientID remote_id;
159 SilcChannelID channel_id;
160 unsigned char *message;
161 SilcUInt32 message_len;
162 SilcChannelPrivateKey key = NULL;
164 SILC_LOG_DEBUG(("Received channel message"));
166 SILC_LOG_HEXDUMP(("Channel message"), silc_buffer_data(buffer),
167 silc_buffer_len(buffer));
169 if (silc_unlikely(packet->dst_id_type != SILC_ID_CHANNEL)) {
170 /** Invalid packet */
171 silc_fsm_next(fsm, silc_client_channel_message_error);
172 return SILC_FSM_CONTINUE;
175 if (silc_unlikely(!silc_id_str2id(packet->src_id,
176 packet->src_id_len, SILC_ID_CLIENT,
177 &remote_id, sizeof(remote_id)))) {
178 /** Invalid source ID */
179 silc_fsm_next(fsm, silc_client_channel_message_error);
180 return SILC_FSM_CONTINUE;
183 /* Get sender client entry */
184 client_entry = silc_client_get_client_by_id(client, conn, &remote_id);
185 if (!client_entry || !client_entry->nickname[0]) {
186 /** Resolve client info */
187 silc_client_unref_client(client, conn, client_entry);
188 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
189 client, conn, &remote_id, NULL,
190 silc_client_channel_message_resolved,
195 if (silc_unlikely(!silc_id_str2id(packet->dst_id, packet->dst_id_len,
196 SILC_ID_CHANNEL, &channel_id,
197 sizeof(channel_id)))) {
198 /** Invalid destination ID */
199 silc_fsm_next(fsm, silc_client_channel_message_error);
200 return SILC_FSM_CONTINUE;
203 /* Find the channel */
204 channel = silc_client_get_channel_by_id(client, conn, &channel_id);
205 if (silc_unlikely(!channel)) {
206 /** Unknown channel */
207 silc_fsm_next(fsm, silc_client_channel_message_error);
208 return SILC_FSM_CONTINUE;
211 /* Check that user is on channel */
212 if (silc_unlikely(!silc_client_on_channel(channel, client_entry))) {
213 /** User not on channel */
214 SILC_LOG_WARNING(("Message from user not on channel, client or "
216 silc_fsm_next(fsm, silc_client_channel_message_error);
217 return SILC_FSM_CONTINUE;
220 /* If there is no channel private key then just decrypt the message
221 with the channel key. If private keys are set then just go through
222 all private keys and check what decrypts correctly. */
223 if (!channel->internal.private_keys) {
224 /* Parse the channel message payload. This also decrypts the payload */
225 payload = silc_message_payload_parse(silc_buffer_data(buffer),
226 silc_buffer_len(buffer), FALSE,
227 FALSE, channel->internal.receive_key,
228 channel->internal.hmac, NULL,
231 /* If decryption failed and we have just performed channel key rekey
232 we will use the old key in decryption. If that fails too then we
233 cannot do more and will drop the packet. */
234 if (silc_unlikely(!payload)) {
238 if (!channel->internal.old_channel_keys ||
239 !silc_dlist_count(channel->internal.old_channel_keys))
242 SILC_LOG_DEBUG(("Attempting to decrypt with old channel key(s)"));
244 silc_dlist_end(channel->internal.old_channel_keys);
245 silc_dlist_end(channel->internal.old_hmacs);
246 while ((cipher = silc_dlist_get(channel->internal.old_channel_keys))) {
247 hmac = silc_dlist_get(channel->internal.old_hmacs);
251 payload = silc_message_payload_parse(silc_buffer_data(buffer),
252 silc_buffer_len(buffer),
253 FALSE, FALSE, cipher, hmac,
262 /* If the private key mode is not set on the channel then try the actual
263 channel key first before trying private keys. */
264 if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
265 payload = silc_message_payload_parse(silc_buffer_data(buffer),
266 silc_buffer_len(buffer),
268 channel->internal.receive_key,
269 channel->internal.hmac, NULL,
273 silc_dlist_start(channel->internal.private_keys);
274 while ((key = silc_dlist_get(channel->internal.private_keys))) {
275 /* Parse the message payload. This also decrypts the payload */
276 payload = silc_message_payload_parse(silc_buffer_data(buffer),
277 silc_buffer_len(buffer),
278 FALSE, FALSE, key->cipher,
279 key->hmac, NULL, FALSE, NULL);
283 if (key == SILC_LIST_END)
288 message = silc_message_get_data(payload, &message_len);
290 /* Pass the message to application */
291 client->internal->ops->channel_message(
292 client, conn, client_entry, channel, payload,
293 key, silc_message_get_flags(payload),
294 message, message_len);
297 silc_client_unref_client(client, conn, client_entry);
298 silc_client_unref_channel(client, conn, channel);
300 silc_message_payload_free(payload);
301 return SILC_FSM_FINISH;
304 /* Channel message error. */
306 SILC_FSM_STATE(silc_client_channel_message_error)
308 SilcPacket packet = state_context;
309 silc_packet_free(packet);
310 return SILC_FSM_FINISH;
313 /******************************* Channel Key ********************************/
315 /* Timeout callback that is called after a short period of time after the
316 new channel key has been created. This removes the first channel key
319 SILC_TASK_CALLBACK(silc_client_save_channel_key_rekey)
321 SilcChannelEntry channel = (SilcChannelEntry)context;
325 if (channel->internal.old_channel_keys) {
326 silc_dlist_start(channel->internal.old_channel_keys);
327 key = silc_dlist_get(channel->internal.old_channel_keys);
329 silc_dlist_del(channel->internal.old_channel_keys, key);
330 silc_cipher_free(key);
334 if (channel->internal.old_hmacs) {
335 silc_dlist_start(channel->internal.old_hmacs);
336 hmac = silc_dlist_get(channel->internal.old_hmacs);
338 silc_dlist_del(channel->internal.old_hmacs, hmac);
339 silc_hmac_free(hmac);
344 /* Saves channel key from encoded `key_payload'. This is used when we receive
345 Channel Key Payload and when we are processing JOIN command reply. */
347 SilcBool silc_client_save_channel_key(SilcClient client,
348 SilcClientConnection conn,
349 SilcBuffer key_payload,
350 SilcChannelEntry channel)
352 unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
355 SilcChannelKeyPayload payload;
357 SILC_LOG_DEBUG(("New channel key"));
359 payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
360 silc_buffer_len(key_payload));
364 id_string = silc_channel_key_get_id(payload, &tmp_len);
366 silc_channel_key_payload_free(payload);
370 if (!silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
371 silc_channel_key_payload_free(payload);
377 channel = silc_client_get_channel_by_id(client, conn, &id);
379 SILC_LOG_DEBUG(("Key for unknown channel"));
380 silc_channel_key_payload_free(payload);
384 silc_client_ref_channel(client, conn, channel);
387 /* Save the old key for a short period of time so that we can decrypt
388 channel message even after the rekey if some client would be sending
389 messages with the old key after the rekey. */
390 if (!channel->internal.old_channel_keys)
391 channel->internal.old_channel_keys = silc_dlist_init();
392 if (!channel->internal.old_hmacs)
393 channel->internal.old_hmacs = silc_dlist_init();
394 if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
395 silc_dlist_add(channel->internal.old_channel_keys,
396 channel->internal.receive_key);
397 silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
398 silc_schedule_task_add_timeout(client->schedule,
399 silc_client_save_channel_key_rekey,
403 /* Get channel cipher */
404 cipher = silc_channel_key_get_cipher(payload, NULL);
405 if (!silc_cipher_alloc(cipher, &channel->internal.send_key)) {
406 client->internal->ops->say(
408 SILC_CLIENT_MESSAGE_AUDIT,
409 "Cannot talk to channel: unsupported cipher %s",
411 silc_client_unref_channel(client, conn, channel);
412 silc_channel_key_payload_free(payload);
415 if (!silc_cipher_alloc(cipher, &channel->internal.receive_key)) {
416 client->internal->ops->say(
418 SILC_CLIENT_MESSAGE_AUDIT,
419 "Cannot talk to channel: unsupported cipher %s",
421 silc_client_unref_channel(client, conn, channel);
422 silc_channel_key_payload_free(payload);
426 /* Set the cipher key. Both sending and receiving keys are same */
427 key = silc_channel_key_get_key(payload, &tmp_len);
428 silc_cipher_set_key(channel->internal.send_key, key, tmp_len * 8, TRUE);
429 silc_cipher_set_key(channel->internal.receive_key, key, tmp_len * 8, FALSE);
431 /* Get channel HMAC */
432 hmac = (channel->internal.hmac ?
433 (char *)silc_hmac_get_name(channel->internal.hmac) :
435 if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
436 client->internal->ops->say(
438 SILC_CLIENT_MESSAGE_AUDIT,
439 "Cannot talk to channel: unsupported HMAC %s",
441 silc_client_unref_channel(client, conn, channel);
442 silc_channel_key_payload_free(payload);
447 silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key,
449 silc_hmac_set_key(channel->internal.hmac, hash,
450 silc_hash_len(silc_hmac_get_hash(channel->internal.hmac)));
451 memset(hash, 0, sizeof(hash));
453 silc_client_unref_channel(client, conn, channel);
458 /* Received channel key packet. The key will replace old channel key. */
460 SILC_FSM_STATE(silc_client_channel_key)
462 SilcClientConnection conn = fsm_context;
463 SilcClient client = conn->client;
464 SilcPacket packet = state_context;
466 SILC_LOG_DEBUG(("Received channel key"));
469 silc_client_save_channel_key(client, conn, &packet->buffer, NULL);
470 silc_packet_free(packet);
472 return SILC_FSM_FINISH;
475 /**************************** Channel Private Key ***************************/
477 /* Add new channel private key */
479 SilcBool silc_client_add_channel_private_key(SilcClient client,
480 SilcClientConnection conn,
481 SilcChannelEntry channel,
487 SilcChannelPrivateKey *ret_key)
489 SilcChannelPrivateKey entry;
490 unsigned char hash[SILC_HASH_MAXLEN];
491 SilcSKEKeyMaterial keymat;
493 if (!client || !conn || !channel)
497 cipher = SILC_DEFAULT_CIPHER;
499 hmac = SILC_DEFAULT_HMAC;
501 if (!silc_cipher_is_supported(cipher))
503 if (!silc_hmac_is_supported(hmac))
506 if (!channel->internal.private_keys) {
507 channel->internal.private_keys = silc_dlist_init();
508 if (!channel->internal.private_keys)
512 /* Produce the key material */
513 keymat = silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
514 conn->internal->sha1hash);
519 entry = silc_calloc(1, sizeof(*entry));
521 silc_ske_free_key_material(keymat);
524 entry->name = name ? strdup(name) : NULL;
526 /* Allocate the cipher and set the key */
527 if (!silc_cipher_alloc(cipher, &entry->cipher)) {
529 silc_free(entry->name);
530 silc_ske_free_key_material(keymat);
533 silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
534 keymat->enc_key_len, TRUE);
536 /* Generate HMAC key from the channel key data and set it */
537 if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
539 silc_free(entry->name);
540 silc_cipher_free(entry->cipher);
541 silc_ske_free_key_material(keymat);
544 silc_hash_make(silc_hmac_get_hash(entry->hmac), keymat->send_enc_key,
545 keymat->enc_key_len / 8, hash);
546 silc_hmac_set_key(entry->hmac, hash,
547 silc_hash_len(silc_hmac_get_hash(entry->hmac)));
548 memset(hash, 0, sizeof(hash));
550 /* Add to the private keys list */
551 silc_dlist_add(channel->internal.private_keys, entry);
553 if (!channel->internal.curr_key)
554 channel->internal.curr_key = entry;
556 /* Free the key material */
557 silc_ske_free_key_material(keymat);
565 /* Removes all private keys from the `channel'. The old channel key is used
566 after calling this to protect the channel messages. Returns FALSE on
567 on error, TRUE otherwise. */
569 SilcBool silc_client_del_channel_private_keys(SilcClient client,
570 SilcClientConnection conn,
571 SilcChannelEntry channel)
573 SilcChannelPrivateKey entry;
575 if (!client || !conn || !channel)
578 if (!channel->internal.private_keys)
581 silc_dlist_start(channel->internal.private_keys);
582 while ((entry = silc_dlist_get(channel->internal.private_keys))) {
583 silc_dlist_del(channel->internal.private_keys, entry);
584 silc_free(entry->name);
585 silc_cipher_free(entry->cipher);
586 silc_hmac_free(entry->hmac);
590 channel->internal.curr_key = NULL;
592 silc_dlist_uninit(channel->internal.private_keys);
593 channel->internal.private_keys = NULL;
598 /* Removes and frees private key `key' from the channel `channel'. The `key'
599 is retrieved by calling the function silc_client_list_channel_private_keys.
600 The key is not used after this. If the key was last private key then the
601 old channel key is used hereafter to protect the channel messages. This
602 returns FALSE on error, TRUE otherwise. */
604 SilcBool silc_client_del_channel_private_key(SilcClient client,
605 SilcClientConnection conn,
606 SilcChannelEntry channel,
607 SilcChannelPrivateKey key)
609 SilcChannelPrivateKey entry;
611 if (!client || !conn || !channel)
614 if (!channel->internal.private_keys)
617 silc_dlist_start(channel->internal.private_keys);
618 while ((entry = silc_dlist_get(channel->internal.private_keys))) {
622 if (channel->internal.curr_key == entry)
623 channel->internal.curr_key = NULL;
625 silc_dlist_del(channel->internal.private_keys, entry);
626 silc_free(entry->name);
627 silc_cipher_free(entry->cipher);
628 silc_hmac_free(entry->hmac);
631 if (silc_dlist_count(channel->internal.private_keys) == 0) {
632 silc_dlist_uninit(channel->internal.private_keys);
633 channel->internal.private_keys = NULL;
642 /* Returns array (pointers) of private keys associated to the `channel'.
643 The caller must free the array by calling the function
644 silc_client_free_channel_private_keys. The pointers in the array may be
645 used to delete the specific key by giving the pointer as argument to the
646 function silc_client_del_channel_private_key. */
648 SilcDList silc_client_list_channel_private_keys(SilcClient client,
649 SilcClientConnection conn,
650 SilcChannelEntry channel)
652 SilcChannelPrivateKey entry;
655 if (!client || !conn || !channel)
658 if (!channel->internal.private_keys)
661 list = silc_dlist_init();
665 silc_dlist_start(channel->internal.private_keys);
666 while ((entry = silc_dlist_get(channel->internal.private_keys)))
667 silc_dlist_add(list, entry);
672 /* Sets the `key' to be used as current channel private key on the
673 `channel'. Packet sent after calling this function will be secured
676 void silc_client_current_channel_private_key(SilcClient client,
677 SilcClientConnection conn,
678 SilcChannelEntry channel,
679 SilcChannelPrivateKey key)
683 channel->internal.curr_key = key;
686 /***************************** Utility routines *****************************/
688 /* Returns the SilcChannelUser entry if the `client_entry' is joined on the
689 channel indicated by the `channel'. NULL if client is not joined on
692 SilcChannelUser silc_client_on_channel(SilcChannelEntry channel,
693 SilcClientEntry client_entry)
697 if (silc_hash_table_find(channel->user_list, client_entry, NULL,
704 /* Adds client to channel. Returns TRUE if user was added or is already
705 added to the channel, FALSE on error. Must be called with both `channel'
706 and `client_entry' locked. */
708 SilcBool silc_client_add_to_channel(SilcClient client,
709 SilcClientConnection conn,
710 SilcChannelEntry channel,
711 SilcClientEntry client_entry,
716 if (silc_client_on_channel(channel, client_entry))
719 SILC_LOG_DEBUG(("Add client %s to channel", client_entry->nickname));
721 chu = silc_calloc(1, sizeof(*chu));
725 chu->client = client_entry;
726 chu->channel = channel;
729 silc_client_ref_client(client, conn, client_entry);
730 silc_client_ref_channel(client, conn, channel);
732 silc_hash_table_add(channel->user_list, client_entry, chu);
733 silc_hash_table_add(client_entry->channels, channel, chu);
738 /* Removes client from a channel. This handles entry locking internally. */
740 SilcBool silc_client_remove_from_channel(SilcClient client,
741 SilcClientConnection conn,
742 SilcChannelEntry channel,
743 SilcClientEntry client_entry)
747 chu = silc_client_on_channel(channel, client_entry);
751 SILC_LOG_DEBUG(("Remove client %s from channel", client_entry->nickname));
753 silc_rwlock_wrlock(client_entry->internal.lock);
754 silc_rwlock_wrlock(channel->internal.lock);
756 silc_hash_table_del(chu->client->channels, chu->channel);
757 silc_hash_table_del(chu->channel->user_list, chu->client);
760 /* If channel became empty, delete it */
761 if (!silc_hash_table_count(channel->user_list))
762 silc_client_del_channel(client, conn, channel);
764 silc_rwlock_unlock(client_entry->internal.lock);
765 silc_rwlock_unlock(channel->internal.lock);
767 silc_client_unref_client(client, conn, client_entry);
768 silc_client_unref_channel(client, conn, channel);
773 /* Removes a client entry from all channels it has joined. This handles
774 entry locking internally. */
776 void silc_client_remove_from_channels(SilcClient client,
777 SilcClientConnection conn,
778 SilcClientEntry client_entry)
780 SilcHashTableList htl;
783 if (!silc_hash_table_count(client_entry->channels))
786 SILC_LOG_DEBUG(("Remove client from all joined channels"));
788 silc_rwlock_wrlock(client_entry->internal.lock);
790 silc_hash_table_list(client_entry->channels, &htl);
791 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
792 silc_rwlock_wrlock(chu->channel->internal.lock);
794 silc_hash_table_del(chu->client->channels, chu->channel);
795 silc_hash_table_del(chu->channel->user_list, chu->client);
797 /* If channel became empty, delete it */
798 if (!silc_hash_table_count(chu->channel->user_list))
799 silc_client_del_channel(client, conn, chu->channel);
801 silc_rwlock_unlock(chu->channel->internal.lock);
803 silc_client_unref_client(client, conn, chu->client);
804 silc_client_unref_channel(client, conn, chu->channel);
808 silc_rwlock_unlock(client_entry->internal.lock);
810 silc_hash_table_list_reset(&htl);
813 /* Empties channel from users. This handles entry locking internally. */
815 void silc_client_empty_channel(SilcClient client,
816 SilcClientConnection conn,
817 SilcChannelEntry channel)
819 SilcHashTableList htl;
822 silc_rwlock_wrlock(channel->internal.lock);
824 silc_hash_table_list(channel->user_list, &htl);
825 while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
826 silc_hash_table_del(chu->client->channels, chu->channel);
827 silc_hash_table_del(chu->channel->user_list, chu->client);
828 silc_client_unref_client(client, conn, chu->client);
829 silc_client_unref_channel(client, conn, chu->channel);
833 silc_rwlock_unlock(channel->internal.lock);
835 silc_hash_table_list_reset(&htl);
838 /* Save public keys to channel public key list. Removes keys that are
839 marked to be removed. Must be called with `channel' locked. */
841 SilcBool silc_client_channel_save_public_keys(SilcChannelEntry channel,
842 unsigned char *chpk_list,
843 SilcUInt32 chpk_list_len)
845 SilcArgumentDecodedList a, b;
849 chpks = silc_argument_list_parse_decoded(chpk_list, chpk_list_len,
850 SILC_ARGUMENT_PUBLIC_KEY);
854 if (!channel->channel_pubkeys) {
855 channel->channel_pubkeys = chpks;
859 silc_dlist_start(chpks);
860 while ((a = silc_dlist_get(chpks))) {
862 silc_dlist_start(channel->channel_pubkeys);
863 while ((b = silc_dlist_get(channel->channel_pubkeys))) {
864 if (silc_pkcs_public_key_compare(a->argument, b->argument)) {
870 if ((a->arg_type == 0x00 || a->arg_type == 0x03) && !found) {
871 silc_dlist_add(channel->channel_pubkeys, a);
872 silc_dlist_del(chpks, a);
873 } else if (a->arg_type == 0x01 && found) {
874 silc_dlist_del(channel->channel_pubkeys, b);
878 silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);