]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - cmdgen.c
Couple of bits missing from the command-line help.
[PuTTY.git] / cmdgen.c
1 /*
2  * cmdgen.c - command-line form of PuTTYgen
3  */
4
5 /*
6  * TODO:
7  * 
8  *  - Test thoroughly.
9  *     + a neat way to do this might be to have a -DTESTMODE for
10  *       this file, which #defines console_get_line and
11  *       get_random_noise to different names in order to be able to
12  *       link them to test stubs rather than the real ones. That
13  *       way I can have a test rig which checks whether passphrases
14  *       are being prompted for.
15  */
16
17 #define PUTTY_DO_GLOBALS
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <limits.h>
23 #include <assert.h>
24 #include <time.h>
25
26 #include "putty.h"
27 #include "ssh.h"
28
29 #ifdef TESTMODE
30 #define get_random_data get_random_data_diagnostic
31 char *get_random_data(int len)
32 {
33     char *buf = snewn(len, char);
34     memset(buf, 'x', len);
35     return buf;
36 }
37 #endif
38
39 struct progress {
40     int phase, current;
41 };
42
43 static void progress_update(void *param, int action, int phase, int iprogress)
44 {
45     struct progress *p = (struct progress *)param;
46     if (action != PROGFN_PROGRESS)
47         return;
48     if (phase > p->phase) {
49         if (p->phase >= 0)
50             fputc('\n', stderr);
51         p->phase = phase;
52         if (iprogress >= 0)
53             p->current = iprogress - 1;
54         else
55             p->current = iprogress;
56     }
57     while (p->current < iprogress) {
58         fputc('+', stdout);
59         p->current++;
60     }
61     fflush(stdout);
62 }
63
64 static void no_progress(void *param, int action, int phase, int iprogress)
65 {
66 }
67
68 void modalfatalbox(char *p, ...)
69 {
70     va_list ap;
71     fprintf(stderr, "FATAL ERROR: ");
72     va_start(ap, p);
73     vfprintf(stderr, p, ap);
74     va_end(ap);
75     fputc('\n', stderr);
76     cleanup_exit(1);
77 }
78
79 /*
80  * Stubs to let everything else link sensibly.
81  */
82 void log_eventlog(void *handle, const char *event)
83 {
84 }
85 char *x_get_default(const char *key)
86 {
87     return NULL;
88 }
89 void sk_cleanup(void)
90 {
91 }
92
93 void showversion(void)
94 {
95     char *verstr = dupstr(ver);
96     verstr[0] = tolower(verstr[0]);
97     printf("PuTTYgen %s\n", verstr);
98     sfree(verstr);
99 }
100
101 void usage(void)
102 {
103     fprintf(stderr,
104             "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n"
105             "                [ -C comment ] [ -P ]\n"
106             "                [ -o output-keyfile ] [ -O type | -l | -L"
107             " | -p ]\n");
108 }
109
110 void help(void)
111 {
112     /*
113      * Help message is an extended version of the usage message. So
114      * start with that, plus a version heading.
115      */
116     showversion();
117     usage();
118     fprintf(stderr,
119             "  -t    specify key type when generating (rsa, dsa, rsa1)\n"
120             "  -b    specify number of bits when generating key\n"
121             "  -C    change or specify key comment\n"
122             "  -P    change key passphrase\n"
123             "  -O    specify output type:\n"
124             "           private             output PuTTY private key format\n"
125             "           private-openssh     export OpenSSH private key\n"
126             "           private-sshcom      export ssh.com private key\n"
127             "           public              standard / ssh.com public key\n"
128             "           public-openssh      OpenSSH public key\n"
129             "           fingerprint         output the key fingerprint\n"
130             "  -o    specify output file\n"
131             "  -l    equivalent to `-O fingerprint'\n"
132             "  -L    equivalent to `-O public-openssh'\n"
133             "  -p    equivalent to `-O public'\n"
134             );
135 }
136
137 static int save_ssh2_pubkey(char *filename, char *comment,
138                             void *v_pub_blob, int pub_len)
139 {
140     unsigned char *pub_blob = (unsigned char *)v_pub_blob;
141     char *p;
142     int i, column;
143     FILE *fp;
144
145     if (filename) {
146         fp = fopen(filename, "wb");
147         if (!fp)
148             return 0;
149     } else
150         fp = stdout;
151
152     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
153
154     if (comment) {
155         fprintf(fp, "Comment: \"");
156         for (p = comment; *p; p++) {
157             if (*p == '\\' || *p == '\"')
158                 fputc('\\', fp);
159             fputc(*p, fp);
160         }
161         fprintf(fp, "\"\n");
162     }
163
164     i = 0;
165     column = 0;
166     while (i < pub_len) {
167         char buf[5];
168         int n = (pub_len - i < 3 ? pub_len - i : 3);
169         base64_encode_atom(pub_blob + i, n, buf);
170         i += n;
171         buf[4] = '\0';
172         fputs(buf, fp);
173         if (++column >= 16) {
174             fputc('\n', fp);
175             column = 0;
176         }
177     }
178     if (column > 0)
179         fputc('\n', fp);
180     
181     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
182     if (filename)
183         fclose(fp);
184     return 1;
185 }
186
187 static void move(char *from, char *to)
188 {
189     int ret;
190
191     ret = rename(from, to);
192     if (ret) {
193         /*
194          * This OS may require us to remove the original file first.
195          */
196         remove(to);
197         ret = rename(from, to);
198     }
199     if (ret) {
200         perror("puttygen: cannot move new file on to old one");
201         exit(1);
202     }
203 }
204
205 static char *blobfp(char *alg, int bits, char *blob, int bloblen)
206 {
207     char buffer[128];
208     unsigned char digest[16];
209     struct MD5Context md5c;
210     int i;
211
212     MD5Init(&md5c);
213     MD5Update(&md5c, blob, bloblen);
214     MD5Final(digest, &md5c);
215
216     sprintf(buffer, "%s ", alg);
217     if (bits > 0)
218         sprintf(buffer + strlen(buffer), "%d ", bits);
219     for (i = 0; i < 16; i++)
220         sprintf(buffer + strlen(buffer), "%s%02x", i ? ":" : "",
221                 digest[i]);
222
223     return dupstr(buffer);
224 }
225
226 int main(int argc, char **argv)
227 {
228     char *infile = NULL;
229     Filename infilename;
230     enum { NOKEYGEN, RSA1, RSA2, DSA } keytype = NOKEYGEN;    
231     char *outfile = NULL, *outfiletmp = NULL;
232     Filename outfilename;
233     enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH, SSHCOM } outtype = PRIVATE;
234     int bits = 1024;
235     char *comment = NULL, *origcomment = NULL;
236     int change_passphrase = FALSE;
237     int errs = FALSE, nogo = FALSE;
238     int intype = SSH_KEYTYPE_UNOPENABLE;
239     int sshver = 0;
240     struct ssh2_userkey *ssh2key = NULL;
241     struct RSAKey *ssh1key = NULL;
242     char *ssh2blob = NULL, *ssh2alg = NULL;
243     const struct ssh_signkey *ssh2algf = NULL;
244     int ssh2bloblen;
245     char *passphrase = NULL;
246     int load_encrypted;
247     progfn_t progressfn = is_interactive() ? progress_update : no_progress;
248
249     /* ------------------------------------------------------------------
250      * Parse the command line to figure out what we've been asked to do.
251      */
252
253     /*
254      * If run with no arguments at all, print the usage message and
255      * return success.
256      */
257     if (argc <= 1) {
258         usage();
259         return 0;
260     }
261
262     /*
263      * Parse command line arguments.
264      */
265     while (--argc) {
266         char *p = *++argv;
267         if (*p == '-') {
268             /*
269              * An option.
270              */
271             while (p && *++p) {
272                 char c = *p;
273                 switch (c) {
274                   case '-':
275                     /*
276                      * Long option.
277                      */
278                     {
279                         char *opt, *val;
280                         opt = p++;     /* opt will have _one_ leading - */
281                         while (*p && *p != '=')
282                             p++;               /* find end of option */
283                         if (*p == '=') {
284                             *p++ = '\0';
285                             val = p;
286                         } else
287                             val = NULL;
288                         if (!strcmp(opt, "-help")) {
289                             help();
290                             nogo = TRUE;
291                         } else if (!strcmp(opt, "-version")) {
292                             showversion();
293                             nogo = TRUE;
294                         }
295                         /*
296                          * A sample option requiring an argument:
297                          * 
298                          * else if (!strcmp(opt, "-output")) {
299                          *     if (!val)
300                          *         errs = TRUE, error(err_optnoarg, opt);
301                          *     else
302                          *         ofile = val;
303                          * }
304                          */
305                         else {
306                             errs = TRUE;
307                             fprintf(stderr,
308                                     "puttygen: no such option `--%s'\n", opt);
309                         }
310                     }
311                     p = NULL;
312                     break;
313                   case 'h':
314                   case 'V':
315                   case 'P':
316                   case 'l':
317                   case 'L':
318                   case 'p':
319                   case 'q':
320                     /*
321                      * Option requiring no parameter.
322                      */
323                     switch (c) {
324                       case 'h':
325                         help();
326                         nogo = TRUE;
327                         break;
328                       case 'V':
329                         showversion();
330                         nogo = TRUE;
331                         break;
332                       case 'P':
333                         change_passphrase = TRUE;
334                         break;
335                       case 'l':
336                         outtype = FP;
337                         break;
338                       case 'L':
339                         outtype = PUBLICO;
340                         break;
341                       case 'p':
342                         outtype = PUBLIC;
343                         break;
344                       case 'q':
345                         progressfn = no_progress;
346                         break;
347                     }
348                     break;
349                   case 't':
350                   case 'b':
351                   case 'C':
352                   case 'O':
353                   case 'o':
354                     /*
355                      * Option requiring parameter.
356                      */
357                     p++;
358                     if (!*p && argc > 1)
359                         --argc, p = *++argv;
360                     else if (!*p) {
361                         fprintf(stderr, "puttygen: option `-%c' expects a"
362                                 " parameter\n", c);
363                         errs = TRUE;
364                     }
365                     /*
366                      * Now c is the option and p is the parameter.
367                      */
368                     switch (c) {
369                       case 't':
370                         if (!strcmp(p, "rsa") || !strcmp(p, "rsa2"))
371                             keytype = RSA2, sshver = 2;
372                         else if (!strcmp(p, "rsa1"))
373                             keytype = RSA1, sshver = 1;
374                         else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
375                             keytype = DSA, sshver = 2;
376                         else {
377                             fprintf(stderr,
378                                     "puttygen: unknown key type `%s'\n", p);
379                             errs = TRUE;
380                         }
381                         break;
382                       case 'b':
383                         bits = atoi(p);
384                         break;
385                       case 'C':
386                         comment = p;
387                         break;
388                       case 'O':
389                         if (!strcmp(p, "public"))
390                             outtype = PUBLIC;
391                         else if (!strcmp(p, "public-openssh"))
392                             outtype = PUBLICO;
393                         else if (!strcmp(p, "private"))
394                             outtype = PRIVATE;
395                         else if (!strcmp(p, "fingerprint"))
396                             outtype = FP;
397                         else if (!strcmp(p, "private-openssh"))
398                             outtype = OPENSSH, sshver = 2;
399                         else if (!strcmp(p, "private-sshcom"))
400                             outtype = SSHCOM, sshver = 2;
401                         else {
402                             fprintf(stderr,
403                                     "puttygen: unknown output type `%s'\n", p);
404                             errs = TRUE;
405                         }
406                         break;
407                       case 'o':
408                         outfile = p;
409                         break;
410                     }
411                     p = NULL;          /* prevent continued processing */
412                     break;
413                   default:
414                     /*
415                      * Unrecognised option.
416                      */
417                     errs = TRUE;
418                     fprintf(stderr, "puttygen: no such option `-%c'\n", c);
419                     break;
420                 }
421             }
422         } else {
423             /*
424              * A non-option argument.
425              */
426             if (!infile)
427                 infile = p;
428             else {
429                 errs = TRUE;
430                 fprintf(stderr, "puttygen: cannot handle more than one"
431                         " input file\n");
432             }
433         }
434     }
435
436     if (errs)
437         return 1;
438
439     if (nogo)
440         return 0;
441
442     /*
443      * If run with at least one argument _but_ not the required
444      * ones, print the usage message and return failure.
445      */
446     if (!infile && keytype == NOKEYGEN) {
447         usage();
448         return 1;
449     }
450
451     /* ------------------------------------------------------------------
452      * Figure out further details of exactly what we're going to do.
453      */
454
455     /*
456      * Bomb out if we've been asked to both load and generate a
457      * key.
458      */
459     if (keytype != NOKEYGEN && intype) {
460         fprintf(stderr, "puttygen: cannot both load and generate a key\n");
461         return 1;
462     }
463
464     /*
465      * Analyse the type of the input file, in case this affects our
466      * course of action.
467      */
468     if (infile) {
469         infilename = filename_from_str(infile);
470
471         intype = key_type(&infilename);
472
473         switch (intype) {
474             /*
475              * It would be nice here to be able to load _public_
476              * key files, in any of a number of forms, and (a)
477              * convert them to other public key types, (b) print
478              * out their fingerprints. Or, I suppose, for real
479              * orthogonality, (c) change their comment!
480              * 
481              * In fact this opens some interesting possibilities.
482              * Suppose ssh2_userkey_loadpub() were able to load
483              * public key files as well as extracting the public
484              * key from private ones. And suppose I did the thing
485              * I've been wanting to do, where specifying a
486              * particular private key file for authentication
487              * causes any _other_ key in the agent to be discarded.
488              * Then, if you had an agent forwarded to the machine
489              * you were running Unix PuTTY or Plink on, and you
490              * needed to specify which of the keys in the agent it
491              * should use, you could do that by supplying a
492              * _public_ key file, thus not needing to trust even
493              * your encrypted private key file to the network. Ooh!
494              */
495
496           case SSH_KEYTYPE_UNOPENABLE:
497           case SSH_KEYTYPE_UNKNOWN:
498             fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
499                     infile, key_type_to_str(intype));
500             return 1;
501
502           case SSH_KEYTYPE_SSH1:
503             if (sshver == 2) {
504                 fprintf(stderr, "puttygen: conversion from SSH1 to SSH2 keys"
505                         " not supported\n");
506                 return 1;
507             }
508             sshver = 1;
509             break;
510
511           case SSH_KEYTYPE_SSH2:
512           case SSH_KEYTYPE_OPENSSH:
513           case SSH_KEYTYPE_SSHCOM:
514             if (sshver == 1) {
515                 fprintf(stderr, "puttygen: conversion from SSH2 to SSH1 keys"
516                         " not supported\n");
517                 return 1;
518             }
519             sshver = 2;
520             break;
521         }
522     }
523
524     /*
525      * Determine the default output file, if none is provided.
526      * 
527      * This will usually be equal to stdout, except that if the
528      * input and output file formats are the same then the default
529      * output is to overwrite the input.
530      * 
531      * Also in this code, we bomb out if the input and output file
532      * formats are the same and no other action is performed.
533      */
534     if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||
535         (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||
536         (intype == SSH_KEYTYPE_OPENSSH && outtype == OPENSSH) ||
537         (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
538         if (!outfile) {
539             outfile = infile;
540             outfiletmp = dupcat(outfile, ".tmp");
541         }
542
543         if (!change_passphrase && !comment) {
544             fprintf(stderr, "puttygen: this command would perform no useful"
545                     " action\n");
546             return 1;
547         }
548     } else {
549         if (!outfile) {
550             /*
551              * Bomb out rather than automatically choosing to write
552              * a private key file to stdout.
553              */
554             if (outtype==PRIVATE || outtype==OPENSSH || outtype==SSHCOM) {
555                 fprintf(stderr, "puttygen: need to specify an output file\n");
556                 return 1;
557             }
558         }
559     }
560
561     /*
562      * Figure out whether we need to load the encrypted part of the
563      * key. This will be the case if either (a) we need to write
564      * out a private key format, or (b) the entire input key file
565      * is encrypted.
566      */
567     if (outtype == PRIVATE || outtype == OPENSSH || outtype == SSHCOM ||
568         intype == SSH_KEYTYPE_OPENSSH || intype == SSH_KEYTYPE_SSHCOM)
569         load_encrypted = TRUE;
570     else
571         load_encrypted = FALSE;
572
573     /* ------------------------------------------------------------------
574      * Now we're ready to actually do some stuff.
575      */
576
577     /*
578      * Either load or generate a key.
579      */
580     if (keytype != NOKEYGEN) {
581         char *entropy;
582         char default_comment[80];
583         time_t t;
584         struct tm *tm;
585         struct progress prog;
586
587         prog.phase = -1;
588         prog.current = -1;
589
590         time(&t);
591         tm = localtime(&t);
592         if (keytype == DSA)
593             strftime(default_comment, 30, "dsa-key-%Y%m%d", tm);
594         else
595             strftime(default_comment, 30, "rsa-key-%Y%m%d", tm);
596
597         random_init();
598         entropy = get_random_data(bits / 8);
599         random_add_heavynoise(entropy, bits / 8);
600         memset(entropy, 0, bits/8);
601         sfree(entropy);
602
603         if (keytype == DSA) {
604             struct dss_key *dsskey = snew(struct dss_key);
605             dsa_generate(dsskey, bits, progressfn, &prog);
606             ssh2key = snew(struct ssh2_userkey);
607             ssh2key->data = dsskey;
608             ssh2key->alg = &ssh_dss;
609             ssh1key = NULL;
610         } else {
611             struct RSAKey *rsakey = snew(struct RSAKey);
612             rsa_generate(rsakey, bits, progressfn, &prog);
613             if (keytype == RSA1) {
614                 ssh1key = rsakey;
615             } else {
616                 ssh2key = snew(struct ssh2_userkey);
617                 ssh2key->data = rsakey;
618                 ssh2key->alg = &ssh_rsa;
619             }
620         }
621         progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1);
622
623         if (ssh2key)
624             ssh2key->comment = dupstr(default_comment);
625         if (ssh1key)
626             ssh1key->comment = dupstr(default_comment);
627
628     } else {
629         const char *error = NULL;
630         int encrypted;
631
632         assert(infile != NULL);
633
634         /*
635          * Find out whether the input key is encrypted.
636          */
637         if (intype == SSH_KEYTYPE_SSH1)
638             encrypted = rsakey_encrypted(&infilename, &origcomment);
639         else if (intype == SSH_KEYTYPE_SSH2)
640             encrypted = ssh2_userkey_encrypted(&infilename, &origcomment);
641         else
642             encrypted = import_encrypted(&infilename, intype, &origcomment);
643
644         /*
645          * If so, ask for a passphrase.
646          */
647         if (encrypted && load_encrypted) {
648             passphrase = snewn(512, char);
649             if (!console_get_line("Enter passphrase to load key: ",
650                                   passphrase, 512, TRUE)) {
651                 perror("puttygen: unable to read passphrase");
652                 return 1;
653             }
654         } else {
655             passphrase = NULL;
656         }
657
658         switch (intype) {
659             int ret;
660
661           case SSH_KEYTYPE_SSH1:
662             ssh1key = snew(struct RSAKey);
663             if (!load_encrypted) {
664                 void *vblob;
665                 char *blob;
666                 int n, bloblen;
667
668                 ret = rsakey_pubblob(&infilename, &vblob, &bloblen, &error);
669                 blob = (char *)vblob;
670
671                 n = 4;                 /* skip modulus bits */
672                 n += ssh1_read_bignum(blob + n, &ssh1key->exponent);
673                 n += ssh1_read_bignum(blob + n, &ssh1key->modulus);
674                 ssh1key->comment = NULL;
675             } else {
676                 ret = loadrsakey(&infilename, ssh1key, passphrase, &error);
677             }
678             if (ret)
679                 error = NULL;
680             else if (!error)
681                 error = "unknown error";
682             break;
683
684           case SSH_KEYTYPE_SSH2:
685             if (!load_encrypted) {
686                 ssh2blob = ssh2_userkey_loadpub(&infilename, &ssh2alg,
687                                                 &ssh2bloblen, &error);
688                 ssh2algf = find_pubkey_alg(ssh2alg);
689                 if (ssh2algf)
690                     bits = ssh2algf->pubkey_bits(ssh2blob, ssh2bloblen);
691                 else
692                     bits = -1;
693             } else {
694                 ssh2key = ssh2_load_userkey(&infilename, passphrase, &error);
695             }
696             if (ssh2key || ssh2blob)
697                 error = NULL;
698             else if (!error) {
699                 if (ssh2key == SSH2_WRONG_PASSPHRASE)
700                     error = "wrong passphrase";
701                 else
702                     error = "unknown error";
703             }
704             break;
705
706           case SSH_KEYTYPE_OPENSSH:
707           case SSH_KEYTYPE_SSHCOM:
708             ssh2key = import_ssh2(&infilename, intype, passphrase);
709             if (ssh2key)
710                 error = NULL;
711             else if (!error) {
712                 if (ssh2key == SSH2_WRONG_PASSPHRASE)
713                     error = "wrong passphrase";
714                 else
715                     error = "unknown error";
716             }
717             break;
718
719           default:
720             assert(0);
721         }
722
723         if (error) {
724             fprintf(stderr, "puttygen: error loading `%s': %s\n",
725                     infile, error);
726             return 1;
727         }
728     }
729
730     /*
731      * Change the comment if asked to.
732      */
733     if (comment) {
734         if (sshver == 1) {
735             assert(ssh1key);
736             sfree(ssh1key->comment);
737             ssh1key->comment = dupstr(comment);
738         } else {
739             assert(ssh2key);
740             sfree(ssh2key->comment);
741             ssh2key->comment = dupstr(comment);
742         }
743     }
744
745     /*
746      * Prompt for a new passphrase if we have been asked to, or if
747      * we have just generated a key.
748      */
749     if (change_passphrase || keytype != NOKEYGEN) {
750         char *passphrase2;
751
752         if (passphrase) {
753             memset(passphrase, 0, strlen(passphrase));
754             sfree(passphrase);
755         }
756
757         passphrase = snewn(512, char);
758         passphrase2 = snewn(512, char);
759         if (!console_get_line("Enter passphrase to save key: ",
760                               passphrase, 512, TRUE) ||
761             !console_get_line("Re-enter passphrase to verify: ",
762                               passphrase2, 512, TRUE)) {
763             perror("puttygen: unable to read new passphrase");
764             return 1;
765         }
766         if (strcmp(passphrase, passphrase2)) {
767             fprintf(stderr, "puttygen: passphrases do not match\n");
768             return 1;
769         }
770         memset(passphrase2, 0, strlen(passphrase2));
771         sfree(passphrase2);
772         if (!*passphrase) {
773             sfree(passphrase);
774             passphrase = NULL;
775         }
776     }
777
778     /*
779      * Write output.
780      * 
781      * (In the case where outfile and outfiletmp are both NULL,
782      * there is no semantic reason to initialise outfilename at
783      * all; but we have to write _something_ to it or some compiler
784      * will probably complain that it might be used uninitialised.)
785      */
786     if (outfiletmp)
787         outfilename = filename_from_str(outfiletmp);
788     else
789         outfilename = filename_from_str(outfile ? outfile : "");
790
791     switch (outtype) {
792         int ret;
793
794       case PRIVATE:
795         if (sshver == 1) {
796             assert(ssh1key);
797             ret = saversakey(&outfilename, ssh1key, passphrase);
798             if (!ret) {
799                 fprintf(stderr, "puttygen: unable to save SSH1 private key\n");
800                 return 1;
801             }
802         } else {
803             assert(ssh2key);
804             ret = ssh2_save_userkey(&outfilename, ssh2key, passphrase);
805             if (!ret) {
806                 fprintf(stderr, "puttygen: unable to save SSH2 private key\n");
807                 return 1;
808             }
809         }
810         if (outfiletmp)
811             move(outfiletmp, outfile);
812         break;
813
814       case PUBLIC:
815       case PUBLICO:
816         if (sshver == 1) {
817             FILE *fp;
818             char *dec1, *dec2;
819
820             assert(ssh1key);
821
822             if (outfile)
823                 fp = f_open(outfilename, "w");
824             else
825                 fp = stdout;
826             dec1 = bignum_decimal(ssh1key->exponent);
827             dec2 = bignum_decimal(ssh1key->modulus);
828             fprintf(fp, "%d %s %s %s\n", bignum_bitcount(ssh1key->modulus),
829                     dec1, dec2, ssh1key->comment);
830             sfree(dec1);
831             sfree(dec2);
832             if (outfile)
833                 fclose(fp);
834         } else if (outtype == PUBLIC) {
835             if (!ssh2blob) {
836                 assert(ssh2key);
837                 ssh2blob = ssh2key->alg->public_blob(ssh2key->data,
838                                                      &ssh2bloblen);
839             }
840             save_ssh2_pubkey(outfile, ssh2key ? ssh2key->comment : origcomment,
841                              ssh2blob, ssh2bloblen);
842         } else if (outtype == PUBLICO) {
843             char *buffer, *p;
844             int i;
845             FILE *fp;
846
847             if (!ssh2blob) {
848                 assert(ssh2key);
849                 ssh2blob = ssh2key->alg->public_blob(ssh2key->data,
850                                                      &ssh2bloblen);
851             }
852             if (!ssh2alg) {
853                 assert(ssh2key);
854                 ssh2alg = ssh2key->alg->name;
855             }
856             if (ssh2key)
857                 comment = ssh2key->comment;
858             else
859                 comment = origcomment;
860
861             buffer = snewn(strlen(ssh2alg) +
862                            4 * ((ssh2bloblen+2) / 3) +
863                            strlen(comment) + 3, char);
864             strcpy(buffer, ssh2alg);
865             p = buffer + strlen(buffer);
866             *p++ = ' ';
867             i = 0;
868             while (i < ssh2bloblen) {
869                 int n = (ssh2bloblen - i < 3 ? ssh2bloblen - i : 3);
870                 base64_encode_atom(ssh2blob + i, n, p);
871                 i += n;
872                 p += 4;
873             }
874             if (*comment) {
875                 *p++ = ' ';
876                 strcpy(p, comment);
877             } else
878                 *p++ = '\0';
879
880             if (outfile)
881                 fp = f_open(outfilename, "w");
882             else
883                 fp = stdout;
884             fprintf(fp, "%s\n", buffer);
885             if (outfile)
886                 fclose(fp);
887
888             sfree(buffer);
889         }
890         break;
891
892       case FP:
893         {
894             FILE *fp;
895             char *fingerprint;
896
897             if (sshver == 1) {
898                 assert(ssh1key);
899                 fingerprint = snewn(128, char);
900                 rsa_fingerprint(fingerprint, 128, ssh1key);
901             } else {
902                 if (ssh2key) {
903                     fingerprint = ssh2key->alg->fingerprint(ssh2key->data);
904                 } else {
905                     assert(ssh2blob);
906                     fingerprint = blobfp(ssh2alg, bits, ssh2blob, ssh2bloblen);
907                 }
908             }
909
910             if (outfile)
911                 fp = f_open(outfilename, "w");
912             else
913                 fp = stdout;
914             fprintf(fp, "%s\n", fingerprint);
915             if (outfile)
916                 fclose(fp);
917
918             sfree(fingerprint);
919         }
920         break;
921         
922       case OPENSSH:
923       case SSHCOM:
924         assert(sshver == 2);
925         assert(ssh2key);
926         ret = export_ssh2(&outfilename, outtype, ssh2key, passphrase);
927         if (!ret) {
928             fprintf(stderr, "puttygen: unable to export key\n");
929             return 1;
930         }
931         if (outfiletmp)
932             move(outfiletmp, outfile);
933         break;
934     }
935
936     if (passphrase) {
937         memset(passphrase, 0, strlen(passphrase));
938         sfree(passphrase);
939     }
940
941     if (ssh1key)
942         freersakey(ssh1key);
943     if (ssh2key) {
944         ssh2key->alg->freekey(ssh2key->data);
945         sfree(ssh2key);
946     }
947
948     return 0;
949 }