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