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