Added SILC SSH2 library providing support for SSH2 public and
[silc.git] / lib / silcssh / silcssh_pkcs.c
1 /*
2
3   silcssh_pkcs.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2007 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; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silc.h"
21 #include "rsa.h"
22 #include "dsa.h"
23 #include "silcssh_pkcs.h"
24
25 /************************** Types and definitions ***************************/
26
27 /* RFC 4716 public key file markers */
28 #define SILC_SSH_PUBLIC_KEY_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
29 #define SILC_SSH_PUBLIC_KEY_END "---- END SSH2 PUBLIC KEY ----"
30
31 /* OpenSSH private key file markers */
32 #define SILC_SSH_RSA_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
33 #define SILC_SSH_RSA_END "-----END RSA PRIVATE KEY-----"
34 #define SILC_SSH_DSA_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
35 #define SILC_SSH_DSA_END "-----END DSA PRIVATE KEY-----"
36
37 /****************************** SSH2 PKCS API *******************************/
38
39 /* Get algorithm context */
40
41 SILC_PKCS_GET_ALGORITHM(silc_pkcs_ssh_get_algorithm)
42 {
43   SilcSshPublicKey pubkey = public_key;
44   return pubkey->pkcs;
45 }
46
47 /* Import public key file */
48
49 SILC_PKCS_IMPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_import_public_key_file)
50 {
51   SilcSshPublicKey pubkey;
52   SilcBufferStruct keybuf, line;
53   SilcHashTable fields;
54   unsigned char *data;
55   SilcSshKeyType type;
56   int ret;
57
58   SILC_LOG_DEBUG(("Parsing SSH2 public key file"));
59
60   if (!ret_public_key)
61     return FALSE;
62   if (encoding == SILC_PKCS_FILE_BIN)
63     return FALSE;
64
65   silc_buffer_set(&keybuf, filedata, filedata_len);
66
67   /* Check for RFC 4716 style public key markers */
68   if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
69     SILC_LOG_DEBUG(("Malformed SSH2 public key markers"));
70     return FALSE;
71   }
72   if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_PUBLIC_KEY_BEGIN) +
73                                    strlen(SILC_SSH_PUBLIC_KEY_END))) ||
74       strncmp(silc_buffer_data(&line), SILC_SSH_PUBLIC_KEY_BEGIN,
75               silc_buffer_len(&line))) {
76     /* We assume the key is OpenSSH style public key. */
77     type = SILC_SSH_KEY_OPENSSH;
78     silc_buffer_set(&keybuf, filedata, filedata_len);
79
80     /* Get subject name from the end of the file */
81     if (!silc_buffer_strchr(&keybuf, ' ', FALSE)) {
82       SILC_LOG_DEBUG(("Malformed SSH2 public key"));
83       return FALSE;
84     }
85     if (!silc_buffer_pull(&keybuf, 1)) {
86       SILC_LOG_DEBUG(("Malformed SSH2 public key"));
87       return FALSE;
88     }
89     if (!silc_buffer_len(&keybuf)) {
90       SILC_LOG_DEBUG(("Malformed SSH2 public key"));
91       return FALSE;
92     }
93
94     /* Add subject name to public key headers */
95     fields = silc_ssh_allocate_fields();
96     if (!fields)
97       return FALSE;
98     silc_hash_table_add(fields, strdup("Subject"),
99                         silc_memdup(silc_buffer_data(&keybuf),
100                                     silc_buffer_len(&keybuf)));
101
102     filedata_len = silc_buffer_headlen(&keybuf) - 1;
103     SILC_LOG_DEBUG(("Add Subject header to public key"));
104
105     /* Skip algorithm name */
106     silc_buffer_start(&keybuf);
107     if (!silc_buffer_strchr(&keybuf, ' ', TRUE)) {
108       SILC_LOG_DEBUG(("Malformed SSH2 public key"));
109       silc_hash_table_free(fields);
110       return FALSE;
111     }
112     if (!silc_buffer_pull(&keybuf, 1)) {
113       SILC_LOG_DEBUG(("Malformed SSH2 public key"));
114       silc_hash_table_free(fields);
115       return FALSE;
116     }
117     if (silc_buffer_len(&keybuf) < filedata_len) {
118       SILC_LOG_DEBUG(("Malformed SSH2 public key"));
119       silc_hash_table_free(fields);
120       return FALSE;
121     }
122
123     filedata = silc_buffer_data(&keybuf);
124     SILC_LOG_DEBUG(("Public key is OpenSSH public key"));
125
126   } else {
127     /* RFC 4716 style public key */
128     type = SILC_SSH_KEY_SSH2;
129
130     filedata = silc_buffer_data(&keybuf);
131     filedata_len = silc_buffer_len(&keybuf) - strlen(SILC_SSH_PUBLIC_KEY_END);
132     silc_buffer_set(&keybuf, filedata, filedata_len);
133
134     /* Parse public key headers */
135     fields = silc_ssh_parse_headers(&keybuf);
136     if (!fields)
137       return FALSE;
138
139     filedata = silc_buffer_data(&keybuf);
140     filedata_len = silc_buffer_len(&keybuf);
141
142     SILC_LOG_DEBUG(("Public key is standard SSH2 public key"));
143   }
144
145   /* Decode */
146   data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
147   if (!data) {
148     silc_hash_table_free(fields);
149     return FALSE;
150   }
151   filedata = data;
152
153   /* Decode the public key */
154   ret = silc_pkcs_ssh_import_public_key(pkcs, NULL, filedata, filedata_len,
155                                         (void *)&pubkey, ret_alg);
156   silc_free(data);
157
158   if (ret) {
159     pubkey->fields = fields;
160     pubkey->type = type;
161     *ret_public_key = pubkey;
162     SILC_LOG_DEBUG(("SSH2 public key file imported successfully"));
163     return TRUE;
164   }
165
166   silc_hash_table_free(fields);
167   return FALSE;
168 }
169
170 /* Import public key */
171
172 SILC_PKCS_IMPORT_PUBLIC_KEY(silc_pkcs_ssh_import_public_key)
173 {
174   SilcSshPublicKey pubkey;
175   int ret;
176
177   ret = silc_ssh_public_key_decode(key, key_len, &pubkey);
178   if (ret) {
179     if (ret_alg)
180       *ret_alg = pubkey->pkcs;
181     if (ret_public_key)
182       *ret_public_key = pubkey;
183   }
184
185   return ret;
186 }
187
188 /* Export public key file */
189
190 SILC_PKCS_EXPORT_PUBLIC_KEY_FILE(silc_pkcs_ssh_export_public_key_file)
191 {
192   SilcSshPublicKey pubkey = public_key;
193   SilcHashTableList htl;
194   SilcBufferStruct buf, fields;
195   unsigned char *key, *data;
196   SilcUInt32 key_len;
197   char *field, *value, tmp[1024], tmp2[1024 + 24];
198   int i, j, c;
199
200   SILC_LOG_DEBUG(("Encoding %s public key file",
201                   pubkey->type == SILC_SSH_KEY_SSH2 ? "SSH2" : "OpenSSH"));
202
203   /* Export key */
204   key = silc_pkcs_ssh_export_public_key(pkcs, stack, pubkey, &key_len);
205   if (!key)
206     return NULL;
207
208   /* Base64 encode the key data */
209   if (pubkey->type == SILC_SSH_KEY_SSH2)
210     data = silc_base64_encode_file(stack, key, key_len);
211   else
212     data = silc_base64_encode(stack, key, key_len);
213   if (!data)
214     return NULL;
215   silc_sfree(stack, key);
216   key = data;
217   key_len = strlen(data);
218
219   memset(&buf, 0, sizeof(buf));
220   memset(&fields, 0, sizeof(fields));
221   memset(tmp, 0, sizeof(tmp));
222   memset(tmp2, 0, sizeof(tmp2));
223
224   switch (pubkey->type) {
225   case SILC_SSH_KEY_SSH2:
226     /* RFC 4716 style public key file */
227
228     if (pubkey->fields) {
229       /* Encode public key headers */
230       silc_hash_table_list(pubkey->fields, &htl);
231       while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
232         /* Wrap lines with over 72 characters */
233         silc_snprintf(tmp, sizeof(tmp), "%s: %s", field, value);
234         for (i = 0, j = 0, c = 1; i < strlen(tmp); i++, c++) {
235           if (c == 72) {
236             tmp2[j++] = '\\';
237             tmp2[j++] = '\n';
238             i--;
239             c = 0;
240             continue;
241           }
242
243           tmp2[j++] = tmp[i];
244         }
245         tmp2[j++] = '\n';
246
247         if (silc_buffer_sstrformat(stack, &fields, tmp2, SILC_STRFMT_END) < 0) {
248           silc_buffer_spurge(stack, &fields);
249           silc_sfree(stack, key);
250           return NULL;
251         }
252
253         memset(tmp2, 0, sizeof(tmp2));
254       }
255       silc_hash_table_list_reset(&htl);
256     }
257
258     /* Encode the file */
259     if (silc_buffer_sformat(stack, &buf,
260                             SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_BEGIN),
261                             SILC_STR_UI32_STRING("\n"),
262                             SILC_STR_UI_XNSTRING(silc_buffer_data(&fields),
263                                                  silc_buffer_len(&fields)),
264                             SILC_STR_UI_XNSTRING(key, key_len),
265                             SILC_STR_UI32_STRING("\n"),
266                             SILC_STR_UI32_STRING(SILC_SSH_PUBLIC_KEY_END),
267                             SILC_STR_UI32_STRING("\n"),
268                             SILC_STR_END) < 0) {
269       silc_buffer_spurge(stack, &fields);
270       silc_sfree(stack, key);
271       return NULL;
272     }
273
274     break;
275
276   case SILC_SSH_KEY_OPENSSH:
277     /* OpenSSH style public key file */
278
279     if (!strcmp(pubkey->pkcs->name, "rsa"))
280       silc_snprintf(tmp, sizeof(tmp), "ssh-rsa ");
281     else if (!strcmp(pubkey->pkcs->name, "dsa"))
282       silc_snprintf(tmp, sizeof(tmp), "ssh-dss ");
283
284     /* Get subject */
285     value = (char *)silc_ssh_public_key_get_field(pubkey, "Subject");
286
287     /* Encode the file */
288     if (silc_buffer_sformat(stack, &buf,
289                             SILC_STR_UI32_STRING(tmp),
290                             SILC_STR_UI_XNSTRING(key, key_len),
291                             SILC_STR_UI32_STRING(" "),
292                             SILC_STR_UI32_STRING(value),
293                             SILC_STR_UI32_STRING("\n"),
294                             SILC_STR_END) < 0) {
295       silc_buffer_spurge(stack, &buf);
296       silc_sfree(stack, key);
297       return NULL;
298     }
299
300     break;
301
302   default:
303     silc_sfree(stack, key);
304     return NULL;
305     break;
306   }
307
308   silc_sfree(stack, key);
309   key = silc_buffer_steal(&buf, ret_len);
310
311   silc_buffer_spurge(stack, &fields);
312
313   return key;
314 }
315
316 /* Export public key */
317
318 SILC_PKCS_EXPORT_PUBLIC_KEY(silc_pkcs_ssh_export_public_key)
319 {
320   return silc_ssh_public_key_encode(stack, public_key, ret_len);
321 }
322
323 /* Return key length in bits */
324
325 SILC_PKCS_PUBLIC_KEY_BITLEN(silc_pkcs_ssh_public_key_bitlen)
326 {
327   SilcSshPublicKey pubkey = public_key;
328   return pubkey->pkcs->public_key_bitlen(pubkey->pkcs, pubkey->public_key);
329 }
330
331 /* Copy public key */
332
333 SILC_PKCS_PUBLIC_KEY_COPY(silc_pkcs_ssh_public_key_copy)
334 {
335   SilcSshPublicKey pubkey = public_key, new_pubkey;
336   SilcHashTableList htl;
337   char *field, *value;
338
339   new_pubkey = silc_calloc(1, sizeof(*new_pubkey));
340   if (!new_pubkey)
341     return NULL;
342   new_pubkey->pkcs = pubkey->pkcs;
343   new_pubkey->type = pubkey->type;
344
345   new_pubkey->public_key =
346     pubkey->pkcs->public_key_copy(pubkey->pkcs, pubkey->public_key);
347   if (!new_pubkey->public_key) {
348     silc_free(new_pubkey);
349     return NULL;
350   }
351
352   if (pubkey->fields) {
353     new_pubkey->fields = silc_ssh_allocate_fields();
354     if (!new_pubkey->fields) {
355       pubkey->pkcs->public_key_free(pubkey->pkcs, pubkey->public_key);
356       silc_free(new_pubkey);
357       return NULL;
358     }
359
360     silc_hash_table_list(pubkey->fields, &htl);
361     while (silc_hash_table_get(&htl, (void *)&field, (void *)&value))
362       silc_hash_table_add(new_pubkey->fields, strdup(field), strdup(value));
363     silc_hash_table_list_reset(&htl);
364   }
365
366   return new_pubkey;
367 }
368
369 /* Compare two public keys */
370
371 SILC_PKCS_PUBLIC_KEY_COMPARE(silc_pkcs_ssh_public_key_compare)
372 {
373   SilcSshPublicKey k1 = key1, k2 = key2;
374   SilcHashTableList htl;
375   char *field, *value, *value2;
376
377   if (strcmp(k1->pkcs->name, k2->pkcs->name))
378     return FALSE;
379
380   if (k1->fields && !k2->fields)
381     return FALSE;
382   if (!k1->fields && k2->fields)
383     return FALSE;
384
385   if (k1->fields && k2->fields) {
386     if (silc_hash_table_count(k1->fields) != silc_hash_table_count(k2->fields))
387       return FALSE;
388
389     silc_hash_table_list(k1->fields, &htl);
390     while (silc_hash_table_get(&htl, (void *)&field, (void *)&value)) {
391       value2 = (char *)silc_ssh_public_key_get_field(k2, field);
392       if (!value2)
393         return FALSE;
394       if (strcmp(value, value2))
395         return FALSE;
396     }
397     silc_hash_table_list_reset(&htl);
398   }
399
400   return k1->pkcs->public_key_compare(k1->pkcs, k1->public_key,
401                                       k2->public_key);
402 }
403
404 /* Free public key */
405
406 SILC_PKCS_PUBLIC_KEY_FREE(silc_pkcs_ssh_public_key_free)
407 {
408   silc_ssh_public_key_free(public_key);
409 }
410
411 /* Import private key file.  Supports only OpenSSH (OpenSSL to be exact)
412    private key files. */
413
414 SILC_PKCS_IMPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_import_private_key_file)
415 {
416   const SilcPKCSAlgorithm *alg;
417   SilcSshPrivateKey privkey = NULL;
418   SilcHashTable fields;
419   SilcBufferStruct keybuf, line;
420   unsigned char *data, iv[8], key[32];
421   SilcSshKeyType type;
422   char *proctype, *dekinfo;
423   SilcCipher des;
424   SilcHash md5;
425   int ret;
426
427   SILC_LOG_DEBUG(("Parsing SSH2 private key file"));
428
429   if (!ret_private_key)
430     return FALSE;
431   if (encoding == SILC_PKCS_FILE_BIN)
432     return FALSE;
433
434   silc_buffer_set(&keybuf, filedata, filedata_len);
435
436   /* Check for private key markers */
437   if (!silc_ssh_parse_line(&keybuf, &line, TRUE)) {
438     SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
439     return FALSE;
440   }
441   if ((silc_buffer_len(&keybuf) < (strlen(SILC_SSH_RSA_BEGIN) +
442                                    strlen(SILC_SSH_RSA_END))) ||
443       (strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
444                silc_buffer_len(&line)) &&
445        strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
446                silc_buffer_len(&line)))) {
447     SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
448     return FALSE;
449   }
450
451   /* Get PKCS algorithm */
452   if (!strncmp(silc_buffer_data(&line), SILC_SSH_RSA_BEGIN,
453                silc_buffer_len(&line))) {
454     alg = silc_pkcs_find_algorithm("rsa", "ssh");
455     if (!alg) {
456       SILC_LOG_ERROR(("Unsupported PKCS algorithm rsa/ssh"));
457       return FALSE;
458     }
459   } else if (!strncmp(silc_buffer_data(&line), SILC_SSH_DSA_BEGIN,
460                       silc_buffer_len(&line))) {
461     alg = silc_pkcs_find_algorithm("dsa", "ssh");
462     if (!alg) {
463       SILC_LOG_ERROR(("Unsupported PKCS algorithm dsa/ssh"));
464       return FALSE;
465     }
466   } else
467     return FALSE;
468
469   type = SILC_SSH_KEY_OPENSSH;
470   filedata = silc_buffer_data(&keybuf);
471
472   /* Skip end marker */
473   if (!silc_buffer_strchr(&keybuf, '-', FALSE)) {
474     SILC_LOG_DEBUG(("Malformed SSH2 private key markers"));
475     return FALSE;
476   }
477   filedata_len = silc_buffer_data(&keybuf) - filedata;
478   silc_buffer_set(&keybuf, filedata, filedata_len);
479
480   /* Parse private key headers.  They define how the private key has been
481      encrypted. */
482   fields = silc_ssh_parse_headers(&keybuf);
483   if (!fields)
484     return FALSE;
485
486   /* Skip empty line after headers */
487   if (silc_hash_table_count(fields) > 0)
488     silc_ssh_parse_line(&keybuf, NULL, TRUE);
489
490   filedata = silc_buffer_data(&keybuf);
491   filedata_len = silc_buffer_len(&keybuf);
492
493   /* Decode */
494   data = silc_base64_decode(NULL, filedata, filedata_len, &filedata_len);
495   if (!data) {
496     SILC_LOG_DEBUG(("Malformed SSH2 private key"));
497     goto err;
498   }
499   filedata = data;
500
501   SILC_LOG_DEBUG(("Private key is %s", (silc_hash_table_count(fields) ?
502                                         "encrypted" : "not encrypted")));
503
504   if (silc_hash_table_count(fields) > 0 && passphrase) {
505     /* Decrypt */
506
507     /* Get encryption info */
508     if (!silc_hash_table_find(fields, "Proc-Type", NULL, (void *)&proctype)) {
509       SILC_LOG_ERROR(("Malformed SSH2 private key"));
510       goto err;
511     }
512     if (strcmp(proctype, "4,ENCRYPTED")) {
513       SILC_LOG_ERROR(("Malformed SSH2 private key"));
514       goto err;
515     }
516
517     /* OpenSSH uses 3DES-EDE only */
518     if (!silc_hash_table_find(fields, "DEK-Info", NULL, (void *)&dekinfo)) {
519       SILC_LOG_ERROR(("Malformed SSH2 private key"));
520       goto err;
521     }
522     if (strncmp(dekinfo, "DES-EDE3-CBC", strlen("DES-EDE3-CBC"))) {
523       SILC_LOG_ERROR(("Unsupported SSH2 private key cipher '%s'", dekinfo));
524       goto err;
525     }
526
527     /* Allocate cipher */
528     if (!silc_cipher_alloc("3des-168-cbc", &des)) {
529       SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
530       goto err;
531     }
532
533     /* Allocate hash */
534     if (!silc_hash_alloc("md5", &md5)) {
535       SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
536       goto err;
537     }
538
539     /* Get IV from private key file */
540     dekinfo = strchr(dekinfo, ',');
541     if (!dekinfo || strlen(dekinfo) < 16) {
542       SILC_LOG_ERROR(("Malformed SSH2 private key"));
543       goto err;
544     }
545     dekinfo++;
546     silc_hex2data(dekinfo, iv, sizeof(iv), NULL);
547
548     /* Generate key from passphrase and IV as salt.  The passphrase is
549        hashed with the IV, then rehashed with the previous hash, passphrase
550        and the IV to produce the final key, which is the concatenation of
551        the two hashes. */
552     silc_hash_init(md5);
553     silc_hash_update(md5, passphrase, passphrase_len);
554     silc_hash_update(md5, iv, 8);
555     silc_hash_final(md5, key);
556     silc_hash_init(md5);
557     silc_hash_update(md5, key, 16);
558     silc_hash_update(md5, passphrase, passphrase_len);
559     silc_hash_update(md5, iv, 8);
560     silc_hash_final(md5, key + 16);
561
562     /* Decrypt */
563     silc_cipher_set_key(des, key, 192, FALSE);
564     if (!silc_cipher_decrypt(des, filedata, filedata, filedata_len, iv)) {
565       SILC_LOG_ERROR(("Malformed SSH2 private key"));
566       silc_cipher_free(des);
567       silc_hash_free(md5);
568       goto err;
569     }
570
571     silc_cipher_free(des);
572     silc_hash_free(md5);
573   }
574
575   /* Decode the private key */
576   ret = silc_pkcs_ssh_import_private_key(pkcs, alg, filedata, filedata_len,
577                                          (void *)&privkey, ret_alg);
578   silc_free(data);
579
580   if (ret) {
581     privkey->fields = fields;
582     privkey->type = type;
583     *ret_private_key = privkey;
584     SILC_LOG_DEBUG(("SSH2 private key file imported successfully"));
585     return TRUE;
586   }
587
588  err:
589   if (fields)
590     silc_hash_table_free(fields);
591   return FALSE;
592 }
593
594 /* Import private key.  The key format for RSA is PKCS#1 compliant and for
595    DSA is equivalent to our DSA implementation, so we just simply call the
596    algorithm specific import function to do the magic. */
597
598 SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_ssh_import_private_key)
599 {
600   SilcSshPrivateKey privkey;
601   int ret;
602
603   if (!ret_private_key || !alg)
604     return 0;
605
606   /* Allocate SSH private key context */
607   privkey = silc_calloc(1, sizeof(*privkey));
608   if (!privkey)
609     return 0;
610
611   /* Import PKCS algorithm private key */
612   ret = alg->import_private_key(alg, key, key_len, &privkey->private_key);
613   if (!ret) {
614     silc_free(privkey);
615     return 0;
616   }
617
618   privkey->pkcs = alg;
619   privkey->type = SILC_SSH_KEY_OPENSSH;
620
621   *ret_private_key = privkey;
622   if (ret_alg)
623     *ret_alg = alg;
624
625   return ret;
626 }
627
628 /* Export private key file */
629
630 SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file)
631 {
632   SilcSshPrivateKey privkey = private_key;
633   const SilcPKCSAlgorithm *alg = privkey->pkcs;
634   SilcBufferStruct buf;
635   unsigned char *key, *keyenc, ivdata[8], iv[16 + 1], enc[32];
636   SilcUInt32 key_len, pad_len;
637   SilcCipher des = NULL;
638   SilcHash md5 = NULL;
639
640   SILC_LOG_DEBUG(("Encode SSH2 private key file"));
641
642   /* Export the private key */
643   key = silc_pkcs_ssh_export_private_key(pkcs, stack, private_key, &key_len);
644   if (!key)
645     return NULL;
646
647   memset(&buf, 0, sizeof(buf));
648   if (!strcmp(alg->name, "rsa")) {
649     if (silc_buffer_sformat(stack, &buf,
650                             SILC_STR_ADVANCE,
651                             SILC_STR_UI32_STRING(SILC_SSH_RSA_BEGIN),
652                             SILC_STR_UI32_STRING("\n"),
653                             SILC_STR_END) < 0)
654       goto err;
655   } else if (!strcmp(alg->name, "dsa")) {
656     if (silc_buffer_sformat(stack, &buf,
657                             SILC_STR_ADVANCE,
658                             SILC_STR_UI32_STRING(SILC_SSH_DSA_BEGIN),
659                             SILC_STR_UI32_STRING("\n"),
660                             SILC_STR_END) < 0)
661       goto err;
662   } else
663     goto err;
664
665   if (passphrase && strlen(passphrase) > 0) {
666     /* Encrypt the key */
667
668     /* Allocate cipher */
669     if (!silc_cipher_alloc("3des-168-cbc", &des)) {
670       SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
671       goto err;
672     }
673
674     /* Allocate hash */
675     if (!silc_hash_alloc("md5", &md5)) {
676       SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
677       goto err;
678     }
679
680     /* Generate IV */
681     silc_rng_get_rn_data(rng, sizeof(ivdata), ivdata, sizeof(ivdata));
682     silc_data2hex(ivdata, sizeof(ivdata), iv, sizeof(iv));
683
684     /* Encode header */
685     if (silc_buffer_sformat(stack, &buf,
686                             SILC_STR_ADVANCE,
687                             SILC_STR_UI32_STRING("Proc-Type: 4,ENCRYPTED\n"),
688                             SILC_STR_UI32_STRING("DEK-Info: DES-EDE3-CBC,"),
689                             SILC_STR_UI32_STRING(iv),
690                             SILC_STR_UI32_STRING("\n\n"),
691                             SILC_STR_END) < 0)
692       goto err;
693
694     /* Generate key from passphrase and IV as salt.  The passphrase is
695        hashed with the IV, then rehashed with the previous hash, passphrase
696        and the IV to produce the final key, which is the concatenation of
697        the two hashes. */
698     silc_hash_init(md5);
699     silc_hash_update(md5, passphrase, passphrase_len);
700     silc_hash_update(md5, ivdata, 8);
701     silc_hash_final(md5, enc);
702     silc_hash_init(md5);
703     silc_hash_update(md5, enc, 16);
704     silc_hash_update(md5, passphrase, passphrase_len);
705     silc_hash_update(md5, ivdata, 8);
706     silc_hash_final(md5, enc + 16);
707
708     /* Pad */
709     pad_len = 8 - (key_len % 8);
710     if (pad_len) {
711       keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
712       if (!key)
713         goto err;
714       memset(keyenc + key_len, 'F', pad_len);
715       memcpy(keyenc, key, key_len);
716     } else {
717       keyenc = silc_memdup(key, key_len);
718       if (!keyenc)
719         goto err;
720     }
721
722     /* Encrypt */
723     silc_cipher_set_key(des, enc, 192, TRUE);
724     silc_cipher_encrypt(des, keyenc, keyenc, key_len + pad_len, ivdata);
725
726     silc_sfree(stack, key);
727     key = keyenc;
728     key_len += pad_len;
729
730     silc_cipher_free(des);
731     silc_hash_free(md5);
732   }
733
734   /* Base64 encode */
735   keyenc = silc_base64_encode_file(stack, key, key_len);
736   if (!keyenc)
737     goto err;
738
739   silc_sfree(stack, key);
740   key = keyenc;
741   key_len = strlen(keyenc);
742
743   /* Encode rest of the public key */
744   if (!strcmp(alg->name, "rsa")) {
745     if (silc_buffer_sformat(stack, &buf,
746                             SILC_STR_ADVANCE,
747                             SILC_STR_DATA(key, key_len),
748                             SILC_STR_UI32_STRING("\n"),
749                             SILC_STR_UI32_STRING(SILC_SSH_RSA_END),
750                             SILC_STR_UI32_STRING("\n"),
751                             SILC_STR_END) < 0)
752       goto err;
753   } else if (!strcmp(alg->name, "dsa")) {
754     if (silc_buffer_sformat(stack, &buf,
755                             SILC_STR_ADVANCE,
756                             SILC_STR_DATA(key, key_len),
757                             SILC_STR_UI32_STRING("\n"),
758                             SILC_STR_UI32_STRING(SILC_SSH_DSA_END),
759                             SILC_STR_UI32_STRING("\n"),
760                             SILC_STR_END) < 0)
761       goto err;
762   }
763
764   silc_sfree(stack, key);
765   key = silc_buffer_steal(&buf, ret_len);
766   return key;
767
768  err:
769   if (des)
770     silc_cipher_free(des);
771   if (md5)
772     silc_hash_free(md5);
773   silc_sfree(stack, key);
774   return NULL;
775 }
776
777 /* Export private key */
778
779 SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_ssh_export_private_key)
780 {
781   SilcSshPrivateKey privkey = private_key;
782   const SilcPKCSAlgorithm *alg = privkey->pkcs;
783
784   SILC_LOG_DEBUG(("Encode SSH2 private key"));
785
786   /* Export PKCS algorithm private key */
787   if (alg->export_private_key)
788     return alg->export_private_key(alg, stack,
789                                    privkey->private_key, ret_len);
790   return NULL;
791 }
792
793 /* Return key length in bits */
794
795 SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_ssh_private_key_bitlen)
796 {
797   SilcSshPrivateKey privkey = private_key;
798   return privkey->pkcs->private_key_bitlen(privkey->pkcs,
799                                            privkey->private_key);
800 }
801
802 /* Free private key */
803
804 SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_ssh_private_key_free)
805 {
806   SilcSshPrivateKey privkey = private_key;
807
808   privkey->pkcs->private_key_free(privkey->pkcs,
809                                   privkey->private_key);
810
811   if (privkey->fields)
812     silc_hash_table_free(privkey->fields);
813   silc_free(privkey);
814 }
815
816 /* Encrypt */
817
818 SILC_PKCS_ENCRYPT(silc_pkcs_ssh_encrypt)
819 {
820   SilcSshPublicKey pubkey = public_key;
821
822   if (!pubkey->pkcs->encrypt) {
823     encrypt_cb(FALSE, NULL, 0, context);
824     return NULL;
825   }
826
827   return pubkey->pkcs->encrypt(pubkey->pkcs, pubkey->public_key,
828                                src, src_len, rng, encrypt_cb, context);
829 }
830
831 /* Decrypt */
832
833 SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt)
834 {
835   SilcSshPrivateKey privkey = private_key;
836
837   if (!privkey->pkcs->decrypt) {
838     decrypt_cb(FALSE, NULL, 0, context);
839     return NULL;
840   }
841
842   return privkey->pkcs->decrypt(privkey->pkcs, privkey->private_key,
843                                 src, src_len, decrypt_cb, context);
844 }
845
846 /* Sign */
847
848 SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
849 {
850   SilcSshPrivateKey privkey = private_key;
851
852   if (!privkey->pkcs->sign) {
853     sign_cb(FALSE, NULL, 0, context);
854     return NULL;
855   }
856
857   return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
858                              src, src_len,
859                              compute_hash, hash, rng,
860                              sign_cb, context);
861 }
862
863 /* Verify */
864
865 SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
866 {
867   SilcSshPublicKey pubkey = public_key;
868
869   if (!pubkey->pkcs->verify) {
870     verify_cb(FALSE, context);
871     return NULL;
872   }
873
874   return pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
875                               signature, signature_len,
876                               data, data_len, hash, rng,
877                               verify_cb, context);
878 }
879
880 /************************** SSH2 PKCS RSA Alg API ***************************/
881
882 /* The SSH2 RSA PKCS Algorithm API.  We implement here only the necessary
883    parts of the API and the common code is used from PKCS#1 Algorithm API
884    in silccrypt/silcpkcs1.c.  Basically everything else is PKCS#1 except
885    the format of the public key. */
886
887 /* Import RSA public key.  Both RFC 4716 and OpenSSH have same format. */
888
889 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
890 {
891   SilcBufferStruct alg_key;
892   RsaPublicKey *pubkey;
893   unsigned char *n, *e;
894   SilcUInt32 n_len, e_len;
895   int ret;
896
897   SILC_LOG_DEBUG(("Import public key"));
898
899   if (!ret_public_key)
900     return 0;
901
902   /* Allocate RSA public key */
903   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
904   if (!pubkey)
905     return 0;
906
907   /* Parse SSH2 RSA public key */
908   silc_buffer_set(&alg_key, key, key_len);
909   ret = silc_buffer_unformat(&alg_key,
910                              SILC_STR_UI32_NSTRING(&e, &e_len),
911                              SILC_STR_UI32_NSTRING(&n, &n_len),
912                              SILC_STR_END);
913   if (ret < 0)
914     goto err;
915   if (!n_len || !e_len)
916     goto err;
917
918   /* Get MP integers */
919   silc_mp_init(&pubkey->n);
920   silc_mp_init(&pubkey->e);
921   silc_mp_bin2mp(n, n_len, &pubkey->n);
922   silc_mp_bin2mp(e, e_len, &pubkey->e);
923
924   /* Set key length */
925   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
926
927   return ret;
928
929  err:
930   silc_free(pubkey);
931   return 0;
932 }
933
934 /* Export RSA public key.  Both RFC 4716 and OpenSSH have same format. */
935
936 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
937 {
938   RsaPublicKey *pubkey = public_key;
939   SilcBufferStruct alg_key;
940   unsigned char *n = NULL, *e = NULL, *ret;
941   SilcUInt32 n_len, e_len;
942
943   SILC_LOG_DEBUG(("Export public key"));
944
945   /* Encode MP integers */
946   n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
947   if (!n)
948     goto err;
949   e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
950   if (!e)
951     goto err;
952
953   /* Encode SSH2 RSA public key */
954   memset(&alg_key, 0, sizeof(alg_key));
955   if (silc_buffer_sformat(stack, &alg_key,
956                           SILC_STR_UI_INT(e_len),
957                           SILC_STR_DATA(e, e_len),
958                           SILC_STR_UI_INT(n_len),
959                           SILC_STR_DATA(n, n_len),
960                           SILC_STR_END) < 0)
961     goto err;
962
963   silc_free(n);
964   silc_free(e);
965
966   ret = silc_buffer_steal(&alg_key, ret_len);
967   return ret;
968
969  err:
970   silc_free(n);
971   silc_free(e);
972   return NULL;
973 }
974
975 /************************** SSH2 PKCS DSA Alg API ***************************/
976
977 /* The SSH2 DSA PKCS Algorithm API.  We implement here only the necessary
978    parts of the API and the common code is used from DSS Algorithm API
979    in silccrypt/dsa.c. */
980
981 /* Import DSA public key.  Both RFC 4716 and OpenSSH have same format. */
982
983 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
984 {
985   SilcBufferStruct alg_key;
986   DsaPublicKey *pubkey;
987   unsigned char *p, *q, *g, *y;
988   SilcUInt32 p_len, q_len, g_len, y_len;
989   int ret;
990
991   SILC_LOG_DEBUG(("Import public key"));
992
993   if (!ret_public_key)
994     return 0;
995
996   /* Allocate DSA public key */
997   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
998   if (!pubkey)
999     return 0;
1000
1001   /* Parse SSH2 DSA public key */
1002   silc_buffer_set(&alg_key, key, key_len);
1003   ret = silc_buffer_unformat(&alg_key,
1004                              SILC_STR_UI32_NSTRING(&p, &p_len),
1005                              SILC_STR_UI32_NSTRING(&q, &q_len),
1006                              SILC_STR_UI32_NSTRING(&g, &g_len),
1007                              SILC_STR_UI32_NSTRING(&y, &y_len),
1008                              SILC_STR_END);
1009   if (ret < 0)
1010     goto err;
1011   if (!p_len || !q_len || !g_len || !y_len)
1012     goto err;
1013
1014   /* Get MP integers */
1015   silc_mp_init(&pubkey->p);
1016   silc_mp_init(&pubkey->q);
1017   silc_mp_init(&pubkey->g);
1018   silc_mp_init(&pubkey->y);
1019   silc_mp_bin2mp(p, p_len, &pubkey->p);
1020   silc_mp_bin2mp(q, q_len, &pubkey->q);
1021   silc_mp_bin2mp(g, g_len, &pubkey->g);
1022   silc_mp_bin2mp(y, y_len, &pubkey->y);
1023
1024   /* Set key length */
1025   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
1026
1027   return ret;
1028
1029  err:
1030   silc_free(pubkey);
1031   return 0;
1032 }
1033
1034 /* Export DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1035
1036 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
1037 {
1038   DsaPublicKey *pubkey = public_key;
1039   SilcBufferStruct alg_key;
1040   unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
1041   SilcUInt32 p_len, q_len, g_len, y_len;
1042
1043   SILC_LOG_DEBUG(("Export public key"));
1044
1045   /* Encode MP integers */
1046   p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
1047   if (!p)
1048     goto err;
1049   q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
1050   if (!q)
1051     goto err;
1052   g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
1053   if (!g)
1054     goto err;
1055   y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
1056   if (!y)
1057     goto err;
1058
1059   /* Encode SSH2 DSA public key */
1060   memset(&alg_key, 0, sizeof(alg_key));
1061   if (silc_buffer_sformat(stack, &alg_key,
1062                           SILC_STR_UI_INT(p_len),
1063                           SILC_STR_DATA(p, p_len),
1064                           SILC_STR_UI_INT(q_len),
1065                           SILC_STR_DATA(q, q_len),
1066                           SILC_STR_UI_INT(g_len),
1067                           SILC_STR_DATA(g, g_len),
1068                           SILC_STR_UI_INT(y_len),
1069                           SILC_STR_DATA(y, y_len),
1070                           SILC_STR_END) < 0)
1071     goto err;
1072
1073   silc_free(p);
1074   silc_free(q);
1075   silc_free(g);
1076   silc_free(y);
1077
1078   ret = silc_buffer_steal(&alg_key, ret_len);
1079   return ret;
1080
1081  err:
1082   silc_free(p);
1083   silc_free(q);
1084   silc_free(g);
1085   silc_free(y);
1086   return NULL;
1087 }