Added synchronous and asynchronous PKCS calls.
[crypto.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 - 2008 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 "silccrypto.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, NULL, 0, filedata,
577                                          filedata_len, (void *)&privkey,
578                                          ret_alg);
579   silc_free(data);
580
581   if (ret) {
582     privkey->fields = fields;
583     privkey->type = type;
584     *ret_private_key = privkey;
585     SILC_LOG_DEBUG(("SSH2 private key file imported successfully"));
586     return TRUE;
587   }
588
589  err:
590   if (fields)
591     silc_hash_table_free(fields);
592   return FALSE;
593 }
594
595 /* Import private key.  The key format for RSA is PKCS#1 compliant and for
596    DSA is equivalent to our DSA implementation, so we just simply call the
597    algorithm specific import function to do the magic. */
598
599 SILC_PKCS_IMPORT_PRIVATE_KEY(silc_pkcs_ssh_import_private_key)
600 {
601   SilcSshPrivateKey privkey;
602   int ret;
603
604   if (!ret_private_key || !alg)
605     return 0;
606
607   /* Allocate SSH private key context */
608   privkey = silc_calloc(1, sizeof(*privkey));
609   if (!privkey)
610     return 0;
611
612   /* Import PKCS algorithm private key */
613   ret = alg->import_private_key(alg, key, key_len, &privkey->private_key);
614   if (!ret) {
615     silc_free(privkey);
616     return 0;
617   }
618
619   privkey->pkcs = alg;
620   privkey->type = SILC_SSH_KEY_OPENSSH;
621
622   *ret_private_key = privkey;
623   if (ret_alg)
624     *ret_alg = alg;
625
626   return ret;
627 }
628
629 /* Export private key file */
630
631 SILC_PKCS_EXPORT_PRIVATE_KEY_FILE(silc_pkcs_ssh_export_private_key_file)
632 {
633   SilcSshPrivateKey privkey = private_key;
634   const SilcPKCSAlgorithm *alg = privkey->pkcs;
635   SilcBufferStruct buf;
636   unsigned char *key, *keyenc, ivdata[8], iv[16 + 1], enc[32];
637   SilcUInt32 key_len, pad_len;
638   SilcCipher des = NULL;
639   SilcHash md5 = NULL;
640
641   SILC_LOG_DEBUG(("Encode SSH2 private key file"));
642
643   /* Export the private key */
644   key = silc_pkcs_ssh_export_private_key(pkcs, stack, private_key, &key_len);
645   if (!key)
646     return NULL;
647
648   memset(&buf, 0, sizeof(buf));
649   if (!strcmp(alg->name, "rsa")) {
650     if (silc_buffer_sformat(stack, &buf,
651                             SILC_STR_ADVANCE,
652                             SILC_STR_UI32_STRING(SILC_SSH_RSA_BEGIN),
653                             SILC_STR_UI32_STRING("\n"),
654                             SILC_STR_END) < 0)
655       goto err;
656   } else if (!strcmp(alg->name, "dsa")) {
657     if (silc_buffer_sformat(stack, &buf,
658                             SILC_STR_ADVANCE,
659                             SILC_STR_UI32_STRING(SILC_SSH_DSA_BEGIN),
660                             SILC_STR_UI32_STRING("\n"),
661                             SILC_STR_END) < 0)
662       goto err;
663   } else
664     goto err;
665
666   if (passphrase && strlen(passphrase) > 0) {
667     /* Encrypt the key */
668
669     /* Allocate cipher */
670     if (!silc_cipher_alloc("3des-168-cbc", &des)) {
671       SILC_LOG_ERROR(("Unsupported algorithm 3des-168-cbc"));
672       goto err;
673     }
674
675     /* Allocate hash */
676     if (!silc_hash_alloc("md5", &md5)) {
677       SILC_LOG_ERROR(("Unsupported hash algorithm md5"));
678       goto err;
679     }
680
681     /* Generate IV */
682     silc_rng_get_rn_data(rng, sizeof(ivdata), ivdata, sizeof(ivdata));
683     silc_data2hex(ivdata, sizeof(ivdata), iv, sizeof(iv));
684
685     /* Encode header */
686     if (silc_buffer_sformat(stack, &buf,
687                             SILC_STR_ADVANCE,
688                             SILC_STR_UI32_STRING("Proc-Type: 4,ENCRYPTED\n"),
689                             SILC_STR_UI32_STRING("DEK-Info: DES-EDE3-CBC,"),
690                             SILC_STR_UI32_STRING(iv),
691                             SILC_STR_UI32_STRING("\n\n"),
692                             SILC_STR_END) < 0)
693       goto err;
694
695     /* Generate key from passphrase and IV as salt.  The passphrase is
696        hashed with the IV, then rehashed with the previous hash, passphrase
697        and the IV to produce the final key, which is the concatenation of
698        the two hashes. */
699     silc_hash_init(md5);
700     silc_hash_update(md5, passphrase, passphrase_len);
701     silc_hash_update(md5, ivdata, 8);
702     silc_hash_final(md5, enc);
703     silc_hash_init(md5);
704     silc_hash_update(md5, enc, 16);
705     silc_hash_update(md5, passphrase, passphrase_len);
706     silc_hash_update(md5, ivdata, 8);
707     silc_hash_final(md5, enc + 16);
708
709     /* Pad */
710     pad_len = (-key_len) % 8;
711     if (pad_len) {
712       keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
713       if (!keyenc)
714         goto err;
715       memset(keyenc + key_len, 'F', pad_len);
716       memcpy(keyenc, key, key_len);
717     } else {
718       keyenc = silc_smemdup(stack, key, key_len);
719       if (!keyenc)
720         goto err;
721     }
722
723     /* Encrypt */
724     silc_cipher_set_key(des, enc, 192, TRUE);
725     silc_cipher_encrypt(des, keyenc, keyenc, key_len + pad_len, ivdata);
726
727     silc_sfree(stack, key);
728     key = keyenc;
729     key_len += pad_len;
730
731     silc_cipher_free(des);
732     silc_hash_free(md5);
733   }
734
735   /* Base64 encode */
736   keyenc = silc_base64_encode_file(stack, key, key_len);
737   if (!keyenc)
738     goto err;
739
740   silc_sfree(stack, key);
741   key = keyenc;
742   key_len = strlen(keyenc);
743
744   /* Encode rest of the public key */
745   if (!strcmp(alg->name, "rsa")) {
746     if (silc_buffer_sformat(stack, &buf,
747                             SILC_STR_ADVANCE,
748                             SILC_STR_DATA(key, key_len),
749                             SILC_STR_UI32_STRING("\n"),
750                             SILC_STR_UI32_STRING(SILC_SSH_RSA_END),
751                             SILC_STR_UI32_STRING("\n"),
752                             SILC_STR_END) < 0)
753       goto err;
754   } else if (!strcmp(alg->name, "dsa")) {
755     if (silc_buffer_sformat(stack, &buf,
756                             SILC_STR_ADVANCE,
757                             SILC_STR_DATA(key, key_len),
758                             SILC_STR_UI32_STRING("\n"),
759                             SILC_STR_UI32_STRING(SILC_SSH_DSA_END),
760                             SILC_STR_UI32_STRING("\n"),
761                             SILC_STR_END) < 0)
762       goto err;
763   }
764
765   silc_sfree(stack, key);
766   key = silc_buffer_steal(&buf, ret_len);
767   return key;
768
769  err:
770   if (des)
771     silc_cipher_free(des);
772   if (md5)
773     silc_hash_free(md5);
774   silc_sfree(stack, key);
775   return NULL;
776 }
777
778 /* Export private key */
779
780 SILC_PKCS_EXPORT_PRIVATE_KEY(silc_pkcs_ssh_export_private_key)
781 {
782   SilcSshPrivateKey privkey = private_key;
783   const SilcPKCSAlgorithm *alg = privkey->pkcs;
784
785   SILC_LOG_DEBUG(("Encode SSH2 private key"));
786
787   /* Export PKCS algorithm private key */
788   if (alg->export_private_key)
789     return alg->export_private_key(alg, stack,
790                                    privkey->private_key, ret_len);
791   return NULL;
792 }
793
794 /* Return key length in bits */
795
796 SILC_PKCS_PRIVATE_KEY_BITLEN(silc_pkcs_ssh_private_key_bitlen)
797 {
798   SilcSshPrivateKey privkey = private_key;
799   return privkey->pkcs->private_key_bitlen(privkey->pkcs,
800                                            privkey->private_key);
801 }
802
803 /* Free private key */
804
805 SILC_PKCS_PRIVATE_KEY_FREE(silc_pkcs_ssh_private_key_free)
806 {
807   SilcSshPrivateKey privkey = private_key;
808
809   privkey->pkcs->private_key_free(privkey->pkcs,
810                                   privkey->private_key);
811
812   if (privkey->fields)
813     silc_hash_table_free(privkey->fields);
814   silc_free(privkey);
815 }
816
817 /* Encrypt */
818
819 SILC_PKCS_ENCRYPT(silc_pkcs_ssh_encrypt)
820 {
821   SilcSshPublicKey pubkey = public_key;
822
823   if (!pubkey->pkcs->encrypt) {
824     encrypt_cb(FALSE, NULL, 0, context);
825     return NULL;
826   }
827
828   return pubkey->pkcs->encrypt(pubkey->pkcs, pubkey->public_key,
829                                src, src_len, rng, encrypt_cb, context);
830 }
831
832 /* Decrypt */
833
834 SILC_PKCS_DECRYPT(silc_pkcs_ssh_decrypt)
835 {
836   SilcSshPrivateKey privkey = private_key;
837
838   if (!privkey->pkcs->decrypt) {
839     decrypt_cb(FALSE, NULL, 0, context);
840     return NULL;
841   }
842
843   return privkey->pkcs->decrypt(privkey->pkcs, privkey->private_key,
844                                 src, src_len, decrypt_cb, context);
845 }
846
847 /* Sign context */
848 typedef struct {
849   SilcStack stack;
850   SilcSshPrivateKey privkey;
851   SilcPKCSSignCb sign_cb;
852   void *context;
853 } *SilcSshSign;
854
855 /* Sign callback.  This formats the signature into SSH2 protocool compliant
856    format. */
857
858 static void silc_pkcs_ssh_sign_cb(SilcBool success,
859                                   const unsigned char *signature,
860                                   SilcUInt32 signature_len,
861                                   void *context)
862 {
863   SilcSshSign sign = context;
864   SilcStack stack = sign->stack;
865   unsigned char rbuf[20], sbuf[20];
866   SilcBufferStruct sig;
867   SilcAsn1 asn1;
868   SilcMPInt r, s;
869
870   memset(&sig, 0, sizeof(sig));
871
872   /* Format the signature.  RSA is easy because PKCS#1 is already in
873      correct format.  For DSA the returned signature is in PKIX compliant
874      format and we have to reformat it for SSH2. */
875   if (!strcmp(sign->privkey->pkcs->name, "dsa")) {
876     asn1 = silc_asn1_alloc(stack);
877     if (!asn1) {
878       sign->sign_cb(FALSE, NULL, 0, sign->context);
879       silc_sfree(stack, sign);
880       silc_stack_free(stack);
881       return;
882     }
883
884     /* Decode the signature */
885     silc_buffer_set(&sig, (unsigned char *)signature, signature_len);
886     if (!silc_asn1_decode(asn1, &sig,
887                           SILC_ASN1_SEQUENCE,
888                             SILC_ASN1_INT(&r),
889                             SILC_ASN1_INT(&s),
890                           SILC_ASN1_END, SILC_ASN1_END)) {
891       sign->sign_cb(FALSE, NULL, 0, sign->context);
892       silc_asn1_free(asn1);
893       silc_sfree(stack, sign);
894       silc_stack_free(stack);
895       return;
896     }
897
898     /* Encode the integers */
899     memset(rbuf, 0, sizeof(rbuf));
900     memset(sbuf, 0, sizeof(sbuf));
901     silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf));
902     silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf));
903
904     silc_asn1_free(asn1);
905
906     /* Encode SSH2 DSS signature */
907     if (silc_buffer_sformat(stack, &sig,
908                             SILC_STR_UI_INT(7),
909                             SILC_STR_UI32_STRING("ssh-dss"),
910                             SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)),
911                             SILC_STR_DATA(rbuf, sizeof(rbuf)),
912                             SILC_STR_DATA(sbuf, sizeof(sbuf)),
913                             SILC_STR_END) < 0) {
914       sign->sign_cb(FALSE, NULL, 0, sign->context);
915       silc_sfree(stack, sign);
916       silc_stack_free(stack);
917       return;
918     }
919   } else {
920     /* Encode SSH2 RSA signature */
921     if (silc_buffer_sformat(stack, &sig,
922                             SILC_STR_UI_INT(7),
923                             SILC_STR_UI32_STRING("ssh-rsa"),
924                             SILC_STR_UI_INT(signature_len),
925                             SILC_STR_DATA(signature, signature_len),
926                             SILC_STR_END) < 0) {
927       sign->sign_cb(FALSE, NULL, 0, sign->context);
928       silc_sfree(stack, sign);
929       silc_stack_free(stack);
930       return;
931     }
932   }
933
934   /* Deliver result */
935   sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
936                 sign->context);
937
938   silc_buffer_spurge(stack, &sig);
939   silc_sfree(stack, sign);
940   silc_stack_free(stack);
941 }
942
943 /* Sign */
944
945 SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
946 {
947   SilcSshPrivateKey privkey = private_key;
948   SilcStack stack;
949   SilcSshSign sign;
950
951   if (!privkey->pkcs->sign) {
952     sign_cb(FALSE, NULL, 0, context);
953     return NULL;
954   }
955
956   stack = silc_stack_alloc(0, silc_crypto_stack());
957
958   sign = silc_scalloc(stack, 1, sizeof(*sign));
959   if (!sign) {
960     sign_cb(FALSE, NULL, 0, context);
961     silc_stack_free(stack);
962     return NULL;
963   }
964
965   sign->stack = stack;
966   sign->privkey = privkey;
967   sign->sign_cb = sign_cb;
968   sign->context = context;
969
970   /* Sign.  The callback will format it to SSH2 compliant format. */
971   return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
972                              src, src_len,
973                              compute_hash, hash, rng,
974                              silc_pkcs_ssh_sign_cb, sign);
975 }
976
977 /* Verify */
978
979 SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
980 {
981   SilcSshPublicKey pubkey = public_key;
982   SilcAsyncOperation op;
983   SilcBufferStruct sig, r, s;
984   unsigned char *signame;
985   SilcStack stack = NULL;
986   SilcAsn1 asn1;
987
988   if (!pubkey->pkcs->verify) {
989     verify_cb(FALSE, context);
990     return NULL;
991   }
992
993   /* Decode the SSH2 protocol style signature encoding. */
994   silc_buffer_set(&sig, signature, signature_len);
995   if (silc_buffer_unformat(&sig,
996                            SILC_STR_UI32_STRING_ALLOC(&signame),
997                            SILC_STR_UI32_NSTRING(&signature, &signature_len),
998                            SILC_STR_END) < 0) {
999     verify_cb(FALSE, context);
1000     return NULL;
1001   }
1002   memset(&sig, 0, sizeof(sig));
1003
1004   /* DSS signature must be formatted to PKIX compliant format since our
1005      implementation expects that. */
1006   if (!strcmp(signame, "ssh-dss")) {
1007     /* The integers must be 160 bits each */
1008     if (signature_len != 40) {
1009       verify_cb(FALSE, context);
1010       silc_free(signame);
1011       return NULL;
1012     }
1013
1014     silc_buffer_set(&r, signature, 20);
1015     silc_buffer_set(&s, signature + 20, 20);
1016
1017     stack = silc_stack_alloc(0, silc_crypto_stack());
1018
1019     asn1 = silc_asn1_alloc(stack);
1020     if (!asn1) {
1021       verify_cb(FALSE, context);
1022       silc_free(signame);
1023       silc_stack_free(stack);
1024       return NULL;
1025     }
1026
1027     /* Encode signature to PKIX compliant format. */
1028     if (!silc_asn1_encode(asn1, &sig,
1029                           SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
1030                           SILC_ASN1_SEQUENCE,
1031                             SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r),
1032                             SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s),
1033                           SILC_ASN1_END, SILC_ASN1_END)) {
1034       verify_cb(FALSE, context);
1035       silc_free(signame);
1036       silc_asn1_free(asn1);
1037       silc_stack_free(stack);
1038       return NULL;
1039     }
1040
1041     signature = silc_buffer_steal(&sig, &signature_len);
1042
1043     silc_asn1_free(asn1);
1044   }
1045
1046   /* Verify */
1047   op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
1048                             signature, signature_len,
1049                             data, data_len, compute_hash, hash, rng,
1050                             verify_cb, context);
1051
1052   silc_free(signame);
1053   silc_buffer_spurge(stack, &sig);
1054   silc_stack_free(stack);
1055
1056   return op;
1057 }
1058
1059 /************************** SSH2 PKCS RSA Alg API ***************************/
1060
1061 /* The SSH2 RSA PKCS Algorithm API.  We implement here only the necessary
1062    parts of the API and the common code is used from PKCS#1 Algorithm API
1063    in silccrypt/silcpkcs1.c.  Basically everything else is PKCS#1 except
1064    the format of the public key. */
1065
1066 /* Import RSA public key.  Both RFC 4716 and OpenSSH have same format. */
1067
1068 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
1069 {
1070   SilcBufferStruct alg_key;
1071   RsaPublicKey *pubkey;
1072   unsigned char *n, *e;
1073   SilcUInt32 n_len, e_len;
1074   int ret;
1075
1076   SILC_LOG_DEBUG(("Import public key"));
1077
1078   if (!ret_public_key)
1079     return 0;
1080
1081   /* Allocate RSA public key */
1082   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1083   if (!pubkey)
1084     return 0;
1085
1086   /* Parse SSH2 RSA public key */
1087   silc_buffer_set(&alg_key, key, key_len);
1088   ret = silc_buffer_unformat(&alg_key,
1089                              SILC_STR_UI32_NSTRING(&e, &e_len),
1090                              SILC_STR_UI32_NSTRING(&n, &n_len),
1091                              SILC_STR_END);
1092   if (ret < 0)
1093     goto err;
1094   if (!n_len || !e_len)
1095     goto err;
1096
1097   /* Get MP integers */
1098   silc_mp_init(&pubkey->n);
1099   silc_mp_init(&pubkey->e);
1100   silc_mp_bin2mp(n, n_len, &pubkey->n);
1101   silc_mp_bin2mp(e, e_len, &pubkey->e);
1102
1103   /* Set key length */
1104   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
1105
1106   return ret;
1107
1108  err:
1109   silc_free(pubkey);
1110   return 0;
1111 }
1112
1113 /* Export RSA public key.  Both RFC 4716 and OpenSSH have same format. */
1114
1115 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
1116 {
1117   RsaPublicKey *pubkey = public_key;
1118   SilcBufferStruct alg_key;
1119   unsigned char *n = NULL, *e = NULL, *ret;
1120   SilcUInt32 n_len, e_len;
1121
1122   SILC_LOG_DEBUG(("Export public key"));
1123
1124   /* Encode MP integers */
1125   n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
1126   if (!n)
1127     goto err;
1128   e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
1129   if (!e)
1130     goto err;
1131
1132   /* Encode SSH2 RSA public key */
1133   memset(&alg_key, 0, sizeof(alg_key));
1134   if (silc_buffer_sformat(stack, &alg_key,
1135                           SILC_STR_UI_INT(e_len),
1136                           SILC_STR_DATA(e, e_len),
1137                           SILC_STR_UI_INT(n_len),
1138                           SILC_STR_DATA(n, n_len),
1139                           SILC_STR_END) < 0)
1140     goto err;
1141
1142   silc_free(n);
1143   silc_free(e);
1144
1145   ret = silc_buffer_steal(&alg_key, ret_len);
1146   return ret;
1147
1148  err:
1149   silc_free(n);
1150   silc_free(e);
1151   return NULL;
1152 }
1153
1154 /************************** SSH2 PKCS DSA Alg API ***************************/
1155
1156 /* The SSH2 DSA PKCS Algorithm API.  We implement here only the necessary
1157    parts of the API and the common code is used from DSS Algorithm API
1158    in silccrypt/dsa.c. */
1159
1160 /* Import DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1161
1162 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
1163 {
1164   SilcBufferStruct alg_key;
1165   DsaPublicKey *pubkey;
1166   unsigned char *p, *q, *g, *y;
1167   SilcUInt32 p_len, q_len, g_len, y_len;
1168   int ret;
1169
1170   SILC_LOG_DEBUG(("Import public key"));
1171
1172   if (!ret_public_key)
1173     return 0;
1174
1175   /* Allocate DSA public key */
1176   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1177   if (!pubkey)
1178     return 0;
1179
1180   /* Parse SSH2 DSA public key */
1181   silc_buffer_set(&alg_key, key, key_len);
1182   ret = silc_buffer_unformat(&alg_key,
1183                              SILC_STR_UI32_NSTRING(&p, &p_len),
1184                              SILC_STR_UI32_NSTRING(&q, &q_len),
1185                              SILC_STR_UI32_NSTRING(&g, &g_len),
1186                              SILC_STR_UI32_NSTRING(&y, &y_len),
1187                              SILC_STR_END);
1188   if (ret < 0)
1189     goto err;
1190   if (!p_len || !q_len || !g_len || !y_len)
1191     goto err;
1192
1193   /* Get MP integers */
1194   silc_mp_init(&pubkey->p);
1195   silc_mp_init(&pubkey->q);
1196   silc_mp_init(&pubkey->g);
1197   silc_mp_init(&pubkey->y);
1198   silc_mp_bin2mp(p, p_len, &pubkey->p);
1199   silc_mp_bin2mp(q, q_len, &pubkey->q);
1200   silc_mp_bin2mp(g, g_len, &pubkey->g);
1201   silc_mp_bin2mp(y, y_len, &pubkey->y);
1202
1203   /* Set key length */
1204   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
1205
1206   return ret;
1207
1208  err:
1209   silc_free(pubkey);
1210   return 0;
1211 }
1212
1213 /* Export DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1214
1215 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
1216 {
1217   DsaPublicKey *pubkey = public_key;
1218   SilcBufferStruct alg_key;
1219   unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
1220   SilcUInt32 p_len, q_len, g_len, y_len;
1221
1222   SILC_LOG_DEBUG(("Export public key"));
1223
1224   /* Encode MP integers */
1225   p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
1226   if (!p)
1227     goto err;
1228   q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
1229   if (!q)
1230     goto err;
1231   g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
1232   if (!g)
1233     goto err;
1234   y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
1235   if (!y)
1236     goto err;
1237
1238   /* Encode SSH2 DSA public key */
1239   memset(&alg_key, 0, sizeof(alg_key));
1240   if (silc_buffer_sformat(stack, &alg_key,
1241                           SILC_STR_UI_INT(p_len),
1242                           SILC_STR_DATA(p, p_len),
1243                           SILC_STR_UI_INT(q_len),
1244                           SILC_STR_DATA(q, q_len),
1245                           SILC_STR_UI_INT(g_len),
1246                           SILC_STR_DATA(g, g_len),
1247                           SILC_STR_UI_INT(y_len),
1248                           SILC_STR_DATA(y, y_len),
1249                           SILC_STR_END) < 0)
1250     goto err;
1251
1252   silc_free(p);
1253   silc_free(q);
1254   silc_free(g);
1255   silc_free(y);
1256
1257   ret = silc_buffer_steal(&alg_key, ret_len);
1258   return ret;
1259
1260  err:
1261   silc_free(p);
1262   silc_free(q);
1263   silc_free(g);
1264   silc_free(y);
1265   return NULL;
1266 }