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