2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
21 #define WM_DONEKEY (WM_APP + 1)
23 #define DEFAULT_KEYSIZE 2048
25 static char *cmdline_keyfile = NULL;
28 * Print a modal (Really Bad) message box and perform a fatal exit.
30 void modalfatalbox(const char *fmt, ...)
36 stuff = dupvprintf(fmt, ap);
38 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
39 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
45 * Print a non-fatal message box and do not exit.
47 void nonfatal(const char *fmt, ...)
53 stuff = dupvprintf(fmt, ap);
55 MessageBox(NULL, stuff, "PuTTYgen Error",
56 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
60 /* ----------------------------------------------------------------------
61 * Progress report code. This is really horrible :-)
63 #define PROGRESSRANGE 65535
69 unsigned startpoint, total;
70 unsigned param, current, n; /* if exponential */
71 unsigned mult; /* if linear */
73 unsigned total, divisor, range;
77 static void progress_update(void *param, int action, int phase, int iprogress)
79 struct progress *p = (struct progress *) param;
80 unsigned progress = iprogress;
83 if (action < PROGFN_READY && p->nphases < phase)
86 case PROGFN_INITIALISE:
89 case PROGFN_LIN_PHASE:
90 p->phases[phase-1].exponential = 0;
91 p->phases[phase-1].mult = p->phases[phase].total / progress;
93 case PROGFN_EXP_PHASE:
94 p->phases[phase-1].exponential = 1;
95 p->phases[phase-1].param = 0x10000 + progress;
96 p->phases[phase-1].current = p->phases[phase-1].total;
97 p->phases[phase-1].n = 0;
99 case PROGFN_PHASE_EXTENT:
100 p->phases[phase-1].total = progress;
106 for (i = 0; i < p->nphases; i++) {
107 p->phases[i].startpoint = total;
108 total += p->phases[i].total;
111 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
112 p->range = p->total / p->divisor;
113 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
116 case PROGFN_PROGRESS:
117 if (p->phases[phase-1].exponential) {
118 while (p->phases[phase-1].n < progress) {
119 p->phases[phase-1].n++;
120 p->phases[phase-1].current *= p->phases[phase-1].param;
121 p->phases[phase-1].current /= 0x10000;
123 position = (p->phases[phase-1].startpoint +
124 p->phases[phase-1].total - p->phases[phase-1].current);
126 position = (p->phases[phase-1].startpoint +
127 progress * p->phases[phase-1].mult);
129 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
136 struct PassphraseProcStruct {
142 * Dialog-box function for the passphrase box.
144 static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg,
145 WPARAM wParam, LPARAM lParam)
147 static char **passphrase = NULL;
148 struct PassphraseProcStruct *p;
152 SetForegroundWindow(hwnd);
153 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
154 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
159 { /* centre the window */
163 hw = GetDesktopWindow();
164 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
166 (rs.right + rs.left + rd.left - rd.right) / 2,
167 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
168 rd.right - rd.left, rd.bottom - rd.top, TRUE);
171 p = (struct PassphraseProcStruct *) lParam;
172 passphrase = p->passphrase;
174 SetDlgItemText(hwnd, 101, p->comment);
175 burnstr(*passphrase);
176 *passphrase = dupstr("");
177 SetDlgItemText(hwnd, 102, *passphrase);
180 switch (LOWORD(wParam)) {
190 case 102: /* edit box */
191 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
192 burnstr(*passphrase);
193 *passphrase = GetDlgItemText_alloc(hwnd, 102);
206 * Prompt for a key file. Assumes the filename buffer is of size
209 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
210 char *filename, int save, int ppk)
213 memset(&of, 0, sizeof(of));
216 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
217 "All Files (*.*)\0*\0\0\0";
218 of.lpstrDefExt = ".ppk";
220 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
222 of.lpstrCustomFilter = NULL;
224 of.lpstrFile = filename;
226 of.nMaxFile = FILENAME_MAX;
227 of.lpstrFileTitle = NULL;
228 of.lpstrTitle = dlgtitle;
230 return request_file(NULL, &of, FALSE, save);
234 * Dialog-box function for the Licence box.
236 static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg,
237 WPARAM wParam, LPARAM lParam)
244 { /* centre the window */
248 hw = GetDesktopWindow();
249 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
251 (rs.right + rs.left + rd.left - rd.right) / 2,
252 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
253 rd.right - rd.left, rd.bottom - rd.top, TRUE);
256 SetDlgItemText(hwnd, 1000,
257 "Copyright 1997-2015 Simon Tatham.\r\n\r\n"
259 "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
260 "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
261 "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
262 "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\r\n\r\n"
264 "Permission is hereby granted, free of charge, to any person "
265 "obtaining a copy of this software and associated documentation "
266 "files (the ""Software""), to deal in the Software without restriction, "
267 "including without limitation the rights to use, copy, modify, merge, "
268 "publish, distribute, sublicense, and/or sell copies of the Software, "
269 "and to permit persons to whom the Software is furnished to do so, "
270 "subject to the following conditions:\r\n\r\n"
272 "The above copyright notice and this permission notice shall be "
273 "included in all copies or substantial portions of the Software.\r\n\r\n"
275 "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
276 "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
277 "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
278 "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
279 "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
280 "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
281 "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
282 "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
283 "CONNECTION WITH THE SOFTWARE OR THE USE OR "
284 "OTHER DEALINGS IN THE SOFTWARE."
288 switch (LOWORD(wParam)) {
303 * Dialog-box function for the About box.
305 static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg,
306 WPARAM wParam, LPARAM lParam)
313 { /* centre the window */
317 hw = GetDesktopWindow();
318 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
320 (rs.right + rs.left + rd.left - rd.right) / 2,
321 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
322 rd.right - rd.left, rd.bottom - rd.top, TRUE);
326 char *text = dupprintf
327 ("Pageant\r\n\r\n%s\r\n\r\n%s",
329 "\251 1997-2015 Simon Tatham. All rights reserved.");
330 SetDlgItemText(hwnd, 1000, text);
335 switch (LOWORD(wParam)) {
341 EnableWindow(hwnd, 0);
342 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
343 EnableWindow(hwnd, 1);
344 SetActiveWindow(hwnd);
355 typedef enum {RSA, DSA, ECDSA, ED25519} keytype;
358 * Thread to generate a key.
360 struct rsa_key_thread_params {
361 HWND progressbar; /* notify this with progress */
362 HWND dialog; /* notify this on completion */
363 int keysize; /* bits in key */
367 struct dss_key *dsskey;
368 struct ec_key *eckey;
371 static DWORD WINAPI generate_rsa_key_thread(void *param)
373 struct rsa_key_thread_params *params =
374 (struct rsa_key_thread_params *) param;
375 struct progress prog;
376 prog.progbar = params->progressbar;
378 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
380 if (params->keytype == DSA)
381 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
382 else if (params->keytype == ECDSA)
383 ec_generate(params->eckey, params->keysize, progress_update, &prog);
384 else if (params->keytype == ED25519)
385 ec_edgenerate(params->eckey, params->keysize, progress_update, &prog);
387 rsa_generate(params->key, params->keysize, progress_update, &prog);
389 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
395 struct MainDlgState {
396 int collecting_entropy;
397 int generation_thread_exists;
399 int entropy_got, entropy_required, entropy_size;
403 char **commentptr; /* points to key.comment or ssh2key.comment */
404 struct ssh2_userkey ssh2key;
408 struct dss_key dsskey;
411 HMENU filemenu, keymenu, cvtmenu;
414 static void hidemany(HWND hwnd, const int *ids, int hideit)
417 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
421 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
423 char *buffer = ssh1_pubkey_str(key);
424 SetDlgItemText(hwnd, id, buffer);
425 SetDlgItemText(hwnd, idstatic,
426 "&Public key for pasting into authorized_keys file:");
430 static void setupbigedit2(HWND hwnd, int id, int idstatic,
431 struct ssh2_userkey *key)
433 char *buffer = ssh2_pubkey_openssh_str(key);
434 SetDlgItemText(hwnd, id, buffer);
435 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
436 "OpenSSH authorized_keys file:");
441 * Warn about the obsolescent key file format.
443 void old_keyfile_warning(void)
445 static const char mbtitle[] = "PuTTY Key File Warning";
446 static const char message[] =
447 "You are loading an SSH-2 private key which has an\n"
448 "old version of the file format. This means your key\n"
449 "file is not fully tamperproof. Future versions of\n"
450 "PuTTY may stop supporting this private key format,\n"
451 "so we recommend you convert your key to the new\n"
454 "Once the key is loaded into PuTTYgen, you can perform\n"
455 "this conversion simply by saving it again.";
457 MessageBox(NULL, message, mbtitle, MB_OK);
461 controlidstart = 100,
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,
474 IDC_GENSTATIC, IDC_GENERATE,
475 IDC_LOADSTATIC, IDC_LOAD,
476 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
478 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
479 IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519,
480 IDC_BITSSTATIC, IDC_BITS,
484 IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW,
488 static const int nokey_ids[] = { IDC_NOKEY, 0 };
489 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
490 static const int gotkey_ids[] = {
491 IDC_PKSTATIC, IDC_KEYDISPLAY,
492 IDC_FPSTATIC, IDC_FINGERPRINT,
493 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
494 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
495 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
499 * Small UI helper function to switch the state of the main dialog
500 * by enabling and disabling controls and menu items.
502 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
508 hidemany(hwnd, nokey_ids, FALSE);
509 hidemany(hwnd, generating_ids, TRUE);
510 hidemany(hwnd, gotkey_ids, TRUE);
511 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
512 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
513 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
514 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
515 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
516 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
517 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
518 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
519 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
520 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
521 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
522 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
523 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
524 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
525 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
526 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
527 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
528 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
529 MF_ENABLED|MF_BYCOMMAND);
530 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
531 MF_ENABLED|MF_BYCOMMAND);
532 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
533 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
534 MF_GRAYED|MF_BYCOMMAND);
535 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
536 MF_GRAYED|MF_BYCOMMAND);
537 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
538 MF_GRAYED|MF_BYCOMMAND);
540 case 1: /* generating key */
541 hidemany(hwnd, nokey_ids, TRUE);
542 hidemany(hwnd, generating_ids, FALSE);
543 hidemany(hwnd, gotkey_ids, TRUE);
544 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
545 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
546 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
547 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
548 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
549 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
550 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
551 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0);
552 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0);
553 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
554 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
555 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
556 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
557 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
558 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
559 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
560 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
561 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
562 MF_GRAYED|MF_BYCOMMAND);
563 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
564 MF_GRAYED|MF_BYCOMMAND);
565 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
566 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO,
567 MF_GRAYED|MF_BYCOMMAND);
568 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW,
569 MF_GRAYED|MF_BYCOMMAND);
570 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
571 MF_GRAYED|MF_BYCOMMAND);
574 hidemany(hwnd, nokey_ids, TRUE);
575 hidemany(hwnd, generating_ids, TRUE);
576 hidemany(hwnd, gotkey_ids, FALSE);
577 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
578 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
579 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
580 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
581 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
582 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
583 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
584 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1);
585 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1);
586 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
587 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
588 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
589 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
590 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
591 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
592 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
593 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
594 EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA,
595 MF_ENABLED|MF_BYCOMMAND);
596 EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519,
597 MF_ENABLED|MF_BYCOMMAND);
598 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
600 * Enable export menu items if and only if the key type
601 * supports this kind of export.
603 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
604 #define do_export_menuitem(x,y) \
605 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
606 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
607 do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO);
608 do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW);
609 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
610 #undef do_export_menuitem
615 void load_key_file(HWND hwnd, struct MainDlgState *state,
616 Filename *filename, int was_import_cmd)
622 const char *errmsg = NULL;
624 struct RSAKey newkey1;
625 struct ssh2_userkey *newkey2 = NULL;
627 type = realtype = key_type(filename);
628 if (type != SSH_KEYTYPE_SSH1 &&
629 type != SSH_KEYTYPE_SSH2 &&
630 !import_possible(type)) {
631 char *msg = dupprintf("Couldn't load private key (%s)",
632 key_type_to_str(type));
633 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
634 HELPCTXID(errors_cantloadkey));
639 if (type != SSH_KEYTYPE_SSH1 &&
640 type != SSH_KEYTYPE_SSH2) {
642 type = import_target_type(type);
647 if (realtype == SSH_KEYTYPE_SSH1)
648 needs_pass = rsakey_encrypted(filename, &comment);
649 else if (realtype == SSH_KEYTYPE_SSH2)
650 needs_pass = ssh2_userkey_encrypted(filename, &comment);
652 needs_pass = import_encrypted(filename, realtype, &comment);
659 struct PassphraseProcStruct pps;
660 pps.passphrase = &passphrase;
661 pps.comment = comment;
662 dlgret = DialogBoxParam(hinst,
663 MAKEINTRESOURCE(210),
664 NULL, PassphraseProc,
670 assert(passphrase != NULL);
672 passphrase = dupstr("");
673 if (type == SSH_KEYTYPE_SSH1) {
674 if (realtype == type)
675 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
677 ret = import_ssh1(filename, realtype, &newkey1,
678 passphrase, &errmsg);
680 if (realtype == type)
681 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
683 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
684 if (newkey2 == SSH2_WRONG_PASSPHRASE)
695 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
696 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
697 HELPCTXID(errors_cantloadkey));
699 } else if (ret == 1) {
701 * Now update the key controls with all the
705 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
707 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
709 if (type == SSH_KEYTYPE_SSH1) {
714 state->commentptr = &state->key.comment;
715 state->key = newkey1;
718 * Set the key fingerprint.
720 savecomment = state->key.comment;
721 state->key.comment = NULL;
722 rsa_fingerprint(buf, sizeof(buf),
724 state->key.comment = savecomment;
726 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
728 * Construct a decimal representation
729 * of the key, for pasting into
730 * .ssh/authorized_keys on a Unix box.
732 setupbigedit1(hwnd, IDC_KEYDISPLAY,
733 IDC_PKSTATIC, &state->key);
740 &state->ssh2key.comment;
741 state->ssh2key = *newkey2; /* structure copy */
744 savecomment = state->ssh2key.comment;
745 state->ssh2key.comment = NULL;
746 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
747 state->ssh2key.comment = savecomment;
749 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
752 setupbigedit2(hwnd, IDC_KEYDISPLAY,
753 IDC_PKSTATIC, &state->ssh2key);
755 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
759 * Finally, hide the progress bar and show
762 ui_set_state(hwnd, state, 2);
763 state->key_exists = TRUE;
766 * If the user has imported a foreign key
767 * using the Load command, let them know.
768 * If they've used the Import command, be
771 if (realtype != type && !was_import_cmd) {
773 sprintf(msg, "Successfully imported foreign key\n"
775 "To use this key with PuTTY, you need to\n"
776 "use the \"Save private key\" command to\n"
777 "save it in PuTTY's own format.",
778 key_type_to_str(realtype));
779 MessageBox(NULL, msg, "PuTTYgen Notice",
780 MB_OK | MB_ICONINFORMATION);
787 * Dialog-box function for the main PuTTYgen dialog box.
789 static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg,
790 WPARAM wParam, LPARAM lParam)
792 static const char generating_msg[] =
793 "Please wait while a key is generated...";
794 static const char entropy_msg[] =
795 "Please generate some randomness by moving the mouse over the blank area.";
796 struct MainDlgState *state;
801 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
802 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
806 * If we add a Help button, this is where we destroy it
807 * if the help file isn't present.
810 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
811 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
813 state = snew(struct MainDlgState);
814 state->generation_thread_exists = FALSE;
815 state->collecting_entropy = FALSE;
816 state->entropy = NULL;
817 state->key_exists = FALSE;
818 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
824 menu1 = CreateMenu();
825 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
826 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
827 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
828 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
829 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
830 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File");
831 state->filemenu = menu1;
833 menu1 = CreateMenu();
834 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
835 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
836 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
837 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
838 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
839 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key");
840 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 ED&25519 key");
841 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key");
842 state->keymenu = menu1;
844 menu1 = CreateMenu();
845 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
846 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
847 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO,
848 "Export &OpenSSH key");
849 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW,
850 "Export &OpenSSH key (force new file format)");
851 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
852 "Export &ssh.com key");
853 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1,
855 state->cvtmenu = menu1;
857 menu1 = CreateMenu();
858 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
860 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
861 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help");
869 { /* centre the window */
873 hw = GetDesktopWindow();
874 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
876 (rs.right + rs.left + rd.left - rd.right) / 2,
877 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
878 rd.right - rd.left, rd.bottom - rd.top, TRUE);
882 struct ctlpos cp, cp2;
884 /* Accelerators used: acglops1rbde */
886 ctlposinit(&cp, hwnd, 4, 4, 4);
887 beginbox(&cp, "Key", IDC_BOX_KEY);
889 statictext(&cp2, "No key.", 1, IDC_NOKEY);
891 statictext(&cp2, "", 1, IDC_GENERATING);
892 progressbar(&cp2, IDC_PROGRESS);
894 "&Public key for pasting into authorized_keys file:",
895 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
896 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
897 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
898 IDC_FINGERPRINT, 75);
899 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
901 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
902 IDC_COMMENTEDIT, 75);
903 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
904 IDC_PASSPHRASE1EDIT, 75);
905 staticpassedit(&cp, "C&onfirm passphrase:",
906 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
908 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
909 staticbtn(&cp, "Generate a public/private key pair",
910 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
911 staticbtn(&cp, "Load an existing private key file",
912 IDC_LOADSTATIC, "&Load", IDC_LOAD);
913 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
914 "Save p&ublic key", IDC_SAVEPUB,
915 "&Save private key", IDC_SAVE);
917 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
918 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5,
919 "&RSA", IDC_KEYSSH2RSA,
920 "&DSA", IDC_KEYSSH2DSA,
921 "&ECDSA", IDC_KEYSSH2ECDSA,
922 "ED&25519", IDC_KEYSSH2ED25519,
923 "SSH-&1 (RSA)", IDC_KEYSSH1,
925 staticedit(&cp, "Number of &bits in a generated key:",
926 IDC_BITSSTATIC, IDC_BITS, 20);
929 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ECDSA, IDC_KEYSSH2RSA);
930 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ECDSA,
931 IDC_KEYSSH2RSA, MF_BYCOMMAND);
932 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
935 * Initially, hide the progress bar and the key display,
936 * and show the no-key display. Also disable the Save
937 * buttons, because with no key we obviously can't save
940 ui_set_state(hwnd, state, 0);
943 * Load a key file if one was provided on the command line.
945 if (cmdline_keyfile) {
946 Filename *fn = filename_from_str(cmdline_keyfile);
947 load_key_file(hwnd, state, fn, 0);
953 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
954 if (state->collecting_entropy &&
955 state->entropy && state->entropy_got < state->entropy_required) {
956 state->entropy[state->entropy_got++] = lParam;
957 state->entropy[state->entropy_got++] = GetMessageTime();
958 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
959 state->entropy_got, 0);
960 if (state->entropy_got >= state->entropy_required) {
961 struct rsa_key_thread_params *params;
965 * Seed the entropy pool
967 random_add_heavynoise(state->entropy, state->entropy_size);
968 smemclr(state->entropy, state->entropy_size);
969 sfree(state->entropy);
970 state->collecting_entropy = FALSE;
972 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
973 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
974 MAKELPARAM(0, PROGRESSRANGE));
975 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
977 params = snew(struct rsa_key_thread_params);
978 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
979 params->dialog = hwnd;
980 params->keysize = state->keysize;
981 params->keytype = state->keytype;
982 params->key = &state->key;
983 params->dsskey = &state->dsskey;
985 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
986 params, 0, &threadid)) {
987 MessageBox(hwnd, "Out of thread resources",
988 "Key generation error",
989 MB_OK | MB_ICONERROR);
992 state->generation_thread_exists = TRUE;
998 switch (LOWORD(wParam)) {
1000 case IDC_KEYSSH2RSA:
1001 case IDC_KEYSSH2DSA:
1002 case IDC_KEYSSH2ECDSA:
1003 case IDC_KEYSSH2ED25519:
1005 state = (struct MainDlgState *)
1006 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1007 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1008 CheckRadioButton(hwnd,
1009 IDC_KEYSSH1, IDC_KEYSSH2ED25519,
1011 CheckMenuRadioItem(state->keymenu,
1012 IDC_KEYSSH1, IDC_KEYSSH2ED25519,
1013 LOWORD(wParam), MF_BYCOMMAND);
1017 PostMessage(hwnd, WM_CLOSE, 0, 0);
1019 case IDC_COMMENTEDIT:
1020 if (HIWORD(wParam) == EN_CHANGE) {
1021 state = (struct MainDlgState *)
1022 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1023 if (state->key_exists) {
1024 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1025 int len = GetWindowTextLength(editctl);
1026 if (*state->commentptr)
1027 sfree(*state->commentptr);
1028 *state->commentptr = snewn(len + 1, char);
1029 GetWindowText(editctl, *state->commentptr, len + 1);
1031 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1034 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1041 EnableWindow(hwnd, 0);
1042 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1043 EnableWindow(hwnd, 1);
1044 SetActiveWindow(hwnd);
1047 if (HIWORD(wParam) == BN_CLICKED ||
1048 HIWORD(wParam) == BN_DOUBLECLICKED) {
1049 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1053 if (HIWORD(wParam) != BN_CLICKED &&
1054 HIWORD(wParam) != BN_DOUBLECLICKED)
1057 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1058 if (!state->generation_thread_exists) {
1060 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1062 state->keysize = DEFAULT_KEYSIZE;
1063 /* If we ever introduce a new key type, check it here! */
1064 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1065 state->keytype = RSA;
1066 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1067 state->keytype = DSA;
1068 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) {
1069 state->keytype = ECDSA;
1070 } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) {
1071 state->keytype = ED25519;
1073 if (state->keysize < 256) {
1074 int ret = MessageBox(hwnd,
1075 "PuTTYgen will not generate a key"
1076 " smaller than 256 bits.\n"
1077 "Key length reset to 256. Continue?",
1079 MB_ICONWARNING | MB_OKCANCEL);
1082 state->keysize = 256;
1083 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1085 if (state->keytype == ECDSA && !(state->keysize == 256 ||
1086 state->keysize == 384 ||
1087 state->keysize == 521)) {
1088 int ret = MessageBox(hwnd,
1089 "Only 256, 384 and 521 bit elliptic"
1090 " curves are supported.\n"
1091 "Key length reset to 256. Continue?",
1093 MB_ICONWARNING | MB_OKCANCEL);
1096 state->keysize = 256;
1097 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1099 if (state->keytype == ED25519 && state->keysize != 256) {
1100 int ret = MessageBox(hwnd,
1101 "Only 256 bit Edwards elliptic"
1102 " curves are supported.\n"
1103 "Key length reset to 256. Continue?",
1105 MB_ICONWARNING | MB_OKCANCEL);
1108 state->keysize = 256;
1109 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1111 ui_set_state(hwnd, state, 1);
1112 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1113 state->key_exists = FALSE;
1114 state->collecting_entropy = TRUE;
1117 * My brief statistical tests on mouse movements
1118 * suggest that there are about 2.5 bits of
1119 * randomness in the x position, 2.5 in the y
1120 * position, and 1.7 in the message time, making
1121 * 5.7 bits of unpredictability per mouse movement.
1122 * However, other people have told me it's far less
1123 * than that, so I'm going to be stupidly cautious
1124 * and knock that down to a nice round 2. With this
1125 * method, we require two words per mouse movement,
1126 * so with 2 bits per mouse movement we expect 2
1127 * bits every 2 words.
1129 state->entropy_required = (state->keysize / 2) * 2;
1130 state->entropy_got = 0;
1131 state->entropy_size = (state->entropy_required *
1133 state->entropy = snewn(state->entropy_required, unsigned);
1135 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1136 MAKELPARAM(0, state->entropy_required));
1137 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1141 case IDC_EXPORT_OPENSSH_AUTO:
1142 case IDC_EXPORT_OPENSSH_NEW:
1143 case IDC_EXPORT_SSHCOM:
1144 if (HIWORD(wParam) != BN_CLICKED)
1147 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1148 if (state->key_exists) {
1149 char filename[FILENAME_MAX];
1150 char *passphrase, *passphrase2;
1154 realtype = SSH_KEYTYPE_SSH2;
1156 realtype = SSH_KEYTYPE_SSH1;
1158 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO)
1159 type = SSH_KEYTYPE_OPENSSH_AUTO;
1160 else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW)
1161 type = SSH_KEYTYPE_OPENSSH_NEW;
1162 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1163 type = SSH_KEYTYPE_SSHCOM;
1167 if (type != realtype &&
1168 import_target_type(type) != realtype) {
1170 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1171 " format", (state->ssh2 ? 2 : 1),
1172 (state->ssh2 ? 1 : 2));
1173 MessageBox(hwnd, msg,
1174 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1178 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1179 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1180 if (strcmp(passphrase, passphrase2)) {
1182 "The two passphrases given do not match.",
1183 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1184 burnstr(passphrase);
1185 burnstr(passphrase2);
1188 burnstr(passphrase2);
1191 ret = MessageBox(hwnd,
1192 "Are you sure you want to save this key\n"
1193 "without a passphrase to protect it?",
1195 MB_YESNO | MB_ICONWARNING);
1197 burnstr(passphrase);
1201 if (prompt_keyfile(hwnd, "Save private key as:",
1202 filename, 1, (type == realtype))) {
1204 FILE *fp = fopen(filename, "r");
1208 buffer = dupprintf("Overwrite existing file\n%s?",
1210 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1211 MB_YESNO | MB_ICONWARNING);
1214 burnstr(passphrase);
1220 Filename *fn = filename_from_str(filename);
1221 if (type != realtype)
1222 ret = export_ssh2(fn, type, &state->ssh2key,
1223 *passphrase ? passphrase : NULL);
1225 ret = ssh2_save_userkey(fn, &state->ssh2key,
1226 *passphrase ? passphrase :
1230 Filename *fn = filename_from_str(filename);
1231 if (type != realtype)
1232 ret = export_ssh1(fn, type, &state->key,
1233 *passphrase ? passphrase : NULL);
1235 ret = saversakey(fn, &state->key,
1236 *passphrase ? passphrase : NULL);
1240 MessageBox(hwnd, "Unable to save key file",
1241 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1244 burnstr(passphrase);
1248 if (HIWORD(wParam) != BN_CLICKED)
1251 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1252 if (state->key_exists) {
1253 char filename[FILENAME_MAX];
1254 if (prompt_keyfile(hwnd, "Save public key as:",
1257 FILE *fp = fopen(filename, "r");
1261 buffer = dupprintf("Overwrite existing file\n%s?",
1263 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1264 MB_YESNO | MB_ICONWARNING);
1269 fp = fopen(filename, "w");
1271 MessageBox(hwnd, "Unable to open key file",
1272 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1276 unsigned char *blob;
1277 blob = state->ssh2key.alg->public_blob
1278 (state->ssh2key.data, &bloblen);
1279 ssh2_write_pubkey(fp, state->ssh2key.comment,
1281 SSH_KEYTYPE_SSH2_PUBLIC_RFC4716);
1283 ssh1_write_pubkey(fp, &state->key);
1285 if (fclose(fp) < 0) {
1286 MessageBox(hwnd, "Unable to save key file",
1287 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1295 if (HIWORD(wParam) != BN_CLICKED)
1298 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1299 if (!state->generation_thread_exists) {
1300 char filename[FILENAME_MAX];
1301 if (prompt_keyfile(hwnd, "Load private key:",
1302 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1303 Filename *fn = filename_from_str(filename);
1304 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1312 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1313 state->generation_thread_exists = FALSE;
1314 state->key_exists = TRUE;
1315 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1316 MAKELPARAM(0, PROGRESSRANGE));
1317 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1319 if (state->keytype == DSA) {
1320 state->ssh2key.data = &state->dsskey;
1321 state->ssh2key.alg = &ssh_dss;
1322 } else if (state->keytype == ECDSA) {
1323 state->ssh2key.data = &state->eckey;
1324 state->ssh2key.alg = state->eckey.signalg;
1325 } else if (state->keytype == ED25519) {
1326 state->ssh2key.data = &state->eckey;
1327 state->ssh2key.alg = &ssh_ecdsa_ed25519;
1329 state->ssh2key.data = &state->key;
1330 state->ssh2key.alg = &ssh_rsa;
1332 state->commentptr = &state->ssh2key.comment;
1334 state->commentptr = &state->key.comment;
1337 * Invent a comment for the key. We'll do this by including
1338 * the date in it. This will be so horrifyingly ugly that
1339 * the user will immediately want to change it, which is
1342 *state->commentptr = snewn(30, char);
1346 if (state->keytype == DSA)
1347 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1348 else if (state->keytype == ECDSA)
1349 strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm);
1350 else if (state->keytype == ED25519)
1351 strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm);
1353 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1357 * Now update the key controls with all the key data.
1362 * Blank passphrase, initially. This isn't dangerous,
1363 * because we will warn (Are You Sure?) before allowing
1364 * the user to save an unprotected private key.
1366 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1367 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1371 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1373 * Set the key fingerprint.
1375 savecomment = *state->commentptr;
1376 *state->commentptr = NULL;
1379 fp = ssh2_fingerprint(state->ssh2key.alg, state->ssh2key.data);
1380 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1384 rsa_fingerprint(buf, sizeof(buf), &state->key);
1385 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1387 *state->commentptr = savecomment;
1389 * Construct a decimal representation of the key, for
1390 * pasting into .ssh/authorized_keys or
1391 * .ssh/authorized_keys2 on a Unix box.
1394 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1395 IDC_PKSTATIC, &state->ssh2key);
1397 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1398 IDC_PKSTATIC, &state->key);
1402 * Finally, hide the progress bar and show the key data.
1404 ui_set_state(hwnd, state, 2);
1408 int id = ((LPHELPINFO)lParam)->iCtrlId;
1409 const char *topic = NULL;
1411 case IDC_GENERATING:
1415 topic = WINHELP_CTX_puttygen_generate; break;
1417 case IDC_KEYDISPLAY:
1418 topic = WINHELP_CTX_puttygen_pastekey; break;
1420 case IDC_FINGERPRINT:
1421 topic = WINHELP_CTX_puttygen_fingerprint; break;
1422 case IDC_COMMENTSTATIC:
1423 case IDC_COMMENTEDIT:
1424 topic = WINHELP_CTX_puttygen_comment; break;
1425 case IDC_PASSPHRASE1STATIC:
1426 case IDC_PASSPHRASE1EDIT:
1427 case IDC_PASSPHRASE2STATIC:
1428 case IDC_PASSPHRASE2EDIT:
1429 topic = WINHELP_CTX_puttygen_passphrase; break;
1430 case IDC_LOADSTATIC:
1432 topic = WINHELP_CTX_puttygen_load; break;
1433 case IDC_SAVESTATIC:
1435 topic = WINHELP_CTX_puttygen_savepriv; break;
1437 topic = WINHELP_CTX_puttygen_savepub; break;
1438 case IDC_TYPESTATIC:
1440 case IDC_KEYSSH2RSA:
1441 case IDC_KEYSSH2DSA:
1442 case IDC_KEYSSH2ECDSA:
1443 case IDC_KEYSSH2ED25519:
1444 topic = WINHELP_CTX_puttygen_keytype; break;
1445 case IDC_BITSSTATIC:
1447 topic = WINHELP_CTX_puttygen_bits; break;
1449 case IDC_EXPORT_OPENSSH_AUTO:
1450 case IDC_EXPORT_OPENSSH_NEW:
1451 case IDC_EXPORT_SSHCOM:
1452 topic = WINHELP_CTX_puttygen_conversions; break;
1455 launch_help(hwnd, topic);
1462 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1471 void cleanup_exit(int code)
1477 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1483 InitCommonControls();
1488 * See if we can find our Help file.
1492 split_into_argv(cmdline, &argc, &argv, NULL);
1495 if (!strcmp(argv[0], "-pgpfp")) {
1500 * Assume the first argument to be a private key file, and
1501 * attempt to load it.
1503 cmdline_keyfile = argv[0];
1508 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1511 return ret; /* just in case optimiser complains */