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(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(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) 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) 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) 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) 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, IDC_KEYSSH1, IDC_KEYSSH2DSA,
973 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
974 LOWORD(wParam), MF_BYCOMMAND);
975 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
977 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
979 LOWORD(wParam), MF_BYCOMMAND);
983 PostMessage(hwnd, WM_CLOSE, 0, 0);
985 case IDC_COMMENTEDIT:
986 if (HIWORD(wParam) == EN_CHANGE) {
987 state = (struct MainDlgState *)
988 GetWindowLongPtr(hwnd, GWLP_USERDATA);
989 if (state->key_exists) {
990 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
991 int len = GetWindowTextLength(editctl);
992 if (*state->commentptr)
993 sfree(*state->commentptr);
994 *state->commentptr = snewn(len + 1, char);
995 GetWindowText(editctl, *state->commentptr, len + 1);
997 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1000 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1007 EnableWindow(hwnd, 0);
1008 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1009 EnableWindow(hwnd, 1);
1010 SetActiveWindow(hwnd);
1013 if (HIWORD(wParam) == BN_CLICKED ||
1014 HIWORD(wParam) == BN_DOUBLECLICKED) {
1015 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1019 if (HIWORD(wParam) != BN_CLICKED &&
1020 HIWORD(wParam) != BN_DOUBLECLICKED)
1023 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1024 if (!state->generation_thread_exists) {
1026 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1028 state->keysize = DEFAULT_KEYSIZE;
1029 /* If we ever introduce a new key type, check it here! */
1030 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1031 state->keytype = RSA;
1032 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1033 state->keytype = DSA;
1034 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1035 state->keytype = ECDSA;
1036 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1037 state->keytype = ED25519;
1039 if (state->keysize < 256) {
1040 int ret = MessageBox(hwnd,
1041 "PuTTYgen will not generate a key"
1042 " smaller than 256 bits.\n"
1043 "Key length reset to 256. Continue?",
1045 MB_ICONWARNING | MB_OKCANCEL);
1048 state->keysize = 256;
1049 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1051 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1052 state->keysize == 384 ||
1053 state->keysize == 521)) {
1054 int ret = MessageBox(hwnd,
1055 "Only 256, 384 and 521 bit elliptic"
1056 " curves are supported.\n"
1057 "Key length reset to 256. Continue?",
1059 MB_ICONWARNING | MB_OKCANCEL);
1062 state->keysize = 256;
1063 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1065 if (state->keytype == ED25519 && state->keysize != 256) {
1066 int ret = MessageBox(hwnd,
1067 "Only 256 bit Edwards elliptic"
1068 " curves are supported.\n"
1069 "Key length reset to 256. Continue?",
1071 MB_ICONWARNING | MB_OKCANCEL);
1074 state->keysize = 256;
1075 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1077 ui_set_state(hwnd, state, 1);
1078 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1079 state->key_exists = FALSE;
1080 state->collecting_entropy = TRUE;
1083 * My brief statistical tests on mouse movements
1084 * suggest that there are about 2.5 bits of
1085 * randomness in the x position, 2.5 in the y
1086 * position, and 1.7 in the message time, making
1087 * 5.7 bits of unpredictability per mouse movement.
1088 * However, other people have told me it's far less
1089 * than that, so I'm going to be stupidly cautious
1090 * and knock that down to a nice round 2. With this
1091 * method, we require two words per mouse movement,
1092 * so with 2 bits per mouse movement we expect 2
1093 * bits every 2 words.
1095 state->entropy_required = (state->keysize / 2) * 2;
1096 state->entropy_got = 0;
1097 state->entropy_size = (state->entropy_required *
1099 state->entropy = snewn(state->entropy_required, unsigned);
1101 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1102 MAKELPARAM(0, state->entropy_required));
1103 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1107 case IDC_EXPORT_OPENSSH_AUTO:
1108 case IDC_EXPORT_OPENSSH_NEW:
1109 case IDC_EXPORT_SSHCOM:
1110 if (HIWORD(wParam) != BN_CLICKED)
1113 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1114 if (state->key_exists) {
1115 char filename[FILENAME_MAX];
1116 char *passphrase, *passphrase2;
1120 realtype = SSH_KEYTYPE_SSH2;
1122 realtype = SSH_KEYTYPE_SSH1;
1124 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1125 type = SSH_KEYTYPE_OPENSSH_AUTO;
1126 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1127 type = SSH_KEYTYPE_OPENSSH_NEW;
1128 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1129 type = SSH_KEYTYPE_SSHCOM;
1133 if (type != realtype &&
1134 import_target_type(type) != realtype) {
1136 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1137 " format", (state->ssh2 ? 2 : 1),
1138 (state->ssh2 ? 1 : 2));
1139 MessageBox(hwnd, msg,
1140 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1144 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1145 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1146 if (strcmp(passphrase, passphrase2)) {
1148 "The two passphrases given do not match.",
1149 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1150 burnstr(passphrase);
1151 burnstr(passphrase2);
1154 burnstr(passphrase2);
1157 ret = MessageBox(hwnd,
1158 "Are you sure you want to save this key\n"
1159 "without a passphrase to protect it?",
1161 MB_YESNO | MB_ICONWARNING);
1163 burnstr(passphrase);
1167 if (prompt_keyfile(hwnd, "Save private key as:",
1168 filename, 1, (type == realtype))) {
1170 FILE *fp = fopen(filename, "r");
1174 buffer = dupprintf("Overwrite existing file\n%s?",
1176 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1177 MB_YESNO | MB_ICONWARNING);
1180 burnstr(passphrase);
1186 Filename *fn = filename_from_str(filename);
1187 if (type != realtype)
1188 ret = export_ssh2(fn, type, &state->ssh2key,
1189 *passphrase ? passphrase : NULL);
1191 ret = ssh2_save_userkey(fn, &state->ssh2key,
1192 *passphrase ? passphrase :
1196 Filename *fn = filename_from_str(filename);
1197 if (type != realtype)
1198 ret = export_ssh1(fn, type, &state->key,
1199 *passphrase ? passphrase : NULL);
1201 ret = saversakey(fn, &state->key,
1202 *passphrase ? passphrase : NULL);
1206 MessageBox(hwnd, "Unable to save key file",
1207 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1210 burnstr(passphrase);
1214 if (HIWORD(wParam) != BN_CLICKED)
1217 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1218 if (state->key_exists) {
1219 char filename[FILENAME_MAX];
1220 if (prompt_keyfile(hwnd, "Save public key as:",
1223 FILE *fp = fopen(filename, "r");
1227 buffer = dupprintf("Overwrite existing file\n%s?",
1229 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1230 MB_YESNO | MB_ICONWARNING);
1235 fp = fopen(filename, "w");
1237 MessageBox(hwnd, "Unable to open key file",
1238 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1242 unsigned char *blob;
1243 blob = state->ssh2key.alg->public_blob
1244 (state->ssh2key.data, &bloblen);
1245 ssh2_write_pubkey(fp, state->ssh2key.comment,
1247 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1249 ssh1_write_pubkey(fp, &state->key);
1251 if (fclose(fp) < 0) {
1252 MessageBox(hwnd, "Unable to save key file",
1253 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1261 if (HIWORD(wParam) != BN_CLICKED)
1264 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1265 if (!state->generation_thread_exists) {
1266 char filename[FILENAME_MAX];
1267 if (prompt_keyfile(hwnd, "Load private key:",
1268 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1269 Filename *fn = filename_from_str(filename);
1270 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1278 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1279 state->generation_thread_exists = FALSE;
1280 state->key_exists = TRUE;
1281 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1282 MAKELPARAM(0, PROGRESSRANGE));
1283 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1285 if (state->keytype == DSA) {
1286 state->ssh2key.data = &state->dsskey;
1287 state->ssh2key.alg = &ssh_dss;
1288 } else if (state->keytype == ECDSA) {
1289 state->ssh2key.data = &state->eckey;
1290 state->ssh2key.alg = state->eckey.signalg;
1291 } else if (state->keytype == ED25519) {
1292 state->ssh2key.data = &state->eckey;
1293 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1295 state->ssh2key.data = &state->key;
1296 state->ssh2key.alg = &ssh_rsa;
1298 state->commentptr = &state->ssh2key.comment;
1300 state->commentptr = &state->key.comment;
1303 * Invent a comment for the key. We'll do this by including
1304 * the date in it. This will be so horrifyingly ugly that
1305 * the user will immediately want to change it, which is
1308 *state->commentptr = snewn(30, char);
1312 if (state->keytype == DSA)
1313 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1314 else if (state->keytype == ECDSA)
1315 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1316 else if (state->keytype == ED25519)
1317 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1319 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1323 * Now update the key controls with all the key data.
1328 * Blank passphrase, initially. This isn't dangerous,
1329 * because we will warn (Are You Sure?) before allowing
1330 * the user to save an unprotected private key.
1332 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1333 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1337 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1339 * Set the key fingerprint.
1341 savecomment = *state->commentptr;
1342 *state->commentptr = NULL;
1345 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1346 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1350 rsa_fingerprint(buf, sizeof(buf), &state->key);
1351 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1353 *state->commentptr = savecomment;
1355 * Construct a decimal representation of the key, for
1356 * pasting into .ssh/authorized_keys or
1357 * .ssh/authorized_keys2 on a Unix box.
1360 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1361 IDC_PKSTATIC, &state->ssh2key);
1363 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1364 IDC_PKSTATIC, &state->key);
1368 * Finally, hide the progress bar and show the key data.
1370 ui_set_state(hwnd, state, 2);
1374 int id = ((LPHELPINFO)lParam)->iCtrlId;
1377 case IDC_GENERATING:
1381 topic = WINHELP_CTX_puttygen_generate; break;
1383 case IDC_KEYDISPLAY:
1384 topic = WINHELP_CTX_puttygen_pastekey; break;
1386 case IDC_FINGERPRINT:
1387 topic = WINHELP_CTX_puttygen_fingerprint; break;
1388 case IDC_COMMENTSTATIC:
1389 case IDC_COMMENTEDIT:
1390 topic = WINHELP_CTX_puttygen_comment; break;
1391 case IDC_PASSPHRASE1STATIC:
1392 case IDC_PASSPHRASE1EDIT:
1393 case IDC_PASSPHRASE2STATIC:
1394 case IDC_PASSPHRASE2EDIT:
1395 topic = WINHELP_CTX_puttygen_passphrase; break;
1396 case IDC_LOADSTATIC:
1398 topic = WINHELP_CTX_puttygen_load; break;
1399 case IDC_SAVESTATIC:
1401 topic = WINHELP_CTX_puttygen_savepriv; break;
1403 topic = WINHELP_CTX_puttygen_savepub; break;
1404 case IDC_TYPESTATIC:
1406 case IDC_KEYSSH2RSA:
1407 case IDC_KEYSSH2DSA:
1408 case IDC_KEYSSH2ECDSA:
1409 case IDC_KEYSSH2ED25519:
1410 topic = WINHELP_CTX_puttygen_keytype; break;
1411 case IDC_BITSSTATIC:
1413 topic = WINHELP_CTX_puttygen_bits; break;
1415 case IDC_EXPORT_OPENSSH_AUTO:
1416 case IDC_EXPORT_OPENSSH_NEW:
1417 case IDC_EXPORT_SSHCOM:
1418 topic = WINHELP_CTX_puttygen_conversions; break;
1421 launch_help(hwnd, topic);
1428 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1437 void cleanup_exit(int code)
1443 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1449 InitCommonControls();
1454 * See if we can find our Help file.
1458 split_into_argv(cmdline, &argc, &argv, NULL);
1461 if (!strcmp(argv[0], "-pgpfp")) {
1466 * Assume the first argument to be a private key file, and
1467 * attempt to load it.
1469 cmdline_keyfile = argv[0];
1474 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1477 return ret; /* just in case optimiser complains */