Fixed signature format (alg name).
[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 context */
847 typedef struct {
848   SilcSshPrivateKey privkey;
849   SilcPKCSSignCb sign_cb;
850   void *context;
851 } *SilcSshSign;
852
853 /* Sign callback.  This formats the signature into SSH2 protocool compliant
854    format. */
855
856 static void silc_pkcs_ssh_sign_cb(SilcBool success,
857                                   const unsigned char *signature,
858                                   SilcUInt32 signature_len,
859                                   void *context)
860 {
861   SilcSshSign sign = context;
862   SilcBufferStruct sig;
863   const char *name;
864
865   if (!strcmp(sign->privkey->pkcs->name, "rsa"))
866     name = "ssh-rsa";
867   else
868     name = "ssh-dss";
869
870   memset(&sig, 0, sizeof(sig));
871   if (silc_buffer_format(&sig,
872                          SILC_STR_UI_INT(strlen(name)),
873                          SILC_STR_UI32_STRING(name),
874                          SILC_STR_UI_INT(signature_len),
875                          SILC_STR_DATA(signature, signature_len),
876                          SILC_STR_END) < 0) {
877     silc_free(sign);
878     sign->sign_cb(FALSE, NULL, 0, sign->context);
879   }
880
881   /* Deliver result */
882   sign->sign_cb(TRUE, silc_buffer_data(&sig), silc_buffer_len(&sig),
883                 sign->context);
884
885   silc_buffer_purge(&sig);
886   silc_free(sign);
887 }
888
889 /* Sign */
890
891 SILC_PKCS_SIGN(silc_pkcs_ssh_sign)
892 {
893   SilcSshPrivateKey privkey = private_key;
894   SilcSshSign sign;
895
896   if (!privkey->pkcs->sign) {
897     sign_cb(FALSE, NULL, 0, context);
898     return NULL;
899   }
900
901   sign = silc_calloc(1, sizeof(*sign));
902   if (!sign) {
903     sign_cb(FALSE, NULL, 0, context);
904     return NULL;
905   }
906
907   sign->privkey = privkey;
908   sign->sign_cb = sign_cb;
909   sign->context = context;
910
911   /* Sign.  The callback will format it to SSH2 compliant format. */
912   return privkey->pkcs->sign(privkey->pkcs, privkey->private_key,
913                              src, src_len,
914                              compute_hash, hash, rng,
915                              silc_pkcs_ssh_sign_cb, sign);
916 }
917
918 /* Verify */
919
920 SILC_PKCS_VERIFY(silc_pkcs_ssh_verify)
921 {
922   SilcSshPublicKey pubkey = public_key;
923   SilcBufferStruct sig;
924
925   if (!pubkey->pkcs->verify) {
926     verify_cb(FALSE, context);
927     return NULL;
928   }
929
930   /* Decode the SSH2 protocol style signature encoding.  Ignore the
931      algorithm name.  We know the algorithm. */
932   silc_buffer_set(&sig, signature, signature_len);
933   if (silc_buffer_unformat(&sig,
934                            SILC_STR_UI32_NSTRING(NULL, NULL),
935                            SILC_STR_UI32_NSTRING(&signature, &signature_len),
936                            SILC_STR_END) < 0) {
937     verify_cb(FALSE, context);
938     return NULL;
939   }
940
941   /* Verify */
942   return pubkey->pkcs->verify(pubkey->pkcs, pubkey->public_key,
943                               signature, signature_len,
944                               data, data_len, hash, rng,
945                               verify_cb, context);
946 }
947
948 /************************** SSH2 PKCS RSA Alg API ***************************/
949
950 /* The SSH2 RSA PKCS Algorithm API.  We implement here only the necessary
951    parts of the API and the common code is used from PKCS#1 Algorithm API
952    in silccrypt/silcpkcs1.c.  Basically everything else is PKCS#1 except
953    the format of the public key. */
954
955 /* Import RSA public key.  Both RFC 4716 and OpenSSH have same format. */
956
957 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_rsa_import_public_key)
958 {
959   SilcBufferStruct alg_key;
960   RsaPublicKey *pubkey;
961   unsigned char *n, *e;
962   SilcUInt32 n_len, e_len;
963   int ret;
964
965   SILC_LOG_DEBUG(("Import public key"));
966
967   if (!ret_public_key)
968     return 0;
969
970   /* Allocate RSA public key */
971   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
972   if (!pubkey)
973     return 0;
974
975   /* Parse SSH2 RSA public key */
976   silc_buffer_set(&alg_key, key, key_len);
977   ret = silc_buffer_unformat(&alg_key,
978                              SILC_STR_UI32_NSTRING(&e, &e_len),
979                              SILC_STR_UI32_NSTRING(&n, &n_len),
980                              SILC_STR_END);
981   if (ret < 0)
982     goto err;
983   if (!n_len || !e_len)
984     goto err;
985
986   /* Get MP integers */
987   silc_mp_init(&pubkey->n);
988   silc_mp_init(&pubkey->e);
989   silc_mp_bin2mp(n, n_len, &pubkey->n);
990   silc_mp_bin2mp(e, e_len, &pubkey->e);
991
992   /* Set key length */
993   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->n, 2) + 7) / 8) * 8;
994
995   return ret;
996
997  err:
998   silc_free(pubkey);
999   return 0;
1000 }
1001
1002 /* Export RSA public key.  Both RFC 4716 and OpenSSH have same format. */
1003
1004 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_rsa_export_public_key)
1005 {
1006   RsaPublicKey *pubkey = public_key;
1007   SilcBufferStruct alg_key;
1008   unsigned char *n = NULL, *e = NULL, *ret;
1009   SilcUInt32 n_len, e_len;
1010
1011   SILC_LOG_DEBUG(("Export public key"));
1012
1013   /* Encode MP integers */
1014   n = silc_mp_mp2bin(&pubkey->n, 0, &n_len);
1015   if (!n)
1016     goto err;
1017   e = silc_mp_mp2bin(&pubkey->e, 0, &e_len);
1018   if (!e)
1019     goto err;
1020
1021   /* Encode SSH2 RSA public key */
1022   memset(&alg_key, 0, sizeof(alg_key));
1023   if (silc_buffer_sformat(stack, &alg_key,
1024                           SILC_STR_UI_INT(e_len),
1025                           SILC_STR_DATA(e, e_len),
1026                           SILC_STR_UI_INT(n_len),
1027                           SILC_STR_DATA(n, n_len),
1028                           SILC_STR_END) < 0)
1029     goto err;
1030
1031   silc_free(n);
1032   silc_free(e);
1033
1034   ret = silc_buffer_steal(&alg_key, ret_len);
1035   return ret;
1036
1037  err:
1038   silc_free(n);
1039   silc_free(e);
1040   return NULL;
1041 }
1042
1043 /************************** SSH2 PKCS DSA Alg API ***************************/
1044
1045 /* The SSH2 DSA PKCS Algorithm API.  We implement here only the necessary
1046    parts of the API and the common code is used from DSS Algorithm API
1047    in silccrypt/dsa.c. */
1048
1049 /* Import DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1050
1051 SILC_PKCS_ALG_IMPORT_PUBLIC_KEY(silc_ssh_dsa_import_public_key)
1052 {
1053   SilcBufferStruct alg_key;
1054   DsaPublicKey *pubkey;
1055   unsigned char *p, *q, *g, *y;
1056   SilcUInt32 p_len, q_len, g_len, y_len;
1057   int ret;
1058
1059   SILC_LOG_DEBUG(("Import public key"));
1060
1061   if (!ret_public_key)
1062     return 0;
1063
1064   /* Allocate DSA public key */
1065   *ret_public_key = pubkey = silc_calloc(1, sizeof(*pubkey));
1066   if (!pubkey)
1067     return 0;
1068
1069   /* Parse SSH2 DSA public key */
1070   silc_buffer_set(&alg_key, key, key_len);
1071   ret = silc_buffer_unformat(&alg_key,
1072                              SILC_STR_UI32_NSTRING(&p, &p_len),
1073                              SILC_STR_UI32_NSTRING(&q, &q_len),
1074                              SILC_STR_UI32_NSTRING(&g, &g_len),
1075                              SILC_STR_UI32_NSTRING(&y, &y_len),
1076                              SILC_STR_END);
1077   if (ret < 0)
1078     goto err;
1079   if (!p_len || !q_len || !g_len || !y_len)
1080     goto err;
1081
1082   /* Get MP integers */
1083   silc_mp_init(&pubkey->p);
1084   silc_mp_init(&pubkey->q);
1085   silc_mp_init(&pubkey->g);
1086   silc_mp_init(&pubkey->y);
1087   silc_mp_bin2mp(p, p_len, &pubkey->p);
1088   silc_mp_bin2mp(q, q_len, &pubkey->q);
1089   silc_mp_bin2mp(g, g_len, &pubkey->g);
1090   silc_mp_bin2mp(y, y_len, &pubkey->y);
1091
1092   /* Set key length */
1093   pubkey->bits = ((silc_mp_sizeinbase(&pubkey->p, 2) + 7) / 8) * 8;
1094
1095   return ret;
1096
1097  err:
1098   silc_free(pubkey);
1099   return 0;
1100 }
1101
1102 /* Export DSA public key.  Both RFC 4716 and OpenSSH have same format. */
1103
1104 SILC_PKCS_ALG_EXPORT_PUBLIC_KEY(silc_ssh_dsa_export_public_key)
1105 {
1106   DsaPublicKey *pubkey = public_key;
1107   SilcBufferStruct alg_key;
1108   unsigned char *p = NULL, *q = NULL, *g = NULL, *y = NULL, *ret;
1109   SilcUInt32 p_len, q_len, g_len, y_len;
1110
1111   SILC_LOG_DEBUG(("Export public key"));
1112
1113   /* Encode MP integers */
1114   p = silc_mp_mp2bin(&pubkey->p, 0, &p_len);
1115   if (!p)
1116     goto err;
1117   q = silc_mp_mp2bin(&pubkey->q, 0, &q_len);
1118   if (!q)
1119     goto err;
1120   g = silc_mp_mp2bin(&pubkey->g, 0, &g_len);
1121   if (!g)
1122     goto err;
1123   y = silc_mp_mp2bin(&pubkey->y, 0, &y_len);
1124   if (!y)
1125     goto err;
1126
1127   /* Encode SSH2 DSA public key */
1128   memset(&alg_key, 0, sizeof(alg_key));
1129   if (silc_buffer_sformat(stack, &alg_key,
1130                           SILC_STR_UI_INT(p_len),
1131                           SILC_STR_DATA(p, p_len),
1132                           SILC_STR_UI_INT(q_len),
1133                           SILC_STR_DATA(q, q_len),
1134                           SILC_STR_UI_INT(g_len),
1135                           SILC_STR_DATA(g, g_len),
1136                           SILC_STR_UI_INT(y_len),
1137                           SILC_STR_DATA(y, y_len),
1138                           SILC_STR_END) < 0)
1139     goto err;
1140
1141   silc_free(p);
1142   silc_free(q);
1143   silc_free(g);
1144   silc_free(y);
1145
1146   ret = silc_buffer_steal(&alg_key, ret_len);
1147   return ret;
1148
1149  err:
1150   silc_free(p);
1151   silc_free(q);
1152   silc_free(g);
1153   silc_free(y);
1154   return NULL;
1155 }