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