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);
323 /* Load web browser */
324 ShellExecute(hwnd, "open",
325 "http://www.chiark.greenend.org.uk/~sgtatham/putty/",
326 0, 0, SW_SHOWDEFAULT);
337 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
340 * Thread to generate a key.
342 struct rsa_key_thread_params {
343 HWND progressbar; /* notify this with progress */
344 HWND dialog; /* notify this on completion */
345 int key_bits; /* bits in key modulus (RSA, DSA) */
346 int curve_bits; /* bits in elliptic curve (ECDSA) */
350 struct dss_key *dsskey;
351 struct ec_key *eckey;
354 static DWORD WINAPI generate_key_thread(void *param)
356 struct rsa_key_thread_params *params =
357 (struct rsa_key_thread_params *) param;
358 struct progress prog;
359 prog.progbar = params->progressbar;
361 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
363 if (params->keytype == DSA)
364 dsa_generate(params->dsskey, params->key_bits, progress_update, &prog);
365 else if (params->keytype == ECDSA)
366 ec_generate(params->eckey, params->curve_bits, progress_update, &prog);
367 else if (params->keytype == ED25519)
368 ec_edgenerate(params->eckey, 256, progress_update, &prog);
370 rsa_generate(params->key, params->key_bits, progress_update, &prog);
372 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
378 struct MainDlgState {
379 int collecting_entropy;
380 int generation_thread_exists;
382 int entropy_got, entropy_required, entropy_size;
383 int key_bits, curve_bits;
386 char **commentptr; /* points to key.comment or ssh2key.comment */
387 struct ssh2_userkey ssh2key;
391 struct dss_key dsskey;
394 HMENU filemenu, keymenu, cvtmenu;
397 static void hidemany(HWND hwnd, const int *ids, int hideit)
400 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
404 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
406 char *buffer = ssh1_pubkey_str(key);
407 SetDlgItemText(hwnd, id, buffer);
408 SetDlgItemText(hwnd, idstatic,
409 "&Public key for pasting into authorized_keys file:");
413 static void setupbigedit2(HWND hwnd, int id, int idstatic,
414 struct ssh2_userkey *key)
416 char *buffer = ssh2_pubkey_openssh_str(key);
417 SetDlgItemText(hwnd, id, buffer);
418 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
419 "OpenSSH authorized_keys file:");
424 * Warn about the obsolescent key file format.
426 void old_keyfile_warning(void)
428 static const char mbtitle[] = "PuTTY Key File Warning";
429 static const char message[] =
430 "You are loading an SSH-2 private key which has an\n"
431 "old version of the file format. This means your key\n"
432 "file is not fully tamperproof. Future versions of\n"
433 "PuTTY may stop supporting this private key format,\n"
434 "so we recommend you convert your key to the new\n"
437 "Once the key is loaded into PuTTYgen, you can perform\n"
438 "this conversion simply by saving it again.";
440 MessageBox(NULL, message, mbtitle, MB_OK);
444 controlidstart = 100,
451 IDC_PKSTATIC, IDC_KEYDISPLAY,
452 IDC_FPSTATIC, IDC_FINGERPRINT,
453 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
454 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
455 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
457 IDC_GENSTATIC, IDC_GENERATE,
458 IDC_LOADSTATIC, IDC_LOAD,
459 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
461 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
462 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
463 IDC_BITSSTATIC, IDC_BITS,
464 IDC_CURVESTATIC, IDC_CURVE,
469 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
473 static const int nokey_ids[] = { IDC_NOKEY, 0 };
474 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
475 static const int gotkey_ids[] = {
476 IDC_PKSTATIC, IDC_KEYDISPLAY,
477 IDC_FPSTATIC, IDC_FINGERPRINT,
478 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
479 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
480 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
484 * Small UI helper function to switch the state of the main dialog
485 * by enabling and disabling controls and menu items.
487 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
493 hidemany(hwnd, nokey_ids, FALSE);
494 hidemany(hwnd, generating_ids, TRUE);
495 hidemany(hwnd, gotkey_ids, TRUE);
496 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
497 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
498 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
499 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
500 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
501 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
502 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
503 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
504 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
505 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
506 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
507 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
508 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
509 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
510 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
511 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
512 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
513 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
514 MF_ENABLED|MF_BYCOMMAND);
515 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
516 MF_ENABLED|MF_BYCOMMAND);
517 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
518 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
519 MF_GRAYED|MF_BYCOMMAND);
520 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
521 MF_GRAYED|MF_BYCOMMAND);
522 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
523 MF_GRAYED|MF_BYCOMMAND);
525 case 1: /* generating key */
526 hidemany(hwnd, nokey_ids, TRUE);
527 hidemany(hwnd, generating_ids, FALSE);
528 hidemany(hwnd, gotkey_ids, TRUE);
529 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
530 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
531 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
532 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
533 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
534 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
535 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
536 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
537 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
538 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
539 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
540 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
541 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
542 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
543 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
544 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
545 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
546 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
547 MF_GRAYED|MF_BYCOMMAND);
548 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
549 MF_GRAYED|MF_BYCOMMAND);
550 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
551 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
552 MF_GRAYED|MF_BYCOMMAND);
553 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
554 MF_GRAYED|MF_BYCOMMAND);
555 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
556 MF_GRAYED|MF_BYCOMMAND);
559 hidemany(hwnd, nokey_ids, TRUE);
560 hidemany(hwnd, generating_ids, TRUE);
561 hidemany(hwnd, gotkey_ids, FALSE);
562 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
563 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
564 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
565 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
566 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
567 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
568 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
569 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
570 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
571 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
572 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
573 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
574 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
575 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
576 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
577 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
578 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
579 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
580 MF_ENABLED|MF_BYCOMMAND);
581 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
582 MF_ENABLED|MF_BYCOMMAND);
583 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
585 * Enable export menu items if and only if the key type
586 * supports this kind of export.
588 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
589 #define do_export_menuitem(x,y) \
590 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
591 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
592 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
593 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
594 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
595 #undef do_export_menuitem
601 * Helper functions to set the key type, taking care of keeping the
602 * menu and radio button selections in sync and also showing/hiding
603 * the appropriate size/curve control for the current key type.
605 void ui_update_key_type_ctrls(HWND hwnd)
607 enum { BITS, CURVE, NOTHING } which;
608 static const int bits_ids[] = {
609 IDC_BITSSTATIC, IDC_BITS, 0
611 static const int curve_ids[] = {
612 IDC_CURVESTATIC, IDC_CURVE, 0
614 static const int nothing_ids[] = {
618 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) ||
619 IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) ||
620 IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
622 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
625 /* ED25519 implicitly only supports one curve */
629 hidemany(hwnd, bits_ids, which != BITS);
630 hidemany(hwnd, curve_ids, which != CURVE);
631 hidemany(hwnd, nothing_ids, which != NOTHING);
633 void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button)
635 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ED25519, button);
636 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ED25519,
637 button, MF_BYCOMMAND);
638 ui_update_key_type_ctrls(hwnd);
641 void load_key_file(HWND hwnd, struct MainDlgState *state,
642 Filename *filename, int was_import_cmd)
648 const char *errmsg = NULL;
650 struct RSAKey newkey1;
651 struct ssh2_userkey *newkey2 = NULL;
653 type = realtype = key_type(filename);
654 if (type != SSH_KEYTYPE_SSH1 &&
655 type != SSH_KEYTYPE_SSH2 &&
656 !import_possible(type)) {
657 char *msg = dupprintf("Couldn't load private key (%s)",
658 key_type_to_str(type));
659 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
660 HELPCTXID(errors_cantloadkey));
665 if (type != SSH_KEYTYPE_SSH1 &&
666 type != SSH_KEYTYPE_SSH2) {
668 type = import_target_type(type);
673 if (realtype == SSH_KEYTYPE_SSH1)
674 needs_pass = rsakey_encrypted(filename, &comment);
675 else if (realtype == SSH_KEYTYPE_SSH2)
676 needs_pass = ssh2_userkey_encrypted(filename, &comment);
678 needs_pass = import_encrypted(filename, realtype, &comment);
685 struct PassphraseProcStruct pps;
686 pps.passphrase = &passphrase;
687 pps.comment = comment;
688 dlgret = DialogBoxParam(hinst,
689 MAKEINTRESOURCE(210),
690 NULL, PassphraseProc,
696 assert(passphrase != NULL);
698 passphrase = dupstr("");
699 if (type == SSH_KEYTYPE_SSH1) {
700 if (realtype == type)
701 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
703 ret = import_ssh1(filename, realtype, &newkey1,
704 passphrase, &errmsg);
706 if (realtype == type)
707 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
709 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
710 if (newkey2 == SSH2_WRONG_PASSPHRASE)
721 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
722 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
723 HELPCTXID(errors_cantloadkey));
725 } else if (ret == 1) {
727 * Now update the key controls with all the
731 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
733 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
735 if (type == SSH_KEYTYPE_SSH1) {
740 state->commentptr = &state->key.comment;
741 state->key = newkey1;
744 * Set the key fingerprint.
746 savecomment = state->key.comment;
747 state->key.comment = NULL;
748 rsa_fingerprint(buf, sizeof(buf),
750 state->key.comment = savecomment;
752 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
754 * Construct a decimal representation
755 * of the key, for pasting into
756 * .ssh/authorized_keys on a Unix box.
758 setupbigedit1(hwnd, IDC_KEYDISPLAY,
759 IDC_PKSTATIC, &state->key);
766 &state->ssh2key.comment;
767 state->ssh2key = *newkey2; /* structure copy */
770 savecomment = state->ssh2key.comment;
771 state->ssh2key.comment = NULL;
772 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
773 state->ssh2key.comment = savecomment;
775 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
778 setupbigedit2(hwnd, IDC_KEYDISPLAY,
779 IDC_PKSTATIC, &state->ssh2key);
781 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
785 * Finally, hide the progress bar and show
788 ui_set_state(hwnd, state, 2);
789 state->key_exists = TRUE;
792 * If the user has imported a foreign key
793 * using the Load command, let them know.
794 * If they've used the Import command, be
797 if (realtype != type && !was_import_cmd) {
799 sprintf(msg, "Successfully imported foreign key\n"
801 "To use this key with PuTTY, you need to\n"
802 "use the \"Save private key\" command to\n"
803 "save it in PuTTY's own format.",
804 key_type_to_str(realtype));
805 MessageBox(NULL, msg, "PuTTYgen Notice",
806 MB_OK | MB_ICONINFORMATION);
813 * Dialog-box function for the main PuTTYgen dialog box.
815 static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
816 WPARAM wParam, LPARAM lParam)
818 static const char generating_msg[] =
819 "Please wait while a key is generated...";
820 static const char entropy_msg[] =
821 "Please generate some randomness by moving the mouse over the blank area.";
822 struct MainDlgState *state;
827 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
828 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
832 * If we add a Help button, this is where we destroy it
833 * if the help file isn't present.
836 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
837 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
839 state = snew(struct MainDlgState);
840 state->generation_thread_exists = FALSE;
841 state->collecting_entropy = FALSE;
842 state->entropy = NULL;
843 state->key_exists = FALSE;
844 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
850 menu1 = CreateMenu();
851 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
852 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
853 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
854 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
855 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
856 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
857 state->filemenu = menu1;
859 menu1 = CreateMenu();
860 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
861 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
862 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
863 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
864 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
865 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
866 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
867 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
868 state->keymenu = menu1;
870 menu1 = CreateMenu();
871 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
872 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
873 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
874 "Export &OpenSSH key");
875 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
876 "Export &OpenSSH key (force new file format)");
877 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
878 "Export &ssh.com key");
879 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
881 state->cvtmenu = menu1;
883 menu1 = CreateMenu();
884 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
886 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
887 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
895 { /* centre the window */
899 hw = GetDesktopWindow();
900 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
902 (rs.right + rs.left + rd.left - rd.right) / 2,
903 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
904 rd.right - rd.left, rd.bottom - rd.top, TRUE);
908 struct ctlpos cp, cp2;
911 /* Accelerators used: acglops1rbvde */
913 ctlposinit(&cp, hwnd, 4, 4, 4);
914 beginbox(&cp, "Key", IDC_BOX_KEY);
916 statictext(&cp2, "No key.", 1, IDC_NOKEY);
918 statictext(&cp2, "", 1, IDC_GENERATING);
919 progressbar(&cp2, IDC_PROGRESS);
921 "&Public key for pasting into authorized_keys file:",
922 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
923 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
924 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
925 IDC_FINGERPRINT, 75);
926 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
928 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
929 IDC_COMMENTEDIT, 75);
930 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
931 IDC_PASSPHRASE1EDIT, 75);
932 staticpassedit(&cp, "C&onfirm passphrase:",
933 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
935 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
936 staticbtn(&cp, "Generate a public/private key pair",
937 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
938 staticbtn(&cp, "Load an existing private key file",
939 IDC_LOADSTATIC, "&Load", IDC_LOAD);
940 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
941 "Save p&ublic key", IDC_SAVEPUB,
942 "&Save private key", IDC_SAVE);
944 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
945 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
946 "&RSA", IDC_KEYSSH2RSA,
947 "&DSA", IDC_KEYSSH2DSA,
948 "&ECDSA", IDC_KEYSSH2ECDSA,
949 "ED&25519", IDC_KEYSSH2ED25519,
950 "SSH-&1 (RSA)", IDC_KEYSSH1,
953 staticedit(&cp2, "Number of &bits in a generated key:",
954 IDC_BITSSTATIC, IDC_BITS, 20);
957 staticddl(&cp2, "Cur&ve to use for generating this key:",
958 IDC_CURVESTATIC, IDC_CURVE, 20);
959 SendDlgItemMessage(hwnd, IDC_CURVE, CB_RESETCONTENT, 0, 0);
962 const struct ec_curve *curve;
963 const struct ssh_signkey *alg;
965 for (i = 0; i < n_ec_nist_curve_lengths; i++) {
966 bits = ec_nist_curve_lengths[i];
967 ec_nist_alg_and_curve_by_bits(bits, &curve, &alg);
968 SendDlgItemMessage(hwnd, IDC_CURVE, CB_ADDSTRING, 0,
969 (LPARAM)curve->textname);
972 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
974 statictext(&cp2, "(nothing to configure for this key type)",
975 1, IDC_NOTHINGSTATIC);
976 ymax = ymax > cp2.ypos ? ymax : cp2.ypos;
980 ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA);
981 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
982 SendDlgItemMessage(hwnd, IDC_CURVE, CB_SETCURSEL,
983 DEFAULT_CURVE_INDEX, 0);
986 * Initially, hide the progress bar and the key display,
987 * and show the no-key display. Also disable the Save
988 * buttons, because with no key we obviously can't save
991 ui_set_state(hwnd, state, 0);
994 * Load a key file if one was provided on the command line.
996 if (cmdline_keyfile) {
997 Filename *fn = filename_from_str(cmdline_keyfile);
998 load_key_file(hwnd, state, fn, 0);
1004 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1005 if (state->collecting_entropy &&
1006 state->entropy && state->entropy_got < state->entropy_required) {
1007 state->entropy[state->entropy_got++] = lParam;
1008 state->entropy[state->entropy_got++] = GetMessageTime();
1009 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1010 state->entropy_got, 0);
1011 if (state->entropy_got >= state->entropy_required) {
1012 struct rsa_key_thread_params *params;
1016 * Seed the entropy pool
1018 random_add_heavynoise(state->entropy, state->entropy_size);
1019 smemclr(state->entropy, state->entropy_size);
1020 sfree(state->entropy);
1021 state->collecting_entropy = FALSE;
1023 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1024 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1025 MAKELPARAM(0, PROGRESSRANGE));
1026 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1028 params = snew(struct rsa_key_thread_params);
1029 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1030 params->dialog = hwnd;
1031 params->key_bits = state->key_bits;
1032 params->curve_bits = state->curve_bits;
1033 params->keytype = state->keytype;
1034 params->key = &state->key;
1035 params->dsskey = &state->dsskey;
1037 if (!CreateThread(NULL, 0, generate_key_thread,
1038 params, 0, &threadid)) {
1039 MessageBox(hwnd, "Out of thread resources",
1040 "Key generation error",
1041 MB_OK | MB_ICONERROR);
1044 state->generation_thread_exists = TRUE;
1050 switch (LOWORD(wParam)) {
1052 case IDC_KEYSSH2RSA:
1053 case IDC_KEYSSH2DSA:
1054 case IDC_KEYSSH2ECDSA:
1055 case IDC_KEYSSH2ED25519:
1057 state = (struct MainDlgState *)
1058 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1059 ui_set_key_type(hwnd, state, LOWORD(wParam));
1063 PostMessage(hwnd, WM_CLOSE, 0, 0);
1065 case IDC_COMMENTEDIT:
1066 if (HIWORD(wParam) == EN_CHANGE) {
1067 state = (struct MainDlgState *)
1068 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1069 if (state->key_exists) {
1070 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1071 int len = GetWindowTextLength(editctl);
1072 if (*state->commentptr)
1073 sfree(*state->commentptr);
1074 *state->commentptr = snewn(len + 1, char);
1075 GetWindowText(editctl, *state->commentptr, len + 1);
1077 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1080 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1087 EnableWindow(hwnd, 0);
1088 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1089 EnableWindow(hwnd, 1);
1090 SetActiveWindow(hwnd);
1093 if (HIWORD(wParam) == BN_CLICKED ||
1094 HIWORD(wParam) == BN_DOUBLECLICKED) {
1095 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1099 if (HIWORD(wParam) != BN_CLICKED &&
1100 HIWORD(wParam) != BN_DOUBLECLICKED)
1103 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1104 if (!state->generation_thread_exists) {
1106 state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1108 state->key_bits = DEFAULT_KEY_BITS;
1110 int curveindex = SendDlgItemMessage(hwnd, IDC_CURVE,
1111 CB_GETCURSEL, 0, 0);
1112 assert(curveindex >= 0);
1113 assert(curveindex < n_ec_nist_curve_lengths);
1114 state->curve_bits = ec_nist_curve_lengths[curveindex];
1116 /* If we ever introduce a new key type, check it here! */
1117 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1118 state->keytype = RSA;
1119 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1120 state->keytype = DSA;
1121 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1122 state->keytype = ECDSA;
1123 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1124 state->keytype = ED25519;
1127 if ((state->keytype == RSA || state->keytype == DSA) &&
1128 state->key_bits < 256) {
1129 char *message = dupprintf
1130 ("PuTTYgen will not generate a key smaller than 256"
1131 " bits.\nKey length reset to default %d. Continue?",
1133 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1134 MB_ICONWARNING | MB_OKCANCEL);
1138 state->key_bits = DEFAULT_KEY_BITS;
1139 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, FALSE);
1140 } else if ((state->keytype == RSA || state->keytype == DSA) &&
1141 state->key_bits < DEFAULT_KEY_BITS) {
1142 char *message = dupprintf
1143 ("Keys shorter than %d bits are not recommended. "
1144 "Really generate this key?", DEFAULT_KEY_BITS);
1145 int ret = MessageBox(hwnd, message, "PuTTYgen Warning",
1146 MB_ICONWARNING | MB_OKCANCEL);
1152 ui_set_state(hwnd, state, 1);
1153 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1154 state->key_exists = FALSE;
1155 state->collecting_entropy = TRUE;
1158 * My brief statistical tests on mouse movements
1159 * suggest that there are about 2.5 bits of
1160 * randomness in the x position, 2.5 in the y
1161 * position, and 1.7 in the message time, making
1162 * 5.7 bits of unpredictability per mouse movement.
1163 * However, other people have told me it's far less
1164 * than that, so I'm going to be stupidly cautious
1165 * and knock that down to a nice round 2. With this
1166 * method, we require two words per mouse movement,
1167 * so with 2 bits per mouse movement we expect 2
1168 * bits every 2 words.
1170 if (state->keytype == RSA || state->keytype == DSA)
1171 state->entropy_required = (state->key_bits / 2) * 2;
1172 else if (state->keytype == ECDSA)
1173 state->entropy_required = (state->curve_bits / 2) * 2;
1175 state->entropy_required = 256;
1177 state->entropy_got = 0;
1178 state->entropy_size = (state->entropy_required *
1180 state->entropy = snewn(state->entropy_required, unsigned);
1182 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1183 MAKELPARAM(0, state->entropy_required));
1184 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1188 case IDC_EXPORT_OPENSSH_AUTO:
1189 case IDC_EXPORT_OPENSSH_NEW:
1190 case IDC_EXPORT_SSHCOM:
1191 if (HIWORD(wParam) != BN_CLICKED)
1194 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1195 if (state->key_exists) {
1196 char filename[FILENAME_MAX];
1197 char *passphrase, *passphrase2;
1201 realtype = SSH_KEYTYPE_SSH2;
1203 realtype = SSH_KEYTYPE_SSH1;
1205 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1206 type = SSH_KEYTYPE_OPENSSH_AUTO;
1207 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1208 type = SSH_KEYTYPE_OPENSSH_NEW;
1209 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1210 type = SSH_KEYTYPE_SSHCOM;
1214 if (type != realtype &&
1215 import_target_type(type) != realtype) {
1217 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1218 " format", (state->ssh2 ? 2 : 1),
1219 (state->ssh2 ? 1 : 2));
1220 MessageBox(hwnd, msg,
1221 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1225 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1226 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1227 if (strcmp(passphrase, passphrase2)) {
1229 "The two passphrases given do not match.",
1230 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1231 burnstr(passphrase);
1232 burnstr(passphrase2);
1235 burnstr(passphrase2);
1238 ret = MessageBox(hwnd,
1239 "Are you sure you want to save this key\n"
1240 "without a passphrase to protect it?",
1242 MB_YESNO | MB_ICONWARNING);
1244 burnstr(passphrase);
1248 if (prompt_keyfile(hwnd, "Save private key as:",
1249 filename, 1, (type == realtype))) {
1251 FILE *fp = fopen(filename, "r");
1255 buffer = dupprintf("Overwrite existing file\n%s?",
1257 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1258 MB_YESNO | MB_ICONWARNING);
1261 burnstr(passphrase);
1267 Filename *fn = filename_from_str(filename);
1268 if (type != realtype)
1269 ret = export_ssh2(fn, type, &state->ssh2key,
1270 *passphrase ? passphrase : NULL);
1272 ret = ssh2_save_userkey(fn, &state->ssh2key,
1273 *passphrase ? passphrase :
1277 Filename *fn = filename_from_str(filename);
1278 if (type != realtype)
1279 ret = export_ssh1(fn, type, &state->key,
1280 *passphrase ? passphrase : NULL);
1282 ret = saversakey(fn, &state->key,
1283 *passphrase ? passphrase : NULL);
1287 MessageBox(hwnd, "Unable to save key file",
1288 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1291 burnstr(passphrase);
1295 if (HIWORD(wParam) != BN_CLICKED)
1298 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1299 if (state->key_exists) {
1300 char filename[FILENAME_MAX];
1301 if (prompt_keyfile(hwnd, "Save public key as:",
1304 FILE *fp = fopen(filename, "r");
1308 buffer = dupprintf("Overwrite existing file\n%s?",
1310 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1311 MB_YESNO | MB_ICONWARNING);
1316 fp = fopen(filename, "w");
1318 MessageBox(hwnd, "Unable to open key file",
1319 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1323 unsigned char *blob;
1324 blob = state->ssh2key.alg->public_blob
1325 (state->ssh2key.data, &bloblen);
1326 ssh2_write_pubkey(fp, state->ssh2key.comment,
1328 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1330 ssh1_write_pubkey(fp, &state->key);
1332 if (fclose(fp) < 0) {
1333 MessageBox(hwnd, "Unable to save key file",
1334 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1342 if (HIWORD(wParam) != BN_CLICKED)
1345 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1346 if (!state->generation_thread_exists) {
1347 char filename[FILENAME_MAX];
1348 if (prompt_keyfile(hwnd, "Load private key:",
1349 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1350 Filename *fn = filename_from_str(filename);
1351 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1359 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1360 state->generation_thread_exists = FALSE;
1361 state->key_exists = TRUE;
1362 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1363 MAKELPARAM(0, PROGRESSRANGE));
1364 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1366 if (state->keytype == DSA) {
1367 state->ssh2key.data = &state->dsskey;
1368 state->ssh2key.alg = &ssh_dss;
1369 } else if (state->keytype == ECDSA) {
1370 state->ssh2key.data = &state->eckey;
1371 state->ssh2key.alg = state->eckey.signalg;
1372 } else if (state->keytype == ED25519) {
1373 state->ssh2key.data = &state->eckey;
1374 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1376 state->ssh2key.data = &state->key;
1377 state->ssh2key.alg = &ssh_rsa;
1379 state->commentptr = &state->ssh2key.comment;
1381 state->commentptr = &state->key.comment;
1384 * Invent a comment for the key. We'll do this by including
1385 * the date in it. This will be so horrifyingly ugly that
1386 * the user will immediately want to change it, which is
1389 *state->commentptr = snewn(30, char);
1393 if (state->keytype == DSA)
1394 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1395 else if (state->keytype == ECDSA)
1396 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1397 else if (state->keytype == ED25519)
1398 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1400 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1404 * Now update the key controls with all the key data.
1409 * Blank passphrase, initially. This isn't dangerous,
1410 * because we will warn (Are You Sure?) before allowing
1411 * the user to save an unprotected private key.
1413 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1414 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1418 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1420 * Set the key fingerprint.
1422 savecomment = *state->commentptr;
1423 *state->commentptr = NULL;
1426 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1427 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1431 rsa_fingerprint(buf, sizeof(buf), &state->key);
1432 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1434 *state->commentptr = savecomment;
1436 * Construct a decimal representation of the key, for
1437 * pasting into .ssh/authorized_keys or
1438 * .ssh/authorized_keys2 on a Unix box.
1441 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1442 IDC_PKSTATIC, &state->ssh2key);
1444 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1445 IDC_PKSTATIC, &state->key);
1449 * Finally, hide the progress bar and show the key data.
1451 ui_set_state(hwnd, state, 2);
1455 int id = ((LPHELPINFO)lParam)->iCtrlId;
1456 const char *topic = NULL;
1458 case IDC_GENERATING:
1462 topic = WINHELP_CTX_puttygen_generate; break;
1464 case IDC_KEYDISPLAY:
1465 topic = WINHELP_CTX_puttygen_pastekey; break;
1467 case IDC_FINGERPRINT:
1468 topic = WINHELP_CTX_puttygen_fingerprint; break;
1469 case IDC_COMMENTSTATIC:
1470 case IDC_COMMENTEDIT:
1471 topic = WINHELP_CTX_puttygen_comment; break;
1472 case IDC_PASSPHRASE1STATIC:
1473 case IDC_PASSPHRASE1EDIT:
1474 case IDC_PASSPHRASE2STATIC:
1475 case IDC_PASSPHRASE2EDIT:
1476 topic = WINHELP_CTX_puttygen_passphrase; break;
1477 case IDC_LOADSTATIC:
1479 topic = WINHELP_CTX_puttygen_load; break;
1480 case IDC_SAVESTATIC:
1482 topic = WINHELP_CTX_puttygen_savepriv; break;
1484 topic = WINHELP_CTX_puttygen_savepub; break;
1485 case IDC_TYPESTATIC:
1487 case IDC_KEYSSH2RSA:
1488 case IDC_KEYSSH2DSA:
1489 case IDC_KEYSSH2ECDSA:
1490 case IDC_KEYSSH2ED25519:
1491 topic = WINHELP_CTX_puttygen_keytype; break;
1492 case IDC_BITSSTATIC:
1494 topic = WINHELP_CTX_puttygen_bits; break;
1496 case IDC_EXPORT_OPENSSH_AUTO:
1497 case IDC_EXPORT_OPENSSH_NEW:
1498 case IDC_EXPORT_SSHCOM:
1499 topic = WINHELP_CTX_puttygen_conversions; break;
1502 launch_help(hwnd, topic);
1509 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1518 void cleanup_exit(int code)
1524 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1530 dll_hijacking_protection();
1532 InitCommonControls();
1537 * See if we can find our Help file.
1541 split_into_argv(cmdline, &argc, &argv, NULL);
1543 for (i = 0; i < argc; i++) {
1544 if (!strcmp(argv[i], "-pgpfp")) {
1547 } else if (!strcmp(argv[i], "-restrict-acl") ||
1548 !strcmp(argv[i], "-restrict_acl") ||
1549 !strcmp(argv[i], "-restrictacl")) {
1550 restrict_process_acl();
1553 * Assume the first argument to be a private key file, and
1554 * attempt to load it.
1556 cmdline_keyfile = argv[i];
1562 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1565 return ret; /* just in case optimiser complains */