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);
423 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
425 unsigned char *pub_blob;
431 pub_blob = key->alg->public_blob(key->data, &pub_len);
433 fp = fopen(filename, "wb");
437 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
439 fprintf(fp, "Comment: \"");
440 for (p = key->comment; *p; p++) {
441 if (*p == '\\' || *p == '\"')
449 while (i < pub_len) {
451 int n = (pub_len - i < 3 ? pub_len - i : 3);
452 base64_encode_atom(pub_blob + i, n, buf);
456 if (++column >= 16) {
464 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
471 controlidstart = 100,
478 IDC_PKSTATIC, IDC_KEYDISPLAY,
479 IDC_FPSTATIC, IDC_FINGERPRINT,
480 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
481 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
482 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
484 IDC_GENSTATIC, IDC_GENERATE,
485 IDC_LOADSTATIC, IDC_LOAD,
486 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
488 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
489 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
490 IDC_BITSSTATIC, IDC_BITS,
494 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
498 static const int nokey_ids[] = { IDC_NOKEY, 0 };
499 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
500 static const int gotkey_ids[] = {
501 IDC_PKSTATIC, IDC_KEYDISPLAY,
502 IDC_FPSTATIC, IDC_FINGERPRINT,
503 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
504 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
505 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
509 * Small UI helper function to switch the state of the main dialog
510 * by enabling and disabling controls and menu items.
512 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
518 hidemany(hwnd, nokey_ids, FALSE);
519 hidemany(hwnd, generating_ids, TRUE);
520 hidemany(hwnd, gotkey_ids, TRUE);
521 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
522 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
523 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
524 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
525 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
526 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
527 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
528 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
529 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
530 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
531 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
532 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
533 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
534 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
535 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
536 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
537 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
538 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
539 MF_ENABLED|MF_BYCOMMAND);
540 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
541 MF_ENABLED|MF_BYCOMMAND);
542 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
543 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
544 MF_GRAYED|MF_BYCOMMAND);
545 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
546 MF_GRAYED|MF_BYCOMMAND);
547 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
548 MF_GRAYED|MF_BYCOMMAND);
550 case 1: /* generating key */
551 hidemany(hwnd, nokey_ids, TRUE);
552 hidemany(hwnd, generating_ids, FALSE);
553 hidemany(hwnd, gotkey_ids, TRUE);
554 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
555 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
556 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
557 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
558 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
559 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
560 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
561 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
562 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
563 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
564 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
565 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
566 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
567 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
568 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
569 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
570 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
571 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
572 MF_GRAYED|MF_BYCOMMAND);
573 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
574 MF_GRAYED|MF_BYCOMMAND);
575 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
576 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
577 MF_GRAYED|MF_BYCOMMAND);
578 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
579 MF_GRAYED|MF_BYCOMMAND);
580 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
581 MF_GRAYED|MF_BYCOMMAND);
584 hidemany(hwnd, nokey_ids, TRUE);
585 hidemany(hwnd, generating_ids, TRUE);
586 hidemany(hwnd, gotkey_ids, FALSE);
587 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
588 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
589 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
590 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
591 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
592 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
593 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
594 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
595 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
596 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
597 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
598 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
599 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
600 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
601 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
602 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
603 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
604 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
605 MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
607 MF_ENABLED|MF_BYCOMMAND);
608 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
610 * Enable export menu items if and only if the key type
611 * supports this kind of export.
613 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
614 #define do_export_menuitem(x,y) \
615 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
616 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
617 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
618 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
619 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
620 #undef do_export_menuitem
625 void load_key_file(HWND hwnd, struct MainDlgState *state,
626 Filename *filename, int was_import_cmd)
632 const char *errmsg = NULL;
634 struct RSAKey newkey1;
635 struct ssh2_userkey *newkey2 = NULL;
637 type = realtype = key_type(filename);
638 if (type != SSH_KEYTYPE_SSH1 &&
639 type != SSH_KEYTYPE_SSH2 &&
640 !import_possible(type)) {
641 char *msg = dupprintf("Couldn't load private key (%s)",
642 key_type_to_str(type));
643 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
644 HELPCTXID(errors_cantloadkey));
649 if (type != SSH_KEYTYPE_SSH1 &&
650 type != SSH_KEYTYPE_SSH2) {
652 type = import_target_type(type);
657 if (realtype == SSH_KEYTYPE_SSH1)
658 needs_pass = rsakey_encrypted(filename, &comment);
659 else if (realtype == SSH_KEYTYPE_SSH2)
660 needs_pass = ssh2_userkey_encrypted(filename, &comment);
662 needs_pass = import_encrypted(filename, realtype, &comment);
669 struct PassphraseProcStruct pps;
670 pps.passphrase = &passphrase;
671 pps.comment = comment;
672 dlgret = DialogBoxParam(hinst,
673 MAKEINTRESOURCE(210),
674 NULL, PassphraseProc,
680 assert(passphrase != NULL);
682 passphrase = dupstr("");
683 if (type == SSH_KEYTYPE_SSH1) {
684 if (realtype == type)
685 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
687 ret = import_ssh1(filename, realtype, &newkey1,
688 passphrase, &errmsg);
690 if (realtype == type)
691 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
693 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
694 if (newkey2 == SSH2_WRONG_PASSPHRASE)
705 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
706 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
707 HELPCTXID(errors_cantloadkey));
709 } else if (ret == 1) {
711 * Now update the key controls with all the
715 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
717 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
719 if (type == SSH_KEYTYPE_SSH1) {
724 state->commentptr = &state->key.comment;
725 state->key = newkey1;
728 * Set the key fingerprint.
730 savecomment = state->key.comment;
731 state->key.comment = NULL;
732 rsa_fingerprint(buf, sizeof(buf),
734 state->key.comment = savecomment;
736 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
738 * Construct a decimal representation
739 * of the key, for pasting into
740 * .ssh/authorized_keys on a Unix box.
742 setupbigedit1(hwnd, IDC_KEYDISPLAY,
743 IDC_PKSTATIC, &state->key);
750 &state->ssh2key.comment;
751 state->ssh2key = *newkey2; /* structure copy */
754 savecomment = state->ssh2key.comment;
755 state->ssh2key.comment = NULL;
758 fingerprint(state->ssh2key.data);
759 state->ssh2key.comment = savecomment;
761 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
764 setupbigedit2(hwnd, IDC_KEYDISPLAY,
765 IDC_PKSTATIC, &state->ssh2key);
767 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
771 * Finally, hide the progress bar and show
774 ui_set_state(hwnd, state, 2);
775 state->key_exists = TRUE;
778 * If the user has imported a foreign key
779 * using the Load command, let them know.
780 * If they've used the Import command, be
783 if (realtype != type && !was_import_cmd) {
785 sprintf(msg, "Successfully imported foreign key\n"
787 "To use this key with PuTTY, you need to\n"
788 "use the \"Save private key\" command to\n"
789 "save it in PuTTY's own format.",
790 key_type_to_str(realtype));
791 MessageBox(NULL, msg, "PuTTYgen Notice",
792 MB_OK | MB_ICONINFORMATION);
799 * Dialog-box function for the main PuTTYgen dialog box.
801 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
802 WPARAM wParam, LPARAM lParam)
804 static const char generating_msg[] =
805 "Please wait while a key is generated...";
806 static const char entropy_msg[] =
807 "Please generate some randomness by moving the mouse over the blank area.";
808 struct MainDlgState *state;
813 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
814 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
818 * If we add a Help button, this is where we destroy it
819 * if the help file isn't present.
822 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
823 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
825 state = snew(struct MainDlgState);
826 state->generation_thread_exists = FALSE;
827 state->collecting_entropy = FALSE;
828 state->entropy = NULL;
829 state->key_exists = FALSE;
830 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
836 menu1 = CreateMenu();
837 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
838 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
839 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
840 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
841 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
842 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
843 state->filemenu = menu1;
845 menu1 = CreateMenu();
846 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
847 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
848 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
849 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
850 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
851 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
852 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
853 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
854 state->keymenu = menu1;
856 menu1 = CreateMenu();
857 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
858 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
859 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
860 "Export &OpenSSH key");
861 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
862 "Export &OpenSSH key (force new file format)");
863 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
864 "Export &ssh.com key");
865 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
867 state->cvtmenu = menu1;
869 menu1 = CreateMenu();
870 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
872 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
873 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
881 { /* centre the window */
885 hw = GetDesktopWindow();
886 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
888 (rs.right + rs.left + rd.left - rd.right) / 2,
889 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
890 rd.right - rd.left, rd.bottom - rd.top, TRUE);
894 struct ctlpos cp, cp2;
896 /* Accelerators used: acglops1rbde */
898 ctlposinit(&cp, hwnd, 4, 4, 4);
899 beginbox(&cp, "Key", IDC_BOX_KEY);
901 statictext(&cp2, "No key.", 1, IDC_NOKEY);
903 statictext(&cp2, "", 1, IDC_GENERATING);
904 progressbar(&cp2, IDC_PROGRESS);
906 "&Public key for pasting into authorized_keys file:",
907 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
908 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
909 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
910 IDC_FINGERPRINT, 75);
911 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
913 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
914 IDC_COMMENTEDIT, 75);
915 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
916 IDC_PASSPHRASE1EDIT, 75);
917 staticpassedit(&cp, "C&onfirm passphrase:",
918 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
920 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
921 staticbtn(&cp, "Generate a public/private key pair",
922 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
923 staticbtn(&cp, "Load an existing private key file",
924 IDC_LOADSTATIC, "&Load", IDC_LOAD);
925 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
926 "Save p&ublic key", IDC_SAVEPUB,
927 "&Save private key", IDC_SAVE);
929 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
930 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 4,
931 "SSH-&1 (RSA)", IDC_KEYSSH1,
932 "SSH-2 &RSA", IDC_KEYSSH2RSA,
933 "SSH-2 &DSA", IDC_KEYSSH2DSA,
934 "SSH-2 &ECDSA", IDC_KEYSSH2ECDSA,
935 "SSH-2 ED&25519", IDC_KEYSSH2ED25519, NULL);
936 staticedit(&cp, "Number of &bits in a generated key:",
937 IDC_BITSSTATIC, IDC_BITS, 20);
940 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
941 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
942 IDC_KEYSSH2RSA, MF_BYCOMMAND);
943 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
946 * Initially, hide the progress bar and the key display,
947 * and show the no-key display. Also disable the Save
948 * buttons, because with no key we obviously can't save
951 ui_set_state(hwnd, state, 0);
954 * Load a key file if one was provided on the command line.
956 if (cmdline_keyfile) {
957 Filename *fn = filename_from_str(cmdline_keyfile);
958 load_key_file(hwnd, state, fn, 0);
964 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
965 if (state->collecting_entropy &&
966 state->entropy && state->entropy_got < state->entropy_required) {
967 state->entropy[state->entropy_got++] = lParam;
968 state->entropy[state->entropy_got++] = GetMessageTime();
969 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
970 state->entropy_got, 0);
971 if (state->entropy_got >= state->entropy_required) {
972 struct rsa_key_thread_params *params;
976 * Seed the entropy pool
978 random_add_heavynoise(state->entropy, state->entropy_size);
979 smemclr(state->entropy, state->entropy_size);
980 sfree(state->entropy);
981 state->collecting_entropy = FALSE;
983 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
984 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
985 MAKELPARAM(0, PROGRESSRANGE));
986 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
988 params = snew(struct rsa_key_thread_params);
989 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
990 params->dialog = hwnd;
991 params->keysize = state->keysize;
992 params->keytype = state->keytype;
993 params->key = &state->key;
994 params->dsskey = &state->dsskey;
996 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
997 params, 0, &threadid)) {
998 MessageBox(hwnd, "Out of thread resources",
999 "Key generation error",
1000 MB_OK | MB_ICONERROR);
1003 state->generation_thread_exists = TRUE;
1009 switch (LOWORD(wParam)) {
1011 case IDC_KEYSSH2RSA:
1012 case IDC_KEYSSH2DSA:
1013 case IDC_KEYSSH2ECDSA:
1014 case IDC_KEYSSH2ED25519:
1016 state = (struct MainDlgState *)
1017 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1018 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1019 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1021 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1022 LOWORD(wParam), MF_BYCOMMAND);
1023 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
1025 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1,
1027 LOWORD(wParam), MF_BYCOMMAND);
1031 PostMessage(hwnd, WM_CLOSE, 0, 0);
1033 case IDC_COMMENTEDIT:
1034 if (HIWORD(wParam) == EN_CHANGE) {
1035 state = (struct MainDlgState *)
1036 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1037 if (state->key_exists) {
1038 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1039 int len = GetWindowTextLength(editctl);
1040 if (*state->commentptr)
1041 sfree(*state->commentptr);
1042 *state->commentptr = snewn(len + 1, char);
1043 GetWindowText(editctl, *state->commentptr, len + 1);
1045 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1048 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1055 EnableWindow(hwnd, 0);
1056 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1057 EnableWindow(hwnd, 1);
1058 SetActiveWindow(hwnd);
1061 if (HIWORD(wParam) == BN_CLICKED ||
1062 HIWORD(wParam) == BN_DOUBLECLICKED) {
1063 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1067 if (HIWORD(wParam) != BN_CLICKED &&
1068 HIWORD(wParam) != BN_DOUBLECLICKED)
1071 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1072 if (!state->generation_thread_exists) {
1074 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1076 state->keysize = DEFAULT_KEYSIZE;
1077 /* If we ever introduce a new key type, check it here! */
1078 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1079 state->keytype = RSA;
1080 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1081 state->keytype = DSA;
1082 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1083 state->keytype = ECDSA;
1084 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1085 state->keytype = ED25519;
1087 if (state->keysize < 256) {
1088 int ret = MessageBox(hwnd,
1089 "PuTTYgen will not generate a key"
1090 " smaller than 256 bits.\n"
1091 "Key length reset to 256. Continue?",
1093 MB_ICONWARNING | MB_OKCANCEL);
1096 state->keysize = 256;
1097 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1099 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1100 state->keysize == 384 ||
1101 state->keysize == 521)) {
1102 int ret = MessageBox(hwnd,
1103 "Only 256, 384 and 521 bit elliptic"
1104 " curves are supported.\n"
1105 "Key length reset to 256. Continue?",
1107 MB_ICONWARNING | MB_OKCANCEL);
1110 state->keysize = 256;
1111 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1113 if (state->keytype == ED25519 && state->keysize != 256) {
1114 int ret = MessageBox(hwnd,
1115 "Only 256 bit Edwards elliptic"
1116 " curves are supported.\n"
1117 "Key length reset to 256. Continue?",
1119 MB_ICONWARNING | MB_OKCANCEL);
1122 state->keysize = 256;
1123 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1125 ui_set_state(hwnd, state, 1);
1126 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1127 state->key_exists = FALSE;
1128 state->collecting_entropy = TRUE;
1131 * My brief statistical tests on mouse movements
1132 * suggest that there are about 2.5 bits of
1133 * randomness in the x position, 2.5 in the y
1134 * position, and 1.7 in the message time, making
1135 * 5.7 bits of unpredictability per mouse movement.
1136 * However, other people have told me it's far less
1137 * than that, so I'm going to be stupidly cautious
1138 * and knock that down to a nice round 2. With this
1139 * method, we require two words per mouse movement,
1140 * so with 2 bits per mouse movement we expect 2
1141 * bits every 2 words.
1143 state->entropy_required = (state->keysize / 2) * 2;
1144 state->entropy_got = 0;
1145 state->entropy_size = (state->entropy_required *
1147 state->entropy = snewn(state->entropy_required, unsigned);
1149 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1150 MAKELPARAM(0, state->entropy_required));
1151 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1155 case IDC_EXPORT_OPENSSH_AUTO:
1156 case IDC_EXPORT_OPENSSH_NEW:
1157 case IDC_EXPORT_SSHCOM:
1158 if (HIWORD(wParam) != BN_CLICKED)
1161 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1162 if (state->key_exists) {
1163 char filename[FILENAME_MAX];
1164 char *passphrase, *passphrase2;
1168 realtype = SSH_KEYTYPE_SSH2;
1170 realtype = SSH_KEYTYPE_SSH1;
1172 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1173 type = SSH_KEYTYPE_OPENSSH_AUTO;
1174 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1175 type = SSH_KEYTYPE_OPENSSH_NEW;
1176 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1177 type = SSH_KEYTYPE_SSHCOM;
1181 if (type != realtype &&
1182 import_target_type(type) != realtype) {
1184 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1185 " format", (state->ssh2 ? 2 : 1),
1186 (state->ssh2 ? 1 : 2));
1187 MessageBox(hwnd, msg,
1188 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1192 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1193 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1194 if (strcmp(passphrase, passphrase2)) {
1196 "The two passphrases given do not match.",
1197 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1198 burnstr(passphrase);
1199 burnstr(passphrase2);
1202 burnstr(passphrase2);
1205 ret = MessageBox(hwnd,
1206 "Are you sure you want to save this key\n"
1207 "without a passphrase to protect it?",
1209 MB_YESNO | MB_ICONWARNING);
1211 burnstr(passphrase);
1215 if (prompt_keyfile(hwnd, "Save private key as:",
1216 filename, 1, (type == realtype))) {
1218 FILE *fp = fopen(filename, "r");
1222 buffer = dupprintf("Overwrite existing file\n%s?",
1224 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1225 MB_YESNO | MB_ICONWARNING);
1228 burnstr(passphrase);
1234 Filename *fn = filename_from_str(filename);
1235 if (type != realtype)
1236 ret = export_ssh2(fn, type, &state->ssh2key,
1237 *passphrase ? passphrase : NULL);
1239 ret = ssh2_save_userkey(fn, &state->ssh2key,
1240 *passphrase ? passphrase :
1244 Filename *fn = filename_from_str(filename);
1245 if (type != realtype)
1246 ret = export_ssh1(fn, type, &state->key,
1247 *passphrase ? passphrase : NULL);
1249 ret = saversakey(fn, &state->key,
1250 *passphrase ? passphrase : NULL);
1254 MessageBox(hwnd, "Unable to save key file",
1255 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1258 burnstr(passphrase);
1262 if (HIWORD(wParam) != BN_CLICKED)
1265 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1266 if (state->key_exists) {
1267 char filename[FILENAME_MAX];
1268 if (prompt_keyfile(hwnd, "Save public key as:",
1271 FILE *fp = fopen(filename, "r");
1275 buffer = dupprintf("Overwrite existing file\n%s?",
1277 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1278 MB_YESNO | MB_ICONWARNING);
1283 fp = fopen(filename, "w");
1285 MessageBox(hwnd, "Unable to open key file",
1286 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1290 unsigned char *blob;
1291 blob = state->ssh2key.alg->public_blob
1292 (state->ssh2key.data, &bloblen);
1293 ssh2_write_pubkey(fp, state->ssh2key.comment,
1295 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1297 ssh1_write_pubkey(fp, &state->key);
1299 if (fclose(fp) < 0) {
1300 MessageBox(hwnd, "Unable to save key file",
1301 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1309 if (HIWORD(wParam) != BN_CLICKED)
1312 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1313 if (!state->generation_thread_exists) {
1314 char filename[FILENAME_MAX];
1315 if (prompt_keyfile(hwnd, "Load private key:",
1316 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1317 Filename *fn = filename_from_str(filename);
1318 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1326 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1327 state->generation_thread_exists = FALSE;
1328 state->key_exists = TRUE;
1329 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1330 MAKELPARAM(0, PROGRESSRANGE));
1331 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1333 if (state->keytype == DSA) {
1334 state->ssh2key.data = &state->dsskey;
1335 state->ssh2key.alg = &ssh_dss;
1336 } else if (state->keytype == ECDSA) {
1337 state->ssh2key.data = &state->eckey;
1338 if (state->eckey.publicKey.curve->fieldBits == 256)
1339 state->ssh2key.alg = &ssh_ecdsa_nistp256;
1340 else if (state->eckey.publicKey.curve->fieldBits == 384)
1341 state->ssh2key.alg = &ssh_ecdsa_nistp384;
1343 state->ssh2key.alg = &ssh_ecdsa_nistp521;
1344 } else if (state->keytype == ED25519) {
1345 state->ssh2key.data = &state->eckey;
1346 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1348 state->ssh2key.data = &state->key;
1349 state->ssh2key.alg = &ssh_rsa;
1351 state->commentptr = &state->ssh2key.comment;
1353 state->commentptr = &state->key.comment;
1356 * Invent a comment for the key. We'll do this by including
1357 * the date in it. This will be so horrifyingly ugly that
1358 * the user will immediately want to change it, which is
1361 *state->commentptr = snewn(30, char);
1365 if (state->keytype == DSA)
1366 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1367 else if (state->keytype == ECDSA)
1368 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1369 else if (state->keytype == ED25519)
1370 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1372 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1376 * Now update the key controls with all the key data.
1381 * Blank passphrase, initially. This isn't dangerous,
1382 * because we will warn (Are You Sure?) before allowing
1383 * the user to save an unprotected private key.
1385 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1386 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1390 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1392 * Set the key fingerprint.
1394 savecomment = *state->commentptr;
1395 *state->commentptr = NULL;
1398 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1399 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1403 rsa_fingerprint(buf, sizeof(buf), &state->key);
1404 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1406 *state->commentptr = savecomment;
1408 * Construct a decimal representation of the key, for
1409 * pasting into .ssh/authorized_keys or
1410 * .ssh/authorized_keys2 on a Unix box.
1413 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1414 IDC_PKSTATIC, &state->ssh2key);
1416 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1417 IDC_PKSTATIC, &state->key);
1421 * Finally, hide the progress bar and show the key data.
1423 ui_set_state(hwnd, state, 2);
1427 int id = ((LPHELPINFO)lParam)->iCtrlId;
1430 case IDC_GENERATING:
1434 topic = WINHELP_CTX_puttygen_generate; break;
1436 case IDC_KEYDISPLAY:
1437 topic = WINHELP_CTX_puttygen_pastekey; break;
1439 case IDC_FINGERPRINT:
1440 topic = WINHELP_CTX_puttygen_fingerprint; break;
1441 case IDC_COMMENTSTATIC:
1442 case IDC_COMMENTEDIT:
1443 topic = WINHELP_CTX_puttygen_comment; break;
1444 case IDC_PASSPHRASE1STATIC:
1445 case IDC_PASSPHRASE1EDIT:
1446 case IDC_PASSPHRASE2STATIC:
1447 case IDC_PASSPHRASE2EDIT:
1448 topic = WINHELP_CTX_puttygen_passphrase; break;
1449 case IDC_LOADSTATIC:
1451 topic = WINHELP_CTX_puttygen_load; break;
1452 case IDC_SAVESTATIC:
1454 topic = WINHELP_CTX_puttygen_savepriv; break;
1456 topic = WINHELP_CTX_puttygen_savepub; break;
1457 case IDC_TYPESTATIC:
1459 case IDC_KEYSSH2RSA:
1460 case IDC_KEYSSH2DSA:
1461 case IDC_KEYSSH2ECDSA:
1462 case IDC_KEYSSH2ED25519:
1463 topic = WINHELP_CTX_puttygen_keytype; break;
1464 case IDC_BITSSTATIC:
1466 topic = WINHELP_CTX_puttygen_bits; break;
1468 case IDC_EXPORT_OPENSSH_AUTO:
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 */