]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
Update to use -title rather than -T
[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);
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);
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                        "&Conversions");
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 fingerprint:", 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             state =
1111                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1112             if (state->key_exists) {
1113                 char filename[FILENAME_MAX];
1114                 char passphrase[PASSPHRASE_MAXLEN];
1115                 char passphrase2[PASSPHRASE_MAXLEN];
1116                 int type, realtype;
1117
1118                 if (state->ssh2)
1119                     realtype = SSH_KEYTYPE_SSH2;
1120                 else
1121                     realtype = SSH_KEYTYPE_SSH1;
1122
1123                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1124                     type = SSH_KEYTYPE_OPENSSH;
1125                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1126                     type = SSH_KEYTYPE_SSHCOM;
1127                 else
1128                     type = realtype;
1129
1130                 if (type != realtype &&
1131                     import_target_type(type) != realtype) {
1132                     char msg[256];
1133                     sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1134                             " format", (state->ssh2 ? 2 : 1),
1135                             (state->ssh2 ? 1 : 2));
1136                     MessageBox(hwnd, msg,
1137                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1138                     break;
1139                 }
1140
1141                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1142                                passphrase, sizeof(passphrase));
1143                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1144                                passphrase2, sizeof(passphrase2));
1145                 if (strcmp(passphrase, passphrase2)) {
1146                     MessageBox(hwnd,
1147                                "The two passphrases given do not match.",
1148                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
1149                     break;
1150                 }
1151                 if (!*passphrase) {
1152                     int ret;
1153                     ret = MessageBox(hwnd,
1154                                      "Are you sure you want to save this key\n"
1155                                      "without a passphrase to protect it?",
1156                                      "PuTTYgen Warning",
1157                                      MB_YESNO | MB_ICONWARNING);
1158                     if (ret != IDYES)
1159                         break;
1160                 }
1161                 if (prompt_keyfile(hwnd, "Save private key as:",
1162                                    filename, 1, (type == realtype))) {
1163                     int ret;
1164                     FILE *fp = fopen(filename, "r");
1165                     if (fp) {
1166                         char *buffer;
1167                         fclose(fp);
1168                         buffer = dupprintf("Overwrite existing file\n%s?",
1169                                            filename);
1170                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1171                                          MB_YESNO | MB_ICONWARNING);
1172                         sfree(buffer);
1173                         if (ret != IDYES)
1174                             break;
1175                     }
1176
1177                     if (state->ssh2) {
1178                         Filename fn = filename_from_str(filename);
1179                         if (type != realtype)
1180                             ret = export_ssh2(&fn, type, &state->ssh2key,
1181                                               *passphrase ? passphrase : NULL);
1182                         else
1183                             ret = ssh2_save_userkey(&fn, &state->ssh2key,
1184                                                     *passphrase ? passphrase :
1185                                                     NULL);
1186                     } else {
1187                         Filename fn = filename_from_str(filename);
1188                         if (type != realtype)
1189                             ret = export_ssh1(&fn, type, &state->key,
1190                                               *passphrase ? passphrase : NULL);
1191                         else
1192                             ret = saversakey(&fn, &state->key,
1193                                              *passphrase ? passphrase : NULL);
1194                     }
1195                     if (ret <= 0) {
1196                         MessageBox(hwnd, "Unable to save key file",
1197                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1198                     }
1199                 }
1200             }
1201             break;
1202           case IDC_SAVEPUB:
1203             state =
1204                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1205             if (state->key_exists) {
1206                 char filename[FILENAME_MAX];
1207                 if (prompt_keyfile(hwnd, "Save public key as:",
1208                                    filename, 1, 0)) {
1209                     int ret;
1210                     FILE *fp = fopen(filename, "r");
1211                     if (fp) {
1212                         char *buffer;
1213                         fclose(fp);
1214                         buffer = dupprintf("Overwrite existing file\n%s?",
1215                                            filename);
1216                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1217                                          MB_YESNO | MB_ICONWARNING);
1218                         sfree(buffer);
1219                         if (ret != IDYES)
1220                             break;
1221                     }
1222                     if (state->ssh2) {
1223                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
1224                     } else {
1225                         ret = save_ssh1_pubkey(filename, &state->key);
1226                     }
1227                     if (ret <= 0) {
1228                         MessageBox(hwnd, "Unable to save key file",
1229                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1230                     }
1231                 }
1232             }
1233             break;
1234           case IDC_LOAD:
1235           case IDC_IMPORT:
1236             state =
1237                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1238             if (!state->generation_thread_exists) {
1239                 char filename[FILENAME_MAX];
1240                 if (prompt_keyfile(hwnd, "Load private key:",
1241                                    filename, 0, LOWORD(wParam)==IDC_LOAD))
1242                     load_key_file(hwnd, state, filename_from_str(filename),
1243                                   LOWORD(wParam) != IDC_LOAD);
1244             }
1245             break;
1246         }
1247         return 0;
1248       case WM_DONEKEY:
1249         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1250         state->generation_thread_exists = FALSE;
1251         state->key_exists = TRUE;
1252         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1253                            MAKELPARAM(0, PROGRESSRANGE));
1254         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1255         if (state->ssh2) {
1256             if (state->is_dsa) {
1257                 state->ssh2key.data = &state->dsskey;
1258                 state->ssh2key.alg = &ssh_dss;
1259             } else {
1260                 state->ssh2key.data = &state->key;
1261                 state->ssh2key.alg = &ssh_rsa;
1262             }
1263             state->commentptr = &state->ssh2key.comment;
1264         } else {
1265             state->commentptr = &state->key.comment;
1266         }
1267         /*
1268          * Invent a comment for the key. We'll do this by including
1269          * the date in it. This will be so horrifyingly ugly that
1270          * the user will immediately want to change it, which is
1271          * what we want :-)
1272          */
1273         *state->commentptr = snewn(30, char);
1274         {
1275             time_t t;
1276             struct tm *tm;
1277             time(&t);
1278             tm = localtime(&t);
1279             if (state->is_dsa)
1280                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1281             else
1282                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1283         }
1284
1285         /*
1286          * Now update the key controls with all the key data.
1287          */
1288         {
1289             char *savecomment;
1290             /*
1291              * Blank passphrase, initially. This isn't dangerous,
1292              * because we will warn (Are You Sure?) before allowing
1293              * the user to save an unprotected private key.
1294              */
1295             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1296             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1297             /*
1298              * Set the comment.
1299              */
1300             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1301             /*
1302              * Set the key fingerprint.
1303              */
1304             savecomment = *state->commentptr;
1305             *state->commentptr = NULL;
1306             if (state->ssh2) {
1307                 char *fp;
1308                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1309                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1310                 sfree(fp);
1311             } else {
1312                 char buf[128];
1313                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1314                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1315             }
1316             *state->commentptr = savecomment;
1317             /*
1318              * Construct a decimal representation of the key, for
1319              * pasting into .ssh/authorized_keys or
1320              * .ssh/authorized_keys2 on a Unix box.
1321              */
1322             if (state->ssh2) {
1323                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1324                               IDC_PKSTATIC, &state->ssh2key);
1325             } else {
1326                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1327                               IDC_PKSTATIC, &state->key);
1328             }
1329         }
1330         /*
1331          * Finally, hide the progress bar and show the key data.
1332          */
1333         ui_set_state(hwnd, state, 2);
1334         break;
1335       case WM_HELP:
1336         if (help_path) {
1337             int id = ((LPHELPINFO)lParam)->iCtrlId;
1338             char *cmd = NULL;
1339             switch (id) {
1340               case IDC_GENERATING:
1341               case IDC_PROGRESS:
1342               case IDC_GENSTATIC:
1343               case IDC_GENERATE:
1344                 cmd = "JI(`',`puttygen.generate')"; break;
1345               case IDC_PKSTATIC:
1346               case IDC_KEYDISPLAY:
1347                 cmd = "JI(`',`puttygen.pastekey')"; break;
1348               case IDC_FPSTATIC:
1349               case IDC_FINGERPRINT:
1350                 cmd = "JI(`',`puttygen.fingerprint')"; break;
1351               case IDC_COMMENTSTATIC:
1352               case IDC_COMMENTEDIT:
1353                 cmd = "JI(`',`puttygen.comment')"; break;
1354               case IDC_PASSPHRASE1STATIC:
1355               case IDC_PASSPHRASE1EDIT:
1356               case IDC_PASSPHRASE2STATIC:
1357               case IDC_PASSPHRASE2EDIT:
1358                 cmd = "JI(`',`puttygen.passphrase')"; break;
1359               case IDC_LOADSTATIC:
1360               case IDC_LOAD:
1361                 cmd = "JI(`',`puttygen.load')"; break;
1362               case IDC_SAVESTATIC:
1363               case IDC_SAVE:
1364                 cmd = "JI(`',`puttygen.savepriv')"; break;
1365               case IDC_SAVEPUB:
1366                 cmd = "JI(`',`puttygen.savepub')"; break;
1367               case IDC_TYPESTATIC:
1368               case IDC_KEYSSH1:
1369               case IDC_KEYSSH2RSA:
1370               case IDC_KEYSSH2DSA:
1371                 cmd = "JI(`',`puttygen.keytype')"; break;
1372               case IDC_BITSSTATIC:
1373               case IDC_BITS:
1374                 cmd = "JI(`',`puttygen.bits')"; break;
1375               case IDC_IMPORT:
1376               case IDC_EXPORT_OPENSSH:
1377               case IDC_EXPORT_SSHCOM:
1378                 cmd = "JI(`',`puttygen.conversions')"; break;
1379             }
1380             if (cmd) {
1381                 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1382                 requested_help = TRUE;
1383             } else {
1384                 MessageBeep(0);
1385             }
1386         }
1387         break;
1388       case WM_CLOSE:
1389         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1390         sfree(state);
1391         if (requested_help) {
1392             WinHelp(hwnd, help_path, HELP_QUIT, 0);
1393             requested_help = FALSE;
1394         }
1395         EndDialog(hwnd, 1);
1396         return 0;
1397     }
1398     return 0;
1399 }
1400
1401 void cleanup_exit(int code) { exit(code); }
1402
1403 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1404 {
1405     int argc;
1406     char **argv;
1407
1408     split_into_argv(cmdline, &argc, &argv, NULL);
1409
1410     if (argc > 0) {
1411         /*
1412          * Assume the first argument to be a private key file, and
1413          * attempt to load it.
1414          */
1415         cmdline_keyfile = argv[0];
1416     }
1417
1418     InitCommonControls();
1419     hinst = inst;
1420
1421     /*
1422      * See if we can find our Help file.
1423      */
1424     {
1425         char b[2048], *p, *q, *r;
1426         FILE *fp;
1427         GetModuleFileName(NULL, b, sizeof(b) - 1);
1428         r = b;
1429         p = strrchr(b, '\\');
1430         if (p && p >= r) r = p+1;
1431         q = strrchr(b, ':');
1432         if (q && q >= r) r = q+1;
1433         strcpy(r, "putty.hlp");
1434         if ( (fp = fopen(b, "r")) != NULL) {
1435             help_path = dupstr(b);
1436             fclose(fp);
1437         } else
1438             help_path = NULL;
1439     }
1440
1441     random_init();
1442     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1443                      MainDlgProc) != IDOK;
1444 }