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