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