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(&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_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     silc_free(payload);
180 }
181
182 /* Return data */
183
184 unsigned char *silc_channel_get_data(SilcChannelPayload payload,
185                                      unsigned int *data_len)
186 {
187   if (data_len)
188     *data_len = payload->data_len;
189
190   return payload->data;
191 }
192
193 /* Return MAC. The caller knows the length of the MAC */
194
195 unsigned char *silc_channel_get_mac(SilcChannelPayload payload)
196 {
197   return payload->mac;
198 }
199
200 /* Return IV. The caller knows the length of the IV */
201
202 unsigned char *silc_channel_get_iv(SilcChannelPayload payload)
203 {
204   return payload->iv;
205 }
206
207 /******************************************************************************
208
209                              Channel Key Payload
210
211 ******************************************************************************/
212
213 /* Channel Key Payload structrue. Channel keys are parsed from SILC
214    packets into this structure. */
215 struct SilcChannelKeyPayloadStruct {
216   unsigned short id_len;
217   unsigned char *id;
218   unsigned short cipher_len;
219   unsigned char *cipher;
220   unsigned short key_len;
221   unsigned char *key;
222 };
223
224 /* Parses channel key payload returning new channel key payload structure */
225
226 SilcChannelKeyPayload silc_channel_key_payload_parse(SilcBuffer buffer)
227 {
228   SilcChannelKeyPayload new;
229   int ret;
230
231   SILC_LOG_DEBUG(("Parsing channel key payload"));
232
233   new = silc_calloc(1, sizeof(*new));
234
235   /* Parse the Channel Key Payload */
236   ret =
237     silc_buffer_unformat(buffer,
238                          SILC_STR_UI16_NSTRING_ALLOC(&new->id, &new->id_len),
239                          SILC_STR_UI16_NSTRING_ALLOC(&new->cipher, 
240                                                      &new->cipher_len),
241                          SILC_STR_UI16_NSTRING_ALLOC(&new->key, &new->key_len),
242                          SILC_STR_END);
243   if (ret == -1)
244     goto err;
245
246   if (new->id_len < 1 || new->key_len < 1 || new->cipher_len < 1) {
247     SILC_LOG_ERROR(("Incorrect channel key payload in packet"));
248     goto err;
249   }
250
251   return new;
252
253  err:
254   if (new->id)
255     silc_free(new->id);
256   if (new->cipher)
257     silc_free(new->cipher);
258   if (new->key)
259     silc_free(new->key);
260   silc_free(new);
261   return NULL;
262 }
263
264 /* Encodes channel key payload into a buffer and returns it. This is used 
265    to add channel key payload into a packet. */
266
267 SilcBuffer silc_channel_key_payload_encode(unsigned short id_len,
268                                            unsigned char *id,
269                                            unsigned short cipher_len,
270                                            unsigned char *cipher,
271                                            unsigned short key_len,
272                                            unsigned char *key)
273 {
274   SilcBuffer buffer;
275   unsigned int len;
276
277   SILC_LOG_DEBUG(("Encoding channel key payload"));
278
279   /* Allocate channel payload buffer. Length is 2 + id + 2 + key + 
280      2 + cipher */
281   len = 2 + id_len + 2 + key_len + 2 + cipher_len;
282   buffer = silc_buffer_alloc(len);
283
284   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
285
286   /* Encode the Channel Payload */
287   silc_buffer_format(buffer, 
288                      SILC_STR_UI_SHORT(id_len),
289                      SILC_STR_UI_XNSTRING(id, id_len),
290                      SILC_STR_UI_SHORT(cipher_len),
291                      SILC_STR_UI_XNSTRING(cipher, cipher_len),
292                      SILC_STR_UI_SHORT(key_len),
293                      SILC_STR_UI_XNSTRING(key, key_len),
294                      SILC_STR_END);
295
296   return buffer;
297 }
298
299 /* Free's Channel Key Payload */
300
301 void silc_channel_key_payload_free(SilcChannelKeyPayload payload)
302 {
303   if (payload) {
304     if (payload->id)
305       silc_free(payload->id);
306     if (payload->cipher)
307       silc_free(payload->cipher);
308     if (payload->key) {
309       memset(payload->key, 0, payload->key_len);
310       silc_free(payload->key);
311     }
312     silc_free(payload);
313   }
314 }
315
316 /* Return ID */
317
318 unsigned char *silc_channel_key_get_id(SilcChannelKeyPayload payload, 
319                                        unsigned int *id_len)
320 {
321   if (id_len)
322     *id_len = payload->id_len;
323
324   return payload->id;
325 }
326
327 /* Return cipher name */
328
329 unsigned char *silc_channel_key_get_cipher(SilcChannelKeyPayload payload,
330                                            unsigned int *cipher_len)
331 {
332   if (cipher_len)
333     *cipher_len = payload->cipher_len;
334
335   return payload->cipher;
336 }
337
338 /* Return key */
339
340 unsigned char *silc_channel_key_get_key(SilcChannelKeyPayload payload,
341                                         unsigned int *key_len)
342 {
343   if (key_len)
344     *key_len = payload->key_len;
345
346   return payload->key;
347 }