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