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