]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
6642ab2b0c285c82dce42ad1e1b87b6857eb5761
[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     };
505     static const int nokey_ids[] = { IDC_NOKEY, 0 };
506     static const int generating_ids[] =
507         { IDC_GENERATING, IDC_PROGRESS, 0 };
508     static const int gotkey_ids[] = {
509         IDC_PKSTATIC, IDC_KEYDISPLAY,
510         IDC_FPSTATIC, IDC_FINGERPRINT,
511         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
512         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
513         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
514     };
515     static const char generating_msg[] =
516         "Please wait while a key is generated...";
517     static const char entropy_msg[] =
518         "Please generate some randomness by moving the mouse over the blank area.";
519     struct MainDlgState *state;
520
521     switch (msg) {
522       case WM_INITDIALOG:
523         if (help_path)
524             SetWindowLong(hwnd, GWL_EXSTYLE,
525                           GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
526         else {
527             /*
528              * If we add a Help button, this is where we destroy it
529              * if the help file isn't present.
530              */
531         }
532         requested_help = FALSE;
533
534         /*
535          * Centre the window.
536          */
537         {                              /* centre the window */
538             RECT rs, rd;
539             HWND hw;
540
541             hw = GetDesktopWindow();
542             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
543                 MoveWindow(hwnd,
544                            (rs.right + rs.left + rd.left - rd.right) / 2,
545                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
546                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
547         }
548
549         state = smalloc(sizeof(*state));
550         state->generation_thread_exists = FALSE;
551         state->collecting_entropy = FALSE;
552         state->entropy = NULL;
553         state->key_exists = FALSE;
554         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
555         {
556             struct ctlpos cp, cp2;
557
558             /* Accelerators used: acglops1rbd */
559
560             ctlposinit(&cp, hwnd, 4, 4, 4);
561             bartitle(&cp, "Public and private key generation for PuTTY",
562                      IDC_TITLE);
563             beginbox(&cp, "Key", IDC_BOX_KEY);
564             cp2 = cp;
565             statictext(&cp2, "No key.", 1, IDC_NOKEY);
566             cp2 = cp;
567             statictext(&cp2, "", 1, IDC_GENERATING);
568             progressbar(&cp2, IDC_PROGRESS);
569             bigeditctrl(&cp,
570                         "&Public key for pasting into authorized_keys file:",
571                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
572             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
573             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
574                        IDC_FINGERPRINT, 75);
575             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
576                                0);
577             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
578                        IDC_COMMENTEDIT, 75);
579             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
580                            IDC_PASSPHRASE1EDIT, 75);
581             staticpassedit(&cp, "C&onfirm passphrase:",
582                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
583             endbox(&cp);
584             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
585             staticbtn(&cp, "Generate a public/private key pair",
586                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
587             staticbtn(&cp, "Load an existing private key file",
588                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
589             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
590                        "Save p&ublic key", IDC_SAVEPUB,
591                        "&Save private key", IDC_SAVE);
592             endbox(&cp);
593             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
594             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
595                       "SSH&1 (RSA)", IDC_KEYSSH1,
596                       "SSH2 &RSA", IDC_KEYSSH2RSA,
597                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
598             staticedit(&cp, "Number of &bits in a generated key:",
599                        IDC_BITSSTATIC, IDC_BITS, 20);
600             endbox(&cp);
601         }
602         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
603         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
604
605         /*
606          * Initially, hide the progress bar and the key display,
607          * and show the no-key display. Also disable the Save
608          * buttons, because with no key we obviously can't save
609          * anything.
610          */
611         hidemany(hwnd, nokey_ids, FALSE);
612         hidemany(hwnd, generating_ids, TRUE);
613         hidemany(hwnd, gotkey_ids, TRUE);
614         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
615         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
616
617         return 1;
618       case WM_MOUSEMOVE:
619         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
620         if (state->collecting_entropy &&
621             state->entropy && state->entropy_got < state->entropy_required) {
622             state->entropy[state->entropy_got++] = lParam;
623             state->entropy[state->entropy_got++] = GetMessageTime();
624             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
625                                state->entropy_got, 0);
626             if (state->entropy_got >= state->entropy_required) {
627                 struct rsa_key_thread_params *params;
628                 DWORD threadid;
629
630                 /*
631                  * Seed the entropy pool
632                  */
633                 random_add_heavynoise(state->entropy, state->entropy_size);
634                 memset(state->entropy, 0, state->entropy_size);
635                 sfree(state->entropy);
636                 state->collecting_entropy = FALSE;
637
638                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
639                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
640                                    MAKELPARAM(0, PROGRESSRANGE));
641                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
642
643                 params = smalloc(sizeof(*params));
644                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
645                 params->dialog = hwnd;
646                 params->keysize = state->keysize;
647                 params->is_dsa = state->is_dsa;
648                 params->key = &state->key;
649                 params->dsskey = &state->dsskey;
650
651                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
652                                   params, 0, &threadid)) {
653                     MessageBox(hwnd, "Out of thread resources",
654                                "Key generation error",
655                                MB_OK | MB_ICONERROR);
656                     sfree(params);
657                 } else {
658                     state->generation_thread_exists = TRUE;
659                 }
660             }
661         }
662         break;
663       case WM_COMMAND:
664         switch (LOWORD(wParam)) {
665           case IDC_COMMENTEDIT:
666             if (HIWORD(wParam) == EN_CHANGE) {
667                 state = (struct MainDlgState *)
668                     GetWindowLong(hwnd, GWL_USERDATA);
669                 if (state->key_exists) {
670                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
671                     int len = GetWindowTextLength(editctl);
672                     if (*state->commentptr)
673                         sfree(*state->commentptr);
674                     *state->commentptr = smalloc(len + 1);
675                     GetWindowText(editctl, *state->commentptr, len + 1);
676                     if (state->ssh2) {
677                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
678                                       &state->ssh2key);
679                     } else {
680                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
681                                       &state->key);
682                     }
683                 }
684             }
685             break;
686           case IDC_ABOUT:
687             EnableWindow(hwnd, 0);
688             DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
689             EnableWindow(hwnd, 1);
690             SetActiveWindow(hwnd);
691             return 0;
692           case IDC_GENERATE:
693             state =
694                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
695             if (!state->generation_thread_exists) {
696                 BOOL ok;
697                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
698                 if (!ok)
699                     state->keysize = DEFAULT_KEYSIZE;
700                 /* If we ever introduce a new key type, check it here! */
701                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
702                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
703                 if (state->keysize < 256) {
704                     int ret = MessageBox(hwnd,
705                                          "PuTTYgen will not generate a key"
706                                          " smaller than 256 bits.\n"
707                                          "Key length reset to 256. Continue?",
708                                          "PuTTYgen Warning",
709                                          MB_ICONWARNING | MB_OKCANCEL);
710                     if (ret != IDOK)
711                         break;
712                     state->keysize = 256;
713                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
714                 }
715                 hidemany(hwnd, nokey_ids, TRUE);
716                 hidemany(hwnd, generating_ids, FALSE);
717                 hidemany(hwnd, gotkey_ids, TRUE);
718                 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
719                 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
720                 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
721                 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
722                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
723                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
724                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
725                 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
726                 state->key_exists = FALSE;
727                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
728                 state->collecting_entropy = TRUE;
729
730                 /*
731                  * My brief statistical tests on mouse movements
732                  * suggest that there are about 2.5 bits of
733                  * randomness in the x position, 2.5 in the y
734                  * position, and 1.7 in the message time, making
735                  * 5.7 bits of unpredictability per mouse movement.
736                  * However, other people have told me it's far less
737                  * than that, so I'm going to be stupidly cautious
738                  * and knock that down to a nice round 2. With this
739                  * method, we require two words per mouse movement,
740                  * so with 2 bits per mouse movement we expect 2
741                  * bits every 2 words.
742                  */
743                 state->entropy_required = (state->keysize / 2) * 2;
744                 state->entropy_got = 0;
745                 state->entropy_size = (state->entropy_required *
746                                        sizeof(*state->entropy));
747                 state->entropy = smalloc(state->entropy_size);
748
749                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
750                                    MAKELPARAM(0, state->entropy_required));
751                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
752             }
753             break;
754           case IDC_SAVE:
755             state =
756                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
757             if (state->key_exists) {
758                 char filename[FILENAME_MAX];
759                 char passphrase[PASSPHRASE_MAXLEN];
760                 char passphrase2[PASSPHRASE_MAXLEN];
761                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
762                                passphrase, sizeof(passphrase));
763                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
764                                passphrase2, sizeof(passphrase2));
765                 if (strcmp(passphrase, passphrase2)) {
766                     MessageBox(hwnd,
767                                "The two passphrases given do not match.",
768                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
769                     break;
770                 }
771                 if (!*passphrase) {
772                     int ret;
773                     ret = MessageBox(hwnd,
774                                      "Are you sure you want to save this key\n"
775                                      "without a passphrase to protect it?",
776                                      "PuTTYgen Warning",
777                                      MB_YESNO | MB_ICONWARNING);
778                     if (ret != IDYES)
779                         break;
780                 }
781                 if (prompt_keyfile(hwnd, "Save private key as:",
782                                    filename, 1)) {
783                     int ret;
784                     FILE *fp = fopen(filename, "r");
785                     if (fp) {
786                         char buffer[FILENAME_MAX + 80];
787                         fclose(fp);
788                         sprintf(buffer, "Overwrite existing file\n%.*s?",
789                                 FILENAME_MAX, filename);
790                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
791                                          MB_YESNO | MB_ICONWARNING);
792                         if (ret != IDYES)
793                             break;
794                     }
795                     if (state->ssh2) {
796                         ret = ssh2_save_userkey(filename, &state->ssh2key,
797                                                 *passphrase ? passphrase :
798                                                 NULL);
799                     } else {
800                         ret = saversakey(filename, &state->key,
801                                          *passphrase ? passphrase : NULL);
802                     }
803                     if (ret <= 0) {
804                         MessageBox(hwnd, "Unable to save key file",
805                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
806                     }
807                 }
808             }
809             break;
810           case IDC_SAVEPUB:
811             state =
812                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
813             if (state->key_exists) {
814                 char filename[FILENAME_MAX];
815                 if (prompt_keyfile(hwnd, "Save public key as:",
816                                    filename, 1)) {
817                     int ret;
818                     FILE *fp = fopen(filename, "r");
819                     if (fp) {
820                         char buffer[FILENAME_MAX + 80];
821                         fclose(fp);
822                         sprintf(buffer, "Overwrite existing file\n%.*s?",
823                                 FILENAME_MAX, filename);
824                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
825                                          MB_YESNO | MB_ICONWARNING);
826                         if (ret != IDYES)
827                             break;
828                     }
829                     if (state->ssh2) {
830                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
831                     } else {
832                         ret = save_ssh1_pubkey(filename, &state->key);
833                     }
834                     if (ret <= 0) {
835                         MessageBox(hwnd, "Unable to save key file",
836                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
837                     }
838                 }
839             }
840             break;
841           case IDC_LOAD:
842             state =
843                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
844             if (!state->generation_thread_exists) {
845                 char filename[FILENAME_MAX];
846                 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
847                     char passphrase[PASSPHRASE_MAXLEN];
848                     int needs_pass;
849                     int type;
850                     int ret;
851                     char *comment;
852                     struct PassphraseProcStruct pps;
853                     struct RSAKey newkey1;
854                     struct ssh2_userkey *newkey2 = NULL;
855
856                     type = key_type(filename);
857                     if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) {
858                         char msg[256];
859                         sprintf(msg, "Couldn't load private key (%s)",
860                                 key_type_to_str(type));
861                         MessageBox(NULL, msg,
862                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
863                         break;
864                     }
865
866                     comment = NULL;
867                     if (type == SSH_KEYTYPE_SSH1)
868                         needs_pass = rsakey_encrypted(filename, &comment);
869                     else
870                         needs_pass =
871                             ssh2_userkey_encrypted(filename, &comment);
872                     pps.passphrase = passphrase;
873                     pps.comment = comment;
874                     do {
875                         if (needs_pass) {
876                             int dlgret;
877                             dlgret = DialogBoxParam(hinst,
878                                                     MAKEINTRESOURCE(210),
879                                                     NULL, PassphraseProc,
880                                                     (LPARAM) & pps);
881                             if (!dlgret) {
882                                 ret = -2;
883                                 break;
884                             }
885                         } else
886                             *passphrase = '\0';
887                         if (type == SSH_KEYTYPE_SSH1)
888                             ret =
889                                 loadrsakey(filename, &newkey1, passphrase);
890                         else {
891                             newkey2 =
892                                 ssh2_load_userkey(filename, passphrase);
893                             if (newkey2 == SSH2_WRONG_PASSPHRASE)
894                                 ret = -1;
895                             else if (!newkey2)
896                                 ret = 0;
897                             else
898                                 ret = 1;
899                         }
900                     } while (ret == -1);
901                     if (comment)
902                         sfree(comment);
903                     if (ret == 0) {
904                         MessageBox(NULL, "Couldn't load private key.",
905                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
906                     } else if (ret == 1) {
907                         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
908                         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
909                         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
910                         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
911                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
912                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
913                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
914                         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
915                         /*
916                          * Now update the key controls with all the
917                          * key data.
918                          */
919                         {
920                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
921                                            passphrase);
922                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
923                                            passphrase);
924                             if (type == SSH_KEYTYPE_SSH1) {
925                                 char buf[128];
926                                 char *savecomment;
927
928                                 state->ssh2 = FALSE;
929                                 state->commentptr = &state->key.comment;
930                                 state->key = newkey1;
931
932                                 /*
933                                  * Set the key fingerprint.
934                                  */
935                                 savecomment = state->key.comment;
936                                 state->key.comment = NULL;
937                                 rsa_fingerprint(buf, sizeof(buf),
938                                                 &state->key);
939                                 state->key.comment = savecomment;
940
941                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
942                                 /*
943                                  * Construct a decimal representation
944                                  * of the key, for pasting into
945                                  * .ssh/authorized_keys on a Unix box.
946                                  */
947                                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
948                                               IDC_PKSTATIC, &state->key);
949                             } else {
950                                 char *fp;
951                                 char *savecomment;
952
953                                 state->ssh2 = TRUE;
954                                 state->commentptr =
955                                     &state->ssh2key.comment;
956                                 state->ssh2key = *newkey2;      /* structure copy */
957                                 sfree(newkey2);
958
959                                 savecomment = state->ssh2key.comment;
960                                 state->ssh2key.comment = NULL;
961                                 fp =
962                                     state->ssh2key.alg->
963                                     fingerprint(state->ssh2key.data);
964                                 state->ssh2key.comment = savecomment;
965
966                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
967                                 sfree(fp);
968
969                                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
970                                               IDC_PKSTATIC, &state->ssh2key);
971                             }
972                             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
973                                            *state->commentptr);
974                         }
975                         /*
976                          * Finally, hide the progress bar and show
977                          * the key data.
978                          */
979                         hidemany(hwnd, nokey_ids, TRUE);
980                         hidemany(hwnd, generating_ids, TRUE);
981                         hidemany(hwnd, gotkey_ids, FALSE);
982                         state->key_exists = TRUE;
983                     }
984                 }
985             }
986             break;
987         }
988         return 0;
989       case WM_DONEKEY:
990         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
991         state->generation_thread_exists = FALSE;
992         state->key_exists = TRUE;
993         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
994                            MAKELPARAM(0, PROGRESSRANGE));
995         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
996         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
997         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
998         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
999         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1000         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1001         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
1002         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1003         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1004         if (state->ssh2) {
1005             if (state->is_dsa) {
1006                 state->ssh2key.data = &state->dsskey;
1007                 state->ssh2key.alg = &ssh_dss;
1008             } else {
1009                 state->ssh2key.data = &state->key;
1010                 state->ssh2key.alg = &ssh_rsa;
1011             }
1012             state->commentptr = &state->ssh2key.comment;
1013         } else {
1014             state->commentptr = &state->key.comment;
1015         }
1016         /*
1017          * Invent a comment for the key. We'll do this by including
1018          * the date in it. This will be so horrifyingly ugly that
1019          * the user will immediately want to change it, which is
1020          * what we want :-)
1021          */
1022         *state->commentptr = smalloc(30);
1023         {
1024             time_t t;
1025             struct tm *tm;
1026             time(&t);
1027             tm = localtime(&t);
1028             if (state->is_dsa)
1029                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1030             else
1031                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1032         }
1033
1034         /*
1035          * Now update the key controls with all the key data.
1036          */
1037         {
1038             char *savecomment;
1039             /*
1040              * Blank passphrase, initially. This isn't dangerous,
1041              * because we will warn (Are You Sure?) before allowing
1042              * the user to save an unprotected private key.
1043              */
1044             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1045             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1046             /*
1047              * Set the comment.
1048              */
1049             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1050             /*
1051              * Set the key fingerprint.
1052              */
1053             savecomment = *state->commentptr;
1054             *state->commentptr = NULL;
1055             if (state->ssh2) {
1056                 char *fp;
1057                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1058                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1059                 sfree(fp);
1060             } else {
1061                 char buf[128];
1062                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1063                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1064             }
1065             *state->commentptr = savecomment;
1066             /*
1067              * Construct a decimal representation of the key, for
1068              * pasting into .ssh/authorized_keys or
1069              * .ssh/authorized_keys2 on a Unix box.
1070              */
1071             if (state->ssh2) {
1072                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1073                               IDC_PKSTATIC, &state->ssh2key);
1074             } else {
1075                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1076                               IDC_PKSTATIC, &state->key);
1077             }
1078         }
1079         /*
1080          * Finally, hide the progress bar and show the key data.
1081          */
1082         hidemany(hwnd, nokey_ids, TRUE);
1083         hidemany(hwnd, generating_ids, TRUE);
1084         hidemany(hwnd, gotkey_ids, FALSE);
1085         break;
1086       case WM_HELP:
1087         if (help_path) {
1088             int id = ((LPHELPINFO)lParam)->iCtrlId;
1089             char *cmd = NULL;
1090             switch (id) {
1091               case IDC_GENERATING:
1092               case IDC_PROGRESS:
1093               case IDC_GENSTATIC:
1094               case IDC_GENERATE:
1095                 cmd = "JI(`',`puttygen.generate')"; break;
1096               case IDC_PKSTATIC:
1097               case IDC_KEYDISPLAY:
1098                 cmd = "JI(`',`puttygen.pastekey')"; break;
1099               case IDC_FPSTATIC:
1100               case IDC_FINGERPRINT:
1101                 cmd = "JI(`',`puttygen.fingerprint')"; break;
1102               case IDC_COMMENTSTATIC:
1103               case IDC_COMMENTEDIT:
1104                 cmd = "JI(`',`puttygen.comment')"; break;
1105               case IDC_PASSPHRASE1STATIC:
1106               case IDC_PASSPHRASE1EDIT:
1107               case IDC_PASSPHRASE2STATIC:
1108               case IDC_PASSPHRASE2EDIT:
1109                 cmd = "JI(`',`puttygen.passphrase')"; break;
1110               case IDC_LOADSTATIC:
1111               case IDC_LOAD:
1112                 cmd = "JI(`',`puttygen.load')"; break;
1113               case IDC_SAVESTATIC:
1114               case IDC_SAVE:
1115                 cmd = "JI(`',`puttygen.savepriv')"; break;
1116               case IDC_SAVEPUB:
1117                 cmd = "JI(`',`puttygen.savepub')"; break;
1118               case IDC_TYPESTATIC:
1119               case IDC_KEYSSH1:
1120               case IDC_KEYSSH2RSA:
1121               case IDC_KEYSSH2DSA:
1122                 cmd = "JI(`',`puttygen.keytype')"; break;
1123               case IDC_BITSSTATIC:
1124               case IDC_BITS:
1125                 cmd = "JI(`',`puttygen.bits')"; break;
1126             }
1127             if (cmd) {
1128                 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1129                 requested_help = TRUE;
1130             } else {
1131                 MessageBeep(0);
1132             }
1133         }
1134         break;
1135       case WM_CLOSE:
1136         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1137         sfree(state);
1138         if (requested_help) {
1139             WinHelp(hwnd, help_path, HELP_QUIT, 0);
1140             requested_help = FALSE;
1141         }
1142         EndDialog(hwnd, 1);
1143         return 0;
1144     }
1145     return 0;
1146 }
1147
1148 void cleanup_exit(int code) { exit(code); }
1149
1150 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1151 {
1152     InitCommonControls();
1153     hinst = inst;
1154
1155     /*
1156      * See if we can find our Help file.
1157      */
1158     {
1159         char b[2048], *p, *q, *r;
1160         FILE *fp;
1161         GetModuleFileName(NULL, b, sizeof(b) - 1);
1162         r = b;
1163         p = strrchr(b, '\\');
1164         if (p && p >= r) r = p+1;
1165         q = strrchr(b, ':');
1166         if (q && q >= r) r = q+1;
1167         strcpy(r, "putty.hlp");
1168         if ( (fp = fopen(b, "r")) != NULL) {
1169             help_path = dupstr(b);
1170             fclose(fp);
1171         } else
1172             help_path = NULL;
1173     }
1174
1175     random_init();
1176     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1177                      MainDlgProc) != IDOK;
1178 }