5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
21 /* This file includes the private message sending and receiving routines
22 and private message key handling routines. */
24 #include "clientlibincludes.h"
25 #include "client_internal.h"
27 /* Sends private message to remote client. If private message key has
28 not been set with this client then the message will be encrypted using
29 normal session keys. Private messages are special packets in SILC
30 network hence we need this own function for them. This is similiar
31 to silc_client_packet_send_to_channel except that we send private
32 message. The `data' is the private message. If the `force_send' is
33 TRUE the packet is sent immediately. */
35 void silc_client_send_private_message(SilcClient client,
36 SilcClientConnection conn,
37 SilcClientEntry client_entry,
38 SilcMessageFlags flags,
43 SilcSocketConnection sock = conn->sock;
45 SilcPacketContext packetdata;
49 SILC_LOG_DEBUG(("Sending private message"));
51 /* Encode private message payload */
52 buffer = silc_private_message_payload_encode(flags,
54 client_entry->send_key);
56 /* If we don't have private message specific key then private messages
57 are just as any normal packet thus call normal packet sending. If
58 the key exist then the encryption process is a bit different and
59 will be done in the rest of this function. */
60 if (!client_entry->send_key) {
61 silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE,
62 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
63 buffer->data, buffer->len, force_send);
67 /* We have private message specific key */
69 /* Get data used in the encryption */
70 cipher = client_entry->send_key;
71 hmac = conn->hmac_send;
73 /* Set the packet context pointers. */
74 packetdata.flags = SILC_PACKET_FLAG_PRIVMSG_KEY;
75 packetdata.type = SILC_PACKET_PRIVATE_MESSAGE;
76 packetdata.src_id = conn->local_id_data;
77 packetdata.src_id_len = silc_id_get_len(conn->local_id, SILC_ID_CLIENT);
78 packetdata.src_id_type = SILC_ID_CLIENT;
79 packetdata.dst_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
80 packetdata.dst_id_len = silc_id_get_len(client_entry->id, SILC_ID_CLIENT);
81 packetdata.dst_id_type = SILC_ID_CLIENT;
82 packetdata.truelen = buffer->len + SILC_PACKET_HEADER_LEN +
83 packetdata.src_id_len + packetdata.dst_id_len;
84 packetdata.padlen = SILC_PACKET_PADLEN((SILC_PACKET_HEADER_LEN +
85 packetdata.src_id_len +
86 packetdata.dst_id_len));
88 /* Prepare outgoing data buffer for packet sending */
89 silc_packet_send_prepare(sock,
90 SILC_PACKET_HEADER_LEN +
91 packetdata.src_id_len +
92 packetdata.dst_id_len,
96 packetdata.buffer = sock->outbuf;
98 /* Put the actual encrypted message payload data into the buffer. */
99 silc_buffer_put(sock->outbuf, buffer->data, buffer->len);
101 /* Create the outgoing packet */
102 silc_packet_assemble(&packetdata);
104 /* Encrypt the header and padding of the packet. */
105 cipher = conn->send_key;
106 silc_packet_encrypt(cipher, hmac, sock->outbuf, SILC_PACKET_HEADER_LEN +
107 packetdata.src_id_len + packetdata.dst_id_len +
110 SILC_LOG_HEXDUMP(("Private message packet, len %d", sock->outbuf->len),
111 sock->outbuf->data, sock->outbuf->len);
113 /* Now actually send the packet */
114 silc_client_packet_send_real(client, sock, force_send, FALSE);
115 silc_free(packetdata.dst_id);
118 silc_buffer_free(buffer);
121 static void silc_client_private_message_cb(SilcClient client,
122 SilcClientConnection conn,
123 SilcClientEntry *clients,
124 uint32 clients_count,
127 SilcPacketContext *packet = (SilcPacketContext *)context;
130 silc_packet_context_free(packet);
134 silc_client_private_message(client, conn->sock, packet);
135 silc_packet_context_free(packet);
138 /* Private message received. This processes the private message and
139 finally displays it on the screen. */
141 void silc_client_private_message(SilcClient client,
142 SilcSocketConnection sock,
143 SilcPacketContext *packet)
145 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
146 SilcPrivateMessagePayload payload = NULL;
147 SilcIDCacheEntry id_cache;
148 SilcClientID *remote_id = NULL;
149 SilcClientEntry remote_client;
150 SilcMessageFlags flags;
152 if (packet->src_id_type != SILC_ID_CLIENT)
155 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
160 /* Check whether we know this client already */
161 if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)remote_id,
163 silc_hash_client_id_compare, NULL,
165 /* Resolve the client info */
166 silc_client_get_client_by_id_resolve(client, conn, remote_id,
167 silc_client_private_message_cb,
168 silc_packet_context_dup(packet));
172 remote_client = (SilcClientEntry)id_cache->context;
174 /* Parse the payload and decrypt it also if private message key is set */
175 payload = silc_private_message_payload_parse(packet->buffer,
176 remote_client->receive_key);
178 silc_free(remote_id);
182 flags = silc_private_message_get_flags(payload);
184 /* Pass the private message to application */
185 client->ops->private_message(client, conn, remote_client, flags,
186 silc_private_message_get_message(payload,
189 /* See if we are away (gone). If we are away we will reply to the
190 sender with the set away message. */
191 if (conn->away && conn->away->away && !(flags & SILC_MESSAGE_FLAG_NOREPLY)) {
192 /* If it's me, ignore */
193 if (SILC_ID_CLIENT_COMPARE(remote_id, conn->local_id))
196 /* Send the away message */
197 silc_client_send_private_message(client, conn, remote_client,
198 SILC_MESSAGE_FLAG_AUTOREPLY |
199 SILC_MESSAGE_FLAG_NOREPLY,
201 strlen(conn->away->away), TRUE);
206 silc_private_message_payload_free(payload);
208 silc_free(remote_id);
211 /* Function that actually employes the received private message key */
213 static void silc_client_private_message_key_cb(SilcClient client,
214 SilcClientConnection conn,
215 SilcClientEntry *clients,
216 uint32 clients_count,
219 SilcPacketContext *packet = (SilcPacketContext *)context;
222 unsigned char *cipher;
228 /* Parse the private message key payload */
229 ret = silc_buffer_unformat(packet->buffer,
230 SILC_STR_UI16_NSTRING(&key, &key_len),
231 SILC_STR_UI16_STRING(&cipher),
236 if (key_len > packet->buffer->len)
239 /* Now take the key in use */
240 if (!silc_client_add_private_message_key(client, conn, clients[0],
241 cipher, key, key_len, FALSE, TRUE))
244 /* Print some info for application */
245 client->ops->say(client, conn,
246 "Received private message key from %s%s%s %s%s%s",
247 clients[0]->nickname,
248 clients[0]->server ? "@" : "",
249 clients[0]->server ? clients[0]->server : "",
250 clients[0]->username ? "(" : "",
251 clients[0]->username ? clients[0]->username : "",
252 clients[0]->username ? ")" : "");
255 silc_packet_context_free(packet);
258 /* Processes incoming Private Message Key payload. The libary always
259 accepts the key and takes it into use. */
261 void silc_client_private_message_key(SilcClient client,
262 SilcSocketConnection sock,
263 SilcPacketContext *packet)
265 SilcClientID *remote_id;
267 if (packet->src_id_type != SILC_ID_CLIENT)
270 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
275 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
276 silc_client_private_message_key_cb,
277 silc_packet_context_dup(packet));
278 silc_free(remote_id);
281 /* Adds private message key to the client library. The key will be used to
282 encrypt all private message between the client and the remote client
283 indicated by the `client_entry'. If the `key' is NULL and the boolean
284 value `generate_key' is TRUE the library will generate random key.
285 The `key' maybe for example pre-shared-key, passphrase or similar.
286 The `cipher' MAY be provided but SHOULD be NULL to assure that the
287 requirements of the SILC protocol are met. The API, however, allows
288 to allocate any cipher.
290 If `responder' is TRUE then the sending and receiving keys will be
291 set according the client being the receiver of the private key. If
292 FALSE the client is being the sender (or negotiator) of the private
295 It is not necessary to set key for normal private message usage. If the
296 key is not set then the private messages are encrypted using normal
297 session keys. Setting the private key, however, increases the security.
299 Returns FALSE if the key is already set for the `client_entry', TRUE
302 int silc_client_add_private_message_key(SilcClient client,
303 SilcClientConnection conn,
304 SilcClientEntry client_entry,
311 unsigned char private_key[32];
314 SilcSKEKeyMaterial *keymat;
316 assert(client_entry);
318 /* Return FALSE if key already set */
319 if (client_entry->send_key && client_entry->receive_key)
323 cipher = "aes-256-cbc";
325 /* Check the requested cipher */
326 if (!silc_cipher_is_supported(cipher))
329 /* Generate key if not provided */
330 if (generate_key == TRUE) {
332 for (i = 0; i < len; i++) private_key[i] = silc_rng_get_byte(client->rng);
335 client_entry->generated = TRUE;
339 client_entry->key = silc_calloc(key_len, sizeof(*client_entry->key));
340 memcpy(client_entry->key, key, key_len);
341 client_entry->key_len = key_len;
343 /* Produce the key material as the protocol defines */
344 keymat = silc_calloc(1, sizeof(*keymat));
345 if (silc_ske_process_key_material_data(key, key_len, 16, 256, 16,
346 client->md5hash, keymat)
347 != SILC_SKE_STATUS_OK)
350 /* Allocate the ciphers */
351 silc_cipher_alloc(cipher, &client_entry->send_key);
352 silc_cipher_alloc(cipher, &client_entry->receive_key);
355 if (responder == TRUE) {
356 silc_cipher_set_key(client_entry->send_key, keymat->receive_enc_key,
357 keymat->enc_key_len);
358 silc_cipher_set_iv(client_entry->send_key, keymat->receive_iv);
359 silc_cipher_set_key(client_entry->receive_key, keymat->send_enc_key,
360 keymat->enc_key_len);
361 silc_cipher_set_iv(client_entry->receive_key, keymat->send_iv);
363 silc_cipher_set_key(client_entry->send_key, keymat->send_enc_key,
364 keymat->enc_key_len);
365 silc_cipher_set_iv(client_entry->send_key, keymat->send_iv);
366 silc_cipher_set_key(client_entry->receive_key, keymat->receive_enc_key,
367 keymat->enc_key_len);
368 silc_cipher_set_iv(client_entry->receive_key, keymat->receive_iv);
371 /* Free the key material */
372 silc_ske_free_key_material(keymat);
377 /* Same as above but takes the key material from the SKE key material
378 structure. This structure is received if the application uses the
379 silc_client_send_key_agreement to negotiate the key material. The
380 `cipher' SHOULD be provided as it is negotiated also in the SKE
383 int silc_client_add_private_message_key_ske(SilcClient client,
384 SilcClientConnection conn,
385 SilcClientEntry client_entry,
387 SilcSKEKeyMaterial *key,
390 assert(client_entry);
392 /* Return FALSE if key already set */
393 if (client_entry->send_key && client_entry->receive_key)
397 cipher = "aes-256-cbc";
399 /* Check the requested cipher */
400 if (!silc_cipher_is_supported(cipher))
403 /* Allocate the ciphers */
404 silc_cipher_alloc(cipher, &client_entry->send_key);
405 silc_cipher_alloc(cipher, &client_entry->receive_key);
408 if (responder == TRUE) {
409 silc_cipher_set_key(client_entry->send_key, key->receive_enc_key,
411 silc_cipher_set_iv(client_entry->send_key, key->receive_iv);
412 silc_cipher_set_key(client_entry->receive_key, key->send_enc_key,
414 silc_cipher_set_iv(client_entry->receive_key, key->send_iv);
416 silc_cipher_set_key(client_entry->send_key, key->send_enc_key,
418 silc_cipher_set_iv(client_entry->send_key, key->send_iv);
419 silc_cipher_set_key(client_entry->receive_key, key->receive_enc_key,
421 silc_cipher_set_iv(client_entry->receive_key, key->receive_iv);
427 /* Sends private message key payload to the remote client indicated by
428 the `client_entry'. If the `force_send' is TRUE the packet is sent
429 immediately. Returns FALSE if error occurs, TRUE otherwise. The
430 application should call this function after setting the key to the
433 Note that the key sent using this function is sent to the remote client
434 through the SILC network. The packet is protected using normal session
437 int silc_client_send_private_message_key(SilcClient client,
438 SilcClientConnection conn,
439 SilcClientEntry client_entry,
442 SilcSocketConnection sock = conn->sock;
446 if (!client_entry->send_key || !client_entry->key)
449 SILC_LOG_DEBUG(("Sending private message key"));
451 cipher_len = strlen(client_entry->send_key->cipher->name);
453 /* Create private message key payload */
454 buffer = silc_buffer_alloc(2 + client_entry->key_len);
455 silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
456 silc_buffer_format(buffer,
457 SILC_STR_UI_SHORT(client_entry->key_len),
458 SILC_STR_UI_XNSTRING(client_entry->key,
459 client_entry->key_len),
460 SILC_STR_UI_SHORT(cipher_len),
461 SILC_STR_UI_XNSTRING(client_entry->send_key->cipher->name,
465 /* Send the packet */
466 silc_client_packet_send(client, sock, SILC_PACKET_PRIVATE_MESSAGE_KEY,
467 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
468 buffer->data, buffer->len, force_send);
474 /* Removes the private message from the library. The key won't be used
475 after this to protect the private messages with the remote `client_entry'
476 client. Returns FALSE on error, TRUE otherwise. */
478 int silc_client_del_private_message_key(SilcClient client,
479 SilcClientConnection conn,
480 SilcClientEntry client_entry)
482 assert(client_entry);
484 if (!client_entry->send_key && !client_entry->receive_key)
487 silc_cipher_free(client_entry->send_key);
488 silc_cipher_free(client_entry->receive_key);
490 if (client_entry->key) {
491 memset(client_entry->key, 0, client_entry->key_len);
492 silc_free(client_entry->key);
495 client_entry->send_key = NULL;
496 client_entry->receive_key = NULL;
497 client_entry->key = NULL;
502 /* Returns array of set private message keys associated to the connection
503 `conn'. Returns allocated SilcPrivateMessageKeys array and the array
504 count to the `key_count' argument. The array must be freed by the caller
505 by calling the silc_client_free_private_message_keys function. Note:
506 the keys returned in the array is in raw format. It might not be desired
507 to show the keys as is. The application might choose not to show the keys
508 at all or to show the fingerprints of the keys. */
510 SilcPrivateMessageKeys
511 silc_client_list_private_message_keys(SilcClient client,
512 SilcClientConnection conn,
515 SilcPrivateMessageKeys keys;
517 SilcIDCacheEntry id_cache;
518 SilcIDCacheList list;
519 SilcClientEntry entry;
521 if (!silc_idcache_get_all(conn->client_cache, &list))
524 if (!silc_idcache_list_count(list)) {
525 silc_idcache_list_free(list);
529 keys = silc_calloc(silc_idcache_list_count(list), sizeof(*keys));
531 silc_idcache_list_first(list, &id_cache);
533 entry = (SilcClientEntry)id_cache->context;
535 if (entry->send_key) {
536 keys[count].client_entry = entry;
537 keys[count].cipher = entry->send_key->cipher->name;
538 keys[count].key = entry->generated == FALSE ? entry->key : NULL;
539 keys[count].key_len = entry->generated == FALSE ? entry->key_len : 0;
543 if (!silc_idcache_list_next(list, &id_cache))
553 /* Frees the SilcPrivateMessageKeys array returned by the function
554 silc_client_list_private_message_keys. */
556 void silc_client_free_private_message_keys(SilcPrivateMessageKeys keys,
562 /* Sets away `message'. The away message may be set when the client's
563 mode is changed to SILC_UMODE_GONE and the client whishes to reply
564 to anyone who sends private message. The `message' will be sent
565 automatically back to the the client who send private message. If
566 away message is already set this replaces the old message with the
567 new one. If `message' is NULL the old away message is removed.
568 The sender may freely free the memory of the `message'. */
570 void silc_client_set_away_message(SilcClient client,
571 SilcClientConnection conn,
574 if (!message && conn->away) {
575 silc_free(conn->away->away);
576 silc_free(conn->away);
582 conn->away = silc_calloc(1, sizeof(*conn->away));
583 if (conn->away->away)
584 silc_free(conn->away->away);
585 conn->away->away = strdup(message);