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