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