]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
Final cleanups on key import/export work. Rationalised the UI (so
[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, exportmenu;
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->exportmenu, IDC_EXPORT_OPENSSH,
543                        MF_GRAYED|MF_BYCOMMAND);
544         EnableMenuItem(state->exportmenu, IDC_EXPORT_SSHCOM,
545                        MF_GRAYED|MF_BYCOMMAND);
546         break;
547       case 1:                          /* generating key */
548         hidemany(hwnd, nokey_ids, TRUE);
549         hidemany(hwnd, generating_ids, FALSE);
550         hidemany(hwnd, gotkey_ids, TRUE);
551         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
552         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
553         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
554         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
555         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
556         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
557         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
558         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
559         EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
560         EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
561         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
562         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
563         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
564         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
565         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
566         EnableMenuItem(state->exportmenu, IDC_EXPORT_OPENSSH,
567                        MF_GRAYED|MF_BYCOMMAND);
568         EnableMenuItem(state->exportmenu, IDC_EXPORT_SSHCOM,
569                        MF_GRAYED|MF_BYCOMMAND);
570         break;
571       case 2:
572         hidemany(hwnd, nokey_ids, TRUE);
573         hidemany(hwnd, generating_ids, TRUE);
574         hidemany(hwnd, gotkey_ids, FALSE);
575         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
576         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
577         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
578         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
579         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
580         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
581         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
582         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
583         EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
584         EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
585         EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
586         EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
587         EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
588         EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
589         EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
590         /*
591          * Enable export menu items if and only if the key type
592          * supports this kind of export.
593          */
594         type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
595 #define do_export_menuitem(x,y) \
596     EnableMenuItem(state->exportmenu, x, MF_BYCOMMAND | \
597                        (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
598         do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
599         do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
600 #undef do_export_menuitem
601         break;
602     }
603 }
604
605 /*
606  * Dialog-box function for the main PuTTYgen dialog box.
607  */
608 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
609                                 WPARAM wParam, LPARAM lParam)
610 {
611     static const char generating_msg[] =
612         "Please wait while a key is generated...";
613     static const char entropy_msg[] =
614         "Please generate some randomness by moving the mouse over the blank area.";
615     struct MainDlgState *state;
616
617     switch (msg) {
618       case WM_INITDIALOG:
619         if (help_path)
620             SetWindowLong(hwnd, GWL_EXSTYLE,
621                           GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
622         else {
623             /*
624              * If we add a Help button, this is where we destroy it
625              * if the help file isn't present.
626              */
627         }
628         requested_help = FALSE;
629
630         state = smalloc(sizeof(*state));
631         state->generation_thread_exists = FALSE;
632         state->collecting_entropy = FALSE;
633         state->entropy = NULL;
634         state->key_exists = FALSE;
635         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
636         {
637             HMENU menu, menu1;
638
639             menu = CreateMenu();
640
641             menu1 = CreateMenu();
642             AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
643             AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
644             AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
645             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
646             AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
647             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
648             state->filemenu = menu1;
649
650             menu1 = CreateMenu();
651             AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
652             AppendMenu(menu1, MF_SEPARATOR, 0, 0);
653             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
654             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
655             AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
656             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
657             state->keymenu = menu1;
658
659             menu1 = CreateMenu();
660             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
661                        "Export &OpenSSH key");
662             AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
663                        "Export &ssh.com key");
664             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
665                        "&Export");
666             state->exportmenu = menu1;
667
668             menu1 = CreateMenu();
669             AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
670             if (help_path)
671                 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
672             AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
673
674             SetMenu(hwnd, menu);
675         }
676
677         /*
678          * Centre the window.
679          */
680         {                              /* centre the window */
681             RECT rs, rd;
682             HWND hw;
683
684             hw = GetDesktopWindow();
685             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
686                 MoveWindow(hwnd,
687                            (rs.right + rs.left + rd.left - rd.right) / 2,
688                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
689                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
690         }
691
692         {
693             struct ctlpos cp, cp2;
694
695             /* Accelerators used: acglops1rbd */
696
697             ctlposinit(&cp, hwnd, 4, 4, 4);
698             beginbox(&cp, "Key", IDC_BOX_KEY);
699             cp2 = cp;
700             statictext(&cp2, "No key.", 1, IDC_NOKEY);
701             cp2 = cp;
702             statictext(&cp2, "", 1, IDC_GENERATING);
703             progressbar(&cp2, IDC_PROGRESS);
704             bigeditctrl(&cp,
705                         "&Public key for pasting into authorized_keys file:",
706                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
707             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
708             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
709                        IDC_FINGERPRINT, 75);
710             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
711                                0);
712             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
713                        IDC_COMMENTEDIT, 75);
714             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
715                            IDC_PASSPHRASE1EDIT, 75);
716             staticpassedit(&cp, "C&onfirm passphrase:",
717                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
718             endbox(&cp);
719             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
720             staticbtn(&cp, "Generate a public/private key pair",
721                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
722             staticbtn(&cp, "Load an existing private key file",
723                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
724             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
725                        "Save p&ublic key", IDC_SAVEPUB,
726                        "&Save private key", IDC_SAVE);
727             endbox(&cp);
728             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
729             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
730                       "SSH&1 (RSA)", IDC_KEYSSH1,
731                       "SSH2 &RSA", IDC_KEYSSH2RSA,
732                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
733             staticedit(&cp, "Number of &bits in a generated key:",
734                        IDC_BITSSTATIC, IDC_BITS, 20);
735             endbox(&cp);
736         }
737         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
738         CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
739                            IDC_KEYSSH1, MF_BYCOMMAND);
740         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
741
742         /*
743          * Initially, hide the progress bar and the key display,
744          * and show the no-key display. Also disable the Save
745          * buttons, because with no key we obviously can't save
746          * anything.
747          */
748         ui_set_state(hwnd, state, 0);
749
750         return 1;
751       case WM_MOUSEMOVE:
752         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
753         if (state->collecting_entropy &&
754             state->entropy && state->entropy_got < state->entropy_required) {
755             state->entropy[state->entropy_got++] = lParam;
756             state->entropy[state->entropy_got++] = GetMessageTime();
757             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
758                                state->entropy_got, 0);
759             if (state->entropy_got >= state->entropy_required) {
760                 struct rsa_key_thread_params *params;
761                 DWORD threadid;
762
763                 /*
764                  * Seed the entropy pool
765                  */
766                 random_add_heavynoise(state->entropy, state->entropy_size);
767                 memset(state->entropy, 0, state->entropy_size);
768                 sfree(state->entropy);
769                 state->collecting_entropy = FALSE;
770
771                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
772                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
773                                    MAKELPARAM(0, PROGRESSRANGE));
774                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
775
776                 params = smalloc(sizeof(*params));
777                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
778                 params->dialog = hwnd;
779                 params->keysize = state->keysize;
780                 params->is_dsa = state->is_dsa;
781                 params->key = &state->key;
782                 params->dsskey = &state->dsskey;
783
784                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
785                                   params, 0, &threadid)) {
786                     MessageBox(hwnd, "Out of thread resources",
787                                "Key generation error",
788                                MB_OK | MB_ICONERROR);
789                     sfree(params);
790                 } else {
791                     state->generation_thread_exists = TRUE;
792                 }
793             }
794         }
795         break;
796       case WM_COMMAND:
797         switch (LOWORD(wParam)) {
798           case IDC_KEYSSH1:
799           case IDC_KEYSSH2RSA:
800           case IDC_KEYSSH2DSA:
801             {
802                 state = (struct MainDlgState *)
803                     GetWindowLong(hwnd, GWL_USERDATA);
804                 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
805                     CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
806                                      LOWORD(wParam));
807                 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
808                                    LOWORD(wParam), MF_BYCOMMAND);
809             }
810             break;
811           case IDC_QUIT:
812             PostMessage(hwnd, WM_CLOSE, 0, 0);
813             break;
814           case IDC_COMMENTEDIT:
815             if (HIWORD(wParam) == EN_CHANGE) {
816                 state = (struct MainDlgState *)
817                     GetWindowLong(hwnd, GWL_USERDATA);
818                 if (state->key_exists) {
819                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
820                     int len = GetWindowTextLength(editctl);
821                     if (*state->commentptr)
822                         sfree(*state->commentptr);
823                     *state->commentptr = smalloc(len + 1);
824                     GetWindowText(editctl, *state->commentptr, len + 1);
825                     if (state->ssh2) {
826                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
827                                       &state->ssh2key);
828                     } else {
829                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
830                                       &state->key);
831                     }
832                 }
833             }
834             break;
835           case IDC_ABOUT:
836             EnableWindow(hwnd, 0);
837             DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
838             EnableWindow(hwnd, 1);
839             SetActiveWindow(hwnd);
840             return 0;
841           case IDC_GIVEHELP:
842             if (HIWORD(wParam) == BN_CLICKED ||
843                 HIWORD(wParam) == BN_DOUBLECLICKED) {
844                 if (help_path) {
845                     WinHelp(hwnd, help_path, HELP_COMMAND,
846                             (DWORD)"JI(`',`puttygen.general')");
847                     requested_help = TRUE;
848                 }
849             }
850             return 0;
851           case IDC_GENERATE:
852             state =
853                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
854             if (!state->generation_thread_exists) {
855                 BOOL ok;
856                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
857                 if (!ok)
858                     state->keysize = DEFAULT_KEYSIZE;
859                 /* If we ever introduce a new key type, check it here! */
860                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
861                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
862                 if (state->keysize < 256) {
863                     int ret = MessageBox(hwnd,
864                                          "PuTTYgen will not generate a key"
865                                          " smaller than 256 bits.\n"
866                                          "Key length reset to 256. Continue?",
867                                          "PuTTYgen Warning",
868                                          MB_ICONWARNING | MB_OKCANCEL);
869                     if (ret != IDOK)
870                         break;
871                     state->keysize = 256;
872                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
873                 }
874                 ui_set_state(hwnd, state, 1);
875                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
876                 state->key_exists = FALSE;
877                 state->collecting_entropy = TRUE;
878
879                 /*
880                  * My brief statistical tests on mouse movements
881                  * suggest that there are about 2.5 bits of
882                  * randomness in the x position, 2.5 in the y
883                  * position, and 1.7 in the message time, making
884                  * 5.7 bits of unpredictability per mouse movement.
885                  * However, other people have told me it's far less
886                  * than that, so I'm going to be stupidly cautious
887                  * and knock that down to a nice round 2. With this
888                  * method, we require two words per mouse movement,
889                  * so with 2 bits per mouse movement we expect 2
890                  * bits every 2 words.
891                  */
892                 state->entropy_required = (state->keysize / 2) * 2;
893                 state->entropy_got = 0;
894                 state->entropy_size = (state->entropy_required *
895                                        sizeof(*state->entropy));
896                 state->entropy = smalloc(state->entropy_size);
897
898                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
899                                    MAKELPARAM(0, state->entropy_required));
900                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
901             }
902             break;
903           case IDC_SAVE:
904           case IDC_EXPORT_OPENSSH:
905           case IDC_EXPORT_SSHCOM:
906             state =
907                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
908             if (state->key_exists) {
909                 char filename[FILENAME_MAX];
910                 char passphrase[PASSPHRASE_MAXLEN];
911                 char passphrase2[PASSPHRASE_MAXLEN];
912                 int type, realtype;
913
914                 if (state->ssh2)
915                     realtype = SSH_KEYTYPE_SSH2;
916                 else
917                     realtype = SSH_KEYTYPE_SSH1;
918
919                 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
920                     type = SSH_KEYTYPE_OPENSSH;
921                 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
922                     type = SSH_KEYTYPE_SSHCOM;
923                 else
924                     type = realtype;
925
926                 if (type != realtype &&
927                     import_target_type(type) != realtype) {
928                     char msg[256];
929                     sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
930                             " format", (state->ssh2 ? 2 : 1),
931                             (state->ssh2 ? 1 : 2));
932                     MessageBox(hwnd, msg,
933                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
934                     break;
935                 }
936
937                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
938                                passphrase, sizeof(passphrase));
939                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
940                                passphrase2, sizeof(passphrase2));
941                 if (strcmp(passphrase, passphrase2)) {
942                     MessageBox(hwnd,
943                                "The two passphrases given do not match.",
944                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
945                     break;
946                 }
947                 if (!*passphrase) {
948                     int ret;
949                     ret = MessageBox(hwnd,
950                                      "Are you sure you want to save this key\n"
951                                      "without a passphrase to protect it?",
952                                      "PuTTYgen Warning",
953                                      MB_YESNO | MB_ICONWARNING);
954                     if (ret != IDYES)
955                         break;
956                 }
957                 if (prompt_keyfile(hwnd, "Save private key as:",
958                                    filename, 1)) {
959                     int ret;
960                     FILE *fp = fopen(filename, "r");
961                     if (fp) {
962                         char buffer[FILENAME_MAX + 80];
963                         fclose(fp);
964                         sprintf(buffer, "Overwrite existing file\n%.*s?",
965                                 FILENAME_MAX, filename);
966                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
967                                          MB_YESNO | MB_ICONWARNING);
968                         if (ret != IDYES)
969                             break;
970                     }
971
972                     if (state->ssh2) {
973                         if (type != realtype)
974                             ret = export_ssh2(filename, type, &state->ssh2key,
975                                               *passphrase ? passphrase : NULL);
976                         else
977                             ret = ssh2_save_userkey(filename, &state->ssh2key,
978                                                     *passphrase ? passphrase :
979                                                     NULL);
980                     } else {
981                         if (type != realtype)
982                             ret = export_ssh1(filename, type, &state->key,
983                                               *passphrase ? passphrase : NULL);
984                         else
985                             ret = saversakey(filename, &state->key,
986                                              *passphrase ? passphrase : NULL);
987                     }
988                     if (ret <= 0) {
989                         MessageBox(hwnd, "Unable to save key file",
990                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
991                     }
992                 }
993             }
994             break;
995           case IDC_SAVEPUB:
996             state =
997                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
998             if (state->key_exists) {
999                 char filename[FILENAME_MAX];
1000                 if (prompt_keyfile(hwnd, "Save public key as:",
1001                                    filename, 1)) {
1002                     int ret;
1003                     FILE *fp = fopen(filename, "r");
1004                     if (fp) {
1005                         char buffer[FILENAME_MAX + 80];
1006                         fclose(fp);
1007                         sprintf(buffer, "Overwrite existing file\n%.*s?",
1008                                 FILENAME_MAX, filename);
1009                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1010                                          MB_YESNO | MB_ICONWARNING);
1011                         if (ret != IDYES)
1012                             break;
1013                     }
1014                     if (state->ssh2) {
1015                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
1016                     } else {
1017                         ret = save_ssh1_pubkey(filename, &state->key);
1018                     }
1019                     if (ret <= 0) {
1020                         MessageBox(hwnd, "Unable to save key file",
1021                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1022                     }
1023                 }
1024             }
1025             break;
1026           case IDC_LOAD:
1027             state =
1028                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1029             if (!state->generation_thread_exists) {
1030                 char filename[FILENAME_MAX];
1031                 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
1032                     char passphrase[PASSPHRASE_MAXLEN];
1033                     int needs_pass;
1034                     int type, realtype;
1035                     int ret;
1036                     char *comment;
1037                     struct PassphraseProcStruct pps;
1038                     struct RSAKey newkey1;
1039                     struct ssh2_userkey *newkey2 = NULL;
1040
1041                     type = realtype = key_type(filename);
1042                     if (type != SSH_KEYTYPE_SSH1 &&
1043                         type != SSH_KEYTYPE_SSH2 &&
1044                         !import_possible(type)) {
1045                         char msg[256];
1046                         sprintf(msg, "Couldn't load private key (%s)",
1047                                 key_type_to_str(type));
1048                         MessageBox(NULL, msg,
1049                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1050                         break;
1051                     }
1052
1053                     if (type != SSH_KEYTYPE_SSH1 &&
1054                         type != SSH_KEYTYPE_SSH2) {
1055                         realtype = type;
1056                         type = import_target_type(type);
1057                     }
1058
1059                     comment = NULL;
1060                     if (realtype == SSH_KEYTYPE_SSH1)
1061                         needs_pass = rsakey_encrypted(filename, &comment);
1062                     else if (realtype == SSH_KEYTYPE_SSH2)
1063                         needs_pass =
1064                             ssh2_userkey_encrypted(filename, &comment);
1065                     else
1066                         needs_pass = import_encrypted(filename, realtype,
1067                                                       &comment);
1068                     pps.passphrase = passphrase;
1069                     pps.comment = comment;
1070                     do {
1071                         if (needs_pass) {
1072                             int dlgret;
1073                             dlgret = DialogBoxParam(hinst,
1074                                                     MAKEINTRESOURCE(210),
1075                                                     NULL, PassphraseProc,
1076                                                     (LPARAM) & pps);
1077                             if (!dlgret) {
1078                                 ret = -2;
1079                                 break;
1080                             }
1081                         } else
1082                             *passphrase = '\0';
1083                         if (type == SSH_KEYTYPE_SSH1) {
1084                             if (realtype == type)
1085                                 ret = loadrsakey(filename, &newkey1,
1086                                                  passphrase);
1087                             else
1088                                 ret = import_ssh1(filename, realtype,
1089                                                   &newkey1, passphrase);
1090                         } else {
1091                             if (realtype == type)
1092                                 newkey2 = ssh2_load_userkey(filename,
1093                                                             passphrase);
1094                             else
1095                                 newkey2 = import_ssh2(filename, realtype,
1096                                                       passphrase);
1097                             if (newkey2 == SSH2_WRONG_PASSPHRASE)
1098                                 ret = -1;
1099                             else if (!newkey2)
1100                                 ret = 0;
1101                             else
1102                                 ret = 1;
1103                         }
1104                     } while (ret == -1);
1105                     if (comment)
1106                         sfree(comment);
1107                     if (ret == 0) {
1108                         MessageBox(NULL, "Couldn't load private key.",
1109                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
1110                     } else if (ret == 1) {
1111                         /*
1112                          * Now update the key controls with all the
1113                          * key data.
1114                          */
1115                         {
1116                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1117                                            passphrase);
1118                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1119                                            passphrase);
1120                             if (type == SSH_KEYTYPE_SSH1) {
1121                                 char buf[128];
1122                                 char *savecomment;
1123
1124                                 state->ssh2 = FALSE;
1125                                 state->commentptr = &state->key.comment;
1126                                 state->key = newkey1;
1127
1128                                 /*
1129                                  * Set the key fingerprint.
1130                                  */
1131                                 savecomment = state->key.comment;
1132                                 state->key.comment = NULL;
1133                                 rsa_fingerprint(buf, sizeof(buf),
1134                                                 &state->key);
1135                                 state->key.comment = savecomment;
1136
1137                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1138                                 /*
1139                                  * Construct a decimal representation
1140                                  * of the key, for pasting into
1141                                  * .ssh/authorized_keys on a Unix box.
1142                                  */
1143                                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1144                                               IDC_PKSTATIC, &state->key);
1145                             } else {
1146                                 char *fp;
1147                                 char *savecomment;
1148
1149                                 state->ssh2 = TRUE;
1150                                 state->commentptr =
1151                                     &state->ssh2key.comment;
1152                                 state->ssh2key = *newkey2;      /* structure copy */
1153                                 sfree(newkey2);
1154
1155                                 savecomment = state->ssh2key.comment;
1156                                 state->ssh2key.comment = NULL;
1157                                 fp =
1158                                     state->ssh2key.alg->
1159                                     fingerprint(state->ssh2key.data);
1160                                 state->ssh2key.comment = savecomment;
1161
1162                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1163                                 sfree(fp);
1164
1165                                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1166                                               IDC_PKSTATIC, &state->ssh2key);
1167                             }
1168                             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1169                                            *state->commentptr);
1170                         }
1171                         /*
1172                          * Finally, hide the progress bar and show
1173                          * the key data.
1174                          */
1175                         ui_set_state(hwnd, state, 2);
1176                         state->key_exists = TRUE;
1177                     }
1178                 }
1179             }
1180             break;
1181         }
1182         return 0;
1183       case WM_DONEKEY:
1184         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1185         state->generation_thread_exists = FALSE;
1186         state->key_exists = TRUE;
1187         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1188                            MAKELPARAM(0, PROGRESSRANGE));
1189         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1190         if (state->ssh2) {
1191             if (state->is_dsa) {
1192                 state->ssh2key.data = &state->dsskey;
1193                 state->ssh2key.alg = &ssh_dss;
1194             } else {
1195                 state->ssh2key.data = &state->key;
1196                 state->ssh2key.alg = &ssh_rsa;
1197             }
1198             state->commentptr = &state->ssh2key.comment;
1199         } else {
1200             state->commentptr = &state->key.comment;
1201         }
1202         /*
1203          * Invent a comment for the key. We'll do this by including
1204          * the date in it. This will be so horrifyingly ugly that
1205          * the user will immediately want to change it, which is
1206          * what we want :-)
1207          */
1208         *state->commentptr = smalloc(30);
1209         {
1210             time_t t;
1211             struct tm *tm;
1212             time(&t);
1213             tm = localtime(&t);
1214             if (state->is_dsa)
1215                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1216             else
1217                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1218         }
1219
1220         /*
1221          * Now update the key controls with all the key data.
1222          */
1223         {
1224             char *savecomment;
1225             /*
1226              * Blank passphrase, initially. This isn't dangerous,
1227              * because we will warn (Are You Sure?) before allowing
1228              * the user to save an unprotected private key.
1229              */
1230             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1231             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1232             /*
1233              * Set the comment.
1234              */
1235             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1236             /*
1237              * Set the key fingerprint.
1238              */
1239             savecomment = *state->commentptr;
1240             *state->commentptr = NULL;
1241             if (state->ssh2) {
1242                 char *fp;
1243                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1244                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1245                 sfree(fp);
1246             } else {
1247                 char buf[128];
1248                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1249                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1250             }
1251             *state->commentptr = savecomment;
1252             /*
1253              * Construct a decimal representation of the key, for
1254              * pasting into .ssh/authorized_keys or
1255              * .ssh/authorized_keys2 on a Unix box.
1256              */
1257             if (state->ssh2) {
1258                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1259                               IDC_PKSTATIC, &state->ssh2key);
1260             } else {
1261                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1262                               IDC_PKSTATIC, &state->key);
1263             }
1264         }
1265         /*
1266          * Finally, hide the progress bar and show the key data.
1267          */
1268         ui_set_state(hwnd, state, 2);
1269         break;
1270       case WM_HELP:
1271         if (help_path) {
1272             int id = ((LPHELPINFO)lParam)->iCtrlId;
1273             char *cmd = NULL;
1274             switch (id) {
1275               case IDC_GENERATING:
1276               case IDC_PROGRESS:
1277               case IDC_GENSTATIC:
1278               case IDC_GENERATE:
1279                 cmd = "JI(`',`puttygen.generate')"; break;
1280               case IDC_PKSTATIC:
1281               case IDC_KEYDISPLAY:
1282                 cmd = "JI(`',`puttygen.pastekey')"; break;
1283               case IDC_FPSTATIC:
1284               case IDC_FINGERPRINT:
1285                 cmd = "JI(`',`puttygen.fingerprint')"; break;
1286               case IDC_COMMENTSTATIC:
1287               case IDC_COMMENTEDIT:
1288                 cmd = "JI(`',`puttygen.comment')"; break;
1289               case IDC_PASSPHRASE1STATIC:
1290               case IDC_PASSPHRASE1EDIT:
1291               case IDC_PASSPHRASE2STATIC:
1292               case IDC_PASSPHRASE2EDIT:
1293                 cmd = "JI(`',`puttygen.passphrase')"; break;
1294               case IDC_LOADSTATIC:
1295               case IDC_LOAD:
1296                 cmd = "JI(`',`puttygen.load')"; break;
1297               case IDC_SAVESTATIC:
1298               case IDC_SAVE:
1299                 cmd = "JI(`',`puttygen.savepriv')"; break;
1300               case IDC_SAVEPUB:
1301                 cmd = "JI(`',`puttygen.savepub')"; break;
1302               case IDC_TYPESTATIC:
1303               case IDC_KEYSSH1:
1304               case IDC_KEYSSH2RSA:
1305               case IDC_KEYSSH2DSA:
1306                 cmd = "JI(`',`puttygen.keytype')"; break;
1307               case IDC_BITSSTATIC:
1308               case IDC_BITS:
1309                 cmd = "JI(`',`puttygen.bits')"; break;
1310               case IDC_EXPORT_OPENSSH:
1311               case IDC_EXPORT_SSHCOM:
1312                 cmd = "JI(`',`puttygen.export')"; break;
1313             }
1314             if (cmd) {
1315                 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1316                 requested_help = TRUE;
1317             } else {
1318                 MessageBeep(0);
1319             }
1320         }
1321         break;
1322       case WM_CLOSE:
1323         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1324         sfree(state);
1325         if (requested_help) {
1326             WinHelp(hwnd, help_path, HELP_QUIT, 0);
1327             requested_help = FALSE;
1328         }
1329         EndDialog(hwnd, 1);
1330         return 0;
1331     }
1332     return 0;
1333 }
1334
1335 void cleanup_exit(int code) { exit(code); }
1336
1337 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1338 {
1339     InitCommonControls();
1340     hinst = inst;
1341
1342     /*
1343      * See if we can find our Help file.
1344      */
1345     {
1346         char b[2048], *p, *q, *r;
1347         FILE *fp;
1348         GetModuleFileName(NULL, b, sizeof(b) - 1);
1349         r = b;
1350         p = strrchr(b, '\\');
1351         if (p && p >= r) r = p+1;
1352         q = strrchr(b, ':');
1353         if (q && q >= r) r = q+1;
1354         strcpy(r, "putty.hlp");
1355         if ( (fp = fopen(b, "r")) != NULL) {
1356             help_path = dupstr(b);
1357             fclose(fp);
1358         } else
1359             help_path = NULL;
1360     }
1361
1362     random_init();
1363     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1364                      MainDlgProc) != IDOK;
1365 }