]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgen.c
Use readonly edit controls in some Windows dialogs.
[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             SetDlgItemText(hwnd, 1000,
257         "Copyright 1997-2015 Simon Tatham.\r\n\r\n"
258
259         "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
260         "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
261         "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
262         "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\r\n\r\n"
263
264         "Permission is hereby granted, free of charge, to any person "
265         "obtaining a copy of this software and associated documentation "
266         "files (the ""Software""), to deal in the Software without restriction, "
267         "including without limitation the rights to use, copy, modify, merge, "
268         "publish, distribute, sublicense, and/or sell copies of the Software, "
269         "and to permit persons to whom the Software is furnished to do so, "
270         "subject to the following conditions:\r\n\r\n"
271
272         "The above copyright notice and this permission notice shall be "
273         "included in all copies or substantial portions of the Software.\r\n\r\n"
274
275         "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
276         "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
277         "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
278         "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
279         "PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE "
280         "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
281         "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
282         "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
283         "CONNECTION WITH THE SOFTWARE OR THE USE OR "
284         "OTHER DEALINGS IN THE SOFTWARE."
285 );
286         return 1;
287       case WM_COMMAND:
288         switch (LOWORD(wParam)) {
289           case IDOK:
290           case IDCANCEL:
291             EndDialog(hwnd, 1);
292             return 0;
293         }
294         return 0;
295       case WM_CLOSE:
296         EndDialog(hwnd, 1);
297         return 0;
298     }
299     return 0;
300 }
301
302 /*
303  * Dialog-box function for the About box.
304  */
305 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
306                               WPARAM wParam, LPARAM lParam)
307 {
308     switch (msg) {
309       case WM_INITDIALOG:
310         /*
311          * Centre the window.
312          */
313         {                              /* centre the window */
314             RECT rs, rd;
315             HWND hw;
316
317             hw = GetDesktopWindow();
318             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
319                 MoveWindow(hwnd,
320                            (rs.right + rs.left + rd.left - rd.right) / 2,
321                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
322                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
323         }
324
325         {
326             char *text = dupprintf
327                 ("Pageant\r\n\r\n%s\r\n\r\n%s",
328                  ver,
329                  "\251 1997-2015 Simon Tatham. All rights reserved.");
330             SetDlgItemText(hwnd, 1000, text);
331             sfree(text);
332         }
333         return 1;
334       case WM_COMMAND:
335         switch (LOWORD(wParam)) {
336           case IDOK:
337           case IDCANCEL:
338             EndDialog(hwnd, 1);
339             return 0;
340           case 101:
341             EnableWindow(hwnd, 0);
342             DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
343             EnableWindow(hwnd, 1);
344             SetActiveWindow(hwnd);
345             return 0;
346         }
347         return 0;
348       case WM_CLOSE:
349         EndDialog(hwnd, 1);
350         return 0;
351     }
352     return 0;
353 }
354
355 /*
356  * Thread to generate a key.
357  */
358 struct rsa_key_thread_params {
359     HWND progressbar;                  /* notify this with progress */
360     HWND dialog;                       /* notify this on completion */
361     int keysize;                       /* bits in key */
362     int is_dsa;
363     struct RSAKey *key;
364     struct dss_key *dsskey;
365 };
366 static DWORD WINAPI generate_rsa_key_thread(void *param)
367 {
368     struct rsa_key_thread_params *params =
369         (struct rsa_key_thread_params *) param;
370     struct progress prog;
371     prog.progbar = params->progressbar;
372
373     progress_update(&prog, PROGFN_INITIALISE, 0, 0);
374
375     if (params->is_dsa)
376         dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
377     else
378         rsa_generate(params->key, params->keysize, progress_update, &prog);
379
380     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
381
382     sfree(params);
383     return 0;
384 }
385
386 struct MainDlgState {
387     int collecting_entropy;
388     int generation_thread_exists;
389     int key_exists;
390     int entropy_got, entropy_required, entropy_size;
391     int keysize;
392     int ssh2, is_dsa;
393     char **commentptr;                 /* points to key.comment or ssh2key.comment */
394     struct ssh2_userkey ssh2key;
395     unsigned *entropy;
396     struct RSAKey key;
397     struct dss_key dsskey;
398     HMENU filemenu, keymenu, cvtmenu;
399 };
400
401 static void hidemany(HWND hwnd, const int *ids, int hideit)
402 {
403     while (*ids) {
404         ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
405     }
406 }
407
408 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
409 {
410     char *buffer;
411     char *dec1, *dec2;
412
413     dec1 = bignum_decimal(key->exponent);
414     dec2 = bignum_decimal(key->modulus);
415     buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
416                        dec1, dec2, key->comment);
417     SetDlgItemText(hwnd, id, buffer);
418     SetDlgItemText(hwnd, idstatic,
419                    "&Public key for pasting into authorized_keys file:");
420     sfree(dec1);
421     sfree(dec2);
422     sfree(buffer);
423 }
424
425 static void setupbigedit2(HWND hwnd, int id, int idstatic,
426                           struct ssh2_userkey *key)
427 {
428     unsigned char *pub_blob;
429     char *buffer, *p;
430     int pub_len;
431     int i;
432
433     pub_blob = key->alg->public_blob(key->data, &pub_len);
434     buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
435                    strlen(key->comment) + 3, char);
436     strcpy(buffer, key->alg->name);
437     p = buffer + strlen(buffer);
438     *p++ = ' ';
439     i = 0;
440     while (i < pub_len) {
441         int n = (pub_len - i < 3 ? pub_len - i : 3);
442         base64_encode_atom(pub_blob + i, n, p);
443         i += n;
444         p += 4;
445     }
446     *p++ = ' ';
447     strcpy(p, key->comment);
448     SetDlgItemText(hwnd, id, buffer);
449     SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
450                    "OpenSSH authorized_keys file:");
451     sfree(pub_blob);
452     sfree(buffer);
453 }
454
455 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
456 {
457     char *dec1, *dec2;
458     FILE *fp;
459
460     fp = fopen(filename, "wb");
461     if (!fp)
462         return 0;
463     dec1 = bignum_decimal(key->exponent);
464     dec2 = bignum_decimal(key->modulus);
465     fprintf(fp, "%d %s %s %s\n",
466             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
467     fclose(fp);
468     sfree(dec1);
469     sfree(dec2);
470     return 1;
471 }
472
473 /*
474  * Warn about the obsolescent key file format.
475  */
476 void old_keyfile_warning(void)
477 {
478     static const char mbtitle[] = "PuTTY Key File Warning";
479     static const char message[] =
480         "You are loading an SSH-2 private key which has an\n"
481         "old version of the file format. This means your key\n"
482         "file is not fully tamperproof. Future versions of\n"
483         "PuTTY may stop supporting this private key format,\n"
484         "so we recommend you convert your key to the new\n"
485         "format.\n"
486         "\n"
487         "Once the key is loaded into PuTTYgen, you can perform\n"
488         "this conversion simply by saving it again.";
489
490     MessageBox(NULL, message, mbtitle, MB_OK);
491 }
492
493 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
494 {
495     unsigned char *pub_blob;
496     char *p;
497     int pub_len;
498     int i, column;
499     FILE *fp;
500
501     pub_blob = key->alg->public_blob(key->data, &pub_len);
502
503     fp = fopen(filename, "wb");
504     if (!fp)
505         return 0;
506
507     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
508
509     fprintf(fp, "Comment: \"");
510     for (p = key->comment; *p; p++) {
511         if (*p == '\\' || *p == '\"')
512             fputc('\\', fp);
513         fputc(*p, fp);
514     }
515     fprintf(fp, "\"\n");
516
517     i = 0;
518     column = 0;
519     while (i < pub_len) {
520         char buf[5];
521         int n = (pub_len - i < 3 ? pub_len - i : 3);
522         base64_encode_atom(pub_blob + i, n, buf);
523         i += n;
524         buf[4] = '\0';
525         fputs(buf, fp);
526         if (++column >= 16) {
527             fputc('\n', fp);
528             column = 0;
529         }
530     }
531     if (column > 0)
532         fputc('\n', fp);
533     
534     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
535     fclose(fp);
536     sfree(pub_blob);
537     return 1;
538 }
539
540 enum {
541     controlidstart = 100,
542     IDC_QUIT,
543     IDC_TITLE,
544     IDC_BOX_KEY,
545     IDC_NOKEY,
546     IDC_GENERATING,
547     IDC_PROGRESS,
548     IDC_PKSTATIC, IDC_KEYDISPLAY,
549     IDC_FPSTATIC, IDC_FINGERPRINT,
550     IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
551     IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
552     IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
553     IDC_BOX_ACTIONS,
554     IDC_GENSTATIC, IDC_GENERATE,
555     IDC_LOADSTATIC, IDC_LOAD,
556     IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
557     IDC_BOX_PARAMS,
558     IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
559     IDC_BITSSTATIC, IDC_BITS,
560     IDC_ABOUT,
561     IDC_GIVEHELP,
562     IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
563 };
564
565 static const int nokey_ids[] = { IDC_NOKEY, 0 };
566 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
567 static const int gotkey_ids[] = {
568     IDC_PKSTATIC, IDC_KEYDISPLAY,
569     IDC_FPSTATIC, IDC_FINGERPRINT,
570     IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
571     IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
572     IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
573 };
574
575 /*
576  * Small UI helper function to switch the state of the main dialog
577  * by enabling and disabling controls and menu items.
578  */
579 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
580 {
581     int type;
582
583     switch (status) {
584       case 0:                          /* no key */
585         hidemany(hwnd, nokey_ids, FALSE);
586         hidemany(hwnd, generating_ids, TRUE);
587         hidemany(hwnd, gotkey_ids, TRUE);
588         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
589         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
590         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
591         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
592         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
593         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
594         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
595         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
596         EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
597         EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
598         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
599         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
600         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
601         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
602         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
603         EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
604         EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
605                        MF_GRAYED|MF_BYCOMMAND);
606         EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
607                        MF_GRAYED|MF_BYCOMMAND);
608         break;
609       case 1:                          /* generating key */
610         hidemany(hwnd, nokey_ids, TRUE);
611         hidemany(hwnd, generating_ids, FALSE);
612         hidemany(hwnd, gotkey_ids, TRUE);
613         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
614         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
615         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
616         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
617         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
618         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
619         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
620         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
621         EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
622         EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
623         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
624         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
625         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
626         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
627         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
628         EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
629         EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
630                        MF_GRAYED|MF_BYCOMMAND);
631         EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
632                        MF_GRAYED|MF_BYCOMMAND);
633         break;
634       case 2:
635         hidemany(hwnd, nokey_ids, TRUE);
636         hidemany(hwnd, generating_ids, TRUE);
637         hidemany(hwnd, gotkey_ids, FALSE);
638         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
639         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
640         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
641         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
642         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
643         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
644         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
645         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
646         EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
647         EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
648         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
649         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
650         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
651         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
652         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
653         EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
654         /*
655          * Enable export menu items if and only if the key type
656          * supports this kind of export.
657          */
658         type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
659 #define do_export_menuitem(x,y) \
660     EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
661                        (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
662         do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
663         do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
664 #undef do_export_menuitem
665         break;
666     }
667 }
668
669 void load_key_file(HWND hwnd, struct MainDlgState *state,
670                    Filename *filename, int was_import_cmd)
671 {
672     char *passphrase;
673     int needs_pass;
674     int type, realtype;
675     int ret;
676     const char *errmsg = NULL;
677     char *comment;
678     struct RSAKey newkey1;
679     struct ssh2_userkey *newkey2 = NULL;
680
681     type = realtype = key_type(filename);
682     if (type != SSH_KEYTYPE_SSH1 &&
683         type != SSH_KEYTYPE_SSH2 &&
684         !import_possible(type)) {
685         char *msg = dupprintf("Couldn't load private key (%s)",
686                               key_type_to_str(type));
687         message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
688                     HELPCTXID(errors_cantloadkey));
689         sfree(msg);
690         return;
691     }
692
693     if (type != SSH_KEYTYPE_SSH1 &&
694         type != SSH_KEYTYPE_SSH2) {
695         realtype = type;
696         type = import_target_type(type);
697     }
698
699     comment = NULL;
700     passphrase = NULL;
701     if (realtype == SSH_KEYTYPE_SSH1)
702         needs_pass = rsakey_encrypted(filename, &comment);
703     else if (realtype == SSH_KEYTYPE_SSH2)
704         needs_pass = ssh2_userkey_encrypted(filename, &comment);
705     else
706         needs_pass = import_encrypted(filename, realtype, &comment);
707     do {
708         burnstr(passphrase);
709         passphrase = NULL;
710
711         if (needs_pass) {
712             int dlgret;
713             struct PassphraseProcStruct pps;
714             pps.passphrase = &passphrase;
715             pps.comment = comment;
716             dlgret = DialogBoxParam(hinst,
717                                     MAKEINTRESOURCE(210),
718                                     NULL, PassphraseProc,
719                                     (LPARAM) &pps);
720             if (!dlgret) {
721                 ret = -2;
722                 break;
723             }
724             assert(passphrase != NULL);
725         } else
726             passphrase = dupstr("");
727         if (type == SSH_KEYTYPE_SSH1) {
728             if (realtype == type)
729                 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
730             else
731                 ret = import_ssh1(filename, realtype, &newkey1,
732                                   passphrase, &errmsg);
733         } else {
734             if (realtype == type)
735                 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
736             else
737                 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
738             if (newkey2 == SSH2_WRONG_PASSPHRASE)
739                 ret = -1;
740             else if (!newkey2)
741                 ret = 0;
742             else
743                 ret = 1;
744         }
745     } while (ret == -1);
746     if (comment)
747         sfree(comment);
748     if (ret == 0) {
749         char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
750         message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
751                     HELPCTXID(errors_cantloadkey));
752         sfree(msg);
753     } else if (ret == 1) {
754         /*
755          * Now update the key controls with all the
756          * key data.
757          */
758         {
759             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
760                            passphrase);
761             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
762                            passphrase);
763             if (type == SSH_KEYTYPE_SSH1) {
764                 char buf[128];
765                 char *savecomment;
766
767                 state->ssh2 = FALSE;
768                 state->commentptr = &state->key.comment;
769                 state->key = newkey1;
770
771                 /*
772                  * Set the key fingerprint.
773                  */
774                 savecomment = state->key.comment;
775                 state->key.comment = NULL;
776                 rsa_fingerprint(buf, sizeof(buf),
777                                 &state->key);
778                 state->key.comment = savecomment;
779
780                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
781                 /*
782                  * Construct a decimal representation
783                  * of the key, for pasting into
784                  * .ssh/authorized_keys on a Unix box.
785                  */
786                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
787                               IDC_PKSTATIC, &state->key);
788             } else {
789                 char *fp;
790                 char *savecomment;
791
792                 state->ssh2 = TRUE;
793                 state->commentptr =
794                     &state->ssh2key.comment;
795                 state->ssh2key = *newkey2;      /* structure copy */
796                 sfree(newkey2);
797
798                 savecomment = state->ssh2key.comment;
799                 state->ssh2key.comment = NULL;
800                 fp =
801                     state->ssh2key.alg->
802                     fingerprint(state->ssh2key.data);
803                 state->ssh2key.comment = savecomment;
804
805                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
806                 sfree(fp);
807
808                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
809                               IDC_PKSTATIC, &state->ssh2key);
810             }
811             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
812                            *state->commentptr);
813         }
814         /*
815          * Finally, hide the progress bar and show
816          * the key data.
817          */
818         ui_set_state(hwnd, state, 2);
819         state->key_exists = TRUE;
820
821         /*
822          * If the user has imported a foreign key
823          * using the Load command, let them know.
824          * If they've used the Import command, be
825          * silent.
826          */
827         if (realtype != type && !was_import_cmd) {
828             char msg[512];
829             sprintf(msg, "Successfully imported foreign key\n"
830                     "(%s).\n"
831                     "To use this key with PuTTY, you need to\n"
832                     "use the \"Save private key\" command to\n"
833                     "save it in PuTTY's own format.",
834                     key_type_to_str(realtype));
835             MessageBox(NULL, msg, "PuTTYgen Notice",
836                        MB_OK | MB_ICONINFORMATION);
837         }
838     }
839     burnstr(passphrase);
840 }
841
842 /*
843  * Dialog-box function for the main PuTTYgen dialog box.
844  */
845 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
846                                 WPARAM wParam, LPARAM lParam)
847 {
848     static const char generating_msg[] =
849         "Please wait while a key is generated...";
850     static const char entropy_msg[] =
851         "Please generate some randomness by moving the mouse over the blank area.";
852     struct MainDlgState *state;
853
854     switch (msg) {
855       case WM_INITDIALOG:
856         if (has_help())
857             SetWindowLongPtr(hwnd, GWL_EXSTYLE,
858                              GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
859                              WS_EX_CONTEXTHELP);
860         else {
861             /*
862              * If we add a Help button, this is where we destroy it
863              * if the help file isn't present.
864              */
865         }
866         SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
867                     (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
868
869         state = snew(struct MainDlgState);
870         state->generation_thread_exists = FALSE;
871         state->collecting_entropy = FALSE;
872         state->entropy = NULL;
873         state->key_exists = FALSE;
874         SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
875         {
876             HMENU menu, menu1;
877
878             menu = CreateMenu();
879
880             menu1 = CreateMenu();
881             AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
882             AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
883             AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
884             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
885             AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
886             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
887             state->filemenu = menu1;
888
889             menu1 = CreateMenu();
890             AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
891             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
892             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
893             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
894             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
895             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
896             state->keymenu = menu1;
897
898             menu1 = CreateMenu();
899             AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
900             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
901             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
902                        "Export &OpenSSH key");
903             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
904                        "Export &ssh.com key");
905             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
906                        "Con&versions");
907             state->cvtmenu = menu1;
908
909             menu1 = CreateMenu();
910             AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
911             if (has_help())
912                 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
913             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
914
915             SetMenu(hwnd, menu);
916         }
917
918         /*
919          * Centre the window.
920          */
921         {                              /* centre the window */
922             RECT rs, rd;
923             HWND hw;
924
925             hw = GetDesktopWindow();
926             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
927                 MoveWindow(hwnd,
928                            (rs.right + rs.left + rd.left - rd.right) / 2,
929                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
930                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
931         }
932
933         {
934             struct ctlpos cp, cp2;
935
936             /* Accelerators used: acglops1rbd */
937
938             ctlposinit(&cp, hwnd, 4, 4, 4);
939             beginbox(&cp, "Key", IDC_BOX_KEY);
940             cp2 = cp;
941             statictext(&cp2, "No key.", 1, IDC_NOKEY);
942             cp2 = cp;
943             statictext(&cp2, "", 1, IDC_GENERATING);
944             progressbar(&cp2, IDC_PROGRESS);
945             bigeditctrl(&cp,
946                         "&Public key for pasting into authorized_keys file:",
947                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
948             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
949             staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
950                        IDC_FINGERPRINT, 75);
951             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
952                                0);
953             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
954                        IDC_COMMENTEDIT, 75);
955             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
956                            IDC_PASSPHRASE1EDIT, 75);
957             staticpassedit(&cp, "C&onfirm passphrase:",
958                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
959             endbox(&cp);
960             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
961             staticbtn(&cp, "Generate a public/private key pair",
962                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
963             staticbtn(&cp, "Load an existing private key file",
964                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
965             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
966                        "Save p&ublic key", IDC_SAVEPUB,
967                        "&Save private key", IDC_SAVE);
968             endbox(&cp);
969             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
970             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
971                       "SSH-&1 (RSA)", IDC_KEYSSH1,
972                       "SSH-2 &RSA", IDC_KEYSSH2RSA,
973                       "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
974             staticedit(&cp, "Number of &bits in a generated key:",
975                        IDC_BITSSTATIC, IDC_BITS, 20);
976             endbox(&cp);
977         }
978         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
979         CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
980                            IDC_KEYSSH2RSA, MF_BYCOMMAND);
981         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
982
983         /*
984          * Initially, hide the progress bar and the key display,
985          * and show the no-key display. Also disable the Save
986          * buttons, because with no key we obviously can't save
987          * anything.
988          */
989         ui_set_state(hwnd, state, 0);
990
991         /*
992          * Load a key file if one was provided on the command line.
993          */
994         if (cmdline_keyfile) {
995             Filename *fn = filename_from_str(cmdline_keyfile);
996             load_key_file(hwnd, state, fn, 0);
997             filename_free(fn);
998         }
999
1000         return 1;
1001       case WM_MOUSEMOVE:
1002         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1003         if (state->collecting_entropy &&
1004             state->entropy && state->entropy_got < state->entropy_required) {
1005             state->entropy[state->entropy_got++] = lParam;
1006             state->entropy[state->entropy_got++] = GetMessageTime();
1007             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1008                                state->entropy_got, 0);
1009             if (state->entropy_got >= state->entropy_required) {
1010                 struct rsa_key_thread_params *params;
1011                 DWORD threadid;
1012
1013                 /*
1014                  * Seed the entropy pool
1015                  */
1016                 random_add_heavynoise(state->entropy, state->entropy_size);
1017                 smemclr(state->entropy, state->entropy_size);
1018                 sfree(state->entropy);
1019                 state->collecting_entropy = FALSE;
1020
1021                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1022                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1023                                    MAKELPARAM(0, PROGRESSRANGE));
1024                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1025
1026                 params = snew(struct rsa_key_thread_params);
1027                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1028                 params->dialog = hwnd;
1029                 params->keysize = state->keysize;
1030                 params->is_dsa = state->is_dsa;
1031                 params->key = &state->key;
1032                 params->dsskey = &state->dsskey;
1033
1034                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
1035                                   params, 0, &threadid)) {
1036                     MessageBox(hwnd, "Out of thread resources",
1037                                "Key generation error",
1038                                MB_OK | MB_ICONERROR);
1039                     sfree(params);
1040                 } else {
1041                     state->generation_thread_exists = TRUE;
1042                 }
1043             }
1044         }
1045         break;
1046       case WM_COMMAND:
1047         switch (LOWORD(wParam)) {
1048           case IDC_KEYSSH1:
1049           case IDC_KEYSSH2RSA:
1050           case IDC_KEYSSH2DSA:
1051             {
1052                 state = (struct MainDlgState *)
1053                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
1054                 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1055                     CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1056                                      LOWORD(wParam));
1057                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1058                                    LOWORD(wParam), MF_BYCOMMAND);
1059             }
1060             break;
1061           case IDC_QUIT:
1062             PostMessage(hwnd, WM_CLOSE, 0, 0);
1063             break;
1064           case IDC_COMMENTEDIT:
1065             if (HIWORD(wParam) == EN_CHANGE) {
1066                 state = (struct MainDlgState *)
1067                     GetWindowLongPtr(hwnd, GWLP_USERDATA);
1068                 if (state->key_exists) {
1069                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1070                     int len = GetWindowTextLength(editctl);
1071                     if (*state->commentptr)
1072                         sfree(*state->commentptr);
1073                     *state->commentptr = snewn(len + 1, char);
1074                     GetWindowText(editctl, *state->commentptr, len + 1);
1075                     if (state->ssh2) {
1076                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1077                                       &state->ssh2key);
1078                     } else {
1079                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1080                                       &state->key);
1081                     }
1082                 }
1083             }
1084             break;
1085           case IDC_ABOUT:
1086             EnableWindow(hwnd, 0);
1087             DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1088             EnableWindow(hwnd, 1);
1089             SetActiveWindow(hwnd);
1090             return 0;
1091           case IDC_GIVEHELP:
1092             if (HIWORD(wParam) == BN_CLICKED ||
1093                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1094                 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1095             }
1096             return 0;
1097           case IDC_GENERATE:
1098             if (HIWORD(wParam) != BN_CLICKED &&
1099                 HIWORD(wParam) != BN_DOUBLECLICKED)
1100                 break;
1101             state =
1102                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1103             if (!state->generation_thread_exists) {
1104                 BOOL ok;
1105                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1106                 if (!ok)
1107                     state->keysize = DEFAULT_KEYSIZE;
1108                 /* If we ever introduce a new key type, check it here! */
1109                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1110                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1111                 if (state->keysize < 256) {
1112                     int ret = MessageBox(hwnd,
1113                                          "PuTTYgen will not generate a key"
1114                                          " smaller than 256 bits.\n"
1115                                          "Key length reset to 256. Continue?",
1116                                          "PuTTYgen Warning",
1117                                          MB_ICONWARNING | MB_OKCANCEL);
1118                     if (ret != IDOK)
1119                         break;
1120                     state->keysize = 256;
1121                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1122                 }
1123                 ui_set_state(hwnd, state, 1);
1124                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1125                 state->key_exists = FALSE;
1126                 state->collecting_entropy = TRUE;
1127
1128                 /*
1129                  * My brief statistical tests on mouse movements
1130                  * suggest that there are about 2.5 bits of
1131                  * randomness in the x position, 2.5 in the y
1132                  * position, and 1.7 in the message time, making
1133                  * 5.7 bits of unpredictability per mouse movement.
1134                  * However, other people have told me it's far less
1135                  * than that, so I'm going to be stupidly cautious
1136                  * and knock that down to a nice round 2. With this
1137                  * method, we require two words per mouse movement,
1138                  * so with 2 bits per mouse movement we expect 2
1139                  * bits every 2 words.
1140                  */
1141                 state->entropy_required = (state->keysize / 2) * 2;
1142                 state->entropy_got = 0;
1143                 state->entropy_size = (state->entropy_required *
1144                                        sizeof(unsigned));
1145                 state->entropy = snewn(state->entropy_required, unsigned);
1146
1147                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1148                                    MAKELPARAM(0, state->entropy_required));
1149                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1150             }
1151             break;
1152           case IDC_SAVE:
1153           case IDC_EXPORT_OPENSSH:
1154           case IDC_EXPORT_SSHCOM:
1155             if (HIWORD(wParam) != BN_CLICKED)
1156                 break;
1157             state =
1158                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1159             if (state->key_exists) {
1160                 char filename[FILENAME_MAX];
1161                 char *passphrase, *passphrase2;
1162                 int type, realtype;
1163
1164                 if (state->ssh2)
1165                     realtype = SSH_KEYTYPE_SSH2;
1166                 else
1167                     realtype = SSH_KEYTYPE_SSH1;
1168
1169                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1170                     type = SSH_KEYTYPE_OPENSSH;
1171                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1172                     type = SSH_KEYTYPE_SSHCOM;
1173                 else
1174                     type = realtype;
1175
1176                 if (type != realtype &&
1177                     import_target_type(type) != realtype) {
1178                     char msg[256];
1179                     sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1180                             " format", (state->ssh2 ? 2 : 1),
1181                             (state->ssh2 ? 1 : 2));
1182                     MessageBox(hwnd, msg,
1183                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1184                     break;
1185                 }
1186
1187                 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1188                 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1189                 if (strcmp(passphrase, passphrase2)) {
1190                     MessageBox(hwnd,
1191                                "The two passphrases given do not match.",
1192                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1193                     burnstr(passphrase);
1194                     burnstr(passphrase2);
1195                     break;
1196                 }
1197                 burnstr(passphrase2);
1198                 if (!*passphrase) {
1199                     int ret;
1200                     ret = MessageBox(hwnd,
1201                                      "Are you sure you want to save this key\n"
1202                                      "without a passphrase to protect it?",
1203                                      "PuTTYgen Warning",
1204                                      MB_YESNO | MB_ICONWARNING);
1205                     if (ret != IDYES) {
1206                         burnstr(passphrase);
1207                         break;
1208                     }
1209                 }
1210                 if (prompt_keyfile(hwnd, "Save private key as:",
1211                                    filename, 1, (type == realtype))) {
1212                     int ret;
1213                     FILE *fp = fopen(filename, "r");
1214                     if (fp) {
1215                         char *buffer;
1216                         fclose(fp);
1217                         buffer = dupprintf("Overwrite existing file\n%s?",
1218                                            filename);
1219                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1220                                          MB_YESNO | MB_ICONWARNING);
1221                         sfree(buffer);
1222                         if (ret != IDYES) {
1223                             burnstr(passphrase);
1224                             break;
1225                         }
1226                     }
1227
1228                     if (state->ssh2) {
1229                         Filename *fn = filename_from_str(filename);
1230                         if (type != realtype)
1231                             ret = export_ssh2(fn, type, &state->ssh2key,
1232                                               *passphrase ? passphrase : NULL);
1233                         else
1234                             ret = ssh2_save_userkey(fn, &state->ssh2key,
1235                                                     *passphrase ? passphrase :
1236                                                     NULL);
1237                         filename_free(fn);
1238                     } else {
1239                         Filename *fn = filename_from_str(filename);
1240                         if (type != realtype)
1241                             ret = export_ssh1(fn, type, &state->key,
1242                                               *passphrase ? passphrase : NULL);
1243                         else
1244                             ret = saversakey(fn, &state->key,
1245                                              *passphrase ? passphrase : NULL);
1246                         filename_free(fn);
1247                     }
1248                     if (ret <= 0) {
1249                         MessageBox(hwnd, "Unable to save key file",
1250                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1251                     }
1252                 }
1253                 burnstr(passphrase);
1254             }
1255             break;
1256           case IDC_SAVEPUB:
1257             if (HIWORD(wParam) != BN_CLICKED)
1258                 break;
1259             state =
1260                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1261             if (state->key_exists) {
1262                 char filename[FILENAME_MAX];
1263                 if (prompt_keyfile(hwnd, "Save public key as:",
1264                                    filename, 1, 0)) {
1265                     int ret;
1266                     FILE *fp = fopen(filename, "r");
1267                     if (fp) {
1268                         char *buffer;
1269                         fclose(fp);
1270                         buffer = dupprintf("Overwrite existing file\n%s?",
1271                                            filename);
1272                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1273                                          MB_YESNO | MB_ICONWARNING);
1274                         sfree(buffer);
1275                         if (ret != IDYES)
1276                             break;
1277                     }
1278                     if (state->ssh2) {
1279                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
1280                     } else {
1281                         ret = save_ssh1_pubkey(filename, &state->key);
1282                     }
1283                     if (ret <= 0) {
1284                         MessageBox(hwnd, "Unable to save key file",
1285                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1286                     }
1287                 }
1288             }
1289             break;
1290           case IDC_LOAD:
1291           case IDC_IMPORT:
1292             if (HIWORD(wParam) != BN_CLICKED)
1293                 break;
1294             state =
1295                 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1296             if (!state->generation_thread_exists) {
1297                 char filename[FILENAME_MAX];
1298                 if (prompt_keyfile(hwnd, "Load private key:",
1299                                    filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1300                     Filename *fn = filename_from_str(filename);
1301                     load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1302                     filename_free(fn);
1303                 }
1304             }
1305             break;
1306         }
1307         return 0;
1308       case WM_DONEKEY:
1309         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1310         state->generation_thread_exists = FALSE;
1311         state->key_exists = TRUE;
1312         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1313                            MAKELPARAM(0, PROGRESSRANGE));
1314         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1315         if (state->ssh2) {
1316             if (state->is_dsa) {
1317                 state->ssh2key.data = &state->dsskey;
1318                 state->ssh2key.alg = &ssh_dss;
1319             } else {
1320                 state->ssh2key.data = &state->key;
1321                 state->ssh2key.alg = &ssh_rsa;
1322             }
1323             state->commentptr = &state->ssh2key.comment;
1324         } else {
1325             state->commentptr = &state->key.comment;
1326         }
1327         /*
1328          * Invent a comment for the key. We'll do this by including
1329          * the date in it. This will be so horrifyingly ugly that
1330          * the user will immediately want to change it, which is
1331          * what we want :-)
1332          */
1333         *state->commentptr = snewn(30, char);
1334         {
1335             struct tm tm;
1336             tm = ltime();
1337             if (state->is_dsa)
1338                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1339             else
1340                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1341         }
1342
1343         /*
1344          * Now update the key controls with all the key data.
1345          */
1346         {
1347             char *savecomment;
1348             /*
1349              * Blank passphrase, initially. This isn't dangerous,
1350              * because we will warn (Are You Sure?) before allowing
1351              * the user to save an unprotected private key.
1352              */
1353             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1354             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1355             /*
1356              * Set the comment.
1357              */
1358             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1359             /*
1360              * Set the key fingerprint.
1361              */
1362             savecomment = *state->commentptr;
1363             *state->commentptr = NULL;
1364             if (state->ssh2) {
1365                 char *fp;
1366                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1367                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1368                 sfree(fp);
1369             } else {
1370                 char buf[128];
1371                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1372                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1373             }
1374             *state->commentptr = savecomment;
1375             /*
1376              * Construct a decimal representation of the key, for
1377              * pasting into .ssh/authorized_keys or
1378              * .ssh/authorized_keys2 on a Unix box.
1379              */
1380             if (state->ssh2) {
1381                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1382                               IDC_PKSTATIC, &state->ssh2key);
1383             } else {
1384                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1385                               IDC_PKSTATIC, &state->key);
1386             }
1387         }
1388         /*
1389          * Finally, hide the progress bar and show the key data.
1390          */
1391         ui_set_state(hwnd, state, 2);
1392         break;
1393       case WM_HELP:
1394         {
1395             int id = ((LPHELPINFO)lParam)->iCtrlId;
1396             char *topic = NULL;
1397             switch (id) {
1398               case IDC_GENERATING:
1399               case IDC_PROGRESS:
1400               case IDC_GENSTATIC:
1401               case IDC_GENERATE:
1402                 topic = WINHELP_CTX_puttygen_generate; break;
1403               case IDC_PKSTATIC:
1404               case IDC_KEYDISPLAY:
1405                 topic = WINHELP_CTX_puttygen_pastekey; break;
1406               case IDC_FPSTATIC:
1407               case IDC_FINGERPRINT:
1408                 topic = WINHELP_CTX_puttygen_fingerprint; break;
1409               case IDC_COMMENTSTATIC:
1410               case IDC_COMMENTEDIT:
1411                 topic = WINHELP_CTX_puttygen_comment; break;
1412               case IDC_PASSPHRASE1STATIC:
1413               case IDC_PASSPHRASE1EDIT:
1414               case IDC_PASSPHRASE2STATIC:
1415               case IDC_PASSPHRASE2EDIT:
1416                 topic = WINHELP_CTX_puttygen_passphrase; break;
1417               case IDC_LOADSTATIC:
1418               case IDC_LOAD:
1419                 topic = WINHELP_CTX_puttygen_load; break;
1420               case IDC_SAVESTATIC:
1421               case IDC_SAVE:
1422                 topic = WINHELP_CTX_puttygen_savepriv; break;
1423               case IDC_SAVEPUB:
1424                 topic = WINHELP_CTX_puttygen_savepub; break;
1425               case IDC_TYPESTATIC:
1426               case IDC_KEYSSH1:
1427               case IDC_KEYSSH2RSA:
1428               case IDC_KEYSSH2DSA:
1429                 topic = WINHELP_CTX_puttygen_keytype; break;
1430               case IDC_BITSSTATIC:
1431               case IDC_BITS:
1432                 topic = WINHELP_CTX_puttygen_bits; break;
1433               case IDC_IMPORT:
1434               case IDC_EXPORT_OPENSSH:
1435               case IDC_EXPORT_SSHCOM:
1436                 topic = WINHELP_CTX_puttygen_conversions; break;
1437             }
1438             if (topic) {
1439                 launch_help(hwnd, topic);
1440             } else {
1441                 MessageBeep(0);
1442             }
1443         }
1444         break;
1445       case WM_CLOSE:
1446         state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1447         sfree(state);
1448         quit_help(hwnd);
1449         EndDialog(hwnd, 1);
1450         return 0;
1451     }
1452     return 0;
1453 }
1454
1455 void cleanup_exit(int code)
1456 {
1457     shutdown_help();
1458     exit(code);
1459 }
1460
1461 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1462 {
1463     int argc;
1464     char **argv;
1465     int ret;
1466
1467     InitCommonControls();
1468     hinst = inst;
1469     hwnd = NULL;
1470
1471     /*
1472      * See if we can find our Help file.
1473      */
1474     init_help();
1475
1476     split_into_argv(cmdline, &argc, &argv, NULL);
1477
1478     if (argc > 0) {
1479         if (!strcmp(argv[0], "-pgpfp")) {
1480             pgp_fingerprints();
1481             exit(1);
1482         } else {
1483             /*
1484              * Assume the first argument to be a private key file, and
1485              * attempt to load it.
1486              */
1487             cmdline_keyfile = argv[0];
1488         }
1489     }
1490
1491     random_ref();
1492     ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1493
1494     cleanup_exit(ret);
1495     return ret;                        /* just in case optimiser complains */
1496 }