]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgen.c
Giant const-correctness patch of doom!
[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, IDC_KEYSSH1, IDC_KEYSSH2DSA,
972                                      LOWORD(wParam));
973                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
974                                    LOWORD(wParam), MF_BYCOMMAND);
975                 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
976                                  LOWORD(wParam));
977                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
978                                    IDC_KEYSSH2ECDSA,
979                                    LOWORD(wParam), MF_BYCOMMAND);
980             }
981             break;
982           case IDC_QUIT:
983             PostMessage(hwnd, WM_CLOSE, 0, 0);
984             break;
985           case IDC_COMMENTEDIT:
986             if (HIWORD(wParam) == EN_CHANGE) {
987                 state = (struct MainDlgState *)
988                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
989                 if (state->key_exists) {
990                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
991                     int len = GetWindowTextLength(editctl);
992                     if (*state->commentptr)
993                         sfree(*state->commentptr);
994                     *state->commentptr = snewn(len + 1, char);
995                     GetWindowText(editctl, *state->commentptr, len + 1);
996                     if (state->ssh2) {
997                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
998                                       &state->ssh2key);
999                     } else {
1000                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1001                                       &state->key);
1002                     }
1003                 }
1004             }
1005             break;
1006           case IDC_ABOUT:
1007             EnableWindow(hwnd, 0);
1008             DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1009             EnableWindow(hwnd, 1);
1010             SetActiveWindow(hwnd);
1011             return 0;
1012           case IDC_GIVEHELP:
1013             if (HIWORD(wParam) == BN_CLICKED ||
1014                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1015                 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1016             }
1017             return 0;
1018           case IDC_GENERATE:
1019             if (HIWORD(wParam) != BN_CLICKED &&
1020                 HIWORD(wParam) != BN_DOUBLECLICKED)
1021                 break;
1022             state =
1023                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1024             if (!state->generation_thread_exists) {
1025                 BOOL ok;
1026                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1027                 if (!ok)
1028                     state->keysize = DEFAULT_KEYSIZE;
1029                 /* If we ever introduce a new key type, check it here! */
1030                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1031                 state->keytype = RSA;
1032                 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1033                     state->keytype = DSA;
1034                 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1035                     state->keytype = ECDSA;
1036                 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1037                     state->keytype = ED25519;
1038                 }
1039                 if (state->keysize < 256) {
1040                     int ret = MessageBox(hwnd,
1041                                          "PuTTYgen will not generate a key"
1042                                          " smaller than 256 bits.\n"
1043                                          "Key length reset to 256. Continue?",
1044                                          "PuTTYgen Warning",
1045                                          MB_ICONWARNING | MB_OKCANCEL);
1046                     if (ret != IDOK)
1047                         break;
1048                     state->keysize = 256;
1049                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1050                 }
1051                 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1052                                                  state->keysize == 384 ||
1053                                                  state->keysize == 521)) {
1054                     int ret = MessageBox(hwnd,
1055                                          "Only 256, 384 and 521 bit elliptic"
1056                                          " curves are supported.\n"
1057                                          "Key length reset to 256. Continue?",
1058                                          "PuTTYgen Warning",
1059                                          MB_ICONWARNING | MB_OKCANCEL);
1060                     if (ret != IDOK)
1061                         break;
1062                     state->keysize = 256;
1063                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1064                 }
1065                 if (state->keytype == ED25519 && state->keysize != 256) {
1066                     int ret = MessageBox(hwnd,
1067                                          "Only 256 bit Edwards elliptic"
1068                                          " curves are supported.\n"
1069                                          "Key length reset to 256. Continue?",
1070                                          "PuTTYgen Warning",
1071                                          MB_ICONWARNING | MB_OKCANCEL);
1072                     if (ret != IDOK)
1073                         break;
1074                     state->keysize = 256;
1075                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1076                 }
1077                 ui_set_state(hwnd, state, 1);
1078                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1079                 state->key_exists = FALSE;
1080                 state->collecting_entropy = TRUE;
1081
1082                 /*
1083                  * My brief statistical tests on mouse movements
1084                  * suggest that there are about 2.5 bits of
1085                  * randomness in the x position, 2.5 in the y
1086                  * position, and 1.7 in the message time, making
1087                  * 5.7 bits of unpredictability per mouse movement.
1088                  * However, other people have told me it's far less
1089                  * than that, so I'm going to be stupidly cautious
1090                  * and knock that down to a nice round 2. With this
1091                  * method, we require two words per mouse movement,
1092                  * so with 2 bits per mouse movement we expect 2
1093                  * bits every 2 words.
1094                  */
1095                 state->entropy_required = (state->keysize / 2) * 2;
1096                 state->entropy_got = 0;
1097                 state->entropy_size = (state->entropy_required *
1098                                        sizeof(unsigned));
1099                 state->entropy = snewn(state->entropy_required, unsigned);
1100
1101                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1102                                    MAKELPARAM(0, state->entropy_required));
1103                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1104             }
1105             break;
1106           case IDC_SAVE:
1107           case IDC_EXPORT_OPENSSH_AUTO:
1108           case IDC_EXPORT_OPENSSH_NEW:
1109           case IDC_EXPORT_SSHCOM:
1110             if (HIWORD(wParam) != BN_CLICKED)
1111                 break;
1112             state =
1113                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1114             if (state->key_exists) {
1115                 char filename[FILENAME_MAX];
1116                 char *passphrase, *passphrase2;
1117                 int type, realtype;
1118
1119                 if (state->ssh2)
1120                     realtype = SSH_KEYTYPE_SSH2;
1121                 else
1122                     realtype = SSH_KEYTYPE_SSH1;
1123
1124                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1125                     type = SSH_KEYTYPE_OPENSSH_AUTO;
1126                 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1127                     type = SSH_KEYTYPE_OPENSSH_NEW;
1128                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1129                     type = SSH_KEYTYPE_SSHCOM;
1130                 else
1131                     type = realtype;
1132
1133                 if (type != realtype &&
1134                     import_target_type(type) != realtype) {
1135                     char msg[256];
1136                     sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1137                             " format", (state->ssh2 ? 2 : 1),
1138                             (state->ssh2 ? 1 : 2));
1139                     MessageBox(hwnd, msg,
1140                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1141                     break;
1142                 }
1143
1144                 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1145                 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1146                 if (strcmp(passphrase, passphrase2)) {
1147                     MessageBox(hwnd,
1148                                "The two passphrases given do not match.",
1149                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1150                     burnstr(passphrase);
1151                     burnstr(passphrase2);
1152                     break;
1153                 }
1154                 burnstr(passphrase2);
1155                 if (!*passphrase) {
1156                     int ret;
1157                     ret = MessageBox(hwnd,
1158                                      "Are you sure you want to save this key\n"
1159                                      "without a passphrase to protect it?",
1160                                      "PuTTYgen Warning",
1161                                      MB_YESNO | MB_ICONWARNING);
1162                     if (ret != IDYES) {
1163                         burnstr(passphrase);
1164                         break;
1165                     }
1166                 }
1167                 if (prompt_keyfile(hwnd, "Save private key as:",
1168                                    filename, 1, (type == realtype))) {
1169                     int ret;
1170                     FILE *fp = fopen(filename, "r");
1171                     if (fp) {
1172                         char *buffer;
1173                         fclose(fp);
1174                         buffer = dupprintf("Overwrite existing file\n%s?",
1175                                            filename);
1176                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1177                                          MB_YESNO | MB_ICONWARNING);
1178                         sfree(buffer);
1179                         if (ret != IDYES) {
1180                             burnstr(passphrase);
1181                             break;
1182                         }
1183                     }
1184
1185                     if (state->ssh2) {
1186                         Filename *fn = filename_from_str(filename);
1187                         if (type != realtype)
1188                             ret = export_ssh2(fn, type, &state->ssh2key,
1189                                               *passphrase ? passphrase : NULL);
1190                         else
1191                             ret = ssh2_save_userkey(fn, &state->ssh2key,
1192                                                     *passphrase ? passphrase :
1193                                                     NULL);
1194                         filename_free(fn);
1195                     } else {
1196                         Filename *fn = filename_from_str(filename);
1197                         if (type != realtype)
1198                             ret = export_ssh1(fn, type, &state->key,
1199                                               *passphrase ? passphrase : NULL);
1200                         else
1201                             ret = saversakey(fn, &state->key,
1202                                              *passphrase ? passphrase : NULL);
1203                         filename_free(fn);
1204                     }
1205                     if (ret <= 0) {
1206                         MessageBox(hwnd, "Unable to save key file",
1207                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1208                     }
1209                 }
1210                 burnstr(passphrase);
1211             }
1212             break;
1213           case IDC_SAVEPUB:
1214             if (HIWORD(wParam) != BN_CLICKED)
1215                 break;
1216             state =
1217                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1218             if (state->key_exists) {
1219                 char filename[FILENAME_MAX];
1220                 if (prompt_keyfile(hwnd, "Save public key as:",
1221                                    filename, 1, 0)) {
1222                     int ret;
1223                     FILE *fp = fopen(filename, "r");
1224                     if (fp) {
1225                         char *buffer;
1226                         fclose(fp);
1227                         buffer = dupprintf("Overwrite existing file\n%s?",
1228                                            filename);
1229                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1230                                          MB_YESNO | MB_ICONWARNING);
1231                         sfree(buffer);
1232                         if (ret != IDYES)
1233                             break;
1234                     }
1235                     fp = fopen(filename, "w");
1236                     if (!fp) {
1237                         MessageBox(hwnd, "Unable to open key file",
1238                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1239                     } else {
1240                         if (state->ssh2) {
1241                             int bloblen;
1242                             unsigned char *blob;
1243                             blob = state->ssh2key.alg->public_blob
1244                                 (state->ssh2key.data, &bloblen);
1245                             ssh2_write_pubkey(fp, state->ssh2key.comment,
1246                                               blob, bloblen,
1247                                               SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1248                         } else {
1249                             ssh1_write_pubkey(fp, &state->key);
1250                         }
1251                         if (fclose(fp) < 0) {
1252                             MessageBox(hwnd, "Unable to save key file",
1253                                        "PuTTYgen Error", MB_OK | MB_ICONERROR);
1254                         }
1255                     }
1256                 }
1257             }
1258             break;
1259           case IDC_LOAD:
1260           case IDC_IMPORT:
1261             if (HIWORD(wParam) != BN_CLICKED)
1262                 break;
1263             state =
1264                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1265             if (!state->generation_thread_exists) {
1266                 char filename[FILENAME_MAX];
1267                 if (prompt_keyfile(hwnd, "Load private key:",
1268                                    filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1269                     Filename *fn = filename_from_str(filename);
1270                     load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1271                     filename_free(fn);
1272                 }
1273             }
1274             break;
1275         }
1276         return 0;
1277       case WM_DONEKEY:
1278         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1279         state->generation_thread_exists = FALSE;
1280         state->key_exists = TRUE;
1281         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1282                            MAKELPARAM(0, PROGRESSRANGE));
1283         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1284         if (state->ssh2) {
1285             if (state->keytype == DSA) {
1286                 state->ssh2key.data = &state->dsskey;
1287                 state->ssh2key.alg = &ssh_dss;
1288             } else if (state->keytype == ECDSA) {
1289                 state->ssh2key.data = &state->eckey;
1290                 state->ssh2key.alg = state->eckey.signalg;
1291             } else if (state->keytype == ED25519) {
1292                 state->ssh2key.data = &state->eckey;
1293                 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1294             } else {
1295                 state->ssh2key.data = &state->key;
1296                 state->ssh2key.alg = &ssh_rsa;
1297             }
1298             state->commentptr = &state->ssh2key.comment;
1299         } else {
1300             state->commentptr = &state->key.comment;
1301         }
1302         /*
1303          * Invent a comment for the key. We'll do this by including
1304          * the date in it. This will be so horrifyingly ugly that
1305          * the user will immediately want to change it, which is
1306          * what we want :-)
1307          */
1308         *state->commentptr = snewn(30, char);
1309         {
1310             struct tm tm;
1311             tm = ltime();
1312             if (state->keytype == DSA)
1313                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1314             else if (state->keytype == ECDSA)
1315                 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1316             else if (state->keytype == ED25519)
1317                 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1318             else
1319                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1320         }
1321
1322         /*
1323          * Now update the key controls with all the key data.
1324          */
1325         {
1326             char *savecomment;
1327             /*
1328              * Blank passphrase, initially. This isn't dangerous,
1329              * because we will warn (Are You Sure?) before allowing
1330              * the user to save an unprotected private key.
1331              */
1332             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1333             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1334             /*
1335              * Set the comment.
1336              */
1337             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1338             /*
1339              * Set the key fingerprint.
1340              */
1341             savecomment = *state->commentptr;
1342             *state->commentptr = NULL;
1343             if (state->ssh2) {
1344                 char *fp;
1345                 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1346                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1347                 sfree(fp);
1348             } else {
1349                 char buf[128];
1350                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1351                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1352             }
1353             *state->commentptr = savecomment;
1354             /*
1355              * Construct a decimal representation of the key, for
1356              * pasting into .ssh/authorized_keys or
1357              * .ssh/authorized_keys2 on a Unix box.
1358              */
1359             if (state->ssh2) {
1360                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1361                               IDC_PKSTATIC, &state->ssh2key);
1362             } else {
1363                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1364                               IDC_PKSTATIC, &state->key);
1365             }
1366         }
1367         /*
1368          * Finally, hide the progress bar and show the key data.
1369          */
1370         ui_set_state(hwnd, state, 2);
1371         break;
1372       case WM_HELP:
1373         {
1374             int id = ((LPHELPINFO)lParam)->iCtrlId;
1375             const char *topic = NULL;
1376             switch (id) {
1377               case IDC_GENERATING:
1378               case IDC_PROGRESS:
1379               case IDC_GENSTATIC:
1380               case IDC_GENERATE:
1381                 topic = WINHELP_CTX_puttygen_generate; break;
1382               case IDC_PKSTATIC:
1383               case IDC_KEYDISPLAY:
1384                 topic = WINHELP_CTX_puttygen_pastekey; break;
1385               case IDC_FPSTATIC:
1386               case IDC_FINGERPRINT:
1387                 topic = WINHELP_CTX_puttygen_fingerprint; break;
1388               case IDC_COMMENTSTATIC:
1389               case IDC_COMMENTEDIT:
1390                 topic = WINHELP_CTX_puttygen_comment; break;
1391               case IDC_PASSPHRASE1STATIC:
1392               case IDC_PASSPHRASE1EDIT:
1393               case IDC_PASSPHRASE2STATIC:
1394               case IDC_PASSPHRASE2EDIT:
1395                 topic = WINHELP_CTX_puttygen_passphrase; break;
1396               case IDC_LOADSTATIC:
1397               case IDC_LOAD:
1398                 topic = WINHELP_CTX_puttygen_load; break;
1399               case IDC_SAVESTATIC:
1400               case IDC_SAVE:
1401                 topic = WINHELP_CTX_puttygen_savepriv; break;
1402               case IDC_SAVEPUB:
1403                 topic = WINHELP_CTX_puttygen_savepub; break;
1404               case IDC_TYPESTATIC:
1405               case IDC_KEYSSH1:
1406               case IDC_KEYSSH2RSA:
1407               case IDC_KEYSSH2DSA:
1408               case IDC_KEYSSH2ECDSA:
1409               case IDC_KEYSSH2ED25519:
1410                 topic = WINHELP_CTX_puttygen_keytype; break;
1411               case IDC_BITSSTATIC:
1412               case IDC_BITS:
1413                 topic = WINHELP_CTX_puttygen_bits; break;
1414               case IDC_IMPORT:
1415               case IDC_EXPORT_OPENSSH_AUTO:
1416               case IDC_EXPORT_OPENSSH_NEW:
1417               case IDC_EXPORT_SSHCOM:
1418                 topic = WINHELP_CTX_puttygen_conversions; break;
1419             }
1420             if (topic) {
1421                 launch_help(hwnd, topic);
1422             } else {
1423                 MessageBeep(0);
1424             }
1425         }
1426         break;
1427       case WM_CLOSE:
1428         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1429         sfree(state);
1430         quit_help(hwnd);
1431         EndDialog(hwnd, 1);
1432         return 0;
1433     }
1434     return 0;
1435 }
1436
1437 void cleanup_exit(int code)
1438 {
1439     shutdown_help();
1440     exit(code);
1441 }
1442
1443 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1444 {
1445     int argc;
1446     char **argv;
1447     int ret;
1448
1449     InitCommonControls();
1450     hinst = inst;
1451     hwnd = NULL;
1452
1453     /*
1454      * See if we can find our Help file.
1455      */
1456     init_help();
1457
1458     split_into_argv(cmdline, &argc, &argv, NULL);
1459
1460     if (argc > 0) {
1461         if (!strcmp(argv[0], "-pgpfp")) {
1462             pgp_fingerprints();
1463             exit(1);
1464         } else {
1465             /*
1466              * Assume the first argument to be a private key file, and
1467              * attempt to load it.
1468              */
1469             cmdline_keyfile = argv[0];
1470         }
1471     }
1472
1473     random_ref();
1474     ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1475
1476     cleanup_exit(ret);
1477     return ret;                        /* just in case optimiser complains */
1478 }