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