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