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