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