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