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