2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
22 #define WM_DONEKEY (WM_APP + 1)
24 #define DEFAULT_KEY_BITS 2048
25 #define DEFAULT_CURVE_INDEX 0
27 static char *cmdline_keyfile = NULL;
30 * Print a modal (Really Bad) message box and perform a fatal exit.
32 void modalfatalbox(const char *fmt, ...)
38 stuff = dupvprintf(fmt, ap);
40 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
41 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
47 * Print a non-fatal message box and do not exit.
49 void nonfatal(const char *fmt, ...)
55 stuff = dupvprintf(fmt, ap);
57 MessageBox(NULL, stuff, "PuTTYgen Error",
58 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
62 /* ----------------------------------------------------------------------
63 * Progress report code. This is really horrible :-)
65 #define PROGRESSRANGE 65535
71 unsigned startpoint, total;
72 unsigned param, current, n; /* if exponential */
73 unsigned mult; /* if linear */
75 unsigned total, divisor, range;
79 static void progress_update(void *param, int action, int phase, int iprogress)
81 struct progress *p = (struct progress *) param;
82 unsigned progress = iprogress;
85 if (action < PROGFN_READY && p->nphases < phase)
88 case PROGFN_INITIALISE:
91 case PROGFN_LIN_PHASE:
92 p->phases[phase-1].exponential = 0;
93 p->phases[phase-1].mult = p->phases[phase].total / progress;
95 case PROGFN_EXP_PHASE:
96 p->phases[phase-1].exponential = 1;
97 p->phases[phase-1].param = 0x10000 + progress;
98 p->phases[phase-1].current = p->phases[phase-1].total;
99 p->phases[phase-1].n = 0;
101 case PROGFN_PHASE_EXTENT:
102 p->phases[phase-1].total = progress;
108 for (i = 0; i < p->nphases; i++) {
109 p->phases[i].startpoint = total;
110 total += p->phases[i].total;
113 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
114 p->range = p->total / p->divisor;
115 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
118 case PROGFN_PROGRESS:
119 if (p->phases[phase-1].exponential) {
120 while (p->phases[phase-1].n < progress) {
121 p->phases[phase-1].n++;
122 p->phases[phase-1].current *= p->phases[phase-1].param;
123 p->phases[phase-1].current /= 0x10000;
125 position = (p->phases[phase-1].startpoint +
126 p->phases[phase-1].total - p->phases[phase-1].current);
128 position = (p->phases[phase-1].startpoint +
129 progress * p->phases[phase-1].mult);
131 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
138 struct PassphraseProcStruct {
144 * Dialog-box function for the passphrase box.
146 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
147 WPARAM wParam, LPARAM lParam)
149 static char **passphrase = NULL;
150 struct PassphraseProcStruct *p;
154 SetForegroundWindow(hwnd);
155 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
156 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
161 { /* centre the window */
165 hw = GetDesktopWindow();
166 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
168 (rs.right + rs.left + rd.left - rd.right) / 2,
169 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
170 rd.right - rd.left, rd.bottom - rd.top, TRUE);
173 p = (struct PassphraseProcStruct *) lParam;
174 passphrase = p->passphrase;
176 SetDlgItemText(hwnd, 101, p->comment);
177 burnstr(*passphrase);
178 *passphrase = dupstr("");
179 SetDlgItemText(hwnd, 102, *passphrase);
182 switch (LOWORD(wParam)) {
192 case 102: /* edit box */
193 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
194 burnstr(*passphrase);
195 *passphrase = GetDlgItemText_alloc(hwnd, 102);
208 * Prompt for a key file. Assumes the filename buffer is of size
211 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
212 char *filename, int save, int ppk)
215 memset(&of, 0, sizeof(of));
218 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
219 "All Files (*.*)\0*\0\0\0";
220 of.lpstrDefExt = ".ppk";
222 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
224 of.lpstrCustomFilter = NULL;
226 of.lpstrFile = filename;
228 of.nMaxFile = FILENAME_MAX;
229 of.lpstrFileTitle = NULL;
230 of.lpstrTitle = dlgtitle;
232 return request_file(NULL, &of, FALSE, save);
236 * Dialog-box function for the Licence box.
238 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
239 WPARAM wParam, LPARAM lParam)
246 { /* centre the window */
250 hw = GetDesktopWindow();
251 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
253 (rs.right + rs.left + rd.left - rd.right) / 2,
254 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
255 rd.right - rd.left, rd.bottom - rd.top, TRUE);
258 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
261 switch (LOWORD(wParam)) {
276 * Dialog-box function for the About box.
278 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
279 WPARAM wParam, LPARAM lParam)
286 { /* centre the window */
290 hw = GetDesktopWindow();
291 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
293 (rs.right + rs.left + rd.left - rd.right) / 2,
294 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
295 rd.right - rd.left, rd.bottom - rd.top, TRUE);
299 char *text = dupprintf
300 ("Pageant\r\n\r\n%s\r\n\r\n%s",
302 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
303 SetDlgItemText(hwnd, 1000, text);
308 switch (LOWORD(wParam)) {
314 EnableWindow(hwnd, 0);
315 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
316 EnableWindow(hwnd, 1);
317 SetActiveWindow(hwnd);
328 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
331 * Thread to generate a key.
333 struct rsa_key_thread_params {
334 HWND progressbar; /* notify this with progress */
335 HWND dialog; /* notify this on completion */
336 int key_bits; /* bits in key modulus (RSA, DSA) */
337 int curve_bits; /* bits in elliptic curve (ECDSA) */
341 struct dss_key *dsskey;
342 struct ec_key *eckey;
345 static DWORD WINAPI generate_rsa_key_thread(void *param)
347 struct rsa_key_thread_params *params =
348 (struct rsa_key_thread_params *) param;
349 struct progress prog;
350 prog.progbar = params->progressbar;
352 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
354 if (params->keytype == DSA)
355 dsa_generate(params->dsskey, params->key_bits, progress_update, &prog);
356 else if (params->keytype == ECDSA)
357 ec_generate(params->eckey, params->curve_bits, progress_update, &prog);
358 else if (params->keytype == ED25519)
359 ec_edgenerate(params->eckey, 256, progress_update, &prog);
361 rsa_generate(params->key, params->key_bits, progress_update, &prog);
363 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
369 struct MainDlgState {
370 int collecting_entropy;
371 int generation_thread_exists;
373 int entropy_got, entropy_required, entropy_size;
374 int key_bits, curve_bits;
377 char **commentptr; /* points to key.comment or ssh2key.comment */
378 struct ssh2_userkey ssh2key;
382 struct dss_key dsskey;
385 HMENU filemenu, keymenu, cvtmenu;
388 static void hidemany(HWND hwnd, const int *ids, int hideit)
391 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
395 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
397 char *buffer = ssh1_pubkey_str(key);
398 SetDlgItemText(hwnd, id, buffer);
399 SetDlgItemText(hwnd, idstatic,
400 "&Public key for pasting into authorized_keys file:");
404 static void setupbigedit2(HWND hwnd, int id, int idstatic,
405 struct ssh2_userkey *key)
407 char *buffer = ssh2_pubkey_openssh_str(key);
408 SetDlgItemText(hwnd, id, buffer);
409 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
410 "OpenSSH authorized_keys file:");
415 * Warn about the obsolescent key file format.
417 void old_keyfile_warning(void)
419 static const char mbtitle[] = "PuTTY Key File Warning";
420 static const char message[] =
421 "You are loading an SSH-2 private key which has an\n"
422 "old version of the file format. This means your key\n"
423 "file is not fully tamperproof. Future versions of\n"
424 "PuTTY may stop supporting this private key format,\n"
425 "so we recommend you convert your key to the new\n"
428 "Once the key is loaded into PuTTYgen, you can perform\n"
429 "this conversion simply by saving it again.";
431 MessageBox(NULL, message, mbtitle, MB_OK);
435 controlidstart = 100,
442 IDC_PKSTATIC, IDC_KEYDISPLAY,
443 IDC_FPSTATIC, IDC_FINGERPRINT,
444 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
445 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
446 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
448 IDC_GENSTATIC, IDC_GENERATE,
449 IDC_LOADSTATIC, IDC_LOAD,
450 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
452 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
453 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
454 IDC_BITSSTATIC, IDC_BITS,
455 IDC_CURVESTATIC, IDC_CURVE,
460 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
464 static const int nokey_ids[] = { IDC_NOKEY, 0 };
465 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
466 static const int gotkey_ids[] = {
467 IDC_PKSTATIC, IDC_KEYDISPLAY,
468 IDC_FPSTATIC, IDC_FINGERPRINT,
469 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
470 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
471 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
475 * Small UI helper function to switch the state of the main dialog
476 * by enabling and disabling controls and menu items.
478 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
484 hidemany(hwnd, nokey_ids, FALSE);
485 hidemany(hwnd, generating_ids, TRUE);
486 hidemany(hwnd, gotkey_ids, TRUE);
487 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
488 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
489 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
490 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
491 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
492 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
493 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
494 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
495 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
496 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
497 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
498 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
499 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
500 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
501 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
502 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
503 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
504 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
505 MF_ENABLED|MF_BYCOMMAND);
506 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
507 MF_ENABLED|MF_BYCOMMAND);
508 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
509 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
510 MF_GRAYED|MF_BYCOMMAND);
511 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
512 MF_GRAYED|MF_BYCOMMAND);
513 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
514 MF_GRAYED|MF_BYCOMMAND);
516 case 1: /* generating key */
517 hidemany(hwnd, nokey_ids, TRUE);
518 hidemany(hwnd, generating_ids, FALSE);
519 hidemany(hwnd, gotkey_ids, TRUE);
520 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
521 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
522 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
523 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
524 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
525 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
526 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
527 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
528 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
529 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
530 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
531 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
532 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
533 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
534 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
535 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
536 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
537 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
538 MF_GRAYED|MF_BYCOMMAND);
539 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
540 MF_GRAYED|MF_BYCOMMAND);
541 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
542 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
543 MF_GRAYED|MF_BYCOMMAND);
544 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
545 MF_GRAYED|MF_BYCOMMAND);
546 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
547 MF_GRAYED|MF_BYCOMMAND);
550 hidemany(hwnd, nokey_ids, TRUE);
551 hidemany(hwnd, generating_ids, TRUE);
552 hidemany(hwnd, gotkey_ids, FALSE);
553 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
554 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
555 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
556 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
557 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
558 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
559 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
560 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
561 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
562 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
563 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
564 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
565 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
566 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
567 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
568 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
569 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
570 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
571 MF_ENABLED|MF_BYCOMMAND);
572 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
573 MF_ENABLED|MF_BYCOMMAND);
574 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
576 * Enable export menu items if and only if the key type
577 * supports this kind of export.
579 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
580 #define do_export_menuitem(x,y) \
581 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
582 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
583 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
584 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
585 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
586 #undef do_export_menuitem
592 * Helper functions to set the key type, taking care of keeping the
593 * menu and radio button selections in sync and also showing/hiding
594 * the appropriate size/curve control for the current key type.
596 void ui_update_key_type_ctrls(HWND hwnd)
598 enum { BITS, CURVE, NOTHING } which;
599 static const int bits_ids[] = {
600 IDC_BITSSTATIC, IDC_BITS, 0
602 static const int curve_ids[] = {
603 IDC_CURVESTATIC, IDC_CURVE, 0
605 static const int nothing_ids[] = {
609 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) ||
610 IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) ||
611 IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
613 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
616 /* ED25519 implicitly only supports one curve */
620 hidemany(hwnd, bits_ids, which != BITS);
621 hidemany(hwnd, curve_ids, which != CURVE);
622 hidemany(hwnd, nothing_ids, which != NOTHING);
624 void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button)
626 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ED25519, button);
627 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ED25519,
628 button, MF_BYCOMMAND);
629 ui_update_key_type_ctrls(hwnd);
632 void load_key_file(HWND hwnd, struct MainDlgState *state,
633 Filename *filename, int was_import_cmd)
639 const char *errmsg = NULL;
641 struct RSAKey newkey1;
642 struct ssh2_userkey *newkey2 = NULL;
644 type = realtype = key_type(filename);
645 if (type != SSH_KEYTYPE_SSH1 &&
646 type != SSH_KEYTYPE_SSH2 &&
647 !import_possible(type)) {
648 char *msg = dupprintf("Couldn't load private key (%s)",
649 key_type_to_str(type));
650 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
651 HELPCTXID(errors_cantloadkey));
656 if (type != SSH_KEYTYPE_SSH1 &&
657 type != SSH_KEYTYPE_SSH2) {
659 type = import_target_type(type);
664 if (realtype == SSH_KEYTYPE_SSH1)
665 needs_pass = rsakey_encrypted(filename, &comment);
666 else if (realtype == SSH_KEYTYPE_SSH2)
667 needs_pass = ssh2_userkey_encrypted(filename, &comment);
669 needs_pass = import_encrypted(filename, realtype, &comment);
676 struct PassphraseProcStruct pps;
677 pps.passphrase = &passphrase;
678 pps.comment = comment;
679 dlgret = DialogBoxParam(hinst,
680 MAKEINTRESOURCE(210),
681 NULL, PassphraseProc,
687 assert(passphrase != NULL);
689 passphrase = dupstr("");
690 if (type == SSH_KEYTYPE_SSH1) {
691 if (realtype == type)
692 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
694 ret = import_ssh1(filename, realtype, &newkey1,
695 passphrase, &errmsg);
697 if (realtype == type)
698 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
700 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
701 if (newkey2 == SSH2_WRONG_PASSPHRASE)
712 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
713 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
714 HELPCTXID(errors_cantloadkey));
716 } else if (ret == 1) {
718 * Now update the key controls with all the
722 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
724 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
726 if (type == SSH_KEYTYPE_SSH1) {
731 state->commentptr = &state->key.comment;
732 state->key = newkey1;
735 * Set the key fingerprint.
737 savecomment = state->key.comment;
738 state->key.comment = NULL;
739 rsa_fingerprint(buf, sizeof(buf),
741 state->key.comment = savecomment;
743 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
745 * Construct a decimal representation
746 * of the key, for pasting into
747 * .ssh/authorized_keys on a Unix box.
749 setupbigedit1(hwnd, IDC_KEYDISPLAY,
750 IDC_PKSTATIC, &state->key);
757 &state->ssh2key.comment;
758 state->ssh2key = *newkey2; /* structure copy */
761 savecomment = state->ssh2key.comment;
762 state->ssh2key.comment = NULL;
763 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
764 state->ssh2key.comment = savecomment;
766 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
769 setupbigedit2(hwnd, IDC_KEYDISPLAY,
770 IDC_PKSTATIC, &state->ssh2key);
772 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
776 * Finally, hide the progress bar and show
779 ui_set_state(hwnd, state, 2);
780 state->key_exists = TRUE;
783 * If the user has imported a foreign key
784 * using the Load command, let them know.
785 * If they've used the Import command, be
788 if (realtype != type && !was_import_cmd) {
790 sprintf(msg, "Successfully imported foreign key\n"
792 "To use this key with PuTTY, you need to\n"
793 "use the \"Save private key\" command to\n"
794 "save it in PuTTY's own format.",
795 key_type_to_str(realtype));
796 MessageBox(NULL, msg, "PuTTYgen Notice",
797 MB_OK | MB_ICONINFORMATION);
804 * Dialog-box function for the main PuTTYgen dialog box.
806 static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
807 WPARAM wParam, LPARAM lParam)
809 static const char generating_msg[] =
810 "Please wait while a key is generated...";
811 static const char entropy_msg[] =
812 "Please generate some randomness by moving the mouse over the blank area.";
813 struct MainDlgState *state;
818 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
819 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
823 * If we add a Help button, this is where we destroy it
824 * if the help file isn't present.
827 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
828 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
830 state = snew(struct MainDlgState);
831 state->generation_thread_exists = FALSE;
832 state->collecting_entropy = FALSE;
833 state->entropy = NULL;
834 state->key_exists = FALSE;
835 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
841 menu1 = CreateMenu();
842 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
843 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
844 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
845 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
846 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
847 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
848 state->filemenu = menu1;
850 menu1 = CreateMenu();
851 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
852 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
853 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
854 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
855 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
856 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
857 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
858 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
859 state->keymenu = menu1;
861 menu1 = CreateMenu();
862 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
863 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
864 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
865 "Export &OpenSSH key");
866 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
867 "Export &OpenSSH key (force new file format)");
868 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
869 "Export &ssh.com key");
870 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
872 state->cvtmenu = menu1;
874 menu1 = CreateMenu();
875 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
877 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
878 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
886 { /* centre the window */
890 hw = GetDesktopWindow();
891 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
893 (rs.right + rs.left + rd.left - rd.right) / 2,
894 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
895 rd.right - rd.left, rd.bottom - rd.top, TRUE);
899 struct ctlpos cp, cp2;
902 /* Accelerators used: acglops1rbvde */
904 ctlposinit(&cp, hwnd, 4, 4, 4);
905 beginbox(&cp, "Key", IDC_BOX_KEY);
907 statictext(&cp2, "No key.", 1, IDC_NOKEY);
909 statictext(&cp2, "", 1, IDC_GENERATING);
910 progressbar(&cp2, IDC_PROGRESS);
912 "&Public key for pasting into authorized_keys file:",
913 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
914 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
915 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
916 IDC_FINGERPRINT, 75);
917 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
919 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
920 IDC_COMMENTEDIT, 75);
921 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
922 IDC_PASSPHRASE1EDIT, 75);
923 staticpassedit(&cp, "C&onfirm passphrase:",
924 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
926 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
927 staticbtn(&cp, "Generate a public/private key pair",
928 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
929 staticbtn(&cp, "Load an existing private key file",
930 IDC_LOADSTATIC, "&Load", IDC_LOAD);
931 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
932 "Save p&ublic key", IDC_SAVEPUB,
933 "&Save private key", IDC_SAVE);
935 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
936 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
937 "&RSA", IDC_KEYSSH2RSA,
938 "&DSA", IDC_KEYSSH2DSA,
939 "&ECDSA", IDC_KEYSSH2ECDSA,
940 "ED&25519", IDC_KEYSSH2ED25519,
941 "SSH-&1 (RSA)", IDC_KEYSSH1,
944 staticedit(&cp2, "Number of &bits in a generated key:",
945 IDC_BITSSTATIC, IDC_BITS, 20);
948 staticddl(&cp2, "Cur&ve to use for generating this key:",
949 IDC_CURVESTATIC, IDC_CURVE, 20);
950 SendDlgItemMessage(hwnd, IDC_CURVE, CB_RESETCONTENT, 0, 0);
953 const struct ec_curve *curve;
954 const struct ssh_signkey *alg;
956 for (i = 0; i < n_ec_nist_curve_lengths; i++) {
957 bits = ec_nist_curve_lengths[i];
958 ec_nist_alg_and_curve_by_bits(bits, &curve, &alg);
959 SendDlgItemMessage(hwnd, IDC_CURVE, CB_ADDSTRING, 0,
960 (LPARAM)curve->textname);
963 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
965 statictext(&cp2, "(nothing to configure for this key type)",
966 1, IDC_NOTHINGSTATIC);
967 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
971 ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA);
972 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
973 SendDlgItemMessage(hwnd, IDC_CURVE, CB_SETCURSEL,
974 DEFAULT_CURVE_INDEX, 0);
977 * Initially, hide the progress bar and the key display,
978 * and show the no-key display. Also disable the Save
979 * buttons, because with no key we obviously can't save
982 ui_set_state(hwnd, state, 0);
985 * Load a key file if one was provided on the command line.
987 if (cmdline_keyfile) {
988 Filename *fn = filename_from_str(cmdline_keyfile);
989 load_key_file(hwnd, state, fn, 0);
995 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
996 if (state->collecting_entropy &&
997 state->entropy && state->entropy_got < state->entropy_required) {
998 state->entropy[state->entropy_got++] = lParam;
999 state->entropy[state->entropy_got++] = GetMessageTime();
1000 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1001 state->entropy_got, 0);
1002 if (state->entropy_got >= state->entropy_required) {
1003 struct rsa_key_thread_params *params;
1007 * Seed the entropy pool
1009 random_add_heavynoise(state->entropy, state->entropy_size);
1010 smemclr(state->entropy, state->entropy_size);
1011 sfree(state->entropy);
1012 state->collecting_entropy = FALSE;
1014 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1015 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1016 MAKELPARAM(0, PROGRESSRANGE));
1017 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1019 params = snew(struct rsa_key_thread_params);
1020 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1021 params->dialog = hwnd;
1022 params->key_bits = state->key_bits;
1023 params->curve_bits = state->curve_bits;
1024 params->keytype = state->keytype;
1025 params->key = &state->key;
1026 params->dsskey = &state->dsskey;
1028 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
1029 params, 0, &threadid)) {
1030 MessageBox(hwnd, "Out of thread resources",
1031 "Key generation error",
1032 MB_OK | MB_ICONERROR);
1035 state->generation_thread_exists = TRUE;
1041 switch (LOWORD(wParam)) {
1043 case IDC_KEYSSH2RSA:
1044 case IDC_KEYSSH2DSA:
1045 case IDC_KEYSSH2ECDSA:
1046 case IDC_KEYSSH2ED25519:
1048 state = (struct MainDlgState *)
1049 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1050 ui_set_key_type(hwnd, state, LOWORD(wParam));
1054 PostMessage(hwnd, WM_CLOSE, 0, 0);
1056 case IDC_COMMENTEDIT:
1057 if (HIWORD(wParam) == EN_CHANGE) {
1058 state = (struct MainDlgState *)
1059 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1060 if (state->key_exists) {
1061 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1062 int len = GetWindowTextLength(editctl);
1063 if (*state->commentptr)
1064 sfree(*state->commentptr);
1065 *state->commentptr = snewn(len + 1, char);
1066 GetWindowText(editctl, *state->commentptr, len + 1);
1068 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1071 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1078 EnableWindow(hwnd, 0);
1079 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1080 EnableWindow(hwnd, 1);
1081 SetActiveWindow(hwnd);
1084 if (HIWORD(wParam) == BN_CLICKED ||
1085 HIWORD(wParam) == BN_DOUBLECLICKED) {
1086 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1090 if (HIWORD(wParam) != BN_CLICKED &&
1091 HIWORD(wParam) != BN_DOUBLECLICKED)
1094 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1095 if (!state->generation_thread_exists) {
1097 state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1099 state->key_bits = DEFAULT_KEY_BITS;
1101 int curveindex = SendDlgItemMessage(hwnd, IDC_CURVE,
1102 CB_GETCURSEL, 0, 0);
1103 assert(curveindex >= 0);
1104 assert(curveindex < n_ec_nist_curve_lengths);
1105 state->curve_bits = ec_nist_curve_lengths[curveindex];
1107 /* If we ever introduce a new key type, check it here! */
1108 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1109 state->keytype = RSA;
1110 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1111 state->keytype = DSA;
1112 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1113 state->keytype = ECDSA;
1114 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1115 state->keytype = ED25519;
1117 if ((state->keytype == RSA || state->keytype == DSA) &&
1118 state->key_bits < 256) {
1119 char *message = dupprintf
1120 ("PuTTYgen will not generate a key smaller than 256"
1121 " bits.\nKey length reset to default %d. Continue?",
1123 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1124 MB_ICONWARNING | MB_OKCANCEL);
1128 state->key_bits = DEFAULT_KEY_BITS;
1129 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
1131 ui_set_state(hwnd, state, 1);
1132 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1133 state->key_exists = FALSE;
1134 state->collecting_entropy = TRUE;
1137 * My brief statistical tests on mouse movements
1138 * suggest that there are about 2.5 bits of
1139 * randomness in the x position, 2.5 in the y
1140 * position, and 1.7 in the message time, making
1141 * 5.7 bits of unpredictability per mouse movement.
1142 * However, other people have told me it's far less
1143 * than that, so I'm going to be stupidly cautious
1144 * and knock that down to a nice round 2. With this
1145 * method, we require two words per mouse movement,
1146 * so with 2 bits per mouse movement we expect 2
1147 * bits every 2 words.
1149 if (state->keytype == RSA || state->keytype == DSA)
1150 state->entropy_required = (state->key_bits / 2) * 2;
1151 else if (state->keytype == ECDSA)
1152 state->entropy_required = (state->curve_bits / 2) * 2;
1154 state->entropy_required = 256;
1156 state->entropy_got = 0;
1157 state->entropy_size = (state->entropy_required *
1159 state->entropy = snewn(state->entropy_required, unsigned);
1161 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1162 MAKELPARAM(0, state->entropy_required));
1163 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1167 case IDC_EXPORT_OPENSSH_AUTO:
1168 case IDC_EXPORT_OPENSSH_NEW:
1169 case IDC_EXPORT_SSHCOM:
1170 if (HIWORD(wParam) != BN_CLICKED)
1173 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1174 if (state->key_exists) {
1175 char filename[FILENAME_MAX];
1176 char *passphrase, *passphrase2;
1180 realtype = SSH_KEYTYPE_SSH2;
1182 realtype = SSH_KEYTYPE_SSH1;
1184 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1185 type = SSH_KEYTYPE_OPENSSH_AUTO;
1186 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1187 type = SSH_KEYTYPE_OPENSSH_NEW;
1188 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1189 type = SSH_KEYTYPE_SSHCOM;
1193 if (type != realtype &&
1194 import_target_type(type) != realtype) {
1196 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1197 " format", (state->ssh2 ? 2 : 1),
1198 (state->ssh2 ? 1 : 2));
1199 MessageBox(hwnd, msg,
1200 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1204 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1205 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1206 if (strcmp(passphrase, passphrase2)) {
1208 "The two passphrases given do not match.",
1209 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1210 burnstr(passphrase);
1211 burnstr(passphrase2);
1214 burnstr(passphrase2);
1217 ret = MessageBox(hwnd,
1218 "Are you sure you want to save this key\n"
1219 "without a passphrase to protect it?",
1221 MB_YESNO | MB_ICONWARNING);
1223 burnstr(passphrase);
1227 if (prompt_keyfile(hwnd, "Save private key as:",
1228 filename, 1, (type == realtype))) {
1230 FILE *fp = fopen(filename, "r");
1234 buffer = dupprintf("Overwrite existing file\n%s?",
1236 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1237 MB_YESNO | MB_ICONWARNING);
1240 burnstr(passphrase);
1246 Filename *fn = filename_from_str(filename);
1247 if (type != realtype)
1248 ret = export_ssh2(fn, type, &state->ssh2key,
1249 *passphrase ? passphrase : NULL);
1251 ret = ssh2_save_userkey(fn, &state->ssh2key,
1252 *passphrase ? passphrase :
1256 Filename *fn = filename_from_str(filename);
1257 if (type != realtype)
1258 ret = export_ssh1(fn, type, &state->key,
1259 *passphrase ? passphrase : NULL);
1261 ret = saversakey(fn, &state->key,
1262 *passphrase ? passphrase : NULL);
1266 MessageBox(hwnd, "Unable to save key file",
1267 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1270 burnstr(passphrase);
1274 if (HIWORD(wParam) != BN_CLICKED)
1277 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1278 if (state->key_exists) {
1279 char filename[FILENAME_MAX];
1280 if (prompt_keyfile(hwnd, "Save public key as:",
1283 FILE *fp = fopen(filename, "r");
1287 buffer = dupprintf("Overwrite existing file\n%s?",
1289 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1290 MB_YESNO | MB_ICONWARNING);
1295 fp = fopen(filename, "w");
1297 MessageBox(hwnd, "Unable to open key file",
1298 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1302 unsigned char *blob;
1303 blob = state->ssh2key.alg->public_blob
1304 (state->ssh2key.data, &bloblen);
1305 ssh2_write_pubkey(fp, state->ssh2key.comment,
1307 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1309 ssh1_write_pubkey(fp, &state->key);
1311 if (fclose(fp) < 0) {
1312 MessageBox(hwnd, "Unable to save key file",
1313 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1321 if (HIWORD(wParam) != BN_CLICKED)
1324 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1325 if (!state->generation_thread_exists) {
1326 char filename[FILENAME_MAX];
1327 if (prompt_keyfile(hwnd, "Load private key:",
1328 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1329 Filename *fn = filename_from_str(filename);
1330 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1338 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1339 state->generation_thread_exists = FALSE;
1340 state->key_exists = TRUE;
1341 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1342 MAKELPARAM(0, PROGRESSRANGE));
1343 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1345 if (state->keytype == DSA) {
1346 state->ssh2key.data = &state->dsskey;
1347 state->ssh2key.alg = &ssh_dss;
1348 } else if (state->keytype == ECDSA) {
1349 state->ssh2key.data = &state->eckey;
1350 state->ssh2key.alg = state->eckey.signalg;
1351 } else if (state->keytype == ED25519) {
1352 state->ssh2key.data = &state->eckey;
1353 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1355 state->ssh2key.data = &state->key;
1356 state->ssh2key.alg = &ssh_rsa;
1358 state->commentptr = &state->ssh2key.comment;
1360 state->commentptr = &state->key.comment;
1363 * Invent a comment for the key. We'll do this by including
1364 * the date in it. This will be so horrifyingly ugly that
1365 * the user will immediately want to change it, which is
1368 *state->commentptr = snewn(30, char);
1372 if (state->keytype == DSA)
1373 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1374 else if (state->keytype == ECDSA)
1375 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1376 else if (state->keytype == ED25519)
1377 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1379 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1383 * Now update the key controls with all the key data.
1388 * Blank passphrase, initially. This isn't dangerous,
1389 * because we will warn (Are You Sure?) before allowing
1390 * the user to save an unprotected private key.
1392 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1393 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1397 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1399 * Set the key fingerprint.
1401 savecomment = *state->commentptr;
1402 *state->commentptr = NULL;
1405 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1406 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1410 rsa_fingerprint(buf, sizeof(buf), &state->key);
1411 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1413 *state->commentptr = savecomment;
1415 * Construct a decimal representation of the key, for
1416 * pasting into .ssh/authorized_keys or
1417 * .ssh/authorized_keys2 on a Unix box.
1420 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1421 IDC_PKSTATIC, &state->ssh2key);
1423 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1424 IDC_PKSTATIC, &state->key);
1428 * Finally, hide the progress bar and show the key data.
1430 ui_set_state(hwnd, state, 2);
1434 int id = ((LPHELPINFO)lParam)->iCtrlId;
1435 const char *topic = NULL;
1437 case IDC_GENERATING:
1441 topic = WINHELP_CTX_puttygen_generate; break;
1443 case IDC_KEYDISPLAY:
1444 topic = WINHELP_CTX_puttygen_pastekey; break;
1446 case IDC_FINGERPRINT:
1447 topic = WINHELP_CTX_puttygen_fingerprint; break;
1448 case IDC_COMMENTSTATIC:
1449 case IDC_COMMENTEDIT:
1450 topic = WINHELP_CTX_puttygen_comment; break;
1451 case IDC_PASSPHRASE1STATIC:
1452 case IDC_PASSPHRASE1EDIT:
1453 case IDC_PASSPHRASE2STATIC:
1454 case IDC_PASSPHRASE2EDIT:
1455 topic = WINHELP_CTX_puttygen_passphrase; break;
1456 case IDC_LOADSTATIC:
1458 topic = WINHELP_CTX_puttygen_load; break;
1459 case IDC_SAVESTATIC:
1461 topic = WINHELP_CTX_puttygen_savepriv; break;
1463 topic = WINHELP_CTX_puttygen_savepub; break;
1464 case IDC_TYPESTATIC:
1466 case IDC_KEYSSH2RSA:
1467 case IDC_KEYSSH2DSA:
1468 case IDC_KEYSSH2ECDSA:
1469 case IDC_KEYSSH2ED25519:
1470 topic = WINHELP_CTX_puttygen_keytype; break;
1471 case IDC_BITSSTATIC:
1473 topic = WINHELP_CTX_puttygen_bits; break;
1475 case IDC_EXPORT_OPENSSH_AUTO:
1476 case IDC_EXPORT_OPENSSH_NEW:
1477 case IDC_EXPORT_SSHCOM:
1478 topic = WINHELP_CTX_puttygen_conversions; break;
1481 launch_help(hwnd, topic);
1488 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1497 void cleanup_exit(int code)
1503 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1509 InitCommonControls();
1514 * See if we can find our Help file.
1518 split_into_argv(cmdline, &argc, &argv, NULL);
1521 if (!strcmp(argv[0], "-pgpfp")) {
1526 * Assume the first argument to be a private key file, and
1527 * attempt to load it.
1529 cmdline_keyfile = argv[0];
1534 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1537 return ret; /* just in case optimiser complains */