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