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