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