]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgen.c
c83d0d20cd5ce04dd96fa6a15c1325999196d775
[PuTTY.git] / windows / winpgen.c
1 /*
2  * PuTTY key generation front end (Windows).
3  */
4
5 #include <time.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <assert.h>
9
10 #define PUTTY_DO_GLOBALS
11
12 #include "putty.h"
13 #include "ssh.h"
14
15 #include <commctrl.h>
16
17 #ifdef MSVC4
18 #define ICON_BIG        1
19 #endif
20
21 #define WM_DONEKEY (WM_APP + 1)
22
23 #define DEFAULT_KEYSIZE 2048
24
25 static char *cmdline_keyfile = NULL;
26
27 /*
28  * Print a modal (Really Bad) message box and perform a fatal exit.
29  */
30 void modalfatalbox(char *fmt, ...)
31 {
32     va_list ap;
33     char *stuff;
34
35     va_start(ap, fmt);
36     stuff = dupvprintf(fmt, ap);
37     va_end(ap);
38     MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
39                MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
40     sfree(stuff);
41     exit(1);
42 }
43
44 /* ----------------------------------------------------------------------
45  * Progress report code. This is really horrible :-)
46  */
47 #define PROGRESSRANGE 65535
48 #define MAXPHASE 5
49 struct progress {
50     int nphases;
51     struct {
52         int exponential;
53         unsigned startpoint, total;
54         unsigned param, current, n;    /* if exponential */
55         unsigned mult;                 /* if linear */
56     } phases[MAXPHASE];
57     unsigned total, divisor, range;
58     HWND progbar;
59 };
60
61 static void progress_update(void *param, int action, int phase, int iprogress)
62 {
63     struct progress *p = (struct progress *) param;
64     unsigned progress = iprogress;
65     int position;
66
67     if (action < PROGFN_READY && p->nphases < phase)
68         p->nphases = phase;
69     switch (action) {
70       case PROGFN_INITIALISE:
71         p->nphases = 0;
72         break;
73       case PROGFN_LIN_PHASE:
74         p->phases[phase-1].exponential = 0;
75         p->phases[phase-1].mult = p->phases[phase].total / progress;
76         break;
77       case PROGFN_EXP_PHASE:
78         p->phases[phase-1].exponential = 1;
79         p->phases[phase-1].param = 0x10000 + progress;
80         p->phases[phase-1].current = p->phases[phase-1].total;
81         p->phases[phase-1].n = 0;
82         break;
83       case PROGFN_PHASE_EXTENT:
84         p->phases[phase-1].total = progress;
85         break;
86       case PROGFN_READY:
87         {
88             unsigned total = 0;
89             int i;
90             for (i = 0; i < p->nphases; i++) {
91                 p->phases[i].startpoint = total;
92                 total += p->phases[i].total;
93             }
94             p->total = total;
95             p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
96             p->range = p->total / p->divisor;
97             SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
98         }
99         break;
100       case PROGFN_PROGRESS:
101         if (p->phases[phase-1].exponential) {
102             while (p->phases[phase-1].n < progress) {
103                 p->phases[phase-1].n++;
104                 p->phases[phase-1].current *= p->phases[phase-1].param;
105                 p->phases[phase-1].current /= 0x10000;
106             }
107             position = (p->phases[phase-1].startpoint +
108                         p->phases[phase-1].total - p->phases[phase-1].current);
109         } else {
110             position = (p->phases[phase-1].startpoint +
111                         progress * p->phases[phase-1].mult);
112         }
113         SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
114         break;
115     }
116 }
117
118 extern char ver[];
119
120 struct PassphraseProcStruct {
121     char **passphrase;
122     char *comment;
123 };
124
125 /*
126  * Dialog-box function for the passphrase box.
127  */
128 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
129                                    WPARAM wParam, LPARAM lParam)
130 {
131     static char **passphrase = NULL;
132     struct PassphraseProcStruct *p;
133
134     switch (msg) {
135       case WM_INITDIALOG:
136         SetForegroundWindow(hwnd);
137         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
138                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
139
140         /*
141          * Centre the window.
142          */
143         {                              /* centre the window */
144             RECT rs, rd;
145             HWND hw;
146
147             hw = GetDesktopWindow();
148             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
149                 MoveWindow(hwnd,
150                            (rs.right + rs.left + rd.left - rd.right) / 2,
151                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
152                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
153         }
154
155         p = (struct PassphraseProcStruct *) lParam;
156         passphrase = p->passphrase;
157         if (p->comment)
158             SetDlgItemText(hwnd, 101, p->comment);
159         burnstr(*passphrase);
160         *passphrase = dupstr("");
161         SetDlgItemText(hwnd, 102, *passphrase);
162         return 0;
163       case WM_COMMAND:
164         switch (LOWORD(wParam)) {
165           case IDOK:
166             if (*passphrase)
167                 EndDialog(hwnd, 1);
168             else
169                 MessageBeep(0);
170             return 0;
171           case IDCANCEL:
172             EndDialog(hwnd, 0);
173             return 0;
174           case 102:                    /* edit box */
175             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
176                 burnstr(*passphrase);
177                 *passphrase = GetDlgItemText_alloc(hwnd, 102);
178             }
179             return 0;
180         }
181         return 0;
182       case WM_CLOSE:
183         EndDialog(hwnd, 0);
184         return 0;
185     }
186     return 0;
187 }
188
189 /*
190  * Prompt for a key file. Assumes the filename buffer is of size
191  * FILENAME_MAX.
192  */
193 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
194                           char *filename, int save, int ppk)
195 {
196     OPENFILENAME of;
197     memset(&of, 0, sizeof(of));
198     of.hwndOwner = hwnd;
199     if (ppk) {
200         of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
201             "All Files (*.*)\0*\0\0\0";
202         of.lpstrDefExt = ".ppk";
203     } else {
204         of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
205     }
206     of.lpstrCustomFilter = NULL;
207     of.nFilterIndex = 1;
208     of.lpstrFile = filename;
209     *filename = '\0';
210     of.nMaxFile = FILENAME_MAX;
211     of.lpstrFileTitle = NULL;
212     of.lpstrTitle = dlgtitle;
213     of.Flags = 0;
214     return request_file(NULL, &of, FALSE, save);
215 }
216
217 /*
218  * Dialog-box function for the Licence box.
219  */
220 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
221                                 WPARAM wParam, LPARAM lParam)
222 {
223     switch (msg) {
224       case WM_INITDIALOG:
225         /*
226          * Centre the window.
227          */
228         {                              /* centre the window */
229             RECT rs, rd;
230             HWND hw;
231
232             hw = GetDesktopWindow();
233             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
234                 MoveWindow(hwnd,
235                            (rs.right + rs.left + rd.left - rd.right) / 2,
236                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
237                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
238         }
239
240         return 1;
241       case WM_COMMAND:
242         switch (LOWORD(wParam)) {
243           case IDOK:
244           case IDCANCEL:
245             EndDialog(hwnd, 1);
246             return 0;
247         }
248         return 0;
249       case WM_CLOSE:
250         EndDialog(hwnd, 1);
251         return 0;
252     }
253     return 0;
254 }
255
256 /*
257  * Dialog-box function for the About box.
258  */
259 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
260                               WPARAM wParam, LPARAM lParam)
261 {
262     switch (msg) {
263       case WM_INITDIALOG:
264         /*
265          * Centre the window.
266          */
267         {                              /* centre the window */
268             RECT rs, rd;
269             HWND hw;
270
271             hw = GetDesktopWindow();
272             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
273                 MoveWindow(hwnd,
274                            (rs.right + rs.left + rd.left - rd.right) / 2,
275                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
276                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
277         }
278
279         SetDlgItemText(hwnd, 100, ver);
280         return 1;
281       case WM_COMMAND:
282         switch (LOWORD(wParam)) {
283           case IDOK:
284           case IDCANCEL:
285             EndDialog(hwnd, 1);
286             return 0;
287           case 101:
288             EnableWindow(hwnd, 0);
289             DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
290             EnableWindow(hwnd, 1);
291             SetActiveWindow(hwnd);
292             return 0;
293         }
294         return 0;
295       case WM_CLOSE:
296         EndDialog(hwnd, 1);
297         return 0;
298     }
299     return 0;
300 }
301
302 /*
303  * Thread to generate a key.
304  */
305 struct rsa_key_thread_params {
306     HWND progressbar;                  /* notify this with progress */
307     HWND dialog;                       /* notify this on completion */
308     int keysize;                       /* bits in key */
309     int is_dsa;
310     struct RSAKey *key;
311     struct dss_key *dsskey;
312 };
313 static DWORD WINAPI generate_rsa_key_thread(void *param)
314 {
315     struct rsa_key_thread_params *params =
316         (struct rsa_key_thread_params *) param;
317     struct progress prog;
318     prog.progbar = params->progressbar;
319
320     progress_update(&prog, PROGFN_INITIALISE, 0, 0);
321
322     if (params->is_dsa)
323         dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
324     else
325         rsa_generate(params->key, params->keysize, progress_update, &prog);
326
327     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
328
329     sfree(params);
330     return 0;
331 }
332
333 struct MainDlgState {
334     int collecting_entropy;
335     int generation_thread_exists;
336     int key_exists;
337     int entropy_got, entropy_required, entropy_size;
338     int keysize;
339     int ssh2, is_dsa;
340     char **commentptr;                 /* points to key.comment or ssh2key.comment */
341     struct ssh2_userkey ssh2key;
342     unsigned *entropy;
343     struct RSAKey key;
344     struct dss_key dsskey;
345     HMENU filemenu, keymenu, cvtmenu;
346 };
347
348 static void hidemany(HWND hwnd, const int *ids, int hideit)
349 {
350     while (*ids) {
351         ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
352     }
353 }
354
355 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
356 {
357     char *buffer;
358     char *dec1, *dec2;
359
360     dec1 = bignum_decimal(key->exponent);
361     dec2 = bignum_decimal(key->modulus);
362     buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
363                        dec1, dec2, key->comment);
364     SetDlgItemText(hwnd, id, buffer);
365     SetDlgItemText(hwnd, idstatic,
366                    "&Public key for pasting into authorized_keys file:");
367     sfree(dec1);
368     sfree(dec2);
369     sfree(buffer);
370 }
371
372 static void setupbigedit2(HWND hwnd, int id, int idstatic,
373                           struct ssh2_userkey *key)
374 {
375     unsigned char *pub_blob;
376     char *buffer, *p;
377     int pub_len;
378     int i;
379
380     pub_blob = key->alg->public_blob(key->data, &pub_len);
381     buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
382                    strlen(key->comment) + 3, char);
383     strcpy(buffer, key->alg->name);
384     p = buffer + strlen(buffer);
385     *p++ = ' ';
386     i = 0;
387     while (i < pub_len) {
388         int n = (pub_len - i < 3 ? pub_len - i : 3);
389         base64_encode_atom(pub_blob + i, n, p);
390         i += n;
391         p += 4;
392     }
393     *p++ = ' ';
394     strcpy(p, key->comment);
395     SetDlgItemText(hwnd, id, buffer);
396     SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
397                    "OpenSSH authorized_keys file:");
398     sfree(pub_blob);
399     sfree(buffer);
400 }
401
402 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
403 {
404     char *dec1, *dec2;
405     FILE *fp;
406
407     dec1 = bignum_decimal(key->exponent);
408     dec2 = bignum_decimal(key->modulus);
409     fp = fopen(filename, "wb");
410     if (!fp)
411         return 0;
412     fprintf(fp, "%d %s %s %s\n",
413             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
414     fclose(fp);
415     sfree(dec1);
416     sfree(dec2);
417     return 1;
418 }
419
420 /*
421  * Warn about the obsolescent key file format.
422  */
423 void old_keyfile_warning(void)
424 {
425     static const char mbtitle[] = "PuTTY Key File Warning";
426     static const char message[] =
427         "You are loading an SSH-2 private key which has an\n"
428         "old version of the file format. This means your key\n"
429         "file is not fully tamperproof. Future versions of\n"
430         "PuTTY may stop supporting this private key format,\n"
431         "so we recommend you convert your key to the new\n"
432         "format.\n"
433         "\n"
434         "Once the key is loaded into PuTTYgen, you can perform\n"
435         "this conversion simply by saving it again.";
436
437     MessageBox(NULL, message, mbtitle, MB_OK);
438 }
439
440 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
441 {
442     unsigned char *pub_blob;
443     char *p;
444     int pub_len;
445     int i, column;
446     FILE *fp;
447
448     pub_blob = key->alg->public_blob(key->data, &pub_len);
449
450     fp = fopen(filename, "wb");
451     if (!fp)
452         return 0;
453
454     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
455
456     fprintf(fp, "Comment: \"");
457     for (p = key->comment; *p; p++) {
458         if (*p == '\\' || *p == '\"')
459             fputc('\\', fp);
460         fputc(*p, fp);
461     }
462     fprintf(fp, "\"\n");
463
464     i = 0;
465     column = 0;
466     while (i < pub_len) {
467         char buf[5];
468         int n = (pub_len - i < 3 ? pub_len - i : 3);
469         base64_encode_atom(pub_blob + i, n, buf);
470         i += n;
471         buf[4] = '\0';
472         fputs(buf, fp);
473         if (++column >= 16) {
474             fputc('\n', fp);
475             column = 0;
476         }
477     }
478     if (column > 0)
479         fputc('\n', fp);
480     
481     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
482     fclose(fp);
483     sfree(pub_blob);
484     return 1;
485 }
486
487 enum {
488     controlidstart = 100,
489     IDC_QUIT,
490     IDC_TITLE,
491     IDC_BOX_KEY,
492     IDC_NOKEY,
493     IDC_GENERATING,
494     IDC_PROGRESS,
495     IDC_PKSTATIC, IDC_KEYDISPLAY,
496     IDC_FPSTATIC, IDC_FINGERPRINT,
497     IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
498     IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
499     IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
500     IDC_BOX_ACTIONS,
501     IDC_GENSTATIC, IDC_GENERATE,
502     IDC_LOADSTATIC, IDC_LOAD,
503     IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
504     IDC_BOX_PARAMS,
505     IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
506     IDC_BITSSTATIC, IDC_BITS,
507     IDC_ABOUT,
508     IDC_GIVEHELP,
509     IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
510 };
511
512 static const int nokey_ids[] = { IDC_NOKEY, 0 };
513 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
514 static const int gotkey_ids[] = {
515     IDC_PKSTATIC, IDC_KEYDISPLAY,
516     IDC_FPSTATIC, IDC_FINGERPRINT,
517     IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
518     IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
519     IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
520 };
521
522 /*
523  * Small UI helper function to switch the state of the main dialog
524  * by enabling and disabling controls and menu items.
525  */
526 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
527 {
528     int type;
529
530     switch (status) {
531       case 0:                          /* no key */
532         hidemany(hwnd, nokey_ids, FALSE);
533         hidemany(hwnd, generating_ids, TRUE);
534         hidemany(hwnd, gotkey_ids, TRUE);
535         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
536         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
537         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
538         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
539         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
540         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
541         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
542         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
543         EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
544         EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
545         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
546         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
547         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
548         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
549         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
550         EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
551         EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
552                        MF_GRAYED|MF_BYCOMMAND);
553         EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
554                        MF_GRAYED|MF_BYCOMMAND);
555         break;
556       case 1:                          /* generating key */
557         hidemany(hwnd, nokey_ids, TRUE);
558         hidemany(hwnd, generating_ids, FALSE);
559         hidemany(hwnd, gotkey_ids, TRUE);
560         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
561         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
562         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
563         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
564         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
565         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
566         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
567         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
568         EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
569         EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
570         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
571         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
572         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
573         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
574         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
575         EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
576         EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
577                        MF_GRAYED|MF_BYCOMMAND);
578         EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
579                        MF_GRAYED|MF_BYCOMMAND);
580         break;
581       case 2:
582         hidemany(hwnd, nokey_ids, TRUE);
583         hidemany(hwnd, generating_ids, TRUE);
584         hidemany(hwnd, gotkey_ids, FALSE);
585         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
586         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
587         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
588         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
589         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
590         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
591         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
592         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
593         EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
594         EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
595         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
596         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
597         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
598         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
599         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
600         EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
601         /*
602          * Enable export menu items if and only if the key type
603          * supports this kind of export.
604          */
605         type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
606 #define do_export_menuitem(x,y) \
607     EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
608                        (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
609         do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
610         do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
611 #undef do_export_menuitem
612         break;
613     }
614 }
615
616 void load_key_file(HWND hwnd, struct MainDlgState *state,
617                    Filename *filename, int was_import_cmd)
618 {
619     char *passphrase;
620     int needs_pass;
621     int type, realtype;
622     int ret;
623     const char *errmsg = NULL;
624     char *comment;
625     struct RSAKey newkey1;
626     struct ssh2_userkey *newkey2 = NULL;
627
628     type = realtype = key_type(filename);
629     if (type != SSH_KEYTYPE_SSH1 &&
630         type != SSH_KEYTYPE_SSH2 &&
631         !import_possible(type)) {
632         char *msg = dupprintf("Couldn't load private key (%s)",
633                               key_type_to_str(type));
634         message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
635                     HELPCTXID(errors_cantloadkey));
636         sfree(msg);
637         return;
638     }
639
640     if (type != SSH_KEYTYPE_SSH1 &&
641         type != SSH_KEYTYPE_SSH2) {
642         realtype = type;
643         type = import_target_type(type);
644     }
645
646     comment = NULL;
647     passphrase = NULL;
648     if (realtype == SSH_KEYTYPE_SSH1)
649         needs_pass = rsakey_encrypted(filename, &comment);
650     else if (realtype == SSH_KEYTYPE_SSH2)
651         needs_pass = ssh2_userkey_encrypted(filename, &comment);
652     else
653         needs_pass = import_encrypted(filename, realtype, &comment);
654     do {
655         burnstr(passphrase);
656         passphrase = NULL;
657
658         if (needs_pass) {
659             int dlgret;
660             struct PassphraseProcStruct pps;
661             pps.passphrase = &passphrase;
662             pps.comment = comment;
663             dlgret = DialogBoxParam(hinst,
664                                     MAKEINTRESOURCE(210),
665                                     NULL, PassphraseProc,
666                                     (LPARAM) &pps);
667             if (!dlgret) {
668                 ret = -2;
669                 break;
670             }
671             assert(passphrase != NULL);
672         } else
673             passphrase = dupstr("");
674         if (type == SSH_KEYTYPE_SSH1) {
675             if (realtype == type)
676                 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
677             else
678                 ret = import_ssh1(filename, realtype, &newkey1,
679                                   passphrase, &errmsg);
680         } else {
681             if (realtype == type)
682                 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
683             else
684                 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
685             if (newkey2 == SSH2_WRONG_PASSPHRASE)
686                 ret = -1;
687             else if (!newkey2)
688                 ret = 0;
689             else
690                 ret = 1;
691         }
692     } while (ret == -1);
693     if (comment)
694         sfree(comment);
695     if (ret == 0) {
696         char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
697         message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
698                     HELPCTXID(errors_cantloadkey));
699         sfree(msg);
700     } else if (ret == 1) {
701         /*
702          * Now update the key controls with all the
703          * key data.
704          */
705         {
706             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
707                            passphrase);
708             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
709                            passphrase);
710             if (type == SSH_KEYTYPE_SSH1) {
711                 char buf[128];
712                 char *savecomment;
713
714                 state->ssh2 = FALSE;
715                 state->commentptr = &state->key.comment;
716                 state->key = newkey1;
717
718                 /*
719                  * Set the key fingerprint.
720                  */
721                 savecomment = state->key.comment;
722                 state->key.comment = NULL;
723                 rsa_fingerprint(buf, sizeof(buf),
724                                 &state->key);
725                 state->key.comment = savecomment;
726
727                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
728                 /*
729                  * Construct a decimal representation
730                  * of the key, for pasting into
731                  * .ssh/authorized_keys on a Unix box.
732                  */
733                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
734                               IDC_PKSTATIC, &state->key);
735             } else {
736                 char *fp;
737                 char *savecomment;
738
739                 state->ssh2 = TRUE;
740                 state->commentptr =
741                     &state->ssh2key.comment;
742                 state->ssh2key = *newkey2;      /* structure copy */
743                 sfree(newkey2);
744
745                 savecomment = state->ssh2key.comment;
746                 state->ssh2key.comment = NULL;
747                 fp =
748                     state->ssh2key.alg->
749                     fingerprint(state->ssh2key.data);
750                 state->ssh2key.comment = savecomment;
751
752                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
753                 sfree(fp);
754
755                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
756                               IDC_PKSTATIC, &state->ssh2key);
757             }
758             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
759                            *state->commentptr);
760         }
761         /*
762          * Finally, hide the progress bar and show
763          * the key data.
764          */
765         ui_set_state(hwnd, state, 2);
766         state->key_exists = TRUE;
767
768         /*
769          * If the user has imported a foreign key
770          * using the Load command, let them know.
771          * If they've used the Import command, be
772          * silent.
773          */
774         if (realtype != type && !was_import_cmd) {
775             char msg[512];
776             sprintf(msg, "Successfully imported foreign key\n"
777                     "(%s).\n"
778                     "To use this key with PuTTY, you need to\n"
779                     "use the \"Save private key\" command to\n"
780                     "save it in PuTTY's own format.",
781                     key_type_to_str(realtype));
782             MessageBox(NULL, msg, "PuTTYgen Notice",
783                        MB_OK | MB_ICONINFORMATION);
784         }
785     }
786     burnstr(passphrase);
787 }
788
789 /*
790  * Dialog-box function for the main PuTTYgen dialog box.
791  */
792 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
793                                 WPARAM wParam, LPARAM lParam)
794 {
795     static const char generating_msg[] =
796         "Please wait while a key is generated...";
797     static const char entropy_msg[] =
798         "Please generate some randomness by moving the mouse over the blank area.";
799     struct MainDlgState *state;
800
801     switch (msg) {
802       case WM_INITDIALOG:
803         if (has_help())
804             SetWindowLongPtr(hwnd, GWL_EXSTYLE,
805                              GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
806                              WS_EX_CONTEXTHELP);
807         else {
808             /*
809              * If we add a Help button, this is where we destroy it
810              * if the help file isn't present.
811              */
812         }
813         SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
814                     (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
815
816         state = snew(struct MainDlgState);
817         state->generation_thread_exists = FALSE;
818         state->collecting_entropy = FALSE;
819         state->entropy = NULL;
820         state->key_exists = FALSE;
821         SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
822         {
823             HMENU menu, menu1;
824
825             menu = CreateMenu();
826
827             menu1 = CreateMenu();
828             AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
829             AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
830             AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
831             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
832             AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
833             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
834             state->filemenu = menu1;
835
836             menu1 = CreateMenu();
837             AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
838             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
839             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
840             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
841             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
842             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
843             state->keymenu = menu1;
844
845             menu1 = CreateMenu();
846             AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
847             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
848             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
849                        "Export &OpenSSH key");
850             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
851                        "Export &ssh.com key");
852             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
853                        "Con&versions");
854             state->cvtmenu = menu1;
855
856             menu1 = CreateMenu();
857             AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
858             if (has_help())
859                 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
860             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
861
862             SetMenu(hwnd, menu);
863         }
864
865         /*
866          * Centre the window.
867          */
868         {                              /* centre the window */
869             RECT rs, rd;
870             HWND hw;
871
872             hw = GetDesktopWindow();
873             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
874                 MoveWindow(hwnd,
875                            (rs.right + rs.left + rd.left - rd.right) / 2,
876                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
877                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
878         }
879
880         {
881             struct ctlpos cp, cp2;
882
883             /* Accelerators used: acglops1rbd */
884
885             ctlposinit(&cp, hwnd, 4, 4, 4);
886             beginbox(&cp, "Key", IDC_BOX_KEY);
887             cp2 = cp;
888             statictext(&cp2, "No key.", 1, IDC_NOKEY);
889             cp2 = cp;
890             statictext(&cp2, "", 1, IDC_GENERATING);
891             progressbar(&cp2, IDC_PROGRESS);
892             bigeditctrl(&cp,
893                         "&Public key for pasting into authorized_keys file:",
894                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
895             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
896             staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
897                        IDC_FINGERPRINT, 75);
898             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
899                                0);
900             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
901                        IDC_COMMENTEDIT, 75);
902             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
903                            IDC_PASSPHRASE1EDIT, 75);
904             staticpassedit(&cp, "C&onfirm passphrase:",
905                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
906             endbox(&cp);
907             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
908             staticbtn(&cp, "Generate a public/private key pair",
909                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
910             staticbtn(&cp, "Load an existing private key file",
911                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
912             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
913                        "Save p&ublic key", IDC_SAVEPUB,
914                        "&Save private key", IDC_SAVE);
915             endbox(&cp);
916             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
917             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
918                       "SSH-&1 (RSA)", IDC_KEYSSH1,
919                       "SSH-2 &RSA", IDC_KEYSSH2RSA,
920                       "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
921             staticedit(&cp, "Number of &bits in a generated key:",
922                        IDC_BITSSTATIC, IDC_BITS, 20);
923             endbox(&cp);
924         }
925         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
926         CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
927                            IDC_KEYSSH2RSA, MF_BYCOMMAND);
928         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
929
930         /*
931          * Initially, hide the progress bar and the key display,
932          * and show the no-key display. Also disable the Save
933          * buttons, because with no key we obviously can't save
934          * anything.
935          */
936         ui_set_state(hwnd, state, 0);
937
938         /*
939          * Load a key file if one was provided on the command line.
940          */
941         if (cmdline_keyfile)
942             load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
943
944         return 1;
945       case WM_MOUSEMOVE:
946         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
947         if (state->collecting_entropy &&
948             state->entropy && state->entropy_got < state->entropy_required) {
949             state->entropy[state->entropy_got++] = lParam;
950             state->entropy[state->entropy_got++] = GetMessageTime();
951             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
952                                state->entropy_got, 0);
953             if (state->entropy_got >= state->entropy_required) {
954                 struct rsa_key_thread_params *params;
955                 DWORD threadid;
956
957                 /*
958                  * Seed the entropy pool
959                  */
960                 random_add_heavynoise(state->entropy, state->entropy_size);
961                 memset(state->entropy, 0, state->entropy_size);
962                 sfree(state->entropy);
963                 state->collecting_entropy = FALSE;
964
965                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
966                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
967                                    MAKELPARAM(0, PROGRESSRANGE));
968                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
969
970                 params = snew(struct rsa_key_thread_params);
971                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
972                 params->dialog = hwnd;
973                 params->keysize = state->keysize;
974                 params->is_dsa = state->is_dsa;
975                 params->key = &state->key;
976                 params->dsskey = &state->dsskey;
977
978                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
979                                   params, 0, &threadid)) {
980                     MessageBox(hwnd, "Out of thread resources",
981                                "Key generation error",
982                                MB_OK | MB_ICONERROR);
983                     sfree(params);
984                 } else {
985                     state->generation_thread_exists = TRUE;
986                 }
987             }
988         }
989         break;
990       case WM_COMMAND:
991         switch (LOWORD(wParam)) {
992           case IDC_KEYSSH1:
993           case IDC_KEYSSH2RSA:
994           case IDC_KEYSSH2DSA:
995             {
996                 state = (struct MainDlgState *)
997                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
998                 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
999                     CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1000                                      LOWORD(wParam));
1001                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1002                                    LOWORD(wParam), MF_BYCOMMAND);
1003             }
1004             break;
1005           case IDC_QUIT:
1006             PostMessage(hwnd, WM_CLOSE, 0, 0);
1007             break;
1008           case IDC_COMMENTEDIT:
1009             if (HIWORD(wParam) == EN_CHANGE) {
1010                 state = (struct MainDlgState *)
1011                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
1012                 if (state->key_exists) {
1013                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1014                     int len = GetWindowTextLength(editctl);
1015                     if (*state->commentptr)
1016                         sfree(*state->commentptr);
1017                     *state->commentptr = snewn(len + 1, char);
1018                     GetWindowText(editctl, *state->commentptr, len + 1);
1019                     if (state->ssh2) {
1020                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1021                                       &state->ssh2key);
1022                     } else {
1023                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1024                                       &state->key);
1025                     }
1026                 }
1027             }
1028             break;
1029           case IDC_ABOUT:
1030             EnableWindow(hwnd, 0);
1031             DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1032             EnableWindow(hwnd, 1);
1033             SetActiveWindow(hwnd);
1034             return 0;
1035           case IDC_GIVEHELP:
1036             if (HIWORD(wParam) == BN_CLICKED ||
1037                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1038                 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1039             }
1040             return 0;
1041           case IDC_GENERATE:
1042             if (HIWORD(wParam) != BN_CLICKED &&
1043                 HIWORD(wParam) != BN_DOUBLECLICKED)
1044                 break;
1045             state =
1046                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1047             if (!state->generation_thread_exists) {
1048                 BOOL ok;
1049                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1050                 if (!ok)
1051                     state->keysize = DEFAULT_KEYSIZE;
1052                 /* If we ever introduce a new key type, check it here! */
1053                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1054                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1055                 if (state->keysize < 256) {
1056                     int ret = MessageBox(hwnd,
1057                                          "PuTTYgen will not generate a key"
1058                                          " smaller than 256 bits.\n"
1059                                          "Key length reset to 256. Continue?",
1060                                          "PuTTYgen Warning",
1061                                          MB_ICONWARNING | MB_OKCANCEL);
1062                     if (ret != IDOK)
1063                         break;
1064                     state->keysize = 256;
1065                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1066                 }
1067                 ui_set_state(hwnd, state, 1);
1068                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1069                 state->key_exists = FALSE;
1070                 state->collecting_entropy = TRUE;
1071
1072                 /*
1073                  * My brief statistical tests on mouse movements
1074                  * suggest that there are about 2.5 bits of
1075                  * randomness in the x position, 2.5 in the y
1076                  * position, and 1.7 in the message time, making
1077                  * 5.7 bits of unpredictability per mouse movement.
1078                  * However, other people have told me it's far less
1079                  * than that, so I'm going to be stupidly cautious
1080                  * and knock that down to a nice round 2. With this
1081                  * method, we require two words per mouse movement,
1082                  * so with 2 bits per mouse movement we expect 2
1083                  * bits every 2 words.
1084                  */
1085                 state->entropy_required = (state->keysize / 2) * 2;
1086                 state->entropy_got = 0;
1087                 state->entropy_size = (state->entropy_required *
1088                                        sizeof(unsigned));
1089                 state->entropy = snewn(state->entropy_required, unsigned);
1090
1091                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1092                                    MAKELPARAM(0, state->entropy_required));
1093                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1094             }
1095             break;
1096           case IDC_SAVE:
1097           case IDC_EXPORT_OPENSSH:
1098           case IDC_EXPORT_SSHCOM:
1099             if (HIWORD(wParam) != BN_CLICKED)
1100                 break;
1101             state =
1102                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1103             if (state->key_exists) {
1104                 char filename[FILENAME_MAX];
1105                 char *passphrase, *passphrase2;
1106                 int type, realtype;
1107
1108                 if (state->ssh2)
1109                     realtype = SSH_KEYTYPE_SSH2;
1110                 else
1111                     realtype = SSH_KEYTYPE_SSH1;
1112
1113                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1114                     type = SSH_KEYTYPE_OPENSSH;
1115                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1116                     type = SSH_KEYTYPE_SSHCOM;
1117                 else
1118                     type = realtype;
1119
1120                 if (type != realtype &&
1121                     import_target_type(type) != realtype) {
1122                     char msg[256];
1123                     sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1124                             " format", (state->ssh2 ? 2 : 1),
1125                             (state->ssh2 ? 1 : 2));
1126                     MessageBox(hwnd, msg,
1127                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1128                     break;
1129                 }
1130
1131                 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1132                 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1133                 if (strcmp(passphrase, passphrase2)) {
1134                     MessageBox(hwnd,
1135                                "The two passphrases given do not match.",
1136                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1137                     burnstr(passphrase);
1138                     burnstr(passphrase2);
1139                     break;
1140                 }
1141                 burnstr(passphrase2);
1142                 if (!*passphrase) {
1143                     int ret;
1144                     ret = MessageBox(hwnd,
1145                                      "Are you sure you want to save this key\n"
1146                                      "without a passphrase to protect it?",
1147                                      "PuTTYgen Warning",
1148                                      MB_YESNO | MB_ICONWARNING);
1149                     if (ret != IDYES) {
1150                         burnstr(passphrase);
1151                         break;
1152                     }
1153                 }
1154                 if (prompt_keyfile(hwnd, "Save private key as:",
1155                                    filename, 1, (type == realtype))) {
1156                     int ret;
1157                     FILE *fp = fopen(filename, "r");
1158                     if (fp) {
1159                         char *buffer;
1160                         fclose(fp);
1161                         buffer = dupprintf("Overwrite existing file\n%s?",
1162                                            filename);
1163                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1164                                          MB_YESNO | MB_ICONWARNING);
1165                         sfree(buffer);
1166                         if (ret != IDYES) {
1167                             burnstr(passphrase);
1168                             break;
1169                         }
1170                     }
1171
1172                     if (state->ssh2) {
1173                         Filename *fn = filename_from_str(filename);
1174                         if (type != realtype)
1175                             ret = export_ssh2(fn, type, &state->ssh2key,
1176                                               *passphrase ? passphrase : NULL);
1177                         else
1178                             ret = ssh2_save_userkey(fn, &state->ssh2key,
1179                                                     *passphrase ? passphrase :
1180                                                     NULL);
1181                         filename_free(fn);
1182                     } else {
1183                         Filename *fn = filename_from_str(filename);
1184                         if (type != realtype)
1185                             ret = export_ssh1(fn, type, &state->key,
1186                                               *passphrase ? passphrase : NULL);
1187                         else
1188                             ret = saversakey(fn, &state->key,
1189                                              *passphrase ? passphrase : NULL);
1190                         filename_free(fn);
1191                     }
1192                     if (ret <= 0) {
1193                         MessageBox(hwnd, "Unable to save key file",
1194                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1195                     }
1196                 }
1197                 burnstr(passphrase);
1198             }
1199             break;
1200           case IDC_SAVEPUB:
1201             if (HIWORD(wParam) != BN_CLICKED)
1202                 break;
1203             state =
1204                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1205             if (state->key_exists) {
1206                 char filename[FILENAME_MAX];
1207                 if (prompt_keyfile(hwnd, "Save public key as:",
1208                                    filename, 1, 0)) {
1209                     int ret;
1210                     FILE *fp = fopen(filename, "r");
1211                     if (fp) {
1212                         char *buffer;
1213                         fclose(fp);
1214                         buffer = dupprintf("Overwrite existing file\n%s?",
1215                                            filename);
1216                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1217                                          MB_YESNO | MB_ICONWARNING);
1218                         sfree(buffer);
1219                         if (ret != IDYES)
1220                             break;
1221                     }
1222                     if (state->ssh2) {
1223                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
1224                     } else {
1225                         ret = save_ssh1_pubkey(filename, &state->key);
1226                     }
1227                     if (ret <= 0) {
1228                         MessageBox(hwnd, "Unable to save key file",
1229                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1230                     }
1231                 }
1232             }
1233             break;
1234           case IDC_LOAD:
1235           case IDC_IMPORT:
1236             if (HIWORD(wParam) != BN_CLICKED)
1237                 break;
1238             state =
1239                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1240             if (!state->generation_thread_exists) {
1241                 char filename[FILENAME_MAX];
1242                 if (prompt_keyfile(hwnd, "Load private key:",
1243                                    filename, 0, LOWORD(wParam)==IDC_LOAD))
1244                     load_key_file(hwnd, state, filename_from_str(filename),
1245                                   LOWORD(wParam) != IDC_LOAD);
1246             }
1247             break;
1248         }
1249         return 0;
1250       case WM_DONEKEY:
1251         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1252         state->generation_thread_exists = FALSE;
1253         state->key_exists = TRUE;
1254         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1255                            MAKELPARAM(0, PROGRESSRANGE));
1256         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1257         if (state->ssh2) {
1258             if (state->is_dsa) {
1259                 state->ssh2key.data = &state->dsskey;
1260                 state->ssh2key.alg = &ssh_dss;
1261             } else {
1262                 state->ssh2key.data = &state->key;
1263                 state->ssh2key.alg = &ssh_rsa;
1264             }
1265             state->commentptr = &state->ssh2key.comment;
1266         } else {
1267             state->commentptr = &state->key.comment;
1268         }
1269         /*
1270          * Invent a comment for the key. We'll do this by including
1271          * the date in it. This will be so horrifyingly ugly that
1272          * the user will immediately want to change it, which is
1273          * what we want :-)
1274          */
1275         *state->commentptr = snewn(30, char);
1276         {
1277             struct tm tm;
1278             tm = ltime();
1279             if (state->is_dsa)
1280                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1281             else
1282                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1283         }
1284
1285         /*
1286          * Now update the key controls with all the key data.
1287          */
1288         {
1289             char *savecomment;
1290             /*
1291              * Blank passphrase, initially. This isn't dangerous,
1292              * because we will warn (Are You Sure?) before allowing
1293              * the user to save an unprotected private key.
1294              */
1295             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1296             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1297             /*
1298              * Set the comment.
1299              */
1300             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1301             /*
1302              * Set the key fingerprint.
1303              */
1304             savecomment = *state->commentptr;
1305             *state->commentptr = NULL;
1306             if (state->ssh2) {
1307                 char *fp;
1308                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1309                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1310                 sfree(fp);
1311             } else {
1312                 char buf[128];
1313                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1314                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1315             }
1316             *state->commentptr = savecomment;
1317             /*
1318              * Construct a decimal representation of the key, for
1319              * pasting into .ssh/authorized_keys or
1320              * .ssh/authorized_keys2 on a Unix box.
1321              */
1322             if (state->ssh2) {
1323                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1324                               IDC_PKSTATIC, &state->ssh2key);
1325             } else {
1326                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1327                               IDC_PKSTATIC, &state->key);
1328             }
1329         }
1330         /*
1331          * Finally, hide the progress bar and show the key data.
1332          */
1333         ui_set_state(hwnd, state, 2);
1334         break;
1335       case WM_HELP:
1336         {
1337             int id = ((LPHELPINFO)lParam)->iCtrlId;
1338             char *topic = NULL;
1339             switch (id) {
1340               case IDC_GENERATING:
1341               case IDC_PROGRESS:
1342               case IDC_GENSTATIC:
1343               case IDC_GENERATE:
1344                 topic = WINHELP_CTX_puttygen_generate; break;
1345               case IDC_PKSTATIC:
1346               case IDC_KEYDISPLAY:
1347                 topic = WINHELP_CTX_puttygen_pastekey; break;
1348               case IDC_FPSTATIC:
1349               case IDC_FINGERPRINT:
1350                 topic = WINHELP_CTX_puttygen_fingerprint; break;
1351               case IDC_COMMENTSTATIC:
1352               case IDC_COMMENTEDIT:
1353                 topic = WINHELP_CTX_puttygen_comment; break;
1354               case IDC_PASSPHRASE1STATIC:
1355               case IDC_PASSPHRASE1EDIT:
1356               case IDC_PASSPHRASE2STATIC:
1357               case IDC_PASSPHRASE2EDIT:
1358                 topic = WINHELP_CTX_puttygen_passphrase; break;
1359               case IDC_LOADSTATIC:
1360               case IDC_LOAD:
1361                 topic = WINHELP_CTX_puttygen_load; break;
1362               case IDC_SAVESTATIC:
1363               case IDC_SAVE:
1364                 topic = WINHELP_CTX_puttygen_savepriv; break;
1365               case IDC_SAVEPUB:
1366                 topic = WINHELP_CTX_puttygen_savepub; break;
1367               case IDC_TYPESTATIC:
1368               case IDC_KEYSSH1:
1369               case IDC_KEYSSH2RSA:
1370               case IDC_KEYSSH2DSA:
1371                 topic = WINHELP_CTX_puttygen_keytype; break;
1372               case IDC_BITSSTATIC:
1373               case IDC_BITS:
1374                 topic = WINHELP_CTX_puttygen_bits; break;
1375               case IDC_IMPORT:
1376               case IDC_EXPORT_OPENSSH:
1377               case IDC_EXPORT_SSHCOM:
1378                 topic = WINHELP_CTX_puttygen_conversions; break;
1379             }
1380             if (topic) {
1381                 launch_help(hwnd, topic);
1382             } else {
1383                 MessageBeep(0);
1384             }
1385         }
1386         break;
1387       case WM_CLOSE:
1388         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1389         sfree(state);
1390         quit_help(hwnd);
1391         EndDialog(hwnd, 1);
1392         return 0;
1393     }
1394     return 0;
1395 }
1396
1397 void cleanup_exit(int code)
1398 {
1399     shutdown_help();
1400     exit(code);
1401 }
1402
1403 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1404 {
1405     int argc;
1406     char **argv;
1407     int ret;
1408
1409     InitCommonControls();
1410     hinst = inst;
1411     hwnd = NULL;
1412
1413     /*
1414      * See if we can find our Help file.
1415      */
1416     init_help();
1417
1418     split_into_argv(cmdline, &argc, &argv, NULL);
1419
1420     if (argc > 0) {
1421         if (!strcmp(argv[0], "-pgpfp")) {
1422             pgp_fingerprints();
1423             exit(1);
1424         } else {
1425             /*
1426              * Assume the first argument to be a private key file, and
1427              * attempt to load it.
1428              */
1429             cmdline_keyfile = argv[0];
1430         }
1431     }
1432
1433     random_ref();
1434     ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1435
1436     cleanup_exit(ret);
1437     return ret;                        /* just in case optimiser complains */
1438 }