Moved silc_client_ch[u]mode[_char] to client library from silc/.
[silc.git] / lib / silccrypt / silcpkcs.c
1 /*
2
3   silcpkcs.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 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 /* $Id$ */
21
22 #include "silcincludes.h"
23
24 #include "rsa.h"
25
26 /* List of all PKCS's in SILC. PKCS's don't support SIM's thus
27    only static declarations are possible. XXX: I hope this to change
28    real soon. */
29 SilcPKCSObject silc_pkcs_list[] =
30 {
31   { "rsa", &silc_rsa_data_context, 
32     silc_rsa_init, silc_rsa_clear_keys, silc_rsa_get_public_key,
33     silc_rsa_get_private_key, silc_rsa_set_public_key,
34     silc_rsa_set_private_key, silc_rsa_context_len,
35     silc_rsa_data_context_len, silc_rsa_set_arg,
36     silc_rsa_encrypt, silc_rsa_decrypt,
37     silc_rsa_sign, silc_rsa_verify },
38
39   { NULL, NULL, NULL, NULL, NULL,
40     NULL, NULL, NULL, NULL, NULL, NULL }
41 };
42
43 /* Allocates a new SilcPKCS object. The new allocated object is returned
44    to the 'new_pkcs' argument. This function also initializes the data
45    context structure. Function returns 1 on success and 0 on error.
46
47 */
48 int silc_pkcs_alloc(const unsigned char *name, SilcPKCS *new_pkcs)
49 {
50   int i;
51
52   SILC_LOG_DEBUG(("Allocating new PKCS object"));
53
54   for (i = 0; silc_pkcs_list[i].name; i++) {
55     if (!strcmp(silc_pkcs_list[i].name, name))
56       break;
57   }
58
59   if (silc_pkcs_list[i].name == NULL)
60     return FALSE;
61
62   *new_pkcs = silc_calloc(1, sizeof(**new_pkcs));
63
64   /* Set the pointers */
65   (*new_pkcs)->pkcs = &silc_pkcs_list[i];
66   (*new_pkcs)->pkcs->data_context = 
67     silc_calloc(1, (*new_pkcs)->pkcs->data_context_len());
68   (*new_pkcs)->context = silc_calloc(1, (*new_pkcs)->pkcs->context_len());
69   (*new_pkcs)->get_key_len = silc_pkcs_get_key_len;
70
71   return TRUE;
72 }
73
74 /* Free's the PKCS object */
75
76 void silc_pkcs_free(SilcPKCS pkcs)
77 {
78   if (pkcs)
79     silc_free(pkcs->context);
80 }
81
82 /* Return TRUE if PKCS algorithm `name' is supported. */
83
84 int silc_pkcs_is_supported(const unsigned char *name)
85 {
86   int i;
87
88   for (i = 0; silc_pkcs_list[i].name; i++) {
89     if (!strcmp(silc_pkcs_list[i].name, name))
90       return TRUE;
91   }
92
93   return FALSE;
94 }
95
96 /* Returns comma separated list of supported PKCS algorithms */
97
98 char *silc_pkcs_get_supported()
99 {
100   char *list = NULL;
101   int i, len;
102
103   len = 0;
104   for (i = 0; silc_pkcs_list[i].name; i++) {
105     len += strlen(silc_pkcs_list[i].name);
106     list = silc_realloc(list, len + 1);
107
108     memcpy(list + (len - strlen(silc_pkcs_list[i].name)), 
109            silc_pkcs_list[i].name, strlen(silc_pkcs_list[i].name));
110     memcpy(list + len, ",", 1);
111     len++;
112   }
113
114   list[len - 1] = 0;
115
116   return list;
117 }
118
119 /* Returns the length of the key */
120
121 unsigned int silc_pkcs_get_key_len(SilcPKCS self)
122 {
123   return self->key_len;
124 }
125
126 /* Returns SILC style public key */
127
128 unsigned char *silc_pkcs_get_public_key(SilcPKCS pkcs, unsigned int *len)
129 {
130   return pkcs->pkcs->get_public_key(pkcs->context, len);
131 }
132
133 /* Returns SILC style private key */
134
135 unsigned char *silc_pkcs_get_private_key(SilcPKCS pkcs, unsigned int *len)
136 {
137   return pkcs->pkcs->get_private_key(pkcs->context, len);
138 }
139
140 /* Sets public key from SilcPublicKey. */
141
142 int silc_pkcs_public_key_set(SilcPKCS pkcs, SilcPublicKey public_key)
143 {
144   return pkcs->pkcs->set_public_key(pkcs->context, public_key->pk, 
145                                     public_key->pk_len);
146 }
147
148 /* Sets public key from data. */
149
150 int silc_pkcs_public_key_data_set(SilcPKCS pkcs, unsigned char *pk,
151                                   unsigned int pk_len)
152 {
153   return pkcs->pkcs->set_public_key(pkcs->context, pk, pk_len);
154 }
155
156 /* Sets private key from SilcPrivateKey. */
157
158 int silc_pkcs_private_key_set(SilcPKCS pkcs, SilcPrivateKey private_key)
159 {
160   return pkcs->pkcs->set_private_key(pkcs->context, private_key->prv, 
161                                      private_key->prv_len);
162 }
163
164 /* Sets private key from data. */
165
166 int silc_pkcs_private_key_data_set(SilcPKCS pkcs, unsigned char *prv,
167                                    unsigned int prv_len)
168 {
169   return pkcs->pkcs->set_private_key(pkcs->context, prv, prv_len);
170 }
171
172 /* Encodes and returns SILC public key identifier. If some of the 
173    arguments is NULL those are not encoded into the identifier string.
174    Protocol says that at least username and host must be provided. */
175
176 char *silc_pkcs_encode_identifier(char *username, char *host, char *realname,
177                                   char *email, char *org, char *country)
178 {
179   SilcBuffer buf;
180   char *identifier;
181   unsigned int len, tlen = 0;
182
183   if (!username || !host)
184     return NULL;
185
186   len = (username ? strlen(username) : 0) +
187         (host     ? strlen(host)     : 0) +
188         (realname ? strlen(realname) : 0) +
189         (email    ? strlen(email)    : 0) +
190         (org      ? strlen(org)      : 0) +
191         (country  ? strlen(country)  : 0);
192   
193   if (len < 3)
194     return NULL;
195
196   len += 3 + 5 + 5 + 4 + 4 + 4;
197   buf = silc_buffer_alloc(len);
198   silc_buffer_pull_tail(buf, len);
199
200   if (username) {
201     silc_buffer_format(buf,
202                        SILC_STR_UI32_STRING("UN="),
203                        SILC_STR_UI32_STRING(username),
204                        SILC_STR_END);
205     silc_buffer_pull(buf, 3 + strlen(username));
206     tlen = 3 + strlen(username); 
207   }
208     
209   if (host) {
210     silc_buffer_format(buf,
211                        SILC_STR_UI32_STRING(", "),
212                        SILC_STR_UI32_STRING("HN="),
213                        SILC_STR_UI32_STRING(host),
214                        SILC_STR_END);
215     silc_buffer_pull(buf, 5 + strlen(host));
216     tlen += 5 + strlen(host); 
217   }
218
219   if (realname) {
220     silc_buffer_format(buf,
221                        SILC_STR_UI32_STRING(", "),
222                        SILC_STR_UI32_STRING("RN="),
223                        SILC_STR_UI32_STRING(realname),
224                        SILC_STR_END);
225     silc_buffer_pull(buf, 5 + strlen(realname));
226     tlen += 5 + strlen(realname); 
227   }
228
229   if (email) {
230     silc_buffer_format(buf,
231                        SILC_STR_UI32_STRING(", "),
232                        SILC_STR_UI32_STRING("E="),
233                        SILC_STR_UI32_STRING(email),
234                        SILC_STR_END);
235     silc_buffer_pull(buf, 4 + strlen(email));
236     tlen += 4 + strlen(email); 
237   }
238
239   if (org) {
240     silc_buffer_format(buf,
241                        SILC_STR_UI32_STRING(", "),
242                        SILC_STR_UI32_STRING("O="),
243                        SILC_STR_UI32_STRING(org),
244                        SILC_STR_END);
245     silc_buffer_pull(buf, 4 + strlen(org));
246     tlen += 4 + strlen(org); 
247   }
248
249   if (country) {
250     silc_buffer_format(buf,
251                        SILC_STR_UI32_STRING(", "),
252                        SILC_STR_UI32_STRING("C="),
253                        SILC_STR_UI32_STRING(country),
254                        SILC_STR_END);
255     silc_buffer_pull(buf, 4 + strlen(country));
256     tlen += 4 + strlen(country); 
257   }
258
259   silc_buffer_push(buf, buf->data - buf->head);
260   identifier = silc_calloc(tlen, sizeof(*identifier));
261   memcpy(identifier, buf->data, tlen);
262   silc_buffer_free(buf);
263
264   return identifier;
265 }
266
267 /* Allocates SILC style public key formed from sent arguments. All data
268    is duplicated. */
269
270 SilcPublicKey silc_pkcs_public_key_alloc(char *name, char *identifier,
271                                          unsigned char *pk, 
272                                          unsigned int pk_len)
273 {
274   SilcPublicKey public_key;
275
276   public_key = silc_calloc(1, sizeof(*public_key));
277   public_key->len = 4 + 2 + strlen(name) + 2 + strlen(identifier) + pk_len;
278   public_key->name = strdup(name);
279   public_key->identifier = strdup(identifier);
280   public_key->pk_len = pk_len;
281   public_key->pk = silc_calloc(pk_len, sizeof(*public_key->pk));
282   memcpy(public_key->pk, pk, pk_len);
283
284   return public_key;
285 }
286
287 /* Free's public key */
288
289 void silc_pkcs_public_key_free(SilcPublicKey public_key)
290 {
291   if (public_key) {
292     silc_free(public_key->name);
293     silc_free(public_key->identifier);
294     silc_free(public_key->pk);
295     silc_free(public_key);
296   }
297 }
298
299 /* Allocates SILC private key formed from sent arguments. All data is
300    duplicated. */
301
302 SilcPrivateKey silc_pkcs_private_key_alloc(char *name, unsigned char *prv,
303                                            unsigned int prv_len)
304 {
305   SilcPrivateKey private_key;
306
307   private_key = silc_calloc(1, sizeof(*private_key));
308   private_key->name = strdup(name);
309   private_key->prv_len = prv_len;
310   private_key->prv = silc_calloc(prv_len, sizeof(*private_key->prv));
311   memcpy(private_key->prv, prv, prv_len);
312
313   return private_key;
314 }
315
316 /* Free's private key */
317
318 void silc_pkcs_private_key_free(SilcPrivateKey private_key)
319 {
320   if (private_key) {
321     silc_free(private_key->name);
322     silc_free(private_key->prv);
323     silc_free(private_key);
324   }
325 }
326
327 /* Encodes SILC style public key from SilcPublicKey. Returns the encoded
328    data. */
329
330 unsigned char *
331 silc_pkcs_public_key_encode(SilcPublicKey public_key, unsigned int *len)
332 {
333   SilcBuffer buf;
334   unsigned char *ret;
335
336   buf = silc_buffer_alloc(public_key->len);
337   silc_buffer_pull_tail(buf, public_key->len);
338
339   silc_buffer_format(buf,
340                      SILC_STR_UI_INT(public_key->len),
341                      SILC_STR_UI_SHORT(strlen(public_key->name)),
342                      SILC_STR_UI32_STRING(public_key->name),
343                      SILC_STR_UI_SHORT(strlen(public_key->identifier)),
344                      SILC_STR_UI32_STRING(public_key->identifier),
345                      SILC_STR_UI_XNSTRING(public_key->pk, 
346                                           public_key->pk_len),
347                      SILC_STR_END);
348   if (len)
349     *len = public_key->len;
350
351   ret = silc_calloc(buf->len, sizeof(*ret));
352   memcpy(ret, buf->data, buf->len);
353   silc_buffer_free(buf);
354
355   return ret;
356 }
357
358 /* Encodes SILC style public key. Returns the encoded data. */
359
360 unsigned char *
361 silc_pkcs_public_key_data_encode(unsigned char *pk, unsigned int pk_len,
362                                  char *pkcs, char *identifier, 
363                                  unsigned int *len)
364 {
365   SilcBuffer buf;
366   unsigned char *ret;
367   unsigned int totlen;
368
369   totlen = 4 + 2 + strlen(pkcs) + 2 + strlen(identifier) + pk_len;
370   buf = silc_buffer_alloc(totlen);
371   silc_buffer_pull_tail(buf, totlen);
372
373   silc_buffer_format(buf,
374                      SILC_STR_UI_INT(totlen),
375                      SILC_STR_UI_SHORT(strlen(pkcs)),
376                      SILC_STR_UI32_STRING(pkcs),
377                      SILC_STR_UI_SHORT(strlen(identifier)),
378                      SILC_STR_UI32_STRING(identifier),
379                      SILC_STR_UI_XNSTRING(pk, pk_len),
380                      SILC_STR_END);
381   if (len)
382     *len = totlen;
383
384   ret = silc_calloc(buf->len, sizeof(*ret));
385   memcpy(ret, buf->data, buf->len);
386   silc_buffer_free(buf);
387
388   return ret;
389 }
390
391 /* Decodes SILC style public key. Returns TRUE if the decoding was
392    successful. Allocates new public key as well. */
393
394 int silc_pkcs_public_key_decode(unsigned char *data, unsigned int data_len,
395                                 SilcPublicKey *public_key)
396 {
397   SilcBuffer buf;
398   SilcPKCS alg;
399   unsigned short pkcs_len, identifier_len;
400   unsigned int totlen, key_len;
401   unsigned char *pkcs_name = NULL, *ident = NULL, *key_data = NULL;
402
403   buf = silc_buffer_alloc(data_len);
404   silc_buffer_pull_tail(buf, data_len);
405   silc_buffer_put(buf, data, data_len);
406
407   /* Get length */
408   silc_buffer_unformat(buf,
409                        SILC_STR_UI_INT(&totlen),
410                        SILC_STR_END);
411
412   if (totlen != data_len) {
413     silc_buffer_free(buf);
414     return FALSE;
415   }
416
417   /* Get algorithm name and identifier */
418   silc_buffer_pull(buf, 4);
419   silc_buffer_unformat(buf,
420                        SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
421                        SILC_STR_UI16_NSTRING_ALLOC(&ident, &identifier_len),
422                        SILC_STR_END);
423
424   if (pkcs_len < 1 || identifier_len < 3 || 
425       pkcs_len + identifier_len > totlen)
426     goto err;
427
428   /* See if we support this algorithm */
429   if (!silc_pkcs_is_supported(pkcs_name))
430     goto err;
431
432   /* Protocol says that at least UN and HN must be provided as identifier,
433      check for these. */
434   if (!strstr(ident, "UN=") && !strstr(ident, "HN="))
435     goto err;
436
437   /* Get key data. We assume that rest of the buffer is key data. */
438   silc_buffer_pull(buf, 2 + pkcs_len + 2 + identifier_len);
439   key_len = buf->len;
440   silc_buffer_unformat(buf,
441                        SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
442                        SILC_STR_END);
443
444   /* Try to set the key. If this fails the key must be malformed. This
445      code assumes that the PKCS routine checks the format of the key. */
446   silc_pkcs_alloc(pkcs_name, &alg);
447   if (!alg->pkcs->set_public_key(alg->context, key_data, key_len))
448     goto err;
449   silc_pkcs_free(alg);
450   
451   if (public_key) {
452     *public_key = silc_calloc(1, sizeof(**public_key));
453     (*public_key)->len = totlen;
454     (*public_key)->name = pkcs_name;
455     (*public_key)->identifier = ident;
456     (*public_key)->pk = key_data;
457     (*public_key)->pk_len = key_len;
458   }
459
460   silc_buffer_free(buf);
461   return TRUE;
462
463  err:
464   if (pkcs_name)
465     silc_free(pkcs_name);
466   if (ident)
467     silc_free(ident);
468   if (key_data)
469     silc_free(key_data);
470   silc_buffer_free(buf);
471   return FALSE;
472 }
473
474 /* Encodes SILC private key from SilcPrivateKey. Returns the encoded data. */
475
476 unsigned char *
477 silc_pkcs_private_key_encode(SilcPrivateKey private_key, unsigned int *len)
478 {
479   SilcBuffer buf;
480   unsigned char *ret;
481   unsigned int totlen;
482
483   totlen = 2 + strlen(private_key->name) + private_key->prv_len;
484   buf = silc_buffer_alloc(totlen);
485   silc_buffer_pull_tail(buf, totlen);
486
487   silc_buffer_format(buf,
488                      SILC_STR_UI_SHORT(strlen(private_key->name)),
489                      SILC_STR_UI32_STRING(private_key->name),
490                      SILC_STR_UI_XNSTRING(private_key->prv, 
491                                           private_key->prv_len),
492                      SILC_STR_END);
493   if (len)
494     *len = totlen;
495
496   ret = silc_calloc(buf->len, sizeof(*ret));
497   memcpy(ret, buf->data, buf->len);
498   silc_buffer_free(buf);
499
500   return ret;
501 }
502
503 /* Encodes SILC private key. Returns the encoded data. */
504
505 unsigned char *
506 silc_pkcs_private_key_data_encode(unsigned char *prv, unsigned int prv_len,
507                                   char *pkcs, unsigned int *len)
508 {
509   SilcBuffer buf;
510   unsigned char *ret;
511   unsigned int totlen;
512
513   totlen = 2 + strlen(pkcs) + prv_len;
514   buf = silc_buffer_alloc(totlen);
515   silc_buffer_pull_tail(buf, totlen);
516
517   silc_buffer_format(buf,
518                      SILC_STR_UI_SHORT(strlen(pkcs)),
519                      SILC_STR_UI32_STRING(pkcs),
520                      SILC_STR_UI_XNSTRING(prv, prv_len),
521                      SILC_STR_END);
522   if (len)
523     *len = totlen;
524
525   ret = silc_calloc(buf->len, sizeof(*ret));
526   memcpy(ret, buf->data, buf->len);
527   silc_buffer_free(buf);
528
529   return ret;
530 }
531
532 /* Decodes SILC style public key. Returns TRUE if the decoding was
533    successful. Allocates new private key as well. */
534
535 int silc_pkcs_private_key_decode(unsigned char *data, unsigned int data_len,
536                                  SilcPrivateKey *private_key)
537 {
538   SilcBuffer buf;
539   SilcPKCS alg;
540   unsigned short pkcs_len;
541   unsigned int key_len;
542   unsigned char *pkcs_name = NULL, *key_data = NULL;
543
544   buf = silc_buffer_alloc(data_len);
545   silc_buffer_pull_tail(buf, data_len);
546   silc_buffer_put(buf, data, data_len);
547
548   /* Get algorithm name and identifier */
549   silc_buffer_unformat(buf,
550                        SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
551                        SILC_STR_END);
552
553   if (pkcs_len < 1 || pkcs_len > buf->truelen)
554     goto err;
555
556   /* See if we support this algorithm */
557   if (!silc_pkcs_is_supported(pkcs_name))
558     goto err;
559
560   /* Get key data. We assume that rest of the buffer is key data. */
561   silc_buffer_pull(buf, 2 + pkcs_len);
562   key_len = buf->len;
563   silc_buffer_unformat(buf,
564                        SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
565                        SILC_STR_END);
566
567   /* Try to set the key. If this fails the key must be malformed. This
568      code assumes that the PKCS routine checks the format of the key. */
569   silc_pkcs_alloc(pkcs_name, &alg);
570   if (!alg->pkcs->set_private_key(alg->context, key_data, key_len))
571     goto err;
572   silc_pkcs_free(alg);
573   
574   if (private_key) {
575     *private_key = silc_calloc(1, sizeof(**private_key));
576     (*private_key)->name = pkcs_name;
577     (*private_key)->prv = key_data;
578     (*private_key)->prv_len = key_len;
579   }
580
581   silc_buffer_free(buf);
582   return TRUE;
583
584  err:
585   if (pkcs_name)
586     silc_free(pkcs_name);
587   if (key_data)
588     silc_free(key_data);
589   silc_buffer_free(buf);
590   return FALSE;
591 }
592
593 /* Internal routine to save public key */
594
595 static int silc_pkcs_save_public_key_internal(char *filename,
596                                               unsigned char *data,
597                                               unsigned int data_len,
598                                               unsigned int encoding)
599 {
600   SilcBuffer buf;
601   unsigned int len;
602
603   switch(encoding) {
604   case SILC_PKCS_FILE_BIN:
605     break;
606   case SILC_PKCS_FILE_PEM:
607     data = silc_encode_pem_file(data, data_len);
608     data_len = strlen(data);
609     break;
610   }
611
612   len = data_len + (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
613                     strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
614   buf = silc_buffer_alloc(len);
615   silc_buffer_pull_tail(buf, len);
616
617   silc_buffer_format(buf,
618                      SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_BEGIN),
619                      SILC_STR_UI_XNSTRING(data, data_len),
620                      SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_END),
621                      SILC_STR_END);
622
623   /* Save into file */
624   if (silc_file_write(filename, buf->data, buf->len)) {
625     silc_buffer_free(buf);
626     return FALSE;
627   }
628
629   silc_buffer_free(buf);
630   return TRUE;
631 }
632
633 /* Saves public key into file */
634
635 int silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
636                               unsigned int encoding)
637 {
638   unsigned char *data;
639   unsigned int data_len;
640
641   data = silc_pkcs_public_key_encode(public_key, &data_len);
642   return silc_pkcs_save_public_key_internal(filename, data, data_len,
643                                             encoding);
644 }
645
646 /* Saves public key into file */
647
648 int silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
649                                    unsigned int data_len,
650                                    unsigned int encoding)
651 {
652   return silc_pkcs_save_public_key_internal(filename, data, data_len,
653                                             encoding);
654 }
655
656 /* Internal routine to save private key. */
657
658 static int silc_pkcs_save_private_key_internal(char *filename,
659                                                unsigned char *data,
660                                                unsigned int data_len,
661                                                unsigned int encoding)
662 {
663   SilcBuffer buf;
664   unsigned int len;
665
666   switch(encoding) {
667   case SILC_PKCS_FILE_BIN:
668     break;
669   case SILC_PKCS_FILE_PEM:
670     data = silc_encode_pem_file(data, data_len);
671     data_len = strlen(data);
672     break;
673   }
674
675   len = data_len + (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
676                     strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
677   buf = silc_buffer_alloc(len);
678   silc_buffer_pull_tail(buf, len);
679
680   silc_buffer_format(buf,
681                      SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_BEGIN),
682                      SILC_STR_UI_XNSTRING(data, data_len),
683                      SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_END),
684                      SILC_STR_END);
685
686   /* Save into a file */
687   if (silc_file_write_mode(filename, buf->data, buf->len, 0600)) {
688     silc_buffer_free(buf);
689     return FALSE;
690   }
691
692   silc_buffer_free(buf);
693   return TRUE;
694 }
695
696 /* Saves private key into file. */
697 /* XXX The buffer should be encrypted if passphrase is provided. */
698
699 int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, 
700                                unsigned char *passphrase,
701                                unsigned int encoding)
702 {
703   unsigned char *data;
704   unsigned int data_len;
705
706   data = silc_pkcs_private_key_encode(private_key, &data_len);
707   return silc_pkcs_save_private_key_internal(filename, data, data_len,
708                                              encoding);
709 }
710
711 /* Saves private key into file. */
712 /* XXX The buffer should be encrypted if passphrase is provided. */
713
714 int silc_pkcs_save_private_key_data(char *filename, unsigned char *data, 
715                                     unsigned int data_len,
716                                     unsigned char *passphrase,
717                                     unsigned int encoding)
718 {
719   return silc_pkcs_save_private_key_internal(filename, data, data_len,
720                                              encoding);
721 }
722
723 /* Loads public key from file and allocates new public key. Returns TRUE
724    is loading was successful. */
725
726 int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
727                               unsigned int encoding)
728 {
729   unsigned char *cp, *old, *data, byte;
730   unsigned int i, data_len, len;
731
732   old = data = silc_file_read(filename, &data_len);
733   if (!data)
734     return FALSE;
735
736   /* Check start of file and remove header from the data. */
737   len = strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN);
738   cp = data;
739   for (i = 0; i < len; i++) {
740     byte = cp[0];
741     cp++;
742     if (byte != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i]) {
743       memset(old, 0, data_len);
744       silc_free(old);
745     }
746   }
747   data = cp;
748
749   /* Decode public key */
750   if (public_key) {
751     len = data_len - (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
752                       strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
753
754     switch(encoding) {
755     case SILC_PKCS_FILE_BIN:
756       break;
757     case SILC_PKCS_FILE_PEM:
758       data = silc_decode_pem(data, len, &len);
759       break;
760     }
761
762     if (!data || !silc_pkcs_public_key_decode(data, len, public_key)) {
763       memset(old, 0, data_len);
764       silc_free(old);
765       return FALSE;
766     }
767   }
768
769   memset(old, 0, data_len);
770   silc_free(old);
771   return TRUE;
772 }
773
774 /* Load private key from file and allocates new private key. Returns TRUE
775    if loading was successful. */
776 /* XXX Should support encrypted private key files */
777
778 int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
779                                unsigned int encoding)
780 {
781   unsigned char *cp, *old, *data, byte;
782   unsigned int i, data_len, len;
783
784   old = data = silc_file_read(filename, &data_len);
785   if (!data)
786     return FALSE;
787
788   /* Check start of file and remove header from the data. */
789   len = strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN);
790   cp = data;
791   for (i = 0; i < len; i++) {
792     byte = cp[0];
793     cp++;
794     if (byte != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i]) {
795       memset(old, 0, data_len);
796       silc_free(old);
797     }
798   }
799   data = cp;
800
801   /* Decode private key */
802   if (private_key) {
803     len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
804                       strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
805
806     switch(encoding) {
807     case SILC_PKCS_FILE_BIN:
808       break;
809     case SILC_PKCS_FILE_PEM:
810       data = silc_decode_pem(data, len, &len);
811       break;
812     }
813
814     if (!data || !silc_pkcs_private_key_decode(data, len, private_key)) {
815       memset(old, 0, data_len);
816       silc_free(old);
817       return FALSE;
818     }
819   }
820
821   memset(old, 0, data_len);
822   silc_free(old);
823   return TRUE;
824 }