]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
4ac0fef9c7853610c12e5d1488759894c8608f97
[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 /* ----------------------------------------------------------------------
22  * Progress report code. This is really horrible :-)
23  */
24 #define PROGRESSRANGE 65535
25 #define MAXPHASE 5
26 struct progress {
27     int nphases;
28     struct {
29         int exponential;
30         unsigned startpoint, total;
31         unsigned param, current, n;    /* if exponential */
32         unsigned mult;                 /* if linear */
33     } phases[MAXPHASE];
34     unsigned total, divisor, range;
35     HWND progbar;
36 };
37
38 static void progress_update(void *param, int action, int phase, int iprogress)
39 {
40     struct progress *p = (struct progress *) param;
41     unsigned progress = iprogress;
42     int position;
43
44     if (action < PROGFN_READY && p->nphases < phase)
45         p->nphases = phase;
46     switch (action) {
47       case PROGFN_INITIALISE:
48         p->nphases = 0;
49         break;
50       case PROGFN_LIN_PHASE:
51         p->phases[phase-1].exponential = 0;
52         p->phases[phase-1].mult = p->phases[phase].total / progress;
53         break;
54       case PROGFN_EXP_PHASE:
55         p->phases[phase-1].exponential = 1;
56         p->phases[phase-1].param = 0x10000 + progress;
57         p->phases[phase-1].current = p->phases[phase-1].total;
58         p->phases[phase-1].n = 0;
59         break;
60       case PROGFN_PHASE_EXTENT:
61         p->phases[phase-1].total = progress;
62         break;
63       case PROGFN_READY:
64         {
65             unsigned total = 0;
66             int i;
67             for (i = 0; i < p->nphases; i++) {
68                 p->phases[i].startpoint = total;
69                 total += p->phases[i].total;
70             }
71             p->total = total;
72             p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
73             p->range = p->total / p->divisor;
74             SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
75         }
76         break;
77       case PROGFN_PROGRESS:
78         if (p->phases[phase-1].exponential) {
79             while (p->phases[phase-1].n < progress) {
80                 p->phases[phase-1].n++;
81                 p->phases[phase-1].current *= p->phases[phase-1].param;
82                 p->phases[phase-1].current /= 0x10000;
83             }
84             position = (p->phases[phase-1].startpoint +
85                         p->phases[phase-1].total - p->phases[phase-1].current);
86         } else {
87             position = (p->phases[phase-1].startpoint +
88                         progress * p->phases[phase-1].mult);
89         }
90         SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
91         break;
92     }
93 }
94
95 extern char ver[];
96
97 #define PASSPHRASE_MAXLEN 512
98
99 struct PassphraseProcStruct {
100     char *passphrase;
101     char *comment;
102 };
103
104 /*
105  * Dialog-box function for the passphrase box.
106  */
107 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
108                                    WPARAM wParam, LPARAM lParam)
109 {
110     static char *passphrase = NULL;
111     struct PassphraseProcStruct *p;
112
113     switch (msg) {
114       case WM_INITDIALOG:
115         SetForegroundWindow(hwnd);
116         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
117                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
118
119         /*
120          * Centre the window.
121          */
122         {                              /* centre the window */
123             RECT rs, rd;
124             HWND hw;
125
126             hw = GetDesktopWindow();
127             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
128                 MoveWindow(hwnd,
129                            (rs.right + rs.left + rd.left - rd.right) / 2,
130                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
131                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
132         }
133
134         p = (struct PassphraseProcStruct *) lParam;
135         passphrase = p->passphrase;
136         if (p->comment)
137             SetDlgItemText(hwnd, 101, p->comment);
138         *passphrase = 0;
139         SetDlgItemText(hwnd, 102, passphrase);
140         return 0;
141       case WM_COMMAND:
142         switch (LOWORD(wParam)) {
143           case IDOK:
144             if (*passphrase)
145                 EndDialog(hwnd, 1);
146             else
147                 MessageBeep(0);
148             return 0;
149           case IDCANCEL:
150             EndDialog(hwnd, 0);
151             return 0;
152           case 102:                    /* edit box */
153             if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
154                 GetDlgItemText(hwnd, 102, passphrase,
155                                PASSPHRASE_MAXLEN - 1);
156                 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
157             }
158             return 0;
159         }
160         return 0;
161       case WM_CLOSE:
162         EndDialog(hwnd, 0);
163         return 0;
164     }
165     return 0;
166 }
167
168 /*
169  * Prompt for a key file. Assumes the filename buffer is of size
170  * FILENAME_MAX.
171  */
172 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
173                           char *filename, int save)
174 {
175     OPENFILENAME of;
176     memset(&of, 0, sizeof(of));
177 #ifdef OPENFILENAME_SIZE_VERSION_400
178     of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
179 #else
180     of.lStructSize = sizeof(of);
181 #endif
182     of.hwndOwner = hwnd;
183     of.lpstrFilter = "All Files\0*\0\0\0";
184     of.lpstrCustomFilter = NULL;
185     of.nFilterIndex = 1;
186     of.lpstrFile = filename;
187     *filename = '\0';
188     of.nMaxFile = FILENAME_MAX;
189     of.lpstrFileTitle = NULL;
190     of.lpstrInitialDir = NULL;
191     of.lpstrTitle = dlgtitle;
192     of.Flags = 0;
193     if (save)
194         return GetSaveFileName(&of);
195     else
196         return GetOpenFileName(&of);
197 }
198
199 /*
200  * This function is needed to link with the DES code. We need not
201  * have it do anything at all.
202  */
203 void logevent(char *msg)
204 {
205 }
206
207 /*
208  * Dialog-box function for the Licence box.
209  */
210 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
211                                 WPARAM wParam, LPARAM lParam)
212 {
213     switch (msg) {
214       case WM_INITDIALOG:
215         /*
216          * Centre the window.
217          */
218         {                              /* centre the window */
219             RECT rs, rd;
220             HWND hw;
221
222             hw = GetDesktopWindow();
223             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
224                 MoveWindow(hwnd,
225                            (rs.right + rs.left + rd.left - rd.right) / 2,
226                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
227                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
228         }
229
230         return 1;
231       case WM_COMMAND:
232         switch (LOWORD(wParam)) {
233           case IDOK:
234             EndDialog(hwnd, 1);
235             return 0;
236         }
237         return 0;
238       case WM_CLOSE:
239         EndDialog(hwnd, 1);
240         return 0;
241     }
242     return 0;
243 }
244
245 /*
246  * Dialog-box function for the About box.
247  */
248 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
249                               WPARAM wParam, LPARAM lParam)
250 {
251     switch (msg) {
252       case WM_INITDIALOG:
253         /*
254          * Centre the window.
255          */
256         {                              /* centre the window */
257             RECT rs, rd;
258             HWND hw;
259
260             hw = GetDesktopWindow();
261             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
262                 MoveWindow(hwnd,
263                            (rs.right + rs.left + rd.left - rd.right) / 2,
264                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
265                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
266         }
267
268         SetDlgItemText(hwnd, 100, ver);
269         return 1;
270       case WM_COMMAND:
271         switch (LOWORD(wParam)) {
272           case IDOK:
273             EndDialog(hwnd, 1);
274             return 0;
275           case 101:
276             EnableWindow(hwnd, 0);
277             DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
278             EnableWindow(hwnd, 1);
279             SetActiveWindow(hwnd);
280             return 0;
281         }
282         return 0;
283       case WM_CLOSE:
284         EndDialog(hwnd, 1);
285         return 0;
286     }
287     return 0;
288 }
289
290 /*
291  * Thread to generate a key.
292  */
293 struct rsa_key_thread_params {
294     HWND progressbar;                  /* notify this with progress */
295     HWND dialog;                       /* notify this on completion */
296     int keysize;                       /* bits in key */
297     int is_dsa;
298     struct RSAKey *key;
299     struct dss_key *dsskey;
300 };
301 static DWORD WINAPI generate_rsa_key_thread(void *param)
302 {
303     struct rsa_key_thread_params *params =
304         (struct rsa_key_thread_params *) param;
305     struct progress prog;
306     prog.progbar = params->progressbar;
307
308     progress_update(&prog, PROGFN_INITIALISE, 0, 0);
309
310     if (params->is_dsa)
311         dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
312     else
313         rsa_generate(params->key, params->keysize, progress_update, &prog);
314
315     PostMessage(params->dialog, WM_DONEKEY, 0, 0);
316
317     sfree(params);
318     return 0;
319 }
320
321 struct MainDlgState {
322     int collecting_entropy;
323     int generation_thread_exists;
324     int key_exists;
325     int entropy_got, entropy_required, entropy_size;
326     int keysize;
327     int ssh2, is_dsa;
328     char **commentptr;                 /* points to key.comment or ssh2key.comment */
329     struct ssh2_userkey ssh2key;
330     unsigned *entropy;
331     struct RSAKey key;
332     struct dss_key dsskey;
333 };
334
335 static void hidemany(HWND hwnd, const int *ids, int hideit)
336 {
337     while (*ids) {
338         ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
339     }
340 }
341
342 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
343 {
344     char *buffer;
345     char *dec1, *dec2;
346
347     dec1 = bignum_decimal(key->exponent);
348     dec2 = bignum_decimal(key->modulus);
349     buffer = smalloc(strlen(dec1) + strlen(dec2) +
350                      strlen(key->comment) + 30);
351     sprintf(buffer, "%d %s %s %s",
352             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
353     SetDlgItemText(hwnd, id, buffer);
354     SetDlgItemText(hwnd, idstatic,
355                    "&Public key for pasting into authorized_keys file:");
356     sfree(dec1);
357     sfree(dec2);
358     sfree(buffer);
359 }
360
361 static void setupbigedit2(HWND hwnd, int id, int idstatic,
362                           struct ssh2_userkey *key)
363 {
364     unsigned char *pub_blob;
365     char *buffer, *p;
366     int pub_len;
367     int i;
368
369     pub_blob = key->alg->public_blob(key->data, &pub_len);
370     buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
371                      strlen(key->comment) + 3);
372     strcpy(buffer, key->alg->name);
373     p = buffer + strlen(buffer);
374     *p++ = ' ';
375     i = 0;
376     while (i < pub_len) {
377         int n = (pub_len - i < 3 ? pub_len - i : 3);
378         base64_encode_atom(pub_blob + i, n, p);
379         i += n;
380         p += 4;
381     }
382     *p++ = ' ';
383     strcpy(p, key->comment);
384     SetDlgItemText(hwnd, id, buffer);
385     SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
386                    "OpenSSH authorized_keys2 file:");
387     sfree(pub_blob);
388     sfree(buffer);
389 }
390
391 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
392 {
393     char *dec1, *dec2;
394     FILE *fp;
395
396     dec1 = bignum_decimal(key->exponent);
397     dec2 = bignum_decimal(key->modulus);
398     fp = fopen(filename, "wb");
399     if (!fp)
400         return 0;
401     fprintf(fp, "%d %s %s %s\n",
402             bignum_bitcount(key->modulus), dec1, dec2, key->comment);
403     fclose(fp);
404     sfree(dec1);
405     sfree(dec2);
406     return 1;
407 }
408
409 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
410 {
411     unsigned char *pub_blob;
412     char *p;
413     int pub_len;
414     int i, column;
415     FILE *fp;
416
417     pub_blob = key->alg->public_blob(key->data, &pub_len);
418
419     fp = fopen(filename, "wb");
420     if (!fp)
421         return 0;
422
423     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
424
425     fprintf(fp, "Comment: \"");
426     for (p = key->comment; *p; p++) {
427         if (*p == '\\' || *p == '\"')
428             fputc('\\', fp);
429         fputc(*p, fp);
430     }
431     fprintf(fp, "\"\n");
432
433     i = 0;
434     column = 0;
435     while (i < pub_len) {
436         char buf[5];
437         int n = (pub_len - i < 3 ? pub_len - i : 3);
438         base64_encode_atom(pub_blob + i, n, buf);
439         i += n;
440         buf[4] = '\0';
441         fputs(buf, fp);
442         if (++column >= 16) {
443             fputc('\n', fp);
444             column = 0;
445         }
446     }
447     if (column > 0)
448         fputc('\n', fp);
449     
450     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
451     fclose(fp);
452     sfree(pub_blob);
453     return 1;
454 }
455
456 /*
457  * Dialog-box function for the main PuTTYgen dialog box.
458  */
459 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
460                                 WPARAM wParam, LPARAM lParam)
461 {
462     enum {
463         controlidstart = 100,
464         IDC_TITLE,
465         IDC_BOX_KEY,
466         IDC_NOKEY,
467         IDC_GENERATING,
468         IDC_PROGRESS,
469         IDC_PKSTATIC, IDC_KEYDISPLAY,
470         IDC_FPSTATIC, IDC_FINGERPRINT,
471         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
472         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
473         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
474         IDC_BOX_ACTIONS,
475         IDC_GENSTATIC, IDC_GENERATE,
476         IDC_LOADSTATIC, IDC_LOAD,
477         IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
478         IDC_BOX_PARAMS,
479         IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
480         IDC_BITSSTATIC, IDC_BITS,
481         IDC_ABOUT,
482     };
483     static const int nokey_ids[] = { IDC_NOKEY, 0 };
484     static const int generating_ids[] =
485         { IDC_GENERATING, IDC_PROGRESS, 0 };
486     static const int gotkey_ids[] = {
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, 0
492     };
493     static const char generating_msg[] =
494         "Please wait while a key is generated...";
495     static const char entropy_msg[] =
496         "Please generate some randomness by moving the mouse over the blank area.";
497     struct MainDlgState *state;
498
499     switch (msg) {
500       case WM_INITDIALOG:
501         /*
502          * Centre the window.
503          */
504         {                              /* centre the window */
505             RECT rs, rd;
506             HWND hw;
507
508             hw = GetDesktopWindow();
509             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
510                 MoveWindow(hwnd,
511                            (rs.right + rs.left + rd.left - rd.right) / 2,
512                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
513                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
514         }
515
516         state = smalloc(sizeof(*state));
517         state->generation_thread_exists = FALSE;
518         state->collecting_entropy = FALSE;
519         state->entropy = NULL;
520         state->key_exists = FALSE;
521         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
522         {
523             struct ctlpos cp, cp2;
524
525             /* Accelerators used: acglops1rbd */
526
527             ctlposinit(&cp, hwnd, 4, 4, 4);
528             bartitle(&cp, "Public and private key generation for PuTTY",
529                      IDC_TITLE);
530             beginbox(&cp, "Key", IDC_BOX_KEY);
531             cp2 = cp;
532             statictext(&cp2, "No key.", 1, IDC_NOKEY);
533             cp2 = cp;
534             statictext(&cp2, "", 1, IDC_GENERATING);
535             progressbar(&cp2, IDC_PROGRESS);
536             bigeditctrl(&cp,
537                         "&Public key for pasting into authorized_keys file:",
538                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
539             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
540             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
541                        IDC_FINGERPRINT, 75);
542             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
543                                0);
544             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
545                        IDC_COMMENTEDIT, 75);
546             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
547                            IDC_PASSPHRASE1EDIT, 75);
548             staticpassedit(&cp, "C&onfirm passphrase:",
549                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
550             endbox(&cp);
551             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
552             staticbtn(&cp, "Generate a public/private key pair",
553                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
554             staticbtn(&cp, "Load an existing private key file",
555                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
556             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
557                        "Save p&ublic key", IDC_SAVEPUB,
558                        "&Save private key", IDC_SAVE);
559             endbox(&cp);
560             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
561             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
562                       "SSH&1 (RSA)", IDC_KEYSSH1,
563                       "SSH2 &RSA", IDC_KEYSSH2RSA,
564                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
565             staticedit(&cp, "Number of &bits in a generated key:",
566                        IDC_BITSSTATIC, IDC_BITS, 20);
567             endbox(&cp);
568         }
569         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
570         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
571
572         /*
573          * Initially, hide the progress bar and the key display,
574          * and show the no-key display. Also disable the Save
575          * buttons, because with no key we obviously can't save
576          * anything.
577          */
578         hidemany(hwnd, nokey_ids, FALSE);
579         hidemany(hwnd, generating_ids, TRUE);
580         hidemany(hwnd, gotkey_ids, TRUE);
581         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
582         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
583
584         return 1;
585       case WM_MOUSEMOVE:
586         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
587         if (state->collecting_entropy &&
588             state->entropy && state->entropy_got < state->entropy_required) {
589             state->entropy[state->entropy_got++] = lParam;
590             state->entropy[state->entropy_got++] = GetMessageTime();
591             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
592                                state->entropy_got, 0);
593             if (state->entropy_got >= state->entropy_required) {
594                 struct rsa_key_thread_params *params;
595                 DWORD threadid;
596
597                 /*
598                  * Seed the entropy pool
599                  */
600                 random_add_heavynoise(state->entropy, state->entropy_size);
601                 memset(state->entropy, 0, state->entropy_size);
602                 sfree(state->entropy);
603                 state->collecting_entropy = FALSE;
604
605                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
606                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
607                                    MAKELPARAM(0, PROGRESSRANGE));
608                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
609
610                 params = smalloc(sizeof(*params));
611                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
612                 params->dialog = hwnd;
613                 params->keysize = state->keysize;
614                 params->is_dsa = state->is_dsa;
615                 params->key = &state->key;
616                 params->dsskey = &state->dsskey;
617
618                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
619                                   params, 0, &threadid)) {
620                     MessageBox(hwnd, "Out of thread resources",
621                                "Key generation error",
622                                MB_OK | MB_ICONERROR);
623                     sfree(params);
624                 } else {
625                     state->generation_thread_exists = TRUE;
626                 }
627             }
628         }
629         break;
630       case WM_COMMAND:
631         switch (LOWORD(wParam)) {
632           case IDC_COMMENTEDIT:
633             if (HIWORD(wParam) == EN_CHANGE) {
634                 state = (struct MainDlgState *)
635                     GetWindowLong(hwnd, GWL_USERDATA);
636                 if (state->key_exists) {
637                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
638                     int len = GetWindowTextLength(editctl);
639                     if (*state->commentptr)
640                         sfree(*state->commentptr);
641                     *state->commentptr = smalloc(len + 1);
642                     GetWindowText(editctl, *state->commentptr, len + 1);
643                     if (state->ssh2) {
644                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
645                                       &state->ssh2key);
646                     } else {
647                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
648                                       &state->key);
649                     }
650                 }
651             }
652             break;
653           case IDC_ABOUT:
654             EnableWindow(hwnd, 0);
655             DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
656             EnableWindow(hwnd, 1);
657             SetActiveWindow(hwnd);
658             return 0;
659           case IDC_GENERATE:
660             state =
661                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
662             if (!state->generation_thread_exists) {
663                 BOOL ok;
664                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
665                 if (!ok)
666                     state->keysize = DEFAULT_KEYSIZE;
667                 /* If we ever introduce a new key type, check it here! */
668                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
669                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
670                 if (state->keysize < 256) {
671                     int ret = MessageBox(hwnd,
672                                          "PuTTYgen will not generate a key"
673                                          " smaller than 256 bits.\n"
674                                          "Key length reset to 256. Continue?",
675                                          "PuTTYgen Warning",
676                                          MB_ICONWARNING | MB_OKCANCEL);
677                     if (ret != IDOK)
678                         break;
679                     state->keysize = 256;
680                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
681                 }
682                 hidemany(hwnd, nokey_ids, TRUE);
683                 hidemany(hwnd, generating_ids, FALSE);
684                 hidemany(hwnd, gotkey_ids, TRUE);
685                 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
686                 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
687                 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
688                 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
689                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
690                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
691                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
692                 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
693                 state->key_exists = FALSE;
694                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
695                 state->collecting_entropy = TRUE;
696
697                 /*
698                  * My brief statistical tests on mouse movements
699                  * suggest that there are about 2.5 bits of
700                  * randomness in the x position, 2.5 in the y
701                  * position, and 1.7 in the message time, making
702                  * 5.7 bits of unpredictability per mouse movement.
703                  * However, other people have told me it's far less
704                  * than that, so I'm going to be stupidly cautious
705                  * and knock that down to a nice round 2. With this
706                  * method, we require two words per mouse movement,
707                  * so with 2 bits per mouse movement we expect 2
708                  * bits every 2 words.
709                  */
710                 state->entropy_required = (state->keysize / 2) * 2;
711                 state->entropy_got = 0;
712                 state->entropy_size = (state->entropy_required *
713                                        sizeof(*state->entropy));
714                 state->entropy = smalloc(state->entropy_size);
715
716                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
717                                    MAKELPARAM(0, state->entropy_required));
718                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
719             }
720             break;
721           case IDC_SAVE:
722             state =
723                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
724             if (state->key_exists) {
725                 char filename[FILENAME_MAX];
726                 char passphrase[PASSPHRASE_MAXLEN];
727                 char passphrase2[PASSPHRASE_MAXLEN];
728                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
729                                passphrase, sizeof(passphrase));
730                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
731                                passphrase2, sizeof(passphrase2));
732                 if (strcmp(passphrase, passphrase2)) {
733                     MessageBox(hwnd,
734                                "The two passphrases given do not match.",
735                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
736                     break;
737                 }
738                 if (!*passphrase) {
739                     int ret;
740                     ret = MessageBox(hwnd,
741                                      "Are you sure you want to save this key\n"
742                                      "without a passphrase to protect it?",
743                                      "PuTTYgen Warning",
744                                      MB_YESNO | MB_ICONWARNING);
745                     if (ret != IDYES)
746                         break;
747                 }
748                 if (prompt_keyfile(hwnd, "Save private key as:",
749                                    filename, 1)) {
750                     int ret;
751                     FILE *fp = fopen(filename, "r");
752                     if (fp) {
753                         char buffer[FILENAME_MAX + 80];
754                         fclose(fp);
755                         sprintf(buffer, "Overwrite existing file\n%.*s?",
756                                 FILENAME_MAX, filename);
757                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
758                                          MB_YESNO | MB_ICONWARNING);
759                         if (ret != IDYES)
760                             break;
761                     }
762                     if (state->ssh2) {
763                         ret = ssh2_save_userkey(filename, &state->ssh2key,
764                                                 *passphrase ? passphrase :
765                                                 NULL);
766                     } else {
767                         ret = saversakey(filename, &state->key,
768                                          *passphrase ? passphrase : NULL);
769                     }
770                     if (ret <= 0) {
771                         MessageBox(hwnd, "Unable to save key file",
772                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
773                     }
774                 }
775             }
776             break;
777           case IDC_SAVEPUB:
778             state =
779                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
780             if (state->key_exists) {
781                 char filename[FILENAME_MAX];
782                 if (prompt_keyfile(hwnd, "Save public key as:",
783                                    filename, 1)) {
784                     int ret;
785                     FILE *fp = fopen(filename, "r");
786                     if (fp) {
787                         char buffer[FILENAME_MAX + 80];
788                         fclose(fp);
789                         sprintf(buffer, "Overwrite existing file\n%.*s?",
790                                 FILENAME_MAX, filename);
791                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
792                                          MB_YESNO | MB_ICONWARNING);
793                         if (ret != IDYES)
794                             break;
795                     }
796                     if (state->ssh2) {
797                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
798                     } else {
799                         ret = save_ssh1_pubkey(filename, &state->key);
800                     }
801                     if (ret <= 0) {
802                         MessageBox(hwnd, "Unable to save key file",
803                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
804                     }
805                 }
806             }
807             break;
808           case IDC_LOAD:
809             state =
810                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
811             if (!state->generation_thread_exists) {
812                 char filename[FILENAME_MAX];
813                 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
814                     char passphrase[PASSPHRASE_MAXLEN];
815                     int needs_pass;
816                     int ver;
817                     int ret;
818                     char *comment;
819                     struct PassphraseProcStruct pps;
820                     struct RSAKey newkey1;
821                     struct ssh2_userkey *newkey2 = NULL;
822
823                     ver = keyfile_version(filename);
824                     if (ver == 0) {
825                         MessageBox(NULL, "Couldn't load private key.",
826                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
827                         break;
828                     }
829
830                     comment = NULL;
831                     if (ver == 1)
832                         needs_pass = rsakey_encrypted(filename, &comment);
833                     else
834                         needs_pass =
835                             ssh2_userkey_encrypted(filename, &comment);
836                     pps.passphrase = passphrase;
837                     pps.comment = comment;
838                     do {
839                         if (needs_pass) {
840                             int dlgret;
841                             dlgret = DialogBoxParam(hinst,
842                                                     MAKEINTRESOURCE(210),
843                                                     NULL, PassphraseProc,
844                                                     (LPARAM) & pps);
845                             if (!dlgret) {
846                                 ret = -2;
847                                 break;
848                             }
849                         } else
850                             *passphrase = '\0';
851                         if (ver == 1)
852                             ret =
853                                 loadrsakey(filename, &newkey1, passphrase);
854                         else {
855                             newkey2 =
856                                 ssh2_load_userkey(filename, passphrase);
857                             if (newkey2 == SSH2_WRONG_PASSPHRASE)
858                                 ret = -1;
859                             else if (!newkey2)
860                                 ret = 0;
861                             else
862                                 ret = 1;
863                         }
864                     } while (ret == -1);
865                     if (comment)
866                         sfree(comment);
867                     if (ret == 0) {
868                         MessageBox(NULL, "Couldn't load private key.",
869                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
870                     } else if (ret == 1) {
871                         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
872                         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
873                         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
874                         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
875                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
876                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
877                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
878                         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
879                         /*
880                          * Now update the key controls with all the
881                          * key data.
882                          */
883                         {
884                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
885                                            passphrase);
886                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
887                                            passphrase);
888                             if (ver == 1) {
889                                 char buf[128];
890                                 char *savecomment;
891
892                                 state->ssh2 = FALSE;
893                                 state->commentptr = &state->key.comment;
894                                 state->key = newkey1;
895
896                                 /*
897                                  * Set the key fingerprint.
898                                  */
899                                 savecomment = state->key.comment;
900                                 state->key.comment = NULL;
901                                 rsa_fingerprint(buf, sizeof(buf),
902                                                 &state->key);
903                                 state->key.comment = savecomment;
904
905                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
906                                 /*
907                                  * Construct a decimal representation
908                                  * of the key, for pasting into
909                                  * .ssh/authorized_keys on a Unix box.
910                                  */
911                                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
912                                               IDC_PKSTATIC, &state->key);
913                             } else {
914                                 char *fp;
915                                 char *savecomment;
916
917                                 state->ssh2 = TRUE;
918                                 state->commentptr =
919                                     &state->ssh2key.comment;
920                                 state->ssh2key = *newkey2;      /* structure copy */
921                                 sfree(newkey2);
922
923                                 savecomment = state->ssh2key.comment;
924                                 state->ssh2key.comment = NULL;
925                                 fp =
926                                     state->ssh2key.alg->
927                                     fingerprint(state->ssh2key.data);
928                                 state->ssh2key.comment = savecomment;
929
930                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
931                                 sfree(fp);
932
933                                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
934                                               IDC_PKSTATIC, &state->ssh2key);
935                             }
936                             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
937                                            *state->commentptr);
938                         }
939                         /*
940                          * Finally, hide the progress bar and show
941                          * the key data.
942                          */
943                         hidemany(hwnd, nokey_ids, TRUE);
944                         hidemany(hwnd, generating_ids, TRUE);
945                         hidemany(hwnd, gotkey_ids, FALSE);
946                         state->key_exists = TRUE;
947                     }
948                 }
949             }
950             break;
951         }
952         return 0;
953       case WM_DONEKEY:
954         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
955         state->generation_thread_exists = FALSE;
956         state->key_exists = TRUE;
957         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
958                            MAKELPARAM(0, PROGRESSRANGE));
959         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
960         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
961         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
962         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
963         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
964         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
965         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
966         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
967         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
968         if (state->ssh2) {
969             if (state->is_dsa) {
970                 state->ssh2key.data = &state->dsskey;
971                 state->ssh2key.alg = &ssh_dss;
972             } else {
973                 state->ssh2key.data = &state->key;
974                 state->ssh2key.alg = &ssh_rsa;
975             }
976             state->commentptr = &state->ssh2key.comment;
977         } else {
978             state->commentptr = &state->key.comment;
979         }
980         /*
981          * Invent a comment for the key. We'll do this by including
982          * the date in it. This will be so horrifyingly ugly that
983          * the user will immediately want to change it, which is
984          * what we want :-)
985          */
986         *state->commentptr = smalloc(30);
987         {
988             time_t t;
989             struct tm *tm;
990             time(&t);
991             tm = localtime(&t);
992             if (state->is_dsa)
993                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
994             else
995                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
996         }
997
998         /*
999          * Now update the key controls with all the key data.
1000          */
1001         {
1002             char *savecomment;
1003             /*
1004              * Blank passphrase, initially. This isn't dangerous,
1005              * because we will warn (Are You Sure?) before allowing
1006              * the user to save an unprotected private key.
1007              */
1008             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1009             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1010             /*
1011              * Set the comment.
1012              */
1013             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1014             /*
1015              * Set the key fingerprint.
1016              */
1017             savecomment = *state->commentptr;
1018             *state->commentptr = NULL;
1019             if (state->ssh2) {
1020                 char *fp;
1021                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1022                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1023                 sfree(fp);
1024             } else {
1025                 char buf[128];
1026                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1027                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1028             }
1029             *state->commentptr = savecomment;
1030             /*
1031              * Construct a decimal representation of the key, for
1032              * pasting into .ssh/authorized_keys or
1033              * .ssh/authorized_keys2 on a Unix box.
1034              */
1035             if (state->ssh2) {
1036                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1037                               IDC_PKSTATIC, &state->ssh2key);
1038             } else {
1039                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1040                               IDC_PKSTATIC, &state->key);
1041             }
1042         }
1043         /*
1044          * Finally, hide the progress bar and show the key data.
1045          */
1046         hidemany(hwnd, nokey_ids, TRUE);
1047         hidemany(hwnd, generating_ids, TRUE);
1048         hidemany(hwnd, gotkey_ids, FALSE);
1049         break;
1050       case WM_CLOSE:
1051         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1052         sfree(state);
1053         EndDialog(hwnd, 1);
1054         return 0;
1055     }
1056     return 0;
1057 }
1058
1059 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1060 {
1061     InitCommonControls();
1062     hinst = inst;
1063     random_init();
1064     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1065                      MainDlgProc) != IDOK;
1066 }