Merged from silc_1_0_branch.
[silc.git] / lib / silcske / payload.c
1 /*
2
3   payload.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2000 - 2002 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silcincludes.h"
22
23 /* Encodes Key Exchange Start Payload into a SILC Buffer to be sent
24    to the other end. */
25
26 SilcSKEStatus silc_ske_payload_start_encode(SilcSKE ske,
27                                             SilcSKEStartPayload *payload,
28                                             SilcBuffer *return_buffer)
29 {
30   SilcBuffer buf;
31   int ret;
32
33   SILC_LOG_DEBUG(("Encoding KE Start Payload"));
34
35   if (!payload)
36     return SILC_SKE_STATUS_ERROR;
37
38   buf = silc_buffer_alloc_size(payload->len);
39   if (!buf)
40     return SILC_SKE_STATUS_OUT_OF_MEMORY;
41
42   /* Encode the payload */
43   ret = silc_buffer_format(buf,
44                            SILC_STR_UI_CHAR(0),        /* RESERVED field */
45                            SILC_STR_UI_CHAR(payload->flags),
46                            SILC_STR_UI_SHORT(payload->len),
47                            SILC_STR_UI_XNSTRING(payload->cookie, 
48                                                 payload->cookie_len),
49                            SILC_STR_UI_SHORT(payload->version_len),
50                            SILC_STR_UI_XNSTRING(payload->version, 
51                                                 payload->version_len),
52                            SILC_STR_UI_SHORT(payload->ke_grp_len),
53                            SILC_STR_UI_XNSTRING(payload->ke_grp_list,
54                                                 payload->ke_grp_len),
55                            SILC_STR_UI_SHORT(payload->pkcs_alg_len),
56                            SILC_STR_UI_XNSTRING(payload->pkcs_alg_list,
57                                                 payload->pkcs_alg_len),
58                            SILC_STR_UI_SHORT(payload->enc_alg_len),
59                            SILC_STR_UI_XNSTRING(payload->enc_alg_list,
60                                                 payload->enc_alg_len),
61                            SILC_STR_UI_SHORT(payload->hash_alg_len),
62                            SILC_STR_UI_XNSTRING(payload->hash_alg_list,
63                                                 payload->hash_alg_len),
64                            SILC_STR_UI_SHORT(payload->hmac_alg_len),
65                            SILC_STR_UI_XNSTRING(payload->hmac_alg_list,
66                                                 payload->hmac_alg_len),
67                            SILC_STR_UI_SHORT(payload->comp_alg_len),
68                            SILC_STR_UI_XNSTRING(payload->comp_alg_list,
69                                                 payload->comp_alg_len),
70                            SILC_STR_END);
71   if (ret == -1) {
72     silc_buffer_free(buf);
73     return SILC_SKE_STATUS_ERROR;
74   }
75
76   /* Return the encoded buffer */
77   *return_buffer = buf;
78
79   SILC_LOG_HEXDUMP(("KE Start Payload"), buf->data, buf->len);
80
81   return SILC_SKE_STATUS_OK;
82 }
83
84 /* Parses the Key Exchange Start Payload. Parsed data is returned
85    to allocated payload structure. */
86
87 SilcSKEStatus 
88 silc_ske_payload_start_decode(SilcSKE ske,
89                               SilcBuffer buffer,
90                               SilcSKEStartPayload **return_payload)
91 {
92   SilcSKEStartPayload *payload;
93   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
94   unsigned char tmp;
95   int ret;
96
97   SILC_LOG_DEBUG(("Decoding Key Exchange Start Payload"));
98
99   SILC_LOG_HEXDUMP(("KE Start Payload"), buffer->data, buffer->len);
100
101   payload = silc_calloc(1, sizeof(*payload));
102   if (!payload)
103     return SILC_SKE_STATUS_OUT_OF_MEMORY;
104   payload->cookie_len = SILC_SKE_COOKIE_LEN;
105
106   /* Parse start of the payload */
107   ret = 
108     silc_buffer_unformat(buffer,
109                          SILC_STR_UI_CHAR(&tmp),     /* RESERVED Field */
110                          SILC_STR_UI_CHAR(&payload->flags),
111                          SILC_STR_UI_SHORT(&payload->len),
112                          SILC_STR_UI_XNSTRING_ALLOC(&payload->cookie, 
113                                                     payload->cookie_len),
114                          SILC_STR_UI16_NSTRING_ALLOC(&payload->version,
115                                                      &payload->version_len),
116                          SILC_STR_UI16_NSTRING_ALLOC(&payload->ke_grp_list,
117                                                      &payload->ke_grp_len),
118                          SILC_STR_UI16_NSTRING_ALLOC(&payload->pkcs_alg_list,
119                                                      &payload->pkcs_alg_len),
120                          SILC_STR_UI16_NSTRING_ALLOC(&payload->enc_alg_list,
121                                                      &payload->enc_alg_len),
122                          SILC_STR_UI16_NSTRING_ALLOC(&payload->hash_alg_list,
123                                                      &payload->hash_alg_len),
124                          SILC_STR_UI16_NSTRING_ALLOC(&payload->hmac_alg_list,
125                                                      &payload->hmac_alg_len),
126                          SILC_STR_UI16_NSTRING_ALLOC(&payload->comp_alg_list,
127                                                      &payload->comp_alg_len),
128                          SILC_STR_END);
129   if (ret == -1) {
130     SILC_LOG_ERROR(("Malformed KE Start Payload"));
131     status = SILC_SKE_STATUS_BAD_PAYLOAD;
132     goto err;
133   }
134
135   if (tmp != 0) {
136     SILC_LOG_ERROR(("Bad RESERVED field in KE Start Payload"));
137     status = SILC_SKE_STATUS_BAD_RESERVED_FIELD;
138     goto err;
139   }
140
141   if (payload->len != buffer->len) {
142     SILC_LOG_ERROR(("Garbage after KE Start Payload"));
143     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
144     goto err;
145   }
146
147   /* Check for mandatory fields */
148   if (!payload->cookie || !payload->version_len ||
149       !payload->ke_grp_len || !payload->pkcs_alg_len ||
150       !payload->enc_alg_len || !payload->hash_alg_len ||
151       !payload->hmac_alg_len) {
152     SILC_LOG_ERROR(("KE Start Payload is missing mandatory fields"));
153     status = SILC_SKE_STATUS_BAD_PAYLOAD;
154     goto err;
155   }
156
157   /* Return the payload */
158   *return_payload = payload;
159
160   return SILC_SKE_STATUS_OK;
161
162  err:
163   silc_ske_payload_start_free(payload);
164
165   ske->status = status;
166   return status;
167 }
168
169 /* Free's Start Payload */
170
171 void silc_ske_payload_start_free(SilcSKEStartPayload *payload)
172 {
173   if (payload) {
174     silc_free(payload->cookie);
175     silc_free(payload->version);
176     silc_free(payload->ke_grp_list);
177     silc_free(payload->pkcs_alg_list);
178     silc_free(payload->enc_alg_list);
179     silc_free(payload->hash_alg_list);
180     silc_free(payload->hmac_alg_list);
181     silc_free(payload->comp_alg_list);
182     silc_free(payload);
183   }
184 }
185
186 /* Encodes Key Exchange Payload into a SILC Buffer to be sent to the other
187    end. */
188
189 SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
190                                          SilcSKEKEPayload *payload,
191                                          SilcBuffer *return_buffer)
192 {
193   SilcBuffer buf;
194   unsigned char *x_str;
195   SilcUInt32 x_len;
196   int ret;
197
198   SILC_LOG_DEBUG(("Encoding KE Payload"));
199
200   if (!payload)
201     return SILC_SKE_STATUS_ERROR;
202
203   if (ske->start_payload && 
204       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
205       !payload->sign_data) {
206     SILC_LOG_DEBUG(("Signature data is missing"));
207     return SILC_SKE_STATUS_ERROR;
208   }
209
210   /* Encode the integer into binary data */
211   x_str = silc_mp_mp2bin(&payload->x, 0, &x_len);
212
213   /* Allocate channel payload buffer. The length of the buffer
214      is 4 + public key + 2 + x + 2 + signature. */
215   buf = silc_buffer_alloc_size(4 + payload->pk_len + 2 + x_len + 
216                                2 + payload->sign_len);
217   if (!buf)
218     return SILC_SKE_STATUS_OUT_OF_MEMORY;
219
220   /* Encode the payload */
221   ret = silc_buffer_format(buf, 
222                            SILC_STR_UI_SHORT(payload->pk_len),
223                            SILC_STR_UI_SHORT(payload->pk_type),
224                            SILC_STR_UI_XNSTRING(payload->pk_data, 
225                                                 payload->pk_len),
226                            SILC_STR_UI_SHORT(x_len),
227                            SILC_STR_UI_XNSTRING(x_str, x_len),
228                            SILC_STR_UI_SHORT(payload->sign_len),
229                            SILC_STR_UI_XNSTRING(payload->sign_data, 
230                                                 payload->sign_len),
231                            SILC_STR_END);
232   if (ret == -1) {
233     memset(x_str, 'F', x_len);
234     silc_free(x_str);
235     silc_buffer_free(buf);
236     return SILC_SKE_STATUS_ERROR;
237   }
238
239   /* Return encoded buffer */
240   *return_buffer = buf;
241
242   SILC_LOG_HEXDUMP(("KE Payload"), buf->data, buf->len);
243
244   memset(x_str, 'F', x_len);
245   silc_free(x_str);
246
247   return SILC_SKE_STATUS_OK;
248 }
249
250 /* Parses the Key Exchange Payload. Parsed data is returned to allocated
251    payload structure. */
252
253 SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
254                                          SilcBuffer buffer,
255                                          SilcSKEKEPayload **return_payload)
256 {
257   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
258   SilcSKEKEPayload *payload;
259   unsigned char *x = NULL;
260   SilcUInt16 x_len;
261   SilcUInt32 tot_len = 0, len2;
262   int ret;
263
264   SILC_LOG_DEBUG(("Decoding Key Exchange Payload"));
265
266   SILC_LOG_HEXDUMP(("KE Payload"), buffer->data, buffer->len);
267
268   payload = silc_calloc(1, sizeof(*payload));
269   if (!payload)
270     return SILC_SKE_STATUS_OUT_OF_MEMORY;
271
272   len2 = buffer->len;
273
274   /* Parse start of the payload */
275   ret = silc_buffer_unformat(buffer,
276                              SILC_STR_UI_SHORT(&payload->pk_len),
277                              SILC_STR_UI_SHORT(&payload->pk_type),
278                              SILC_STR_END);
279   if (ret == -1) {
280     SILC_LOG_ERROR(("Cannot decode public key from KE payload"));
281     status = SILC_SKE_STATUS_BAD_PAYLOAD;
282     goto err;
283   }
284
285   if (ske->start_payload &&
286       ((payload->pk_type < SILC_SKE_PK_TYPE_SILC || 
287         payload->pk_type > SILC_SKE_PK_TYPE_SPKI) || !payload->pk_len)) {
288     SILC_LOG_ERROR(("Malformed public key in KE payload"));
289     status = SILC_SKE_STATUS_BAD_PAYLOAD;
290     goto err;
291   }
292
293   tot_len += payload->pk_len + 4;
294
295   /* Parse PK data and the signature */
296   silc_buffer_pull(buffer, 4);
297   ret = silc_buffer_unformat(buffer,
298                              SILC_STR_UI_XNSTRING_ALLOC(&payload->pk_data,
299                                                         payload->pk_len),
300                              SILC_STR_UI16_NSTRING_ALLOC(&x, &x_len),
301                              SILC_STR_UI16_NSTRING_ALLOC(&payload->sign_data, 
302                                                          &payload->sign_len),
303                              SILC_STR_END);
304   if (ret == -1) {
305     SILC_LOG_ERROR(("Malformed KE Payload"));
306     status = SILC_SKE_STATUS_BAD_PAYLOAD;
307     goto err;
308   }
309
310   tot_len += x_len + 2;
311   tot_len += payload->sign_len + 2;
312
313   if (x_len < 16) {
314     SILC_LOG_ERROR(("Too short DH value in KE Payload"));
315     status = SILC_SKE_STATUS_BAD_PAYLOAD;
316     goto err;
317   }
318
319   if (ske->start_payload && 
320       (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
321       (payload->sign_len < 3 || !payload->sign_data)) {
322     SILC_LOG_ERROR(("The signature data is missing - both parties are "
323                     "required to do authentication"));
324     status = SILC_SKE_STATUS_BAD_PAYLOAD;
325     goto err;
326   }
327
328   if (tot_len != len2) {
329     SILC_LOG_ERROR(("Garbage after KE payload"));
330     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
331     goto err;
332   }
333   
334   /* Decode the binary data to integer */
335   silc_mp_init(&payload->x);
336   silc_mp_bin2mp(x, x_len, &payload->x);
337   memset(x, 0, sizeof(x_len));
338   silc_free(x);
339
340   /* Return the payload */
341   *return_payload = payload;
342
343   return SILC_SKE_STATUS_OK;
344
345  err:
346   silc_free(payload->pk_data);
347   silc_free(payload->sign_data);
348   silc_free(x);
349   silc_free(payload);
350   ske->status = status;
351   return status;
352 }
353
354 /* Free's KE Payload */
355
356 void silc_ske_payload_ke_free(SilcSKEKEPayload *payload)
357 {
358   if (payload) {
359     silc_free(payload->pk_data);
360     silc_mp_uninit(&payload->x);
361     silc_free(payload->sign_data);
362     silc_free(payload);
363   }
364 }