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