2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
22 #define WM_DONEKEY (WM_APP + 1)
24 #define DEFAULT_KEYSIZE 2048
26 static char *cmdline_keyfile = NULL;
29 * Print a modal (Really Bad) message box and perform a fatal exit.
31 void modalfatalbox(const char *fmt, ...)
37 stuff = dupvprintf(fmt, ap);
39 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
40 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
46 * Print a non-fatal message box and do not exit.
48 void nonfatal(const char *fmt, ...)
54 stuff = dupvprintf(fmt, ap);
56 MessageBox(NULL, stuff, "PuTTYgen Error",
57 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
61 /* ----------------------------------------------------------------------
62 * Progress report code. This is really horrible :-)
64 #define PROGRESSRANGE 65535
70 unsigned startpoint, total;
71 unsigned param, current, n; /* if exponential */
72 unsigned mult; /* if linear */
74 unsigned total, divisor, range;
78 static void progress_update(void *param, int action, int phase, int iprogress)
80 struct progress *p = (struct progress *) param;
81 unsigned progress = iprogress;
84 if (action < PROGFN_READY && p->nphases < phase)
87 case PROGFN_INITIALISE:
90 case PROGFN_LIN_PHASE:
91 p->phases[phase-1].exponential = 0;
92 p->phases[phase-1].mult = p->phases[phase].total / progress;
94 case PROGFN_EXP_PHASE:
95 p->phases[phase-1].exponential = 1;
96 p->phases[phase-1].param = 0x10000 + progress;
97 p->phases[phase-1].current = p->phases[phase-1].total;
98 p->phases[phase-1].n = 0;
100 case PROGFN_PHASE_EXTENT:
101 p->phases[phase-1].total = progress;
107 for (i = 0; i < p->nphases; i++) {
108 p->phases[i].startpoint = total;
109 total += p->phases[i].total;
112 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
113 p->range = p->total / p->divisor;
114 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
117 case PROGFN_PROGRESS:
118 if (p->phases[phase-1].exponential) {
119 while (p->phases[phase-1].n < progress) {
120 p->phases[phase-1].n++;
121 p->phases[phase-1].current *= p->phases[phase-1].param;
122 p->phases[phase-1].current /= 0x10000;
124 position = (p->phases[phase-1].startpoint +
125 p->phases[phase-1].total - p->phases[phase-1].current);
127 position = (p->phases[phase-1].startpoint +
128 progress * p->phases[phase-1].mult);
130 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
137 struct PassphraseProcStruct {
143 * Dialog-box function for the passphrase box.
145 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
146 WPARAM wParam, LPARAM lParam)
148 static char **passphrase = NULL;
149 struct PassphraseProcStruct *p;
153 SetForegroundWindow(hwnd);
154 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
155 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
160 { /* centre the window */
164 hw = GetDesktopWindow();
165 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
167 (rs.right + rs.left + rd.left - rd.right) / 2,
168 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
169 rd.right - rd.left, rd.bottom - rd.top, TRUE);
172 p = (struct PassphraseProcStruct *) lParam;
173 passphrase = p->passphrase;
175 SetDlgItemText(hwnd, 101, p->comment);
176 burnstr(*passphrase);
177 *passphrase = dupstr("");
178 SetDlgItemText(hwnd, 102, *passphrase);
181 switch (LOWORD(wParam)) {
191 case 102: /* edit box */
192 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
193 burnstr(*passphrase);
194 *passphrase = GetDlgItemText_alloc(hwnd, 102);
207 * Prompt for a key file. Assumes the filename buffer is of size
210 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
211 char *filename, int save, int ppk)
214 memset(&of, 0, sizeof(of));
217 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
218 "All Files (*.*)\0*\0\0\0";
219 of.lpstrDefExt = ".ppk";
221 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
223 of.lpstrCustomFilter = NULL;
225 of.lpstrFile = filename;
227 of.nMaxFile = FILENAME_MAX;
228 of.lpstrFileTitle = NULL;
229 of.lpstrTitle = dlgtitle;
231 return request_file(NULL, &of, FALSE, save);
235 * Dialog-box function for the Licence box.
237 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
238 WPARAM wParam, LPARAM lParam)
245 { /* centre the window */
249 hw = GetDesktopWindow();
250 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
252 (rs.right + rs.left + rd.left - rd.right) / 2,
253 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
254 rd.right - rd.left, rd.bottom - rd.top, TRUE);
257 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
260 switch (LOWORD(wParam)) {
275 * Dialog-box function for the About box.
277 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
278 WPARAM wParam, LPARAM lParam)
285 { /* centre the window */
289 hw = GetDesktopWindow();
290 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
292 (rs.right + rs.left + rd.left - rd.right) / 2,
293 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
294 rd.right - rd.left, rd.bottom - rd.top, TRUE);
298 char *text = dupprintf
299 ("Pageant\r\n\r\n%s\r\n\r\n%s",
301 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
302 SetDlgItemText(hwnd, 1000, text);
307 switch (LOWORD(wParam)) {
313 EnableWindow(hwnd, 0);
314 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
315 EnableWindow(hwnd, 1);
316 SetActiveWindow(hwnd);
327 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
330 * Thread to generate a key.
332 struct rsa_key_thread_params {
333 HWND progressbar; /* notify this with progress */
334 HWND dialog; /* notify this on completion */
335 int keysize; /* bits in key */
339 struct dss_key *dsskey;
340 struct ec_key *eckey;
343 static DWORD WINAPI generate_rsa_key_thread(void *param)
345 struct rsa_key_thread_params *params =
346 (struct rsa_key_thread_params *) param;
347 struct progress prog;
348 prog.progbar = params->progressbar;
350 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
352 if (params->keytype == DSA)
353 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
354 else if (params->keytype == ECDSA)
355 ec_generate(params->eckey, params->keysize, progress_update, &prog);
356 else if (params->keytype == ED25519)
357 ec_edgenerate(params->eckey, params->keysize, progress_update, &prog);
359 rsa_generate(params->key, params->keysize, progress_update, &prog);
361 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
367 struct MainDlgState {
368 int collecting_entropy;
369 int generation_thread_exists;
371 int entropy_got, entropy_required, entropy_size;
375 char **commentptr; /* points to key.comment or ssh2key.comment */
376 struct ssh2_userkey ssh2key;
380 struct dss_key dsskey;
383 HMENU filemenu, keymenu, cvtmenu;
386 static void hidemany(HWND hwnd, const int *ids, int hideit)
389 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
393 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
395 char *buffer = ssh1_pubkey_str(key);
396 SetDlgItemText(hwnd, id, buffer);
397 SetDlgItemText(hwnd, idstatic,
398 "&Public key for pasting into authorized_keys file:");
402 static void setupbigedit2(HWND hwnd, int id, int idstatic,
403 struct ssh2_userkey *key)
405 char *buffer = ssh2_pubkey_openssh_str(key);
406 SetDlgItemText(hwnd, id, buffer);
407 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
408 "OpenSSH authorized_keys file:");
413 * Warn about the obsolescent key file format.
415 void old_keyfile_warning(void)
417 static const char mbtitle[] = "PuTTY Key File Warning";
418 static const char message[] =
419 "You are loading an SSH-2 private key which has an\n"
420 "old version of the file format. This means your key\n"
421 "file is not fully tamperproof. Future versions of\n"
422 "PuTTY may stop supporting this private key format,\n"
423 "so we recommend you convert your key to the new\n"
426 "Once the key is loaded into PuTTYgen, you can perform\n"
427 "this conversion simply by saving it again.";
429 MessageBox(NULL, message, mbtitle, MB_OK);
433 controlidstart = 100,
440 IDC_PKSTATIC, IDC_KEYDISPLAY,
441 IDC_FPSTATIC, IDC_FINGERPRINT,
442 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
443 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
444 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
446 IDC_GENSTATIC, IDC_GENERATE,
447 IDC_LOADSTATIC, IDC_LOAD,
448 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
450 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
451 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
452 IDC_BITSSTATIC, IDC_BITS,
456 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
460 static const int nokey_ids[] = { IDC_NOKEY, 0 };
461 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
462 static const int gotkey_ids[] = {
463 IDC_PKSTATIC, IDC_KEYDISPLAY,
464 IDC_FPSTATIC, IDC_FINGERPRINT,
465 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
466 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
467 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
471 * Small UI helper function to switch the state of the main dialog
472 * by enabling and disabling controls and menu items.
474 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
480 hidemany(hwnd, nokey_ids, FALSE);
481 hidemany(hwnd, generating_ids, TRUE);
482 hidemany(hwnd, gotkey_ids, TRUE);
483 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
484 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
485 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
486 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
487 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
488 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
489 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
490 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
491 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
492 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
493 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
494 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
495 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
496 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
497 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
498 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
499 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
500 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
501 MF_ENABLED|MF_BYCOMMAND);
502 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
503 MF_ENABLED|MF_BYCOMMAND);
504 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
505 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
506 MF_GRAYED|MF_BYCOMMAND);
507 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
508 MF_GRAYED|MF_BYCOMMAND);
509 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
510 MF_GRAYED|MF_BYCOMMAND);
512 case 1: /* generating key */
513 hidemany(hwnd, nokey_ids, TRUE);
514 hidemany(hwnd, generating_ids, FALSE);
515 hidemany(hwnd, gotkey_ids, TRUE);
516 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
517 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
518 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
519 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
520 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
521 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
522 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
523 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
524 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
525 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
526 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
527 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
528 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
529 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
530 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
531 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
532 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
533 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
534 MF_GRAYED|MF_BYCOMMAND);
535 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
536 MF_GRAYED|MF_BYCOMMAND);
537 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
538 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
539 MF_GRAYED|MF_BYCOMMAND);
540 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
541 MF_GRAYED|MF_BYCOMMAND);
542 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
543 MF_GRAYED|MF_BYCOMMAND);
546 hidemany(hwnd, nokey_ids, TRUE);
547 hidemany(hwnd, generating_ids, TRUE);
548 hidemany(hwnd, gotkey_ids, FALSE);
549 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
550 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
551 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
553 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
554 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
555 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
556 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
557 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 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_ENABLED|MF_BYCOMMAND);
561 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|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->keymenu, IDC_KEYSSH2ECDSA,
567 MF_ENABLED|MF_BYCOMMAND);
568 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
569 MF_ENABLED|MF_BYCOMMAND);
570 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
572 * Enable export menu items if and only if the key type
573 * supports this kind of export.
575 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
576 #define do_export_menuitem(x,y) \
577 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
578 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
579 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
580 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
581 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
582 #undef do_export_menuitem
587 void load_key_file(HWND hwnd, struct MainDlgState *state,
588 Filename *filename, int was_import_cmd)
594 const char *errmsg = NULL;
596 struct RSAKey newkey1;
597 struct ssh2_userkey *newkey2 = NULL;
599 type = realtype = key_type(filename);
600 if (type != SSH_KEYTYPE_SSH1 &&
601 type != SSH_KEYTYPE_SSH2 &&
602 !import_possible(type)) {
603 char *msg = dupprintf("Couldn't load private key (%s)",
604 key_type_to_str(type));
605 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
606 HELPCTXID(errors_cantloadkey));
611 if (type != SSH_KEYTYPE_SSH1 &&
612 type != SSH_KEYTYPE_SSH2) {
614 type = import_target_type(type);
619 if (realtype == SSH_KEYTYPE_SSH1)
620 needs_pass = rsakey_encrypted(filename, &comment);
621 else if (realtype == SSH_KEYTYPE_SSH2)
622 needs_pass = ssh2_userkey_encrypted(filename, &comment);
624 needs_pass = import_encrypted(filename, realtype, &comment);
631 struct PassphraseProcStruct pps;
632 pps.passphrase = &passphrase;
633 pps.comment = comment;
634 dlgret = DialogBoxParam(hinst,
635 MAKEINTRESOURCE(210),
636 NULL, PassphraseProc,
642 assert(passphrase != NULL);
644 passphrase = dupstr("");
645 if (type == SSH_KEYTYPE_SSH1) {
646 if (realtype == type)
647 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
649 ret = import_ssh1(filename, realtype, &newkey1,
650 passphrase, &errmsg);
652 if (realtype == type)
653 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
655 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
656 if (newkey2 == SSH2_WRONG_PASSPHRASE)
667 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
668 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
669 HELPCTXID(errors_cantloadkey));
671 } else if (ret == 1) {
673 * Now update the key controls with all the
677 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
679 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
681 if (type == SSH_KEYTYPE_SSH1) {
686 state->commentptr = &state->key.comment;
687 state->key = newkey1;
690 * Set the key fingerprint.
692 savecomment = state->key.comment;
693 state->key.comment = NULL;
694 rsa_fingerprint(buf, sizeof(buf),
696 state->key.comment = savecomment;
698 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
700 * Construct a decimal representation
701 * of the key, for pasting into
702 * .ssh/authorized_keys on a Unix box.
704 setupbigedit1(hwnd, IDC_KEYDISPLAY,
705 IDC_PKSTATIC, &state->key);
712 &state->ssh2key.comment;
713 state->ssh2key = *newkey2; /* structure copy */
716 savecomment = state->ssh2key.comment;
717 state->ssh2key.comment = NULL;
718 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
719 state->ssh2key.comment = savecomment;
721 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
724 setupbigedit2(hwnd, IDC_KEYDISPLAY,
725 IDC_PKSTATIC, &state->ssh2key);
727 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
731 * Finally, hide the progress bar and show
734 ui_set_state(hwnd, state, 2);
735 state->key_exists = TRUE;
738 * If the user has imported a foreign key
739 * using the Load command, let them know.
740 * If they've used the Import command, be
743 if (realtype != type && !was_import_cmd) {
745 sprintf(msg, "Successfully imported foreign key\n"
747 "To use this key with PuTTY, you need to\n"
748 "use the \"Save private key\" command to\n"
749 "save it in PuTTY's own format.",
750 key_type_to_str(realtype));
751 MessageBox(NULL, msg, "PuTTYgen Notice",
752 MB_OK | MB_ICONINFORMATION);
759 * Dialog-box function for the main PuTTYgen dialog box.
761 static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
762 WPARAM wParam, LPARAM lParam)
764 static const char generating_msg[] =
765 "Please wait while a key is generated...";
766 static const char entropy_msg[] =
767 "Please generate some randomness by moving the mouse over the blank area.";
768 struct MainDlgState *state;
773 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
774 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
778 * If we add a Help button, this is where we destroy it
779 * if the help file isn't present.
782 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
783 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
785 state = snew(struct MainDlgState);
786 state->generation_thread_exists = FALSE;
787 state->collecting_entropy = FALSE;
788 state->entropy = NULL;
789 state->key_exists = FALSE;
790 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
796 menu1 = CreateMenu();
797 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
798 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
799 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
800 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
801 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
802 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
803 state->filemenu = menu1;
805 menu1 = CreateMenu();
806 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
807 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
808 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
809 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
810 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
811 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
812 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
813 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
814 state->keymenu = menu1;
816 menu1 = CreateMenu();
817 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
818 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
819 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
820 "Export &OpenSSH key");
821 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
822 "Export &OpenSSH key (force new file format)");
823 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
824 "Export &ssh.com key");
825 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
827 state->cvtmenu = menu1;
829 menu1 = CreateMenu();
830 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
832 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
833 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
841 { /* centre the window */
845 hw = GetDesktopWindow();
846 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
848 (rs.right + rs.left + rd.left - rd.right) / 2,
849 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
850 rd.right - rd.left, rd.bottom - rd.top, TRUE);
854 struct ctlpos cp, cp2;
856 /* Accelerators used: acglops1rbde */
858 ctlposinit(&cp, hwnd, 4, 4, 4);
859 beginbox(&cp, "Key", IDC_BOX_KEY);
861 statictext(&cp2, "No key.", 1, IDC_NOKEY);
863 statictext(&cp2, "", 1, IDC_GENERATING);
864 progressbar(&cp2, IDC_PROGRESS);
866 "&Public key for pasting into authorized_keys file:",
867 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
868 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
869 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
870 IDC_FINGERPRINT, 75);
871 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
873 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
874 IDC_COMMENTEDIT, 75);
875 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
876 IDC_PASSPHRASE1EDIT, 75);
877 staticpassedit(&cp, "C&onfirm passphrase:",
878 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
880 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
881 staticbtn(&cp, "Generate a public/private key pair",
882 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
883 staticbtn(&cp, "Load an existing private key file",
884 IDC_LOADSTATIC, "&Load", IDC_LOAD);
885 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
886 "Save p&ublic key", IDC_SAVEPUB,
887 "&Save private key", IDC_SAVE);
889 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
890 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
891 "&RSA", IDC_KEYSSH2RSA,
892 "&DSA", IDC_KEYSSH2DSA,
893 "&ECDSA", IDC_KEYSSH2ECDSA,
894 "ED&25519", IDC_KEYSSH2ED25519,
895 "SSH-&1 (RSA)", IDC_KEYSSH1,
897 staticedit(&cp, "Number of &bits in a generated key:",
898 IDC_BITSSTATIC, IDC_BITS, 20);
901 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
902 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
903 IDC_KEYSSH2RSA, MF_BYCOMMAND);
904 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
907 * Initially, hide the progress bar and the key display,
908 * and show the no-key display. Also disable the Save
909 * buttons, because with no key we obviously can't save
912 ui_set_state(hwnd, state, 0);
915 * Load a key file if one was provided on the command line.
917 if (cmdline_keyfile) {
918 Filename *fn = filename_from_str(cmdline_keyfile);
919 load_key_file(hwnd, state, fn, 0);
925 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
926 if (state->collecting_entropy &&
927 state->entropy && state->entropy_got < state->entropy_required) {
928 state->entropy[state->entropy_got++] = lParam;
929 state->entropy[state->entropy_got++] = GetMessageTime();
930 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
931 state->entropy_got, 0);
932 if (state->entropy_got >= state->entropy_required) {
933 struct rsa_key_thread_params *params;
937 * Seed the entropy pool
939 random_add_heavynoise(state->entropy, state->entropy_size);
940 smemclr(state->entropy, state->entropy_size);
941 sfree(state->entropy);
942 state->collecting_entropy = FALSE;
944 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
945 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
946 MAKELPARAM(0, PROGRESSRANGE));
947 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
949 params = snew(struct rsa_key_thread_params);
950 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
951 params->dialog = hwnd;
952 params->keysize = state->keysize;
953 params->keytype = state->keytype;
954 params->key = &state->key;
955 params->dsskey = &state->dsskey;
957 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
958 params, 0, &threadid)) {
959 MessageBox(hwnd, "Out of thread resources",
960 "Key generation error",
961 MB_OK | MB_ICONERROR);
964 state->generation_thread_exists = TRUE;
970 switch (LOWORD(wParam)) {
974 case IDC_KEYSSH2ECDSA:
975 case IDC_KEYSSH2ED25519:
977 state = (struct MainDlgState *)
978 GetWindowLongPtr(hwnd, GWLP_USERDATA);
979 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
980 CheckRadioButton(hwnd,
981 IDC_KEYSSH1, IDC_KEYSSH2ED25519,
983 CheckMenuRadioItem(state->keymenu,
984 IDC_KEYSSH1, IDC_KEYSSH2ED25519,
985 LOWORD(wParam), MF_BYCOMMAND);
989 PostMessage(hwnd, WM_CLOSE, 0, 0);
991 case IDC_COMMENTEDIT:
992 if (HIWORD(wParam) == EN_CHANGE) {
993 state = (struct MainDlgState *)
994 GetWindowLongPtr(hwnd, GWLP_USERDATA);
995 if (state->key_exists) {
996 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
997 int len = GetWindowTextLength(editctl);
998 if (*state->commentptr)
999 sfree(*state->commentptr);
1000 *state->commentptr = snewn(len + 1, char);
1001 GetWindowText(editctl, *state->commentptr, len + 1);
1003 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1006 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1013 EnableWindow(hwnd, 0);
1014 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1015 EnableWindow(hwnd, 1);
1016 SetActiveWindow(hwnd);
1019 if (HIWORD(wParam) == BN_CLICKED ||
1020 HIWORD(wParam) == BN_DOUBLECLICKED) {
1021 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1025 if (HIWORD(wParam) != BN_CLICKED &&
1026 HIWORD(wParam) != BN_DOUBLECLICKED)
1029 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1030 if (!state->generation_thread_exists) {
1032 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1034 state->keysize = DEFAULT_KEYSIZE;
1035 /* If we ever introduce a new key type, check it here! */
1036 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1037 state->keytype = RSA;
1038 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1039 state->keytype = DSA;
1040 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1041 state->keytype = ECDSA;
1042 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1043 state->keytype = ED25519;
1045 if (state->keysize < 256) {
1046 int ret = MessageBox(hwnd,
1047 "PuTTYgen will not generate a key"
1048 " smaller than 256 bits.\n"
1049 "Key length reset to 256. Continue?",
1051 MB_ICONWARNING | MB_OKCANCEL);
1054 state->keysize = 256;
1055 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1057 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1058 state->keysize == 384 ||
1059 state->keysize == 521)) {
1060 int ret = MessageBox(hwnd,
1061 "Only 256, 384 and 521 bit elliptic"
1062 " curves are supported.\n"
1063 "Key length reset to 256. Continue?",
1065 MB_ICONWARNING | MB_OKCANCEL);
1068 state->keysize = 256;
1069 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1071 if (state->keytype == ED25519 && state->keysize != 256) {
1072 int ret = MessageBox(hwnd,
1073 "Only 256 bit Edwards elliptic"
1074 " curves are supported.\n"
1075 "Key length reset to 256. Continue?",
1077 MB_ICONWARNING | MB_OKCANCEL);
1080 state->keysize = 256;
1081 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1083 ui_set_state(hwnd, state, 1);
1084 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1085 state->key_exists = FALSE;
1086 state->collecting_entropy = TRUE;
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.
1101 state->entropy_required = (state->keysize / 2) * 2;
1102 state->entropy_got = 0;
1103 state->entropy_size = (state->entropy_required *
1105 state->entropy = snewn(state->entropy_required, unsigned);
1107 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1108 MAKELPARAM(0, state->entropy_required));
1109 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1113 case IDC_EXPORT_OPENSSH_AUTO:
1114 case IDC_EXPORT_OPENSSH_NEW:
1115 case IDC_EXPORT_SSHCOM:
1116 if (HIWORD(wParam) != BN_CLICKED)
1119 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1120 if (state->key_exists) {
1121 char filename[FILENAME_MAX];
1122 char *passphrase, *passphrase2;
1126 realtype = SSH_KEYTYPE_SSH2;
1128 realtype = SSH_KEYTYPE_SSH1;
1130 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1131 type = SSH_KEYTYPE_OPENSSH_AUTO;
1132 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1133 type = SSH_KEYTYPE_OPENSSH_NEW;
1134 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1135 type = SSH_KEYTYPE_SSHCOM;
1139 if (type != realtype &&
1140 import_target_type(type) != realtype) {
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);
1150 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1151 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1152 if (strcmp(passphrase, passphrase2)) {
1154 "The two passphrases given do not match.",
1155 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1156 burnstr(passphrase);
1157 burnstr(passphrase2);
1160 burnstr(passphrase2);
1163 ret = MessageBox(hwnd,
1164 "Are you sure you want to save this key\n"
1165 "without a passphrase to protect it?",
1167 MB_YESNO | MB_ICONWARNING);
1169 burnstr(passphrase);
1173 if (prompt_keyfile(hwnd, "Save private key as:",
1174 filename, 1, (type == realtype))) {
1176 FILE *fp = fopen(filename, "r");
1180 buffer = dupprintf("Overwrite existing file\n%s?",
1182 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1183 MB_YESNO | MB_ICONWARNING);
1186 burnstr(passphrase);
1192 Filename *fn = filename_from_str(filename);
1193 if (type != realtype)
1194 ret = export_ssh2(fn, type, &state->ssh2key,
1195 *passphrase ? passphrase : NULL);
1197 ret = ssh2_save_userkey(fn, &state->ssh2key,
1198 *passphrase ? passphrase :
1202 Filename *fn = filename_from_str(filename);
1203 if (type != realtype)
1204 ret = export_ssh1(fn, type, &state->key,
1205 *passphrase ? passphrase : NULL);
1207 ret = saversakey(fn, &state->key,
1208 *passphrase ? passphrase : NULL);
1212 MessageBox(hwnd, "Unable to save key file",
1213 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1216 burnstr(passphrase);
1220 if (HIWORD(wParam) != BN_CLICKED)
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:",
1229 FILE *fp = fopen(filename, "r");
1233 buffer = dupprintf("Overwrite existing file\n%s?",
1235 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1236 MB_YESNO | MB_ICONWARNING);
1241 fp = fopen(filename, "w");
1243 MessageBox(hwnd, "Unable to open key file",
1244 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1248 unsigned char *blob;
1249 blob = state->ssh2key.alg->public_blob
1250 (state->ssh2key.data, &bloblen);
1251 ssh2_write_pubkey(fp, state->ssh2key.comment,
1253 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1255 ssh1_write_pubkey(fp, &state->key);
1257 if (fclose(fp) < 0) {
1258 MessageBox(hwnd, "Unable to save key file",
1259 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1267 if (HIWORD(wParam) != BN_CLICKED)
1270 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1271 if (!state->generation_thread_exists) {
1272 char filename[FILENAME_MAX];
1273 if (prompt_keyfile(hwnd, "Load private key:",
1274 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1275 Filename *fn = filename_from_str(filename);
1276 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1284 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1285 state->generation_thread_exists = FALSE;
1286 state->key_exists = TRUE;
1287 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1288 MAKELPARAM(0, PROGRESSRANGE));
1289 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1291 if (state->keytype == DSA) {
1292 state->ssh2key.data = &state->dsskey;
1293 state->ssh2key.alg = &ssh_dss;
1294 } else if (state->keytype == ECDSA) {
1295 state->ssh2key.data = &state->eckey;
1296 state->ssh2key.alg = state->eckey.signalg;
1297 } else if (state->keytype == ED25519) {
1298 state->ssh2key.data = &state->eckey;
1299 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1301 state->ssh2key.data = &state->key;
1302 state->ssh2key.alg = &ssh_rsa;
1304 state->commentptr = &state->ssh2key.comment;
1306 state->commentptr = &state->key.comment;
1309 * Invent a comment for the key. We'll do this by including
1310 * the date in it. This will be so horrifyingly ugly that
1311 * the user will immediately want to change it, which is
1314 *state->commentptr = snewn(30, char);
1318 if (state->keytype == DSA)
1319 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1320 else if (state->keytype == ECDSA)
1321 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1322 else if (state->keytype == ED25519)
1323 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1325 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1329 * Now update the key controls with all the key data.
1334 * Blank passphrase, initially. This isn't dangerous,
1335 * because we will warn (Are You Sure?) before allowing
1336 * the user to save an unprotected private key.
1338 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1339 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1343 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1345 * Set the key fingerprint.
1347 savecomment = *state->commentptr;
1348 *state->commentptr = NULL;
1351 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1352 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1356 rsa_fingerprint(buf, sizeof(buf), &state->key);
1357 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1359 *state->commentptr = savecomment;
1361 * Construct a decimal representation of the key, for
1362 * pasting into .ssh/authorized_keys or
1363 * .ssh/authorized_keys2 on a Unix box.
1366 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1367 IDC_PKSTATIC, &state->ssh2key);
1369 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1370 IDC_PKSTATIC, &state->key);
1374 * Finally, hide the progress bar and show the key data.
1376 ui_set_state(hwnd, state, 2);
1380 int id = ((LPHELPINFO)lParam)->iCtrlId;
1381 const char *topic = NULL;
1383 case IDC_GENERATING:
1387 topic = WINHELP_CTX_puttygen_generate; break;
1389 case IDC_KEYDISPLAY:
1390 topic = WINHELP_CTX_puttygen_pastekey; break;
1392 case IDC_FINGERPRINT:
1393 topic = WINHELP_CTX_puttygen_fingerprint; break;
1394 case IDC_COMMENTSTATIC:
1395 case IDC_COMMENTEDIT:
1396 topic = WINHELP_CTX_puttygen_comment; break;
1397 case IDC_PASSPHRASE1STATIC:
1398 case IDC_PASSPHRASE1EDIT:
1399 case IDC_PASSPHRASE2STATIC:
1400 case IDC_PASSPHRASE2EDIT:
1401 topic = WINHELP_CTX_puttygen_passphrase; break;
1402 case IDC_LOADSTATIC:
1404 topic = WINHELP_CTX_puttygen_load; break;
1405 case IDC_SAVESTATIC:
1407 topic = WINHELP_CTX_puttygen_savepriv; break;
1409 topic = WINHELP_CTX_puttygen_savepub; break;
1410 case IDC_TYPESTATIC:
1412 case IDC_KEYSSH2RSA:
1413 case IDC_KEYSSH2DSA:
1414 case IDC_KEYSSH2ECDSA:
1415 case IDC_KEYSSH2ED25519:
1416 topic = WINHELP_CTX_puttygen_keytype; break;
1417 case IDC_BITSSTATIC:
1419 topic = WINHELP_CTX_puttygen_bits; break;
1421 case IDC_EXPORT_OPENSSH_AUTO:
1422 case IDC_EXPORT_OPENSSH_NEW:
1423 case IDC_EXPORT_SSHCOM:
1424 topic = WINHELP_CTX_puttygen_conversions; break;
1427 launch_help(hwnd, topic);
1434 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1443 void cleanup_exit(int code)
1449 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1455 InitCommonControls();
1460 * See if we can find our Help file.
1464 split_into_argv(cmdline, &argc, &argv, NULL);
1467 if (!strcmp(argv[0], "-pgpfp")) {
1472 * Assume the first argument to be a private key file, and
1473 * attempt to load it.
1475 cmdline_keyfile = argv[0];
1480 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1483 return ret; /* just in case optimiser complains */