]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/winpgen.c
Now that we have Subversion's file renaming ability, it's time at
[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     char *comment;
636     struct PassphraseProcStruct pps;
637     struct RSAKey newkey1;
638     struct ssh2_userkey *newkey2 = NULL;
639
640     type = realtype = key_type(&filename);
641     if (type != SSH_KEYTYPE_SSH1 &&
642         type != SSH_KEYTYPE_SSH2 &&
643         !import_possible(type)) {
644         char msg[256];
645         sprintf(msg, "Couldn't load private key (%s)",
646                 key_type_to_str(type));
647         MessageBox(NULL, msg,
648                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
649         return;
650     }
651
652     if (type != SSH_KEYTYPE_SSH1 &&
653         type != SSH_KEYTYPE_SSH2) {
654         realtype = type;
655         type = import_target_type(type);
656     }
657
658     comment = NULL;
659     if (realtype == SSH_KEYTYPE_SSH1)
660         needs_pass = rsakey_encrypted(&filename, &comment);
661     else if (realtype == SSH_KEYTYPE_SSH2)
662         needs_pass =
663         ssh2_userkey_encrypted(&filename, &comment);
664     else
665         needs_pass = import_encrypted(&filename, realtype,
666                                       &comment);
667     pps.passphrase = passphrase;
668     pps.comment = comment;
669     do {
670         if (needs_pass) {
671             int dlgret;
672             dlgret = DialogBoxParam(hinst,
673                                     MAKEINTRESOURCE(210),
674                                     NULL, PassphraseProc,
675                                     (LPARAM) & pps);
676             if (!dlgret) {
677                 ret = -2;
678                 break;
679             }
680         } else
681             *passphrase = '\0';
682         if (type == SSH_KEYTYPE_SSH1) {
683             if (realtype == type)
684                 ret = loadrsakey(&filename, &newkey1,
685                                  passphrase, NULL);
686             else
687                 ret = import_ssh1(&filename, realtype,
688                                   &newkey1, passphrase);
689         } else {
690             if (realtype == type)
691                 newkey2 = ssh2_load_userkey(&filename,
692                                             passphrase, NULL);
693             else
694                 newkey2 = import_ssh2(&filename, realtype,
695                                       passphrase);
696             if (newkey2 == SSH2_WRONG_PASSPHRASE)
697                 ret = -1;
698             else if (!newkey2)
699                 ret = 0;
700             else
701                 ret = 1;
702         }
703     } while (ret == -1);
704     if (comment)
705         sfree(comment);
706     if (ret == 0) {
707         MessageBox(NULL, "Couldn't load private key.",
708                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
709     } else if (ret == 1) {
710         /*
711          * Now update the key controls with all the
712          * key data.
713          */
714         {
715             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
716                            passphrase);
717             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
718                            passphrase);
719             if (type == SSH_KEYTYPE_SSH1) {
720                 char buf[128];
721                 char *savecomment;
722
723                 state->ssh2 = FALSE;
724                 state->commentptr = &state->key.comment;
725                 state->key = newkey1;
726
727                 /*
728                  * Set the key fingerprint.
729                  */
730                 savecomment = state->key.comment;
731                 state->key.comment = NULL;
732                 rsa_fingerprint(buf, sizeof(buf),
733                                 &state->key);
734                 state->key.comment = savecomment;
735
736                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
737                 /*
738                  * Construct a decimal representation
739                  * of the key, for pasting into
740                  * .ssh/authorized_keys on a Unix box.
741                  */
742                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
743                               IDC_PKSTATIC, &state->key);
744             } else {
745                 char *fp;
746                 char *savecomment;
747
748                 state->ssh2 = TRUE;
749                 state->commentptr =
750                     &state->ssh2key.comment;
751                 state->ssh2key = *newkey2;      /* structure copy */
752                 sfree(newkey2);
753
754                 savecomment = state->ssh2key.comment;
755                 state->ssh2key.comment = NULL;
756                 fp =
757                     state->ssh2key.alg->
758                     fingerprint(state->ssh2key.data);
759                 state->ssh2key.comment = savecomment;
760
761                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
762                 sfree(fp);
763
764                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
765                               IDC_PKSTATIC, &state->ssh2key);
766             }
767             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
768                            *state->commentptr);
769         }
770         /*
771          * Finally, hide the progress bar and show
772          * the key data.
773          */
774         ui_set_state(hwnd, state, 2);
775         state->key_exists = TRUE;
776
777         /*
778          * If the user has imported a foreign key
779          * using the Load command, let them know.
780          * If they've used the Import command, be
781          * silent.
782          */
783         if (realtype != type && !was_import_cmd) {
784             char msg[512];
785             sprintf(msg, "Successfully imported foreign key\n"
786                     "(%s).\n"
787                     "To use this key with PuTTY, you need to\n"
788                     "use the \"Save private key\" command to\n"
789                     "save it in PuTTY's own format.",
790                     key_type_to_str(realtype));
791             MessageBox(NULL, msg, "PuTTYgen Notice",
792                        MB_OK | MB_ICONINFORMATION);
793         }
794     }
795 }
796
797 /*
798  * Dialog-box function for the main PuTTYgen dialog box.
799  */
800 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
801                                 WPARAM wParam, LPARAM lParam)
802 {
803     static const char generating_msg[] =
804         "Please wait while a key is generated...";
805     static const char entropy_msg[] =
806         "Please generate some randomness by moving the mouse over the blank area.";
807     struct MainDlgState *state;
808
809     switch (msg) {
810       case WM_INITDIALOG:
811         if (help_path)
812             SetWindowLong(hwnd, GWL_EXSTYLE,
813                           GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
814         else {
815             /*
816              * If we add a Help button, this is where we destroy it
817              * if the help file isn't present.
818              */
819         }
820         requested_help = FALSE;
821         SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
822                     (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
823
824         state = snew(struct MainDlgState);
825         state->generation_thread_exists = FALSE;
826         state->collecting_entropy = FALSE;
827         state->entropy = NULL;
828         state->key_exists = FALSE;
829         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
830         {
831             HMENU menu, menu1;
832
833             menu = CreateMenu();
834
835             menu1 = CreateMenu();
836             AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
837             AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
838             AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
839             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
840             AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
841             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
842             state->filemenu = menu1;
843
844             menu1 = CreateMenu();
845             AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
846             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
847             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
848             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
849             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
850             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
851             state->keymenu = menu1;
852
853             menu1 = CreateMenu();
854             AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
855             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
856             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
857                        "Export &OpenSSH key");
858             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
859                        "Export &ssh.com key");
860             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
861                        "Con&versions");
862             state->cvtmenu = menu1;
863
864             menu1 = CreateMenu();
865             AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
866             if (help_path)
867                 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
868             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
869
870             SetMenu(hwnd, menu);
871         }
872
873         /*
874          * Centre the window.
875          */
876         {                              /* centre the window */
877             RECT rs, rd;
878             HWND hw;
879
880             hw = GetDesktopWindow();
881             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
882                 MoveWindow(hwnd,
883                            (rs.right + rs.left + rd.left - rd.right) / 2,
884                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
885                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
886         }
887
888         {
889             struct ctlpos cp, cp2;
890
891             /* Accelerators used: acglops1rbd */
892
893             ctlposinit(&cp, hwnd, 4, 4, 4);
894             beginbox(&cp, "Key", IDC_BOX_KEY);
895             cp2 = cp;
896             statictext(&cp2, "No key.", 1, IDC_NOKEY);
897             cp2 = cp;
898             statictext(&cp2, "", 1, IDC_GENERATING);
899             progressbar(&cp2, IDC_PROGRESS);
900             bigeditctrl(&cp,
901                         "&Public key for pasting into authorized_keys file:",
902                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
903             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
904             staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
905                        IDC_FINGERPRINT, 75);
906             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
907                                0);
908             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
909                        IDC_COMMENTEDIT, 75);
910             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
911                            IDC_PASSPHRASE1EDIT, 75);
912             staticpassedit(&cp, "C&onfirm passphrase:",
913                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
914             endbox(&cp);
915             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
916             staticbtn(&cp, "Generate a public/private key pair",
917                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
918             staticbtn(&cp, "Load an existing private key file",
919                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
920             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
921                        "Save p&ublic key", IDC_SAVEPUB,
922                        "&Save private key", IDC_SAVE);
923             endbox(&cp);
924             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
925             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
926                       "SSH&1 (RSA)", IDC_KEYSSH1,
927                       "SSH2 &RSA", IDC_KEYSSH2RSA,
928                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
929             staticedit(&cp, "Number of &bits in a generated key:",
930                        IDC_BITSSTATIC, IDC_BITS, 20);
931             endbox(&cp);
932         }
933         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
934         CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
935                            IDC_KEYSSH2RSA, MF_BYCOMMAND);
936         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
937
938         /*
939          * Initially, hide the progress bar and the key display,
940          * and show the no-key display. Also disable the Save
941          * buttons, because with no key we obviously can't save
942          * anything.
943          */
944         ui_set_state(hwnd, state, 0);
945
946         /*
947          * Load a key file if one was provided on the command line.
948          */
949         if (cmdline_keyfile)
950             load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
951
952         return 1;
953       case WM_MOUSEMOVE:
954         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
955         if (state->collecting_entropy &&
956             state->entropy && state->entropy_got < state->entropy_required) {
957             state->entropy[state->entropy_got++] = lParam;
958             state->entropy[state->entropy_got++] = GetMessageTime();
959             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
960                                state->entropy_got, 0);
961             if (state->entropy_got >= state->entropy_required) {
962                 struct rsa_key_thread_params *params;
963                 DWORD threadid;
964
965                 /*
966                  * Seed the entropy pool
967                  */
968                 random_add_heavynoise(state->entropy, state->entropy_size);
969                 memset(state->entropy, 0, state->entropy_size);
970                 sfree(state->entropy);
971                 state->collecting_entropy = FALSE;
972
973                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
974                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
975                                    MAKELPARAM(0, PROGRESSRANGE));
976                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
977
978                 params = snew(struct rsa_key_thread_params);
979                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
980                 params->dialog = hwnd;
981                 params->keysize = state->keysize;
982                 params->is_dsa = state->is_dsa;
983                 params->key = &state->key;
984                 params->dsskey = &state->dsskey;
985
986                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
987                                   params, 0, &threadid)) {
988                     MessageBox(hwnd, "Out of thread resources",
989                                "Key generation error",
990                                MB_OK | MB_ICONERROR);
991                     sfree(params);
992                 } else {
993                     state->generation_thread_exists = TRUE;
994                 }
995             }
996         }
997         break;
998       case WM_COMMAND:
999         switch (LOWORD(wParam)) {
1000           case IDC_KEYSSH1:
1001           case IDC_KEYSSH2RSA:
1002           case IDC_KEYSSH2DSA:
1003             {
1004                 state = (struct MainDlgState *)
1005                     GetWindowLong(hwnd, GWL_USERDATA);
1006                 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1007                     CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1008                                      LOWORD(wParam));
1009                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1010                                    LOWORD(wParam), MF_BYCOMMAND);
1011             }
1012             break;
1013           case IDC_QUIT:
1014             PostMessage(hwnd, WM_CLOSE, 0, 0);
1015             break;
1016           case IDC_COMMENTEDIT:
1017             if (HIWORD(wParam) == EN_CHANGE) {
1018                 state = (struct MainDlgState *)
1019                     GetWindowLong(hwnd, GWL_USERDATA);
1020                 if (state->key_exists) {
1021                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1022                     int len = GetWindowTextLength(editctl);
1023                     if (*state->commentptr)
1024                         sfree(*state->commentptr);
1025                     *state->commentptr = snewn(len + 1, char);
1026                     GetWindowText(editctl, *state->commentptr, len + 1);
1027                     if (state->ssh2) {
1028                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1029                                       &state->ssh2key);
1030                     } else {
1031                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1032                                       &state->key);
1033                     }
1034                 }
1035             }
1036             break;
1037           case IDC_ABOUT:
1038             EnableWindow(hwnd, 0);
1039             DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1040             EnableWindow(hwnd, 1);
1041             SetActiveWindow(hwnd);
1042             return 0;
1043           case IDC_GIVEHELP:
1044             if (HIWORD(wParam) == BN_CLICKED ||
1045                 HIWORD(wParam) == BN_DOUBLECLICKED) {
1046                 if (help_path) {
1047                     WinHelp(hwnd, help_path, HELP_COMMAND,
1048                             (DWORD)"JI(`',`puttygen.general')");
1049                     requested_help = TRUE;
1050                 }
1051             }
1052             return 0;
1053           case IDC_GENERATE:
1054             if (HIWORD(wParam) != BN_CLICKED &&
1055                 HIWORD(wParam) != BN_DOUBLECLICKED)
1056                 break;
1057             state =
1058                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1059             if (!state->generation_thread_exists) {
1060                 BOOL ok;
1061                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1062                 if (!ok)
1063                     state->keysize = DEFAULT_KEYSIZE;
1064                 /* If we ever introduce a new key type, check it here! */
1065                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1066                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1067                 if (state->keysize < 256) {
1068                     int ret = MessageBox(hwnd,
1069                                          "PuTTYgen will not generate a key"
1070                                          " smaller than 256 bits.\n"
1071                                          "Key length reset to 256. Continue?",
1072                                          "PuTTYgen Warning",
1073                                          MB_ICONWARNING | MB_OKCANCEL);
1074                     if (ret != IDOK)
1075                         break;
1076                     state->keysize = 256;
1077                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1078                 }
1079                 ui_set_state(hwnd, state, 1);
1080                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1081                 state->key_exists = FALSE;
1082                 state->collecting_entropy = TRUE;
1083
1084                 /*
1085                  * My brief statistical tests on mouse movements
1086                  * suggest that there are about 2.5 bits of
1087                  * randomness in the x position, 2.5 in the y
1088                  * position, and 1.7 in the message time, making
1089                  * 5.7 bits of unpredictability per mouse movement.
1090                  * However, other people have told me it's far less
1091                  * than that, so I'm going to be stupidly cautious
1092                  * and knock that down to a nice round 2. With this
1093                  * method, we require two words per mouse movement,
1094                  * so with 2 bits per mouse movement we expect 2
1095                  * bits every 2 words.
1096                  */
1097                 state->entropy_required = (state->keysize / 2) * 2;
1098                 state->entropy_got = 0;
1099                 state->entropy_size = (state->entropy_required *
1100                                        sizeof(unsigned));
1101                 state->entropy = snewn(state->entropy_required, unsigned);
1102
1103                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1104                                    MAKELPARAM(0, state->entropy_required));
1105                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1106             }
1107             break;
1108           case IDC_SAVE:
1109           case IDC_EXPORT_OPENSSH:
1110           case IDC_EXPORT_SSHCOM:
1111             if (HIWORD(wParam) != BN_CLICKED)
1112                 break;
1113             state =
1114                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1115             if (state->key_exists) {
1116                 char filename[FILENAME_MAX];
1117                 char passphrase[PASSPHRASE_MAXLEN];
1118                 char passphrase2[PASSPHRASE_MAXLEN];
1119                 int type, realtype;
1120
1121                 if (state->ssh2)
1122                     realtype = SSH_KEYTYPE_SSH2;
1123                 else
1124                     realtype = SSH_KEYTYPE_SSH1;
1125
1126                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1127                     type = SSH_KEYTYPE_OPENSSH;
1128                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1129                     type = SSH_KEYTYPE_SSHCOM;
1130                 else
1131                     type = realtype;
1132
1133                 if (type != realtype &&
1134                     import_target_type(type) != realtype) {
1135                     char msg[256];
1136                     sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1137                             " format", (state->ssh2 ? 2 : 1),
1138                             (state->ssh2 ? 1 : 2));
1139                     MessageBox(hwnd, msg,
1140                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1141                     break;
1142                 }
1143
1144                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1145                                passphrase, sizeof(passphrase));
1146                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1147                                passphrase2, sizeof(passphrase2));
1148                 if (strcmp(passphrase, passphrase2)) {
1149                     MessageBox(hwnd,
1150                                "The two passphrases given do not match.",
1151                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1152                     break;
1153                 }
1154                 if (!*passphrase) {
1155                     int ret;
1156                     ret = MessageBox(hwnd,
1157                                      "Are you sure you want to save this key\n"
1158                                      "without a passphrase to protect it?",
1159                                      "PuTTYgen Warning",
1160                                      MB_YESNO | MB_ICONWARNING);
1161                     if (ret != IDYES)
1162                         break;
1163                 }
1164                 if (prompt_keyfile(hwnd, "Save private key as:",
1165                                    filename, 1, (type == realtype))) {
1166                     int ret;
1167                     FILE *fp = fopen(filename, "r");
1168                     if (fp) {
1169                         char *buffer;
1170                         fclose(fp);
1171                         buffer = dupprintf("Overwrite existing file\n%s?",
1172                                            filename);
1173                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1174                                          MB_YESNO | MB_ICONWARNING);
1175                         sfree(buffer);
1176                         if (ret != IDYES)
1177                             break;
1178                     }
1179
1180                     if (state->ssh2) {
1181                         Filename fn = filename_from_str(filename);
1182                         if (type != realtype)
1183                             ret = export_ssh2(&fn, type, &state->ssh2key,
1184                                               *passphrase ? passphrase : NULL);
1185                         else
1186                             ret = ssh2_save_userkey(&fn, &state->ssh2key,
1187                                                     *passphrase ? passphrase :
1188                                                     NULL);
1189                     } else {
1190                         Filename fn = filename_from_str(filename);
1191                         if (type != realtype)
1192                             ret = export_ssh1(&fn, type, &state->key,
1193                                               *passphrase ? passphrase : NULL);
1194                         else
1195                             ret = saversakey(&fn, &state->key,
1196                                              *passphrase ? passphrase : NULL);
1197                     }
1198                     if (ret <= 0) {
1199                         MessageBox(hwnd, "Unable to save key file",
1200                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1201                     }
1202                 }
1203             }
1204             break;
1205           case IDC_SAVEPUB:
1206             if (HIWORD(wParam) != BN_CLICKED)
1207                 break;
1208             state =
1209                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1210             if (state->key_exists) {
1211                 char filename[FILENAME_MAX];
1212                 if (prompt_keyfile(hwnd, "Save public key as:",
1213                                    filename, 1, 0)) {
1214                     int ret;
1215                     FILE *fp = fopen(filename, "r");
1216                     if (fp) {
1217                         char *buffer;
1218                         fclose(fp);
1219                         buffer = dupprintf("Overwrite existing file\n%s?",
1220                                            filename);
1221                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1222                                          MB_YESNO | MB_ICONWARNING);
1223                         sfree(buffer);
1224                         if (ret != IDYES)
1225                             break;
1226                     }
1227                     if (state->ssh2) {
1228                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
1229                     } else {
1230                         ret = save_ssh1_pubkey(filename, &state->key);
1231                     }
1232                     if (ret <= 0) {
1233                         MessageBox(hwnd, "Unable to save key file",
1234                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1235                     }
1236                 }
1237             }
1238             break;
1239           case IDC_LOAD:
1240           case IDC_IMPORT:
1241             if (HIWORD(wParam) != BN_CLICKED)
1242                 break;
1243             state =
1244                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1245             if (!state->generation_thread_exists) {
1246                 char filename[FILENAME_MAX];
1247                 if (prompt_keyfile(hwnd, "Load private key:",
1248                                    filename, 0, LOWORD(wParam)==IDC_LOAD))
1249                     load_key_file(hwnd, state, filename_from_str(filename),
1250                                   LOWORD(wParam) != IDC_LOAD);
1251             }
1252             break;
1253         }
1254         return 0;
1255       case WM_DONEKEY:
1256         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1257         state->generation_thread_exists = FALSE;
1258         state->key_exists = TRUE;
1259         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1260                            MAKELPARAM(0, PROGRESSRANGE));
1261         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1262         if (state->ssh2) {
1263             if (state->is_dsa) {
1264                 state->ssh2key.data = &state->dsskey;
1265                 state->ssh2key.alg = &ssh_dss;
1266             } else {
1267                 state->ssh2key.data = &state->key;
1268                 state->ssh2key.alg = &ssh_rsa;
1269             }
1270             state->commentptr = &state->ssh2key.comment;
1271         } else {
1272             state->commentptr = &state->key.comment;
1273         }
1274         /*
1275          * Invent a comment for the key. We'll do this by including
1276          * the date in it. This will be so horrifyingly ugly that
1277          * the user will immediately want to change it, which is
1278          * what we want :-)
1279          */
1280         *state->commentptr = snewn(30, char);
1281         {
1282             time_t t;
1283             struct tm *tm;
1284             time(&t);
1285             tm = localtime(&t);
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 *cmd = NULL;
1346             switch (id) {
1347               case IDC_GENERATING:
1348               case IDC_PROGRESS:
1349               case IDC_GENSTATIC:
1350               case IDC_GENERATE:
1351                 cmd = "JI(`',`puttygen.generate')"; break;
1352               case IDC_PKSTATIC:
1353               case IDC_KEYDISPLAY:
1354                 cmd = "JI(`',`puttygen.pastekey')"; break;
1355               case IDC_FPSTATIC:
1356               case IDC_FINGERPRINT:
1357                 cmd = "JI(`',`puttygen.fingerprint')"; break;
1358               case IDC_COMMENTSTATIC:
1359               case IDC_COMMENTEDIT:
1360                 cmd = "JI(`',`puttygen.comment')"; break;
1361               case IDC_PASSPHRASE1STATIC:
1362               case IDC_PASSPHRASE1EDIT:
1363               case IDC_PASSPHRASE2STATIC:
1364               case IDC_PASSPHRASE2EDIT:
1365                 cmd = "JI(`',`puttygen.passphrase')"; break;
1366               case IDC_LOADSTATIC:
1367               case IDC_LOAD:
1368                 cmd = "JI(`',`puttygen.load')"; break;
1369               case IDC_SAVESTATIC:
1370               case IDC_SAVE:
1371                 cmd = "JI(`',`puttygen.savepriv')"; break;
1372               case IDC_SAVEPUB:
1373                 cmd = "JI(`',`puttygen.savepub')"; break;
1374               case IDC_TYPESTATIC:
1375               case IDC_KEYSSH1:
1376               case IDC_KEYSSH2RSA:
1377               case IDC_KEYSSH2DSA:
1378                 cmd = "JI(`',`puttygen.keytype')"; break;
1379               case IDC_BITSSTATIC:
1380               case IDC_BITS:
1381                 cmd = "JI(`',`puttygen.bits')"; break;
1382               case IDC_IMPORT:
1383               case IDC_EXPORT_OPENSSH:
1384               case IDC_EXPORT_SSHCOM:
1385                 cmd = "JI(`',`puttygen.conversions')"; break;
1386             }
1387             if (cmd) {
1388                 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1389                 requested_help = TRUE;
1390             } else {
1391                 MessageBeep(0);
1392             }
1393         }
1394         break;
1395       case WM_CLOSE:
1396         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1397         sfree(state);
1398         if (requested_help) {
1399             WinHelp(hwnd, help_path, HELP_QUIT, 0);
1400             requested_help = FALSE;
1401         }
1402         EndDialog(hwnd, 1);
1403         return 0;
1404     }
1405     return 0;
1406 }
1407
1408 void cleanup_exit(int code) { exit(code); }
1409
1410 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1411 {
1412     int argc;
1413     char **argv;
1414
1415     split_into_argv(cmdline, &argc, &argv, NULL);
1416
1417     if (argc > 0) {
1418         /*
1419          * Assume the first argument to be a private key file, and
1420          * attempt to load it.
1421          */
1422         cmdline_keyfile = argv[0];
1423     }
1424
1425     InitCommonControls();
1426     hinst = inst;
1427
1428     /*
1429      * See if we can find our Help file.
1430      */
1431     {
1432         char b[2048], *p, *q, *r;
1433         FILE *fp;
1434         GetModuleFileName(NULL, b, sizeof(b) - 1);
1435         r = b;
1436         p = strrchr(b, '\\');
1437         if (p && p >= r) r = p+1;
1438         q = strrchr(b, ':');
1439         if (q && q >= r) r = q+1;
1440         strcpy(r, "putty.hlp");
1441         if ( (fp = fopen(b, "r")) != NULL) {
1442             help_path = dupstr(b);
1443             fclose(fp);
1444         } else
1445             help_path = NULL;
1446     }
1447
1448     random_init();
1449     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1450                      MainDlgProc) != IDOK;
1451 }