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