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