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