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