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