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