]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgen.c
Fix layout overflow in Windows PuTTYgen due to ED25519.
[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, 5,
929                       "&RSA", IDC_KEYSSH2RSA,
930                       "&DSA", IDC_KEYSSH2DSA,
931                       "&ECDSA", IDC_KEYSSH2ECDSA,
932                       "ED&25519", IDC_KEYSSH2ED25519,
933                       "SSH-&1 (RSA)", IDC_KEYSSH1,
934                       NULL);
935             staticedit(&cp, "Number of &bits in a generated key:",
936                        IDC_BITSSTATIC, IDC_BITS, 20);
937             endbox(&cp);
938         }
939         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
940         CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
941                            IDC_KEYSSH2RSA, MF_BYCOMMAND);
942         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
943
944         /*
945          * Initially, hide the progress bar and the key display,
946          * and show the no-key display. Also disable the Save
947          * buttons, because with no key we obviously can't save
948          * anything.
949          */
950         ui_set_state(hwnd, state, 0);
951
952         /*
953          * Load a key file if one was provided on the command line.
954          */
955         if (cmdline_keyfile) {
956             Filename *fn = filename_from_str(cmdline_keyfile);
957             load_key_file(hwnd, state, fn, 0);
958             filename_free(fn);
959         }
960
961         return 1;
962       case WM_MOUSEMOVE:
963         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
964         if (state->collecting_entropy &&
965             state->entropy && state->entropy_got < state->entropy_required) {
966             state->entropy[state->entropy_got++] = lParam;
967             state->entropy[state->entropy_got++] = GetMessageTime();
968             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
969                                state->entropy_got, 0);
970             if (state->entropy_got >= state->entropy_required) {
971                 struct rsa_key_thread_params *params;
972                 DWORD threadid;
973
974                 /*
975                  * Seed the entropy pool
976                  */
977                 random_add_heavynoise(state->entropy, state->entropy_size);
978                 smemclr(state->entropy, state->entropy_size);
979                 sfree(state->entropy);
980                 state->collecting_entropy = FALSE;
981
982                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
983                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
984                                    MAKELPARAM(0, PROGRESSRANGE));
985                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
986
987                 params = snew(struct rsa_key_thread_params);
988                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
989                 params->dialog = hwnd;
990                 params->keysize = state->keysize;
991                 params->keytype = state->keytype;
992                 params->key = &state->key;
993                 params->dsskey = &state->dsskey;
994
995                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
996                                   params, 0, &threadid)) {
997                     MessageBox(hwnd, "Out of thread resources",
998                                "Key generation error",
999                                MB_OK | MB_ICONERROR);
1000                     sfree(params);
1001                 } else {
1002                     state->generation_thread_exists = TRUE;
1003                 }
1004             }
1005         }
1006         break;
1007       case WM_COMMAND:
1008         switch (LOWORD(wParam)) {
1009           case IDC_KEYSSH1:
1010           case IDC_KEYSSH2RSA:
1011           case IDC_KEYSSH2DSA:
1012           case IDC_KEYSSH2ECDSA:
1013           case IDC_KEYSSH2ED25519:
1014             {
1015                 state = (struct MainDlgState *)
1016                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
1017                 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1018                     CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1019                                      LOWORD(wParam));
1020                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1021                                    LOWORD(wParam), MF_BYCOMMAND);
1022                 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
1023                                  LOWORD(wParam));
1024                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
1025                                    IDC_KEYSSH2ECDSA,
1026                                    LOWORD(wParam), MF_BYCOMMAND);
1027             }
1028             break;
1029           case IDC_QUIT:
1030             PostMessage(hwnd, WM_CLOSE, 0, 0);
1031             break;
1032           case IDC_COMMENTEDIT:
1033             if (HIWORD(wParam) == EN_CHANGE) {
1034                 state = (struct MainDlgState *)
1035                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
1036                 if (state->key_exists) {
1037                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1038                     int len = GetWindowTextLength(editctl);
1039                     if (*state->commentptr)
1040                         sfree(*state->commentptr);
1041                     *state->commentptr = snewn(len + 1, char);
1042                     GetWindowText(editctl, *state->commentptr, len + 1);
1043                     if (state->ssh2) {
1044                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1045                                       &state->ssh2key);
1046                     } else {
1047                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1048                                       &state->key);
1049                     }
1050                 }
1051             }
1052             break;
1053           case IDC_ABOUT:
1054             EnableWindow(hwnd, 0);
1055             DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1056             EnableWindow(hwnd, 1);
1057             SetActiveWindow(hwnd);
1058             return 0;
1059           case IDC_GIVEHELP:
1060             if (HIWORD(wParam) == BN_CLICKED ||
1061                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1062                 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1063             }
1064             return 0;
1065           case IDC_GENERATE:
1066             if (HIWORD(wParam) != BN_CLICKED &&
1067                 HIWORD(wParam) != BN_DOUBLECLICKED)
1068                 break;
1069             state =
1070                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1071             if (!state->generation_thread_exists) {
1072                 BOOL ok;
1073                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1074                 if (!ok)
1075                     state->keysize = DEFAULT_KEYSIZE;
1076                 /* If we ever introduce a new key type, check it here! */
1077                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1078                 state->keytype = RSA;
1079                 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1080                     state->keytype = DSA;
1081                 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1082                     state->keytype = ECDSA;
1083                 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1084                     state->keytype = ED25519;
1085                 }
1086                 if (state->keysize < 256) {
1087                     int ret = MessageBox(hwnd,
1088                                          "PuTTYgen will not generate a key"
1089                                          " smaller than 256 bits.\n"
1090                                          "Key length reset to 256. Continue?",
1091                                          "PuTTYgen Warning",
1092                                          MB_ICONWARNING | MB_OKCANCEL);
1093                     if (ret != IDOK)
1094                         break;
1095                     state->keysize = 256;
1096                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1097                 }
1098                 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1099                                                  state->keysize == 384 ||
1100                                                  state->keysize == 521)) {
1101                     int ret = MessageBox(hwnd,
1102                                          "Only 256, 384 and 521 bit elliptic"
1103                                          " curves are supported.\n"
1104                                          "Key length reset to 256. Continue?",
1105                                          "PuTTYgen Warning",
1106                                          MB_ICONWARNING | MB_OKCANCEL);
1107                     if (ret != IDOK)
1108                         break;
1109                     state->keysize = 256;
1110                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1111                 }
1112                 if (state->keytype == ED25519 && state->keysize != 256) {
1113                     int ret = MessageBox(hwnd,
1114                                          "Only 256 bit Edwards elliptic"
1115                                          " curves are supported.\n"
1116                                          "Key length reset to 256. Continue?",
1117                                          "PuTTYgen Warning",
1118                                          MB_ICONWARNING | MB_OKCANCEL);
1119                     if (ret != IDOK)
1120                         break;
1121                     state->keysize = 256;
1122                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1123                 }
1124                 ui_set_state(hwnd, state, 1);
1125                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1126                 state->key_exists = FALSE;
1127                 state->collecting_entropy = TRUE;
1128
1129                 /*
1130                  * My brief statistical tests on mouse movements
1131                  * suggest that there are about 2.5 bits of
1132                  * randomness in the x position, 2.5 in the y
1133                  * position, and 1.7 in the message time, making
1134                  * 5.7 bits of unpredictability per mouse movement.
1135                  * However, other people have told me it's far less
1136                  * than that, so I'm going to be stupidly cautious
1137                  * and knock that down to a nice round 2. With this
1138                  * method, we require two words per mouse movement,
1139                  * so with 2 bits per mouse movement we expect 2
1140                  * bits every 2 words.
1141                  */
1142                 state->entropy_required = (state->keysize / 2) * 2;
1143                 state->entropy_got = 0;
1144                 state->entropy_size = (state->entropy_required *
1145                                        sizeof(unsigned));
1146                 state->entropy = snewn(state->entropy_required, unsigned);
1147
1148                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1149                                    MAKELPARAM(0, state->entropy_required));
1150                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1151             }
1152             break;
1153           case IDC_SAVE:
1154           case IDC_EXPORT_OPENSSH_AUTO:
1155           case IDC_EXPORT_OPENSSH_NEW:
1156           case IDC_EXPORT_SSHCOM:
1157             if (HIWORD(wParam) != BN_CLICKED)
1158                 break;
1159             state =
1160                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1161             if (state->key_exists) {
1162                 char filename[FILENAME_MAX];
1163                 char *passphrase, *passphrase2;
1164                 int type, realtype;
1165
1166                 if (state->ssh2)
1167                     realtype = SSH_KEYTYPE_SSH2;
1168                 else
1169                     realtype = SSH_KEYTYPE_SSH1;
1170
1171                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1172                     type = SSH_KEYTYPE_OPENSSH_AUTO;
1173                 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1174                     type = SSH_KEYTYPE_OPENSSH_NEW;
1175                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1176                     type = SSH_KEYTYPE_SSHCOM;
1177                 else
1178                     type = realtype;
1179
1180                 if (type != realtype &&
1181                     import_target_type(type) != realtype) {
1182                     char msg[256];
1183                     sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1184                             " format", (state->ssh2 ? 2 : 1),
1185                             (state->ssh2 ? 1 : 2));
1186                     MessageBox(hwnd, msg,
1187                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1188                     break;
1189                 }
1190
1191                 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1192                 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1193                 if (strcmp(passphrase, passphrase2)) {
1194                     MessageBox(hwnd,
1195                                "The two passphrases given do not match.",
1196                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1197                     burnstr(passphrase);
1198                     burnstr(passphrase2);
1199                     break;
1200                 }
1201                 burnstr(passphrase2);
1202                 if (!*passphrase) {
1203                     int ret;
1204                     ret = MessageBox(hwnd,
1205                                      "Are you sure you want to save this key\n"
1206                                      "without a passphrase to protect it?",
1207                                      "PuTTYgen Warning",
1208                                      MB_YESNO | MB_ICONWARNING);
1209                     if (ret != IDYES) {
1210                         burnstr(passphrase);
1211                         break;
1212                     }
1213                 }
1214                 if (prompt_keyfile(hwnd, "Save private key as:",
1215                                    filename, 1, (type == realtype))) {
1216                     int ret;
1217                     FILE *fp = fopen(filename, "r");
1218                     if (fp) {
1219                         char *buffer;
1220                         fclose(fp);
1221                         buffer = dupprintf("Overwrite existing file\n%s?",
1222                                            filename);
1223                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1224                                          MB_YESNO | MB_ICONWARNING);
1225                         sfree(buffer);
1226                         if (ret != IDYES) {
1227                             burnstr(passphrase);
1228                             break;
1229                         }
1230                     }
1231
1232                     if (state->ssh2) {
1233                         Filename *fn = filename_from_str(filename);
1234                         if (type != realtype)
1235                             ret = export_ssh2(fn, type, &state->ssh2key,
1236                                               *passphrase ? passphrase : NULL);
1237                         else
1238                             ret = ssh2_save_userkey(fn, &state->ssh2key,
1239                                                     *passphrase ? passphrase :
1240                                                     NULL);
1241                         filename_free(fn);
1242                     } else {
1243                         Filename *fn = filename_from_str(filename);
1244                         if (type != realtype)
1245                             ret = export_ssh1(fn, type, &state->key,
1246                                               *passphrase ? passphrase : NULL);
1247                         else
1248                             ret = saversakey(fn, &state->key,
1249                                              *passphrase ? passphrase : NULL);
1250                         filename_free(fn);
1251                     }
1252                     if (ret <= 0) {
1253                         MessageBox(hwnd, "Unable to save key file",
1254                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1255                     }
1256                 }
1257                 burnstr(passphrase);
1258             }
1259             break;
1260           case IDC_SAVEPUB:
1261             if (HIWORD(wParam) != BN_CLICKED)
1262                 break;
1263             state =
1264                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1265             if (state->key_exists) {
1266                 char filename[FILENAME_MAX];
1267                 if (prompt_keyfile(hwnd, "Save public key as:",
1268                                    filename, 1, 0)) {
1269                     int ret;
1270                     FILE *fp = fopen(filename, "r");
1271                     if (fp) {
1272                         char *buffer;
1273                         fclose(fp);
1274                         buffer = dupprintf("Overwrite existing file\n%s?",
1275                                            filename);
1276                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1277                                          MB_YESNO | MB_ICONWARNING);
1278                         sfree(buffer);
1279                         if (ret != IDYES)
1280                             break;
1281                     }
1282                     fp = fopen(filename, "w");
1283                     if (!fp) {
1284                         MessageBox(hwnd, "Unable to open key file",
1285                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1286                     } else {
1287                         if (state->ssh2) {
1288                             int bloblen;
1289                             unsigned char *blob;
1290                             blob = state->ssh2key.alg->public_blob
1291                                 (state->ssh2key.data, &bloblen);
1292                             ssh2_write_pubkey(fp, state->ssh2key.comment,
1293                                               blob, bloblen,
1294                                               SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1295                         } else {
1296                             ssh1_write_pubkey(fp, &state->key);
1297                         }
1298                         if (fclose(fp) < 0) {
1299                             MessageBox(hwnd, "Unable to save key file",
1300                                        "PuTTYgen Error", MB_OK | MB_ICONERROR);
1301                         }
1302                     }
1303                 }
1304             }
1305             break;
1306           case IDC_LOAD:
1307           case IDC_IMPORT:
1308             if (HIWORD(wParam) != BN_CLICKED)
1309                 break;
1310             state =
1311                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1312             if (!state->generation_thread_exists) {
1313                 char filename[FILENAME_MAX];
1314                 if (prompt_keyfile(hwnd, "Load private key:",
1315                                    filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1316                     Filename *fn = filename_from_str(filename);
1317                     load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1318                     filename_free(fn);
1319                 }
1320             }
1321             break;
1322         }
1323         return 0;
1324       case WM_DONEKEY:
1325         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1326         state->generation_thread_exists = FALSE;
1327         state->key_exists = TRUE;
1328         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1329                            MAKELPARAM(0, PROGRESSRANGE));
1330         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1331         if (state->ssh2) {
1332             if (state->keytype == DSA) {
1333                 state->ssh2key.data = &state->dsskey;
1334                 state->ssh2key.alg = &ssh_dss;
1335             } else if (state->keytype == ECDSA) {
1336                 state->ssh2key.data = &state->eckey;
1337                 if (state->eckey.publicKey.curve->fieldBits == 256)
1338                     state->ssh2key.alg = &ssh_ecdsa_nistp256;
1339                 else if (state->eckey.publicKey.curve->fieldBits == 384)
1340                     state->ssh2key.alg = &ssh_ecdsa_nistp384;
1341                 else
1342                     state->ssh2key.alg = &ssh_ecdsa_nistp521;
1343             } else if (state->keytype == ED25519) {
1344                 state->ssh2key.data = &state->eckey;
1345                 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1346             } else {
1347                 state->ssh2key.data = &state->key;
1348                 state->ssh2key.alg = &ssh_rsa;
1349             }
1350             state->commentptr = &state->ssh2key.comment;
1351         } else {
1352             state->commentptr = &state->key.comment;
1353         }
1354         /*
1355          * Invent a comment for the key. We'll do this by including
1356          * the date in it. This will be so horrifyingly ugly that
1357          * the user will immediately want to change it, which is
1358          * what we want :-)
1359          */
1360         *state->commentptr = snewn(30, char);
1361         {
1362             struct tm tm;
1363             tm = ltime();
1364             if (state->keytype == DSA)
1365                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1366             else if (state->keytype == ECDSA)
1367                 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1368             else if (state->keytype == ED25519)
1369                 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1370             else
1371                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1372         }
1373
1374         /*
1375          * Now update the key controls with all the key data.
1376          */
1377         {
1378             char *savecomment;
1379             /*
1380              * Blank passphrase, initially. This isn't dangerous,
1381              * because we will warn (Are You Sure?) before allowing
1382              * the user to save an unprotected private key.
1383              */
1384             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1385             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1386             /*
1387              * Set the comment.
1388              */
1389             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1390             /*
1391              * Set the key fingerprint.
1392              */
1393             savecomment = *state->commentptr;
1394             *state->commentptr = NULL;
1395             if (state->ssh2) {
1396                 char *fp;
1397                 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1398                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1399                 sfree(fp);
1400             } else {
1401                 char buf[128];
1402                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1403                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1404             }
1405             *state->commentptr = savecomment;
1406             /*
1407              * Construct a decimal representation of the key, for
1408              * pasting into .ssh/authorized_keys or
1409              * .ssh/authorized_keys2 on a Unix box.
1410              */
1411             if (state->ssh2) {
1412                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1413                               IDC_PKSTATIC, &state->ssh2key);
1414             } else {
1415                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1416                               IDC_PKSTATIC, &state->key);
1417             }
1418         }
1419         /*
1420          * Finally, hide the progress bar and show the key data.
1421          */
1422         ui_set_state(hwnd, state, 2);
1423         break;
1424       case WM_HELP:
1425         {
1426             int id = ((LPHELPINFO)lParam)->iCtrlId;
1427             char *topic = NULL;
1428             switch (id) {
1429               case IDC_GENERATING:
1430               case IDC_PROGRESS:
1431               case IDC_GENSTATIC:
1432               case IDC_GENERATE:
1433                 topic = WINHELP_CTX_puttygen_generate; break;
1434               case IDC_PKSTATIC:
1435               case IDC_KEYDISPLAY:
1436                 topic = WINHELP_CTX_puttygen_pastekey; break;
1437               case IDC_FPSTATIC:
1438               case IDC_FINGERPRINT:
1439                 topic = WINHELP_CTX_puttygen_fingerprint; break;
1440               case IDC_COMMENTSTATIC:
1441               case IDC_COMMENTEDIT:
1442                 topic = WINHELP_CTX_puttygen_comment; break;
1443               case IDC_PASSPHRASE1STATIC:
1444               case IDC_PASSPHRASE1EDIT:
1445               case IDC_PASSPHRASE2STATIC:
1446               case IDC_PASSPHRASE2EDIT:
1447                 topic = WINHELP_CTX_puttygen_passphrase; break;
1448               case IDC_LOADSTATIC:
1449               case IDC_LOAD:
1450                 topic = WINHELP_CTX_puttygen_load; break;
1451               case IDC_SAVESTATIC:
1452               case IDC_SAVE:
1453                 topic = WINHELP_CTX_puttygen_savepriv; break;
1454               case IDC_SAVEPUB:
1455                 topic = WINHELP_CTX_puttygen_savepub; break;
1456               case IDC_TYPESTATIC:
1457               case IDC_KEYSSH1:
1458               case IDC_KEYSSH2RSA:
1459               case IDC_KEYSSH2DSA:
1460               case IDC_KEYSSH2ECDSA:
1461               case IDC_KEYSSH2ED25519:
1462                 topic = WINHELP_CTX_puttygen_keytype; break;
1463               case IDC_BITSSTATIC:
1464               case IDC_BITS:
1465                 topic = WINHELP_CTX_puttygen_bits; break;
1466               case IDC_IMPORT:
1467               case IDC_EXPORT_OPENSSH_AUTO:
1468               case IDC_EXPORT_OPENSSH_NEW:
1469               case IDC_EXPORT_SSHCOM:
1470                 topic = WINHELP_CTX_puttygen_conversions; break;
1471             }
1472             if (topic) {
1473                 launch_help(hwnd, topic);
1474             } else {
1475                 MessageBeep(0);
1476             }
1477         }
1478         break;
1479       case WM_CLOSE:
1480         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1481         sfree(state);
1482         quit_help(hwnd);
1483         EndDialog(hwnd, 1);
1484         return 0;
1485     }
1486     return 0;
1487 }
1488
1489 void cleanup_exit(int code)
1490 {
1491     shutdown_help();
1492     exit(code);
1493 }
1494
1495 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1496 {
1497     int argc;
1498     char **argv;
1499     int ret;
1500
1501     InitCommonControls();
1502     hinst = inst;
1503     hwnd = NULL;
1504
1505     /*
1506      * See if we can find our Help file.
1507      */
1508     init_help();
1509
1510     split_into_argv(cmdline, &argc, &argv, NULL);
1511
1512     if (argc > 0) {
1513         if (!strcmp(argv[0], "-pgpfp")) {
1514             pgp_fingerprints();
1515             exit(1);
1516         } else {
1517             /*
1518              * Assume the first argument to be a private key file, and
1519              * attempt to load it.
1520              */
1521             cmdline_keyfile = argv[0];
1522         }
1523     }
1524
1525     random_ref();
1526     ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1527
1528     cleanup_exit(ret);
1529     return ret;                        /* just in case optimiser complains */
1530 }