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} 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);
348 rsa_generate(params->key, params->keysize, progress_update, &prog);
350 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
356 struct MainDlgState {
357 int collecting_entropy;
358 int generation_thread_exists;
360 int entropy_got, entropy_required, entropy_size;
364 char **commentptr; /* points to key.comment or ssh2key.comment */
365 struct ssh2_userkey ssh2key;
369 struct dss_key dsskey;
372 HMENU filemenu, keymenu, cvtmenu;
375 static void hidemany(HWND hwnd, const int *ids, int hideit)
378 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
382 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
387 dec1 = bignum_decimal(key->exponent);
388 dec2 = bignum_decimal(key->modulus);
389 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
390 dec1, dec2, key->comment);
391 SetDlgItemText(hwnd, id, buffer);
392 SetDlgItemText(hwnd, idstatic,
393 "&Public key for pasting into authorized_keys file:");
399 static void setupbigedit2(HWND hwnd, int id, int idstatic,
400 struct ssh2_userkey *key)
402 unsigned char *pub_blob;
407 pub_blob = key->alg->public_blob(key->data, &pub_len);
408 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
409 strlen(key->comment) + 3, char);
410 strcpy(buffer, key->alg->name);
411 p = buffer + strlen(buffer);
414 while (i < pub_len) {
415 int n = (pub_len - i < 3 ? pub_len - i : 3);
416 base64_encode_atom(pub_blob + i, n, p);
421 strcpy(p, key->comment);
422 SetDlgItemText(hwnd, id, buffer);
423 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
424 "OpenSSH authorized_keys file:");
429 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
434 fp = fopen(filename, "wb");
437 dec1 = bignum_decimal(key->exponent);
438 dec2 = bignum_decimal(key->modulus);
439 fprintf(fp, "%d %s %s %s\n",
440 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
448 * Warn about the obsolescent key file format.
450 void old_keyfile_warning(void)
452 static const char mbtitle[] = "PuTTY Key File Warning";
453 static const char message[] =
454 "You are loading an SSH-2 private key which has an\n"
455 "old version of the file format. This means your key\n"
456 "file is not fully tamperproof. Future versions of\n"
457 "PuTTY may stop supporting this private key format,\n"
458 "so we recommend you convert your key to the new\n"
461 "Once the key is loaded into PuTTYgen, you can perform\n"
462 "this conversion simply by saving it again.";
464 MessageBox(NULL, message, mbtitle, MB_OK);
467 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
469 unsigned char *pub_blob;
475 pub_blob = key->alg->public_blob(key->data, &pub_len);
477 fp = fopen(filename, "wb");
481 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
483 fprintf(fp, "Comment: \"");
484 for (p = key->comment; *p; p++) {
485 if (*p == '\\' || *p == '\"')
493 while (i < pub_len) {
495 int n = (pub_len - i < 3 ? pub_len - i : 3);
496 base64_encode_atom(pub_blob + i, n, buf);
500 if (++column >= 16) {
508 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
515 controlidstart = 100,
522 IDC_PKSTATIC, IDC_KEYDISPLAY,
523 IDC_FPSTATIC, IDC_FINGERPRINT,
524 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
525 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
526 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
528 IDC_GENSTATIC, IDC_GENERATE,
529 IDC_LOADSTATIC, IDC_LOAD,
530 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
532 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
534 IDC_BITSSTATIC, IDC_BITS,
538 IDC_EXPORT_OPENSSH_PEM, IDC_EXPORT_OPENSSH_NEW,
542 static const int nokey_ids[] = { IDC_NOKEY, 0 };
543 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
544 static const int gotkey_ids[] = {
545 IDC_PKSTATIC, IDC_KEYDISPLAY,
546 IDC_FPSTATIC, IDC_FINGERPRINT,
547 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
548 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
549 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
553 * Small UI helper function to switch the state of the main dialog
554 * by enabling and disabling controls and menu items.
556 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
562 hidemany(hwnd, nokey_ids, FALSE);
563 hidemany(hwnd, generating_ids, TRUE);
564 hidemany(hwnd, gotkey_ids, TRUE);
565 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
566 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
567 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
568 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
569 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
570 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
571 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
572 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
573 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
574 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
575 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
576 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
577 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
578 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
579 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
580 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
581 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
582 MF_ENABLED|MF_BYCOMMAND);
583 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
584 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_PEM,
585 MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
587 MF_GRAYED|MF_BYCOMMAND);
588 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
589 MF_GRAYED|MF_BYCOMMAND);
591 case 1: /* generating key */
592 hidemany(hwnd, nokey_ids, TRUE);
593 hidemany(hwnd, generating_ids, FALSE);
594 hidemany(hwnd, gotkey_ids, TRUE);
595 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
596 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
597 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
598 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
599 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
600 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
601 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
602 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
603 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
604 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
605 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
606 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
607 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
608 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
609 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
610 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
611 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
612 MF_GRAYED|MF_BYCOMMAND);
613 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
614 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_PEM,
615 MF_GRAYED|MF_BYCOMMAND);
616 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
617 MF_GRAYED|MF_BYCOMMAND);
618 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
619 MF_GRAYED|MF_BYCOMMAND);
622 hidemany(hwnd, nokey_ids, TRUE);
623 hidemany(hwnd, generating_ids, TRUE);
624 hidemany(hwnd, gotkey_ids, FALSE);
625 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
626 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
627 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
628 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
629 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
630 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
631 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
632 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
633 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
634 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
635 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
636 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
637 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
638 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
639 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
640 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
641 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
642 MF_ENABLED|MF_BYCOMMAND);
643 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
645 * Enable export menu items if and only if the key type
646 * supports this kind of export.
648 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
649 #define do_export_menuitem(x,y) \
650 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
651 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
652 do_export_menuitem(IDC_EXPORT_OPENSSH_PEM, SSH_KEYTYPE_OPENSSH_PEM);
653 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
654 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
655 #undef do_export_menuitem
660 void load_key_file(HWND hwnd, struct MainDlgState *state,
661 Filename *filename, int was_import_cmd)
667 const char *errmsg = NULL;
669 struct RSAKey newkey1;
670 struct ssh2_userkey *newkey2 = NULL;
672 type = realtype = key_type(filename);
673 if (type != SSH_KEYTYPE_SSH1 &&
674 type != SSH_KEYTYPE_SSH2 &&
675 !import_possible(type)) {
676 char *msg = dupprintf("Couldn't load private key (%s)",
677 key_type_to_str(type));
678 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
679 HELPCTXID(errors_cantloadkey));
684 if (type != SSH_KEYTYPE_SSH1 &&
685 type != SSH_KEYTYPE_SSH2) {
687 type = import_target_type(type);
692 if (realtype == SSH_KEYTYPE_SSH1)
693 needs_pass = rsakey_encrypted(filename, &comment);
694 else if (realtype == SSH_KEYTYPE_SSH2)
695 needs_pass = ssh2_userkey_encrypted(filename, &comment);
697 needs_pass = import_encrypted(filename, realtype, &comment);
704 struct PassphraseProcStruct pps;
705 pps.passphrase = &passphrase;
706 pps.comment = comment;
707 dlgret = DialogBoxParam(hinst,
708 MAKEINTRESOURCE(210),
709 NULL, PassphraseProc,
715 assert(passphrase != NULL);
717 passphrase = dupstr("");
718 if (type == SSH_KEYTYPE_SSH1) {
719 if (realtype == type)
720 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
722 ret = import_ssh1(filename, realtype, &newkey1,
723 passphrase, &errmsg);
725 if (realtype == type)
726 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
728 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
729 if (newkey2 == SSH2_WRONG_PASSPHRASE)
740 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
741 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
742 HELPCTXID(errors_cantloadkey));
744 } else if (ret == 1) {
746 * Now update the key controls with all the
750 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
752 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
754 if (type == SSH_KEYTYPE_SSH1) {
759 state->commentptr = &state->key.comment;
760 state->key = newkey1;
763 * Set the key fingerprint.
765 savecomment = state->key.comment;
766 state->key.comment = NULL;
767 rsa_fingerprint(buf, sizeof(buf),
769 state->key.comment = savecomment;
771 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
773 * Construct a decimal representation
774 * of the key, for pasting into
775 * .ssh/authorized_keys on a Unix box.
777 setupbigedit1(hwnd, IDC_KEYDISPLAY,
778 IDC_PKSTATIC, &state->key);
785 &state->ssh2key.comment;
786 state->ssh2key = *newkey2; /* structure copy */
789 savecomment = state->ssh2key.comment;
790 state->ssh2key.comment = NULL;
793 fingerprint(state->ssh2key.data);
794 state->ssh2key.comment = savecomment;
796 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
799 setupbigedit2(hwnd, IDC_KEYDISPLAY,
800 IDC_PKSTATIC, &state->ssh2key);
802 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
806 * Finally, hide the progress bar and show
809 ui_set_state(hwnd, state, 2);
810 state->key_exists = TRUE;
813 * If the user has imported a foreign key
814 * using the Load command, let them know.
815 * If they've used the Import command, be
818 if (realtype != type && !was_import_cmd) {
820 sprintf(msg, "Successfully imported foreign key\n"
822 "To use this key with PuTTY, you need to\n"
823 "use the \"Save private key\" command to\n"
824 "save it in PuTTY's own format.",
825 key_type_to_str(realtype));
826 MessageBox(NULL, msg, "PuTTYgen Notice",
827 MB_OK | MB_ICONINFORMATION);
834 * Dialog-box function for the main PuTTYgen dialog box.
836 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
837 WPARAM wParam, LPARAM lParam)
839 static const char generating_msg[] =
840 "Please wait while a key is generated...";
841 static const char entropy_msg[] =
842 "Please generate some randomness by moving the mouse over the blank area.";
843 struct MainDlgState *state;
848 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
849 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
853 * If we add a Help button, this is where we destroy it
854 * if the help file isn't present.
857 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
858 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
860 state = snew(struct MainDlgState);
861 state->generation_thread_exists = FALSE;
862 state->collecting_entropy = FALSE;
863 state->entropy = NULL;
864 state->key_exists = FALSE;
865 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
871 menu1 = CreateMenu();
872 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
873 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
874 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
875 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
876 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
877 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
878 state->filemenu = menu1;
880 menu1 = CreateMenu();
881 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
882 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
883 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
884 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
885 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
886 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
887 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
888 state->keymenu = menu1;
890 menu1 = CreateMenu();
891 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
892 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
893 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_PEM,
894 "Export &OpenSSH key (old PEM format)");
895 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
896 "Export &OpenSSH key (new format)");
897 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
898 "Export &ssh.com key");
899 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
901 state->cvtmenu = menu1;
903 menu1 = CreateMenu();
904 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
906 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
907 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
915 { /* centre the window */
919 hw = GetDesktopWindow();
920 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
922 (rs.right + rs.left + rd.left - rd.right) / 2,
923 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
924 rd.right - rd.left, rd.bottom - rd.top, TRUE);
928 struct ctlpos cp, cp2;
930 /* Accelerators used: acglops1rbde */
932 ctlposinit(&cp, hwnd, 4, 4, 4);
933 beginbox(&cp, "Key", IDC_BOX_KEY);
935 statictext(&cp2, "No key.", 1, IDC_NOKEY);
937 statictext(&cp2, "", 1, IDC_GENERATING);
938 progressbar(&cp2, IDC_PROGRESS);
940 "&Public key for pasting into authorized_keys file:",
941 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
942 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
943 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
944 IDC_FINGERPRINT, 75);
945 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
947 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
948 IDC_COMMENTEDIT, 75);
949 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
950 IDC_PASSPHRASE1EDIT, 75);
951 staticpassedit(&cp, "C&onfirm passphrase:",
952 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
954 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
955 staticbtn(&cp, "Generate a public/private key pair",
956 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
957 staticbtn(&cp, "Load an existing private key file",
958 IDC_LOADSTATIC, "&Load", IDC_LOAD);
959 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
960 "Save p&ublic key", IDC_SAVEPUB,
961 "&Save private key", IDC_SAVE);
963 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
964 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 4,
965 "SSH-&1 (RSA)", IDC_KEYSSH1,
966 "SSH-2 &RSA", IDC_KEYSSH2RSA,
967 "SSH-2 &DSA", IDC_KEYSSH2DSA,
968 "SSH-2 &ECDSA", IDC_KEYSSH2ECDSA, NULL);
969 staticedit(&cp, "Number of &bits in a generated key:",
970 IDC_BITSSTATIC, IDC_BITS, 20);
973 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
974 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
975 IDC_KEYSSH2RSA, MF_BYCOMMAND);
976 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
979 * Initially, hide the progress bar and the key display,
980 * and show the no-key display. Also disable the Save
981 * buttons, because with no key we obviously can't save
984 ui_set_state(hwnd, state, 0);
987 * Load a key file if one was provided on the command line.
989 if (cmdline_keyfile) {
990 Filename *fn = filename_from_str(cmdline_keyfile);
991 load_key_file(hwnd, state, fn, 0);
997 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
998 if (state->collecting_entropy &&
999 state->entropy && state->entropy_got < state->entropy_required) {
1000 state->entropy[state->entropy_got++] = lParam;
1001 state->entropy[state->entropy_got++] = GetMessageTime();
1002 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1003 state->entropy_got, 0);
1004 if (state->entropy_got >= state->entropy_required) {
1005 struct rsa_key_thread_params *params;
1009 * Seed the entropy pool
1011 random_add_heavynoise(state->entropy, state->entropy_size);
1012 smemclr(state->entropy, state->entropy_size);
1013 sfree(state->entropy);
1014 state->collecting_entropy = FALSE;
1016 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1017 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1018 MAKELPARAM(0, PROGRESSRANGE));
1019 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1021 params = snew(struct rsa_key_thread_params);
1022 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1023 params->dialog = hwnd;
1024 params->keysize = state->keysize;
1025 params->keytype = state->keytype;
1026 params->key = &state->key;
1027 params->dsskey = &state->dsskey;
1029 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
1030 params, 0, &threadid)) {
1031 MessageBox(hwnd, "Out of thread resources",
1032 "Key generation error",
1033 MB_OK | MB_ICONERROR);
1036 state->generation_thread_exists = TRUE;
1042 switch (LOWORD(wParam)) {
1044 case IDC_KEYSSH2RSA:
1045 case IDC_KEYSSH2DSA:
1046 case IDC_KEYSSH2ECDSA:
1048 state = (struct MainDlgState *)
1049 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1050 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1051 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1053 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1054 LOWORD(wParam), MF_BYCOMMAND);
1055 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
1057 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
1059 LOWORD(wParam), MF_BYCOMMAND);
1063 PostMessage(hwnd, WM_CLOSE, 0, 0);
1065 case IDC_COMMENTEDIT:
1066 if (HIWORD(wParam) == EN_CHANGE) {
1067 state = (struct MainDlgState *)
1068 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1069 if (state->key_exists) {
1070 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1071 int len = GetWindowTextLength(editctl);
1072 if (*state->commentptr)
1073 sfree(*state->commentptr);
1074 *state->commentptr = snewn(len + 1, char);
1075 GetWindowText(editctl, *state->commentptr, len + 1);
1077 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1080 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1087 EnableWindow(hwnd, 0);
1088 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1089 EnableWindow(hwnd, 1);
1090 SetActiveWindow(hwnd);
1093 if (HIWORD(wParam) == BN_CLICKED ||
1094 HIWORD(wParam) == BN_DOUBLECLICKED) {
1095 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1099 if (HIWORD(wParam) != BN_CLICKED &&
1100 HIWORD(wParam) != BN_DOUBLECLICKED)
1103 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1104 if (!state->generation_thread_exists) {
1106 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1108 state->keysize = DEFAULT_KEYSIZE;
1109 /* If we ever introduce a new key type, check it here! */
1110 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1111 state->keytype = RSA;
1112 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1113 state->keytype = DSA;
1114 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1115 state->keytype = ECDSA;
1117 if (state->keysize < 256) {
1118 int ret = MessageBox(hwnd,
1119 "PuTTYgen will not generate a key"
1120 " smaller than 256 bits.\n"
1121 "Key length reset to 256. Continue?",
1123 MB_ICONWARNING | MB_OKCANCEL);
1126 state->keysize = 256;
1127 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1129 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1130 state->keysize == 384 ||
1131 state->keysize == 521)) {
1132 int ret = MessageBox(hwnd,
1133 "Only 256, 384 and 521 bit elliptic"
1134 " curves are supported.\n"
1135 "Key length reset to 256. Continue?",
1137 MB_ICONWARNING | MB_OKCANCEL);
1140 state->keysize = 256;
1141 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1143 ui_set_state(hwnd, state, 1);
1144 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1145 state->key_exists = FALSE;
1146 state->collecting_entropy = TRUE;
1149 * My brief statistical tests on mouse movements
1150 * suggest that there are about 2.5 bits of
1151 * randomness in the x position, 2.5 in the y
1152 * position, and 1.7 in the message time, making
1153 * 5.7 bits of unpredictability per mouse movement.
1154 * However, other people have told me it's far less
1155 * than that, so I'm going to be stupidly cautious
1156 * and knock that down to a nice round 2. With this
1157 * method, we require two words per mouse movement,
1158 * so with 2 bits per mouse movement we expect 2
1159 * bits every 2 words.
1161 state->entropy_required = (state->keysize / 2) * 2;
1162 state->entropy_got = 0;
1163 state->entropy_size = (state->entropy_required *
1165 state->entropy = snewn(state->entropy_required, unsigned);
1167 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1168 MAKELPARAM(0, state->entropy_required));
1169 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1173 case IDC_EXPORT_OPENSSH_PEM:
1174 case IDC_EXPORT_OPENSSH_NEW:
1175 case IDC_EXPORT_SSHCOM:
1176 if (HIWORD(wParam) != BN_CLICKED)
1179 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1180 if (state->key_exists) {
1181 char filename[FILENAME_MAX];
1182 char *passphrase, *passphrase2;
1186 realtype = SSH_KEYTYPE_SSH2;
1188 realtype = SSH_KEYTYPE_SSH1;
1190 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_PEM)
1191 type = SSH_KEYTYPE_OPENSSH_PEM;
1192 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1193 type = SSH_KEYTYPE_OPENSSH_NEW;
1194 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1195 type = SSH_KEYTYPE_SSHCOM;
1199 if (type != realtype &&
1200 import_target_type(type) != realtype) {
1202 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1203 " format", (state->ssh2 ? 2 : 1),
1204 (state->ssh2 ? 1 : 2));
1205 MessageBox(hwnd, msg,
1206 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1210 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1211 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1212 if (strcmp(passphrase, passphrase2)) {
1214 "The two passphrases given do not match.",
1215 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1216 burnstr(passphrase);
1217 burnstr(passphrase2);
1220 burnstr(passphrase2);
1223 ret = MessageBox(hwnd,
1224 "Are you sure you want to save this key\n"
1225 "without a passphrase to protect it?",
1227 MB_YESNO | MB_ICONWARNING);
1229 burnstr(passphrase);
1233 if (prompt_keyfile(hwnd, "Save private key as:",
1234 filename, 1, (type == realtype))) {
1236 FILE *fp = fopen(filename, "r");
1240 buffer = dupprintf("Overwrite existing file\n%s?",
1242 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1243 MB_YESNO | MB_ICONWARNING);
1246 burnstr(passphrase);
1252 Filename *fn = filename_from_str(filename);
1253 if (type != realtype)
1254 ret = export_ssh2(fn, type, &state->ssh2key,
1255 *passphrase ? passphrase : NULL);
1257 ret = ssh2_save_userkey(fn, &state->ssh2key,
1258 *passphrase ? passphrase :
1262 Filename *fn = filename_from_str(filename);
1263 if (type != realtype)
1264 ret = export_ssh1(fn, type, &state->key,
1265 *passphrase ? passphrase : NULL);
1267 ret = saversakey(fn, &state->key,
1268 *passphrase ? passphrase : NULL);
1272 MessageBox(hwnd, "Unable to save key file",
1273 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1276 burnstr(passphrase);
1280 if (HIWORD(wParam) != BN_CLICKED)
1283 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1284 if (state->key_exists) {
1285 char filename[FILENAME_MAX];
1286 if (prompt_keyfile(hwnd, "Save public key as:",
1289 FILE *fp = fopen(filename, "r");
1293 buffer = dupprintf("Overwrite existing file\n%s?",
1295 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1296 MB_YESNO | MB_ICONWARNING);
1302 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1304 ret = save_ssh1_pubkey(filename, &state->key);
1307 MessageBox(hwnd, "Unable to save key file",
1308 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1315 if (HIWORD(wParam) != BN_CLICKED)
1318 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1319 if (!state->generation_thread_exists) {
1320 char filename[FILENAME_MAX];
1321 if (prompt_keyfile(hwnd, "Load private key:",
1322 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1323 Filename *fn = filename_from_str(filename);
1324 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1332 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1333 state->generation_thread_exists = FALSE;
1334 state->key_exists = TRUE;
1335 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1336 MAKELPARAM(0, PROGRESSRANGE));
1337 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1339 if (state->keytype == DSA) {
1340 state->ssh2key.data = &state->dsskey;
1341 state->ssh2key.alg = &ssh_dss;
1342 } else if (state->keytype == ECDSA) {
1343 state->ssh2key.data = &state->eckey;
1344 if (state->eckey.publicKey.curve->fieldBits == 256)
1345 state->ssh2key.alg = &ssh_ecdsa_nistp256;
1346 else if (state->eckey.publicKey.curve->fieldBits == 384)
1347 state->ssh2key.alg = &ssh_ecdsa_nistp384;
1349 state->ssh2key.alg = &ssh_ecdsa_nistp521;
1351 state->ssh2key.data = &state->key;
1352 state->ssh2key.alg = &ssh_rsa;
1354 state->commentptr = &state->ssh2key.comment;
1356 state->commentptr = &state->key.comment;
1359 * Invent a comment for the key. We'll do this by including
1360 * the date in it. This will be so horrifyingly ugly that
1361 * the user will immediately want to change it, which is
1364 *state->commentptr = snewn(30, char);
1368 if (state->keytype == DSA)
1369 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1370 else if (state->keytype == ECDSA)
1371 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1373 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1377 * Now update the key controls with all the key data.
1382 * Blank passphrase, initially. This isn't dangerous,
1383 * because we will warn (Are You Sure?) before allowing
1384 * the user to save an unprotected private key.
1386 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1387 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1391 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1393 * Set the key fingerprint.
1395 savecomment = *state->commentptr;
1396 *state->commentptr = NULL;
1399 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1400 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1404 rsa_fingerprint(buf, sizeof(buf), &state->key);
1405 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1407 *state->commentptr = savecomment;
1409 * Construct a decimal representation of the key, for
1410 * pasting into .ssh/authorized_keys or
1411 * .ssh/authorized_keys2 on a Unix box.
1414 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1415 IDC_PKSTATIC, &state->ssh2key);
1417 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1418 IDC_PKSTATIC, &state->key);
1422 * Finally, hide the progress bar and show the key data.
1424 ui_set_state(hwnd, state, 2);
1428 int id = ((LPHELPINFO)lParam)->iCtrlId;
1431 case IDC_GENERATING:
1435 topic = WINHELP_CTX_puttygen_generate; break;
1437 case IDC_KEYDISPLAY:
1438 topic = WINHELP_CTX_puttygen_pastekey; break;
1440 case IDC_FINGERPRINT:
1441 topic = WINHELP_CTX_puttygen_fingerprint; break;
1442 case IDC_COMMENTSTATIC:
1443 case IDC_COMMENTEDIT:
1444 topic = WINHELP_CTX_puttygen_comment; break;
1445 case IDC_PASSPHRASE1STATIC:
1446 case IDC_PASSPHRASE1EDIT:
1447 case IDC_PASSPHRASE2STATIC:
1448 case IDC_PASSPHRASE2EDIT:
1449 topic = WINHELP_CTX_puttygen_passphrase; break;
1450 case IDC_LOADSTATIC:
1452 topic = WINHELP_CTX_puttygen_load; break;
1453 case IDC_SAVESTATIC:
1455 topic = WINHELP_CTX_puttygen_savepriv; break;
1457 topic = WINHELP_CTX_puttygen_savepub; break;
1458 case IDC_TYPESTATIC:
1460 case IDC_KEYSSH2RSA:
1461 case IDC_KEYSSH2DSA:
1462 case IDC_KEYSSH2ECDSA:
1463 topic = WINHELP_CTX_puttygen_keytype; break;
1464 case IDC_BITSSTATIC:
1466 topic = WINHELP_CTX_puttygen_bits; break;
1468 case IDC_EXPORT_OPENSSH_PEM:
1469 case IDC_EXPORT_OPENSSH_NEW:
1470 case IDC_EXPORT_SSHCOM:
1471 topic = WINHELP_CTX_puttygen_conversions; break;
1474 launch_help(hwnd, topic);
1481 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1490 void cleanup_exit(int code)
1496 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1502 InitCommonControls();
1507 * See if we can find our Help file.
1511 split_into_argv(cmdline, &argc, &argv, NULL);
1514 if (!strcmp(argv[0], "-pgpfp")) {
1519 * Assume the first argument to be a private key file, and
1520 * attempt to load it.
1522 cmdline_keyfile = argv[0];
1527 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1530 return ret; /* just in case optimiser complains */