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