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