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