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(char *fmt, ...)
36 stuff = dupvprintf(fmt, ap);
38 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
39 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
44 /* ----------------------------------------------------------------------
45 * Progress report code. This is really horrible :-)
47 #define PROGRESSRANGE 65535
53 unsigned startpoint, total;
54 unsigned param, current, n; /* if exponential */
55 unsigned mult; /* if linear */
57 unsigned total, divisor, range;
61 static void progress_update(void *param, int action, int phase, int iprogress)
63 struct progress *p = (struct progress *) param;
64 unsigned progress = iprogress;
67 if (action < PROGFN_READY && p->nphases < phase)
70 case PROGFN_INITIALISE:
73 case PROGFN_LIN_PHASE:
74 p->phases[phase-1].exponential = 0;
75 p->phases[phase-1].mult = p->phases[phase].total / progress;
77 case PROGFN_EXP_PHASE:
78 p->phases[phase-1].exponential = 1;
79 p->phases[phase-1].param = 0x10000 + progress;
80 p->phases[phase-1].current = p->phases[phase-1].total;
81 p->phases[phase-1].n = 0;
83 case PROGFN_PHASE_EXTENT:
84 p->phases[phase-1].total = progress;
90 for (i = 0; i < p->nphases; i++) {
91 p->phases[i].startpoint = total;
92 total += p->phases[i].total;
95 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
96 p->range = p->total / p->divisor;
97 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
100 case PROGFN_PROGRESS:
101 if (p->phases[phase-1].exponential) {
102 while (p->phases[phase-1].n < progress) {
103 p->phases[phase-1].n++;
104 p->phases[phase-1].current *= p->phases[phase-1].param;
105 p->phases[phase-1].current /= 0x10000;
107 position = (p->phases[phase-1].startpoint +
108 p->phases[phase-1].total - p->phases[phase-1].current);
110 position = (p->phases[phase-1].startpoint +
111 progress * p->phases[phase-1].mult);
113 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
120 struct PassphraseProcStruct {
126 * Dialog-box function for the passphrase box.
128 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
129 WPARAM wParam, LPARAM lParam)
131 static char **passphrase = NULL;
132 struct PassphraseProcStruct *p;
136 SetForegroundWindow(hwnd);
137 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
138 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
143 { /* centre the window */
147 hw = GetDesktopWindow();
148 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
150 (rs.right + rs.left + rd.left - rd.right) / 2,
151 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
152 rd.right - rd.left, rd.bottom - rd.top, TRUE);
155 p = (struct PassphraseProcStruct *) lParam;
156 passphrase = p->passphrase;
158 SetDlgItemText(hwnd, 101, p->comment);
159 burnstr(*passphrase);
160 *passphrase = dupstr("");
161 SetDlgItemText(hwnd, 102, *passphrase);
164 switch (LOWORD(wParam)) {
174 case 102: /* edit box */
175 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
176 burnstr(*passphrase);
177 *passphrase = GetDlgItemText_alloc(hwnd, 102);
190 * Prompt for a key file. Assumes the filename buffer is of size
193 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
194 char *filename, int save, int ppk)
197 memset(&of, 0, sizeof(of));
200 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
201 "All Files (*.*)\0*\0\0\0";
202 of.lpstrDefExt = ".ppk";
204 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
206 of.lpstrCustomFilter = NULL;
208 of.lpstrFile = filename;
210 of.nMaxFile = FILENAME_MAX;
211 of.lpstrFileTitle = NULL;
212 of.lpstrTitle = dlgtitle;
214 return request_file(NULL, &of, FALSE, save);
218 * Dialog-box function for the Licence box.
220 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
221 WPARAM wParam, LPARAM lParam)
228 { /* centre the window */
232 hw = GetDesktopWindow();
233 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
235 (rs.right + rs.left + rd.left - rd.right) / 2,
236 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
237 rd.right - rd.left, rd.bottom - rd.top, TRUE);
242 switch (LOWORD(wParam)) {
257 * Dialog-box function for the About box.
259 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
260 WPARAM wParam, LPARAM lParam)
267 { /* centre the window */
271 hw = GetDesktopWindow();
272 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
274 (rs.right + rs.left + rd.left - rd.right) / 2,
275 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
276 rd.right - rd.left, rd.bottom - rd.top, TRUE);
279 SetDlgItemText(hwnd, 100, ver);
282 switch (LOWORD(wParam)) {
288 EnableWindow(hwnd, 0);
289 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
290 EnableWindow(hwnd, 1);
291 SetActiveWindow(hwnd);
303 * Thread to generate a key.
305 struct rsa_key_thread_params {
306 HWND progressbar; /* notify this with progress */
307 HWND dialog; /* notify this on completion */
308 int keysize; /* bits in key */
311 struct dss_key *dsskey;
313 static DWORD WINAPI generate_rsa_key_thread(void *param)
315 struct rsa_key_thread_params *params =
316 (struct rsa_key_thread_params *) param;
317 struct progress prog;
318 prog.progbar = params->progressbar;
320 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
323 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
325 rsa_generate(params->key, params->keysize, progress_update, &prog);
327 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
333 struct MainDlgState {
334 int collecting_entropy;
335 int generation_thread_exists;
337 int entropy_got, entropy_required, entropy_size;
340 char **commentptr; /* points to key.comment or ssh2key.comment */
341 struct ssh2_userkey ssh2key;
344 struct dss_key dsskey;
345 HMENU filemenu, keymenu, cvtmenu;
348 static void hidemany(HWND hwnd, const int *ids, int hideit)
351 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
355 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
360 dec1 = bignum_decimal(key->exponent);
361 dec2 = bignum_decimal(key->modulus);
362 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
363 dec1, dec2, key->comment);
364 SetDlgItemText(hwnd, id, buffer);
365 SetDlgItemText(hwnd, idstatic,
366 "&Public key for pasting into authorized_keys file:");
372 static void setupbigedit2(HWND hwnd, int id, int idstatic,
373 struct ssh2_userkey *key)
375 unsigned char *pub_blob;
380 pub_blob = key->alg->public_blob(key->data, &pub_len);
381 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
382 strlen(key->comment) + 3, char);
383 strcpy(buffer, key->alg->name);
384 p = buffer + strlen(buffer);
387 while (i < pub_len) {
388 int n = (pub_len - i < 3 ? pub_len - i : 3);
389 base64_encode_atom(pub_blob + i, n, p);
394 strcpy(p, key->comment);
395 SetDlgItemText(hwnd, id, buffer);
396 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
397 "OpenSSH authorized_keys file:");
402 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
407 dec1 = bignum_decimal(key->exponent);
408 dec2 = bignum_decimal(key->modulus);
409 fp = fopen(filename, "wb");
412 fprintf(fp, "%d %s %s %s\n",
413 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
421 * Warn about the obsolescent key file format.
423 void old_keyfile_warning(void)
425 static const char mbtitle[] = "PuTTY Key File Warning";
426 static const char message[] =
427 "You are loading an SSH-2 private key which has an\n"
428 "old version of the file format. This means your key\n"
429 "file is not fully tamperproof. Future versions of\n"
430 "PuTTY may stop supporting this private key format,\n"
431 "so we recommend you convert your key to the new\n"
434 "Once the key is loaded into PuTTYgen, you can perform\n"
435 "this conversion simply by saving it again.";
437 MessageBox(NULL, message, mbtitle, MB_OK);
440 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
442 unsigned char *pub_blob;
448 pub_blob = key->alg->public_blob(key->data, &pub_len);
450 fp = fopen(filename, "wb");
454 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
456 fprintf(fp, "Comment: \"");
457 for (p = key->comment; *p; p++) {
458 if (*p == '\\' || *p == '\"')
466 while (i < pub_len) {
468 int n = (pub_len - i < 3 ? pub_len - i : 3);
469 base64_encode_atom(pub_blob + i, n, buf);
473 if (++column >= 16) {
481 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
488 controlidstart = 100,
495 IDC_PKSTATIC, IDC_KEYDISPLAY,
496 IDC_FPSTATIC, IDC_FINGERPRINT,
497 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
498 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
499 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
501 IDC_GENSTATIC, IDC_GENERATE,
502 IDC_LOADSTATIC, IDC_LOAD,
503 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
505 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
506 IDC_BITSSTATIC, IDC_BITS,
509 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
512 static const int nokey_ids[] = { IDC_NOKEY, 0 };
513 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
514 static const int gotkey_ids[] = {
515 IDC_PKSTATIC, IDC_KEYDISPLAY,
516 IDC_FPSTATIC, IDC_FINGERPRINT,
517 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
518 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
519 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
523 * Small UI helper function to switch the state of the main dialog
524 * by enabling and disabling controls and menu items.
526 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
532 hidemany(hwnd, nokey_ids, FALSE);
533 hidemany(hwnd, generating_ids, TRUE);
534 hidemany(hwnd, gotkey_ids, TRUE);
535 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
536 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
537 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
538 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
539 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
540 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
541 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
542 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
543 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
544 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
545 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
546 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
547 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
548 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
549 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
550 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
551 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
552 MF_GRAYED|MF_BYCOMMAND);
553 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
554 MF_GRAYED|MF_BYCOMMAND);
556 case 1: /* generating key */
557 hidemany(hwnd, nokey_ids, TRUE);
558 hidemany(hwnd, generating_ids, FALSE);
559 hidemany(hwnd, gotkey_ids, TRUE);
560 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
561 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
562 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
563 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
564 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
565 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
566 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
567 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
568 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
569 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
570 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
571 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
572 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
573 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
574 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
575 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
576 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
577 MF_GRAYED|MF_BYCOMMAND);
578 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
579 MF_GRAYED|MF_BYCOMMAND);
582 hidemany(hwnd, nokey_ids, TRUE);
583 hidemany(hwnd, generating_ids, TRUE);
584 hidemany(hwnd, gotkey_ids, FALSE);
585 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
586 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
587 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
588 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
589 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
590 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
591 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
592 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
593 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
594 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
595 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
596 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
597 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
598 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
599 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
600 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
602 * Enable export menu items if and only if the key type
603 * supports this kind of export.
605 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
606 #define do_export_menuitem(x,y) \
607 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
608 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
609 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
610 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
611 #undef do_export_menuitem
616 void load_key_file(HWND hwnd, struct MainDlgState *state,
617 Filename *filename, int was_import_cmd)
623 const char *errmsg = NULL;
625 struct RSAKey newkey1;
626 struct ssh2_userkey *newkey2 = NULL;
628 type = realtype = key_type(filename);
629 if (type != SSH_KEYTYPE_SSH1 &&
630 type != SSH_KEYTYPE_SSH2 &&
631 !import_possible(type)) {
632 char *msg = dupprintf("Couldn't load private key (%s)",
633 key_type_to_str(type));
634 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
635 HELPCTXID(errors_cantloadkey));
640 if (type != SSH_KEYTYPE_SSH1 &&
641 type != SSH_KEYTYPE_SSH2) {
643 type = import_target_type(type);
648 if (realtype == SSH_KEYTYPE_SSH1)
649 needs_pass = rsakey_encrypted(filename, &comment);
650 else if (realtype == SSH_KEYTYPE_SSH2)
651 needs_pass = ssh2_userkey_encrypted(filename, &comment);
653 needs_pass = import_encrypted(filename, realtype, &comment);
660 struct PassphraseProcStruct pps;
661 pps.passphrase = &passphrase;
662 pps.comment = comment;
663 dlgret = DialogBoxParam(hinst,
664 MAKEINTRESOURCE(210),
665 NULL, PassphraseProc,
671 assert(passphrase != NULL);
673 passphrase = dupstr("");
674 if (type == SSH_KEYTYPE_SSH1) {
675 if (realtype == type)
676 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
678 ret = import_ssh1(filename, realtype, &newkey1,
679 passphrase, &errmsg);
681 if (realtype == type)
682 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
684 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
685 if (newkey2 == SSH2_WRONG_PASSPHRASE)
696 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
697 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
698 HELPCTXID(errors_cantloadkey));
700 } else if (ret == 1) {
702 * Now update the key controls with all the
706 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
708 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
710 if (type == SSH_KEYTYPE_SSH1) {
715 state->commentptr = &state->key.comment;
716 state->key = newkey1;
719 * Set the key fingerprint.
721 savecomment = state->key.comment;
722 state->key.comment = NULL;
723 rsa_fingerprint(buf, sizeof(buf),
725 state->key.comment = savecomment;
727 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
729 * Construct a decimal representation
730 * of the key, for pasting into
731 * .ssh/authorized_keys on a Unix box.
733 setupbigedit1(hwnd, IDC_KEYDISPLAY,
734 IDC_PKSTATIC, &state->key);
741 &state->ssh2key.comment;
742 state->ssh2key = *newkey2; /* structure copy */
745 savecomment = state->ssh2key.comment;
746 state->ssh2key.comment = NULL;
749 fingerprint(state->ssh2key.data);
750 state->ssh2key.comment = savecomment;
752 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
755 setupbigedit2(hwnd, IDC_KEYDISPLAY,
756 IDC_PKSTATIC, &state->ssh2key);
758 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
762 * Finally, hide the progress bar and show
765 ui_set_state(hwnd, state, 2);
766 state->key_exists = TRUE;
769 * If the user has imported a foreign key
770 * using the Load command, let them know.
771 * If they've used the Import command, be
774 if (realtype != type && !was_import_cmd) {
776 sprintf(msg, "Successfully imported foreign key\n"
778 "To use this key with PuTTY, you need to\n"
779 "use the \"Save private key\" command to\n"
780 "save it in PuTTY's own format.",
781 key_type_to_str(realtype));
782 MessageBox(NULL, msg, "PuTTYgen Notice",
783 MB_OK | MB_ICONINFORMATION);
790 * Dialog-box function for the main PuTTYgen dialog box.
792 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
793 WPARAM wParam, LPARAM lParam)
795 static const char generating_msg[] =
796 "Please wait while a key is generated...";
797 static const char entropy_msg[] =
798 "Please generate some randomness by moving the mouse over the blank area.";
799 struct MainDlgState *state;
804 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
805 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
809 * If we add a Help button, this is where we destroy it
810 * if the help file isn't present.
813 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
814 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
816 state = snew(struct MainDlgState);
817 state->generation_thread_exists = FALSE;
818 state->collecting_entropy = FALSE;
819 state->entropy = NULL;
820 state->key_exists = FALSE;
821 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
827 menu1 = CreateMenu();
828 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
829 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
830 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
831 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
832 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
833 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
834 state->filemenu = menu1;
836 menu1 = CreateMenu();
837 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
838 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
839 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
840 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
841 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
842 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
843 state->keymenu = menu1;
845 menu1 = CreateMenu();
846 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
847 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
848 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
849 "Export &OpenSSH key");
850 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
851 "Export &ssh.com key");
852 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
854 state->cvtmenu = menu1;
856 menu1 = CreateMenu();
857 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
859 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
860 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
868 { /* centre the window */
872 hw = GetDesktopWindow();
873 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
875 (rs.right + rs.left + rd.left - rd.right) / 2,
876 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
877 rd.right - rd.left, rd.bottom - rd.top, TRUE);
881 struct ctlpos cp, cp2;
883 /* Accelerators used: acglops1rbd */
885 ctlposinit(&cp, hwnd, 4, 4, 4);
886 beginbox(&cp, "Key", IDC_BOX_KEY);
888 statictext(&cp2, "No key.", 1, IDC_NOKEY);
890 statictext(&cp2, "", 1, IDC_GENERATING);
891 progressbar(&cp2, IDC_PROGRESS);
893 "&Public key for pasting into authorized_keys file:",
894 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
895 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
896 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
897 IDC_FINGERPRINT, 75);
898 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
900 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
901 IDC_COMMENTEDIT, 75);
902 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
903 IDC_PASSPHRASE1EDIT, 75);
904 staticpassedit(&cp, "C&onfirm passphrase:",
905 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
907 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
908 staticbtn(&cp, "Generate a public/private key pair",
909 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
910 staticbtn(&cp, "Load an existing private key file",
911 IDC_LOADSTATIC, "&Load", IDC_LOAD);
912 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
913 "Save p&ublic key", IDC_SAVEPUB,
914 "&Save private key", IDC_SAVE);
916 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
917 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
918 "SSH-&1 (RSA)", IDC_KEYSSH1,
919 "SSH-2 &RSA", IDC_KEYSSH2RSA,
920 "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
921 staticedit(&cp, "Number of &bits in a generated key:",
922 IDC_BITSSTATIC, IDC_BITS, 20);
925 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
926 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
927 IDC_KEYSSH2RSA, MF_BYCOMMAND);
928 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
931 * Initially, hide the progress bar and the key display,
932 * and show the no-key display. Also disable the Save
933 * buttons, because with no key we obviously can't save
936 ui_set_state(hwnd, state, 0);
939 * Load a key file if one was provided on the command line.
942 load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
946 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
947 if (state->collecting_entropy &&
948 state->entropy && state->entropy_got < state->entropy_required) {
949 state->entropy[state->entropy_got++] = lParam;
950 state->entropy[state->entropy_got++] = GetMessageTime();
951 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
952 state->entropy_got, 0);
953 if (state->entropy_got >= state->entropy_required) {
954 struct rsa_key_thread_params *params;
958 * Seed the entropy pool
960 random_add_heavynoise(state->entropy, state->entropy_size);
961 smemclr(state->entropy, state->entropy_size);
962 sfree(state->entropy);
963 state->collecting_entropy = FALSE;
965 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
966 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
967 MAKELPARAM(0, PROGRESSRANGE));
968 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
970 params = snew(struct rsa_key_thread_params);
971 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
972 params->dialog = hwnd;
973 params->keysize = state->keysize;
974 params->is_dsa = state->is_dsa;
975 params->key = &state->key;
976 params->dsskey = &state->dsskey;
978 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
979 params, 0, &threadid)) {
980 MessageBox(hwnd, "Out of thread resources",
981 "Key generation error",
982 MB_OK | MB_ICONERROR);
985 state->generation_thread_exists = TRUE;
991 switch (LOWORD(wParam)) {
996 state = (struct MainDlgState *)
997 GetWindowLongPtr(hwnd, GWLP_USERDATA);
998 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
999 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1001 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1002 LOWORD(wParam), MF_BYCOMMAND);
1006 PostMessage(hwnd, WM_CLOSE, 0, 0);
1008 case IDC_COMMENTEDIT:
1009 if (HIWORD(wParam) == EN_CHANGE) {
1010 state = (struct MainDlgState *)
1011 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1012 if (state->key_exists) {
1013 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1014 int len = GetWindowTextLength(editctl);
1015 if (*state->commentptr)
1016 sfree(*state->commentptr);
1017 *state->commentptr = snewn(len + 1, char);
1018 GetWindowText(editctl, *state->commentptr, len + 1);
1020 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1023 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1030 EnableWindow(hwnd, 0);
1031 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1032 EnableWindow(hwnd, 1);
1033 SetActiveWindow(hwnd);
1036 if (HIWORD(wParam) == BN_CLICKED ||
1037 HIWORD(wParam) == BN_DOUBLECLICKED) {
1038 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1042 if (HIWORD(wParam) != BN_CLICKED &&
1043 HIWORD(wParam) != BN_DOUBLECLICKED)
1046 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1047 if (!state->generation_thread_exists) {
1049 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1051 state->keysize = DEFAULT_KEYSIZE;
1052 /* If we ever introduce a new key type, check it here! */
1053 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1054 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1055 if (state->keysize < 256) {
1056 int ret = MessageBox(hwnd,
1057 "PuTTYgen will not generate a key"
1058 " smaller than 256 bits.\n"
1059 "Key length reset to 256. Continue?",
1061 MB_ICONWARNING | MB_OKCANCEL);
1064 state->keysize = 256;
1065 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1067 ui_set_state(hwnd, state, 1);
1068 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1069 state->key_exists = FALSE;
1070 state->collecting_entropy = TRUE;
1073 * My brief statistical tests on mouse movements
1074 * suggest that there are about 2.5 bits of
1075 * randomness in the x position, 2.5 in the y
1076 * position, and 1.7 in the message time, making
1077 * 5.7 bits of unpredictability per mouse movement.
1078 * However, other people have told me it's far less
1079 * than that, so I'm going to be stupidly cautious
1080 * and knock that down to a nice round 2. With this
1081 * method, we require two words per mouse movement,
1082 * so with 2 bits per mouse movement we expect 2
1083 * bits every 2 words.
1085 state->entropy_required = (state->keysize / 2) * 2;
1086 state->entropy_got = 0;
1087 state->entropy_size = (state->entropy_required *
1089 state->entropy = snewn(state->entropy_required, unsigned);
1091 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1092 MAKELPARAM(0, state->entropy_required));
1093 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1097 case IDC_EXPORT_OPENSSH:
1098 case IDC_EXPORT_SSHCOM:
1099 if (HIWORD(wParam) != BN_CLICKED)
1102 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1103 if (state->key_exists) {
1104 char filename[FILENAME_MAX];
1105 char *passphrase, *passphrase2;
1109 realtype = SSH_KEYTYPE_SSH2;
1111 realtype = SSH_KEYTYPE_SSH1;
1113 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1114 type = SSH_KEYTYPE_OPENSSH;
1115 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1116 type = SSH_KEYTYPE_SSHCOM;
1120 if (type != realtype &&
1121 import_target_type(type) != realtype) {
1123 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1124 " format", (state->ssh2 ? 2 : 1),
1125 (state->ssh2 ? 1 : 2));
1126 MessageBox(hwnd, msg,
1127 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1131 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1132 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1133 if (strcmp(passphrase, passphrase2)) {
1135 "The two passphrases given do not match.",
1136 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1137 burnstr(passphrase);
1138 burnstr(passphrase2);
1141 burnstr(passphrase2);
1144 ret = MessageBox(hwnd,
1145 "Are you sure you want to save this key\n"
1146 "without a passphrase to protect it?",
1148 MB_YESNO | MB_ICONWARNING);
1150 burnstr(passphrase);
1154 if (prompt_keyfile(hwnd, "Save private key as:",
1155 filename, 1, (type == realtype))) {
1157 FILE *fp = fopen(filename, "r");
1161 buffer = dupprintf("Overwrite existing file\n%s?",
1163 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1164 MB_YESNO | MB_ICONWARNING);
1167 burnstr(passphrase);
1173 Filename *fn = filename_from_str(filename);
1174 if (type != realtype)
1175 ret = export_ssh2(fn, type, &state->ssh2key,
1176 *passphrase ? passphrase : NULL);
1178 ret = ssh2_save_userkey(fn, &state->ssh2key,
1179 *passphrase ? passphrase :
1183 Filename *fn = filename_from_str(filename);
1184 if (type != realtype)
1185 ret = export_ssh1(fn, type, &state->key,
1186 *passphrase ? passphrase : NULL);
1188 ret = saversakey(fn, &state->key,
1189 *passphrase ? passphrase : NULL);
1193 MessageBox(hwnd, "Unable to save key file",
1194 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1197 burnstr(passphrase);
1201 if (HIWORD(wParam) != BN_CLICKED)
1204 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1205 if (state->key_exists) {
1206 char filename[FILENAME_MAX];
1207 if (prompt_keyfile(hwnd, "Save public key as:",
1210 FILE *fp = fopen(filename, "r");
1214 buffer = dupprintf("Overwrite existing file\n%s?",
1216 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1217 MB_YESNO | MB_ICONWARNING);
1223 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1225 ret = save_ssh1_pubkey(filename, &state->key);
1228 MessageBox(hwnd, "Unable to save key file",
1229 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1236 if (HIWORD(wParam) != BN_CLICKED)
1239 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1240 if (!state->generation_thread_exists) {
1241 char filename[FILENAME_MAX];
1242 if (prompt_keyfile(hwnd, "Load private key:",
1243 filename, 0, LOWORD(wParam)==IDC_LOAD))
1244 load_key_file(hwnd, state, filename_from_str(filename),
1245 LOWORD(wParam) != IDC_LOAD);
1251 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1252 state->generation_thread_exists = FALSE;
1253 state->key_exists = TRUE;
1254 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1255 MAKELPARAM(0, PROGRESSRANGE));
1256 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1258 if (state->is_dsa) {
1259 state->ssh2key.data = &state->dsskey;
1260 state->ssh2key.alg = &ssh_dss;
1262 state->ssh2key.data = &state->key;
1263 state->ssh2key.alg = &ssh_rsa;
1265 state->commentptr = &state->ssh2key.comment;
1267 state->commentptr = &state->key.comment;
1270 * Invent a comment for the key. We'll do this by including
1271 * the date in it. This will be so horrifyingly ugly that
1272 * the user will immediately want to change it, which is
1275 *state->commentptr = snewn(30, char);
1280 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1282 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1286 * Now update the key controls with all the key data.
1291 * Blank passphrase, initially. This isn't dangerous,
1292 * because we will warn (Are You Sure?) before allowing
1293 * the user to save an unprotected private key.
1295 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1296 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1300 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1302 * Set the key fingerprint.
1304 savecomment = *state->commentptr;
1305 *state->commentptr = NULL;
1308 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1309 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1313 rsa_fingerprint(buf, sizeof(buf), &state->key);
1314 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1316 *state->commentptr = savecomment;
1318 * Construct a decimal representation of the key, for
1319 * pasting into .ssh/authorized_keys or
1320 * .ssh/authorized_keys2 on a Unix box.
1323 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1324 IDC_PKSTATIC, &state->ssh2key);
1326 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1327 IDC_PKSTATIC, &state->key);
1331 * Finally, hide the progress bar and show the key data.
1333 ui_set_state(hwnd, state, 2);
1337 int id = ((LPHELPINFO)lParam)->iCtrlId;
1340 case IDC_GENERATING:
1344 topic = WINHELP_CTX_puttygen_generate; break;
1346 case IDC_KEYDISPLAY:
1347 topic = WINHELP_CTX_puttygen_pastekey; break;
1349 case IDC_FINGERPRINT:
1350 topic = WINHELP_CTX_puttygen_fingerprint; break;
1351 case IDC_COMMENTSTATIC:
1352 case IDC_COMMENTEDIT:
1353 topic = WINHELP_CTX_puttygen_comment; break;
1354 case IDC_PASSPHRASE1STATIC:
1355 case IDC_PASSPHRASE1EDIT:
1356 case IDC_PASSPHRASE2STATIC:
1357 case IDC_PASSPHRASE2EDIT:
1358 topic = WINHELP_CTX_puttygen_passphrase; break;
1359 case IDC_LOADSTATIC:
1361 topic = WINHELP_CTX_puttygen_load; break;
1362 case IDC_SAVESTATIC:
1364 topic = WINHELP_CTX_puttygen_savepriv; break;
1366 topic = WINHELP_CTX_puttygen_savepub; break;
1367 case IDC_TYPESTATIC:
1369 case IDC_KEYSSH2RSA:
1370 case IDC_KEYSSH2DSA:
1371 topic = WINHELP_CTX_puttygen_keytype; break;
1372 case IDC_BITSSTATIC:
1374 topic = WINHELP_CTX_puttygen_bits; break;
1376 case IDC_EXPORT_OPENSSH:
1377 case IDC_EXPORT_SSHCOM:
1378 topic = WINHELP_CTX_puttygen_conversions; break;
1381 launch_help(hwnd, topic);
1388 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1397 void cleanup_exit(int code)
1403 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1409 InitCommonControls();
1414 * See if we can find our Help file.
1418 split_into_argv(cmdline, &argc, &argv, NULL);
1421 if (!strcmp(argv[0], "-pgpfp")) {
1426 * Assume the first argument to be a private key file, and
1427 * attempt to load it.
1429 cmdline_keyfile = argv[0];
1434 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1437 return ret; /* just in case optimiser complains */