2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
21 #define WM_DONEKEY (WM_APP + 1)
23 #define DEFAULT_KEYSIZE 2048
25 static char *cmdline_keyfile = NULL;
28 * Print a modal (Really Bad) message box and perform a fatal exit.
30 void modalfatalbox(const char *fmt, ...)
36 stuff = dupvprintf(fmt, ap);
38 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
39 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
45 * Print a non-fatal message box and do not exit.
47 void nonfatal(const char *fmt, ...)
53 stuff = dupvprintf(fmt, ap);
55 MessageBox(NULL, stuff, "PuTTYgen Error",
56 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
60 /* ----------------------------------------------------------------------
61 * Progress report code. This is really horrible :-)
63 #define PROGRESSRANGE 65535
69 unsigned startpoint, total;
70 unsigned param, current, n; /* if exponential */
71 unsigned mult; /* if linear */
73 unsigned total, divisor, range;
77 static void progress_update(void *param, int action, int phase, int iprogress)
79 struct progress *p = (struct progress *) param;
80 unsigned progress = iprogress;
83 if (action < PROGFN_READY && p->nphases < phase)
86 case PROGFN_INITIALISE:
89 case PROGFN_LIN_PHASE:
90 p->phases[phase-1].exponential = 0;
91 p->phases[phase-1].mult = p->phases[phase].total / progress;
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;
99 case PROGFN_PHASE_EXTENT:
100 p->phases[phase-1].total = progress;
106 for (i = 0; i < p->nphases; i++) {
107 p->phases[i].startpoint = total;
108 total += p->phases[i].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));
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;
123 position = (p->phases[phase-1].startpoint +
124 p->phases[phase-1].total - p->phases[phase-1].current);
126 position = (p->phases[phase-1].startpoint +
127 progress * p->phases[phase-1].mult);
129 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
136 struct PassphraseProcStruct {
142 * Dialog-box function for the passphrase box.
144 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
145 WPARAM wParam, LPARAM lParam)
147 static char **passphrase = NULL;
148 struct PassphraseProcStruct *p;
152 SetForegroundWindow(hwnd);
153 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
154 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
159 { /* centre the window */
163 hw = GetDesktopWindow();
164 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
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);
171 p = (struct PassphraseProcStruct *) lParam;
172 passphrase = p->passphrase;
174 SetDlgItemText(hwnd, 101, p->comment);
175 burnstr(*passphrase);
176 *passphrase = dupstr("");
177 SetDlgItemText(hwnd, 102, *passphrase);
180 switch (LOWORD(wParam)) {
190 case 102: /* edit box */
191 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
192 burnstr(*passphrase);
193 *passphrase = GetDlgItemText_alloc(hwnd, 102);
206 * Prompt for a key file. Assumes the filename buffer is of size
209 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
210 char *filename, int save, int ppk)
213 memset(&of, 0, sizeof(of));
216 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
217 "All Files (*.*)\0*\0\0\0";
218 of.lpstrDefExt = ".ppk";
220 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
222 of.lpstrCustomFilter = NULL;
224 of.lpstrFile = filename;
226 of.nMaxFile = FILENAME_MAX;
227 of.lpstrFileTitle = NULL;
228 of.lpstrTitle = dlgtitle;
230 return request_file(NULL, &of, FALSE, save);
234 * Dialog-box function for the Licence box.
236 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
237 WPARAM wParam, LPARAM lParam)
244 { /* centre the window */
248 hw = GetDesktopWindow();
249 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
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);
258 switch (LOWORD(wParam)) {
273 * Dialog-box function for the About box.
275 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
276 WPARAM wParam, LPARAM lParam)
283 { /* centre the window */
287 hw = GetDesktopWindow();
288 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
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);
295 SetDlgItemText(hwnd, 100, ver);
298 switch (LOWORD(wParam)) {
304 EnableWindow(hwnd, 0);
305 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
306 EnableWindow(hwnd, 1);
307 SetActiveWindow(hwnd);
318 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
321 * Thread to generate a key.
323 struct rsa_key_thread_params {
324 HWND progressbar; /* notify this with progress */
325 HWND dialog; /* notify this on completion */
326 int keysize; /* bits in key */
330 struct dss_key *dsskey;
331 struct ec_key *eckey;
334 static DWORD WINAPI generate_rsa_key_thread(void *param)
336 struct rsa_key_thread_params *params =
337 (struct rsa_key_thread_params *) param;
338 struct progress prog;
339 prog.progbar = params->progressbar;
341 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
343 if (params->keytype == DSA)
344 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
345 else if (params->keytype == ECDSA)
346 ec_generate(params->eckey, params->keysize, progress_update, &prog);
347 else if (params->keytype == ED25519)
348 ec_edgenerate(params->eckey, params->keysize, progress_update, &prog);
350 rsa_generate(params->key, params->keysize, progress_update, &prog);
352 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
358 struct MainDlgState {
359 int collecting_entropy;
360 int generation_thread_exists;
362 int entropy_got, entropy_required, entropy_size;
366 char **commentptr; /* points to key.comment or ssh2key.comment */
367 struct ssh2_userkey ssh2key;
371 struct dss_key dsskey;
374 HMENU filemenu, keymenu, cvtmenu;
377 static void hidemany(HWND hwnd, const int *ids, int hideit)
380 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
384 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
386 char *buffer = ssh1_pubkey_str(key);
387 SetDlgItemText(hwnd, id, buffer);
388 SetDlgItemText(hwnd, idstatic,
389 "&Public key for pasting into authorized_keys file:");
393 static void setupbigedit2(HWND hwnd, int id, int idstatic,
394 struct ssh2_userkey *key)
396 char *buffer = ssh2_pubkey_openssh_str(key);
397 SetDlgItemText(hwnd, id, buffer);
398 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
399 "OpenSSH authorized_keys file:");
404 * Warn about the obsolescent key file format.
406 void old_keyfile_warning(void)
408 static const char mbtitle[] = "PuTTY Key File Warning";
409 static const char message[] =
410 "You are loading an SSH-2 private key which has an\n"
411 "old version of the file format. This means your key\n"
412 "file is not fully tamperproof. Future versions of\n"
413 "PuTTY may stop supporting this private key format,\n"
414 "so we recommend you convert your key to the new\n"
417 "Once the key is loaded into PuTTYgen, you can perform\n"
418 "this conversion simply by saving it again.";
420 MessageBox(NULL, message, mbtitle, MB_OK);
424 controlidstart = 100,
431 IDC_PKSTATIC, IDC_KEYDISPLAY,
432 IDC_FPSTATIC, IDC_FINGERPRINT,
433 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
434 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
435 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
437 IDC_GENSTATIC, IDC_GENERATE,
438 IDC_LOADSTATIC, IDC_LOAD,
439 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
441 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
442 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
443 IDC_BITSSTATIC, IDC_BITS,
447 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
451 static const int nokey_ids[] = { IDC_NOKEY, 0 };
452 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
453 static const int gotkey_ids[] = {
454 IDC_PKSTATIC, IDC_KEYDISPLAY,
455 IDC_FPSTATIC, IDC_FINGERPRINT,
456 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
457 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
458 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
462 * Small UI helper function to switch the state of the main dialog
463 * by enabling and disabling controls and menu items.
465 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
471 hidemany(hwnd, nokey_ids, FALSE);
472 hidemany(hwnd, generating_ids, TRUE);
473 hidemany(hwnd, gotkey_ids, TRUE);
474 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
475 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
476 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
477 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
478 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
479 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
480 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
481 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
482 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
483 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
484 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
485 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
486 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
487 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
488 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
489 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
490 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
491 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
492 MF_ENABLED|MF_BYCOMMAND);
493 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
494 MF_ENABLED|MF_BYCOMMAND);
495 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
496 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
497 MF_GRAYED|MF_BYCOMMAND);
498 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
499 MF_GRAYED|MF_BYCOMMAND);
500 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
501 MF_GRAYED|MF_BYCOMMAND);
503 case 1: /* generating key */
504 hidemany(hwnd, nokey_ids, TRUE);
505 hidemany(hwnd, generating_ids, FALSE);
506 hidemany(hwnd, gotkey_ids, TRUE);
507 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
508 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
509 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
510 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
511 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
512 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
513 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
514 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
515 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
516 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
517 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
518 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
519 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
520 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
521 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
522 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
523 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
524 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
525 MF_GRAYED|MF_BYCOMMAND);
526 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
527 MF_GRAYED|MF_BYCOMMAND);
528 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
529 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
530 MF_GRAYED|MF_BYCOMMAND);
531 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
532 MF_GRAYED|MF_BYCOMMAND);
533 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
534 MF_GRAYED|MF_BYCOMMAND);
537 hidemany(hwnd, nokey_ids, TRUE);
538 hidemany(hwnd, generating_ids, TRUE);
539 hidemany(hwnd, gotkey_ids, FALSE);
540 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
541 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
542 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
543 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
544 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
545 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
546 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
547 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
548 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
549 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
550 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
551 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
552 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
553 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
554 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
555 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
556 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
557 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
558 MF_ENABLED|MF_BYCOMMAND);
559 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
560 MF_ENABLED|MF_BYCOMMAND);
561 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
563 * Enable export menu items if and only if the key type
564 * supports this kind of export.
566 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
567 #define do_export_menuitem(x,y) \
568 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
569 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
570 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
571 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
572 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
573 #undef do_export_menuitem
578 void load_key_file(HWND hwnd, struct MainDlgState *state,
579 Filename *filename, int was_import_cmd)
585 const char *errmsg = NULL;
587 struct RSAKey newkey1;
588 struct ssh2_userkey *newkey2 = NULL;
590 type = realtype = key_type(filename);
591 if (type != SSH_KEYTYPE_SSH1 &&
592 type != SSH_KEYTYPE_SSH2 &&
593 !import_possible(type)) {
594 char *msg = dupprintf("Couldn't load private key (%s)",
595 key_type_to_str(type));
596 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
597 HELPCTXID(errors_cantloadkey));
602 if (type != SSH_KEYTYPE_SSH1 &&
603 type != SSH_KEYTYPE_SSH2) {
605 type = import_target_type(type);
610 if (realtype == SSH_KEYTYPE_SSH1)
611 needs_pass = rsakey_encrypted(filename, &comment);
612 else if (realtype == SSH_KEYTYPE_SSH2)
613 needs_pass = ssh2_userkey_encrypted(filename, &comment);
615 needs_pass = import_encrypted(filename, realtype, &comment);
622 struct PassphraseProcStruct pps;
623 pps.passphrase = &passphrase;
624 pps.comment = comment;
625 dlgret = DialogBoxParam(hinst,
626 MAKEINTRESOURCE(210),
627 NULL, PassphraseProc,
633 assert(passphrase != NULL);
635 passphrase = dupstr("");
636 if (type == SSH_KEYTYPE_SSH1) {
637 if (realtype == type)
638 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
640 ret = import_ssh1(filename, realtype, &newkey1,
641 passphrase, &errmsg);
643 if (realtype == type)
644 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
646 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
647 if (newkey2 == SSH2_WRONG_PASSPHRASE)
658 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
659 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
660 HELPCTXID(errors_cantloadkey));
662 } else if (ret == 1) {
664 * Now update the key controls with all the
668 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
670 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
672 if (type == SSH_KEYTYPE_SSH1) {
677 state->commentptr = &state->key.comment;
678 state->key = newkey1;
681 * Set the key fingerprint.
683 savecomment = state->key.comment;
684 state->key.comment = NULL;
685 rsa_fingerprint(buf, sizeof(buf),
687 state->key.comment = savecomment;
689 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
691 * Construct a decimal representation
692 * of the key, for pasting into
693 * .ssh/authorized_keys on a Unix box.
695 setupbigedit1(hwnd, IDC_KEYDISPLAY,
696 IDC_PKSTATIC, &state->key);
703 &state->ssh2key.comment;
704 state->ssh2key = *newkey2; /* structure copy */
707 savecomment = state->ssh2key.comment;
708 state->ssh2key.comment = NULL;
709 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
710 state->ssh2key.comment = savecomment;
712 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
715 setupbigedit2(hwnd, IDC_KEYDISPLAY,
716 IDC_PKSTATIC, &state->ssh2key);
718 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
722 * Finally, hide the progress bar and show
725 ui_set_state(hwnd, state, 2);
726 state->key_exists = TRUE;
729 * If the user has imported a foreign key
730 * using the Load command, let them know.
731 * If they've used the Import command, be
734 if (realtype != type && !was_import_cmd) {
736 sprintf(msg, "Successfully imported foreign key\n"
738 "To use this key with PuTTY, you need to\n"
739 "use the \"Save private key\" command to\n"
740 "save it in PuTTY's own format.",
741 key_type_to_str(realtype));
742 MessageBox(NULL, msg, "PuTTYgen Notice",
743 MB_OK | MB_ICONINFORMATION);
750 * Dialog-box function for the main PuTTYgen dialog box.
752 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
753 WPARAM wParam, LPARAM lParam)
755 static const char generating_msg[] =
756 "Please wait while a key is generated...";
757 static const char entropy_msg[] =
758 "Please generate some randomness by moving the mouse over the blank area.";
759 struct MainDlgState *state;
764 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
765 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
769 * If we add a Help button, this is where we destroy it
770 * if the help file isn't present.
773 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
774 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
776 state = snew(struct MainDlgState);
777 state->generation_thread_exists = FALSE;
778 state->collecting_entropy = FALSE;
779 state->entropy = NULL;
780 state->key_exists = FALSE;
781 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
787 menu1 = CreateMenu();
788 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
789 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
790 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
791 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
792 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
793 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
794 state->filemenu = menu1;
796 menu1 = CreateMenu();
797 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
798 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
799 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
800 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
801 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
802 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
803 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
804 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
805 state->keymenu = menu1;
807 menu1 = CreateMenu();
808 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
809 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
810 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
811 "Export &OpenSSH key");
812 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
813 "Export &OpenSSH key (force new file format)");
814 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
815 "Export &ssh.com key");
816 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
818 state->cvtmenu = menu1;
820 menu1 = CreateMenu();
821 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
823 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
824 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
832 { /* centre the window */
836 hw = GetDesktopWindow();
837 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
839 (rs.right + rs.left + rd.left - rd.right) / 2,
840 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
841 rd.right - rd.left, rd.bottom - rd.top, TRUE);
845 struct ctlpos cp, cp2;
847 /* Accelerators used: acglops1rbde */
849 ctlposinit(&cp, hwnd, 4, 4, 4);
850 beginbox(&cp, "Key", IDC_BOX_KEY);
852 statictext(&cp2, "No key.", 1, IDC_NOKEY);
854 statictext(&cp2, "", 1, IDC_GENERATING);
855 progressbar(&cp2, IDC_PROGRESS);
857 "&Public key for pasting into authorized_keys file:",
858 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
859 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
860 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
861 IDC_FINGERPRINT, 75);
862 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
864 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
865 IDC_COMMENTEDIT, 75);
866 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
867 IDC_PASSPHRASE1EDIT, 75);
868 staticpassedit(&cp, "C&onfirm passphrase:",
869 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
871 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
872 staticbtn(&cp, "Generate a public/private key pair",
873 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
874 staticbtn(&cp, "Load an existing private key file",
875 IDC_LOADSTATIC, "&Load", IDC_LOAD);
876 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
877 "Save p&ublic key", IDC_SAVEPUB,
878 "&Save private key", IDC_SAVE);
880 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
881 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
882 "&RSA", IDC_KEYSSH2RSA,
883 "&DSA", IDC_KEYSSH2DSA,
884 "&ECDSA", IDC_KEYSSH2ECDSA,
885 "ED&25519", IDC_KEYSSH2ED25519,
886 "SSH-&1 (RSA)", IDC_KEYSSH1,
888 staticedit(&cp, "Number of &bits in a generated key:",
889 IDC_BITSSTATIC, IDC_BITS, 20);
892 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
893 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
894 IDC_KEYSSH2RSA, MF_BYCOMMAND);
895 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
898 * Initially, hide the progress bar and the key display,
899 * and show the no-key display. Also disable the Save
900 * buttons, because with no key we obviously can't save
903 ui_set_state(hwnd, state, 0);
906 * Load a key file if one was provided on the command line.
908 if (cmdline_keyfile) {
909 Filename *fn = filename_from_str(cmdline_keyfile);
910 load_key_file(hwnd, state, fn, 0);
916 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
917 if (state->collecting_entropy &&
918 state->entropy && state->entropy_got < state->entropy_required) {
919 state->entropy[state->entropy_got++] = lParam;
920 state->entropy[state->entropy_got++] = GetMessageTime();
921 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
922 state->entropy_got, 0);
923 if (state->entropy_got >= state->entropy_required) {
924 struct rsa_key_thread_params *params;
928 * Seed the entropy pool
930 random_add_heavynoise(state->entropy, state->entropy_size);
931 smemclr(state->entropy, state->entropy_size);
932 sfree(state->entropy);
933 state->collecting_entropy = FALSE;
935 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
936 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
937 MAKELPARAM(0, PROGRESSRANGE));
938 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
940 params = snew(struct rsa_key_thread_params);
941 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
942 params->dialog = hwnd;
943 params->keysize = state->keysize;
944 params->keytype = state->keytype;
945 params->key = &state->key;
946 params->dsskey = &state->dsskey;
948 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
949 params, 0, &threadid)) {
950 MessageBox(hwnd, "Out of thread resources",
951 "Key generation error",
952 MB_OK | MB_ICONERROR);
955 state->generation_thread_exists = TRUE;
961 switch (LOWORD(wParam)) {
965 case IDC_KEYSSH2ECDSA:
966 case IDC_KEYSSH2ED25519:
968 state = (struct MainDlgState *)
969 GetWindowLongPtr(hwnd, GWLP_USERDATA);
970 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
971 CheckRadioButton(hwnd,
972 IDC_KEYSSH1, IDC_KEYSSH2ED25519,
974 CheckMenuRadioItem(state->keymenu,
975 IDC_KEYSSH1, IDC_KEYSSH2ED25519,
976 LOWORD(wParam), MF_BYCOMMAND);
980 PostMessage(hwnd, WM_CLOSE, 0, 0);
982 case IDC_COMMENTEDIT:
983 if (HIWORD(wParam) == EN_CHANGE) {
984 state = (struct MainDlgState *)
985 GetWindowLongPtr(hwnd, GWLP_USERDATA);
986 if (state->key_exists) {
987 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
988 int len = GetWindowTextLength(editctl);
989 if (*state->commentptr)
990 sfree(*state->commentptr);
991 *state->commentptr = snewn(len + 1, char);
992 GetWindowText(editctl, *state->commentptr, len + 1);
994 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
997 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1004 EnableWindow(hwnd, 0);
1005 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1006 EnableWindow(hwnd, 1);
1007 SetActiveWindow(hwnd);
1010 if (HIWORD(wParam) == BN_CLICKED ||
1011 HIWORD(wParam) == BN_DOUBLECLICKED) {
1012 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1016 if (HIWORD(wParam) != BN_CLICKED &&
1017 HIWORD(wParam) != BN_DOUBLECLICKED)
1020 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1021 if (!state->generation_thread_exists) {
1023 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1025 state->keysize = DEFAULT_KEYSIZE;
1026 /* If we ever introduce a new key type, check it here! */
1027 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1028 state->keytype = RSA;
1029 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1030 state->keytype = DSA;
1031 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1032 state->keytype = ECDSA;
1033 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1034 state->keytype = ED25519;
1036 if (state->keysize < 256) {
1037 int ret = MessageBox(hwnd,
1038 "PuTTYgen will not generate a key"
1039 " smaller than 256 bits.\n"
1040 "Key length reset to 256. Continue?",
1042 MB_ICONWARNING | MB_OKCANCEL);
1045 state->keysize = 256;
1046 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1048 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1049 state->keysize == 384 ||
1050 state->keysize == 521)) {
1051 int ret = MessageBox(hwnd,
1052 "Only 256, 384 and 521 bit elliptic"
1053 " curves are supported.\n"
1054 "Key length reset to 256. Continue?",
1056 MB_ICONWARNING | MB_OKCANCEL);
1059 state->keysize = 256;
1060 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1062 if (state->keytype == ED25519 && state->keysize != 256) {
1063 int ret = MessageBox(hwnd,
1064 "Only 256 bit Edwards elliptic"
1065 " curves are supported.\n"
1066 "Key length reset to 256. Continue?",
1068 MB_ICONWARNING | MB_OKCANCEL);
1071 state->keysize = 256;
1072 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1074 ui_set_state(hwnd, state, 1);
1075 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1076 state->key_exists = FALSE;
1077 state->collecting_entropy = TRUE;
1080 * My brief statistical tests on mouse movements
1081 * suggest that there are about 2.5 bits of
1082 * randomness in the x position, 2.5 in the y
1083 * position, and 1.7 in the message time, making
1084 * 5.7 bits of unpredictability per mouse movement.
1085 * However, other people have told me it's far less
1086 * than that, so I'm going to be stupidly cautious
1087 * and knock that down to a nice round 2. With this
1088 * method, we require two words per mouse movement,
1089 * so with 2 bits per mouse movement we expect 2
1090 * bits every 2 words.
1092 state->entropy_required = (state->keysize / 2) * 2;
1093 state->entropy_got = 0;
1094 state->entropy_size = (state->entropy_required *
1096 state->entropy = snewn(state->entropy_required, unsigned);
1098 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1099 MAKELPARAM(0, state->entropy_required));
1100 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1104 case IDC_EXPORT_OPENSSH_AUTO:
1105 case IDC_EXPORT_OPENSSH_NEW:
1106 case IDC_EXPORT_SSHCOM:
1107 if (HIWORD(wParam) != BN_CLICKED)
1110 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1111 if (state->key_exists) {
1112 char filename[FILENAME_MAX];
1113 char *passphrase, *passphrase2;
1117 realtype = SSH_KEYTYPE_SSH2;
1119 realtype = SSH_KEYTYPE_SSH1;
1121 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1122 type = SSH_KEYTYPE_OPENSSH_AUTO;
1123 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1124 type = SSH_KEYTYPE_OPENSSH_NEW;
1125 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1126 type = SSH_KEYTYPE_SSHCOM;
1130 if (type != realtype &&
1131 import_target_type(type) != realtype) {
1133 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1134 " format", (state->ssh2 ? 2 : 1),
1135 (state->ssh2 ? 1 : 2));
1136 MessageBox(hwnd, msg,
1137 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1141 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1142 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1143 if (strcmp(passphrase, passphrase2)) {
1145 "The two passphrases given do not match.",
1146 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1147 burnstr(passphrase);
1148 burnstr(passphrase2);
1151 burnstr(passphrase2);
1154 ret = MessageBox(hwnd,
1155 "Are you sure you want to save this key\n"
1156 "without a passphrase to protect it?",
1158 MB_YESNO | MB_ICONWARNING);
1160 burnstr(passphrase);
1164 if (prompt_keyfile(hwnd, "Save private key as:",
1165 filename, 1, (type == realtype))) {
1167 FILE *fp = fopen(filename, "r");
1171 buffer = dupprintf("Overwrite existing file\n%s?",
1173 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1174 MB_YESNO | MB_ICONWARNING);
1177 burnstr(passphrase);
1183 Filename *fn = filename_from_str(filename);
1184 if (type != realtype)
1185 ret = export_ssh2(fn, type, &state->ssh2key,
1186 *passphrase ? passphrase : NULL);
1188 ret = ssh2_save_userkey(fn, &state->ssh2key,
1189 *passphrase ? passphrase :
1193 Filename *fn = filename_from_str(filename);
1194 if (type != realtype)
1195 ret = export_ssh1(fn, type, &state->key,
1196 *passphrase ? passphrase : NULL);
1198 ret = saversakey(fn, &state->key,
1199 *passphrase ? passphrase : NULL);
1203 MessageBox(hwnd, "Unable to save key file",
1204 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1207 burnstr(passphrase);
1211 if (HIWORD(wParam) != BN_CLICKED)
1214 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1215 if (state->key_exists) {
1216 char filename[FILENAME_MAX];
1217 if (prompt_keyfile(hwnd, "Save public key as:",
1220 FILE *fp = fopen(filename, "r");
1224 buffer = dupprintf("Overwrite existing file\n%s?",
1226 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1227 MB_YESNO | MB_ICONWARNING);
1232 fp = fopen(filename, "w");
1234 MessageBox(hwnd, "Unable to open key file",
1235 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1239 unsigned char *blob;
1240 blob = state->ssh2key.alg->public_blob
1241 (state->ssh2key.data, &bloblen);
1242 ssh2_write_pubkey(fp, state->ssh2key.comment,
1244 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1246 ssh1_write_pubkey(fp, &state->key);
1248 if (fclose(fp) < 0) {
1249 MessageBox(hwnd, "Unable to save key file",
1250 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1258 if (HIWORD(wParam) != BN_CLICKED)
1261 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1262 if (!state->generation_thread_exists) {
1263 char filename[FILENAME_MAX];
1264 if (prompt_keyfile(hwnd, "Load private key:",
1265 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1266 Filename *fn = filename_from_str(filename);
1267 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1275 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1276 state->generation_thread_exists = FALSE;
1277 state->key_exists = TRUE;
1278 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1279 MAKELPARAM(0, PROGRESSRANGE));
1280 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1282 if (state->keytype == DSA) {
1283 state->ssh2key.data = &state->dsskey;
1284 state->ssh2key.alg = &ssh_dss;
1285 } else if (state->keytype == ECDSA) {
1286 state->ssh2key.data = &state->eckey;
1287 state->ssh2key.alg = state->eckey.signalg;
1288 } else if (state->keytype == ED25519) {
1289 state->ssh2key.data = &state->eckey;
1290 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1292 state->ssh2key.data = &state->key;
1293 state->ssh2key.alg = &ssh_rsa;
1295 state->commentptr = &state->ssh2key.comment;
1297 state->commentptr = &state->key.comment;
1300 * Invent a comment for the key. We'll do this by including
1301 * the date in it. This will be so horrifyingly ugly that
1302 * the user will immediately want to change it, which is
1305 *state->commentptr = snewn(30, char);
1309 if (state->keytype == DSA)
1310 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1311 else if (state->keytype == ECDSA)
1312 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1313 else if (state->keytype == ED25519)
1314 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1316 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1320 * Now update the key controls with all the key data.
1325 * Blank passphrase, initially. This isn't dangerous,
1326 * because we will warn (Are You Sure?) before allowing
1327 * the user to save an unprotected private key.
1329 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1330 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1334 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1336 * Set the key fingerprint.
1338 savecomment = *state->commentptr;
1339 *state->commentptr = NULL;
1342 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1343 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1347 rsa_fingerprint(buf, sizeof(buf), &state->key);
1348 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1350 *state->commentptr = savecomment;
1352 * Construct a decimal representation of the key, for
1353 * pasting into .ssh/authorized_keys or
1354 * .ssh/authorized_keys2 on a Unix box.
1357 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1358 IDC_PKSTATIC, &state->ssh2key);
1360 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1361 IDC_PKSTATIC, &state->key);
1365 * Finally, hide the progress bar and show the key data.
1367 ui_set_state(hwnd, state, 2);
1371 int id = ((LPHELPINFO)lParam)->iCtrlId;
1372 const char *topic = NULL;
1374 case IDC_GENERATING:
1378 topic = WINHELP_CTX_puttygen_generate; break;
1380 case IDC_KEYDISPLAY:
1381 topic = WINHELP_CTX_puttygen_pastekey; break;
1383 case IDC_FINGERPRINT:
1384 topic = WINHELP_CTX_puttygen_fingerprint; break;
1385 case IDC_COMMENTSTATIC:
1386 case IDC_COMMENTEDIT:
1387 topic = WINHELP_CTX_puttygen_comment; break;
1388 case IDC_PASSPHRASE1STATIC:
1389 case IDC_PASSPHRASE1EDIT:
1390 case IDC_PASSPHRASE2STATIC:
1391 case IDC_PASSPHRASE2EDIT:
1392 topic = WINHELP_CTX_puttygen_passphrase; break;
1393 case IDC_LOADSTATIC:
1395 topic = WINHELP_CTX_puttygen_load; break;
1396 case IDC_SAVESTATIC:
1398 topic = WINHELP_CTX_puttygen_savepriv; break;
1400 topic = WINHELP_CTX_puttygen_savepub; break;
1401 case IDC_TYPESTATIC:
1403 case IDC_KEYSSH2RSA:
1404 case IDC_KEYSSH2DSA:
1405 case IDC_KEYSSH2ECDSA:
1406 case IDC_KEYSSH2ED25519:
1407 topic = WINHELP_CTX_puttygen_keytype; break;
1408 case IDC_BITSSTATIC:
1410 topic = WINHELP_CTX_puttygen_bits; break;
1412 case IDC_EXPORT_OPENSSH_AUTO:
1413 case IDC_EXPORT_OPENSSH_NEW:
1414 case IDC_EXPORT_SSHCOM:
1415 topic = WINHELP_CTX_puttygen_conversions; break;
1418 launch_help(hwnd, topic);
1425 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1434 void cleanup_exit(int code)
1440 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1446 InitCommonControls();
1451 * See if we can find our Help file.
1455 split_into_argv(cmdline, &argc, &argv, NULL);
1458 if (!strcmp(argv[0], "-pgpfp")) {
1463 * Assume the first argument to be a private key file, and
1464 * attempt to load it.
1466 cmdline_keyfile = argv[0];
1471 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1474 return ret; /* just in case optimiser complains */