Code auditing weekend results and fixes committing.
[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 + 1, 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, SILC_BUFFER_END(buf));
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, SILC_BUFFER_END(buf));
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   int ret;
403
404   buf = silc_buffer_alloc(data_len);
405   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
406   silc_buffer_put(buf, data, data_len);
407
408   /* Get length */
409   ret = silc_buffer_unformat(buf,
410                              SILC_STR_UI_INT(&totlen),
411                              SILC_STR_END);
412   if (ret == -1) {
413     silc_buffer_free(buf);
414     return FALSE;
415   }
416
417   if (totlen != data_len) {
418     silc_buffer_free(buf);
419     return FALSE;
420   }
421
422   /* Get algorithm name and identifier */
423   silc_buffer_pull(buf, 4);
424   ret =
425     silc_buffer_unformat(buf,
426                          SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
427                          SILC_STR_UI16_NSTRING_ALLOC(&ident, &identifier_len),
428                          SILC_STR_END);
429   if (ret == -1)
430     goto err;
431
432   if (pkcs_len < 1 || identifier_len < 3 || 
433       pkcs_len + identifier_len > totlen)
434     goto err;
435
436   /* See if we support this algorithm */
437   if (!silc_pkcs_is_supported(pkcs_name))
438     goto err;
439
440   /* Protocol says that at least UN and HN must be provided as identifier,
441      check for these. */
442   if (!strstr(ident, "UN=") && !strstr(ident, "HN="))
443     goto err;
444
445   /* Get key data. We assume that rest of the buffer is key data. */
446   silc_buffer_pull(buf, 2 + pkcs_len + 2 + identifier_len);
447   key_len = buf->len;
448   ret = silc_buffer_unformat(buf,
449                              SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
450                              SILC_STR_END);
451   if (ret == -1)
452     goto err;
453
454   /* Try to set the key. If this fails the key must be malformed. This
455      code assumes that the PKCS routine checks the format of the key. */
456   silc_pkcs_alloc(pkcs_name, &alg);
457   if (!alg->pkcs->set_public_key(alg->context, key_data, key_len))
458     goto err;
459   silc_pkcs_free(alg);
460   
461   if (public_key) {
462     *public_key = silc_calloc(1, sizeof(**public_key));
463     (*public_key)->len = totlen;
464     (*public_key)->name = pkcs_name;
465     (*public_key)->identifier = ident;
466     (*public_key)->pk = key_data;
467     (*public_key)->pk_len = key_len;
468   }
469
470   silc_buffer_free(buf);
471   return TRUE;
472
473  err:
474   if (pkcs_name)
475     silc_free(pkcs_name);
476   if (ident)
477     silc_free(ident);
478   if (key_data)
479     silc_free(key_data);
480   silc_buffer_free(buf);
481   return FALSE;
482 }
483
484 /* Encodes SILC private key from SilcPrivateKey. Returns the encoded data. */
485
486 unsigned char *
487 silc_pkcs_private_key_encode(SilcPrivateKey private_key, unsigned int *len)
488 {
489   SilcBuffer buf;
490   unsigned char *ret;
491   unsigned int totlen;
492
493   totlen = 2 + strlen(private_key->name) + private_key->prv_len;
494   buf = silc_buffer_alloc(totlen);
495   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
496
497   silc_buffer_format(buf,
498                      SILC_STR_UI_SHORT(strlen(private_key->name)),
499                      SILC_STR_UI32_STRING(private_key->name),
500                      SILC_STR_UI_XNSTRING(private_key->prv, 
501                                           private_key->prv_len),
502                      SILC_STR_END);
503   if (len)
504     *len = totlen;
505
506   ret = silc_calloc(buf->len, sizeof(*ret));
507   memcpy(ret, buf->data, buf->len);
508   silc_buffer_free(buf);
509
510   return ret;
511 }
512
513 /* Encodes SILC private key. Returns the encoded data. */
514
515 unsigned char *
516 silc_pkcs_private_key_data_encode(unsigned char *prv, unsigned int prv_len,
517                                   char *pkcs, unsigned int *len)
518 {
519   SilcBuffer buf;
520   unsigned char *ret;
521   unsigned int totlen;
522
523   totlen = 2 + strlen(pkcs) + prv_len;
524   buf = silc_buffer_alloc(totlen);
525   silc_buffer_pull_tail(buf, totlen);
526
527   silc_buffer_format(buf,
528                      SILC_STR_UI_SHORT(strlen(pkcs)),
529                      SILC_STR_UI32_STRING(pkcs),
530                      SILC_STR_UI_XNSTRING(prv, prv_len),
531                      SILC_STR_END);
532   if (len)
533     *len = totlen;
534
535   ret = silc_calloc(buf->len, sizeof(*ret));
536   memcpy(ret, buf->data, buf->len);
537   silc_buffer_free(buf);
538
539   return ret;
540 }
541
542 /* Decodes SILC style public key. Returns TRUE if the decoding was
543    successful. Allocates new private key as well. */
544
545 int silc_pkcs_private_key_decode(unsigned char *data, unsigned int data_len,
546                                  SilcPrivateKey *private_key)
547 {
548   SilcBuffer buf;
549   SilcPKCS alg;
550   unsigned short pkcs_len;
551   unsigned int key_len;
552   unsigned char *pkcs_name = NULL, *key_data = NULL;
553   int ret;
554
555   buf = silc_buffer_alloc(data_len);
556   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
557   silc_buffer_put(buf, data, data_len);
558
559   /* Get algorithm name and identifier */
560   ret = 
561     silc_buffer_unformat(buf,
562                          SILC_STR_UI16_NSTRING_ALLOC(&pkcs_name, &pkcs_len),
563                          SILC_STR_END);
564   if (ret == -1)
565     goto err;
566
567   if (pkcs_len < 1 || pkcs_len > buf->truelen)
568     goto err;
569
570   /* See if we support this algorithm */
571   if (!silc_pkcs_is_supported(pkcs_name))
572     goto err;
573
574   /* Get key data. We assume that rest of the buffer is key data. */
575   silc_buffer_pull(buf, 2 + pkcs_len);
576   key_len = buf->len;
577   ret = silc_buffer_unformat(buf,
578                              SILC_STR_UI_XNSTRING_ALLOC(&key_data, key_len),
579                              SILC_STR_END);
580   if (ret == -1)
581     goto err;
582
583   /* Try to set the key. If this fails the key must be malformed. This
584      code assumes that the PKCS routine checks the format of the key. */
585   silc_pkcs_alloc(pkcs_name, &alg);
586   if (!alg->pkcs->set_private_key(alg->context, key_data, key_len))
587     goto err;
588   silc_pkcs_free(alg);
589   
590   if (private_key) {
591     *private_key = silc_calloc(1, sizeof(**private_key));
592     (*private_key)->name = pkcs_name;
593     (*private_key)->prv = key_data;
594     (*private_key)->prv_len = key_len;
595   }
596
597   silc_buffer_free(buf);
598   return TRUE;
599
600  err:
601   if (pkcs_name)
602     silc_free(pkcs_name);
603   if (key_data)
604     silc_free(key_data);
605   silc_buffer_free(buf);
606   return FALSE;
607 }
608
609 /* Internal routine to save public key */
610
611 static int silc_pkcs_save_public_key_internal(char *filename,
612                                               unsigned char *data,
613                                               unsigned int data_len,
614                                               unsigned int encoding)
615 {
616   SilcBuffer buf;
617   unsigned int len;
618
619   switch(encoding) {
620   case SILC_PKCS_FILE_BIN:
621     break;
622   case SILC_PKCS_FILE_PEM:
623     data = silc_encode_pem_file(data, data_len);
624     data_len = strlen(data);
625     break;
626   }
627
628   len = data_len + (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
629                     strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
630   buf = silc_buffer_alloc(len);
631   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
632
633   silc_buffer_format(buf,
634                      SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_BEGIN),
635                      SILC_STR_UI_XNSTRING(data, data_len),
636                      SILC_STR_UI32_STRING(SILC_PKCS_PUBLIC_KEYFILE_END),
637                      SILC_STR_END);
638
639   /* Save into file */
640   if (silc_file_write(filename, buf->data, buf->len)) {
641     silc_buffer_free(buf);
642     return FALSE;
643   }
644
645   silc_buffer_free(buf);
646   return TRUE;
647 }
648
649 /* Saves public key into file */
650
651 int silc_pkcs_save_public_key(char *filename, SilcPublicKey public_key,
652                               unsigned int encoding)
653 {
654   unsigned char *data;
655   unsigned int data_len;
656
657   data = silc_pkcs_public_key_encode(public_key, &data_len);
658   return silc_pkcs_save_public_key_internal(filename, data, data_len,
659                                             encoding);
660 }
661
662 /* Saves public key into file */
663
664 int silc_pkcs_save_public_key_data(char *filename, unsigned char *data,
665                                    unsigned int data_len,
666                                    unsigned int encoding)
667 {
668   return silc_pkcs_save_public_key_internal(filename, data, data_len,
669                                             encoding);
670 }
671
672 /* Internal routine to save private key. */
673
674 static int silc_pkcs_save_private_key_internal(char *filename,
675                                                unsigned char *data,
676                                                unsigned int data_len,
677                                                unsigned int encoding)
678 {
679   SilcBuffer buf;
680   unsigned int len;
681
682   switch(encoding) {
683   case SILC_PKCS_FILE_BIN:
684     break;
685   case SILC_PKCS_FILE_PEM:
686     data = silc_encode_pem_file(data, data_len);
687     data_len = strlen(data);
688     break;
689   }
690
691   len = data_len + (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
692                     strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
693   buf = silc_buffer_alloc(len);
694   silc_buffer_pull_tail(buf, SILC_BUFFER_END(buf));
695
696   silc_buffer_format(buf,
697                      SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_BEGIN),
698                      SILC_STR_UI_XNSTRING(data, data_len),
699                      SILC_STR_UI32_STRING(SILC_PKCS_PRIVATE_KEYFILE_END),
700                      SILC_STR_END);
701
702   /* Save into a file */
703   if (silc_file_write_mode(filename, buf->data, buf->len, 0600)) {
704     silc_buffer_free(buf);
705     return FALSE;
706   }
707
708   silc_buffer_free(buf);
709   return TRUE;
710 }
711
712 /* Saves private key into file. */
713 /* XXX The buffer should be encrypted if passphrase is provided. */
714
715 int silc_pkcs_save_private_key(char *filename, SilcPrivateKey private_key, 
716                                unsigned char *passphrase,
717                                unsigned int encoding)
718 {
719   unsigned char *data;
720   unsigned int data_len;
721
722   data = silc_pkcs_private_key_encode(private_key, &data_len);
723   return silc_pkcs_save_private_key_internal(filename, data, data_len,
724                                              encoding);
725 }
726
727 /* Saves private key into file. */
728 /* XXX The buffer should be encrypted if passphrase is provided. */
729
730 int silc_pkcs_save_private_key_data(char *filename, unsigned char *data, 
731                                     unsigned int data_len,
732                                     unsigned char *passphrase,
733                                     unsigned int encoding)
734 {
735   return silc_pkcs_save_private_key_internal(filename, data, data_len,
736                                              encoding);
737 }
738
739 /* Loads public key from file and allocates new public key. Returns TRUE
740    is loading was successful. */
741
742 int silc_pkcs_load_public_key(char *filename, SilcPublicKey *public_key,
743                               unsigned int encoding)
744 {
745   unsigned char *cp, *old, *data, byte;
746   unsigned int i, data_len, len;
747
748   old = data = silc_file_read(filename, &data_len);
749   if (!data)
750     return FALSE;
751
752   /* Check start of file and remove header from the data. */
753   len = strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN);
754   cp = data;
755   for (i = 0; i < len; i++) {
756     byte = cp[0];
757     cp++;
758     if (byte != SILC_PKCS_PUBLIC_KEYFILE_BEGIN[i]) {
759       memset(old, 0, data_len);
760       silc_free(old);
761     }
762   }
763   data = cp;
764
765   /* Decode public key */
766   if (public_key) {
767     len = data_len - (strlen(SILC_PKCS_PUBLIC_KEYFILE_BEGIN) +
768                       strlen(SILC_PKCS_PUBLIC_KEYFILE_END));
769
770     switch(encoding) {
771     case SILC_PKCS_FILE_BIN:
772       break;
773     case SILC_PKCS_FILE_PEM:
774       data = silc_decode_pem(data, len, &len);
775       break;
776     }
777
778     if (!data || !silc_pkcs_public_key_decode(data, len, public_key)) {
779       memset(old, 0, data_len);
780       silc_free(old);
781       return FALSE;
782     }
783   }
784
785   memset(old, 0, data_len);
786   silc_free(old);
787   return TRUE;
788 }
789
790 /* Load private key from file and allocates new private key. Returns TRUE
791    if loading was successful. */
792 /* XXX Should support encrypted private key files */
793
794 int silc_pkcs_load_private_key(char *filename, SilcPrivateKey *private_key,
795                                unsigned int encoding)
796 {
797   unsigned char *cp, *old, *data, byte;
798   unsigned int i, data_len, len;
799
800   old = data = silc_file_read(filename, &data_len);
801   if (!data)
802     return FALSE;
803
804   /* Check start of file and remove header from the data. */
805   len = strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN);
806   cp = data;
807   for (i = 0; i < len; i++) {
808     byte = cp[0];
809     cp++;
810     if (byte != SILC_PKCS_PRIVATE_KEYFILE_BEGIN[i]) {
811       memset(old, 0, data_len);
812       silc_free(old);
813     }
814   }
815   data = cp;
816
817   /* Decode private key */
818   if (private_key) {
819     len = data_len - (strlen(SILC_PKCS_PRIVATE_KEYFILE_BEGIN) +
820                       strlen(SILC_PKCS_PRIVATE_KEYFILE_END));
821
822     switch(encoding) {
823     case SILC_PKCS_FILE_BIN:
824       break;
825     case SILC_PKCS_FILE_PEM:
826       data = silc_decode_pem(data, len, &len);
827       break;
828     }
829
830     if (!data || !silc_pkcs_private_key_decode(data, len, private_key)) {
831       memset(old, 0, data_len);
832       silc_free(old);
833       return FALSE;
834     }
835   }
836
837   memset(old, 0, data_len);
838   silc_free(old);
839   return TRUE;
840 }