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