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