]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - puttygen.c
INCOMPATIBLE CHANGE to the SSH2 private key file format. There is
[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 /*
410  * Warn about the obsolescent key file format.
411  */
412 void old_keyfile_warning(void)
413 {
414     static const char mbtitle[] = "PuTTY Key File Warning";
415     static const char message[] =
416         "You are loading an SSH 2 private key which has an\n"
417         "old version of the file format. This means your key\n"
418         "file is not fully tamperproof. Future versions of\n"
419         "PuTTY may stop supporting this private key format,\n"
420         "so we recommend you convert your key to the new\n"
421         "format.\n"
422         "\n"
423         "Once the key is loaded into PuTTYgen, you can perform\n"
424         "this conversion simply by saving it again.";
425
426     MessageBox(NULL, message, mbtitle, MB_OK);
427 }
428
429 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
430 {
431     unsigned char *pub_blob;
432     char *p;
433     int pub_len;
434     int i, column;
435     FILE *fp;
436
437     pub_blob = key->alg->public_blob(key->data, &pub_len);
438
439     fp = fopen(filename, "wb");
440     if (!fp)
441         return 0;
442
443     fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
444
445     fprintf(fp, "Comment: \"");
446     for (p = key->comment; *p; p++) {
447         if (*p == '\\' || *p == '\"')
448             fputc('\\', fp);
449         fputc(*p, fp);
450     }
451     fprintf(fp, "\"\n");
452
453     i = 0;
454     column = 0;
455     while (i < pub_len) {
456         char buf[5];
457         int n = (pub_len - i < 3 ? pub_len - i : 3);
458         base64_encode_atom(pub_blob + i, n, buf);
459         i += n;
460         buf[4] = '\0';
461         fputs(buf, fp);
462         if (++column >= 16) {
463             fputc('\n', fp);
464             column = 0;
465         }
466     }
467     if (column > 0)
468         fputc('\n', fp);
469     
470     fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
471     fclose(fp);
472     sfree(pub_blob);
473     return 1;
474 }
475
476 /*
477  * Dialog-box function for the main PuTTYgen dialog box.
478  */
479 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
480                                 WPARAM wParam, LPARAM lParam)
481 {
482     enum {
483         controlidstart = 100,
484         IDC_TITLE,
485         IDC_BOX_KEY,
486         IDC_NOKEY,
487         IDC_GENERATING,
488         IDC_PROGRESS,
489         IDC_PKSTATIC, IDC_KEYDISPLAY,
490         IDC_FPSTATIC, IDC_FINGERPRINT,
491         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
492         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
493         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
494         IDC_BOX_ACTIONS,
495         IDC_GENSTATIC, IDC_GENERATE,
496         IDC_LOADSTATIC, IDC_LOAD,
497         IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
498         IDC_BOX_PARAMS,
499         IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
500         IDC_BITSSTATIC, IDC_BITS,
501         IDC_ABOUT,
502     };
503     static const int nokey_ids[] = { IDC_NOKEY, 0 };
504     static const int generating_ids[] =
505         { IDC_GENERATING, IDC_PROGRESS, 0 };
506     static const int gotkey_ids[] = {
507         IDC_PKSTATIC, IDC_KEYDISPLAY,
508         IDC_FPSTATIC, IDC_FINGERPRINT,
509         IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
510         IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
511         IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
512     };
513     static const char generating_msg[] =
514         "Please wait while a key is generated...";
515     static const char entropy_msg[] =
516         "Please generate some randomness by moving the mouse over the blank area.";
517     struct MainDlgState *state;
518
519     switch (msg) {
520       case WM_INITDIALOG:
521         /*
522          * Centre the window.
523          */
524         {                              /* centre the window */
525             RECT rs, rd;
526             HWND hw;
527
528             hw = GetDesktopWindow();
529             if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
530                 MoveWindow(hwnd,
531                            (rs.right + rs.left + rd.left - rd.right) / 2,
532                            (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
533                            rd.right - rd.left, rd.bottom - rd.top, TRUE);
534         }
535
536         state = smalloc(sizeof(*state));
537         state->generation_thread_exists = FALSE;
538         state->collecting_entropy = FALSE;
539         state->entropy = NULL;
540         state->key_exists = FALSE;
541         SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
542         {
543             struct ctlpos cp, cp2;
544
545             /* Accelerators used: acglops1rbd */
546
547             ctlposinit(&cp, hwnd, 4, 4, 4);
548             bartitle(&cp, "Public and private key generation for PuTTY",
549                      IDC_TITLE);
550             beginbox(&cp, "Key", IDC_BOX_KEY);
551             cp2 = cp;
552             statictext(&cp2, "No key.", 1, IDC_NOKEY);
553             cp2 = cp;
554             statictext(&cp2, "", 1, IDC_GENERATING);
555             progressbar(&cp2, IDC_PROGRESS);
556             bigeditctrl(&cp,
557                         "&Public key for pasting into authorized_keys file:",
558                         IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
559             SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
560             staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
561                        IDC_FINGERPRINT, 75);
562             SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
563                                0);
564             staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
565                        IDC_COMMENTEDIT, 75);
566             staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
567                            IDC_PASSPHRASE1EDIT, 75);
568             staticpassedit(&cp, "C&onfirm passphrase:",
569                            IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
570             endbox(&cp);
571             beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
572             staticbtn(&cp, "Generate a public/private key pair",
573                       IDC_GENSTATIC, "&Generate", IDC_GENERATE);
574             staticbtn(&cp, "Load an existing private key file",
575                       IDC_LOADSTATIC, "&Load", IDC_LOAD);
576             static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
577                        "Save p&ublic key", IDC_SAVEPUB,
578                        "&Save private key", IDC_SAVE);
579             endbox(&cp);
580             beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
581             radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
582                       "SSH&1 (RSA)", IDC_KEYSSH1,
583                       "SSH2 &RSA", IDC_KEYSSH2RSA,
584                       "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
585             staticedit(&cp, "Number of &bits in a generated key:",
586                        IDC_BITSSTATIC, IDC_BITS, 20);
587             endbox(&cp);
588         }
589         CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
590         SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
591
592         /*
593          * Initially, hide the progress bar and the key display,
594          * and show the no-key display. Also disable the Save
595          * buttons, because with no key we obviously can't save
596          * anything.
597          */
598         hidemany(hwnd, nokey_ids, FALSE);
599         hidemany(hwnd, generating_ids, TRUE);
600         hidemany(hwnd, gotkey_ids, TRUE);
601         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
602         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
603
604         return 1;
605       case WM_MOUSEMOVE:
606         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
607         if (state->collecting_entropy &&
608             state->entropy && state->entropy_got < state->entropy_required) {
609             state->entropy[state->entropy_got++] = lParam;
610             state->entropy[state->entropy_got++] = GetMessageTime();
611             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
612                                state->entropy_got, 0);
613             if (state->entropy_got >= state->entropy_required) {
614                 struct rsa_key_thread_params *params;
615                 DWORD threadid;
616
617                 /*
618                  * Seed the entropy pool
619                  */
620                 random_add_heavynoise(state->entropy, state->entropy_size);
621                 memset(state->entropy, 0, state->entropy_size);
622                 sfree(state->entropy);
623                 state->collecting_entropy = FALSE;
624
625                 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
626                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
627                                    MAKELPARAM(0, PROGRESSRANGE));
628                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
629
630                 params = smalloc(sizeof(*params));
631                 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
632                 params->dialog = hwnd;
633                 params->keysize = state->keysize;
634                 params->is_dsa = state->is_dsa;
635                 params->key = &state->key;
636                 params->dsskey = &state->dsskey;
637
638                 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
639                                   params, 0, &threadid)) {
640                     MessageBox(hwnd, "Out of thread resources",
641                                "Key generation error",
642                                MB_OK | MB_ICONERROR);
643                     sfree(params);
644                 } else {
645                     state->generation_thread_exists = TRUE;
646                 }
647             }
648         }
649         break;
650       case WM_COMMAND:
651         switch (LOWORD(wParam)) {
652           case IDC_COMMENTEDIT:
653             if (HIWORD(wParam) == EN_CHANGE) {
654                 state = (struct MainDlgState *)
655                     GetWindowLong(hwnd, GWL_USERDATA);
656                 if (state->key_exists) {
657                     HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
658                     int len = GetWindowTextLength(editctl);
659                     if (*state->commentptr)
660                         sfree(*state->commentptr);
661                     *state->commentptr = smalloc(len + 1);
662                     GetWindowText(editctl, *state->commentptr, len + 1);
663                     if (state->ssh2) {
664                         setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
665                                       &state->ssh2key);
666                     } else {
667                         setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
668                                       &state->key);
669                     }
670                 }
671             }
672             break;
673           case IDC_ABOUT:
674             EnableWindow(hwnd, 0);
675             DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
676             EnableWindow(hwnd, 1);
677             SetActiveWindow(hwnd);
678             return 0;
679           case IDC_GENERATE:
680             state =
681                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
682             if (!state->generation_thread_exists) {
683                 BOOL ok;
684                 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
685                 if (!ok)
686                     state->keysize = DEFAULT_KEYSIZE;
687                 /* If we ever introduce a new key type, check it here! */
688                 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
689                 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
690                 if (state->keysize < 256) {
691                     int ret = MessageBox(hwnd,
692                                          "PuTTYgen will not generate a key"
693                                          " smaller than 256 bits.\n"
694                                          "Key length reset to 256. Continue?",
695                                          "PuTTYgen Warning",
696                                          MB_ICONWARNING | MB_OKCANCEL);
697                     if (ret != IDOK)
698                         break;
699                     state->keysize = 256;
700                     SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
701                 }
702                 hidemany(hwnd, nokey_ids, TRUE);
703                 hidemany(hwnd, generating_ids, FALSE);
704                 hidemany(hwnd, gotkey_ids, TRUE);
705                 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
706                 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
707                 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
708                 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
709                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
710                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
711                 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
712                 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
713                 state->key_exists = FALSE;
714                 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
715                 state->collecting_entropy = TRUE;
716
717                 /*
718                  * My brief statistical tests on mouse movements
719                  * suggest that there are about 2.5 bits of
720                  * randomness in the x position, 2.5 in the y
721                  * position, and 1.7 in the message time, making
722                  * 5.7 bits of unpredictability per mouse movement.
723                  * However, other people have told me it's far less
724                  * than that, so I'm going to be stupidly cautious
725                  * and knock that down to a nice round 2. With this
726                  * method, we require two words per mouse movement,
727                  * so with 2 bits per mouse movement we expect 2
728                  * bits every 2 words.
729                  */
730                 state->entropy_required = (state->keysize / 2) * 2;
731                 state->entropy_got = 0;
732                 state->entropy_size = (state->entropy_required *
733                                        sizeof(*state->entropy));
734                 state->entropy = smalloc(state->entropy_size);
735
736                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
737                                    MAKELPARAM(0, state->entropy_required));
738                 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
739             }
740             break;
741           case IDC_SAVE:
742             state =
743                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
744             if (state->key_exists) {
745                 char filename[FILENAME_MAX];
746                 char passphrase[PASSPHRASE_MAXLEN];
747                 char passphrase2[PASSPHRASE_MAXLEN];
748                 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
749                                passphrase, sizeof(passphrase));
750                 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
751                                passphrase2, sizeof(passphrase2));
752                 if (strcmp(passphrase, passphrase2)) {
753                     MessageBox(hwnd,
754                                "The two passphrases given do not match.",
755                                "PuTTYgen Error", MB_OK | MB_ICONERROR);
756                     break;
757                 }
758                 if (!*passphrase) {
759                     int ret;
760                     ret = MessageBox(hwnd,
761                                      "Are you sure you want to save this key\n"
762                                      "without a passphrase to protect it?",
763                                      "PuTTYgen Warning",
764                                      MB_YESNO | MB_ICONWARNING);
765                     if (ret != IDYES)
766                         break;
767                 }
768                 if (prompt_keyfile(hwnd, "Save private key as:",
769                                    filename, 1)) {
770                     int ret;
771                     FILE *fp = fopen(filename, "r");
772                     if (fp) {
773                         char buffer[FILENAME_MAX + 80];
774                         fclose(fp);
775                         sprintf(buffer, "Overwrite existing file\n%.*s?",
776                                 FILENAME_MAX, filename);
777                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
778                                          MB_YESNO | MB_ICONWARNING);
779                         if (ret != IDYES)
780                             break;
781                     }
782                     if (state->ssh2) {
783                         ret = ssh2_save_userkey(filename, &state->ssh2key,
784                                                 *passphrase ? passphrase :
785                                                 NULL);
786                     } else {
787                         ret = saversakey(filename, &state->key,
788                                          *passphrase ? passphrase : NULL);
789                     }
790                     if (ret <= 0) {
791                         MessageBox(hwnd, "Unable to save key file",
792                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
793                     }
794                 }
795             }
796             break;
797           case IDC_SAVEPUB:
798             state =
799                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
800             if (state->key_exists) {
801                 char filename[FILENAME_MAX];
802                 if (prompt_keyfile(hwnd, "Save public key as:",
803                                    filename, 1)) {
804                     int ret;
805                     FILE *fp = fopen(filename, "r");
806                     if (fp) {
807                         char buffer[FILENAME_MAX + 80];
808                         fclose(fp);
809                         sprintf(buffer, "Overwrite existing file\n%.*s?",
810                                 FILENAME_MAX, filename);
811                         ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
812                                          MB_YESNO | MB_ICONWARNING);
813                         if (ret != IDYES)
814                             break;
815                     }
816                     if (state->ssh2) {
817                         ret = save_ssh2_pubkey(filename, &state->ssh2key);
818                     } else {
819                         ret = save_ssh1_pubkey(filename, &state->key);
820                     }
821                     if (ret <= 0) {
822                         MessageBox(hwnd, "Unable to save key file",
823                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
824                     }
825                 }
826             }
827             break;
828           case IDC_LOAD:
829             state =
830                 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
831             if (!state->generation_thread_exists) {
832                 char filename[FILENAME_MAX];
833                 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
834                     char passphrase[PASSPHRASE_MAXLEN];
835                     int needs_pass;
836                     int ver;
837                     int ret;
838                     char *comment;
839                     struct PassphraseProcStruct pps;
840                     struct RSAKey newkey1;
841                     struct ssh2_userkey *newkey2 = NULL;
842
843                     ver = keyfile_version(filename);
844                     if (ver == 0) {
845                         MessageBox(NULL, "Couldn't load private key.",
846                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
847                         break;
848                     }
849
850                     comment = NULL;
851                     if (ver == 1)
852                         needs_pass = rsakey_encrypted(filename, &comment);
853                     else
854                         needs_pass =
855                             ssh2_userkey_encrypted(filename, &comment);
856                     pps.passphrase = passphrase;
857                     pps.comment = comment;
858                     do {
859                         if (needs_pass) {
860                             int dlgret;
861                             dlgret = DialogBoxParam(hinst,
862                                                     MAKEINTRESOURCE(210),
863                                                     NULL, PassphraseProc,
864                                                     (LPARAM) & pps);
865                             if (!dlgret) {
866                                 ret = -2;
867                                 break;
868                             }
869                         } else
870                             *passphrase = '\0';
871                         if (ver == 1)
872                             ret =
873                                 loadrsakey(filename, &newkey1, passphrase);
874                         else {
875                             newkey2 =
876                                 ssh2_load_userkey(filename, passphrase);
877                             if (newkey2 == SSH2_WRONG_PASSPHRASE)
878                                 ret = -1;
879                             else if (!newkey2)
880                                 ret = 0;
881                             else
882                                 ret = 1;
883                         }
884                     } while (ret == -1);
885                     if (comment)
886                         sfree(comment);
887                     if (ret == 0) {
888                         MessageBox(NULL, "Couldn't load private key.",
889                                    "PuTTYgen Error", MB_OK | MB_ICONERROR);
890                     } else if (ret == 1) {
891                         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
892                         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
893                         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
894                         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
895                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
896                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
897                         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
898                         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
899                         /*
900                          * Now update the key controls with all the
901                          * key data.
902                          */
903                         {
904                             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
905                                            passphrase);
906                             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
907                                            passphrase);
908                             if (ver == 1) {
909                                 char buf[128];
910                                 char *savecomment;
911
912                                 state->ssh2 = FALSE;
913                                 state->commentptr = &state->key.comment;
914                                 state->key = newkey1;
915
916                                 /*
917                                  * Set the key fingerprint.
918                                  */
919                                 savecomment = state->key.comment;
920                                 state->key.comment = NULL;
921                                 rsa_fingerprint(buf, sizeof(buf),
922                                                 &state->key);
923                                 state->key.comment = savecomment;
924
925                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
926                                 /*
927                                  * Construct a decimal representation
928                                  * of the key, for pasting into
929                                  * .ssh/authorized_keys on a Unix box.
930                                  */
931                                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
932                                               IDC_PKSTATIC, &state->key);
933                             } else {
934                                 char *fp;
935                                 char *savecomment;
936
937                                 state->ssh2 = TRUE;
938                                 state->commentptr =
939                                     &state->ssh2key.comment;
940                                 state->ssh2key = *newkey2;      /* structure copy */
941                                 sfree(newkey2);
942
943                                 savecomment = state->ssh2key.comment;
944                                 state->ssh2key.comment = NULL;
945                                 fp =
946                                     state->ssh2key.alg->
947                                     fingerprint(state->ssh2key.data);
948                                 state->ssh2key.comment = savecomment;
949
950                                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
951                                 sfree(fp);
952
953                                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
954                                               IDC_PKSTATIC, &state->ssh2key);
955                             }
956                             SetDlgItemText(hwnd, IDC_COMMENTEDIT,
957                                            *state->commentptr);
958                         }
959                         /*
960                          * Finally, hide the progress bar and show
961                          * the key data.
962                          */
963                         hidemany(hwnd, nokey_ids, TRUE);
964                         hidemany(hwnd, generating_ids, TRUE);
965                         hidemany(hwnd, gotkey_ids, FALSE);
966                         state->key_exists = TRUE;
967                     }
968                 }
969             }
970             break;
971         }
972         return 0;
973       case WM_DONEKEY:
974         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
975         state->generation_thread_exists = FALSE;
976         state->key_exists = TRUE;
977         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
978                            MAKELPARAM(0, PROGRESSRANGE));
979         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
980         EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
981         EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
982         EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
983         EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
984         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
985         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
986         EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
987         EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
988         if (state->ssh2) {
989             if (state->is_dsa) {
990                 state->ssh2key.data = &state->dsskey;
991                 state->ssh2key.alg = &ssh_dss;
992             } else {
993                 state->ssh2key.data = &state->key;
994                 state->ssh2key.alg = &ssh_rsa;
995             }
996             state->commentptr = &state->ssh2key.comment;
997         } else {
998             state->commentptr = &state->key.comment;
999         }
1000         /*
1001          * Invent a comment for the key. We'll do this by including
1002          * the date in it. This will be so horrifyingly ugly that
1003          * the user will immediately want to change it, which is
1004          * what we want :-)
1005          */
1006         *state->commentptr = smalloc(30);
1007         {
1008             time_t t;
1009             struct tm *tm;
1010             time(&t);
1011             tm = localtime(&t);
1012             if (state->is_dsa)
1013                 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1014             else
1015                 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1016         }
1017
1018         /*
1019          * Now update the key controls with all the key data.
1020          */
1021         {
1022             char *savecomment;
1023             /*
1024              * Blank passphrase, initially. This isn't dangerous,
1025              * because we will warn (Are You Sure?) before allowing
1026              * the user to save an unprotected private key.
1027              */
1028             SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1029             SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1030             /*
1031              * Set the comment.
1032              */
1033             SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1034             /*
1035              * Set the key fingerprint.
1036              */
1037             savecomment = *state->commentptr;
1038             *state->commentptr = NULL;
1039             if (state->ssh2) {
1040                 char *fp;
1041                 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1042                 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1043                 sfree(fp);
1044             } else {
1045                 char buf[128];
1046                 rsa_fingerprint(buf, sizeof(buf), &state->key);
1047                 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1048             }
1049             *state->commentptr = savecomment;
1050             /*
1051              * Construct a decimal representation of the key, for
1052              * pasting into .ssh/authorized_keys or
1053              * .ssh/authorized_keys2 on a Unix box.
1054              */
1055             if (state->ssh2) {
1056                 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1057                               IDC_PKSTATIC, &state->ssh2key);
1058             } else {
1059                 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1060                               IDC_PKSTATIC, &state->key);
1061             }
1062         }
1063         /*
1064          * Finally, hide the progress bar and show the key data.
1065          */
1066         hidemany(hwnd, nokey_ids, TRUE);
1067         hidemany(hwnd, generating_ids, TRUE);
1068         hidemany(hwnd, gotkey_ids, FALSE);
1069         break;
1070       case WM_CLOSE:
1071         state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1072         sfree(state);
1073         EndDialog(hwnd, 1);
1074         return 0;
1075     }
1076     return 0;
1077 }
1078
1079 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1080 {
1081     InitCommonControls();
1082     hinst = inst;
1083     random_init();
1084     return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1085                      MainDlgProc) != IDOK;
1086 }