OSDN Git Service

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