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