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