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