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