]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - cmdgen.c
Add command-line passphrase-file options to command-line PuTTYgen.
[PuTTY.git] / cmdgen.c
1 /*
2  * cmdgen.c - command-line form of PuTTYgen
3  */
4
5 #define PUTTY_DO_GLOBALS
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <ctype.h>
10 #include <limits.h>
11 #include <assert.h>
12 #include <time.h>
13 #include <errno.h>
14 #include <string.h>
15
16 #include "putty.h"
17 #include "ssh.h"
18
19 #ifdef TEST_CMDGEN
20 /*
21  * This section overrides some definitions below for test purposes.
22  * When compiled with -DTEST_CMDGEN:
23  * 
24  *  - Calls to get_random_data() are replaced with the diagnostic
25  *    function below (I #define the name so that I can still link
26  *    with the original set of modules without symbol clash), in
27  *    order to avoid depleting the test system's /dev/random
28  *    unnecessarily.
29  * 
30  *  - Calls to console_get_userpass_input() are replaced with the
31  *    diagnostic function below, so that I can run tests in an
32  *    automated manner and provide their interactive passphrase
33  *    inputs.
34  * 
35  *  - main() is renamed to cmdgen_main(); at the bottom of the file
36  *    I define another main() which calls the former repeatedly to
37  *    run tests.
38  */
39 #define get_random_data get_random_data_diagnostic
40 char *get_random_data(int len)
41 {
42     char *buf = snewn(len, char);
43     memset(buf, 'x', len);
44     return buf;
45 }
46 #define console_get_userpass_input console_get_userpass_input_diagnostic
47 int nprompts, promptsgot;
48 const char *prompts[3];
49 int console_get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
50 {
51     size_t i;
52     int ret = 1;
53     for (i = 0; i < p->n_prompts; i++) {
54         if (promptsgot < nprompts) {
55             assert(strlen(prompts[promptsgot]) < p->prompts[i]->result_len);
56             strcpy(p->prompts[i]->result, prompts[promptsgot++]);
57         } else {
58             promptsgot++;           /* track number of requests anyway */
59             ret = 0;
60         }
61     }
62     return ret;
63 }
64 #define main cmdgen_main
65 #endif
66
67 struct progress {
68     int phase, current;
69 };
70
71 static void progress_update(void *param, int action, int phase, int iprogress)
72 {
73     struct progress *p = (struct progress *)param;
74     if (action != PROGFN_PROGRESS)
75         return;
76     if (phase > p->phase) {
77         if (p->phase >= 0)
78             fputc('\n', stderr);
79         p->phase = phase;
80         if (iprogress >= 0)
81             p->current = iprogress - 1;
82         else
83             p->current = iprogress;
84     }
85     while (p->current < iprogress) {
86         fputc('+', stdout);
87         p->current++;
88     }
89     fflush(stdout);
90 }
91
92 static void no_progress(void *param, int action, int phase, int iprogress)
93 {
94 }
95
96 void modalfatalbox(const char *p, ...)
97 {
98     va_list ap;
99     fprintf(stderr, "FATAL ERROR: ");
100     va_start(ap, p);
101     vfprintf(stderr, p, ap);
102     va_end(ap);
103     fputc('\n', stderr);
104     cleanup_exit(1);
105 }
106
107 void nonfatal(const char *p, ...)
108 {
109     va_list ap;
110     fprintf(stderr, "ERROR: ");
111     va_start(ap, p);
112     vfprintf(stderr, p, ap);
113     va_end(ap);
114     fputc('\n', stderr);
115 }
116
117 /*
118  * Stubs to let everything else link sensibly.
119  */
120 void log_eventlog(void *handle, const char *event)
121 {
122 }
123 char *x_get_default(const char *key)
124 {
125     return NULL;
126 }
127 void sk_cleanup(void)
128 {
129 }
130
131 void showversion(void)
132 {
133     printf("puttygen: %s\n", ver);
134 }
135
136 void usage(int standalone)
137 {
138     fprintf(stderr,
139             "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n"
140             "                [ -C comment ] [ -P ] [ -q ]\n"
141             "                [ -o output-keyfile ] [ -O type | -l | -L"
142             " | -p ]\n");
143     if (standalone)
144         fprintf(stderr,
145                 "Use \"puttygen --help\" for more detail.\n");
146 }
147
148 void help(void)
149 {
150     /*
151      * Help message is an extended version of the usage message. So
152      * start with that, plus a version heading.
153      */
154     showversion();
155     usage(FALSE);
156     fprintf(stderr,
157             "  -t    specify key type when generating (ed25519, ecdsa, rsa, "
158                                                         "dsa, rsa1)\n"
159             "  -b    specify number of bits when generating key\n"
160             "  -C    change or specify key comment\n"
161             "  -P    change key passphrase\n"
162             "  -q    quiet: do not display progress bar\n"
163             "  -O    specify output type:\n"
164             "           private             output PuTTY private key format\n"
165             "           private-openssh     export OpenSSH private key\n"
166             "           private-openssh-new export OpenSSH private key "
167                                              "(force new file format)\n"
168             "           private-sshcom      export ssh.com private key\n"
169             "           public              RFC 4716 / ssh.com public key\n"
170             "           public-openssh      OpenSSH public key\n"
171             "           fingerprint         output the key fingerprint\n"
172             "  -o    specify output file\n"
173             "  -l    equivalent to `-O fingerprint'\n"
174             "  -L    equivalent to `-O public-openssh'\n"
175             "  -p    equivalent to `-O public'\n"
176             "  --old-passphrase file\n"
177             "        specify file containing old key passphrase\n"
178             "  --new-passphrase file\n"
179             "        specify file containing new key passphrase\n"
180             );
181 }
182
183 static int move(char *from, char *to)
184 {
185     int ret;
186
187     ret = rename(from, to);
188     if (ret) {
189         /*
190          * This OS may require us to remove the original file first.
191          */
192         remove(to);
193         ret = rename(from, to);
194     }
195     if (ret) {
196         perror("puttygen: cannot move new file on to old one");
197         return FALSE;
198     }
199     return TRUE;
200 }
201
202 static char *readpassphrase(const char *filename)
203 {
204     FILE *fp;
205     char *line;
206
207     fp = fopen(filename, "r");
208     if (!fp) {
209         fprintf(stderr, "puttygen: cannot open %s: %s\n",
210                 filename, strerror(errno));
211         return NULL;
212     }
213     line = fgetline(fp);
214     if (line)
215         line[strcspn(line, "\r\n")] = '\0';
216     else if (ferror(fp))
217         fprintf(stderr, "puttygen: error reading from %s: %s\n",
218                 filename, strerror(errno));
219     else        /* empty file */
220         line = dupstr("");
221     fclose(fp);
222     return line;
223 }
224
225 int main(int argc, char **argv)
226 {
227     char *infile = NULL;
228     Filename *infilename = NULL, *outfilename = NULL;
229     enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, ED25519 } keytype = NOKEYGEN;
230     char *outfile = NULL, *outfiletmp = NULL;
231     enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO,
232            OPENSSH_NEW, SSHCOM } outtype = PRIVATE;
233     int bits = -1;
234     char *comment = NULL, *origcomment = NULL;
235     int change_passphrase = FALSE;
236     int errs = FALSE, nogo = FALSE;
237     int intype = SSH_KEYTYPE_UNOPENABLE;
238     int sshver = 0;
239     struct ssh2_userkey *ssh2key = NULL;
240     struct RSAKey *ssh1key = NULL;
241     unsigned char *ssh2blob = NULL;
242     char *ssh2alg = NULL;
243     const struct ssh_signkey *ssh2algf = NULL;
244     int ssh2bloblen;
245     char *old_passphrase = NULL, *new_passphrase = NULL;
246     int load_encrypted;
247     progfn_t progressfn = is_interactive() ? progress_update : no_progress;
248
249     /* ------------------------------------------------------------------
250      * Parse the command line to figure out what we've been asked to do.
251      */
252
253     /*
254      * If run with no arguments at all, print the usage message and
255      * return success.
256      */
257     if (argc <= 1) {
258         usage(TRUE);
259         return 0;
260     }
261
262     /*
263      * Parse command line arguments.
264      */
265     while (--argc) {
266         char *p = *++argv;
267         if (*p == '-') {
268             /*
269              * An option.
270              */
271             while (p && *++p) {
272                 char c = *p;
273                 switch (c) {
274                   case '-':
275                     /*
276                      * Long option.
277                      */
278                     {
279                         char *opt, *val;
280                         opt = p++;     /* opt will have _one_ leading - */
281                         while (*p && *p != '=')
282                             p++;               /* find end of option */
283                         if (*p == '=') {
284                             *p++ = '\0';
285                             val = p;
286                         } else
287                             val = NULL;
288
289                         if (!strcmp(opt, "-help")) {
290                             if (val) {
291                                 errs = TRUE;
292                                 fprintf(stderr, "puttygen: option `-%s'"
293                                         " expects no argument\n", opt);
294                             } else {
295                                 help();
296                                 nogo = TRUE;
297                             }
298                         } else if (!strcmp(opt, "-version")) {
299                             if (val) {
300                                 errs = TRUE;
301                                 fprintf(stderr, "puttygen: option `-%s'"
302                                         " expects no argument\n", opt);
303                             } else {
304                                 showversion();
305                                 nogo = TRUE;
306                             }
307                         } else if (!strcmp(opt, "-pgpfp")) {
308                             if (val) {
309                                 errs = TRUE;
310                                 fprintf(stderr, "puttygen: option `-%s'"
311                                         " expects no argument\n", opt);
312                             } else {
313                                 /* support --pgpfp for consistency */
314                                 pgp_fingerprints();
315                                 nogo = TRUE;
316                             }
317                         } else if (!strcmp(opt, "-old-passphrase")) {
318                             if (!val && argc > 1)
319                                 --argc, val = *++argv;
320                             if (!val) {
321                                 errs = TRUE;
322                                 fprintf(stderr, "puttygen: option `-%s'"
323                                         " expects an argument\n", opt);
324                             } else {
325                                 old_passphrase = readpassphrase(val);
326                                 if (!old_passphrase)
327                                     errs = TRUE;
328                             }
329                         } else if (!strcmp(opt, "-new-passphrase")) {
330                             if (!val && argc > 1)
331                                 --argc, val = *++argv;
332                             if (!val) {
333                                 errs = TRUE;
334                                 fprintf(stderr, "puttygen: option `-%s'"
335                                         " expects an argument\n", opt);
336                             } else {
337                                 new_passphrase = readpassphrase(val);
338                                 if (!new_passphrase)
339                                     errs = TRUE;
340                             }
341                         } else {
342                             errs = TRUE;
343                             fprintf(stderr,
344                                     "puttygen: no such option `-%s'\n", opt);
345                         }
346                     }
347                     p = NULL;
348                     break;
349                   case 'h':
350                   case 'V':
351                   case 'P':
352                   case 'l':
353                   case 'L':
354                   case 'p':
355                   case 'q':
356                     /*
357                      * Option requiring no parameter.
358                      */
359                     switch (c) {
360                       case 'h':
361                         help();
362                         nogo = TRUE;
363                         break;
364                       case 'V':
365                         showversion();
366                         nogo = TRUE;
367                         break;
368                       case 'P':
369                         change_passphrase = TRUE;
370                         break;
371                       case 'l':
372                         outtype = FP;
373                         break;
374                       case 'L':
375                         outtype = PUBLICO;
376                         break;
377                       case 'p':
378                         outtype = PUBLIC;
379                         break;
380                       case 'q':
381                         progressfn = no_progress;
382                         break;
383                     }
384                     break;
385                   case 't':
386                   case 'b':
387                   case 'C':
388                   case 'O':
389                   case 'o':
390                     /*
391                      * Option requiring parameter.
392                      */
393                     p++;
394                     if (!*p && argc > 1)
395                         --argc, p = *++argv;
396                     else if (!*p) {
397                         fprintf(stderr, "puttygen: option `-%c' expects a"
398                                 " parameter\n", c);
399                         errs = TRUE;
400                     }
401                     /*
402                      * Now c is the option and p is the parameter.
403                      */
404                     switch (c) {
405                       case 't':
406                         if (!strcmp(p, "rsa") || !strcmp(p, "rsa2"))
407                             keytype = RSA2, sshver = 2;
408                         else if (!strcmp(p, "rsa1"))
409                             keytype = RSA1, sshver = 1;
410                         else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
411                             keytype = DSA, sshver = 2;
412                         else if (!strcmp(p, "ecdsa"))
413                             keytype = ECDSA, sshver = 2;
414                         else if (!strcmp(p, "ed25519"))
415                             keytype = ED25519, sshver = 2;
416                         else {
417                             fprintf(stderr,
418                                     "puttygen: unknown key type `%s'\n", p);
419                             errs = TRUE;
420                         }
421                         break;
422                       case 'b':
423                         bits = atoi(p);
424                         break;
425                       case 'C':
426                         comment = p;
427                         break;
428                       case 'O':
429                         if (!strcmp(p, "public"))
430                             outtype = PUBLIC;
431                         else if (!strcmp(p, "public-openssh"))
432                             outtype = PUBLICO;
433                         else if (!strcmp(p, "private"))
434                             outtype = PRIVATE;
435                         else if (!strcmp(p, "fingerprint"))
436                             outtype = FP;
437                         else if (!strcmp(p, "private-openssh"))
438                             outtype = OPENSSH_AUTO, sshver = 2;
439                         else if (!strcmp(p, "private-openssh-new"))
440                             outtype = OPENSSH_NEW, sshver = 2;
441                         else if (!strcmp(p, "private-sshcom"))
442                             outtype = SSHCOM, sshver = 2;
443                         else {
444                             fprintf(stderr,
445                                     "puttygen: unknown output type `%s'\n", p);
446                             errs = TRUE;
447                         }
448                         break;
449                       case 'o':
450                         outfile = p;
451                         break;
452                     }
453                     p = NULL;          /* prevent continued processing */
454                     break;
455                   default:
456                     /*
457                      * Unrecognised option.
458                      */
459                     errs = TRUE;
460                     fprintf(stderr, "puttygen: no such option `-%c'\n", c);
461                     break;
462                 }
463             }
464         } else {
465             /*
466              * A non-option argument.
467              */
468             if (!infile)
469                 infile = p;
470             else {
471                 errs = TRUE;
472                 fprintf(stderr, "puttygen: cannot handle more than one"
473                         " input file\n");
474             }
475         }
476     }
477
478     if (bits == -1) {
479         /*
480          * No explicit key size was specified. Default varies
481          * depending on key type.
482          */
483         switch (keytype) {
484           case ECDSA:
485             bits = 384;
486             break;
487           case ED25519:
488             bits = 256;
489             break;
490           default:
491             bits = 2048;
492             break;
493         }
494     }
495
496     if (keytype == ECDSA && (bits != 256 && bits != 384 && bits != 521)) {
497         fprintf(stderr, "puttygen: invalid bits for ECDSA, choose 256, 384 or 521\n");
498         errs = TRUE;
499     }
500
501     if (keytype == ED25519 && (bits != 256)) {
502         fprintf(stderr, "puttygen: invalid bits for ED25519, choose 256\n");
503         errs = TRUE;
504     }
505
506     if (errs)
507         return 1;
508
509     if (nogo)
510         return 0;
511
512     /*
513      * If run with at least one argument _but_ not the required
514      * ones, print the usage message and return failure.
515      */
516     if (!infile && keytype == NOKEYGEN) {
517         usage(TRUE);
518         return 1;
519     }
520
521     /* ------------------------------------------------------------------
522      * Figure out further details of exactly what we're going to do.
523      */
524
525     /*
526      * Bomb out if we've been asked to both load and generate a
527      * key.
528      */
529     if (keytype != NOKEYGEN && infile) {
530         fprintf(stderr, "puttygen: cannot both load and generate a key\n");
531         return 1;
532     }
533
534     /* 
535      * We must save the private part when generating a new key.
536      */
537     if (keytype != NOKEYGEN &&
538         (outtype != PRIVATE && outtype != OPENSSH_AUTO &&
539          outtype != OPENSSH_NEW && outtype != SSHCOM)) {
540         fprintf(stderr, "puttygen: this would generate a new key but "
541                 "discard the private part\n");
542         return 1;
543     }
544
545     /*
546      * Analyse the type of the input file, in case this affects our
547      * course of action.
548      */
549     if (infile) {
550         infilename = filename_from_str(infile);
551
552         intype = key_type(infilename);
553
554         switch (intype) {
555           case SSH_KEYTYPE_UNOPENABLE:
556           case SSH_KEYTYPE_UNKNOWN:
557             fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
558                     infile, key_type_to_str(intype));
559             return 1;
560
561           case SSH_KEYTYPE_SSH1:
562           case SSH_KEYTYPE_SSH1_PUBLIC:
563             if (sshver == 2) {
564                 fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys"
565                         " not supported\n");
566                 return 1;
567             }
568             sshver = 1;
569             break;
570
571           case SSH_KEYTYPE_SSH2:
572           case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
573           case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
574           case SSH_KEYTYPE_OPENSSH_PEM:
575           case SSH_KEYTYPE_OPENSSH_NEW:
576           case SSH_KEYTYPE_SSHCOM:
577             if (sshver == 1) {
578                 fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys"
579                         " not supported\n");
580                 return 1;
581             }
582             sshver = 2;
583             break;
584
585           case SSH_KEYTYPE_OPENSSH_AUTO:
586           default:
587             assert(0 && "Should never see these types on an input file");
588         }
589     }
590
591     /*
592      * Determine the default output file, if none is provided.
593      * 
594      * This will usually be equal to stdout, except that if the
595      * input and output file formats are the same then the default
596      * output is to overwrite the input.
597      * 
598      * Also in this code, we bomb out if the input and output file
599      * formats are the same and no other action is performed.
600      */
601     if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||
602         (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||
603         (intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) ||
604         (intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) ||
605         (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
606         if (!outfile) {
607             outfile = infile;
608             outfiletmp = dupcat(outfile, ".tmp", NULL);
609         }
610
611         if (!change_passphrase && !comment) {
612             fprintf(stderr, "puttygen: this command would perform no useful"
613                     " action\n");
614             return 1;
615         }
616     } else {
617         if (!outfile) {
618             /*
619              * Bomb out rather than automatically choosing to write
620              * a private key file to stdout.
621              */
622             if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
623                 outtype == OPENSSH_NEW || outtype == SSHCOM) {
624                 fprintf(stderr, "puttygen: need to specify an output file\n");
625                 return 1;
626             }
627         }
628     }
629
630     /*
631      * Figure out whether we need to load the encrypted part of the
632      * key. This will be the case if either (a) we need to write
633      * out a private key format, or (b) the entire input key file
634      * is encrypted.
635      */
636     if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
637         outtype == OPENSSH_NEW || outtype == SSHCOM ||
638         intype == SSH_KEYTYPE_OPENSSH_PEM ||
639         intype == SSH_KEYTYPE_OPENSSH_NEW ||
640         intype == SSH_KEYTYPE_SSHCOM)
641         load_encrypted = TRUE;
642     else
643         load_encrypted = FALSE;
644
645     if (load_encrypted && (intype == SSH_KEYTYPE_SSH1_PUBLIC ||
646                            intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
647                            intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)) {
648         fprintf(stderr, "puttygen: cannot perform this action on a "
649                 "public-key-only input file\n");
650         return 1;
651     }
652
653     /* ------------------------------------------------------------------
654      * Now we're ready to actually do some stuff.
655      */
656
657     /*
658      * Either load or generate a key.
659      */
660     if (keytype != NOKEYGEN) {
661         char *entropy;
662         char default_comment[80];
663         struct tm tm;
664         struct progress prog;
665
666         prog.phase = -1;
667         prog.current = -1;
668
669         tm = ltime();
670         if (keytype == DSA)
671             strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
672         else if (keytype == ECDSA)
673             strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
674         else if (keytype == ED25519)
675             strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm);
676         else
677             strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
678
679         random_ref();
680         entropy = get_random_data(bits / 8);
681         if (!entropy) {
682             fprintf(stderr, "puttygen: failed to collect entropy, "
683                     "could not generate key\n");
684             return 1;
685         }
686         random_add_heavynoise(entropy, bits / 8);
687         smemclr(entropy, bits/8);
688         sfree(entropy);
689
690         if (keytype == DSA) {
691             struct dss_key *dsskey = snew(struct dss_key);
692             dsa_generate(dsskey, bits, progressfn, &prog);
693             ssh2key = snew(struct ssh2_userkey);
694             ssh2key->data = dsskey;
695             ssh2key->alg = &ssh_dss;
696             ssh1key = NULL;
697         } else if (keytype == ECDSA) {
698             struct ec_key *ec = snew(struct ec_key);
699             ec_generate(ec, bits, progressfn, &prog);
700             ssh2key = snew(struct ssh2_userkey);
701             ssh2key->data = ec;
702             ssh2key->alg = ec->signalg;
703             ssh1key = NULL;
704         } else if (keytype == ED25519) {
705             struct ec_key *ec = snew(struct ec_key);
706             ec_edgenerate(ec, bits, progressfn, &prog);
707             ssh2key = snew(struct ssh2_userkey);
708             ssh2key->data = ec;
709             ssh2key->alg = &ssh_ecdsa_ed25519;
710             ssh1key = NULL;
711         } else {
712             struct RSAKey *rsakey = snew(struct RSAKey);
713             rsa_generate(rsakey, bits, progressfn, &prog);
714             rsakey->comment = NULL;
715             if (keytype == RSA1) {
716                 ssh1key = rsakey;
717             } else {
718                 ssh2key = snew(struct ssh2_userkey);
719                 ssh2key->data = rsakey;
720                 ssh2key->alg = &ssh_rsa;
721             }
722         }
723         progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1);
724
725         if (ssh2key)
726             ssh2key->comment = dupstr(default_comment);
727         if (ssh1key)
728             ssh1key->comment = dupstr(default_comment);
729
730     } else {
731         const char *error = NULL;
732         int encrypted;
733
734         assert(infile != NULL);
735
736         /*
737          * Find out whether the input key is encrypted.
738          */
739         if (intype == SSH_KEYTYPE_SSH1)
740             encrypted = rsakey_encrypted(infilename, &origcomment);
741         else if (intype == SSH_KEYTYPE_SSH2)
742             encrypted = ssh2_userkey_encrypted(infilename, &origcomment);
743         else
744             encrypted = import_encrypted(infilename, intype, &origcomment);
745
746         /*
747          * If so, ask for a passphrase.
748          */
749         if (encrypted && load_encrypted) {
750             if (!old_passphrase) {
751                 prompts_t *p = new_prompts(NULL);
752                 int ret;
753                 p->to_server = FALSE;
754                 p->name = dupstr("SSH key passphrase");
755                 add_prompt(p, dupstr("Enter passphrase to load key: "), FALSE);
756                 ret = console_get_userpass_input(p, NULL, 0);
757                 assert(ret >= 0);
758                 if (!ret) {
759                     free_prompts(p);
760                     perror("puttygen: unable to read passphrase");
761                     return 1;
762                 } else {
763                     old_passphrase = dupstr(p->prompts[0]->result);
764                     free_prompts(p);
765                 }
766             }
767         } else {
768             old_passphrase = NULL;
769         }
770
771         switch (intype) {
772             int ret;
773
774           case SSH_KEYTYPE_SSH1:
775           case SSH_KEYTYPE_SSH1_PUBLIC:
776             ssh1key = snew(struct RSAKey);
777             if (!load_encrypted) {
778                 void *vblob;
779                 unsigned char *blob;
780                 int n, l, bloblen;
781
782                 ret = rsakey_pubblob(infilename, &vblob, &bloblen,
783                                      &origcomment, &error);
784                 blob = (unsigned char *)vblob;
785
786                 n = 4;                 /* skip modulus bits */
787                 
788                 l = ssh1_read_bignum(blob + n, bloblen - n,
789                                      &ssh1key->exponent);
790                 if (l < 0) {
791                     error = "SSH-1 public key blob was too short";
792                 } else {
793                     n += l;
794                     l = ssh1_read_bignum(blob + n, bloblen - n,
795                                          &ssh1key->modulus);
796                     if (l < 0) {
797                         error = "SSH-1 public key blob was too short";
798                     } else
799                         n += l;
800                 }
801                 ssh1key->comment = dupstr(origcomment);
802                 ssh1key->private_exponent = NULL;
803                 ssh1key->p = NULL;
804                 ssh1key->q = NULL;
805                 ssh1key->iqmp = NULL;
806             } else {
807                 ret = loadrsakey(infilename, ssh1key, old_passphrase, &error);
808             }
809             if (ret > 0)
810                 error = NULL;
811             else if (!error)
812                 error = "unknown error";
813             break;
814
815           case SSH_KEYTYPE_SSH2:
816           case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
817           case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
818             if (!load_encrypted) {
819                 ssh2blob = ssh2_userkey_loadpub(infilename, &ssh2alg,
820                                                 &ssh2bloblen, &origcomment,
821                                                 &error);
822                 if (ssh2blob) {
823                     ssh2algf = find_pubkey_alg(ssh2alg);
824                     if (ssh2algf)
825                         bits = ssh2algf->pubkey_bits(ssh2algf,
826                                                      ssh2blob, ssh2bloblen);
827                     else
828                         bits = -1;
829                 }
830                 sfree(ssh2alg);
831             } else {
832                 ssh2key = ssh2_load_userkey(infilename, old_passphrase,
833                                             &error);
834             }
835             if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
836                 error = NULL;
837             else if (!error) {
838                 if (ssh2key == SSH2_WRONG_PASSPHRASE)
839                     error = "wrong passphrase";
840                 else
841                     error = "unknown error";
842             }
843             break;
844
845           case SSH_KEYTYPE_OPENSSH_PEM:
846           case SSH_KEYTYPE_OPENSSH_NEW:
847           case SSH_KEYTYPE_SSHCOM:
848             ssh2key = import_ssh2(infilename, intype, old_passphrase, &error);
849             if (ssh2key) {
850                 if (ssh2key != SSH2_WRONG_PASSPHRASE)
851                     error = NULL;
852                 else
853                     error = "wrong passphrase";
854             } else if (!error)
855                 error = "unknown error";
856             break;
857
858           default:
859             assert(0);
860         }
861
862         if (error) {
863             fprintf(stderr, "puttygen: error loading `%s': %s\n",
864                     infile, error);
865             return 1;
866         }
867     }
868
869     /*
870      * Change the comment if asked to.
871      */
872     if (comment) {
873         if (sshver == 1) {
874             assert(ssh1key);
875             sfree(ssh1key->comment);
876             ssh1key->comment = dupstr(comment);
877         } else {
878             assert(ssh2key);
879             sfree(ssh2key->comment);
880             ssh2key->comment = dupstr(comment);
881         }
882     }
883
884     /*
885      * Unless we're changing the passphrase, the old one (if any) is a
886      * reasonable default.
887      */
888     if (!change_passphrase && old_passphrase && !new_passphrase)
889         new_passphrase = dupstr(old_passphrase);
890
891     /*
892      * Prompt for a new passphrase if we have been asked to, or if
893      * we have just generated a key.
894      */
895     if (!new_passphrase && (change_passphrase || keytype != NOKEYGEN)) {
896         prompts_t *p = new_prompts(NULL);
897         int ret;
898
899         p->to_server = FALSE;
900         p->name = dupstr("New SSH key passphrase");
901         add_prompt(p, dupstr("Enter passphrase to save key: "), FALSE);
902         add_prompt(p, dupstr("Re-enter passphrase to verify: "), FALSE);
903         ret = console_get_userpass_input(p, NULL, 0);
904         assert(ret >= 0);
905         if (!ret) {
906             free_prompts(p);
907             perror("puttygen: unable to read new passphrase");
908             return 1;
909         } else {
910             if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) {
911                 free_prompts(p);
912                 fprintf(stderr, "puttygen: passphrases do not match\n");
913                 return 1;
914             }
915             new_passphrase = dupstr(p->prompts[0]->result);
916             free_prompts(p);
917         }
918     }
919     if (new_passphrase && !*new_passphrase) {
920         sfree(new_passphrase);
921         new_passphrase = NULL;
922     }
923
924     /*
925      * Write output.
926      * 
927      * (In the case where outfile and outfiletmp are both NULL,
928      * there is no semantic reason to initialise outfilename at
929      * all; but we have to write _something_ to it or some compiler
930      * will probably complain that it might be used uninitialised.)
931      */
932     if (outfiletmp)
933         outfilename = filename_from_str(outfiletmp);
934     else
935         outfilename = filename_from_str(outfile ? outfile : "");
936
937     switch (outtype) {
938         int ret, real_outtype;
939
940       case PRIVATE:
941         if (sshver == 1) {
942             assert(ssh1key);
943             ret = saversakey(outfilename, ssh1key, new_passphrase);
944             if (!ret) {
945                 fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
946                 return 1;
947             }
948         } else {
949             assert(ssh2key);
950             ret = ssh2_save_userkey(outfilename, ssh2key, new_passphrase);
951             if (!ret) {
952                 fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
953                 return 1;
954             }
955         }
956         if (outfiletmp) {
957             if (!move(outfiletmp, outfile))
958                 return 1;              /* rename failed */
959         }
960         break;
961
962       case PUBLIC:
963       case PUBLICO:
964         {
965             FILE *fp;
966
967             if (outfile)
968                 fp = f_open(outfilename, "w", FALSE);
969             else
970                 fp = stdout;
971
972             if (sshver == 1) {
973                 ssh1_write_pubkey(fp, ssh1key);
974             } else {
975                 if (!ssh2blob) {
976                     assert(ssh2key);
977                     ssh2blob = ssh2key->alg->public_blob(ssh2key->data,
978                                                          &ssh2bloblen);
979                 }
980
981                 ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
982                                   ssh2blob, ssh2bloblen,
983                                   (outtype == PUBLIC ?
984                                    SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 :
985                                    SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH));
986             }
987
988             if (outfile)
989                 fclose(fp);
990         }
991         break;
992
993       case FP:
994         {
995             FILE *fp;
996             char *fingerprint;
997
998             if (sshver == 1) {
999                 assert(ssh1key);
1000                 fingerprint = snewn(128, char);
1001                 rsa_fingerprint(fingerprint, 128, ssh1key);
1002             } else {
1003                 if (ssh2key) {
1004                     fingerprint = ssh2_fingerprint(ssh2key->alg,
1005                                                    ssh2key->data);
1006                 } else {
1007                     assert(ssh2blob);
1008                     fingerprint = ssh2_fingerprint_blob(ssh2blob, ssh2bloblen);
1009                 }
1010             }
1011
1012             if (outfile)
1013                 fp = f_open(outfilename, "w", FALSE);
1014             else
1015                 fp = stdout;
1016             fprintf(fp, "%s\n", fingerprint);
1017             if (outfile)
1018                 fclose(fp);
1019
1020             sfree(fingerprint);
1021         }
1022         break;
1023         
1024       case OPENSSH_AUTO:
1025       case OPENSSH_NEW:
1026       case SSHCOM:
1027         assert(sshver == 2);
1028         assert(ssh2key);
1029         random_ref(); /* both foreign key types require randomness,
1030                        * for IV or padding */
1031         switch (outtype) {
1032           case OPENSSH_AUTO:
1033             real_outtype = SSH_KEYTYPE_OPENSSH_AUTO;
1034             break;
1035           case OPENSSH_NEW:
1036             real_outtype = SSH_KEYTYPE_OPENSSH_NEW;
1037             break;
1038           case SSHCOM:
1039             real_outtype = SSH_KEYTYPE_SSHCOM;
1040             break;
1041           default:
1042             assert(0 && "control flow goof");
1043         }
1044         ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
1045         if (!ret) {
1046             fprintf(stderr, "puttygen: unable to export key\n");
1047             return 1;
1048         }
1049         if (outfiletmp) {
1050             if (!move(outfiletmp, outfile))
1051                 return 1;              /* rename failed */
1052         }
1053         break;
1054     }
1055
1056     if (old_passphrase) {
1057         smemclr(old_passphrase, strlen(old_passphrase));
1058         sfree(old_passphrase);
1059     }
1060     if (new_passphrase) {
1061         smemclr(new_passphrase, strlen(new_passphrase));
1062         sfree(new_passphrase);
1063     }
1064
1065     if (ssh1key)
1066         freersakey(ssh1key);
1067     if (ssh2key) {
1068         ssh2key->alg->freekey(ssh2key->data);
1069         sfree(ssh2key);
1070     }
1071
1072     return 0;
1073 }
1074
1075 #ifdef TEST_CMDGEN
1076
1077 #undef main
1078
1079 #include <stdarg.h>
1080
1081 int passes, fails;
1082
1083 void setup_passphrases(char *first, ...)
1084 {
1085     va_list ap;
1086     char *next;
1087
1088     nprompts = 0;
1089     if (first) {
1090         prompts[nprompts++] = first;
1091         va_start(ap, first);
1092         while ((next = va_arg(ap, char *)) != NULL) {
1093             assert(nprompts < lenof(prompts));
1094             prompts[nprompts++] = next;
1095         }
1096         va_end(ap);
1097     }
1098 }
1099
1100 void test(int retval, ...)
1101 {
1102     va_list ap;
1103     int i, argc, ret;
1104     char **argv;
1105
1106     argc = 0;
1107     va_start(ap, retval);
1108     while (va_arg(ap, char *) != NULL)
1109         argc++;
1110     va_end(ap);
1111
1112     argv = snewn(argc+1, char *);
1113     va_start(ap, retval);
1114     for (i = 0; i <= argc; i++)
1115         argv[i] = va_arg(ap, char *);
1116     va_end(ap);
1117
1118     promptsgot = 0;
1119     ret = cmdgen_main(argc, argv);
1120
1121     if (ret != retval) {
1122         printf("FAILED retval (exp %d got %d):", retval, ret);
1123         for (i = 0; i < argc; i++)
1124             printf(" %s", argv[i]);
1125         printf("\n");
1126         fails++;
1127     } else if (promptsgot != nprompts) {
1128         printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot);
1129         for (i = 0; i < argc; i++)
1130             printf(" %s", argv[i]);
1131         printf("\n");
1132         fails++;
1133     } else {
1134         passes++;
1135     }
1136 }
1137
1138 void filecmp(char *file1, char *file2, char *fmt, ...)
1139 {
1140     /*
1141      * Ideally I should do file comparison myself, to maximise the
1142      * portability of this test suite once this application begins
1143      * running on non-Unix platforms. For the moment, though,
1144      * calling Unix diff is perfectly adequate.
1145      */
1146     char *buf;
1147     int ret;
1148
1149     buf = dupprintf("diff -q '%s' '%s'", file1, file2);
1150     ret = system(buf);
1151     sfree(buf);
1152
1153     if (ret) {
1154         va_list ap;
1155
1156         printf("FAILED diff (ret=%d): ", ret);
1157
1158         va_start(ap, fmt);
1159         vprintf(fmt, ap);
1160         va_end(ap);
1161
1162         printf("\n");
1163
1164         fails++;
1165     } else
1166         passes++;
1167 }
1168
1169 char *cleanup_fp(char *s)
1170 {
1171     char *p;
1172
1173     if (!strncmp(s, "ssh-", 4)) {
1174         s += strcspn(s, " \n\t");
1175         s += strspn(s, " \n\t");
1176     }
1177
1178     p = s;
1179     s += strcspn(s, " \n\t");
1180     s += strspn(s, " \n\t");
1181     s += strcspn(s, " \n\t");
1182
1183     return dupprintf("%.*s", s - p, p);
1184 }
1185
1186 char *get_fp(char *filename)
1187 {
1188     FILE *fp;
1189     char buf[256], *ret;
1190
1191     fp = fopen(filename, "r");
1192     if (!fp)
1193         return NULL;
1194     ret = fgets(buf, sizeof(buf), fp);
1195     fclose(fp);
1196     if (!ret)
1197         return NULL;
1198     return cleanup_fp(buf);
1199 }
1200
1201 void check_fp(char *filename, char *fp, char *fmt, ...)
1202 {
1203     char *newfp;
1204
1205     if (!fp)
1206         return;
1207
1208     newfp = get_fp(filename);
1209
1210     if (!strcmp(fp, newfp)) {
1211         passes++;
1212     } else {
1213         va_list ap;
1214
1215         printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp);
1216
1217         va_start(ap, fmt);
1218         vprintf(fmt, ap);
1219         va_end(ap);
1220
1221         printf("\n");
1222
1223         fails++;
1224     }
1225
1226     sfree(newfp);
1227 }
1228
1229 int main(int argc, char **argv)
1230 {
1231     int i;
1232     static char *const keytypes[] = { "rsa1", "dsa", "rsa" };
1233
1234     /*
1235      * Even when this thing is compiled for automatic test mode,
1236      * it's helpful to be able to invoke it with command-line
1237      * options for _manual_ tests.
1238      */
1239     if (argc > 1)
1240         return cmdgen_main(argc, argv);
1241
1242     passes = fails = 0;
1243
1244     for (i = 0; i < lenof(keytypes); i++) {
1245         char filename[128], osfilename[128], scfilename[128];
1246         char pubfilename[128], tmpfilename1[128], tmpfilename2[128];
1247         char *fp;
1248
1249         sprintf(filename, "test-%s.ppk", keytypes[i]);
1250         sprintf(pubfilename, "test-%s.pub", keytypes[i]);
1251         sprintf(osfilename, "test-%s.os", keytypes[i]);
1252         sprintf(scfilename, "test-%s.sc", keytypes[i]);
1253         sprintf(tmpfilename1, "test-%s.tmp1", keytypes[i]);
1254         sprintf(tmpfilename2, "test-%s.tmp2", keytypes[i]);
1255
1256         /*
1257          * Create an encrypted key.
1258          */
1259         setup_passphrases("sponge", "sponge", NULL);
1260         test(0, "puttygen", "-t", keytypes[i], "-o", filename, NULL);
1261
1262         /*
1263          * List the public key in OpenSSH format.
1264          */
1265         setup_passphrases(NULL);
1266         test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL);
1267         {
1268             char cmdbuf[256];
1269             fp = NULL;
1270             sprintf(cmdbuf, "ssh-keygen -l -f '%s' > '%s'",
1271                     pubfilename, tmpfilename1);
1272             if (system(cmdbuf) ||
1273                 (fp = get_fp(tmpfilename1)) == NULL) {
1274                 printf("UNABLE to test fingerprint matching against OpenSSH");
1275             }
1276         }
1277
1278         /*
1279          * List the public key in IETF/ssh.com format.
1280          */
1281         setup_passphrases(NULL);
1282         test(0, "puttygen", "-p", filename, NULL);
1283
1284         /*
1285          * List the fingerprint of the key.
1286          */
1287         setup_passphrases(NULL);
1288         test(0, "puttygen", "-l", filename, "-o", tmpfilename1, NULL);
1289         if (!fp) {
1290             /*
1291              * If we can't test fingerprints against OpenSSH, we
1292              * can at the very least test equality of all the
1293              * fingerprints we generate of this key throughout
1294              * testing.
1295              */
1296             fp = get_fp(tmpfilename1);
1297         } else {
1298             check_fp(tmpfilename1, fp, "%s initial fp", keytypes[i]);
1299         }
1300
1301         /*
1302          * Change the comment of the key; this _does_ require a
1303          * passphrase owing to the tamperproofing.
1304          * 
1305          * NOTE: In SSH-1, this only requires a passphrase because
1306          * of inadequacies of the loading and saving mechanisms. In
1307          * _principle_, it should be perfectly possible to modify
1308          * the comment on an SSH-1 key without requiring a
1309          * passphrase; the only reason I can't do it is because my
1310          * loading and saving mechanisms don't include a method of
1311          * loading all the key data without also trying to decrypt
1312          * the private section.
1313          * 
1314          * I don't consider this to be a problem worth solving,
1315          * because (a) to fix it would probably end up bloating
1316          * PuTTY proper, and (b) SSH-1 is on the way out anyway so
1317          * it shouldn't be highly significant. If it seriously
1318          * bothers anyone then perhaps I _might_ be persuadable.
1319          */
1320         setup_passphrases("sponge", NULL);
1321         test(0, "puttygen", "-C", "new-comment", filename, NULL);
1322
1323         /*
1324          * Change the passphrase to nothing.
1325          */
1326         setup_passphrases("sponge", "", "", NULL);
1327         test(0, "puttygen", "-P", filename, NULL);
1328
1329         /*
1330          * Change the comment of the key again; this time we expect no
1331          * passphrase to be required.
1332          */
1333         setup_passphrases(NULL);
1334         test(0, "puttygen", "-C", "new-comment-2", filename, NULL);
1335
1336         /*
1337          * Export the private key into OpenSSH format; no passphrase
1338          * should be required since the key is currently unencrypted.
1339          * For RSA1 keys, this should give an error.
1340          */
1341         setup_passphrases(NULL);
1342         test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,
1343              filename, NULL);
1344
1345         if (i) {
1346             /*
1347              * List the fingerprint of the OpenSSH-formatted key.
1348              */
1349             setup_passphrases(NULL);
1350             test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
1351             check_fp(tmpfilename1, fp, "%s openssh clear fp", keytypes[i]);
1352
1353             /*
1354              * List the public half of the OpenSSH-formatted key in
1355              * OpenSSH format.
1356              */
1357             setup_passphrases(NULL);
1358             test(0, "puttygen", "-L", osfilename, NULL);
1359
1360             /*
1361              * List the public half of the OpenSSH-formatted key in
1362              * IETF/ssh.com format.
1363              */
1364             setup_passphrases(NULL);
1365             test(0, "puttygen", "-p", osfilename, NULL);
1366         }
1367
1368         /*
1369          * Export the private key into ssh.com format; no passphrase
1370          * should be required since the key is currently unencrypted.
1371          * For RSA1 keys, this should give an error.
1372          */
1373         setup_passphrases(NULL);
1374         test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,
1375              filename, NULL);
1376
1377         if (i) {
1378             /*
1379              * List the fingerprint of the ssh.com-formatted key.
1380              */
1381             setup_passphrases(NULL);
1382             test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
1383             check_fp(tmpfilename1, fp, "%s ssh.com clear fp", keytypes[i]);
1384
1385             /*
1386              * List the public half of the ssh.com-formatted key in
1387              * OpenSSH format.
1388              */
1389             setup_passphrases(NULL);
1390             test(0, "puttygen", "-L", scfilename, NULL);
1391
1392             /*
1393              * List the public half of the ssh.com-formatted key in
1394              * IETF/ssh.com format.
1395              */
1396             setup_passphrases(NULL);
1397             test(0, "puttygen", "-p", scfilename, NULL);
1398         }
1399
1400         if (i) {
1401             /*
1402              * Convert from OpenSSH into ssh.com.
1403              */
1404             setup_passphrases(NULL);
1405             test(0, "puttygen", osfilename, "-o", tmpfilename1,
1406                  "-O", "private-sshcom", NULL);
1407
1408             /*
1409              * Convert from ssh.com back into a PuTTY key,
1410              * supplying the same comment as we had before we
1411              * started to ensure the comparison works.
1412              */
1413             setup_passphrases(NULL);
1414             test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
1415                  "-o", tmpfilename2, NULL);
1416
1417             /*
1418              * See if the PuTTY key thus generated is the same as
1419              * the original.
1420              */
1421             filecmp(filename, tmpfilename2,
1422                     "p->o->s->p clear %s", keytypes[i]);
1423
1424             /*
1425              * Convert from ssh.com to OpenSSH.
1426              */
1427             setup_passphrases(NULL);
1428             test(0, "puttygen", scfilename, "-o", tmpfilename1,
1429                  "-O", "private-openssh", NULL);
1430
1431             /*
1432              * Convert from OpenSSH back into a PuTTY key,
1433              * supplying the same comment as we had before we
1434              * started to ensure the comparison works.
1435              */
1436             setup_passphrases(NULL);
1437             test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
1438                  "-o", tmpfilename2, NULL);
1439
1440             /*
1441              * See if the PuTTY key thus generated is the same as
1442              * the original.
1443              */
1444             filecmp(filename, tmpfilename2,
1445                     "p->s->o->p clear %s", keytypes[i]);
1446
1447             /*
1448              * Finally, do a round-trip conversion between PuTTY
1449              * and ssh.com without involving OpenSSH, to test that
1450              * the key comment is preserved in that case.
1451              */
1452             setup_passphrases(NULL);
1453             test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
1454                  filename, NULL);
1455             setup_passphrases(NULL);
1456             test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
1457             filecmp(filename, tmpfilename2,
1458                     "p->s->p clear %s", keytypes[i]);
1459         }
1460
1461         /*
1462          * Check that mismatched passphrases cause an error.
1463          */
1464         setup_passphrases("sponge2", "sponge3", NULL);
1465         test(1, "puttygen", "-P", filename, NULL);
1466
1467         /*
1468          * Put a passphrase back on.
1469          */
1470         setup_passphrases("sponge2", "sponge2", NULL);
1471         test(0, "puttygen", "-P", filename, NULL);
1472
1473         /*
1474          * Export the private key into OpenSSH format, this time
1475          * while encrypted. For RSA1 keys, this should give an
1476          * error.
1477          */
1478         if (i == 0)
1479             setup_passphrases(NULL);   /* error, hence no passphrase read */
1480         else
1481             setup_passphrases("sponge2", NULL);
1482         test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,
1483              filename, NULL);
1484
1485         if (i) {
1486             /*
1487              * List the fingerprint of the OpenSSH-formatted key.
1488              */
1489             setup_passphrases("sponge2", NULL);
1490             test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
1491             check_fp(tmpfilename1, fp, "%s openssh encrypted fp", keytypes[i]);
1492
1493             /*
1494              * List the public half of the OpenSSH-formatted key in
1495              * OpenSSH format.
1496              */
1497             setup_passphrases("sponge2", NULL);
1498             test(0, "puttygen", "-L", osfilename, NULL);
1499
1500             /*
1501              * List the public half of the OpenSSH-formatted key in
1502              * IETF/ssh.com format.
1503              */
1504             setup_passphrases("sponge2", NULL);
1505             test(0, "puttygen", "-p", osfilename, NULL);
1506         }
1507
1508         /*
1509          * Export the private key into ssh.com format, this time
1510          * while encrypted. For RSA1 keys, this should give an
1511          * error.
1512          */
1513         if (i == 0)
1514             setup_passphrases(NULL);   /* error, hence no passphrase read */
1515         else
1516             setup_passphrases("sponge2", NULL);
1517         test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,
1518              filename, NULL);
1519
1520         if (i) {
1521             /*
1522              * List the fingerprint of the ssh.com-formatted key.
1523              */
1524             setup_passphrases("sponge2", NULL);
1525             test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
1526             check_fp(tmpfilename1, fp, "%s ssh.com encrypted fp", keytypes[i]);
1527
1528             /*
1529              * List the public half of the ssh.com-formatted key in
1530              * OpenSSH format.
1531              */
1532             setup_passphrases("sponge2", NULL);
1533             test(0, "puttygen", "-L", scfilename, NULL);
1534
1535             /*
1536              * List the public half of the ssh.com-formatted key in
1537              * IETF/ssh.com format.
1538              */
1539             setup_passphrases("sponge2", NULL);
1540             test(0, "puttygen", "-p", scfilename, NULL);
1541         }
1542
1543         if (i) {
1544             /*
1545              * Convert from OpenSSH into ssh.com.
1546              */
1547             setup_passphrases("sponge2", NULL);
1548             test(0, "puttygen", osfilename, "-o", tmpfilename1,
1549                  "-O", "private-sshcom", NULL);
1550
1551             /*
1552              * Convert from ssh.com back into a PuTTY key,
1553              * supplying the same comment as we had before we
1554              * started to ensure the comparison works.
1555              */
1556             setup_passphrases("sponge2", NULL);
1557             test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
1558                  "-o", tmpfilename2, NULL);
1559
1560             /*
1561              * See if the PuTTY key thus generated is the same as
1562              * the original.
1563              */
1564             filecmp(filename, tmpfilename2,
1565                     "p->o->s->p encrypted %s", keytypes[i]);
1566
1567             /*
1568              * Convert from ssh.com to OpenSSH.
1569              */
1570             setup_passphrases("sponge2", NULL);
1571             test(0, "puttygen", scfilename, "-o", tmpfilename1,
1572                  "-O", "private-openssh", NULL);
1573
1574             /*
1575              * Convert from OpenSSH back into a PuTTY key,
1576              * supplying the same comment as we had before we
1577              * started to ensure the comparison works.
1578              */
1579             setup_passphrases("sponge2", NULL);
1580             test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
1581                  "-o", tmpfilename2, NULL);
1582
1583             /*
1584              * See if the PuTTY key thus generated is the same as
1585              * the original.
1586              */
1587             filecmp(filename, tmpfilename2,
1588                     "p->s->o->p encrypted %s", keytypes[i]);
1589
1590             /*
1591              * Finally, do a round-trip conversion between PuTTY
1592              * and ssh.com without involving OpenSSH, to test that
1593              * the key comment is preserved in that case.
1594              */
1595             setup_passphrases("sponge2", NULL);
1596             test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
1597                  filename, NULL);
1598             setup_passphrases("sponge2", NULL);
1599             test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
1600             filecmp(filename, tmpfilename2,
1601                     "p->s->p encrypted %s", keytypes[i]);
1602         }
1603
1604         /*
1605          * Load with the wrong passphrase.
1606          */
1607         setup_passphrases("sponge8", NULL);
1608         test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL);
1609
1610         /*
1611          * Load a totally bogus file.
1612          */
1613         setup_passphrases(NULL);
1614         test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL);
1615     }
1616     printf("%d passes, %d fails\n", passes, fails);
1617     return 0;
1618 }
1619
1620 #endif