2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
23 #define WM_DONEKEY (WM_APP + 1)
25 #define DEFAULT_KEY_BITS 2048
26 #define DEFAULT_CURVE_INDEX 0
28 static char *cmdline_keyfile = NULL;
31 * Print a modal (Really Bad) message box and perform a fatal exit.
33 void modalfatalbox(const char *fmt, ...)
39 stuff = dupvprintf(fmt, ap);
41 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
42 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
48 * Print a non-fatal message box and do not exit.
50 void nonfatal(const char *fmt, ...)
56 stuff = dupvprintf(fmt, ap);
58 MessageBox(NULL, stuff, "PuTTYgen Error",
59 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
63 /* ----------------------------------------------------------------------
64 * Progress report code. This is really horrible :-)
66 #define PROGRESSRANGE 65535
72 unsigned startpoint, total;
73 unsigned param, current, n; /* if exponential */
74 unsigned mult; /* if linear */
76 unsigned total, divisor, range;
80 static void progress_update(void *param, int action, int phase, int iprogress)
82 struct progress *p = (struct progress *) param;
83 unsigned progress = iprogress;
86 if (action < PROGFN_READY && p->nphases < phase)
89 case PROGFN_INITIALISE:
92 case PROGFN_LIN_PHASE:
93 p->phases[phase-1].exponential = 0;
94 p->phases[phase-1].mult = p->phases[phase].total / progress;
96 case PROGFN_EXP_PHASE:
97 p->phases[phase-1].exponential = 1;
98 p->phases[phase-1].param = 0x10000 + progress;
99 p->phases[phase-1].current = p->phases[phase-1].total;
100 p->phases[phase-1].n = 0;
102 case PROGFN_PHASE_EXTENT:
103 p->phases[phase-1].total = progress;
109 for (i = 0; i < p->nphases; i++) {
110 p->phases[i].startpoint = total;
111 total += p->phases[i].total;
114 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
115 p->range = p->total / p->divisor;
116 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
119 case PROGFN_PROGRESS:
120 if (p->phases[phase-1].exponential) {
121 while (p->phases[phase-1].n < progress) {
122 p->phases[phase-1].n++;
123 p->phases[phase-1].current *= p->phases[phase-1].param;
124 p->phases[phase-1].current /= 0x10000;
126 position = (p->phases[phase-1].startpoint +
127 p->phases[phase-1].total - p->phases[phase-1].current);
129 position = (p->phases[phase-1].startpoint +
130 progress * p->phases[phase-1].mult);
132 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
137 extern const char ver[];
139 struct PassphraseProcStruct {
145 * Dialog-box function for the passphrase box.
147 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
148 WPARAM wParam, LPARAM lParam)
150 static char **passphrase = NULL;
151 struct PassphraseProcStruct *p;
155 SetForegroundWindow(hwnd);
156 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
157 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
162 { /* centre the window */
166 hw = GetDesktopWindow();
167 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
169 (rs.right + rs.left + rd.left - rd.right) / 2,
170 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
171 rd.right - rd.left, rd.bottom - rd.top, TRUE);
174 p = (struct PassphraseProcStruct *) lParam;
175 passphrase = p->passphrase;
177 SetDlgItemText(hwnd, 101, p->comment);
178 burnstr(*passphrase);
179 *passphrase = dupstr("");
180 SetDlgItemText(hwnd, 102, *passphrase);
183 switch (LOWORD(wParam)) {
193 case 102: /* edit box */
194 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
195 burnstr(*passphrase);
196 *passphrase = GetDlgItemText_alloc(hwnd, 102);
209 * Prompt for a key file. Assumes the filename buffer is of size
212 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
213 char *filename, int save, int ppk)
216 memset(&of, 0, sizeof(of));
219 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
220 "All Files (*.*)\0*\0\0\0";
221 of.lpstrDefExt = ".ppk";
223 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
225 of.lpstrCustomFilter = NULL;
227 of.lpstrFile = filename;
229 of.nMaxFile = FILENAME_MAX;
230 of.lpstrFileTitle = NULL;
231 of.lpstrTitle = dlgtitle;
233 return request_file(NULL, &of, FALSE, save);
237 * Dialog-box function for the Licence box.
239 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
240 WPARAM wParam, LPARAM lParam)
247 { /* centre the window */
251 hw = GetDesktopWindow();
252 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
254 (rs.right + rs.left + rd.left - rd.right) / 2,
255 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
256 rd.right - rd.left, rd.bottom - rd.top, TRUE);
259 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
262 switch (LOWORD(wParam)) {
277 * Dialog-box function for the About box.
279 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
280 WPARAM wParam, LPARAM lParam)
287 { /* centre the window */
291 hw = GetDesktopWindow();
292 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
294 (rs.right + rs.left + rd.left - rd.right) / 2,
295 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
296 rd.right - rd.left, rd.bottom - rd.top, TRUE);
300 char *buildinfo_text = buildinfo("\r\n");
301 char *text = dupprintf
302 ("PuTTYgen\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s",
304 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
305 sfree(buildinfo_text);
306 SetDlgItemText(hwnd, 1000, text);
311 switch (LOWORD(wParam)) {
317 EnableWindow(hwnd, 0);
318 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
319 EnableWindow(hwnd, 1);
320 SetActiveWindow(hwnd);
331 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
334 * Thread to generate a key.
336 struct rsa_key_thread_params {
337 HWND progressbar; /* notify this with progress */
338 HWND dialog; /* notify this on completion */
339 int key_bits; /* bits in key modulus (RSA, DSA) */
340 int curve_bits; /* bits in elliptic curve (ECDSA) */
344 struct dss_key *dsskey;
345 struct ec_key *eckey;
348 static DWORD WINAPI generate_key_thread(void *param)
350 struct rsa_key_thread_params *params =
351 (struct rsa_key_thread_params *) param;
352 struct progress prog;
353 prog.progbar = params->progressbar;
355 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
357 if (params->keytype == DSA)
358 dsa_generate(params->dsskey, params->key_bits, progress_update, &prog);
359 else if (params->keytype == ECDSA)
360 ec_generate(params->eckey, params->curve_bits, progress_update, &prog);
361 else if (params->keytype == ED25519)
362 ec_edgenerate(params->eckey, 256, progress_update, &prog);
364 rsa_generate(params->key, params->key_bits, progress_update, &prog);
366 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
372 struct MainDlgState {
373 int collecting_entropy;
374 int generation_thread_exists;
376 int entropy_got, entropy_required, entropy_size;
377 int key_bits, curve_bits;
380 char **commentptr; /* points to key.comment or ssh2key.comment */
381 struct ssh2_userkey ssh2key;
385 struct dss_key dsskey;
388 HMENU filemenu, keymenu, cvtmenu;
391 static void hidemany(HWND hwnd, const int *ids, int hideit)
394 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
398 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
400 char *buffer = ssh1_pubkey_str(key);
401 SetDlgItemText(hwnd, id, buffer);
402 SetDlgItemText(hwnd, idstatic,
403 "&Public key for pasting into authorized_keys file:");
407 static void setupbigedit2(HWND hwnd, int id, int idstatic,
408 struct ssh2_userkey *key)
410 char *buffer = ssh2_pubkey_openssh_str(key);
411 SetDlgItemText(hwnd, id, buffer);
412 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
413 "OpenSSH authorized_keys file:");
418 * Warn about the obsolescent key file format.
420 void old_keyfile_warning(void)
422 static const char mbtitle[] = "PuTTY Key File Warning";
423 static const char message[] =
424 "You are loading an SSH-2 private key which has an\n"
425 "old version of the file format. This means your key\n"
426 "file is not fully tamperproof. Future versions of\n"
427 "PuTTY may stop supporting this private key format,\n"
428 "so we recommend you convert your key to the new\n"
431 "Once the key is loaded into PuTTYgen, you can perform\n"
432 "this conversion simply by saving it again.";
434 MessageBox(NULL, message, mbtitle, MB_OK);
438 controlidstart = 100,
445 IDC_PKSTATIC, IDC_KEYDISPLAY,
446 IDC_FPSTATIC, IDC_FINGERPRINT,
447 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
448 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
449 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
451 IDC_GENSTATIC, IDC_GENERATE,
452 IDC_LOADSTATIC, IDC_LOAD,
453 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
455 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
456 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
457 IDC_BITSSTATIC, IDC_BITS,
458 IDC_CURVESTATIC, IDC_CURVE,
463 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
467 static const int nokey_ids[] = { IDC_NOKEY, 0 };
468 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
469 static const int gotkey_ids[] = {
470 IDC_PKSTATIC, IDC_KEYDISPLAY,
471 IDC_FPSTATIC, IDC_FINGERPRINT,
472 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
473 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
474 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
478 * Small UI helper function to switch the state of the main dialog
479 * by enabling and disabling controls and menu items.
481 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
487 hidemany(hwnd, nokey_ids, FALSE);
488 hidemany(hwnd, generating_ids, TRUE);
489 hidemany(hwnd, gotkey_ids, TRUE);
490 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
491 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
492 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
493 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
494 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
495 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
496 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
497 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
498 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
499 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
500 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
501 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
502 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
503 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
504 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
505 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
506 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
507 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
508 MF_ENABLED|MF_BYCOMMAND);
509 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
510 MF_ENABLED|MF_BYCOMMAND);
511 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
512 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
513 MF_GRAYED|MF_BYCOMMAND);
514 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
515 MF_GRAYED|MF_BYCOMMAND);
516 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
517 MF_GRAYED|MF_BYCOMMAND);
519 case 1: /* generating key */
520 hidemany(hwnd, nokey_ids, TRUE);
521 hidemany(hwnd, generating_ids, FALSE);
522 hidemany(hwnd, gotkey_ids, TRUE);
523 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
524 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
525 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
526 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
527 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
528 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
529 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
530 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
531 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
532 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
533 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
534 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
535 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
536 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
537 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
538 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
539 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
540 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
541 MF_GRAYED|MF_BYCOMMAND);
542 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
543 MF_GRAYED|MF_BYCOMMAND);
544 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
545 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
546 MF_GRAYED|MF_BYCOMMAND);
547 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
548 MF_GRAYED|MF_BYCOMMAND);
549 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
550 MF_GRAYED|MF_BYCOMMAND);
553 hidemany(hwnd, nokey_ids, TRUE);
554 hidemany(hwnd, generating_ids, TRUE);
555 hidemany(hwnd, gotkey_ids, FALSE);
556 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
557 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
558 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
559 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
560 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
561 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
562 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
563 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
564 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
565 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
566 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
567 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
568 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
569 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
570 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
571 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
572 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
573 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
574 MF_ENABLED|MF_BYCOMMAND);
575 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
576 MF_ENABLED|MF_BYCOMMAND);
577 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
579 * Enable export menu items if and only if the key type
580 * supports this kind of export.
582 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
583 #define do_export_menuitem(x,y) \
584 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
585 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
586 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
587 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
588 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
589 #undef do_export_menuitem
595 * Helper functions to set the key type, taking care of keeping the
596 * menu and radio button selections in sync and also showing/hiding
597 * the appropriate size/curve control for the current key type.
599 void ui_update_key_type_ctrls(HWND hwnd)
601 enum { BITS, CURVE, NOTHING } which;
602 static const int bits_ids[] = {
603 IDC_BITSSTATIC, IDC_BITS, 0
605 static const int curve_ids[] = {
606 IDC_CURVESTATIC, IDC_CURVE, 0
608 static const int nothing_ids[] = {
612 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) ||
613 IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) ||
614 IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
616 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
619 /* ED25519 implicitly only supports one curve */
623 hidemany(hwnd, bits_ids, which != BITS);
624 hidemany(hwnd, curve_ids, which != CURVE);
625 hidemany(hwnd, nothing_ids, which != NOTHING);
627 void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button)
629 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ED25519, button);
630 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ED25519,
631 button, MF_BYCOMMAND);
632 ui_update_key_type_ctrls(hwnd);
635 void load_key_file(HWND hwnd, struct MainDlgState *state,
636 Filename *filename, int was_import_cmd)
642 const char *errmsg = NULL;
644 struct RSAKey newkey1;
645 struct ssh2_userkey *newkey2 = NULL;
647 type = realtype = key_type(filename);
648 if (type != SSH_KEYTYPE_SSH1 &&
649 type != SSH_KEYTYPE_SSH2 &&
650 !import_possible(type)) {
651 char *msg = dupprintf("Couldn't load private key (%s)",
652 key_type_to_str(type));
653 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
654 HELPCTXID(errors_cantloadkey));
659 if (type != SSH_KEYTYPE_SSH1 &&
660 type != SSH_KEYTYPE_SSH2) {
662 type = import_target_type(type);
667 if (realtype == SSH_KEYTYPE_SSH1)
668 needs_pass = rsakey_encrypted(filename, &comment);
669 else if (realtype == SSH_KEYTYPE_SSH2)
670 needs_pass = ssh2_userkey_encrypted(filename, &comment);
672 needs_pass = import_encrypted(filename, realtype, &comment);
679 struct PassphraseProcStruct pps;
680 pps.passphrase = &passphrase;
681 pps.comment = comment;
682 dlgret = DialogBoxParam(hinst,
683 MAKEINTRESOURCE(210),
684 NULL, PassphraseProc,
690 assert(passphrase != NULL);
692 passphrase = dupstr("");
693 if (type == SSH_KEYTYPE_SSH1) {
694 if (realtype == type)
695 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
697 ret = import_ssh1(filename, realtype, &newkey1,
698 passphrase, &errmsg);
700 if (realtype == type)
701 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
703 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
704 if (newkey2 == SSH2_WRONG_PASSPHRASE)
715 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
716 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
717 HELPCTXID(errors_cantloadkey));
719 } else if (ret == 1) {
721 * Now update the key controls with all the
725 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
727 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
729 if (type == SSH_KEYTYPE_SSH1) {
734 state->commentptr = &state->key.comment;
735 state->key = newkey1;
738 * Set the key fingerprint.
740 savecomment = state->key.comment;
741 state->key.comment = NULL;
742 rsa_fingerprint(buf, sizeof(buf),
744 state->key.comment = savecomment;
746 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
748 * Construct a decimal representation
749 * of the key, for pasting into
750 * .ssh/authorized_keys on a Unix box.
752 setupbigedit1(hwnd, IDC_KEYDISPLAY,
753 IDC_PKSTATIC, &state->key);
760 &state->ssh2key.comment;
761 state->ssh2key = *newkey2; /* structure copy */
764 savecomment = state->ssh2key.comment;
765 state->ssh2key.comment = NULL;
766 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
767 state->ssh2key.comment = savecomment;
769 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
772 setupbigedit2(hwnd, IDC_KEYDISPLAY,
773 IDC_PKSTATIC, &state->ssh2key);
775 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
779 * Finally, hide the progress bar and show
782 ui_set_state(hwnd, state, 2);
783 state->key_exists = TRUE;
786 * If the user has imported a foreign key
787 * using the Load command, let them know.
788 * If they've used the Import command, be
791 if (realtype != type && !was_import_cmd) {
793 sprintf(msg, "Successfully imported foreign key\n"
795 "To use this key with PuTTY, you need to\n"
796 "use the \"Save private key\" command to\n"
797 "save it in PuTTY's own format.",
798 key_type_to_str(realtype));
799 MessageBox(NULL, msg, "PuTTYgen Notice",
800 MB_OK | MB_ICONINFORMATION);
807 * Dialog-box function for the main PuTTYgen dialog box.
809 static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
810 WPARAM wParam, LPARAM lParam)
812 static const char generating_msg[] =
813 "Please wait while a key is generated...";
814 static const char entropy_msg[] =
815 "Please generate some randomness by moving the mouse over the blank area.";
816 struct MainDlgState *state;
821 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
822 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
826 * If we add a Help button, this is where we destroy it
827 * if the help file isn't present.
830 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
831 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
833 state = snew(struct MainDlgState);
834 state->generation_thread_exists = FALSE;
835 state->collecting_entropy = FALSE;
836 state->entropy = NULL;
837 state->key_exists = FALSE;
838 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
844 menu1 = CreateMenu();
845 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
846 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
847 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
848 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
849 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
850 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
851 state->filemenu = menu1;
853 menu1 = CreateMenu();
854 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
855 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
856 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
857 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
858 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
859 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
860 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
861 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
862 state->keymenu = menu1;
864 menu1 = CreateMenu();
865 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
866 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
867 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
868 "Export &OpenSSH key");
869 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
870 "Export &OpenSSH key (force new file format)");
871 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
872 "Export &ssh.com key");
873 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
875 state->cvtmenu = menu1;
877 menu1 = CreateMenu();
878 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
880 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
881 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
889 { /* centre the window */
893 hw = GetDesktopWindow();
894 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
896 (rs.right + rs.left + rd.left - rd.right) / 2,
897 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
898 rd.right - rd.left, rd.bottom - rd.top, TRUE);
902 struct ctlpos cp, cp2;
905 /* Accelerators used: acglops1rbvde */
907 ctlposinit(&cp, hwnd, 4, 4, 4);
908 beginbox(&cp, "Key", IDC_BOX_KEY);
910 statictext(&cp2, "No key.", 1, IDC_NOKEY);
912 statictext(&cp2, "", 1, IDC_GENERATING);
913 progressbar(&cp2, IDC_PROGRESS);
915 "&Public key for pasting into authorized_keys file:",
916 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
917 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
918 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
919 IDC_FINGERPRINT, 75);
920 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
922 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
923 IDC_COMMENTEDIT, 75);
924 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
925 IDC_PASSPHRASE1EDIT, 75);
926 staticpassedit(&cp, "C&onfirm passphrase:",
927 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
929 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
930 staticbtn(&cp, "Generate a public/private key pair",
931 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
932 staticbtn(&cp, "Load an existing private key file",
933 IDC_LOADSTATIC, "&Load", IDC_LOAD);
934 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
935 "Save p&ublic key", IDC_SAVEPUB,
936 "&Save private key", IDC_SAVE);
938 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
939 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
940 "&RSA", IDC_KEYSSH2RSA,
941 "&DSA", IDC_KEYSSH2DSA,
942 "&ECDSA", IDC_KEYSSH2ECDSA,
943 "ED&25519", IDC_KEYSSH2ED25519,
944 "SSH-&1 (RSA)", IDC_KEYSSH1,
947 staticedit(&cp2, "Number of &bits in a generated key:",
948 IDC_BITSSTATIC, IDC_BITS, 20);
951 staticddl(&cp2, "Cur&ve to use for generating this key:",
952 IDC_CURVESTATIC, IDC_CURVE, 20);
953 SendDlgItemMessage(hwnd, IDC_CURVE, CB_RESETCONTENT, 0, 0);
956 const struct ec_curve *curve;
957 const struct ssh_signkey *alg;
959 for (i = 0; i < n_ec_nist_curve_lengths; i++) {
960 bits = ec_nist_curve_lengths[i];
961 ec_nist_alg_and_curve_by_bits(bits, &curve, &alg);
962 SendDlgItemMessage(hwnd, IDC_CURVE, CB_ADDSTRING, 0,
963 (LPARAM)curve->textname);
966 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
968 statictext(&cp2, "(nothing to configure for this key type)",
969 1, IDC_NOTHINGSTATIC);
970 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
974 ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA);
975 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
976 SendDlgItemMessage(hwnd, IDC_CURVE, CB_SETCURSEL,
977 DEFAULT_CURVE_INDEX, 0);
980 * Initially, hide the progress bar and the key display,
981 * and show the no-key display. Also disable the Save
982 * buttons, because with no key we obviously can't save
985 ui_set_state(hwnd, state, 0);
988 * Load a key file if one was provided on the command line.
990 if (cmdline_keyfile) {
991 Filename *fn = filename_from_str(cmdline_keyfile);
992 load_key_file(hwnd, state, fn, 0);
998 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
999 if (state->collecting_entropy &&
1000 state->entropy && state->entropy_got < state->entropy_required) {
1001 state->entropy[state->entropy_got++] = lParam;
1002 state->entropy[state->entropy_got++] = GetMessageTime();
1003 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1004 state->entropy_got, 0);
1005 if (state->entropy_got >= state->entropy_required) {
1006 struct rsa_key_thread_params *params;
1010 * Seed the entropy pool
1012 random_add_heavynoise(state->entropy, state->entropy_size);
1013 smemclr(state->entropy, state->entropy_size);
1014 sfree(state->entropy);
1015 state->collecting_entropy = FALSE;
1017 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1018 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1019 MAKELPARAM(0, PROGRESSRANGE));
1020 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1022 params = snew(struct rsa_key_thread_params);
1023 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1024 params->dialog = hwnd;
1025 params->key_bits = state->key_bits;
1026 params->curve_bits = state->curve_bits;
1027 params->keytype = state->keytype;
1028 params->key = &state->key;
1029 params->dsskey = &state->dsskey;
1031 if (!CreateThread(NULL, 0, generate_key_thread,
1032 params, 0, &threadid)) {
1033 MessageBox(hwnd, "Out of thread resources",
1034 "Key generation error",
1035 MB_OK | MB_ICONERROR);
1038 state->generation_thread_exists = TRUE;
1044 switch (LOWORD(wParam)) {
1046 case IDC_KEYSSH2RSA:
1047 case IDC_KEYSSH2DSA:
1048 case IDC_KEYSSH2ECDSA:
1049 case IDC_KEYSSH2ED25519:
1051 state = (struct MainDlgState *)
1052 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1053 ui_set_key_type(hwnd, state, LOWORD(wParam));
1057 PostMessage(hwnd, WM_CLOSE, 0, 0);
1059 case IDC_COMMENTEDIT:
1060 if (HIWORD(wParam) == EN_CHANGE) {
1061 state = (struct MainDlgState *)
1062 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1063 if (state->key_exists) {
1064 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1065 int len = GetWindowTextLength(editctl);
1066 if (*state->commentptr)
1067 sfree(*state->commentptr);
1068 *state->commentptr = snewn(len + 1, char);
1069 GetWindowText(editctl, *state->commentptr, len + 1);
1071 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1074 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1081 EnableWindow(hwnd, 0);
1082 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1083 EnableWindow(hwnd, 1);
1084 SetActiveWindow(hwnd);
1087 if (HIWORD(wParam) == BN_CLICKED ||
1088 HIWORD(wParam) == BN_DOUBLECLICKED) {
1089 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1093 if (HIWORD(wParam) != BN_CLICKED &&
1094 HIWORD(wParam) != BN_DOUBLECLICKED)
1097 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1098 if (!state->generation_thread_exists) {
1100 state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1102 state->key_bits = DEFAULT_KEY_BITS;
1104 int curveindex = SendDlgItemMessage(hwnd, IDC_CURVE,
1105 CB_GETCURSEL, 0, 0);
1106 assert(curveindex >= 0);
1107 assert(curveindex < n_ec_nist_curve_lengths);
1108 state->curve_bits = ec_nist_curve_lengths[curveindex];
1110 /* If we ever introduce a new key type, check it here! */
1111 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1112 state->keytype = RSA;
1113 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1114 state->keytype = DSA;
1115 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1116 state->keytype = ECDSA;
1117 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1118 state->keytype = ED25519;
1121 if ((state->keytype == RSA || state->keytype == DSA) &&
1122 state->key_bits < 256) {
1123 char *message = dupprintf
1124 ("PuTTYgen will not generate a key smaller than 256"
1125 " bits.\nKey length reset to default %d. Continue?",
1127 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1128 MB_ICONWARNING | MB_OKCANCEL);
1132 state->key_bits = DEFAULT_KEY_BITS;
1133 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
1134 } else if ((state->keytype == RSA || state->keytype == DSA) &&
1135 state->key_bits < DEFAULT_KEY_BITS) {
1136 char *message = dupprintf
1137 ("Keys shorter than %d bits are not recommended. "
1138 "Really generate this key?", DEFAULT_KEY_BITS);
1139 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1140 MB_ICONWARNING | MB_OKCANCEL);
1146 ui_set_state(hwnd, state, 1);
1147 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1148 state->key_exists = FALSE;
1149 state->collecting_entropy = TRUE;
1152 * My brief statistical tests on mouse movements
1153 * suggest that there are about 2.5 bits of
1154 * randomness in the x position, 2.5 in the y
1155 * position, and 1.7 in the message time, making
1156 * 5.7 bits of unpredictability per mouse movement.
1157 * However, other people have told me it's far less
1158 * than that, so I'm going to be stupidly cautious
1159 * and knock that down to a nice round 2. With this
1160 * method, we require two words per mouse movement,
1161 * so with 2 bits per mouse movement we expect 2
1162 * bits every 2 words.
1164 if (state->keytype == RSA || state->keytype == DSA)
1165 state->entropy_required = (state->key_bits / 2) * 2;
1166 else if (state->keytype == ECDSA)
1167 state->entropy_required = (state->curve_bits / 2) * 2;
1169 state->entropy_required = 256;
1171 state->entropy_got = 0;
1172 state->entropy_size = (state->entropy_required *
1174 state->entropy = snewn(state->entropy_required, unsigned);
1176 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1177 MAKELPARAM(0, state->entropy_required));
1178 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1182 case IDC_EXPORT_OPENSSH_AUTO:
1183 case IDC_EXPORT_OPENSSH_NEW:
1184 case IDC_EXPORT_SSHCOM:
1185 if (HIWORD(wParam) != BN_CLICKED)
1188 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1189 if (state->key_exists) {
1190 char filename[FILENAME_MAX];
1191 char *passphrase, *passphrase2;
1195 realtype = SSH_KEYTYPE_SSH2;
1197 realtype = SSH_KEYTYPE_SSH1;
1199 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1200 type = SSH_KEYTYPE_OPENSSH_AUTO;
1201 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1202 type = SSH_KEYTYPE_OPENSSH_NEW;
1203 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1204 type = SSH_KEYTYPE_SSHCOM;
1208 if (type != realtype &&
1209 import_target_type(type) != realtype) {
1211 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1212 " format", (state->ssh2 ? 2 : 1),
1213 (state->ssh2 ? 1 : 2));
1214 MessageBox(hwnd, msg,
1215 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1219 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1220 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1221 if (strcmp(passphrase, passphrase2)) {
1223 "The two passphrases given do not match.",
1224 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1225 burnstr(passphrase);
1226 burnstr(passphrase2);
1229 burnstr(passphrase2);
1232 ret = MessageBox(hwnd,
1233 "Are you sure you want to save this key\n"
1234 "without a passphrase to protect it?",
1236 MB_YESNO | MB_ICONWARNING);
1238 burnstr(passphrase);
1242 if (prompt_keyfile(hwnd, "Save private key as:",
1243 filename, 1, (type == realtype))) {
1245 FILE *fp = fopen(filename, "r");
1249 buffer = dupprintf("Overwrite existing file\n%s?",
1251 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1252 MB_YESNO | MB_ICONWARNING);
1255 burnstr(passphrase);
1261 Filename *fn = filename_from_str(filename);
1262 if (type != realtype)
1263 ret = export_ssh2(fn, type, &state->ssh2key,
1264 *passphrase ? passphrase : NULL);
1266 ret = ssh2_save_userkey(fn, &state->ssh2key,
1267 *passphrase ? passphrase :
1271 Filename *fn = filename_from_str(filename);
1272 if (type != realtype)
1273 ret = export_ssh1(fn, type, &state->key,
1274 *passphrase ? passphrase : NULL);
1276 ret = saversakey(fn, &state->key,
1277 *passphrase ? passphrase : NULL);
1281 MessageBox(hwnd, "Unable to save key file",
1282 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1285 burnstr(passphrase);
1289 if (HIWORD(wParam) != BN_CLICKED)
1292 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1293 if (state->key_exists) {
1294 char filename[FILENAME_MAX];
1295 if (prompt_keyfile(hwnd, "Save public key as:",
1298 FILE *fp = fopen(filename, "r");
1302 buffer = dupprintf("Overwrite existing file\n%s?",
1304 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1305 MB_YESNO | MB_ICONWARNING);
1310 fp = fopen(filename, "w");
1312 MessageBox(hwnd, "Unable to open key file",
1313 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1317 unsigned char *blob;
1318 blob = state->ssh2key.alg->public_blob
1319 (state->ssh2key.data, &bloblen);
1320 ssh2_write_pubkey(fp, state->ssh2key.comment,
1322 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1324 ssh1_write_pubkey(fp, &state->key);
1326 if (fclose(fp) < 0) {
1327 MessageBox(hwnd, "Unable to save key file",
1328 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1336 if (HIWORD(wParam) != BN_CLICKED)
1339 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1340 if (!state->generation_thread_exists) {
1341 char filename[FILENAME_MAX];
1342 if (prompt_keyfile(hwnd, "Load private key:",
1343 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1344 Filename *fn = filename_from_str(filename);
1345 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1353 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1354 state->generation_thread_exists = FALSE;
1355 state->key_exists = TRUE;
1356 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1357 MAKELPARAM(0, PROGRESSRANGE));
1358 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1360 if (state->keytype == DSA) {
1361 state->ssh2key.data = &state->dsskey;
1362 state->ssh2key.alg = &ssh_dss;
1363 } else if (state->keytype == ECDSA) {
1364 state->ssh2key.data = &state->eckey;
1365 state->ssh2key.alg = state->eckey.signalg;
1366 } else if (state->keytype == ED25519) {
1367 state->ssh2key.data = &state->eckey;
1368 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1370 state->ssh2key.data = &state->key;
1371 state->ssh2key.alg = &ssh_rsa;
1373 state->commentptr = &state->ssh2key.comment;
1375 state->commentptr = &state->key.comment;
1378 * Invent a comment for the key. We'll do this by including
1379 * the date in it. This will be so horrifyingly ugly that
1380 * the user will immediately want to change it, which is
1383 *state->commentptr = snewn(30, char);
1387 if (state->keytype == DSA)
1388 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1389 else if (state->keytype == ECDSA)
1390 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1391 else if (state->keytype == ED25519)
1392 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1394 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1398 * Now update the key controls with all the key data.
1403 * Blank passphrase, initially. This isn't dangerous,
1404 * because we will warn (Are You Sure?) before allowing
1405 * the user to save an unprotected private key.
1407 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1408 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1412 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1414 * Set the key fingerprint.
1416 savecomment = *state->commentptr;
1417 *state->commentptr = NULL;
1420 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1421 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1425 rsa_fingerprint(buf, sizeof(buf), &state->key);
1426 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1428 *state->commentptr = savecomment;
1430 * Construct a decimal representation of the key, for
1431 * pasting into .ssh/authorized_keys or
1432 * .ssh/authorized_keys2 on a Unix box.
1435 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1436 IDC_PKSTATIC, &state->ssh2key);
1438 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1439 IDC_PKSTATIC, &state->key);
1443 * Finally, hide the progress bar and show the key data.
1445 ui_set_state(hwnd, state, 2);
1449 int id = ((LPHELPINFO)lParam)->iCtrlId;
1450 const char *topic = NULL;
1452 case IDC_GENERATING:
1456 topic = WINHELP_CTX_puttygen_generate; break;
1458 case IDC_KEYDISPLAY:
1459 topic = WINHELP_CTX_puttygen_pastekey; break;
1461 case IDC_FINGERPRINT:
1462 topic = WINHELP_CTX_puttygen_fingerprint; break;
1463 case IDC_COMMENTSTATIC:
1464 case IDC_COMMENTEDIT:
1465 topic = WINHELP_CTX_puttygen_comment; break;
1466 case IDC_PASSPHRASE1STATIC:
1467 case IDC_PASSPHRASE1EDIT:
1468 case IDC_PASSPHRASE2STATIC:
1469 case IDC_PASSPHRASE2EDIT:
1470 topic = WINHELP_CTX_puttygen_passphrase; break;
1471 case IDC_LOADSTATIC:
1473 topic = WINHELP_CTX_puttygen_load; break;
1474 case IDC_SAVESTATIC:
1476 topic = WINHELP_CTX_puttygen_savepriv; break;
1478 topic = WINHELP_CTX_puttygen_savepub; break;
1479 case IDC_TYPESTATIC:
1481 case IDC_KEYSSH2RSA:
1482 case IDC_KEYSSH2DSA:
1483 case IDC_KEYSSH2ECDSA:
1484 case IDC_KEYSSH2ED25519:
1485 topic = WINHELP_CTX_puttygen_keytype; break;
1486 case IDC_BITSSTATIC:
1488 topic = WINHELP_CTX_puttygen_bits; break;
1490 case IDC_EXPORT_OPENSSH_AUTO:
1491 case IDC_EXPORT_OPENSSH_NEW:
1492 case IDC_EXPORT_SSHCOM:
1493 topic = WINHELP_CTX_puttygen_conversions; break;
1496 launch_help(hwnd, topic);
1503 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1512 void cleanup_exit(int code)
1518 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1524 dll_hijacking_protection();
1526 InitCommonControls();
1531 * See if we can find our Help file.
1535 split_into_argv(cmdline, &argc, &argv, NULL);
1538 if (!strcmp(argv[0], "-pgpfp")) {
1543 * Assume the first argument to be a private key file, and
1544 * attempt to load it.
1546 cmdline_keyfile = argv[0];
1550 #if !defined UNPROTECT && !defined NO_SECURITY
1552 * Protect our process.
1556 if (!setprocessacl(error)) {
1557 char *message = dupprintf("Could not restrict process ACL: %s",
1559 MessageBox(NULL, message, "PuTTYgen Warning",
1560 MB_ICONWARNING | MB_OK);
1568 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1571 return ret; /* just in case optimiser complains */