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