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