be670e467323713af42b9b0cfb1d73a947ca66dc
[silc.git] / lib / silcske / payload.c
1 /*
2
3   payload.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2000 - 2005 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, silc_buffer_len(buf));
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,
100                    silc_buffer_len(buffer));
101
102   payload = silc_calloc(1, sizeof(*payload));
103   if (!payload)
104     return SILC_SKE_STATUS_OUT_OF_MEMORY;
105   payload->cookie_len = SILC_SKE_COOKIE_LEN;
106
107   /* Parse start of the payload */
108   ret =
109     silc_buffer_unformat(buffer,
110                          SILC_STR_UI_CHAR(&tmp),     /* RESERVED Field */
111                          SILC_STR_UI_CHAR(&payload->flags),
112                          SILC_STR_UI_SHORT(&payload->len),
113                          SILC_STR_UI_XNSTRING_ALLOC(&payload->cookie,
114                                                     payload->cookie_len),
115                          SILC_STR_UI16_NSTRING_ALLOC(&payload->version,
116                                                      &payload->version_len),
117                          SILC_STR_UI16_NSTRING_ALLOC(&payload->ke_grp_list,
118                                                      &payload->ke_grp_len),
119                          SILC_STR_UI16_NSTRING_ALLOC(&payload->pkcs_alg_list,
120                                                      &payload->pkcs_alg_len),
121                          SILC_STR_UI16_NSTRING_ALLOC(&payload->enc_alg_list,
122                                                      &payload->enc_alg_len),
123                          SILC_STR_UI16_NSTRING_ALLOC(&payload->hash_alg_list,
124                                                      &payload->hash_alg_len),
125                          SILC_STR_UI16_NSTRING_ALLOC(&payload->hmac_alg_list,
126                                                      &payload->hmac_alg_len),
127                          SILC_STR_UI16_NSTRING_ALLOC(&payload->comp_alg_list,
128                                                      &payload->comp_alg_len),
129                          SILC_STR_END);
130   if (ret == -1) {
131     SILC_LOG_ERROR(("Malformed KE Start Payload"));
132     status = SILC_SKE_STATUS_BAD_PAYLOAD;
133     goto err;
134   }
135
136   if (tmp != 0) {
137     SILC_LOG_ERROR(("Bad RESERVED field in KE Start Payload"));
138     status = SILC_SKE_STATUS_BAD_RESERVED_FIELD;
139     goto err;
140   }
141
142   if (payload->len != silc_buffer_len(buffer)) {
143     SILC_LOG_ERROR(("Garbage after KE Start Payload"));
144     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
145     goto err;
146   }
147
148   /* Check for mandatory fields */
149   if (!payload->cookie || !payload->version_len ||
150       !payload->ke_grp_len || !payload->pkcs_alg_len ||
151       !payload->enc_alg_len || !payload->hash_alg_len ||
152       !payload->hmac_alg_len) {
153     SILC_LOG_ERROR(("KE Start Payload is missing mandatory fields"));
154     status = SILC_SKE_STATUS_BAD_PAYLOAD;
155     goto err;
156   }
157
158   /* Return the payload */
159   *return_payload = payload;
160
161   return SILC_SKE_STATUS_OK;
162
163  err:
164   silc_ske_payload_start_free(payload);
165
166   ske->status = status;
167   return status;
168 }
169
170 /* Free's Start Payload */
171
172 void silc_ske_payload_start_free(SilcSKEStartPayload payload)
173 {
174   if (payload) {
175     silc_free(payload->cookie);
176     silc_free(payload->version);
177     silc_free(payload->ke_grp_list);
178     silc_free(payload->pkcs_alg_list);
179     silc_free(payload->enc_alg_list);
180     silc_free(payload->hash_alg_list);
181     silc_free(payload->hmac_alg_list);
182     silc_free(payload->comp_alg_list);
183     silc_free(payload);
184   }
185 }
186
187 /* Encodes Key Exchange Payload into a SILC Buffer to be sent to the other
188    end. */
189
190 SilcSKEStatus silc_ske_payload_ke_encode(SilcSKE ske,
191                                          SilcSKEKEPayload payload,
192                                          SilcBuffer *return_buffer)
193 {
194   SilcBuffer buf;
195   unsigned char *x_str;
196   SilcUInt32 x_len;
197   int ret;
198
199   SILC_LOG_DEBUG(("Encoding KE Payload"));
200
201   if (!payload)
202     return SILC_SKE_STATUS_ERROR;
203
204   if (ske->start_payload &&
205       ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL &&
206       !payload->sign_data) {
207     SILC_LOG_DEBUG(("Signature data is missing"));
208     return SILC_SKE_STATUS_ERROR;
209   }
210
211   /* Encode the integer into binary data */
212   x_str = silc_mp_mp2bin(&payload->x, 0, &x_len);
213
214   /* Allocate channel payload buffer. The length of the buffer
215      is 4 + public key + 2 + x + 2 + signature. */
216   buf = silc_buffer_alloc_size(4 + payload->pk_len + 2 + x_len +
217                                2 + payload->sign_len);
218   if (!buf)
219     return SILC_SKE_STATUS_OUT_OF_MEMORY;
220
221   /* Encode the payload */
222   ret = silc_buffer_format(buf,
223                            SILC_STR_UI_SHORT(payload->pk_len),
224                            SILC_STR_UI_SHORT(payload->pk_type),
225                            SILC_STR_UI_XNSTRING(payload->pk_data,
226                                                 payload->pk_len),
227                            SILC_STR_UI_SHORT(x_len),
228                            SILC_STR_UI_XNSTRING(x_str, x_len),
229                            SILC_STR_UI_SHORT(payload->sign_len),
230                            SILC_STR_UI_XNSTRING(payload->sign_data,
231                                                 payload->sign_len),
232                            SILC_STR_END);
233   if (ret == -1) {
234     memset(x_str, 'F', x_len);
235     silc_free(x_str);
236     silc_buffer_free(buf);
237     return SILC_SKE_STATUS_ERROR;
238   }
239
240   /* Return encoded buffer */
241   *return_buffer = buf;
242
243   SILC_LOG_HEXDUMP(("KE Payload"), buf->data, silc_buffer_len(buf));
244
245   memset(x_str, 'F', x_len);
246   silc_free(x_str);
247
248   return SILC_SKE_STATUS_OK;
249 }
250
251 /* Parses the Key Exchange Payload. Parsed data is returned to allocated
252    payload structure. */
253
254 SilcSKEStatus silc_ske_payload_ke_decode(SilcSKE ske,
255                                          SilcBuffer buffer,
256                                          SilcSKEKEPayload *return_payload)
257 {
258   SilcSKEStatus status = SILC_SKE_STATUS_ERROR;
259   SilcSKEKEPayload payload;
260   unsigned char *x = NULL;
261   SilcUInt16 x_len;
262   SilcUInt32 tot_len = 0, len2;
263   int ret;
264
265   SILC_LOG_DEBUG(("Decoding Key Exchange Payload"));
266
267   SILC_LOG_HEXDUMP(("KE Payload"), buffer->data, silc_buffer_len(buffer));
268
269   payload = silc_calloc(1, sizeof(*payload));
270   if (!payload)
271     return SILC_SKE_STATUS_OUT_OF_MEMORY;
272
273   len2 = silc_buffer_len(buffer);
274
275   /* Parse start of the payload */
276   ret = silc_buffer_unformat(buffer,
277                              SILC_STR_UI_SHORT(&payload->pk_len),
278                              SILC_STR_UI_SHORT(&payload->pk_type),
279                              SILC_STR_END);
280   if (ret == -1) {
281     SILC_LOG_ERROR(("Cannot decode public key from KE payload"));
282     status = SILC_SKE_STATUS_BAD_PAYLOAD;
283     goto err;
284   }
285
286   if (ske->start_payload &&
287       ((payload->pk_type < SILC_SKE_PK_TYPE_SILC ||
288         payload->pk_type > SILC_SKE_PK_TYPE_SPKI) || !payload->pk_len)) {
289     SILC_LOG_ERROR(("Malformed public key in KE payload"));
290     status = SILC_SKE_STATUS_BAD_PAYLOAD;
291     goto err;
292   }
293
294   tot_len += payload->pk_len + 4;
295
296   /* Parse PK data and the signature */
297   silc_buffer_pull(buffer, 4);
298   ret = silc_buffer_unformat(buffer,
299                              SILC_STR_UI_XNSTRING_ALLOC(&payload->pk_data,
300                                                         payload->pk_len),
301                              SILC_STR_UI16_NSTRING_ALLOC(&x, &x_len),
302                              SILC_STR_UI16_NSTRING_ALLOC(&payload->sign_data,
303                                                          &payload->sign_len),
304                              SILC_STR_END);
305   if (ret == -1) {
306     SILC_LOG_ERROR(("Malformed KE Payload"));
307     status = SILC_SKE_STATUS_BAD_PAYLOAD;
308     goto err;
309   }
310
311   tot_len += x_len + 2;
312   tot_len += payload->sign_len + 2;
313
314   if (x_len < 16) {
315     SILC_LOG_ERROR(("Too short DH value in KE Payload"));
316     status = SILC_SKE_STATUS_BAD_PAYLOAD;
317     goto err;
318   }
319
320   if (ske->start_payload &&
321       (ske->start_payload->flags & SILC_SKE_SP_FLAG_MUTUAL) &&
322       (payload->sign_len < 3 || !payload->sign_data)) {
323     SILC_LOG_ERROR(("The signature data is missing - both parties are "
324                     "required to do authentication"));
325     status = SILC_SKE_STATUS_BAD_PAYLOAD;
326     goto err;
327   }
328
329   if (tot_len != len2) {
330     SILC_LOG_ERROR(("Garbage after KE payload"));
331     status = SILC_SKE_STATUS_BAD_PAYLOAD_LENGTH;
332     goto err;
333   }
334
335   /* Decode the binary data to integer */
336   silc_mp_init(&payload->x);
337   silc_mp_bin2mp(x, x_len, &payload->x);
338   memset(x, 0, sizeof(x_len));
339   silc_free(x);
340
341   /* Return the payload */
342   *return_payload = payload;
343
344   return SILC_SKE_STATUS_OK;
345
346  err:
347   silc_free(payload->pk_data);
348   silc_free(payload->sign_data);
349   silc_free(x);
350   silc_free(payload);
351   ske->status = status;
352   return status;
353 }
354
355 /* Free's KE Payload */
356
357 void silc_ske_payload_ke_free(SilcSKEKEPayload payload)
358 {
359   if (payload) {
360     silc_free(payload->pk_data);
361     silc_mp_uninit(&payload->x);
362     silc_free(payload->sign_data);
363     silc_free(payload);
364   }
365 }