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