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