updates.
[silc.git] / lib / silccore / silcchannel.c
1 /*
2
3   silcchannel.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
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.
13   
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.
18
19 */
20 /* Channel Payload, Channel Message Payload and Channel Key Payload 
21    implementations. */
22 /* $Id$ */
23
24 #include "silcincludes.h"
25 #include "silcchannel.h"
26
27 /******************************************************************************
28
29                               Channel Payload
30
31 ******************************************************************************/
32
33 /* Channel Message Payload structure. Contents of this structure is parsed
34    from SILC packets. */
35 struct SilcChannelPayloadStruct {
36   unsigned short name_len;
37   unsigned char *channel_name;
38   unsigned short id_len;
39   unsigned char *channel_id;
40   unsigned int mode;
41 };
42
43 /* Parses channel payload returning new channel payload structure. */
44
45 SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer)
46 {
47   SilcChannelPayload new;
48   int ret;
49
50   SILC_LOG_DEBUG(("Parsing channel payload"));
51
52   new = silc_calloc(1, sizeof(*new));
53
54   /* Parse the Channel Payload. Ignore the padding. */
55   ret = silc_buffer_unformat(buffer,
56                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
57                                                          &new->name_len),
58                              SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
59                                                          &new->id_len),
60                              SILC_STR_UI_INT(&new->mode),
61                              SILC_STR_END);
62   if (ret == -1)
63     goto err;
64
65   if ((new->name_len < 1 || new->name_len > buffer->len) ||
66       (new->id_len < 1 || new->id_len > buffer->len)) {
67     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
68     goto err;
69   }
70
71   return new;
72
73  err:
74   silc_channel_payload_free(new);
75   return NULL;
76 }
77
78 /* Parses list of channel payloads returning list of payloads. */
79
80 SilcDList silc_channel_payload_parse_list(SilcBuffer buffer)
81 {
82   SilcDList list;
83   SilcChannelPayload new;
84   int len, ret;
85
86   SILC_LOG_DEBUG(("Parsing channel payload list"));
87
88   list = silc_dlist_init();
89
90   while (buffer->len) {
91     new = silc_calloc(1, sizeof(*new));
92     ret = silc_buffer_unformat(buffer,
93                                SILC_STR_UI16_NSTRING_ALLOC(&new->channel_name, 
94                                                            &new->name_len),
95                                SILC_STR_UI16_NSTRING_ALLOC(&new->channel_id, 
96                                                            &new->id_len),
97                                SILC_STR_UI_INT(&new->mode),
98                                SILC_STR_END);
99     if (ret == -1)
100       goto err;
101
102     if ((new->name_len < 1 || new->name_len > buffer->len) ||
103         (new->id_len < 1 || new->id_len > buffer->len)) {
104       SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
105       goto err;
106     }
107
108     len = 2 + new->name_len + 2 + new->id_len + 4;
109     if (buffer->len < len)
110       break;
111     silc_buffer_pull(buffer, len);
112
113     silc_dlist_add(list, new);
114   }
115   
116   return list;
117
118  err:
119   silc_channel_payload_list_free(list);
120   return NULL;
121 }
122
123 /* Encode new channel payload and returns it as buffer. */
124
125 SilcBuffer silc_channel_payload_encode(unsigned char *channel_name,
126                                        unsigned short channel_name_len,
127                                        unsigned char *channel_id,
128                                        unsigned int channel_id_len,
129                                        unsigned int mode)
130 {
131   SilcBuffer buffer;
132
133   SILC_LOG_DEBUG(("Encoding message payload"));
134
135   buffer = silc_buffer_alloc(2 + channel_name_len + 2 + channel_id_len + 4);
136   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
137
138   /* Encode the Channel Payload */
139   silc_buffer_format(buffer, 
140                      SILC_STR_UI_SHORT(channel_name_len),
141                      SILC_STR_UI_XNSTRING(channel_name, channel_name_len),
142                      SILC_STR_UI_SHORT(channel_id_len),
143                      SILC_STR_UI_XNSTRING(channel_id, channel_id_len),
144                      SILC_STR_UI_INT(mode),
145                      SILC_STR_END);
146
147   return buffer;
148 }
149
150 /* Free's Channel Payload */
151
152 void silc_channel_payload_free(SilcChannelPayload payload)
153 {
154   silc_free(payload->channel_name);
155   silc_free(payload->channel_id);
156   silc_free(payload);
157 }
158
159 /* Free's list of Channel Payloads */
160
161 void silc_channel_payload_list_free(SilcDList list)
162 {
163   SilcChannelPayload entry;
164
165   silc_dlist_start(list);
166   while ((entry = silc_dlist_get(list)) != SILC_LIST_END) {
167     silc_free(entry->channel_name);
168     silc_free(entry->channel_id);
169     silc_free(entry);
170     silc_dlist_del(list, entry);
171   }
172
173   silc_dlist_uninit(list);
174 }
175
176 /* Return the channel name */
177
178 unsigned char *silc_channel_get_name(SilcChannelPayload payload,
179                                      unsigned int *channel_name_len)
180 {
181   if (channel_name_len)
182     *channel_name_len = payload->name_len;
183
184   return payload->channel_name;
185 }
186
187 /* Return the channel ID */
188
189 unsigned char *silc_channel_get_id(SilcChannelPayload payload,
190                                    unsigned int *channel_id_len)
191 {
192   if (channel_id_len)
193     *channel_id_len = payload->id_len;
194
195   return payload->channel_id;
196 }
197
198 /* Return the channel ID as parsed ID. */
199
200 SilcChannelID *silc_channel_get_id_parse(SilcChannelPayload payload)
201 {
202   return silc_id_str2id(payload->channel_id, payload->id_len,
203                         SILC_ID_CHANNEL);
204 }
205
206 /* Return the mode. The mode is arbitrary. It can be the mode of the
207    channel or perhaps the mode of the client on the channel.  The protocol
208    dictates what the usage of the mode is in different circumstances. */
209
210 unsigned int silc_channel_get_mode(SilcChannelPayload payload)
211 {
212   return payload->mode;
213 }
214
215 /******************************************************************************
216
217                           Channel Message Payload
218
219 ******************************************************************************/
220
221 /* Channel Message Payload structure. Contents of this structure is parsed
222    from SILC packets. */
223 struct SilcChannelMessagePayloadStruct {
224   unsigned short data_len;
225   unsigned char *data;
226   unsigned char *mac;
227   unsigned char *iv;
228 };
229
230 /* Decrypts the channel message payload. */
231
232 int silc_channel_message_payload_decrypt(unsigned char *data,
233                                          size_t data_len,
234                                          SilcCipher cipher,
235                                          SilcHmac hmac)
236 {
237   unsigned int iv_len, mac_len;
238   unsigned char *end, *mac, mac2[32];
239
240   /* Decrypt the channel message. First push the IV out of the packet.
241      The IV is used in the decryption process. Then decrypt the message.
242      After decyprtion, take the MAC from the decrypted packet, compute MAC
243      and compare the MACs.  If they match, the decryption was successfull
244      and we have the channel message ready to be displayed. */
245   end = data + data_len;
246
247   /* Push the IV out of the packet */
248   iv_len = silc_cipher_get_block_len(cipher);
249
250   /* Decrypt the channel message */
251   silc_cipher_decrypt(cipher, data, data, data_len - iv_len, (end - iv_len));
252
253   /* Take the MAC */
254   if (hmac) {
255     mac_len = silc_hmac_len(hmac);
256     mac = (end - iv_len - mac_len);
257
258     /* Check the MAC of the message */
259     SILC_LOG_DEBUG(("Checking channel message MACs"));
260     silc_hmac_make(hmac, data, (data_len - iv_len - mac_len), mac2, &mac_len);
261     if (memcmp(mac, mac2, mac_len)) {
262       SILC_LOG_DEBUG(("Channel message MACs does not match"));
263       return FALSE;
264     }
265     SILC_LOG_DEBUG(("MAC is Ok"));
266   }
267
268   return TRUE;
269 }
270
271 /* Parses channel message payload returning new channel payload structure.
272    This also decrypts it and checks the MAC. */
273
274 SilcChannelMessagePayload 
275 silc_channel_message_payload_parse(SilcBuffer buffer,
276                                    SilcCipher cipher,
277                                    SilcHmac hmac)
278 {
279   SilcChannelMessagePayload new;
280   int ret;
281   unsigned int iv_len, mac_len;
282
283   SILC_LOG_DEBUG(("Parsing channel message payload"));
284
285   /* Decrypt the payload */
286   ret = silc_channel_message_payload_decrypt(buffer->data, buffer->len,
287                                      cipher, hmac);
288   if (ret == FALSE)
289     return NULL;
290
291   iv_len = silc_cipher_get_block_len(cipher);
292   mac_len = silc_hmac_len(hmac);
293
294   new = silc_calloc(1, sizeof(*new));
295
296   /* Parse the Channel Message Payload. Ignore the padding. */
297   ret = silc_buffer_unformat(buffer,
298                              SILC_STR_UI16_NSTRING_ALLOC(&new->data, 
299                                                          &new->data_len),
300                              SILC_STR_UI16_NSTRING(NULL, NULL),
301                              SILC_STR_UI_XNSTRING(&new->mac, mac_len),
302                              SILC_STR_UI_XNSTRING(&new->iv, iv_len),
303                              SILC_STR_END);
304   if (ret == -1)
305     goto err;
306
307   if (new->data_len < 1 || new->data_len > buffer->len) {
308     SILC_LOG_ERROR(("Incorrect channel messaeg payload in packet, "
309                     "packet dropped"));
310     goto err;
311   }
312
313   return new;
314
315  err:
316   silc_channel_message_payload_free(new);
317   return NULL;
318 }
319
320 /* Encodes channel message payload into a buffer and returns it. This is used 
321    to add channel message payload into a packet. As the channel payload is
322    encrypted separately from other parts of the packet padding must
323    be applied to the payload. */
324
325 SilcBuffer silc_channel_message_payload_encode(unsigned short data_len,
326                                                unsigned char *data,
327                                                unsigned short iv_len,
328                                                unsigned char *iv,
329                                                SilcCipher cipher,
330                                                SilcHmac hmac,
331                                                SilcRng rng)
332 {
333   int i;
334   SilcBuffer buffer;
335   unsigned int len, pad_len, mac_len;
336   unsigned char pad[SILC_PACKET_MAX_PADLEN];
337   unsigned char mac[32];
338
339   SILC_LOG_DEBUG(("Encoding channel message payload"));
340
341   /* Calculate length of padding. IV is not included into the calculation
342      since it is not encrypted. */
343   mac_len = silc_hmac_len(hmac);
344   len = 4 + data_len + mac_len;
345   pad_len = SILC_PACKET_PADLEN((len + 2));
346
347   /* Allocate channel payload buffer */
348   len += pad_len + iv_len;
349   buffer = silc_buffer_alloc(len);
350
351   /* Generate padding */
352   for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte(rng);
353
354   /* Encode the Channel Message Payload */
355   silc_buffer_pull_tail(buffer, 4 + data_len + pad_len);
356   silc_buffer_format(buffer, 
357                      SILC_STR_UI_SHORT(data_len),
358                      SILC_STR_UI_XNSTRING(data, data_len),
359                      SILC_STR_UI_SHORT(pad_len),
360                      SILC_STR_UI_XNSTRING(pad, pad_len),
361                      SILC_STR_END);
362
363   /* Compute the MAC of the channel message data */
364   silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
365
366   /* Put rest of the data to the payload */
367   silc_buffer_pull_tail(buffer, mac_len + iv_len);
368   silc_buffer_pull(buffer, 4 + data_len + pad_len);
369   silc_buffer_format(buffer, 
370                      SILC_STR_UI_XNSTRING(mac, mac_len),
371                      SILC_STR_UI_XNSTRING(iv, iv_len),
372                      SILC_STR_END);
373   silc_buffer_push(buffer, 4 + data_len + pad_len);
374
375   /* Encrypt payload of the packet. This is encrypted with the channel key. */
376   silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
377                       buffer->len - iv_len, iv);
378
379   memset(pad, 0, sizeof(pad));
380   memset(mac, 0, sizeof(mac));
381
382   return buffer;
383 }
384
385 /* Free's Channel Message Payload */
386
387 void silc_channel_message_payload_free(SilcChannelMessagePayload payload)
388 {
389   if (payload->data) {
390     memset(payload->data, 0, payload->data_len);
391     silc_free(payload->data);
392   }
393   silc_free(payload);
394 }
395
396 /* Return data */
397
398 unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload,
399                                              unsigned int *data_len)
400 {
401   if (data_len)
402     *data_len = payload->data_len;
403
404   return payload->data;
405 }
406
407 /* Return MAC. The caller knows the length of the MAC */
408
409 unsigned char *silc_channel_mesage_get_mac(SilcChannelMessagePayload payload)
410 {
411   return payload->mac;
412 }
413
414 /* Return IV. The caller knows the length of the IV */
415
416 unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload)
417 {
418   return payload->iv;
419 }
420
421 /******************************************************************************
422
423                              Channel Key Payload
424
425 ******************************************************************************/
426
427 /* Channel Key Payload structrue. Channel keys are parsed from SILC
428    packets into this structure. */
429 struct SilcChannelKeyPayloadStruct {
430   unsigned short id_len;
431   unsigned char *id;
432   unsigned short cipher_len;
433   unsigned char *cipher;
434   unsigned short key_len;
435   unsigned char *key;
436 };
437
438 /* Parses channel key payload returning new channel key payload structure */
439
440 SilcChannelKeyPayload silc_channel_key_payload_parse(SilcBuffer buffer)
441 {
442   SilcChannelKeyPayload new;
443   int ret;
444
445   SILC_LOG_DEBUG(("Parsing channel key payload"));
446
447   new = silc_calloc(1, sizeof(*new));
448
449   /* Parse the Channel Key Payload */
450   ret =
451     silc_buffer_unformat(buffer,
452                          SILC_STR_UI16_NSTRING_ALLOC(&new->id, &new->id_len),
453                          SILC_STR_UI16_NSTRING_ALLOC(&new->cipher, 
454                                                      &new->cipher_len),
455                          SILC_STR_UI16_NSTRING_ALLOC(&new->key, &new->key_len),
456                          SILC_STR_END);
457   if (ret == -1)
458     goto err;
459
460   if (new->id_len < 1 || new->key_len < 1 || new->cipher_len < 1) {
461     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
462     goto err;
463   }
464
465   return new;
466
467  err:
468   if (new->id)
469     silc_free(new->id);
470   if (new->cipher)
471     silc_free(new->cipher);
472   if (new->key)
473     silc_free(new->key);
474   silc_free(new);
475   return NULL;
476 }
477
478 /* Encodes channel key payload into a buffer and returns it. This is used 
479    to add channel key payload into a packet. */
480
481 SilcBuffer silc_channel_key_payload_encode(unsigned short id_len,
482                                            unsigned char *id,
483                                            unsigned short cipher_len,
484                                            unsigned char *cipher,
485                                            unsigned short key_len,
486                                            unsigned char *key)
487 {
488   SilcBuffer buffer;
489   unsigned int len;
490
491   SILC_LOG_DEBUG(("Encoding channel key payload"));
492
493   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
494      2 + cipher */
495   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
496   buffer = silc_buffer_alloc(len);
497
498   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
499
500   /* Encode the Channel Payload */
501   silc_buffer_format(buffer, 
502                      SILC_STR_UI_SHORT(id_len),
503                      SILC_STR_UI_XNSTRING(id, id_len),
504                      SILC_STR_UI_SHORT(cipher_len),
505                      SILC_STR_UI_XNSTRING(cipher, cipher_len),
506                      SILC_STR_UI_SHORT(key_len),
507                      SILC_STR_UI_XNSTRING(key, key_len),
508                      SILC_STR_END);
509
510   return buffer;
511 }
512
513 /* Free's Channel Key Payload */
514
515 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
516 {
517   if (payload) {
518     if (payload->id)
519       silc_free(payload->id);
520     if (payload->cipher)
521       silc_free(payload->cipher);
522     if (payload->key) {
523       memset(payload->key, 0, payload->key_len);
524       silc_free(payload->key);
525     }
526     silc_free(payload);
527   }
528 }
529
530 /* Return ID */
531
532 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
533                                        unsigned int *id_len)
534 {
535   if (id_len)
536     *id_len = payload->id_len;
537
538   return payload->id;
539 }
540
541 /* Return cipher name */
542
543 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
544                                            unsigned int *cipher_len)
545 {
546   if (cipher_len)
547     *cipher_len = payload->cipher_len;
548
549   return payload->cipher;
550 }
551
552 /* Return key */
553
554 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
555                                         unsigned int *key_len)
556 {
557   if (key_len)
558     *key_len = payload->key_len;
559
560   return payload->key;
561 }