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