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 and Channel Key Payload implementations. */
21 /* $Id$ */
22
23 #include "silcincludes.h"
24 #include "silcchannel.h"
25
26 /******************************************************************************
27
28                           Channel Message Payload
29
30 ******************************************************************************/
31
32 /* Channel Message Payload structure. Contents of this structure is parsed
33    from SILC packets. */
34 struct SilcChannelPayloadStruct {
35   unsigned short data_len;
36   unsigned char *data;
37   unsigned char *mac;
38   unsigned char *iv;
39 };
40
41 /* Decrypts the channel message payload. */
42
43 int silc_channel_payload_decrypt(unsigned char *data,
44                                  size_t data_len,
45                                  SilcCipher cipher,
46                                  SilcHmac hmac)
47 {
48   unsigned int iv_len, mac_len;
49   unsigned char *end, *mac, mac2[32];
50
51   /* Decrypt the channel message. First push the IV out of the packet.
52      The IV is used in the decryption process. Then decrypt the message.
53      After decyprtion, take the MAC from the decrypted packet, compute MAC
54      and compare the MACs.  If they match, the decryption was successfull
55      and we have the channel message ready to be displayed. */
56   end = data + data_len;
57
58   /* Push the IV out of the packet */
59   iv_len = silc_cipher_get_block_len(cipher);
60
61   /* Decrypt the channel message */
62   silc_cipher_decrypt(cipher, data, data, data_len - iv_len, (end - iv_len));
63
64   /* Take the MAC */
65   if (hmac) {
66     mac_len = silc_hmac_len(hmac);
67     mac = (end - iv_len - mac_len);
68
69     /* Check the MAC of the message */
70     SILC_LOG_DEBUG(("Checking channel message MACs"));
71     silc_hmac_make(hmac, data, (data_len - iv_len - mac_len), mac2, &mac_len);
72     if (memcmp(mac, mac2, mac_len)) {
73       SILC_LOG_DEBUG(("Channel message MACs does not match"));
74       return FALSE;
75     }
76     SILC_LOG_DEBUG(("MAC is Ok"));
77   }
78
79   return TRUE;
80 }
81
82 /* Parses channel payload returning new channel payload structure. This
83    also decrypts it and checks the MAC. */
84
85 SilcChannelPayload silc_channel_payload_parse(SilcBuffer buffer,
86                                               SilcCipher cipher,
87                                               SilcHmac hmac)
88 {
89   SilcChannelPayload new;
90   int ret;
91   unsigned int iv_len, mac_len;
92
93   SILC_LOG_DEBUG(("Parsing channel payload"));
94
95   /* Decrypt the payload */
96   ret = silc_channel_payload_decrypt(buffer->data, buffer->len,
97                                      cipher, hmac);
98   if (ret == FALSE)
99     return NULL;
100
101   iv_len = silc_cipher_get_block_len(cipher);
102   mac_len = silc_hmac_len(hmac);
103
104   new = silc_calloc(1, sizeof(*new));
105
106   /* Parse the Channel Payload. Ignore the padding. */
107   ret = silc_buffer_unformat(buffer,
108                              SILC_STR_UI16_NSTRING_ALLOC(&new->data, 
109                                                          &new->data_len),
110                              SILC_STR_UI16_NSTRING(NULL, NULL),
111                              SILC_STR_UI_XNSTRING(&new->mac, mac_len),
112                              SILC_STR_UI_XNSTRING(&new->iv, iv_len),
113                              SILC_STR_END);
114   if (ret == -1)
115     goto err;
116
117   if (new->data_len < 1 || new->data_len > buffer->len) {
118     SILC_LOG_ERROR(("Incorrect channel payload in packet, packet dropped"));
119     goto err;
120   }
121
122   return new;
123
124  err:
125   silc_channel_payload_free(new);
126   return NULL;
127 }
128
129 /* Encodes channel payload into a buffer and returns it. This is used 
130    to add channel payload into a packet. As the channel payload is
131    encrypted separately from other parts of the packet padding must
132    be applied to the payload. */
133
134 SilcBuffer silc_channel_payload_encode(unsigned short data_len,
135                                        unsigned char *data,
136                                        unsigned short iv_len,
137                                        unsigned char *iv,
138                                        SilcCipher cipher,
139                                        SilcHmac hmac,
140                                        SilcRng rng)
141 {
142   int i;
143   SilcBuffer buffer;
144   unsigned int len, pad_len, mac_len;
145   unsigned char pad[SILC_PACKET_MAX_PADLEN];
146   unsigned char mac[32];
147
148   SILC_LOG_DEBUG(("Encoding channel payload"));
149
150   /* Calculate length of padding. IV is not included into the calculation
151      since it is not encrypted. */
152   mac_len = silc_hmac_len(hmac);
153   len = 4 + data_len + mac_len;
154   pad_len = SILC_PACKET_PADLEN((len + 2));
155
156   /* Allocate channel payload buffer */
157   len += pad_len + iv_len;
158   buffer = silc_buffer_alloc(len);
159
160   /* Generate padding */
161   for (i = 0; i < pad_len; i++) pad[i] = silc_rng_get_byte(rng);
162
163   /* Encode the Channel Payload */
164   silc_buffer_pull_tail(buffer, 4 + data_len + pad_len);
165   silc_buffer_format(buffer, 
166                      SILC_STR_UI_SHORT(data_len),
167                      SILC_STR_UI_XNSTRING(data, data_len),
168                      SILC_STR_UI_SHORT(pad_len),
169                      SILC_STR_UI_XNSTRING(pad, pad_len),
170                      SILC_STR_END);
171
172   /* Compute the MAC of the channel message data */
173   silc_hmac_make(hmac, buffer->data, buffer->len, mac, &mac_len);
174
175   /* Put rest of the data to the payload */
176   silc_buffer_pull_tail(buffer, mac_len + iv_len);
177   silc_buffer_pull(buffer, 4 + data_len + pad_len);
178   silc_buffer_format(buffer, 
179                      SILC_STR_UI_XNSTRING(mac, mac_len),
180                      SILC_STR_UI_XNSTRING(iv, iv_len),
181                      SILC_STR_END);
182   silc_buffer_push(buffer, 4 + data_len + pad_len);
183
184   /* Encrypt payload of the packet. This is encrypted with the channel key. */
185   silc_cipher_encrypt(cipher, buffer->data, buffer->data, 
186                       buffer->len - iv_len, iv);
187
188   memset(pad, 0, sizeof(pad));
189   memset(mac, 0, sizeof(mac));
190
191   return buffer;
192 }
193
194 /* Free's Channel Payload */
195
196 void silc_channel_payload_free(SilcChannelPayload payload)
197 {
198   if (payload) {
199     if (payload->data) {
200       memset(payload->data, 0, payload->data_len);
201       silc_free(payload->data);
202     }
203     silc_free(payload);
204   }
205 }
206
207 /* Return data */
208
209 unsigned char *silc_channel_get_data(SilcChannelPayload payload,
210                                      unsigned int *data_len)
211 {
212   if (data_len)
213     *data_len = payload->data_len;
214
215   return payload->data;
216 }
217
218 /* Return MAC. The caller knows the length of the MAC */
219
220 unsigned char *silc_channel_get_mac(SilcChannelPayload payload)
221 {
222   return payload->mac;
223 }
224
225 /* Return IV. The caller knows the length of the IV */
226
227 unsigned char *silc_channel_get_iv(SilcChannelPayload payload)
228 {
229   return payload->iv;
230 }
231
232 /******************************************************************************
233
234                              Channel Key Payload
235
236 ******************************************************************************/
237
238 /* Channel Key Payload structrue. Channel keys are parsed from SILC
239    packets into this structure. */
240 struct SilcChannelKeyPayloadStruct {
241   unsigned short id_len;
242   unsigned char *id;
243   unsigned short cipher_len;
244   unsigned char *cipher;
245   unsigned short key_len;
246   unsigned char *key;
247 };
248
249 /* Parses channel key payload returning new channel key payload structure */
250
251 SilcChannelKeyPayload silc_channel_key_payload_parse(SilcBuffer buffer)
252 {
253   SilcChannelKeyPayload new;
254   int ret;
255
256   SILC_LOG_DEBUG(("Parsing channel key payload"));
257
258   new = silc_calloc(1, sizeof(*new));
259
260   /* Parse the Channel Key Payload */
261   ret =
262     silc_buffer_unformat(buffer,
263                          SILC_STR_UI16_NSTRING_ALLOC(&new->id, &new->id_len),
264                          SILC_STR_UI16_NSTRING_ALLOC(&new->cipher, 
265                                                      &new->cipher_len),
266                          SILC_STR_UI16_NSTRING_ALLOC(&new->key, &new->key_len),
267                          SILC_STR_END);
268   if (ret == -1)
269     goto err;
270
271   if (new->id_len < 1 || new->key_len < 1 || new->cipher_len < 1) {
272     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
273     goto err;
274   }
275
276   return new;
277
278  err:
279   if (new->id)
280     silc_free(new->id);
281   if (new->cipher)
282     silc_free(new->cipher);
283   if (new->key)
284     silc_free(new->key);
285   silc_free(new);
286   return NULL;
287 }
288
289 /* Encodes channel key payload into a buffer and returns it. This is used 
290    to add channel key payload into a packet. */
291
292 SilcBuffer silc_channel_key_payload_encode(unsigned short id_len,
293                                            unsigned char *id,
294                                            unsigned short cipher_len,
295                                            unsigned char *cipher,
296                                            unsigned short key_len,
297                                            unsigned char *key)
298 {
299   SilcBuffer buffer;
300   unsigned int len;
301
302   SILC_LOG_DEBUG(("Encoding channel key payload"));
303
304   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
305      2 + cipher */
306   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
307   buffer = silc_buffer_alloc(len);
308
309   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
310
311   /* Encode the Channel Payload */
312   silc_buffer_format(buffer, 
313                      SILC_STR_UI_SHORT(id_len),
314                      SILC_STR_UI_XNSTRING(id, id_len),
315                      SILC_STR_UI_SHORT(cipher_len),
316                      SILC_STR_UI_XNSTRING(cipher, cipher_len),
317                      SILC_STR_UI_SHORT(key_len),
318                      SILC_STR_UI_XNSTRING(key, key_len),
319                      SILC_STR_END);
320
321   return buffer;
322 }
323
324 /* Free's Channel Key Payload */
325
326 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
327 {
328   if (payload) {
329     if (payload->id)
330       silc_free(payload->id);
331     if (payload->cipher)
332       silc_free(payload->cipher);
333     if (payload->key) {
334       memset(payload->key, 0, payload->key_len);
335       silc_free(payload->key);
336     }
337     silc_free(payload);
338   }
339 }
340
341 /* Return ID */
342
343 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
344                                        unsigned int *id_len)
345 {
346   if (id_len)
347     *id_len = payload->id_len;
348
349   return payload->id;
350 }
351
352 /* Return cipher name */
353
354 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
355                                            unsigned int *cipher_len)
356 {
357   if (cipher_len)
358     *cipher_len = payload->cipher_len;
359
360   return payload->cipher;
361 }
362
363 /* Return key */
364
365 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
366                                         unsigned int *key_len)
367 {
368   if (key_len)
369     *key_len = payload->key_len;
370
371   return payload->key;
372 }