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