Added SILC Thread Queue API
[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 = (-key_len) % 8;
710     if (pad_len) {
711       keyenc = silc_smalloc(stack, (key_len + pad_len) * sizeof(*keyenc));
712       if (!keyenc)
713         goto err;
714       memset(keyenc + key_len, 'F', pad_len);
715       memcpy(keyenc, key, key_len);
716     } else {
717       keyenc = silc_smemdup(stack, 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 context */
847 typedef struct {
848   SilcStack stack;
849   SilcSshPrivateKey privkey;
850   SilcPKCSSignCb sign_cb;
851   void *context;
852 } *SilcSshSign;
853
854 /* Sign callback.  This formats the signature into SSH2 protocool compliant
855    format. */
856
857 static void silc_pkcs_ssh_sign_cb(SilcBool success,
858                                   const unsigned char *signature,
859                                   SilcUInt32 signature_len,
860                                   void *context)
861 {
862   SilcSshSign sign = context;
863   SilcStack stack = sign->stack;
864   unsigned char rbuf[20], sbuf[20];
865   SilcBufferStruct sig;
866   SilcAsn1 asn1;
867   SilcMPInt r, s;
868
869   memset(&sig, 0, sizeof(sig));
870
871   /* Format the signature.  RSA is easy because PKCS#1 is already in
872      correct format.  For DSA the returned signature is in PKIX compliant
873      format and we have to reformat it for SSH2. */
874   if (!strcmp(sign->privkey->pkcs->name, "dsa")) {
875     asn1 = silc_asn1_alloc(stack);
876     if (!asn1) {
877       sign->sign_cb(FALSE, NULL, 0, sign->context);
878       silc_sfree(stack, sign);
879       silc_stack_free(stack);
880       return;
881     }
882
883     /* Decode the signature */
884     silc_buffer_set(&sig, (unsigned char *)signature, signature_len);
885     if (!silc_asn1_decode(asn1, &sig,
886                           SILC_ASN1_SEQUENCE,
887                             SILC_ASN1_INT(&r),
888                             SILC_ASN1_INT(&s),
889                           SILC_ASN1_END, SILC_ASN1_END)) {
890       sign->sign_cb(FALSE, NULL, 0, sign->context);
891       silc_asn1_free(asn1);
892       silc_sfree(stack, sign);
893       silc_stack_free(stack);
894       return;
895     }
896
897     /* Encode the integers */
898     memset(rbuf, 0, sizeof(rbuf));
899     memset(sbuf, 0, sizeof(sbuf));
900     silc_mp_mp2bin_noalloc(&r, rbuf, sizeof(rbuf));
901     silc_mp_mp2bin_noalloc(&s, sbuf, sizeof(sbuf));
902
903     silc_asn1_free(asn1);
904
905     /* Encode SSH2 DSS signature */
906     if (silc_buffer_sformat(stack, &sig,
907                             SILC_STR_UI_INT(7),
908                             SILC_STR_UI32_STRING("ssh-dss"),
909                             SILC_STR_UI_INT(sizeof(rbuf) + sizeof(sbuf)),
910                             SILC_STR_DATA(rbuf, sizeof(rbuf)),
911                             SILC_STR_DATA(sbuf, sizeof(sbuf)),
912                             SILC_STR_END) < 0) {
913       sign->sign_cb(FALSE, NULL, 0, sign->context);
914       silc_sfree(stack, sign);
915       silc_stack_free(stack);
916       return;
917     }
918   } else {
919     /* Encode SSH2 RSA signature */
920     if (silc_buffer_sformat(stack, &sig,
921                             SILC_STR_UI_INT(7),
922                             SILC_STR_UI32_STRING("ssh-rsa"),
923                             SILC_STR_UI_INT(signature_len),
924                             SILC_STR_DATA(signature, signature_len),
925                             SILC_STR_END) < 0) {
926       sign->sign_cb(FALSE, NULL, 0, sign->context);
927       silc_sfree(stack, sign);
928       silc_stack_free(stack);
929       return;
930     }
931   }
932
933   /* Deliver result */
934   sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
935                 sign->context);
936
937   silc_buffer_spurge(stack, &sig);
938   silc_sfree(stack, sign);
939   silc_stack_free(stack);
940 }
941
942 /* Sign */
943
944 SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
945 {
946   SilcSshPrivateKey privkey = private_key;
947   SilcStack stack;
948   SilcSshSign sign;
949
950   if (!privkey->pkcs->sign) {
951     sign_cb(FALSE, NULL, 0, context);
952     return NULL;
953   }
954
955   stack = silc_stack_alloc(0, silc_crypto_stack());
956
957   sign = silc_scalloc(stack, 1, sizeof(*sign));
958   if (!sign) {
959     sign_cb(FALSE, NULL, 0, context);
960     silc_stack_free(stack);
961     return NULL;
962   }
963
964   sign->stack = stack;
965   sign->privkey = privkey;
966   sign->sign_cb = sign_cb;
967   sign->context = context;
968
969   /* Sign.  The callback will format it to SSH2 compliant format. */
970   return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
971                              src, src_len,
972                              compute_hash, hash, rng,
973                              silc_pkcs_ssh_sign_cb, sign);
974 }
975
976 /* Verify */
977
978 SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
979 {
980   SilcSshPublicKey pubkey = public_key;
981   SilcAsyncOperation op;
982   SilcBufferStruct sig, r, s;
983   unsigned char *signame;
984   SilcStack stack = NULL;
985   SilcAsn1 asn1;
986
987   if (!pubkey->pkcs->verify) {
988     verify_cb(FALSE, context);
989     return NULL;
990   }
991
992   /* Decode the SSH2 protocol style signature encoding. */
993   silc_buffer_set(&sig, signature, signature_len);
994   if (silc_buffer_unformat(&sig,
995                            SILC_STR_UI32_STRING_ALLOC(&signame),
996                            SILC_STR_UI32_NSTRING(&signature, &signature_len),
997                            SILC_STR_END) < 0) {
998     verify_cb(FALSE, context);
999     return NULL;
1000   }
1001   memset(&sig, 0, sizeof(sig));
1002
1003   /* DSS signature must be formatted to PKIX compliant format since our
1004      implementation expects that. */
1005   if (!strcmp(signame, "ssh-dss")) {
1006     /* The integers must be 160 bits each */
1007     if (signature_len != 40) {
1008       verify_cb(FALSE, context);
1009       silc_free(signame);
1010       return NULL;
1011     }
1012
1013     silc_buffer_set(&r, signature, 20);
1014     silc_buffer_set(&s, signature + 20, 20);
1015
1016     stack = silc_stack_alloc(0, silc_crypto_stack());
1017
1018     asn1 = silc_asn1_alloc(stack);
1019     if (!asn1) {
1020       verify_cb(FALSE, context);
1021       silc_free(signame);
1022       silc_stack_free(stack);
1023       return NULL;
1024     }
1025
1026     /* Encode signature to PKIX compliant format. */
1027     if (!silc_asn1_encode(asn1, &sig,
1028                           SILC_ASN1_OPTS(SILC_ASN1_ALLOC),
1029                           SILC_ASN1_SEQUENCE,
1030                             SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &r),
1031                             SILC_ASN1_ANY_PRIMITIVE(SILC_ASN1_TAG_INTEGER, &s),
1032                           SILC_ASN1_END, SILC_ASN1_END)) {
1033       verify_cb(FALSE, context);
1034       silc_free(signame);
1035       silc_asn1_free(asn1);
1036       silc_stack_free(stack);
1037       return NULL;
1038     }
1039
1040     signature = silc_buffer_steal(&sig, &signature_len);
1041
1042     silc_asn1_free(asn1);
1043   }
1044
1045   /* Verify */
1046   op = pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
1047                             signature, signature_len,
1048                             data, data_len, hash, rng,
1049                             verify_cb, context);
1050
1051   silc_free(signame);
1052   silc_buffer_spurge(stack, &sig);
1053   silc_stack_free(stack);
1054
1055   return op;
1056 }
1057
1058 /************************** SSH2 PKCS RSA Alg API ***************************/
1059
1060 /* The SSH2 RSA PKCS Algorithm API.  We implement here only the necessary
1061    parts of the API and the common code is used from PKCS#1 Algorithm API
1062    in silccrypt/silcpkcs1.c.  Basically everything else is PKCS#1 except
1063    the format of the public key. */
1064
1065 /* Import RSA public key.  Both RFC 4716 and OpenSSH have same format. */
1066
1067 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
1068 {
1069   SilcBufferStruct alg_key;
1070   RsaPublicKey *pubkey;
1071   unsigned char *n, *e;
1072   SilcUInt32 n_len, e_len;
1073   int ret;
1074
1075   SILC_LOG_DEBUG(("Import public key"));
1076
1077   if (!ret_public_key)
1078     return 0;
1079
1080   /* Allocate RSA public key */
1081   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1082   if (!pubkey)
1083     return 0;
1084
1085   /* Parse SSH2 RSA public key */
1086   silc_buffer_set(&alg_key, key, key_len);
1087   ret = silc_buffer_unformat(&alg_key,
1088                              SILC_STR_UI32_NSTRING(&e, &e_len),
1089                              SILC_STR_UI32_NSTRING(&n, &n_len),
1090                              SILC_STR_END);
1091   if (ret < 0)
1092     goto err;
1093   if (!n_len || !e_len)
1094     goto err;
1095
1096   /* Get MP integers */
1097   silc_mp_init(&pubkey->n);
1098   silc_mp_init(&pubkey->e);
1099   silc_mp_bin2mp(n, n_len, &pubkey->n);
1100   silc_mp_bin2mp(e, e_len, &pubkey->e);
1101
1102   /* Set key length */
1103   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
1104
1105   return ret;
1106
1107  err:
1108   silc_free(pubkey);
1109   return 0;
1110 }
1111
1112 /* Export RSA public key.  Both RFC 4716 and OpenSSH have same format. */
1113
1114 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
1115 {
1116   RsaPublicKey *pubkey = public_key;
1117   SilcBufferStruct alg_key;
1118   unsigned char *n = NULL, *e = NULL, *ret;
1119   SilcUInt32 n_len, e_len;
1120
1121   SILC_LOG_DEBUG(("Export public key"));
1122
1123   /* Encode MP integers */
1124   n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
1125   if (!n)
1126     goto err;
1127   e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
1128   if (!e)
1129     goto err;
1130
1131   /* Encode SSH2 RSA public key */
1132   memset(&alg_key, 0, sizeof(alg_key));
1133   if (silc_buffer_sformat(stack, &alg_key,
1134                           SILC_STR_UI_INT(e_len),
1135                           SILC_STR_DATA(e, e_len),
1136                           SILC_STR_UI_INT(n_len),
1137                           SILC_STR_DATA(n, n_len),
1138                           SILC_STR_END) < 0)
1139     goto err;
1140
1141   silc_free(n);
1142   silc_free(e);
1143
1144   ret = silc_buffer_steal(&alg_key, ret_len);
1145   return ret;
1146
1147  err:
1148   silc_free(n);
1149   silc_free(e);
1150   return NULL;
1151 }
1152
1153 /************************** SSH2 PKCS DSA Alg API ***************************/
1154
1155 /* The SSH2 DSA PKCS Algorithm API.  We implement here only the necessary
1156    parts of the API and the common code is used from DSS Algorithm API
1157    in silccrypt/dsa.c. */
1158
1159 /* Import DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1160
1161 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
1162 {
1163   SilcBufferStruct alg_key;
1164   DsaPublicKey *pubkey;
1165   unsigned char *p, *q, *g, *y;
1166   SilcUInt32 p_len, q_len, g_len, y_len;
1167   int ret;
1168
1169   SILC_LOG_DEBUG(("Import public key"));
1170
1171   if (!ret_public_key)
1172     return 0;
1173
1174   /* Allocate DSA public key */
1175   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1176   if (!pubkey)
1177     return 0;
1178
1179   /* Parse SSH2 DSA public key */
1180   silc_buffer_set(&alg_key, key, key_len);
1181   ret = silc_buffer_unformat(&alg_key,
1182                              SILC_STR_UI32_NSTRING(&p, &p_len),
1183                              SILC_STR_UI32_NSTRING(&q, &q_len),
1184                              SILC_STR_UI32_NSTRING(&g, &g_len),
1185                              SILC_STR_UI32_NSTRING(&y, &y_len),
1186                              SILC_STR_END);
1187   if (ret < 0)
1188     goto err;
1189   if (!p_len || !q_len || !g_len || !y_len)
1190     goto err;
1191
1192   /* Get MP integers */
1193   silc_mp_init(&pubkey->p);
1194   silc_mp_init(&pubkey->q);
1195   silc_mp_init(&pubkey->g);
1196   silc_mp_init(&pubkey->y);
1197   silc_mp_bin2mp(p, p_len, &pubkey->p);
1198   silc_mp_bin2mp(q, q_len, &pubkey->q);
1199   silc_mp_bin2mp(g, g_len, &pubkey->g);
1200   silc_mp_bin2mp(y, y_len, &pubkey->y);
1201
1202   /* Set key length */
1203   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
1204
1205   return ret;
1206
1207  err:
1208   silc_free(pubkey);
1209   return 0;
1210 }
1211
1212 /* Export DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1213
1214 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
1215 {
1216   DsaPublicKey *pubkey = public_key;
1217   SilcBufferStruct alg_key;
1218   unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
1219   SilcUInt32 p_len, q_len, g_len, y_len;
1220
1221   SILC_LOG_DEBUG(("Export public key"));
1222
1223   /* Encode MP integers */
1224   p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
1225   if (!p)
1226     goto err;
1227   q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
1228   if (!q)
1229     goto err;
1230   g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
1231   if (!g)
1232     goto err;
1233   y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
1234   if (!y)
1235     goto err;
1236
1237   /* Encode SSH2 DSA public key */
1238   memset(&alg_key, 0, sizeof(alg_key));
1239   if (silc_buffer_sformat(stack, &alg_key,
1240                           SILC_STR_UI_INT(p_len),
1241                           SILC_STR_DATA(p, p_len),
1242                           SILC_STR_UI_INT(q_len),
1243                           SILC_STR_DATA(q, q_len),
1244                           SILC_STR_UI_INT(g_len),
1245                           SILC_STR_DATA(g, g_len),
1246                           SILC_STR_UI_INT(y_len),
1247                           SILC_STR_DATA(y, y_len),
1248                           SILC_STR_END) < 0)
1249     goto err;
1250
1251   silc_free(p);
1252   silc_free(q);
1253   silc_free(g);
1254   silc_free(y);
1255
1256   ret = silc_buffer_steal(&alg_key, ret_len);
1257   return ret;
1258
1259  err:
1260   silc_free(p);
1261   silc_free(q);
1262   silc_free(g);
1263   silc_free(y);
1264   return NULL;
1265 }