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