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