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