Initial revision
[silc.git] / lib / silcske / payload.c
1 /*
2
3   payload.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 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 /* XXX TODO: This is not optimized version and should be optimized! 
21    Use *_ALLOC buffer formatting in payload decodings! */
22 /*
23  * $Id$
24  * $Log$
25  * Revision 1.1  2000/06/27 11:36:56  priikone
26  * Initial revision
27  *
28  *
29  */
30
31 #include "silcincludes.h"
32 #include "payload_internal.h"
33
34 /* Temporary buffer used in payload decoding */
35 unsigned char buf[16384];
36
37 /* Encodes Key Exchange Start Payload into a SILC Buffer to be sent
38    to the other end. */
39
40 SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
41                                             SilcSKEStartPayload *payload,
42                                             SilcBuffer *return_buffer)
43 {
44   SilcBuffer buf;
45
46   SILC_LOG_DEBUG(("Encoding KE Start Payload"));
47
48   if (!payload)
49     return SILC_SKE_STATUS_ERROR;
50
51   /* Allocate channel payload buffer. */
52   buf = silc_buffer_alloc(payload->len);
53   if (!buf) {
54     SILC_LOG_ERROR(("Could not allocate encode buffer"));
55     return SILC_SKE_STATUS_ERROR;
56   }
57
58   silc_buffer_pull_tail(buf, payload->len);
59
60   /* Encode the payload */
61   silc_buffer_format(buf,
62                      SILC_STR_UI_CHAR(0),        /* RESERVED field */
63                      SILC_STR_UI_CHAR(payload->flags),
64                      SILC_STR_UI_SHORT(payload->len),
65                      SILC_STR_UI_XNSTRING(payload->cookie, 
66                                           payload->cookie_len),
67                      SILC_STR_UI_SHORT(payload->ke_grp_len),
68                      SILC_STR_UI_XNSTRING(payload->ke_grp_list,
69                                           payload->ke_grp_len),
70                      SILC_STR_UI_SHORT(payload->pkcs_alg_len),
71                      SILC_STR_UI_XNSTRING(payload->pkcs_alg_list,
72                                           payload->pkcs_alg_len),
73                      SILC_STR_UI_SHORT(payload->enc_alg_len),
74                      SILC_STR_UI_XNSTRING(payload->enc_alg_list,
75                                           payload->enc_alg_len),
76                      SILC_STR_UI_SHORT(payload->hash_alg_len),
77                      SILC_STR_UI_XNSTRING(payload->hash_alg_list,
78                                           payload->hash_alg_len),
79                      SILC_STR_UI_SHORT(payload->comp_alg_len),
80                      SILC_STR_UI_XNSTRING(payload->comp_alg_list,
81                                           payload->comp_alg_len),
82                      SILC_STR_END);
83
84   /* Return the encoded buffer */
85   *return_buffer = buf;
86
87   SILC_LOG_HEXDUMP(("KE Start Payload"), buf->data, buf->len);
88
89   return SILC_SKE_STATUS_OK;
90 }
91
92 /* Parses the Key Exchange Start Payload. Parsed data is returned
93    to allocated payload structure. */
94
95 SilcSKEStatus 
96 silc_ske_payload_start_decode(SilcSKE ske,
97                               SilcBuffer buffer,
98                               SilcSKEStartPayload **return_payload)
99 {
100   SilcSKEStartPayload *payload;
101   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
102   unsigned char tmp;
103   int len, len2;
104
105   SILC_LOG_DEBUG(("Decoding Key Exchange Start Payload"));
106
107   SILC_LOG_HEXDUMP(("KE Start Payload"), buffer->data, buffer->len);
108
109   payload = silc_calloc(1, sizeof(*payload));
110   memset(buf, 0, sizeof(buf));
111
112   /* Parse the entire payload */
113   silc_buffer_unformat(buffer,
114                        SILC_STR_UI_CHAR(&tmp),     /* RESERVED Field */
115                        SILC_STR_UI_CHAR(&payload->flags),
116                        SILC_STR_UI_SHORT(&payload->len),
117                        SILC_STR_UI_XNSTRING(&buf, SILC_SKE_COOKIE_LEN),
118                        SILC_STR_UI_SHORT(&payload->ke_grp_len),
119                        SILC_STR_END);
120
121   if (tmp != 0) {
122     SILC_LOG_DEBUG(("Bad reserved field"));
123     status = SILC_SKE_STATUS_BAD_RESERVED_FIELD;
124     goto err;
125   }
126
127   if (payload->len != buffer->len) {
128     SILC_LOG_DEBUG(("Bad payload length"));
129     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
130     goto err;
131   }
132
133   if (payload->ke_grp_len < 1) {
134     SILC_LOG_DEBUG(("Bad payload length"));
135     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
136     goto err;
137   }
138
139   len2 = len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 2;
140   silc_buffer_pull(buffer, len);
141
142   /* Copy cookie from payload */
143   payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, 
144                                 sizeof(unsigned char));
145   payload->cookie_len = SILC_SKE_COOKIE_LEN;
146   memcpy(payload->cookie, buf, SILC_SKE_COOKIE_LEN);
147   memset(buf, 0, sizeof(buf));
148
149   silc_buffer_unformat(buffer,
150                        SILC_STR_UI_XNSTRING(&buf, payload->ke_grp_len),
151                        SILC_STR_UI_SHORT(&payload->pkcs_alg_len),
152                        SILC_STR_END);
153
154   if (payload->pkcs_alg_len < 1) {
155     SILC_LOG_DEBUG(("Bad payload length"));
156     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
157     goto err;
158   }
159
160   len2 += len = payload->ke_grp_len + 2;
161   silc_buffer_pull(buffer, len);
162
163   /* Copy KE groups from payload */
164   payload->ke_grp_list = silc_calloc(payload->ke_grp_len + 1, 
165                                      sizeof(unsigned char));
166   memcpy(payload->ke_grp_list, buf, payload->ke_grp_len);
167   memset(buf, 0, sizeof(buf));
168
169   silc_buffer_unformat(buffer,
170                        SILC_STR_UI_XNSTRING(&buf, payload->pkcs_alg_len),
171                        SILC_STR_UI_SHORT(&payload->enc_alg_len),
172                        SILC_STR_END);
173
174   if (payload->enc_alg_len < 1) {
175     SILC_LOG_DEBUG(("Bad payload length"));
176     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
177     goto err;
178   }
179
180   len2 += len = payload->pkcs_alg_len + 2;
181   silc_buffer_pull(buffer, len);
182
183   /* Copy PKCS algs from payload */
184   payload->pkcs_alg_list = silc_calloc(payload->pkcs_alg_len + 1, 
185                                        sizeof(unsigned char));
186   memcpy(payload->pkcs_alg_list, buf, payload->pkcs_alg_len);
187   memset(buf, 0, sizeof(buf));
188
189   silc_buffer_unformat(buffer,
190                        SILC_STR_UI_XNSTRING(&buf, payload->enc_alg_len),
191                        SILC_STR_UI_SHORT(&payload->hash_alg_len),
192                        SILC_STR_END);
193
194   if (payload->hash_alg_len < 1) {
195     SILC_LOG_DEBUG(("Bad payload length"));
196     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
197     goto err;
198   }
199
200   len2 += len = payload->enc_alg_len + 2;
201   silc_buffer_pull(buffer, len);
202
203   /* Copy encryption algs from payload */
204   payload->enc_alg_list = silc_calloc(payload->enc_alg_len + 1, 
205                                       sizeof(unsigned char));
206   memcpy(payload->enc_alg_list, buf, payload->enc_alg_len);
207   memset(buf, 0, sizeof(buf));
208
209   silc_buffer_unformat(buffer,
210                        SILC_STR_UI_XNSTRING(&buf, payload->hash_alg_len),
211                        SILC_STR_UI_SHORT(&payload->comp_alg_len),
212                        SILC_STR_END);
213
214   len2 += len = payload->hash_alg_len + 2;
215   silc_buffer_pull(buffer, len);
216
217   /* Copy hash algs from payload */
218   payload->hash_alg_list = silc_calloc(payload->hash_alg_len + 1, 
219                                        sizeof(unsigned char));
220   memcpy(payload->hash_alg_list, buf, payload->hash_alg_len);
221   memset(buf, 0, sizeof(buf));
222
223   if (payload->comp_alg_len) {
224     silc_buffer_unformat(buffer,
225                          SILC_STR_UI_XNSTRING(&buf, payload->comp_alg_len),
226                          SILC_STR_END);
227
228     /* Copy compression algs from payload */
229     payload->comp_alg_list = silc_calloc(payload->comp_alg_len + 1, 
230                                          sizeof(unsigned char));
231     memcpy(payload->comp_alg_list, buf, payload->comp_alg_len);
232     memset(buf, 0, sizeof(buf));
233   }
234
235   silc_buffer_push(buffer, len2);
236
237   /* Return the payload */
238   *return_payload = payload;
239
240   return SILC_SKE_STATUS_OK;
241
242  err:
243   silc_ske_payload_start_free(payload);
244
245   return status;
246 }
247
248 /* Free's Start Payload */
249
250 void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
251 {
252   if (payload) {
253     if (payload->cookie)
254       silc_free(payload->cookie);
255     if (payload->ke_grp_list)
256       silc_free(payload->ke_grp_list);
257     if (payload->pkcs_alg_list)
258       silc_free(payload->pkcs_alg_list);
259     if (payload->enc_alg_list)
260       silc_free(payload->enc_alg_list);
261     if (payload->hash_alg_list)
262       silc_free(payload->hash_alg_list);
263     if (payload->comp_alg_list)
264       silc_free(payload->comp_alg_list);
265     silc_free(payload);
266   }
267 }
268
269 /* Encodes Key Exchange 1 Payload into a SILC Buffer to be sent
270    to the other end. */
271
272 SilcSKEStatus silc_ske_payload_one_encode(SilcSKE ske,
273                                           SilcSKEOnePayload *payload,
274                                           SilcBuffer *return_buffer)
275 {
276   SilcBuffer buf;
277   unsigned char *e_str;
278   unsigned short e_len;
279
280   SILC_LOG_DEBUG(("Encoding KE 1 Payload"));
281
282   if (!payload)
283     return SILC_SKE_STATUS_ERROR;
284
285   /* Encode the integer into HEX string */
286   e_len = silc_mp_sizeinbase(&payload->e, 16);
287   e_str = silc_calloc(e_len + 1, sizeof(unsigned char));
288   silc_mp_get_str(e_str, 16, &payload->e);
289
290   /* Allocate channel payload buffer. The length of the buffer
291      is 2 + e. */
292   buf = silc_buffer_alloc(e_len + 2);
293   if (!buf) {
294     SILC_LOG_ERROR(("Could not allocate encode buffer"));
295     return SILC_SKE_STATUS_ERROR;
296   }
297
298   silc_buffer_pull_tail(buf, e_len + 2);
299
300   /* Encode the payload */
301   silc_buffer_format(buf, 
302                      SILC_STR_UI_SHORT(e_len + 2),
303                      SILC_STR_UI_XNSTRING(e_str, e_len),
304                      SILC_STR_END);
305
306   /* Return encoded buffer */
307   *return_buffer = buf;
308
309   memset(e_str, 'F', e_len);
310   silc_free(e_str);
311
312   return SILC_SKE_STATUS_OK;
313 }
314
315 /* Parses the Key Exchange 1 Payload. Parsed data is returned
316    to allocated payload structure. */
317
318 SilcSKEStatus silc_ske_payload_one_decode(SilcSKE ske,
319                                           SilcBuffer buffer,
320                                           SilcSKEOnePayload **return_payload)
321 {
322   SilcSKEOnePayload *payload;
323   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
324   unsigned short e_len;
325
326   SILC_LOG_DEBUG(("Decoding Key Exchange 1 Payload"));
327
328   SILC_LOG_HEXDUMP(("KE 1 Payload"), buffer->data, buffer->len);
329
330   payload = silc_calloc(1, sizeof(*payload));
331
332   memset(buf, 0, sizeof(buf));
333
334   /* Parse the payload */
335   silc_buffer_unformat(buffer,
336                        SILC_STR_UI_SHORT(&e_len),
337                        SILC_STR_END);
338                        
339   if (e_len < 1) {
340     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
341     goto err;
342   }
343
344   if (e_len != buffer->len) {
345     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
346     goto err;
347   }
348
349   /* Length includes the length field length as well. Remove it. */
350   e_len -= 2;
351
352   silc_buffer_unformat(buffer,
353                        SILC_STR_UI_SHORT(NULL),
354                        SILC_STR_UI_XNSTRING(&buf, e_len),
355                        SILC_STR_END);
356
357   /* Decode the HEX string to integer */
358   silc_mp_init(&payload->e);
359   silc_mp_set_str(&payload->e, buf, 16);
360   memset(buf, 0, sizeof(buf));
361
362   /* Return the payload */
363   *return_payload = payload;
364
365   return SILC_SKE_STATUS_OK;
366
367  err:
368   silc_free(payload);
369
370   return status;
371 }
372
373 /* Free's KE1 Payload */
374
375 void silc_ske_payload_one_free(SilcSKEOnePayload *payload)
376 {
377   if (payload) {
378     silc_mp_clear(&payload->e);
379     silc_free(payload);
380   }
381 }
382
383 /* Encodes Key Exchange 2 Payload into a SILC Buffer to be sent
384    to the other end. */
385
386 SilcSKEStatus silc_ske_payload_two_encode(SilcSKE ske,
387                                           SilcSKETwoPayload *payload,
388                                           SilcBuffer *return_buffer)
389 {
390   SilcBuffer buf;
391   unsigned char *f_str;
392   unsigned int f_len;
393   unsigned int len;
394
395   SILC_LOG_DEBUG(("Encoding KE 2 Payload"));
396
397   if (!payload)
398     return SILC_SKE_STATUS_ERROR;
399
400   /* Encode the integer into HEX string */
401   f_len = silc_mp_sizeinbase(&payload->f, 16);
402   f_str = silc_calloc(f_len + 1, sizeof(unsigned char));
403   silc_mp_get_str(f_str, 16, &payload->f);
404
405   /* Allocate channel payload buffer. The length of the buffer
406      is 2 + 2 + public key + 2 + f + 2 + signature. */
407   len = payload->pk_len + 2 + 2 + f_len + 2 + payload->sign_len + 2;
408   buf = silc_buffer_alloc(len);
409   if (!buf) {
410     SILC_LOG_ERROR(("Could not allocate encode buffer"));
411     return SILC_SKE_STATUS_ERROR;
412   }
413
414   silc_buffer_pull_tail(buf, len);
415
416   /* Encode the payload */
417   silc_buffer_format(buf, 
418                      SILC_STR_UI_SHORT(payload->pk_len + 4),
419                      SILC_STR_UI_SHORT(payload->pk_type),
420                      SILC_STR_UI_XNSTRING(payload->pk_data, 
421                                           payload->pk_len),
422                      SILC_STR_UI_SHORT(f_len + 2),
423                      SILC_STR_UI_XNSTRING(f_str, f_len),
424                      SILC_STR_UI_SHORT(payload->sign_len + 2),
425                      SILC_STR_UI_XNSTRING(payload->sign_data, 
426                                           payload->sign_len),
427                      SILC_STR_END);
428
429   /* Return encoded buffer */
430   *return_buffer = buf;
431
432   memset(f_str, 'F', f_len);
433   silc_free(f_str);
434
435   return SILC_SKE_STATUS_OK;
436 }
437
438 /* Parses the Key Exchange 2 Payload. Parsed data is returned
439    to allocated payload structure. */
440
441 SilcSKEStatus silc_ske_payload_two_decode(SilcSKE ske,
442                                           SilcBuffer buffer,
443                                           SilcSKETwoPayload **return_payload)
444 {
445   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
446   SilcSKETwoPayload *payload;
447   unsigned short f_len;
448   unsigned int tot_len = 0, len2;
449
450   SILC_LOG_DEBUG(("Decoding Key Exchange 2 Payload"));
451
452   SILC_LOG_HEXDUMP(("KE 2 Payload"), buffer->data, buffer->len);
453
454   payload = silc_calloc(1, sizeof(*payload));
455   memset(buf, 0, sizeof(buf));
456
457   len2 = buffer->len;
458
459   /* Parse the payload */
460   silc_buffer_unformat(buffer,
461                        SILC_STR_UI_SHORT(&payload->pk_len),
462                        SILC_STR_UI_SHORT(&payload->pk_type),
463                        SILC_STR_END);
464
465   if (payload->pk_len < 5) {
466     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
467     goto err;
468   }
469
470   tot_len += payload->pk_len;
471
472   payload->pk_len -= 4;
473   silc_buffer_pull(buffer, 4);
474   silc_buffer_unformat(buffer,
475                        SILC_STR_UI_XNSTRING(&buf, payload->pk_len),
476                        SILC_STR_UI_SHORT(&f_len),
477                        SILC_STR_END);
478
479   if (f_len < 3) {
480     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
481     goto err;
482   }
483
484   tot_len += f_len;
485
486   payload->pk_data = silc_calloc(payload->pk_len + 1, 
487                                  sizeof(unsigned char));
488   memcpy(payload->pk_data, buf, payload->pk_len);
489   memset(buf, 0, sizeof(buf));
490
491   f_len -= 2;
492   silc_buffer_pull(buffer, payload->pk_len + 2);
493   silc_buffer_unformat(buffer,
494                        SILC_STR_UI_XNSTRING(&buf, f_len),
495                        SILC_STR_UI_SHORT(&payload->sign_len),
496                        SILC_STR_END);
497
498   if (payload->sign_len < 3) {
499     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
500     goto err;
501   }
502
503   tot_len += payload->sign_len;
504
505   if (tot_len != len2) {
506     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
507     goto err;
508   }
509   
510   /* Decode the HEX string to integer */
511   silc_mp_init(&payload->f);
512   silc_mp_set_str(&payload->f, buf, 16);
513   memset(buf, 0, sizeof(buf));
514
515   payload->sign_len -= 2;
516   silc_buffer_pull(buffer, f_len + 2);
517   silc_buffer_unformat(buffer,
518                        SILC_STR_UI_XNSTRING(&buf, payload->sign_len),
519                        SILC_STR_END);
520
521   payload->sign_data = silc_calloc(payload->sign_len + 1, 
522                                  sizeof(unsigned char));
523   memcpy(payload->sign_data, buf, payload->sign_len);
524   memset(buf, 0, sizeof(buf));
525
526   /* Return the payload */
527   *return_payload = payload;
528
529   return SILC_SKE_STATUS_OK;
530
531  err:
532   if (payload->pk_data)
533     silc_free(payload->pk_data);
534   if (payload->sign_data)
535     silc_free(payload->sign_data);
536   silc_free(payload);
537
538   return status;
539 }
540
541 /* Free's KE2 Payload */
542
543 void silc_ske_payload_two_free(SilcSKETwoPayload *payload)
544 {
545   if (payload) {
546     if (payload->pk_data)
547       silc_free(payload->pk_data);
548     if (payload->sign_data)
549       silc_free(payload->sign_data);
550     silc_mp_clear(&payload->f);
551     silc_free(payload);
552   }
553 }