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