Another big batch of memory leak fixes, again mostly on error paths.
[u/mdw/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 load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
959
960 return 1;
961 case WM_MOUSEMOVE:
962 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
963 if (state->collecting_entropy &&
964 state->entropy && state->entropy_got < state->entropy_required) {
965 state->entropy[state->entropy_got++] = lParam;
966 state->entropy[state->entropy_got++] = GetMessageTime();
967 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
968 state->entropy_got, 0);
969 if (state->entropy_got >= state->entropy_required) {
970 struct rsa_key_thread_params *params;
971 DWORD threadid;
972
973 /*
974 * Seed the entropy pool
975 */
976 random_add_heavynoise(state->entropy, state->entropy_size);
977 smemclr(state->entropy, state->entropy_size);
978 sfree(state->entropy);
979 state->collecting_entropy = FALSE;
980
981 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
982 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
983 MAKELPARAM(0, PROGRESSRANGE));
984 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
985
986 params = snew(struct rsa_key_thread_params);
987 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
988 params->dialog = hwnd;
989 params->keysize = state->keysize;
990 params->is_dsa = state->is_dsa;
991 params->key = &state->key;
992 params->dsskey = &state->dsskey;
993
994 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
995 params, 0, &threadid)) {
996 MessageBox(hwnd, "Out of thread resources",
997 "Key generation error",
998 MB_OK | MB_ICONERROR);
999 sfree(params);
1000 } else {
1001 state->generation_thread_exists = TRUE;
1002 }
1003 }
1004 }
1005 break;
1006 case WM_COMMAND:
1007 switch (LOWORD(wParam)) {
1008 case IDC_KEYSSH1:
1009 case IDC_KEYSSH2RSA:
1010 case IDC_KEYSSH2DSA:
1011 {
1012 state = (struct MainDlgState *)
1013 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1014 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1015 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1016 LOWORD(wParam));
1017 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1018 LOWORD(wParam), MF_BYCOMMAND);
1019 }
1020 break;
1021 case IDC_QUIT:
1022 PostMessage(hwnd, WM_CLOSE, 0, 0);
1023 break;
1024 case IDC_COMMENTEDIT:
1025 if (HIWORD(wParam) == EN_CHANGE) {
1026 state = (struct MainDlgState *)
1027 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1028 if (state->key_exists) {
1029 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1030 int len = GetWindowTextLength(editctl);
1031 if (*state->commentptr)
1032 sfree(*state->commentptr);
1033 *state->commentptr = snewn(len + 1, char);
1034 GetWindowText(editctl, *state->commentptr, len + 1);
1035 if (state->ssh2) {
1036 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1037 &state->ssh2key);
1038 } else {
1039 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1040 &state->key);
1041 }
1042 }
1043 }
1044 break;
1045 case IDC_ABOUT:
1046 EnableWindow(hwnd, 0);
1047 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1048 EnableWindow(hwnd, 1);
1049 SetActiveWindow(hwnd);
1050 return 0;
1051 case IDC_GIVEHELP:
1052 if (HIWORD(wParam) == BN_CLICKED ||
1053 HIWORD(wParam) == BN_DOUBLECLICKED) {
1054 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1055 }
1056 return 0;
1057 case IDC_GENERATE:
1058 if (HIWORD(wParam) != BN_CLICKED &&
1059 HIWORD(wParam) != BN_DOUBLECLICKED)
1060 break;
1061 state =
1062 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1063 if (!state->generation_thread_exists) {
1064 BOOL ok;
1065 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1066 if (!ok)
1067 state->keysize = DEFAULT_KEYSIZE;
1068 /* If we ever introduce a new key type, check it here! */
1069 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1070 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1071 if (state->keysize < 256) {
1072 int ret = MessageBox(hwnd,
1073 "PuTTYgen will not generate a key"
1074 " smaller than 256 bits.\n"
1075 "Key length reset to 256. Continue?",
1076 "PuTTYgen Warning",
1077 MB_ICONWARNING | MB_OKCANCEL);
1078 if (ret != IDOK)
1079 break;
1080 state->keysize = 256;
1081 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1082 }
1083 ui_set_state(hwnd, state, 1);
1084 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1085 state->key_exists = FALSE;
1086 state->collecting_entropy = TRUE;
1087
1088 /*
1089 * My brief statistical tests on mouse movements
1090 * suggest that there are about 2.5 bits of
1091 * randomness in the x position, 2.5 in the y
1092 * position, and 1.7 in the message time, making
1093 * 5.7 bits of unpredictability per mouse movement.
1094 * However, other people have told me it's far less
1095 * than that, so I'm going to be stupidly cautious
1096 * and knock that down to a nice round 2. With this
1097 * method, we require two words per mouse movement,
1098 * so with 2 bits per mouse movement we expect 2
1099 * bits every 2 words.
1100 */
1101 state->entropy_required = (state->keysize / 2) * 2;
1102 state->entropy_got = 0;
1103 state->entropy_size = (state->entropy_required *
1104 sizeof(unsigned));
1105 state->entropy = snewn(state->entropy_required, unsigned);
1106
1107 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1108 MAKELPARAM(0, state->entropy_required));
1109 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1110 }
1111 break;
1112 case IDC_SAVE:
1113 case IDC_EXPORT_OPENSSH:
1114 case IDC_EXPORT_SSHCOM:
1115 if (HIWORD(wParam) != BN_CLICKED)
1116 break;
1117 state =
1118 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1119 if (state->key_exists) {
1120 char filename[FILENAME_MAX];
1121 char *passphrase, *passphrase2;
1122 int type, realtype;
1123
1124 if (state->ssh2)
1125 realtype = SSH_KEYTYPE_SSH2;
1126 else
1127 realtype = SSH_KEYTYPE_SSH1;
1128
1129 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1130 type = SSH_KEYTYPE_OPENSSH;
1131 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1132 type = SSH_KEYTYPE_SSHCOM;
1133 else
1134 type = realtype;
1135
1136 if (type != realtype &&
1137 import_target_type(type) != realtype) {
1138 char msg[256];
1139 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1140 " format", (state->ssh2 ? 2 : 1),
1141 (state->ssh2 ? 1 : 2));
1142 MessageBox(hwnd, msg,
1143 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1144 break;
1145 }
1146
1147 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1148 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1149 if (strcmp(passphrase, passphrase2)) {
1150 MessageBox(hwnd,
1151 "The two passphrases given do not match.",
1152 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1153 burnstr(passphrase);
1154 burnstr(passphrase2);
1155 break;
1156 }
1157 burnstr(passphrase2);
1158 if (!*passphrase) {
1159 int ret;
1160 ret = MessageBox(hwnd,
1161 "Are you sure you want to save this key\n"
1162 "without a passphrase to protect it?",
1163 "PuTTYgen Warning",
1164 MB_YESNO | MB_ICONWARNING);
1165 if (ret != IDYES) {
1166 burnstr(passphrase);
1167 break;
1168 }
1169 }
1170 if (prompt_keyfile(hwnd, "Save private key as:",
1171 filename, 1, (type == realtype))) {
1172 int ret;
1173 FILE *fp = fopen(filename, "r");
1174 if (fp) {
1175 char *buffer;
1176 fclose(fp);
1177 buffer = dupprintf("Overwrite existing file\n%s?",
1178 filename);
1179 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1180 MB_YESNO | MB_ICONWARNING);
1181 sfree(buffer);
1182 if (ret != IDYES) {
1183 burnstr(passphrase);
1184 break;
1185 }
1186 }
1187
1188 if (state->ssh2) {
1189 Filename *fn = filename_from_str(filename);
1190 if (type != realtype)
1191 ret = export_ssh2(fn, type, &state->ssh2key,
1192 *passphrase ? passphrase : NULL);
1193 else
1194 ret = ssh2_save_userkey(fn, &state->ssh2key,
1195 *passphrase ? passphrase :
1196 NULL);
1197 filename_free(fn);
1198 } else {
1199 Filename *fn = filename_from_str(filename);
1200 if (type != realtype)
1201 ret = export_ssh1(fn, type, &state->key,
1202 *passphrase ? passphrase : NULL);
1203 else
1204 ret = saversakey(fn, &state->key,
1205 *passphrase ? passphrase : NULL);
1206 filename_free(fn);
1207 }
1208 if (ret <= 0) {
1209 MessageBox(hwnd, "Unable to save key file",
1210 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1211 }
1212 }
1213 burnstr(passphrase);
1214 }
1215 break;
1216 case IDC_SAVEPUB:
1217 if (HIWORD(wParam) != BN_CLICKED)
1218 break;
1219 state =
1220 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1221 if (state->key_exists) {
1222 char filename[FILENAME_MAX];
1223 if (prompt_keyfile(hwnd, "Save public key as:",
1224 filename, 1, 0)) {
1225 int ret;
1226 FILE *fp = fopen(filename, "r");
1227 if (fp) {
1228 char *buffer;
1229 fclose(fp);
1230 buffer = dupprintf("Overwrite existing file\n%s?",
1231 filename);
1232 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1233 MB_YESNO | MB_ICONWARNING);
1234 sfree(buffer);
1235 if (ret != IDYES)
1236 break;
1237 }
1238 if (state->ssh2) {
1239 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1240 } else {
1241 ret = save_ssh1_pubkey(filename, &state->key);
1242 }
1243 if (ret <= 0) {
1244 MessageBox(hwnd, "Unable to save key file",
1245 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1246 }
1247 }
1248 }
1249 break;
1250 case IDC_LOAD:
1251 case IDC_IMPORT:
1252 if (HIWORD(wParam) != BN_CLICKED)
1253 break;
1254 state =
1255 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1256 if (!state->generation_thread_exists) {
1257 char filename[FILENAME_MAX];
1258 if (prompt_keyfile(hwnd, "Load private key:",
1259 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1260 Filename *fn = filename_from_str(filename);
1261 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1262 filename_free(fn);
1263 }
1264 }
1265 break;
1266 }
1267 return 0;
1268 case WM_DONEKEY:
1269 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1270 state->generation_thread_exists = FALSE;
1271 state->key_exists = TRUE;
1272 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1273 MAKELPARAM(0, PROGRESSRANGE));
1274 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1275 if (state->ssh2) {
1276 if (state->is_dsa) {
1277 state->ssh2key.data = &state->dsskey;
1278 state->ssh2key.alg = &ssh_dss;
1279 } else {
1280 state->ssh2key.data = &state->key;
1281 state->ssh2key.alg = &ssh_rsa;
1282 }
1283 state->commentptr = &state->ssh2key.comment;
1284 } else {
1285 state->commentptr = &state->key.comment;
1286 }
1287 /*
1288 * Invent a comment for the key. We'll do this by including
1289 * the date in it. This will be so horrifyingly ugly that
1290 * the user will immediately want to change it, which is
1291 * what we want :-)
1292 */
1293 *state->commentptr = snewn(30, char);
1294 {
1295 struct tm tm;
1296 tm = ltime();
1297 if (state->is_dsa)
1298 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1299 else
1300 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1301 }
1302
1303 /*
1304 * Now update the key controls with all the key data.
1305 */
1306 {
1307 char *savecomment;
1308 /*
1309 * Blank passphrase, initially. This isn't dangerous,
1310 * because we will warn (Are You Sure?) before allowing
1311 * the user to save an unprotected private key.
1312 */
1313 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1314 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1315 /*
1316 * Set the comment.
1317 */
1318 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1319 /*
1320 * Set the key fingerprint.
1321 */
1322 savecomment = *state->commentptr;
1323 *state->commentptr = NULL;
1324 if (state->ssh2) {
1325 char *fp;
1326 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1327 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1328 sfree(fp);
1329 } else {
1330 char buf[128];
1331 rsa_fingerprint(buf, sizeof(buf), &state->key);
1332 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1333 }
1334 *state->commentptr = savecomment;
1335 /*
1336 * Construct a decimal representation of the key, for
1337 * pasting into .ssh/authorized_keys or
1338 * .ssh/authorized_keys2 on a Unix box.
1339 */
1340 if (state->ssh2) {
1341 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1342 IDC_PKSTATIC, &state->ssh2key);
1343 } else {
1344 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1345 IDC_PKSTATIC, &state->key);
1346 }
1347 }
1348 /*
1349 * Finally, hide the progress bar and show the key data.
1350 */
1351 ui_set_state(hwnd, state, 2);
1352 break;
1353 case WM_HELP:
1354 {
1355 int id = ((LPHELPINFO)lParam)->iCtrlId;
1356 char *topic = NULL;
1357 switch (id) {
1358 case IDC_GENERATING:
1359 case IDC_PROGRESS:
1360 case IDC_GENSTATIC:
1361 case IDC_GENERATE:
1362 topic = WINHELP_CTX_puttygen_generate; break;
1363 case IDC_PKSTATIC:
1364 case IDC_KEYDISPLAY:
1365 topic = WINHELP_CTX_puttygen_pastekey; break;
1366 case IDC_FPSTATIC:
1367 case IDC_FINGERPRINT:
1368 topic = WINHELP_CTX_puttygen_fingerprint; break;
1369 case IDC_COMMENTSTATIC:
1370 case IDC_COMMENTEDIT:
1371 topic = WINHELP_CTX_puttygen_comment; break;
1372 case IDC_PASSPHRASE1STATIC:
1373 case IDC_PASSPHRASE1EDIT:
1374 case IDC_PASSPHRASE2STATIC:
1375 case IDC_PASSPHRASE2EDIT:
1376 topic = WINHELP_CTX_puttygen_passphrase; break;
1377 case IDC_LOADSTATIC:
1378 case IDC_LOAD:
1379 topic = WINHELP_CTX_puttygen_load; break;
1380 case IDC_SAVESTATIC:
1381 case IDC_SAVE:
1382 topic = WINHELP_CTX_puttygen_savepriv; break;
1383 case IDC_SAVEPUB:
1384 topic = WINHELP_CTX_puttygen_savepub; break;
1385 case IDC_TYPESTATIC:
1386 case IDC_KEYSSH1:
1387 case IDC_KEYSSH2RSA:
1388 case IDC_KEYSSH2DSA:
1389 topic = WINHELP_CTX_puttygen_keytype; break;
1390 case IDC_BITSSTATIC:
1391 case IDC_BITS:
1392 topic = WINHELP_CTX_puttygen_bits; break;
1393 case IDC_IMPORT:
1394 case IDC_EXPORT_OPENSSH:
1395 case IDC_EXPORT_SSHCOM:
1396 topic = WINHELP_CTX_puttygen_conversions; break;
1397 }
1398 if (topic) {
1399 launch_help(hwnd, topic);
1400 } else {
1401 MessageBeep(0);
1402 }
1403 }
1404 break;
1405 case WM_CLOSE:
1406 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1407 sfree(state);
1408 quit_help(hwnd);
1409 EndDialog(hwnd, 1);
1410 return 0;
1411 }
1412 return 0;
1413 }
1414
1415 void cleanup_exit(int code)
1416 {
1417 shutdown_help();
1418 exit(code);
1419 }
1420
1421 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1422 {
1423 int argc;
1424 char **argv;
1425 int ret;
1426
1427 InitCommonControls();
1428 hinst = inst;
1429 hwnd = NULL;
1430
1431 /*
1432 * See if we can find our Help file.
1433 */
1434 init_help();
1435
1436 split_into_argv(cmdline, &argc, &argv, NULL);
1437
1438 if (argc > 0) {
1439 if (!strcmp(argv[0], "-pgpfp")) {
1440 pgp_fingerprints();
1441 exit(1);
1442 } else {
1443 /*
1444 * Assume the first argument to be a private key file, and
1445 * attempt to load it.
1446 */
1447 cmdline_keyfile = argv[0];
1448 }
1449 }
1450
1451 random_ref();
1452 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1453
1454 cleanup_exit(ret);
1455 return ret; /* just in case optimiser complains */
1456 }