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