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