Protocol 1.2 integration continues.
[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                                           bool check_mac)
230 {
231   SilcUInt32 iv_len, mac_len;
232   unsigned char *mac, mac2[32];
233
234   mac_len = silc_hmac_len(hmac);
235   iv_len = silc_cipher_get_block_len(cipher);
236
237   if (data_len < mac_len)
238     return FALSE;
239
240   if (check_mac) {
241     /* Take the MAC */
242     mac = data + (data_len - mac_len);
243
244     /* Check the MAC of the message */
245     SILC_LOG_DEBUG(("Checking channel message MAC"));
246     silc_hmac_init(hmac);
247     silc_hmac_update(hmac, data, data_len - mac_len);
248     silc_hmac_final(hmac, mac2, &mac_len);
249     if (memcmp(mac, mac2, mac_len)) {
250       SILC_LOG_DEBUG(("Channel message MAC does not match"));
251       return FALSE;
252     }
253     SILC_LOG_DEBUG(("MAC is Ok"));
254   }
255
256   /* Decrypt the channel message */
257   silc_cipher_decrypt(cipher, data, data,
258                       data_len - iv_len - mac_len,
259                       data + (data_len - iv_len - mac_len));
260   return TRUE;
261 }
262
263 /* Parses channel message payload returning new channel payload structure.
264    This also decrypts it and checks the MAC. */
265
266 SilcChannelMessagePayload 
267 silc_channel_message_payload_parse(unsigned char *payload,
268                                    SilcUInt32 payload_len,
269                                    SilcCipher cipher,
270                                    SilcHmac hmac)
271 {
272   SilcBufferStruct buffer;
273   SilcChannelMessagePayload newp;
274   int ret;
275   SilcUInt32 iv_len, mac_len;
276
277   SILC_LOG_DEBUG(("Parsing channel message payload"));
278
279   silc_buffer_set(&buffer, payload, payload_len);
280
281   /* Decrypt the payload */
282   ret = silc_channel_message_payload_decrypt(buffer.data, buffer.len,
283                                              cipher, hmac, TRUE);
284   if (ret == FALSE)
285     return NULL;
286
287   iv_len = silc_cipher_get_block_len(cipher);
288   mac_len = silc_hmac_len(hmac);
289
290   newp = silc_calloc(1, sizeof(*newp));
291   if (!newp)
292     return NULL;
293
294   /* Parse the Channel Message Payload. */
295   ret = silc_buffer_unformat(&buffer,
296                              SILC_STR_UI_SHORT(&newp->flags),
297                              SILC_STR_UI16_NSTRING_ALLOC(&newp->data, 
298                                                          &newp->data_len),
299                              SILC_STR_UI16_NSTRING_ALLOC(&newp->pad, 
300                                                          &newp->pad_len),
301                              SILC_STR_UI_XNSTRING(&newp->iv, iv_len),
302                              SILC_STR_UI_XNSTRING(&newp->mac, mac_len),
303                              SILC_STR_END);
304   if (ret == -1)
305     goto err;
306
307   if ((newp->data_len > buffer.len - 6 - mac_len - iv_len) ||
308       (newp->pad_len + newp->data_len > buffer.len - 6 - mac_len - iv_len)) {
309     SILC_LOG_ERROR(("Incorrect channel message payload in packet, "
310                     "packet dropped"));
311     goto err;
312   }
313
314   newp->iv_len = iv_len;
315
316   return newp;
317
318  err:
319   silc_channel_message_payload_free(newp);
320   return NULL;
321 }
322
323 /* This function is used to encrypt the Channel Messsage Payload which is
324    the `data' and `data_len'.  This is used internally by the Channel Message
325    Payload encoding routines but application may call this too if needed. 
326    The `data_len' is the data lenght which is used to create MAC out of.
327    The `true_len' is the true length of `data' message payload and is used
328    assemble rest of the packet after MAC creation. The `true_len' length
329    packet will then be encrypted. */
330
331 bool silc_channel_message_payload_encrypt(unsigned char *data,
332                                           SilcUInt32 data_len,
333                                           unsigned char *iv,
334                                           SilcUInt32 iv_len,
335                                           SilcCipher cipher,
336                                           SilcHmac hmac)
337 {
338   unsigned char mac[32];
339   SilcUInt32 mac_len;
340   SilcBufferStruct buf;
341
342   /* Encrypt payload of the packet. This is encrypted with the channel key. */
343   silc_cipher_encrypt(cipher, data, data, data_len - iv_len, iv);
344
345   /* Compute the MAC of the encrypted channel message data */
346   silc_hmac_init(hmac);
347   silc_hmac_update(hmac, data, data_len);
348   silc_hmac_final(hmac, mac, &mac_len);
349
350   /* Put rest of the data to the payload */
351   silc_buffer_set(&buf, data, data_len + mac_len);
352   silc_buffer_pull(&buf, data_len);
353   silc_buffer_put(&buf, mac, mac_len);
354
355   return TRUE;
356 }
357
358 /* Encodes channel message payload into a buffer and returns it. This is used 
359    to add channel message payload into a packet. As the channel payload is
360    encrypted separately from other parts of the packet padding must
361    be applied to the payload. */
362
363 SilcBuffer silc_channel_message_payload_encode(SilcMessageFlags flags,
364                                                SilcUInt16 data_len,
365                                                const unsigned char *data,
366                                                SilcUInt16 iv_len,
367                                                unsigned char *iv,
368                                                SilcCipher cipher,
369                                                SilcHmac hmac,
370                                                SilcRng rng)
371 {
372   int i;
373   SilcBuffer buffer;
374   SilcUInt32 len, pad_len, mac_len;
375   unsigned char pad[16];
376
377   SILC_LOG_DEBUG(("Encoding channel message payload"));
378
379   /* Calculate length of padding. IV is not included into the calculation
380      since it is not encrypted. */
381   mac_len = silc_hmac_len(hmac);
382   data_len = SILC_CHANNEL_MESSAGE_DATALEN(data_len, mac_len + iv_len);
383   len = 6 + data_len;
384   pad_len = SILC_CHANNEL_MESSAGE_PAD(len);
385
386   /* Allocate channel payload buffer */
387   len += pad_len + iv_len + mac_len;
388   buffer = silc_buffer_alloc(len);
389   if (!buffer)
390     return NULL;
391
392   /* Generate padding */
393   if (rng) {
394     for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte_fast(rng);
395   } else {
396     for (i = 0; i < pad_len; i++) pad[i] = silc_rng_global_get_byte_fast();
397   }
398
399   /* Encode the Channel Message Payload */
400   silc_buffer_pull_tail(buffer, 6 + data_len + pad_len + iv_len);
401   silc_buffer_format(buffer, 
402                      SILC_STR_UI_SHORT(flags),
403                      SILC_STR_UI_SHORT(data_len),
404                      SILC_STR_UI_XNSTRING(data, data_len),
405                      SILC_STR_UI_SHORT(pad_len),
406                      SILC_STR_UI_XNSTRING(pad, pad_len),
407                      SILC_STR_UI_XNSTRING(iv, iv_len),
408                      SILC_STR_END);
409
410   memset(pad, 0, sizeof(pad));
411
412   if (!silc_channel_message_payload_encrypt(buffer->data, buffer->len,
413                                             iv, iv_len, cipher, hmac)) {
414     silc_buffer_free(buffer);
415     return NULL;
416   }
417
418   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer) - buffer->len);
419
420   return buffer;
421 }
422
423 /* Free's Channel Message Payload */
424
425 void silc_channel_message_payload_free(SilcChannelMessagePayload payload)
426 {
427   if (payload->data) {
428     memset(payload->data, 0, payload->data_len);
429     silc_free(payload->data);
430   }
431   silc_free(payload);
432 }
433
434 /* Return flags */
435
436 SilcMessageFlags
437 silc_channel_message_get_flags(SilcChannelMessagePayload payload)
438 {
439   return payload->flags;
440 }
441
442 /* Return data */
443
444 unsigned char *silc_channel_message_get_data(SilcChannelMessagePayload payload,
445                                              SilcUInt32 *data_len)
446 {
447   if (data_len)
448     *data_len = payload->data_len;
449
450   return payload->data;
451 }
452
453 /* Return MAC. The caller knows the length of the MAC */
454
455 unsigned char *silc_channel_message_get_mac(SilcChannelMessagePayload payload)
456 {
457   return payload->mac;
458 }
459
460 /* Return IV. The caller knows the length of the IV */
461
462 unsigned char *silc_channel_message_get_iv(SilcChannelMessagePayload payload)
463 {
464   return payload->iv;
465 }
466
467 /******************************************************************************
468
469                              Channel Key Payload
470
471 ******************************************************************************/
472
473 /* Parses channel key payload returning new channel key payload structure */
474
475 SilcChannelKeyPayload 
476 silc_channel_key_payload_parse(const unsigned char *payload,
477                                SilcUInt32 payload_len)
478 {
479   SilcBufferStruct buffer;
480   SilcChannelKeyPayload newp;
481   int ret;
482
483   SILC_LOG_DEBUG(("Parsing channel key payload"));
484
485   silc_buffer_set(&buffer, (unsigned char *)payload, payload_len);
486   newp = silc_calloc(1, sizeof(*newp));
487   if (!newp)
488     return NULL;
489
490   /* Parse the Channel Key Payload */
491   ret =
492     silc_buffer_unformat(&buffer,
493                          SILC_STR_UI16_NSTRING_ALLOC(&newp->id, &newp->id_len),
494                          SILC_STR_UI16_NSTRING_ALLOC(&newp->cipher, 
495                                                      &newp->cipher_len),
496                          SILC_STR_UI16_NSTRING_ALLOC(&newp->key, 
497                                                      &newp->key_len),
498                          SILC_STR_END);
499   if (ret == -1)
500     goto err;
501
502   if (newp->id_len < 1 || newp->key_len < 1 || newp->cipher_len < 1 ||
503       newp->id_len + newp->cipher_len + newp->key_len > buffer.len - 6) {
504     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
505     goto err;
506   }
507
508   return newp;
509
510  err:
511   if (newp->id)
512     silc_free(newp->id);
513   if (newp->cipher)
514     silc_free(newp->cipher);
515   if (newp->key)
516     silc_free(newp->key);
517   silc_free(newp);
518   return NULL;
519 }
520
521 /* Encodes channel key payload into a buffer and returns it. This is used 
522    to add channel key payload into a packet. */
523
524 SilcBuffer silc_channel_key_payload_encode(SilcUInt16 id_len,
525                                            const unsigned char *id,
526                                            SilcUInt16 cipher_len,
527                                            const unsigned char *cipher,
528                                            SilcUInt16 key_len,
529                                            const unsigned char *key)
530 {
531   SilcBuffer buffer;
532   SilcUInt32 len;
533
534   SILC_LOG_DEBUG(("Encoding channel key payload"));
535
536   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
537      2 + cipher */
538   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
539   buffer = silc_buffer_alloc_size(len);
540   if (!buffer)
541     return NULL;
542
543   /* Encode the Channel Payload */
544   silc_buffer_format(buffer, 
545                      SILC_STR_UI_SHORT(id_len),
546                      SILC_STR_UI_XNSTRING(id, id_len),
547                      SILC_STR_UI_SHORT(cipher_len),
548                      SILC_STR_UI_XNSTRING(cipher, cipher_len),
549                      SILC_STR_UI_SHORT(key_len),
550                      SILC_STR_UI_XNSTRING(key, key_len),
551                      SILC_STR_END);
552
553   return buffer;
554 }
555
556 /* Frees Channel Key Payload */
557
558 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
559 {
560   if (payload) {
561     silc_free(payload->id);
562     silc_free(payload->cipher);
563     if (payload->key) {
564       memset(payload->key, 0, payload->key_len);
565       silc_free(payload->key);
566     }
567     silc_free(payload);
568   }
569 }
570
571 /* Return ID */
572
573 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
574                                        SilcUInt32 *id_len)
575 {
576   if (id_len)
577     *id_len = payload->id_len;
578
579   return payload->id;
580 }
581
582 /* Return cipher name */
583
584 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
585                                            SilcUInt32 *cipher_len)
586 {
587   if (cipher_len)
588     *cipher_len = payload->cipher_len;
589
590   return payload->cipher;
591 }
592
593 /* Return key */
594
595 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
596                                         SilcUInt32 *key_len)
597 {
598   if (key_len)
599     *key_len = payload->key_len;
600
601   return payload->key;
602 }