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