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 *text = dupprintf
301 ("PuTTYgen\r\n\r\n%s\r\n\r\n%s",
303 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
304 SetDlgItemText(hwnd, 1000, text);
309 switch (LOWORD(wParam)) {
315 EnableWindow(hwnd, 0);
316 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
317 EnableWindow(hwnd, 1);
318 SetActiveWindow(hwnd);
329 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
332 * Thread to generate a key.
334 struct rsa_key_thread_params {
335 HWND progressbar; /* notify this with progress */
336 HWND dialog; /* notify this on completion */
337 int key_bits; /* bits in key modulus (RSA, DSA) */
338 int curve_bits; /* bits in elliptic curve (ECDSA) */
342 struct dss_key *dsskey;
343 struct ec_key *eckey;
346 static DWORD WINAPI generate_key_thread(void *param)
348 struct rsa_key_thread_params *params =
349 (struct rsa_key_thread_params *) param;
350 struct progress prog;
351 prog.progbar = params->progressbar;
353 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
355 if (params->keytype == DSA)
356 dsa_generate(params->dsskey, params->key_bits, progress_update, &prog);
357 else if (params->keytype == ECDSA)
358 ec_generate(params->eckey, params->curve_bits, progress_update, &prog);
359 else if (params->keytype == ED25519)
360 ec_edgenerate(params->eckey, 256, progress_update, &prog);
362 rsa_generate(params->key, params->key_bits, progress_update, &prog);
364 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
370 struct MainDlgState {
371 int collecting_entropy;
372 int generation_thread_exists;
374 int entropy_got, entropy_required, entropy_size;
375 int key_bits, curve_bits;
378 char **commentptr; /* points to key.comment or ssh2key.comment */
379 struct ssh2_userkey ssh2key;
383 struct dss_key dsskey;
386 HMENU filemenu, keymenu, cvtmenu;
389 static void hidemany(HWND hwnd, const int *ids, int hideit)
392 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
396 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
398 char *buffer = ssh1_pubkey_str(key);
399 SetDlgItemText(hwnd, id, buffer);
400 SetDlgItemText(hwnd, idstatic,
401 "&Public key for pasting into authorized_keys file:");
405 static void setupbigedit2(HWND hwnd, int id, int idstatic,
406 struct ssh2_userkey *key)
408 char *buffer = ssh2_pubkey_openssh_str(key);
409 SetDlgItemText(hwnd, id, buffer);
410 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
411 "OpenSSH authorized_keys file:");
416 * Warn about the obsolescent key file format.
418 void old_keyfile_warning(void)
420 static const char mbtitle[] = "PuTTY Key File Warning";
421 static const char message[] =
422 "You are loading an SSH-2 private key which has an\n"
423 "old version of the file format. This means your key\n"
424 "file is not fully tamperproof. Future versions of\n"
425 "PuTTY may stop supporting this private key format,\n"
426 "so we recommend you convert your key to the new\n"
429 "Once the key is loaded into PuTTYgen, you can perform\n"
430 "this conversion simply by saving it again.";
432 MessageBox(NULL, message, mbtitle, MB_OK);
436 controlidstart = 100,
443 IDC_PKSTATIC, IDC_KEYDISPLAY,
444 IDC_FPSTATIC, IDC_FINGERPRINT,
445 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
446 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
447 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
449 IDC_GENSTATIC, IDC_GENERATE,
450 IDC_LOADSTATIC, IDC_LOAD,
451 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
453 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
454 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
455 IDC_BITSSTATIC, IDC_BITS,
456 IDC_CURVESTATIC, IDC_CURVE,
461 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
465 static const int nokey_ids[] = { IDC_NOKEY, 0 };
466 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
467 static const int gotkey_ids[] = {
468 IDC_PKSTATIC, IDC_KEYDISPLAY,
469 IDC_FPSTATIC, IDC_FINGERPRINT,
470 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
471 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
472 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
476 * Small UI helper function to switch the state of the main dialog
477 * by enabling and disabling controls and menu items.
479 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
485 hidemany(hwnd, nokey_ids, FALSE);
486 hidemany(hwnd, generating_ids, TRUE);
487 hidemany(hwnd, gotkey_ids, TRUE);
488 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
489 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
490 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
491 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
492 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
493 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
494 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
495 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
496 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
497 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
498 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
499 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
500 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
501 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
502 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
503 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
504 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
505 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
506 MF_ENABLED|MF_BYCOMMAND);
507 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
508 MF_ENABLED|MF_BYCOMMAND);
509 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
510 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
511 MF_GRAYED|MF_BYCOMMAND);
512 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
513 MF_GRAYED|MF_BYCOMMAND);
514 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
515 MF_GRAYED|MF_BYCOMMAND);
517 case 1: /* generating key */
518 hidemany(hwnd, nokey_ids, TRUE);
519 hidemany(hwnd, generating_ids, FALSE);
520 hidemany(hwnd, gotkey_ids, TRUE);
521 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
522 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
523 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
524 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
525 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
526 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
527 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
528 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
529 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
530 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
531 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|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_GRAYED|MF_BYCOMMAND);
535 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
536 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
537 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
538 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
539 MF_GRAYED|MF_BYCOMMAND);
540 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
541 MF_GRAYED|MF_BYCOMMAND);
542 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|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);
551 hidemany(hwnd, nokey_ids, TRUE);
552 hidemany(hwnd, generating_ids, TRUE);
553 hidemany(hwnd, gotkey_ids, FALSE);
554 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
555 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
556 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
557 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
558 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
559 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
560 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
561 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
562 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
563 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
564 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
565 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
566 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
567 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
568 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
569 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
570 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
571 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
572 MF_ENABLED|MF_BYCOMMAND);
573 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
574 MF_ENABLED|MF_BYCOMMAND);
575 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
577 * Enable export menu items if and only if the key type
578 * supports this kind of export.
580 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
581 #define do_export_menuitem(x,y) \
582 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
583 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
584 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
585 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
586 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
587 #undef do_export_menuitem
593 * Helper functions to set the key type, taking care of keeping the
594 * menu and radio button selections in sync and also showing/hiding
595 * the appropriate size/curve control for the current key type.
597 void ui_update_key_type_ctrls(HWND hwnd)
599 enum { BITS, CURVE, NOTHING } which;
600 static const int bits_ids[] = {
601 IDC_BITSSTATIC, IDC_BITS, 0
603 static const int curve_ids[] = {
604 IDC_CURVESTATIC, IDC_CURVE, 0
606 static const int nothing_ids[] = {
610 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) ||
611 IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) ||
612 IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
614 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
617 /* ED25519 implicitly only supports one curve */
621 hidemany(hwnd, bits_ids, which != BITS);
622 hidemany(hwnd, curve_ids, which != CURVE);
623 hidemany(hwnd, nothing_ids, which != NOTHING);
625 void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button)
627 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ED25519, button);
628 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ED25519,
629 button, MF_BYCOMMAND);
630 ui_update_key_type_ctrls(hwnd);
633 void load_key_file(HWND hwnd, struct MainDlgState *state,
634 Filename *filename, int was_import_cmd)
640 const char *errmsg = NULL;
642 struct RSAKey newkey1;
643 struct ssh2_userkey *newkey2 = NULL;
645 type = realtype = key_type(filename);
646 if (type != SSH_KEYTYPE_SSH1 &&
647 type != SSH_KEYTYPE_SSH2 &&
648 !import_possible(type)) {
649 char *msg = dupprintf("Couldn't load private key (%s)",
650 key_type_to_str(type));
651 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
652 HELPCTXID(errors_cantloadkey));
657 if (type != SSH_KEYTYPE_SSH1 &&
658 type != SSH_KEYTYPE_SSH2) {
660 type = import_target_type(type);
665 if (realtype == SSH_KEYTYPE_SSH1)
666 needs_pass = rsakey_encrypted(filename, &comment);
667 else if (realtype == SSH_KEYTYPE_SSH2)
668 needs_pass = ssh2_userkey_encrypted(filename, &comment);
670 needs_pass = import_encrypted(filename, realtype, &comment);
677 struct PassphraseProcStruct pps;
678 pps.passphrase = &passphrase;
679 pps.comment = comment;
680 dlgret = DialogBoxParam(hinst,
681 MAKEINTRESOURCE(210),
682 NULL, PassphraseProc,
688 assert(passphrase != NULL);
690 passphrase = dupstr("");
691 if (type == SSH_KEYTYPE_SSH1) {
692 if (realtype == type)
693 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
695 ret = import_ssh1(filename, realtype, &newkey1,
696 passphrase, &errmsg);
698 if (realtype == type)
699 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
701 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
702 if (newkey2 == SSH2_WRONG_PASSPHRASE)
713 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
714 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
715 HELPCTXID(errors_cantloadkey));
717 } else if (ret == 1) {
719 * Now update the key controls with all the
723 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
725 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
727 if (type == SSH_KEYTYPE_SSH1) {
732 state->commentptr = &state->key.comment;
733 state->key = newkey1;
736 * Set the key fingerprint.
738 savecomment = state->key.comment;
739 state->key.comment = NULL;
740 rsa_fingerprint(buf, sizeof(buf),
742 state->key.comment = savecomment;
744 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
746 * Construct a decimal representation
747 * of the key, for pasting into
748 * .ssh/authorized_keys on a Unix box.
750 setupbigedit1(hwnd, IDC_KEYDISPLAY,
751 IDC_PKSTATIC, &state->key);
758 &state->ssh2key.comment;
759 state->ssh2key = *newkey2; /* structure copy */
762 savecomment = state->ssh2key.comment;
763 state->ssh2key.comment = NULL;
764 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
765 state->ssh2key.comment = savecomment;
767 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
770 setupbigedit2(hwnd, IDC_KEYDISPLAY,
771 IDC_PKSTATIC, &state->ssh2key);
773 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
777 * Finally, hide the progress bar and show
780 ui_set_state(hwnd, state, 2);
781 state->key_exists = TRUE;
784 * If the user has imported a foreign key
785 * using the Load command, let them know.
786 * If they've used the Import command, be
789 if (realtype != type && !was_import_cmd) {
791 sprintf(msg, "Successfully imported foreign key\n"
793 "To use this key with PuTTY, you need to\n"
794 "use the \"Save private key\" command to\n"
795 "save it in PuTTY's own format.",
796 key_type_to_str(realtype));
797 MessageBox(NULL, msg, "PuTTYgen Notice",
798 MB_OK | MB_ICONINFORMATION);
805 * Dialog-box function for the main PuTTYgen dialog box.
807 static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
808 WPARAM wParam, LPARAM lParam)
810 static const char generating_msg[] =
811 "Please wait while a key is generated...";
812 static const char entropy_msg[] =
813 "Please generate some randomness by moving the mouse over the blank area.";
814 struct MainDlgState *state;
819 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
820 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
824 * If we add a Help button, this is where we destroy it
825 * if the help file isn't present.
828 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
829 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
831 state = snew(struct MainDlgState);
832 state->generation_thread_exists = FALSE;
833 state->collecting_entropy = FALSE;
834 state->entropy = NULL;
835 state->key_exists = FALSE;
836 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
842 menu1 = CreateMenu();
843 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
844 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
845 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
846 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
847 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
848 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
849 state->filemenu = menu1;
851 menu1 = CreateMenu();
852 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
853 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
854 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
855 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
856 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
857 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
858 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
859 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
860 state->keymenu = menu1;
862 menu1 = CreateMenu();
863 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
864 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
865 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
866 "Export &OpenSSH key");
867 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
868 "Export &OpenSSH key (force new file format)");
869 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
870 "Export &ssh.com key");
871 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
873 state->cvtmenu = menu1;
875 menu1 = CreateMenu();
876 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
878 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
879 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
887 { /* centre the window */
891 hw = GetDesktopWindow();
892 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
894 (rs.right + rs.left + rd.left - rd.right) / 2,
895 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
896 rd.right - rd.left, rd.bottom - rd.top, TRUE);
900 struct ctlpos cp, cp2;
903 /* Accelerators used: acglops1rbvde */
905 ctlposinit(&cp, hwnd, 4, 4, 4);
906 beginbox(&cp, "Key", IDC_BOX_KEY);
908 statictext(&cp2, "No key.", 1, IDC_NOKEY);
910 statictext(&cp2, "", 1, IDC_GENERATING);
911 progressbar(&cp2, IDC_PROGRESS);
913 "&Public key for pasting into authorized_keys file:",
914 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
915 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
916 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
917 IDC_FINGERPRINT, 75);
918 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
920 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
921 IDC_COMMENTEDIT, 75);
922 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
923 IDC_PASSPHRASE1EDIT, 75);
924 staticpassedit(&cp, "C&onfirm passphrase:",
925 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
927 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
928 staticbtn(&cp, "Generate a public/private key pair",
929 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
930 staticbtn(&cp, "Load an existing private key file",
931 IDC_LOADSTATIC, "&Load", IDC_LOAD);
932 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
933 "Save p&ublic key", IDC_SAVEPUB,
934 "&Save private key", IDC_SAVE);
936 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
937 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
938 "&RSA", IDC_KEYSSH2RSA,
939 "&DSA", IDC_KEYSSH2DSA,
940 "&ECDSA", IDC_KEYSSH2ECDSA,
941 "ED&25519", IDC_KEYSSH2ED25519,
942 "SSH-&1 (RSA)", IDC_KEYSSH1,
945 staticedit(&cp2, "Number of &bits in a generated key:",
946 IDC_BITSSTATIC, IDC_BITS, 20);
949 staticddl(&cp2, "Cur&ve to use for generating this key:",
950 IDC_CURVESTATIC, IDC_CURVE, 20);
951 SendDlgItemMessage(hwnd, IDC_CURVE, CB_RESETCONTENT, 0, 0);
954 const struct ec_curve *curve;
955 const struct ssh_signkey *alg;
957 for (i = 0; i < n_ec_nist_curve_lengths; i++) {
958 bits = ec_nist_curve_lengths[i];
959 ec_nist_alg_and_curve_by_bits(bits, &curve, &alg);
960 SendDlgItemMessage(hwnd, IDC_CURVE, CB_ADDSTRING, 0,
961 (LPARAM)curve->textname);
964 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
966 statictext(&cp2, "(nothing to configure for this key type)",
967 1, IDC_NOTHINGSTATIC);
968 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
972 ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA);
973 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
974 SendDlgItemMessage(hwnd, IDC_CURVE, CB_SETCURSEL,
975 DEFAULT_CURVE_INDEX, 0);
978 * Initially, hide the progress bar and the key display,
979 * and show the no-key display. Also disable the Save
980 * buttons, because with no key we obviously can't save
983 ui_set_state(hwnd, state, 0);
986 * Load a key file if one was provided on the command line.
988 if (cmdline_keyfile) {
989 Filename *fn = filename_from_str(cmdline_keyfile);
990 load_key_file(hwnd, state, fn, 0);
996 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
997 if (state->collecting_entropy &&
998 state->entropy && state->entropy_got < state->entropy_required) {
999 state->entropy[state->entropy_got++] = lParam;
1000 state->entropy[state->entropy_got++] = GetMessageTime();
1001 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1002 state->entropy_got, 0);
1003 if (state->entropy_got >= state->entropy_required) {
1004 struct rsa_key_thread_params *params;
1008 * Seed the entropy pool
1010 random_add_heavynoise(state->entropy, state->entropy_size);
1011 smemclr(state->entropy, state->entropy_size);
1012 sfree(state->entropy);
1013 state->collecting_entropy = FALSE;
1015 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1016 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1017 MAKELPARAM(0, PROGRESSRANGE));
1018 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1020 params = snew(struct rsa_key_thread_params);
1021 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1022 params->dialog = hwnd;
1023 params->key_bits = state->key_bits;
1024 params->curve_bits = state->curve_bits;
1025 params->keytype = state->keytype;
1026 params->key = &state->key;
1027 params->dsskey = &state->dsskey;
1029 if (!CreateThread(NULL, 0, generate_key_thread,
1030 params, 0, &threadid)) {
1031 MessageBox(hwnd, "Out of thread resources",
1032 "Key generation error",
1033 MB_OK | MB_ICONERROR);
1036 state->generation_thread_exists = TRUE;
1042 switch (LOWORD(wParam)) {
1044 case IDC_KEYSSH2RSA:
1045 case IDC_KEYSSH2DSA:
1046 case IDC_KEYSSH2ECDSA:
1047 case IDC_KEYSSH2ED25519:
1049 state = (struct MainDlgState *)
1050 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1051 ui_set_key_type(hwnd, state, LOWORD(wParam));
1055 PostMessage(hwnd, WM_CLOSE, 0, 0);
1057 case IDC_COMMENTEDIT:
1058 if (HIWORD(wParam) == EN_CHANGE) {
1059 state = (struct MainDlgState *)
1060 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1061 if (state->key_exists) {
1062 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1063 int len = GetWindowTextLength(editctl);
1064 if (*state->commentptr)
1065 sfree(*state->commentptr);
1066 *state->commentptr = snewn(len + 1, char);
1067 GetWindowText(editctl, *state->commentptr, len + 1);
1069 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1072 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1079 EnableWindow(hwnd, 0);
1080 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1081 EnableWindow(hwnd, 1);
1082 SetActiveWindow(hwnd);
1085 if (HIWORD(wParam) == BN_CLICKED ||
1086 HIWORD(wParam) == BN_DOUBLECLICKED) {
1087 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1091 if (HIWORD(wParam) != BN_CLICKED &&
1092 HIWORD(wParam) != BN_DOUBLECLICKED)
1095 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1096 if (!state->generation_thread_exists) {
1098 state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1100 state->key_bits = DEFAULT_KEY_BITS;
1102 int curveindex = SendDlgItemMessage(hwnd, IDC_CURVE,
1103 CB_GETCURSEL, 0, 0);
1104 assert(curveindex >= 0);
1105 assert(curveindex < n_ec_nist_curve_lengths);
1106 state->curve_bits = ec_nist_curve_lengths[curveindex];
1108 /* If we ever introduce a new key type, check it here! */
1109 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1110 state->keytype = RSA;
1111 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1112 state->keytype = DSA;
1113 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1114 state->keytype = ECDSA;
1115 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1116 state->keytype = ED25519;
1119 if ((state->keytype == RSA || state->keytype == DSA) &&
1120 state->key_bits < 256) {
1121 char *message = dupprintf
1122 ("PuTTYgen will not generate a key smaller than 256"
1123 " bits.\nKey length reset to default %d. Continue?",
1125 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1126 MB_ICONWARNING | MB_OKCANCEL);
1130 state->key_bits = DEFAULT_KEY_BITS;
1131 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
1132 } else if ((state->keytype == RSA || state->keytype == DSA) &&
1133 state->key_bits < DEFAULT_KEY_BITS) {
1134 char *message = dupprintf
1135 ("Keys shorter than %d bits are not recommended. "
1136 "Really generate this key?", DEFAULT_KEY_BITS);
1137 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1138 MB_ICONWARNING | MB_OKCANCEL);
1144 ui_set_state(hwnd, state, 1);
1145 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1146 state->key_exists = FALSE;
1147 state->collecting_entropy = TRUE;
1150 * My brief statistical tests on mouse movements
1151 * suggest that there are about 2.5 bits of
1152 * randomness in the x position, 2.5 in the y
1153 * position, and 1.7 in the message time, making
1154 * 5.7 bits of unpredictability per mouse movement.
1155 * However, other people have told me it's far less
1156 * than that, so I'm going to be stupidly cautious
1157 * and knock that down to a nice round 2. With this
1158 * method, we require two words per mouse movement,
1159 * so with 2 bits per mouse movement we expect 2
1160 * bits every 2 words.
1162 if (state->keytype == RSA || state->keytype == DSA)
1163 state->entropy_required = (state->key_bits / 2) * 2;
1164 else if (state->keytype == ECDSA)
1165 state->entropy_required = (state->curve_bits / 2) * 2;
1167 state->entropy_required = 256;
1169 state->entropy_got = 0;
1170 state->entropy_size = (state->entropy_required *
1172 state->entropy = snewn(state->entropy_required, unsigned);
1174 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1175 MAKELPARAM(0, state->entropy_required));
1176 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1180 case IDC_EXPORT_OPENSSH_AUTO:
1181 case IDC_EXPORT_OPENSSH_NEW:
1182 case IDC_EXPORT_SSHCOM:
1183 if (HIWORD(wParam) != BN_CLICKED)
1186 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1187 if (state->key_exists) {
1188 char filename[FILENAME_MAX];
1189 char *passphrase, *passphrase2;
1193 realtype = SSH_KEYTYPE_SSH2;
1195 realtype = SSH_KEYTYPE_SSH1;
1197 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1198 type = SSH_KEYTYPE_OPENSSH_AUTO;
1199 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1200 type = SSH_KEYTYPE_OPENSSH_NEW;
1201 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1202 type = SSH_KEYTYPE_SSHCOM;
1206 if (type != realtype &&
1207 import_target_type(type) != realtype) {
1209 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1210 " format", (state->ssh2 ? 2 : 1),
1211 (state->ssh2 ? 1 : 2));
1212 MessageBox(hwnd, msg,
1213 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1217 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1218 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1219 if (strcmp(passphrase, passphrase2)) {
1221 "The two passphrases given do not match.",
1222 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1223 burnstr(passphrase);
1224 burnstr(passphrase2);
1227 burnstr(passphrase2);
1230 ret = MessageBox(hwnd,
1231 "Are you sure you want to save this key\n"
1232 "without a passphrase to protect it?",
1234 MB_YESNO | MB_ICONWARNING);
1236 burnstr(passphrase);
1240 if (prompt_keyfile(hwnd, "Save private key as:",
1241 filename, 1, (type == realtype))) {
1243 FILE *fp = fopen(filename, "r");
1247 buffer = dupprintf("Overwrite existing file\n%s?",
1249 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1250 MB_YESNO | MB_ICONWARNING);
1253 burnstr(passphrase);
1259 Filename *fn = filename_from_str(filename);
1260 if (type != realtype)
1261 ret = export_ssh2(fn, type, &state->ssh2key,
1262 *passphrase ? passphrase : NULL);
1264 ret = ssh2_save_userkey(fn, &state->ssh2key,
1265 *passphrase ? passphrase :
1269 Filename *fn = filename_from_str(filename);
1270 if (type != realtype)
1271 ret = export_ssh1(fn, type, &state->key,
1272 *passphrase ? passphrase : NULL);
1274 ret = saversakey(fn, &state->key,
1275 *passphrase ? passphrase : NULL);
1279 MessageBox(hwnd, "Unable to save key file",
1280 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1283 burnstr(passphrase);
1287 if (HIWORD(wParam) != BN_CLICKED)
1290 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1291 if (state->key_exists) {
1292 char filename[FILENAME_MAX];
1293 if (prompt_keyfile(hwnd, "Save public key as:",
1296 FILE *fp = fopen(filename, "r");
1300 buffer = dupprintf("Overwrite existing file\n%s?",
1302 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1303 MB_YESNO | MB_ICONWARNING);
1308 fp = fopen(filename, "w");
1310 MessageBox(hwnd, "Unable to open key file",
1311 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1315 unsigned char *blob;
1316 blob = state->ssh2key.alg->public_blob
1317 (state->ssh2key.data, &bloblen);
1318 ssh2_write_pubkey(fp, state->ssh2key.comment,
1320 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1322 ssh1_write_pubkey(fp, &state->key);
1324 if (fclose(fp) < 0) {
1325 MessageBox(hwnd, "Unable to save key file",
1326 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1334 if (HIWORD(wParam) != BN_CLICKED)
1337 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1338 if (!state->generation_thread_exists) {
1339 char filename[FILENAME_MAX];
1340 if (prompt_keyfile(hwnd, "Load private key:",
1341 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1342 Filename *fn = filename_from_str(filename);
1343 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1351 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1352 state->generation_thread_exists = FALSE;
1353 state->key_exists = TRUE;
1354 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1355 MAKELPARAM(0, PROGRESSRANGE));
1356 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1358 if (state->keytype == DSA) {
1359 state->ssh2key.data = &state->dsskey;
1360 state->ssh2key.alg = &ssh_dss;
1361 } else if (state->keytype == ECDSA) {
1362 state->ssh2key.data = &state->eckey;
1363 state->ssh2key.alg = state->eckey.signalg;
1364 } else if (state->keytype == ED25519) {
1365 state->ssh2key.data = &state->eckey;
1366 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1368 state->ssh2key.data = &state->key;
1369 state->ssh2key.alg = &ssh_rsa;
1371 state->commentptr = &state->ssh2key.comment;
1373 state->commentptr = &state->key.comment;
1376 * Invent a comment for the key. We'll do this by including
1377 * the date in it. This will be so horrifyingly ugly that
1378 * the user will immediately want to change it, which is
1381 *state->commentptr = snewn(30, char);
1385 if (state->keytype == DSA)
1386 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1387 else if (state->keytype == ECDSA)
1388 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1389 else if (state->keytype == ED25519)
1390 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1392 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1396 * Now update the key controls with all the key data.
1401 * Blank passphrase, initially. This isn't dangerous,
1402 * because we will warn (Are You Sure?) before allowing
1403 * the user to save an unprotected private key.
1405 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1406 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1410 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1412 * Set the key fingerprint.
1414 savecomment = *state->commentptr;
1415 *state->commentptr = NULL;
1418 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1419 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1423 rsa_fingerprint(buf, sizeof(buf), &state->key);
1424 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1426 *state->commentptr = savecomment;
1428 * Construct a decimal representation of the key, for
1429 * pasting into .ssh/authorized_keys or
1430 * .ssh/authorized_keys2 on a Unix box.
1433 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1434 IDC_PKSTATIC, &state->ssh2key);
1436 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1437 IDC_PKSTATIC, &state->key);
1441 * Finally, hide the progress bar and show the key data.
1443 ui_set_state(hwnd, state, 2);
1447 int id = ((LPHELPINFO)lParam)->iCtrlId;
1448 const char *topic = NULL;
1450 case IDC_GENERATING:
1454 topic = WINHELP_CTX_puttygen_generate; break;
1456 case IDC_KEYDISPLAY:
1457 topic = WINHELP_CTX_puttygen_pastekey; break;
1459 case IDC_FINGERPRINT:
1460 topic = WINHELP_CTX_puttygen_fingerprint; break;
1461 case IDC_COMMENTSTATIC:
1462 case IDC_COMMENTEDIT:
1463 topic = WINHELP_CTX_puttygen_comment; break;
1464 case IDC_PASSPHRASE1STATIC:
1465 case IDC_PASSPHRASE1EDIT:
1466 case IDC_PASSPHRASE2STATIC:
1467 case IDC_PASSPHRASE2EDIT:
1468 topic = WINHELP_CTX_puttygen_passphrase; break;
1469 case IDC_LOADSTATIC:
1471 topic = WINHELP_CTX_puttygen_load; break;
1472 case IDC_SAVESTATIC:
1474 topic = WINHELP_CTX_puttygen_savepriv; break;
1476 topic = WINHELP_CTX_puttygen_savepub; break;
1477 case IDC_TYPESTATIC:
1479 case IDC_KEYSSH2RSA:
1480 case IDC_KEYSSH2DSA:
1481 case IDC_KEYSSH2ECDSA:
1482 case IDC_KEYSSH2ED25519:
1483 topic = WINHELP_CTX_puttygen_keytype; break;
1484 case IDC_BITSSTATIC:
1486 topic = WINHELP_CTX_puttygen_bits; break;
1488 case IDC_EXPORT_OPENSSH_AUTO:
1489 case IDC_EXPORT_OPENSSH_NEW:
1490 case IDC_EXPORT_SSHCOM:
1491 topic = WINHELP_CTX_puttygen_conversions; break;
1494 launch_help(hwnd, topic);
1501 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1510 void cleanup_exit(int code)
1516 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1522 dll_hijacking_protection();
1524 InitCommonControls();
1529 * See if we can find our Help file.
1533 split_into_argv(cmdline, &argc, &argv, NULL);
1536 if (!strcmp(argv[0], "-pgpfp")) {
1541 * Assume the first argument to be a private key file, and
1542 * attempt to load it.
1544 cmdline_keyfile = argv[0];
1548 #if !defined UNPROTECT && !defined NO_SECURITY
1550 * Protect our process.
1554 if (!setprocessacl(error)) {
1555 char *message = dupprintf("Could not restrict process ACL: %s",
1557 MessageBox(NULL, message, "PuTTYgen Warning",
1558 MB_ICONWARNING | MB_OK);
1566 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1569 return ret; /* just in case optimiser complains */