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