updates.
[silc.git] / lib / silcske / silcske.c
1 /*
2
3   silcske.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 2000 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 #include "payload_internal.h"
24 #include "groups_internal.h"
25
26 /* Allocates new SKE object. */
27
28 SilcSKE silc_ske_alloc()
29 {
30   SilcSKE ske;
31
32   SILC_LOG_DEBUG(("Allocating new Key Exchange object"));
33
34   ske = silc_calloc(1, sizeof(*ske));
35   ske->status = SILC_SKE_STATUS_OK;
36
37   return ske;
38 }
39
40 /* Free's SKE object. */
41
42 void silc_ske_free(SilcSKE ske)
43 {
44   SILC_LOG_DEBUG(("Freeing Key Exchange object"));
45
46   if (ske) {
47     /* Free start payload */
48     if (ske->start_payload)
49       silc_ske_payload_start_free(ske->start_payload);
50
51     /* Free KE1 payload */
52     if (ske->ke1_payload)
53       silc_ske_payload_one_free(ske->ke1_payload);
54
55     /* Free KE2 payload */
56     if (ske->ke2_payload)
57       silc_ske_payload_two_free(ske->ke2_payload);
58
59     /* Free rest */
60     if (ske->prop) {
61       if (ske->prop->group)
62         silc_free(ske->prop->group);
63       if (ske->prop->pkcs)
64         silc_pkcs_free(ske->prop->pkcs);
65       if (ske->prop->cipher)
66         silc_cipher_free(ske->prop->cipher);
67       if (ske->prop->hash)
68         silc_hash_free(ske->prop->hash);
69       if (ske->prop->hmac)
70         silc_hmac_free(ske->prop->hmac);
71       silc_free(ske->prop);
72     }
73     if (ske->start_payload_copy)
74       silc_buffer_free(ske->start_payload_copy);
75     if (ske->pk)
76       silc_free(ske->pk);
77     if (ske->x) {
78       silc_mp_clear(ske->x);
79       silc_free(ske->x);
80     }
81     if (ske->KEY) {
82       silc_mp_clear(ske->KEY);
83       silc_free(ske->KEY);
84     }
85     if (ske->hash)
86       silc_free(ske->hash);
87     silc_free(ske);
88   }
89 }
90
91 /* Starts the SILC Key Exchange protocol for initiator. The connection
92    to the remote end must be established before calling this function
93    and the connecting socket must be sent as argument. This function
94    creates the Key Exchange Start Paload which includes all our
95    configured security properties. This payload is then sent to the
96    remote end for further processing. This payload must be sent as
97    argument to the function, however, it must not be encoded
98    already, it is done by this function.
99
100    The packet sending is done by calling a callback function. Caller
101    must provide a routine to send the packet. */
102
103 SilcSKEStatus silc_ske_initiator_start(SilcSKE ske, SilcRng rng,
104                                        SilcSocketConnection sock,
105                                        SilcSKEStartPayload *start_payload,
106                                        SilcSKESendPacketCb send_packet,
107                                        void *context)
108 {
109   SilcSKEStatus status = SILC_SKE_STATUS_OK;
110   SilcBuffer payload_buf;
111
112   SILC_LOG_DEBUG(("Start"));
113
114   ske->sock = sock;
115   ske->rng = rng;
116
117   /* Encode the payload */
118   status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
119   if (status != SILC_SKE_STATUS_OK)
120     return status;
121
122   /* Take a copy of the payload buffer for future use. It is used to
123      compute the HASH value. */
124   ske->start_payload_copy = silc_buffer_copy(payload_buf);
125
126   /* Send the packet. */
127   if (send_packet)
128     (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, context);
129
130   silc_buffer_free(payload_buf);
131
132   return status;
133 }
134
135 /* Function called after ske_initiator_start fuction. This receives
136    the remote ends Key Exchange Start payload which includes the
137    security properties selected by the responder from our payload
138    sent in the silc_ske_initiator_start function. */
139
140 SilcSKEStatus silc_ske_initiator_phase_1(SilcSKE ske, 
141                                          SilcBuffer start_payload,
142                                          SilcSKECb callback,
143                                          void *context)
144 {
145   SilcSKEStatus status = SILC_SKE_STATUS_OK;
146   SilcSKEStartPayload *payload;
147   SilcSKESecurityProperties prop;
148   SilcSKEDiffieHellmanGroup group;
149
150   SILC_LOG_DEBUG(("Start"));
151
152   /* Decode the payload */
153   status = silc_ske_payload_start_decode(ske, start_payload, &payload);
154   if (status != SILC_SKE_STATUS_OK) {
155     ske->status = status;
156     return status;
157   }
158
159   /* Take the selected security properties into use while doing
160      the key exchange. This is used only while doing the key 
161      exchange. The same data is returned to upper levels by calling
162      the callback function. */
163   ske->prop = prop = silc_calloc(1, sizeof(*prop));
164   prop->flags = payload->flags;
165   status = silc_ske_get_group_by_name(payload->ke_grp_list, &group);
166   if (status != SILC_SKE_STATUS_OK)
167     goto err;
168
169   prop->group = group;
170
171   if (silc_pkcs_alloc(payload->pkcs_alg_list, &prop->pkcs) == FALSE) {
172     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
173     goto err;
174   }
175
176   if (silc_cipher_alloc(payload->enc_alg_list, &prop->cipher) == FALSE) {
177     status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
178     goto err;
179   }
180
181   if (silc_hash_alloc(payload->hash_alg_list, &prop->hash) == FALSE) {
182     status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
183     goto err;
184   }
185
186   if (silc_hmac_alloc(payload->hmac_alg_list, NULL, &prop->hmac) == FALSE) {
187     status = SILC_SKE_STATUS_UNKNOWN_HMAC;
188     goto err;
189   }
190
191   ske->start_payload = payload;
192
193   /* Return the received payload by calling the callback function. */
194   if (callback)
195     (*callback)(ske, context);
196
197   return status;
198
199  err:
200   if (payload)
201     silc_ske_payload_start_free(payload);
202
203   silc_free(group);
204
205   if (prop->pkcs)
206     silc_pkcs_free(prop->pkcs);
207   if (prop->cipher)
208     silc_cipher_free(prop->cipher);
209   if (prop->hash)
210     silc_hash_free(prop->hash);
211   if (prop->hmac)
212     silc_hmac_free(prop->hmac);
213   silc_free(prop);
214   ske->prop = NULL;
215
216   if (status == SILC_SKE_STATUS_OK)
217     return SILC_SKE_STATUS_ERROR;
218
219   ske->status = status;
220   return status;
221 }
222
223 /* This function creates random number x, such that 1 < x < q and 
224    computes e = g ^ x mod p and sends the result to the remote end in 
225    Key Exchange 1 Payload. */
226
227 SilcSKEStatus silc_ske_initiator_phase_2(SilcSKE ske,
228                                          SilcPublicKey public_key,
229                                          SilcSKESendPacketCb send_packet,
230                                          void *context)
231 {
232   SilcSKEStatus status = SILC_SKE_STATUS_OK;
233   SilcBuffer payload_buf;
234   SilcInt *x, e;
235   SilcSKEOnePayload *payload;
236   unsigned int pk_len;
237
238   SILC_LOG_DEBUG(("Start"));
239
240   /* Create the random number x, 1 < x < q. */
241   x = silc_calloc(1, sizeof(*x));
242   silc_mp_init(x);
243   status = 
244     silc_ske_create_rnd(ske, ske->prop->group->group_order,
245                         silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
246                         x);
247   if (status != SILC_SKE_STATUS_OK) {
248     silc_mp_clear(x);
249     silc_free(x);
250     ske->status = status;
251     return status;
252   }
253
254   SILC_LOG_DEBUG(("Computing e = g ^ x mod p"));
255
256   /* Do the Diffie Hellman computation, e = g ^ x mod p */
257   silc_mp_init(&e);
258   silc_mp_powm(&e, &ske->prop->group->generator, x, 
259                &ske->prop->group->group);
260   
261   /* Encode the result to Key Exchange 1 Payload. */
262   payload = silc_calloc(1, sizeof(*payload));
263   payload->e = e;
264   payload->pk_data = silc_pkcs_public_key_encode(public_key, &pk_len);
265   if (!payload->pk_data) {
266     silc_mp_clear(x);
267     silc_free(x);
268     silc_mp_clear(&e);
269     silc_free(payload);
270     ske->status = SILC_SKE_STATUS_OK;
271     return ske->status;
272   }
273   payload->pk_len = pk_len;
274   payload->pk_type = SILC_SKE_PK_TYPE_SILC;
275   status = silc_ske_payload_one_encode(ske, payload, &payload_buf);
276   if (status != SILC_SKE_STATUS_OK) {
277     silc_mp_clear(x);
278     silc_free(x);
279     silc_mp_clear(&e);
280     silc_free(payload->pk_data);
281     silc_free(payload);
282     ske->status = status;
283     return status;
284   }
285
286   ske->ke1_payload = payload;
287   ske->x = x;
288
289   /* Send the packet. */
290   if (send_packet)
291     (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_1, context);
292
293   silc_buffer_free(payload_buf);
294
295   return status;
296 }
297
298 /* Receives Key Exchange 2 Payload from responder consisting responders
299    public key, f, and signature. This function verifies the public key,
300    computes the secret shared key and verifies the signature. */
301
302 SilcSKEStatus silc_ske_initiator_finish(SilcSKE ske,
303                                         SilcBuffer ke2_payload,
304                                         SilcSKEVerifyCb verify_key,
305                                         void *verify_context,
306                                         SilcSKECb callback,
307                                         void *context)
308 {
309   SilcSKEStatus status = SILC_SKE_STATUS_OK;
310   SilcSKETwoPayload *payload;
311   SilcPublicKey public_key = NULL;
312   SilcInt *KEY;
313   unsigned char hash[32];
314   unsigned int hash_len;
315
316   SILC_LOG_DEBUG(("Start"));
317
318   /* Decode the payload */
319   status = silc_ske_payload_two_decode(ske, ke2_payload, &payload);
320   if (status != SILC_SKE_STATUS_OK) {
321     ske->status = status;
322     return status;
323   }
324   ske->ke2_payload = payload;
325
326   SILC_LOG_DEBUG(("Computing KEY = f ^ x mod p"));
327
328   /* Compute the shared secret key */
329   KEY = silc_calloc(1, sizeof(*KEY));
330   silc_mp_init(KEY);
331   silc_mp_powm(KEY, &payload->f, ske->x, &ske->prop->group->group);
332   ske->KEY = KEY;
333
334   SILC_LOG_DEBUG(("Verifying public key"));
335
336   if (!silc_pkcs_public_key_decode(payload->pk_data, payload->pk_len, 
337                                    &public_key)) {
338     status = SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY;
339     goto err;
340   }
341
342   if (verify_key) {
343     status = (*verify_key)(ske, payload->pk_data, payload->pk_len,
344                            payload->pk_type, verify_context);
345     if (status != SILC_SKE_STATUS_OK)
346       goto err;
347   }  
348
349   SILC_LOG_DEBUG(("Public key is authentic"));
350
351   /* Compute the hash value */
352   status = silc_ske_make_hash(ske, hash, &hash_len);
353   if (status != SILC_SKE_STATUS_OK)
354     goto err;
355
356   ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
357   memcpy(ske->hash, hash, hash_len);
358   ske->hash_len = hash_len;
359
360   SILC_LOG_DEBUG(("Verifying signature"));
361
362   /* Verify signature */
363   silc_pkcs_public_key_data_set(ske->prop->pkcs, public_key->pk, 
364                                 public_key->pk_len);
365   if (silc_pkcs_verify(ske->prop->pkcs, payload->sign_data, 
366                        payload->sign_len, hash, hash_len) == FALSE) {
367
368     SILC_LOG_DEBUG(("Signature don't match"));
369
370     status = SILC_SKE_STATUS_INCORRECT_SIGNATURE;
371     goto err;
372   }
373
374   SILC_LOG_DEBUG(("Signature is Ok"));
375
376   silc_pkcs_public_key_free(public_key);
377   memset(hash, 'F', hash_len);
378
379   /* Call the callback. */
380   if (callback)
381     (*callback)(ske, context);
382
383   return status;
384
385  err:
386   memset(hash, 'F', sizeof(hash));
387   silc_ske_payload_two_free(payload);
388   ske->ke2_payload = NULL;
389
390   silc_mp_clear(ske->KEY);
391   silc_free(ske->KEY);
392   ske->KEY = NULL;
393
394   if (public_key)
395     silc_pkcs_public_key_free(public_key);
396
397   if (ske->hash) {
398     memset(ske->hash, 'F', hash_len);
399     silc_free(ske->hash);
400     ske->hash = NULL;
401   }
402
403   if (status == SILC_SKE_STATUS_OK)
404     return SILC_SKE_STATUS_ERROR;
405
406   ske->status = status;
407   return status;
408 }
409
410 /* Starts Key Exchange protocol for responder. Responder receives
411    Key Exchange Start Payload from initiator consisting of all the
412    security properties the initiator supports. This function decodes
413    the payload and parses the payload further and selects the right 
414    security properties. */
415
416 SilcSKEStatus silc_ske_responder_start(SilcSKE ske, SilcRng rng,
417                                        SilcSocketConnection sock,
418                                        char *version,
419                                        SilcBuffer start_payload,
420                                        SilcSKECb callback,
421                                        void *context)
422 {
423   SilcSKEStatus status = SILC_SKE_STATUS_OK;
424   SilcSKEStartPayload *remote_payload = NULL, *payload = NULL;
425
426   SILC_LOG_DEBUG(("Start"));
427
428   ske->sock = sock;
429   ske->rng = rng;
430
431   /* Decode the payload */
432   status = silc_ske_payload_start_decode(ske, start_payload, &remote_payload);
433   if (status != SILC_SKE_STATUS_OK) {
434     ske->status = status;
435     return status;
436   }
437
438   /* Take a copy of the payload buffer for future use. It is used to
439      compute the HASH value. */
440   ske->start_payload_copy = silc_buffer_copy(start_payload);
441
442   /* Parse and select the security properties from the payload */
443   payload = silc_calloc(1, sizeof(*payload));
444   status = silc_ske_select_security_properties(ske, version,
445                                                payload, remote_payload);
446   if (status != SILC_SKE_STATUS_OK)
447     goto err;
448
449   ske->start_payload = payload;
450
451   /* Call the callback function. */
452   if (callback)
453     (*callback)(ske, context);
454
455   return status;
456
457  err:
458   if (remote_payload)
459     silc_ske_payload_start_free(remote_payload);
460   if (payload)
461     silc_free(payload);
462
463   if (status == SILC_SKE_STATUS_OK)
464     return SILC_SKE_STATUS_ERROR;
465
466   ske->status = status;
467   return status;
468 }
469
470 /* The selected security properties from the initiator payload is now 
471    encoded into Key Exchange Start Payload and sent to the initiator. */
472
473 SilcSKEStatus silc_ske_responder_phase_1(SilcSKE ske, 
474                                          SilcSKEStartPayload *start_payload,
475                                          SilcSKESendPacketCb send_packet,
476                                          void *context)
477 {
478   SilcSKEStatus status = SILC_SKE_STATUS_OK;
479   SilcBuffer payload_buf;
480   SilcSKESecurityProperties prop;
481   SilcSKEDiffieHellmanGroup group = NULL;
482
483   SILC_LOG_DEBUG(("Start"));
484
485   /* Allocate security properties from the payload. These are allocated
486      only for this negotiation and will be free'd after KE is over. */
487   ske->prop = prop = silc_calloc(1, sizeof(*prop));
488   prop->flags = start_payload->flags;
489   status = silc_ske_get_group_by_name(start_payload->ke_grp_list, &group);
490   if (status != SILC_SKE_STATUS_OK)
491     goto err;
492
493   prop->group = group;
494
495   if (silc_pkcs_alloc(start_payload->pkcs_alg_list, 
496                       &prop->pkcs) == FALSE) {
497     status = SILC_SKE_STATUS_UNKNOWN_PKCS;
498     goto err;
499   }
500
501   if (silc_cipher_alloc(start_payload->enc_alg_list, 
502                         &prop->cipher) == FALSE) {
503     status = SILC_SKE_STATUS_UNKNOWN_CIPHER;
504     goto err;
505   }
506
507   if (silc_hash_alloc(start_payload->hash_alg_list,
508                       &prop->hash) == FALSE) {
509     status = SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
510     goto err;
511   }
512
513   if (silc_hmac_alloc(start_payload->hmac_alg_list, NULL,
514                       &prop->hmac) == FALSE) {
515     status = SILC_SKE_STATUS_UNKNOWN_HMAC;
516     goto err;
517   }
518
519   /* Encode the payload */
520   status = silc_ske_payload_start_encode(ske, start_payload, &payload_buf);
521   if (status != SILC_SKE_STATUS_OK)
522     goto err;
523
524   /* Send the packet. */
525   if (send_packet)
526     (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE, context);
527
528   silc_buffer_free(payload_buf);
529
530   return status;
531
532  err:
533   if (group)
534     silc_free(group);
535
536   if (prop->pkcs)
537     silc_pkcs_free(prop->pkcs);
538   if (prop->cipher)
539     silc_cipher_free(prop->cipher);
540   if (prop->hash)
541     silc_hash_free(prop->hash);
542   if (prop->hmac)
543     silc_hmac_free(prop->hmac);
544   silc_free(prop);
545   ske->prop = NULL;
546
547   if (status == SILC_SKE_STATUS_OK)
548     return SILC_SKE_STATUS_ERROR;
549
550   ske->status = status;
551   return status;
552 }
553
554 /* This function receives the Key Exchange 1 Payload from the initiator.
555    After processing the payload this then selects random number x,
556    such that 1 < x < q and computes f = g ^ x mod p. This then puts
557    the result f to a Key Exchange 2 Payload which is later processed
558    in ske_responder_finish function. The callback function should
559    not touch the payload (it should merely call the ske_responder_finish
560    function). */
561
562 SilcSKEStatus silc_ske_responder_phase_2(SilcSKE ske,
563                                          SilcBuffer ke1_payload,
564                                          SilcSKECb callback,
565                                          void *context)
566 {
567   SilcSKEStatus status = SILC_SKE_STATUS_OK;
568   SilcSKEOnePayload *one_payload;
569   SilcSKETwoPayload *two_payload;
570   SilcInt *x, f;
571
572   SILC_LOG_DEBUG(("Start"));
573
574   /* Decode Key Exchange 1 Payload */
575   status = silc_ske_payload_one_decode(ske, ke1_payload, &one_payload);
576   if (status != SILC_SKE_STATUS_OK) {
577     ske->status = status;
578     return status;
579   }
580
581   /* Create the random number x, 1 < x < q. */
582   x = silc_calloc(1, sizeof(*x));
583   silc_mp_init(x);
584   status = 
585     silc_ske_create_rnd(ske, ske->prop->group->group_order,
586                         silc_mp_sizeinbase(&ske->prop->group->group_order, 2),
587                         x);
588   if (status != SILC_SKE_STATUS_OK) {
589     silc_mp_clear(x);
590     silc_free(x);
591     return status;
592   }
593
594   SILC_LOG_DEBUG(("Computing f = g ^ x mod p"));
595
596   /* Do the Diffie Hellman computation, f = g ^ x mod p */
597   silc_mp_init(&f);
598   silc_mp_powm(&f, &ske->prop->group->generator, x, 
599                &ske->prop->group->group);
600   
601   /* Save the results for later processing */
602   two_payload = silc_calloc(1, sizeof(*two_payload));
603   two_payload->f = f;
604   ske->x = x;
605   ske->ke1_payload = one_payload;
606   ske->ke2_payload = two_payload;
607
608   /* Call the callback. */
609   if (callback)
610     (*callback)(ske, context);
611
612   return status;
613 }
614
615 /* This function computes the secret shared key KEY = e ^ x mod p, and, 
616    a hash value to be signed and sent to the other end. This then
617    encodes Key Exchange 2 Payload and sends it to the other end. */
618
619 SilcSKEStatus silc_ske_responder_finish(SilcSKE ske,
620                                         SilcPublicKey public_key,
621                                         SilcPrivateKey private_key,
622                                         SilcSKEPKType pk_type,
623                                         SilcSKESendPacketCb send_packet,
624                                         void *context)
625 {
626   SilcSKEStatus status = SILC_SKE_STATUS_OK;
627   SilcBuffer payload_buf;
628   SilcInt *KEY;
629   unsigned char hash[32], sign[1024], *pk;
630   unsigned int hash_len, sign_len, pk_len;
631
632   SILC_LOG_DEBUG(("Start"));
633
634   if (!public_key || !private_key) {
635     status = SILC_SKE_STATUS_ERROR;
636     goto err;
637   }
638
639   SILC_LOG_DEBUG(("Computing KEY = e ^ x mod p"));
640
641   /* Compute the shared secret key */
642   KEY = silc_calloc(1, sizeof(*KEY));
643   silc_mp_init(KEY);
644   silc_mp_powm(KEY, &ske->ke1_payload->e, ske->x, 
645                &ske->prop->group->group);
646   ske->KEY = KEY;
647
648   SILC_LOG_DEBUG(("Getting public key"));
649
650   /* Get the public key */
651   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
652   if (!pk) {
653     status = SILC_SKE_STATUS_ERROR;
654     goto err;
655   }
656   ske->ke2_payload->pk_data = pk;
657   ske->ke2_payload->pk_len = pk_len;
658   ske->ke2_payload->pk_type = pk_type;
659
660   SILC_LOG_DEBUG(("Computing HASH value"));
661
662   /* Compute the hash value */
663   memset(hash, 0, sizeof(hash));
664   status = silc_ske_make_hash(ske, hash, &hash_len);
665   if (status != SILC_SKE_STATUS_OK)
666     goto err;
667
668   ske->hash = silc_calloc(hash_len, sizeof(unsigned char));
669   memcpy(ske->hash, hash, hash_len);
670   ske->hash_len = hash_len;
671
672   SILC_LOG_DEBUG(("Signing HASH value"));
673
674   /* Sign the hash value */
675   silc_pkcs_private_key_data_set(ske->prop->pkcs, private_key->prv, 
676                                  private_key->prv_len);
677   silc_pkcs_sign(ske->prop->pkcs, hash, hash_len, sign, &sign_len);
678   ske->ke2_payload->sign_data = silc_calloc(sign_len, sizeof(unsigned char));
679   memcpy(ske->ke2_payload->sign_data, sign, sign_len);
680   memset(sign, 0, sizeof(sign));
681   ske->ke2_payload->sign_len = sign_len;
682
683   /* Encode the Key Exchange 2 Payload */
684   status = silc_ske_payload_two_encode(ske, ske->ke2_payload,
685                                        &payload_buf);
686   if (status != SILC_SKE_STATUS_OK)
687     goto err;
688
689   /* Send the packet. */
690   if (send_packet)
691     (*send_packet)(ske, payload_buf, SILC_PACKET_KEY_EXCHANGE_2, context);
692
693   silc_buffer_free(payload_buf);
694
695   return status;
696
697  err:
698   silc_mp_clear(ske->KEY);
699   silc_free(ske->KEY);
700   ske->KEY = NULL;
701   silc_ske_payload_two_free(ske->ke2_payload);
702
703   if (status == SILC_SKE_STATUS_OK)
704     return SILC_SKE_STATUS_ERROR;
705
706   ske->status = status;
707   return status;
708 }
709
710 /* The Key Exchange protocol is ended by calling this function. This
711    must not be called until the keys are processed like the protocol
712    defines. This function is for both initiator and responder. */
713
714 SilcSKEStatus silc_ske_end(SilcSKE ske,
715                            SilcSKESendPacketCb send_packet,
716                            void *context)
717 {
718   SilcBuffer packet;
719
720   SILC_LOG_DEBUG(("Start"));
721
722   packet = silc_buffer_alloc(4);
723   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
724   silc_buffer_format(packet,
725                      SILC_STR_UI_SHORT(SILC_SKE_STATUS_OK),
726                      SILC_STR_END);
727
728   if (send_packet)
729     (*send_packet)(ske, packet, SILC_PACKET_SUCCESS, context);
730
731   silc_buffer_free(packet);
732
733   return SILC_SKE_STATUS_OK;
734 }
735
736 /* Aborts the Key Exchange protocol. This is called if error occurs
737    while performing the protocol. The status argument is the error
738    status and it is sent to the remote end. */
739
740 SilcSKEStatus silc_ske_abort(SilcSKE ske, SilcSKEStatus status,
741                              SilcSKESendPacketCb send_packet,
742                              void *context)
743 {
744   SilcBuffer packet;
745
746   SILC_LOG_DEBUG(("Start"));
747
748   packet = silc_buffer_alloc(4);
749   silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
750   silc_buffer_format(packet,
751                      SILC_STR_UI_SHORT(status),
752                      SILC_STR_END);
753
754   if (send_packet)
755     (*send_packet)(ske, packet, SILC_PACKET_FAILURE, context);
756
757   silc_buffer_free(packet);
758
759   return SILC_SKE_STATUS_OK;
760 }
761
762 /* Assembles security properties to Key Exchange Start Payload to be
763    sent to the remote end. This checks system wide (SILC system, that is)
764    settings and chooses from those. However, if other properties
765    should be used this function is easy to replace by another function,
766    as, this function is called by the caller of the protocol and not
767    by the protocol itself. */
768
769 SilcSKEStatus 
770 silc_ske_assemble_security_properties(SilcSKE ske,
771                                       unsigned char flags,
772                                       char *version,
773                                       SilcSKEStartPayload **return_payload)
774 {
775   SilcSKEStartPayload *rp;
776   int i;
777
778   SILC_LOG_DEBUG(("Assembling KE Start Payload"));
779
780   rp = silc_calloc(1, sizeof(*rp));
781
782   /* Set flags */
783   rp->flags = flags;
784
785   /* Set random cookie */
786   rp->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(*rp->cookie));
787   for (i = 0; i < SILC_SKE_COOKIE_LEN; i++)
788     rp->cookie[i] = silc_rng_get_byte(ske->rng);
789   rp->cookie_len = SILC_SKE_COOKIE_LEN;
790
791   /* Put version */
792   rp->version = strdup(version);
793   rp->version_len = strlen(version);
794
795   /* Get supported Key Exhange groups */
796   rp->ke_grp_list = silc_ske_get_supported_groups();
797   rp->ke_grp_len = strlen(rp->ke_grp_list);
798
799   /* Get supported PKCS algorithms */
800   rp->pkcs_alg_list = silc_pkcs_get_supported();
801   rp->pkcs_alg_len = strlen(rp->pkcs_alg_list);
802
803   /* Get supported encryption algorithms */
804   rp->enc_alg_list = silc_cipher_get_supported();
805   rp->enc_alg_len = strlen(rp->enc_alg_list);
806
807   /* Get supported hash algorithms */
808   rp->hash_alg_list = silc_hash_get_supported();
809   rp->hash_alg_len = strlen(rp->hash_alg_list);
810
811   /* Get supported HMACs */
812   rp->hmac_alg_list = silc_hmac_get_supported();
813   rp->hmac_alg_len = strlen(rp->hmac_alg_list);
814
815   /* XXX */
816   /* Get supported compression algorithms */
817   rp->comp_alg_list = "";
818   rp->comp_alg_len = 0;
819
820   rp->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 
821     2 + rp->version_len +
822     2 + rp->ke_grp_len + 2 + rp->pkcs_alg_len + 
823     2 + rp->enc_alg_len + 2 + rp->hash_alg_len + 
824     2 + rp->hmac_alg_len + 2 + rp->comp_alg_len;
825
826   *return_payload = rp;
827
828   return SILC_SKE_STATUS_OK;
829 }
830
831 /* Selects the supported security properties from the remote end's Key 
832    Exchange Start Payload. */
833
834 SilcSKEStatus 
835 silc_ske_select_security_properties(SilcSKE ske,
836                                     char *version,
837                                     SilcSKEStartPayload *payload,
838                                     SilcSKEStartPayload *remote_payload)
839 {
840   SilcSKEStatus status;
841   SilcSKEStartPayload *rp;
842   char *cp;
843   int len;
844
845   SILC_LOG_DEBUG(("Parsing KE Start Payload"));
846
847   rp = remote_payload;
848
849   /* Check version string */
850   status = silc_ske_check_version(ske, rp->version, rp->version_len);
851   if (status != SILC_SKE_STATUS_OK) {
852     ske->status = status;
853     return status;
854   }
855
856   /* Flags are returned unchanged. */
857   payload->flags = rp->flags;
858
859   /* Take cookie */
860   payload->cookie = silc_calloc(SILC_SKE_COOKIE_LEN, sizeof(unsigned char));
861   payload->cookie_len = SILC_SKE_COOKIE_LEN;
862   memcpy(payload->cookie, rp->cookie, SILC_SKE_COOKIE_LEN);
863
864   /* Put our version to our reply */
865   payload->version = strdup(version);
866   payload->version_len = strlen(version);
867
868   /* Get supported Key Exchange groups */
869   cp = rp->ke_grp_list;
870   if (cp && strchr(cp, ',')) {
871     while(cp) {
872       char *item;
873
874       len = strcspn(cp, ",");
875       item = silc_calloc(len + 1, sizeof(char));
876       memcpy(item, cp, len);
877
878       SILC_LOG_DEBUG(("Proposed KE group `%s'", item));
879
880       if (silc_ske_get_group_by_name(item, NULL) == SILC_SKE_STATUS_OK) {
881         SILC_LOG_DEBUG(("Found KE group `%s'", item));
882
883         payload->ke_grp_len = len;
884         payload->ke_grp_list = item;
885         break;
886       }
887
888       cp += len;
889       if (strlen(cp) == 0)
890         cp = NULL;
891       else
892         cp++;
893
894       if (item)
895         silc_free(item);
896     }
897
898     if (!payload->ke_grp_len && !payload->ke_grp_list) {
899       SILC_LOG_DEBUG(("Could not find supported KE group"));
900       silc_free(payload);
901       return SILC_SKE_STATUS_UNKNOWN_GROUP;
902     }
903   } else {
904
905     if (!rp->ke_grp_len) {
906       SILC_LOG_DEBUG(("KE group not defined in payload"));
907       silc_free(payload);
908       return SILC_SKE_STATUS_BAD_PAYLOAD;
909     }
910
911     SILC_LOG_DEBUG(("Proposed KE group `%s'", rp->ke_grp_list));
912     SILC_LOG_DEBUG(("Found KE group `%s'", rp->ke_grp_list));
913
914     payload->ke_grp_len = rp->ke_grp_len;
915     payload->ke_grp_list = strdup(rp->ke_grp_list);
916   }
917
918   /* Get supported PKCS algorithms */
919   cp = rp->pkcs_alg_list;
920   if (cp && strchr(cp, ',')) {
921     while(cp) {
922       char *item;
923
924       len = strcspn(cp, ",");
925       item = silc_calloc(len + 1, sizeof(char));
926       memcpy(item, cp, len);
927
928       SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", item));
929
930       if (silc_pkcs_is_supported(item) == TRUE) {
931         SILC_LOG_DEBUG(("Found PKCS alg `%s'", item));
932
933         payload->pkcs_alg_len = len;
934         payload->pkcs_alg_list = item;
935         break;
936       }
937
938       cp += len;
939       if (strlen(cp) == 0)
940         cp = NULL;
941       else
942         cp++;
943
944       if (item)
945         silc_free(item);
946     }
947
948     if (!payload->pkcs_alg_len && !payload->pkcs_alg_list) {
949       SILC_LOG_DEBUG(("Could not find supported PKCS alg"));
950       silc_free(payload->ke_grp_list);
951       silc_free(payload);
952       return SILC_SKE_STATUS_UNKNOWN_PKCS;
953     }
954   } else {
955
956     if (!rp->pkcs_alg_len) {
957       SILC_LOG_DEBUG(("PKCS alg not defined in payload"));
958       silc_free(payload->ke_grp_list);
959       silc_free(payload);
960       return SILC_SKE_STATUS_BAD_PAYLOAD;
961     }
962
963     SILC_LOG_DEBUG(("Proposed PKCS alg `%s'", rp->pkcs_alg_list));
964     SILC_LOG_DEBUG(("Found PKCS alg `%s'", rp->pkcs_alg_list));
965
966     payload->pkcs_alg_len = rp->pkcs_alg_len;
967     payload->pkcs_alg_list = strdup(rp->pkcs_alg_list);
968   }
969
970   /* Get supported encryption algorithms */
971   cp = rp->enc_alg_list;
972   if (cp && strchr(cp, ',')) {
973     while(cp) {
974       char *item;
975
976       len = strcspn(cp, ",");
977       item = silc_calloc(len + 1, sizeof(char));
978       memcpy(item, cp, len);
979
980       SILC_LOG_DEBUG(("Proposed encryption alg `%s'", item));
981
982       if (silc_cipher_is_supported(item) == TRUE) {
983         SILC_LOG_DEBUG(("Found encryption alg `%s'", item));
984
985         payload->enc_alg_len = len;
986         payload->enc_alg_list = item;
987         break;
988       }
989
990       cp += len;
991       if (strlen(cp) == 0)
992         cp = NULL;
993       else
994         cp++;
995
996       if (item)
997         silc_free(item);
998     }
999
1000     if (!payload->enc_alg_len && !payload->enc_alg_list) {
1001       SILC_LOG_DEBUG(("Could not find supported encryption alg"));
1002       silc_free(payload->ke_grp_list);
1003       silc_free(payload->pkcs_alg_list);
1004       silc_free(payload);
1005       return SILC_SKE_STATUS_UNKNOWN_CIPHER;
1006     }
1007   } else {
1008
1009     if (!rp->enc_alg_len) {
1010       SILC_LOG_DEBUG(("Encryption alg not defined in payload"));
1011       silc_free(payload->ke_grp_list);
1012       silc_free(payload->pkcs_alg_list);
1013       silc_free(payload);
1014       return SILC_SKE_STATUS_BAD_PAYLOAD;
1015     }
1016
1017     SILC_LOG_DEBUG(("Proposed encryption alg `%s' and selected it",
1018                     rp->enc_alg_list));
1019
1020     payload->enc_alg_len = rp->enc_alg_len;
1021     payload->enc_alg_list = strdup(rp->enc_alg_list);
1022   }
1023
1024   /* Get supported hash algorithms */
1025   cp = rp->hash_alg_list;
1026   if (cp && strchr(cp, ',')) {
1027     while(cp) {
1028       char *item;
1029
1030       len = strcspn(cp, ",");
1031       item = silc_calloc(len + 1, sizeof(char));
1032       memcpy(item, cp, len);
1033
1034       SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
1035
1036       if (silc_hash_is_supported(item) == TRUE) {
1037         SILC_LOG_DEBUG(("Found hash alg `%s'", item));
1038
1039         payload->hash_alg_len = len;
1040         payload->hash_alg_list = item;
1041         break;
1042       }
1043
1044       cp += len;
1045       if (strlen(cp) == 0)
1046         cp = NULL;
1047       else
1048         cp++;
1049
1050       if (item)
1051         silc_free(item);
1052     }
1053
1054     if (!payload->hash_alg_len && !payload->hash_alg_list) {
1055       SILC_LOG_DEBUG(("Could not find supported hash alg"));
1056       silc_free(payload->ke_grp_list);
1057       silc_free(payload->pkcs_alg_list);
1058       silc_free(payload->enc_alg_list);
1059       silc_free(payload);
1060       return SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION;
1061     }
1062   } else {
1063
1064     if (!rp->hash_alg_len) {
1065       SILC_LOG_DEBUG(("Hash alg not defined in payload"));
1066       silc_free(payload->ke_grp_list);
1067       silc_free(payload->pkcs_alg_list);
1068       silc_free(payload->enc_alg_list);
1069       silc_free(payload);
1070       return SILC_SKE_STATUS_BAD_PAYLOAD;
1071     }
1072
1073     SILC_LOG_DEBUG(("Proposed hash alg `%s' and selected it",
1074                     rp->hash_alg_list));
1075
1076     payload->hash_alg_len = rp->hash_alg_len;
1077     payload->hash_alg_list = strdup(rp->hash_alg_list);
1078   }
1079
1080   /* Get supported HMACs */
1081   cp = rp->hmac_alg_list;
1082   if (cp && strchr(cp, ',')) {
1083     while(cp) {
1084       char *item;
1085
1086       len = strcspn(cp, ",");
1087       item = silc_calloc(len + 1, sizeof(char));
1088       memcpy(item, cp, len);
1089
1090       SILC_LOG_DEBUG(("Proposed HMAC `%s'", item));
1091
1092       if (silc_hmac_is_supported(item) == TRUE) {
1093         SILC_LOG_DEBUG(("Found HMAC `%s'", item));
1094
1095         payload->hmac_alg_len = len;
1096         payload->hmac_alg_list = item;
1097         break;
1098       }
1099
1100       cp += len;
1101       if (strlen(cp) == 0)
1102         cp = NULL;
1103       else
1104         cp++;
1105
1106       if (item)
1107         silc_free(item);
1108     }
1109
1110     if (!payload->hmac_alg_len && !payload->hmac_alg_list) {
1111       SILC_LOG_DEBUG(("Could not find supported HMAC"));
1112       silc_free(payload->ke_grp_list);
1113       silc_free(payload->pkcs_alg_list);
1114       silc_free(payload->enc_alg_list);
1115       silc_free(payload->hash_alg_list);
1116       silc_free(payload);
1117       return SILC_SKE_STATUS_UNKNOWN_HMAC;
1118     }
1119   } else {
1120
1121     if (!rp->hmac_alg_len) {
1122       SILC_LOG_DEBUG(("HMAC not defined in payload"));
1123       silc_free(payload->ke_grp_list);
1124       silc_free(payload->pkcs_alg_list);
1125       silc_free(payload->enc_alg_list);
1126       silc_free(payload->hash_alg_list);
1127       silc_free(payload);
1128       return SILC_SKE_STATUS_BAD_PAYLOAD;
1129     }
1130
1131     SILC_LOG_DEBUG(("Proposed HMAC `%s' and selected it",
1132                     rp->hmac_alg_list));
1133
1134     payload->hmac_alg_len = rp->hmac_alg_len;
1135     payload->hmac_alg_list = strdup(rp->hmac_alg_list);
1136   }
1137
1138 #if 0
1139   /* Get supported compression algorithms */
1140   cp = rp->hash_alg_list;
1141   if (cp && strchr(cp, ',')) {
1142     while(cp) {
1143       char *item;
1144
1145       len = strcspn(cp, ",");
1146       item = silc_calloc(len + 1, sizeof(char));
1147       memcpy(item, cp, len);
1148
1149       SILC_LOG_DEBUG(("Proposed hash alg `%s'", item));
1150
1151       if (silc_hash_is_supported(item) == TRUE) {
1152         SILC_LOG_DEBUG(("Found hash alg `%s'", item));
1153
1154         payload->hash_alg_len = len;
1155         payload->hash_alg_list = item;
1156         break;
1157       }
1158
1159       cp += len;
1160       if (strlen(cp) == 0)
1161         cp = NULL;
1162       else
1163         cp++;
1164
1165       if (item)
1166         silc_free(item);
1167     }
1168
1169     if (!payload->hash_alg_len && !payload->hash_alg_list) {
1170       SILC_LOG_DEBUG(("Could not find supported hash alg"));
1171       silc_ske_abort(ske, SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION);
1172       silc_free(payload->ke_grp_list);
1173       silc_free(payload->pkcs_alg_list);
1174       silc_free(payload->enc_alg_list);
1175       silc_free(payload);
1176       return;
1177     }
1178   } else {
1179
1180   }
1181 #endif
1182
1183   payload->len = 1 + 1 + 2 + SILC_SKE_COOKIE_LEN + 
1184     2 + payload->version_len + 
1185     2 + payload->ke_grp_len + 2 + payload->pkcs_alg_len + 
1186     2 + payload->enc_alg_len + 2 + payload->hash_alg_len + 
1187     2 + payload->hmac_alg_len + 2 + payload->comp_alg_len;
1188
1189   return SILC_SKE_STATUS_OK;
1190 }
1191
1192 /* Creates random number such that 1 < rnd < n and at most length
1193    of len bits. The rnd sent as argument must be initialized. */
1194
1195 SilcSKEStatus silc_ske_create_rnd(SilcSKE ske, SilcInt n, 
1196                                   unsigned int len, 
1197                                   SilcInt *rnd)
1198 {
1199   SilcSKEStatus status = SILC_SKE_STATUS_OK;
1200   unsigned char *string;
1201
1202   SILC_LOG_DEBUG(("Creating random number"));
1203
1204   /* Get the random number as string */
1205   string = silc_rng_get_rn_data(ske->rng, (len / 8));
1206   if (!string)
1207     return SILC_SKE_STATUS_ERROR;
1208
1209   /* Decode the string into a MP integer */
1210   silc_mp_bin2mp(string, (len / 8), rnd);
1211   silc_mp_mod_2exp(rnd, rnd, len);
1212
1213   /* Checks */
1214   if (silc_mp_cmp_ui(rnd, 1) < 0)
1215     status = SILC_SKE_STATUS_ERROR;
1216
1217   if (silc_mp_cmp(rnd, &n) >= 0)
1218     status = SILC_SKE_STATUS_ERROR;
1219
1220   memset(string, 'F', (len / 8));
1221   silc_free(string);
1222
1223   return status;
1224 }
1225
1226 /* Creates a hash value HASH as defined in the SKE protocol. */
1227
1228 SilcSKEStatus silc_ske_make_hash(SilcSKE ske, 
1229                                  unsigned char *return_hash,
1230                                  unsigned int *return_hash_len)
1231 {
1232   SilcSKEStatus status = SILC_SKE_STATUS_OK;
1233   SilcBuffer buf;
1234   unsigned char *e, *f, *KEY;
1235   unsigned int e_len, f_len, KEY_len;
1236   int ret;
1237
1238   SILC_LOG_DEBUG(("Start"));
1239
1240   e = silc_mp_mp2bin(&ske->ke1_payload->e, 0, &e_len);
1241   f = silc_mp_mp2bin(&ske->ke2_payload->f, 0, &f_len);
1242   KEY = silc_mp_mp2bin(ske->KEY, 0, &KEY_len);
1243
1244   buf = silc_buffer_alloc(ske->start_payload_copy->len + 
1245                           ske->pk_len + e_len + f_len + KEY_len);
1246   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
1247
1248   /* Format the buffer used to compute the hash value */
1249   ret = silc_buffer_format(buf,
1250                            SILC_STR_UI_XNSTRING(ske->start_payload_copy->data,
1251                                                 ske->start_payload_copy->len),
1252                            SILC_STR_UI_XNSTRING(ske->pk, ske->pk_len),
1253                            SILC_STR_UI_XNSTRING(e, e_len),
1254                            SILC_STR_UI_XNSTRING(f, f_len),
1255                            SILC_STR_UI_XNSTRING(KEY, KEY_len),
1256                            SILC_STR_END);
1257   if (ret == -1) {
1258     silc_buffer_free(buf);
1259     memset(e, 0, e_len);
1260     memset(f, 0, f_len);
1261     memset(KEY, 0, KEY_len);
1262     silc_free(e);
1263     silc_free(f);
1264     silc_free(KEY);
1265     return SILC_SKE_STATUS_ERROR;
1266   }
1267
1268   /* Make the hash */
1269   silc_hash_make(ske->prop->hash, buf->data, buf->len, return_hash);
1270   *return_hash_len = ske->prop->hash->hash->hash_len;
1271
1272   SILC_LOG_HEXDUMP(("Hash"), return_hash, *return_hash_len);
1273
1274   silc_buffer_free(buf);
1275   memset(e, 0, e_len);
1276   memset(f, 0, f_len);
1277   memset(KEY, 0, KEY_len);
1278   silc_free(e);
1279   silc_free(f);
1280   silc_free(KEY);
1281
1282   return status;
1283 }
1284
1285 /* Processes the provided key material `data' as the SILC protocol 
1286    specification specifies. */
1287
1288 SilcSKEStatus 
1289 silc_ske_process_key_material_data(unsigned char *data,
1290                                    unsigned int data_len,
1291                                    unsigned int req_iv_len,
1292                                    unsigned int req_enc_key_len,
1293                                    unsigned int req_hmac_key_len,
1294                                    SilcHash hash,
1295                                    SilcSKEKeyMaterial *key)
1296 {
1297   SilcBuffer buf;
1298   unsigned char hashd[32];
1299   unsigned int hash_len = req_hmac_key_len;
1300   unsigned int enc_key_len = req_enc_key_len / 8;
1301
1302   SILC_LOG_DEBUG(("Start"));
1303
1304   buf = silc_buffer_alloc(1 + data_len);
1305   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
1306   silc_buffer_format(buf,
1307                      SILC_STR_UI_CHAR(0),
1308                      SILC_STR_UI_XNSTRING(data, data_len),
1309                      SILC_STR_END);
1310
1311   /* Take IVs */
1312   memset(hashd, 0, sizeof(hashd));
1313   buf->data[0] = 0;
1314   silc_hash_make(hash, buf->data, buf->len, hashd);
1315   key->send_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
1316   memcpy(key->send_iv, hashd, req_iv_len);
1317   memset(hashd, 0, sizeof(hashd));
1318   buf->data[0] = 1;
1319   silc_hash_make(hash, buf->data, buf->len, hashd);
1320   key->receive_iv = silc_calloc(req_iv_len, sizeof(unsigned char));
1321   memcpy(key->receive_iv, hashd, req_iv_len);
1322   key->iv_len = req_iv_len;
1323
1324   /* Take the encryption keys. If requested key size is more than
1325      the size of hash length we will distribute more key material
1326      as protocol defines. */
1327   buf->data[0] = 2;
1328   if (enc_key_len > hash_len) {
1329     SilcBuffer dist;
1330     unsigned char k1[32], k2[32], k3[32];
1331     unsigned char *dtmp;
1332     
1333     /* XXX */
1334     if (enc_key_len > (3 * hash_len))
1335       return SILC_SKE_STATUS_ERROR;
1336     
1337     /* Take first round */
1338     memset(k1, 0, sizeof(k1));
1339     silc_hash_make(hash, buf->data, buf->len, k1);
1340     
1341     /* Take second round */
1342     dist = silc_buffer_alloc(data_len + hash_len);
1343     silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
1344     silc_buffer_format(dist,
1345                        SILC_STR_UI_XNSTRING(data, data_len),
1346                        SILC_STR_UI_XNSTRING(k1, hash_len),
1347                        SILC_STR_END);
1348     memset(k2, 0, sizeof(k2));
1349     silc_hash_make(hash, dist->data, dist->len, k2);
1350     
1351     /* Take third round */
1352     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
1353     silc_buffer_pull_tail(dist, hash_len);
1354     silc_buffer_pull(dist, data_len + hash_len);
1355     silc_buffer_format(dist,
1356                        SILC_STR_UI_XNSTRING(k2, hash_len),
1357                        SILC_STR_END);
1358     silc_buffer_push(dist, data_len + hash_len);
1359     memset(k3, 0, sizeof(k3));
1360     silc_hash_make(hash, dist->data, dist->len, k3);
1361
1362     /* Then, save the keys */
1363     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
1364     memcpy(dtmp, k1, hash_len);
1365     memcpy(dtmp + hash_len, k2, hash_len);
1366     memcpy(dtmp + hash_len, k3, hash_len);
1367
1368     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
1369     memcpy(key->send_enc_key, dtmp, enc_key_len);
1370     key->enc_key_len = req_enc_key_len;
1371
1372     memset(dtmp, 0, (3 * hash_len));
1373     memset(k1, 0, sizeof(k1));
1374     memset(k2, 0, sizeof(k2));
1375     memset(k3, 0, sizeof(k3));
1376     silc_free(dtmp);
1377     silc_buffer_free(dist);
1378   } else {
1379     /* Take normal hash as key */
1380     memset(hashd, 0, sizeof(hashd));
1381     silc_hash_make(hash, buf->data, buf->len, hashd);
1382     key->send_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
1383     memcpy(key->send_enc_key, hashd, enc_key_len);
1384     key->enc_key_len = req_enc_key_len;
1385   }
1386
1387   buf->data[0] = 3;
1388   if (enc_key_len > hash_len) {
1389     SilcBuffer dist;
1390     unsigned char k1[32], k2[32], k3[32];
1391     unsigned char *dtmp;
1392     
1393     /* XXX */
1394     if (enc_key_len > (3 * hash_len))
1395       return SILC_SKE_STATUS_ERROR;
1396     
1397     /* Take first round */
1398     memset(k1, 0, sizeof(k1));
1399     silc_hash_make(hash, buf->data, buf->len, k1);
1400     
1401     /* Take second round */
1402     dist = silc_buffer_alloc(data_len + hash_len);
1403     silc_buffer_pull_tail(dist, SILC_BUFFER_END(dist));
1404     silc_buffer_format(dist,
1405                        SILC_STR_UI_XNSTRING(data, data_len),
1406                        SILC_STR_UI_XNSTRING(k1, hash_len),
1407                        SILC_STR_END);
1408     memset(k2, 0, sizeof(k2));
1409     silc_hash_make(hash, dist->data, dist->len, k2);
1410     
1411     /* Take third round */
1412     dist = silc_buffer_realloc(dist, data_len + hash_len + hash_len);
1413     silc_buffer_pull_tail(dist, hash_len);
1414     silc_buffer_pull(dist, data_len + hash_len);
1415     silc_buffer_format(dist,
1416                        SILC_STR_UI_XNSTRING(k2, hash_len),
1417                        SILC_STR_END);
1418     silc_buffer_push(dist, data_len + hash_len);
1419     memset(k3, 0, sizeof(k3));
1420     silc_hash_make(hash, dist->data, dist->len, k3);
1421
1422     /* Then, save the keys */
1423     dtmp = silc_calloc((3 * hash_len), sizeof(unsigned char));
1424     memcpy(dtmp, k1, hash_len);
1425     memcpy(dtmp + hash_len, k2, hash_len);
1426     memcpy(dtmp + hash_len, k3, hash_len);
1427
1428     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
1429     memcpy(key->receive_enc_key, dtmp, enc_key_len);
1430     key->enc_key_len = req_enc_key_len;
1431
1432     memset(dtmp, 0, (3 * hash_len));
1433     memset(k1, 0, sizeof(k1));
1434     memset(k2, 0, sizeof(k2));
1435     memset(k3, 0, sizeof(k3));
1436     silc_free(dtmp);
1437     silc_buffer_free(dist);
1438   } else {
1439     /* Take normal hash as key */
1440     memset(hashd, 0, sizeof(hashd));
1441     silc_hash_make(hash, buf->data, buf->len, hashd);
1442     key->receive_enc_key = silc_calloc(enc_key_len, sizeof(unsigned char));
1443     memcpy(key->receive_enc_key, hashd, enc_key_len);
1444     key->enc_key_len = req_enc_key_len;
1445   }
1446
1447   /* Take HMAC key */
1448   memset(hashd, 0, sizeof(hashd));
1449   buf->data[0] = 4;
1450   silc_hash_make(hash, buf->data, buf->len, hashd);
1451   key->hmac_key = silc_calloc(req_hmac_key_len, sizeof(unsigned char));
1452   memcpy(key->hmac_key, hashd, req_hmac_key_len);
1453   key->hmac_key_len = req_hmac_key_len;
1454
1455   silc_buffer_free(buf);
1456
1457   return SILC_SKE_STATUS_OK;
1458 }
1459
1460 /* Processes negotiated key material as protocol specifies. This returns
1461    the actual keys to be used in the SILC. */
1462
1463 SilcSKEStatus silc_ske_process_key_material(SilcSKE ske, 
1464                                             unsigned int req_iv_len,
1465                                             unsigned int req_enc_key_len,
1466                                             unsigned int req_hmac_key_len,
1467                                             SilcSKEKeyMaterial *key)
1468 {
1469   SilcSKEStatus status;
1470   SilcBuffer buf;
1471   unsigned char *tmpbuf;
1472   int klen;
1473
1474   /* Encode KEY to binary data */
1475   tmpbuf = silc_mp_mp2bin(ske->KEY, 0, &klen);
1476
1477   buf = silc_buffer_alloc(klen + ske->hash_len);
1478   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
1479   silc_buffer_format(buf,
1480                      SILC_STR_UI_XNSTRING(tmpbuf, klen),
1481                      SILC_STR_UI_XNSTRING(ske->hash, ske->hash_len),
1482                      SILC_STR_END);
1483
1484   /* Process the key material */
1485   status = silc_ske_process_key_material_data(buf->data, buf->len,
1486                                               req_iv_len, req_enc_key_len,
1487                                               req_hmac_key_len, 
1488                                               ske->prop->hash, key);
1489
1490   memset(tmpbuf, 0, klen);
1491   silc_free(tmpbuf);
1492   silc_buffer_free(buf);
1493
1494   return status;
1495 }
1496
1497 /* Free key material structure */
1498
1499 void silc_ske_free_key_material(SilcSKEKeyMaterial *key)
1500 {
1501   if (!key)
1502     return;
1503
1504   if (key->send_iv)
1505     silc_free(key->send_iv);
1506   if (key->receive_iv)
1507     silc_free(key->receive_iv);
1508   if (key->send_enc_key) {
1509     memset(key->send_enc_key, 0, key->enc_key_len / 8);
1510     silc_free(key->send_enc_key);
1511   }
1512   if (key->receive_enc_key) {
1513     memset(key->receive_enc_key, 0, key->enc_key_len / 8);
1514     silc_free(key->receive_enc_key);
1515   }
1516   if (key->hmac_key) {
1517     memset(key->hmac_key, 0, key->hmac_key_len);
1518     silc_free(key->hmac_key);
1519   }
1520   silc_free(key);
1521 }