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