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);
45 * Print a non-fatal message box and do not exit.
47 void nonfatal(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 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 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);
258 switch (LOWORD(wParam)) {
273 * Dialog-box function for the About box.
275 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
276 WPARAM wParam, LPARAM lParam)
283 { /* centre the window */
287 hw = GetDesktopWindow();
288 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
290 (rs.right + rs.left + rd.left - rd.right) / 2,
291 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
292 rd.right - rd.left, rd.bottom - rd.top, TRUE);
295 SetDlgItemText(hwnd, 100, ver);
298 switch (LOWORD(wParam)) {
304 EnableWindow(hwnd, 0);
305 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
306 EnableWindow(hwnd, 1);
307 SetActiveWindow(hwnd);
319 * Thread to generate a key.
321 struct rsa_key_thread_params {
322 HWND progressbar; /* notify this with progress */
323 HWND dialog; /* notify this on completion */
324 int keysize; /* bits in key */
327 struct dss_key *dsskey;
329 static DWORD WINAPI generate_rsa_key_thread(void *param)
331 struct rsa_key_thread_params *params =
332 (struct rsa_key_thread_params *) param;
333 struct progress prog;
334 prog.progbar = params->progressbar;
336 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
339 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
341 rsa_generate(params->key, params->keysize, progress_update, &prog);
343 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
349 struct MainDlgState {
350 int collecting_entropy;
351 int generation_thread_exists;
353 int entropy_got, entropy_required, entropy_size;
356 char **commentptr; /* points to key.comment or ssh2key.comment */
357 struct ssh2_userkey ssh2key;
360 struct dss_key dsskey;
361 HMENU filemenu, keymenu, cvtmenu;
364 static void hidemany(HWND hwnd, const int *ids, int hideit)
367 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
371 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
376 dec1 = bignum_decimal(key->exponent);
377 dec2 = bignum_decimal(key->modulus);
378 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
379 dec1, dec2, key->comment);
380 SetDlgItemText(hwnd, id, buffer);
381 SetDlgItemText(hwnd, idstatic,
382 "&Public key for pasting into authorized_keys file:");
388 static void setupbigedit2(HWND hwnd, int id, int idstatic,
389 struct ssh2_userkey *key)
391 unsigned char *pub_blob;
396 pub_blob = key->alg->public_blob(key->data, &pub_len);
397 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
398 strlen(key->comment) + 3, char);
399 strcpy(buffer, key->alg->name);
400 p = buffer + strlen(buffer);
403 while (i < pub_len) {
404 int n = (pub_len - i < 3 ? pub_len - i : 3);
405 base64_encode_atom(pub_blob + i, n, p);
410 strcpy(p, key->comment);
411 SetDlgItemText(hwnd, id, buffer);
412 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
413 "OpenSSH authorized_keys file:");
418 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
423 fp = fopen(filename, "wb");
426 dec1 = bignum_decimal(key->exponent);
427 dec2 = bignum_decimal(key->modulus);
428 fprintf(fp, "%d %s %s %s\n",
429 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
437 * Warn about the obsolescent key file format.
439 void old_keyfile_warning(void)
441 static const char mbtitle[] = "PuTTY Key File Warning";
442 static const char message[] =
443 "You are loading an SSH-2 private key which has an\n"
444 "old version of the file format. This means your key\n"
445 "file is not fully tamperproof. Future versions of\n"
446 "PuTTY may stop supporting this private key format,\n"
447 "so we recommend you convert your key to the new\n"
450 "Once the key is loaded into PuTTYgen, you can perform\n"
451 "this conversion simply by saving it again.";
453 MessageBox(NULL, message, mbtitle, MB_OK);
456 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
458 unsigned char *pub_blob;
464 pub_blob = key->alg->public_blob(key->data, &pub_len);
466 fp = fopen(filename, "wb");
470 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
472 fprintf(fp, "Comment: \"");
473 for (p = key->comment; *p; p++) {
474 if (*p == '\\' || *p == '\"')
482 while (i < pub_len) {
484 int n = (pub_len - i < 3 ? pub_len - i : 3);
485 base64_encode_atom(pub_blob + i, n, buf);
489 if (++column >= 16) {
497 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
504 controlidstart = 100,
511 IDC_PKSTATIC, IDC_KEYDISPLAY,
512 IDC_FPSTATIC, IDC_FINGERPRINT,
513 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
514 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
515 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
517 IDC_GENSTATIC, IDC_GENERATE,
518 IDC_LOADSTATIC, IDC_LOAD,
519 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
521 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
522 IDC_BITSSTATIC, IDC_BITS,
525 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
528 static const int nokey_ids[] = { IDC_NOKEY, 0 };
529 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
530 static const int gotkey_ids[] = {
531 IDC_PKSTATIC, IDC_KEYDISPLAY,
532 IDC_FPSTATIC, IDC_FINGERPRINT,
533 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
534 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
535 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
539 * Small UI helper function to switch the state of the main dialog
540 * by enabling and disabling controls and menu items.
542 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
548 hidemany(hwnd, nokey_ids, FALSE);
549 hidemany(hwnd, generating_ids, TRUE);
550 hidemany(hwnd, gotkey_ids, TRUE);
551 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
553 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
554 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
555 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
556 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
557 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
558 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
559 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
560 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
561 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
562 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
563 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
564 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
565 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
566 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
567 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
568 MF_GRAYED|MF_BYCOMMAND);
569 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
570 MF_GRAYED|MF_BYCOMMAND);
572 case 1: /* generating key */
573 hidemany(hwnd, nokey_ids, TRUE);
574 hidemany(hwnd, generating_ids, FALSE);
575 hidemany(hwnd, gotkey_ids, TRUE);
576 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
577 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
578 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
579 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
580 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
581 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
582 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
583 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
584 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
587 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
588 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
589 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
590 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
591 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
592 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
593 MF_GRAYED|MF_BYCOMMAND);
594 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
595 MF_GRAYED|MF_BYCOMMAND);
598 hidemany(hwnd, nokey_ids, TRUE);
599 hidemany(hwnd, generating_ids, TRUE);
600 hidemany(hwnd, gotkey_ids, FALSE);
601 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
602 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
603 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
604 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
605 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
606 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
607 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
608 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
609 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
610 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
611 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
612 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
613 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
614 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
615 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
616 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
618 * Enable export menu items if and only if the key type
619 * supports this kind of export.
621 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
622 #define do_export_menuitem(x,y) \
623 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
624 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
625 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
626 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
627 #undef do_export_menuitem
632 void load_key_file(HWND hwnd, struct MainDlgState *state,
633 Filename *filename, int was_import_cmd)
639 const char *errmsg = NULL;
641 struct RSAKey newkey1;
642 struct ssh2_userkey *newkey2 = NULL;
644 type = realtype = key_type(filename);
645 if (type != SSH_KEYTYPE_SSH1 &&
646 type != SSH_KEYTYPE_SSH2 &&
647 !import_possible(type)) {
648 char *msg = dupprintf("Couldn't load private key (%s)",
649 key_type_to_str(type));
650 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
651 HELPCTXID(errors_cantloadkey));
656 if (type != SSH_KEYTYPE_SSH1 &&
657 type != SSH_KEYTYPE_SSH2) {
659 type = import_target_type(type);
664 if (realtype == SSH_KEYTYPE_SSH1)
665 needs_pass = rsakey_encrypted(filename, &comment);
666 else if (realtype == SSH_KEYTYPE_SSH2)
667 needs_pass = ssh2_userkey_encrypted(filename, &comment);
669 needs_pass = import_encrypted(filename, realtype, &comment);
676 struct PassphraseProcStruct pps;
677 pps.passphrase = &passphrase;
678 pps.comment = comment;
679 dlgret = DialogBoxParam(hinst,
680 MAKEINTRESOURCE(210),
681 NULL, PassphraseProc,
687 assert(passphrase != NULL);
689 passphrase = dupstr("");
690 if (type == SSH_KEYTYPE_SSH1) {
691 if (realtype == type)
692 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
694 ret = import_ssh1(filename, realtype, &newkey1,
695 passphrase, &errmsg);
697 if (realtype == type)
698 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
700 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
701 if (newkey2 == SSH2_WRONG_PASSPHRASE)
712 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
713 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
714 HELPCTXID(errors_cantloadkey));
716 } else if (ret == 1) {
718 * Now update the key controls with all the
722 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
724 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
726 if (type == SSH_KEYTYPE_SSH1) {
731 state->commentptr = &state->key.comment;
732 state->key = newkey1;
735 * Set the key fingerprint.
737 savecomment = state->key.comment;
738 state->key.comment = NULL;
739 rsa_fingerprint(buf, sizeof(buf),
741 state->key.comment = savecomment;
743 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
745 * Construct a decimal representation
746 * of the key, for pasting into
747 * .ssh/authorized_keys on a Unix box.
749 setupbigedit1(hwnd, IDC_KEYDISPLAY,
750 IDC_PKSTATIC, &state->key);
757 &state->ssh2key.comment;
758 state->ssh2key = *newkey2; /* structure copy */
761 savecomment = state->ssh2key.comment;
762 state->ssh2key.comment = NULL;
765 fingerprint(state->ssh2key.data);
766 state->ssh2key.comment = savecomment;
768 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
771 setupbigedit2(hwnd, IDC_KEYDISPLAY,
772 IDC_PKSTATIC, &state->ssh2key);
774 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
778 * Finally, hide the progress bar and show
781 ui_set_state(hwnd, state, 2);
782 state->key_exists = TRUE;
785 * If the user has imported a foreign key
786 * using the Load command, let them know.
787 * If they've used the Import command, be
790 if (realtype != type && !was_import_cmd) {
792 sprintf(msg, "Successfully imported foreign key\n"
794 "To use this key with PuTTY, you need to\n"
795 "use the \"Save private key\" command to\n"
796 "save it in PuTTY's own format.",
797 key_type_to_str(realtype));
798 MessageBox(NULL, msg, "PuTTYgen Notice",
799 MB_OK | MB_ICONINFORMATION);
806 * Dialog-box function for the main PuTTYgen dialog box.
808 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
809 WPARAM wParam, LPARAM lParam)
811 static const char generating_msg[] =
812 "Please wait while a key is generated...";
813 static const char entropy_msg[] =
814 "Please generate some randomness by moving the mouse over the blank area.";
815 struct MainDlgState *state;
820 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
821 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
825 * If we add a Help button, this is where we destroy it
826 * if the help file isn't present.
829 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
830 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
832 state = snew(struct MainDlgState);
833 state->generation_thread_exists = FALSE;
834 state->collecting_entropy = FALSE;
835 state->entropy = NULL;
836 state->key_exists = FALSE;
837 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
843 menu1 = CreateMenu();
844 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
845 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
846 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
847 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
848 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
849 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
850 state->filemenu = menu1;
852 menu1 = CreateMenu();
853 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
854 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
855 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
856 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
857 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
858 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
859 state->keymenu = menu1;
861 menu1 = CreateMenu();
862 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
863 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
864 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
865 "Export &OpenSSH key");
866 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
867 "Export &ssh.com key");
868 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
870 state->cvtmenu = menu1;
872 menu1 = CreateMenu();
873 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
875 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
876 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
884 { /* centre the window */
888 hw = GetDesktopWindow();
889 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
891 (rs.right + rs.left + rd.left - rd.right) / 2,
892 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
893 rd.right - rd.left, rd.bottom - rd.top, TRUE);
897 struct ctlpos cp, cp2;
899 /* Accelerators used: acglops1rbd */
901 ctlposinit(&cp, hwnd, 4, 4, 4);
902 beginbox(&cp, "Key", IDC_BOX_KEY);
904 statictext(&cp2, "No key.", 1, IDC_NOKEY);
906 statictext(&cp2, "", 1, IDC_GENERATING);
907 progressbar(&cp2, IDC_PROGRESS);
909 "&Public key for pasting into authorized_keys file:",
910 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
911 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
912 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
913 IDC_FINGERPRINT, 75);
914 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
916 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
917 IDC_COMMENTEDIT, 75);
918 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
919 IDC_PASSPHRASE1EDIT, 75);
920 staticpassedit(&cp, "C&onfirm passphrase:",
921 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
923 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
924 staticbtn(&cp, "Generate a public/private key pair",
925 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
926 staticbtn(&cp, "Load an existing private key file",
927 IDC_LOADSTATIC, "&Load", IDC_LOAD);
928 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
929 "Save p&ublic key", IDC_SAVEPUB,
930 "&Save private key", IDC_SAVE);
932 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
933 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
934 "SSH-&1 (RSA)", IDC_KEYSSH1,
935 "SSH-2 &RSA", IDC_KEYSSH2RSA,
936 "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
937 staticedit(&cp, "Number of &bits in a generated key:",
938 IDC_BITSSTATIC, IDC_BITS, 20);
941 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
942 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
943 IDC_KEYSSH2RSA, MF_BYCOMMAND);
944 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
947 * Initially, hide the progress bar and the key display,
948 * and show the no-key display. Also disable the Save
949 * buttons, because with no key we obviously can't save
952 ui_set_state(hwnd, state, 0);
955 * Load a key file if one was provided on the command line.
957 if (cmdline_keyfile) {
958 Filename *fn = filename_from_str(cmdline_keyfile);
959 load_key_file(hwnd, state, fn, 0);
965 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
966 if (state->collecting_entropy &&
967 state->entropy && state->entropy_got < state->entropy_required) {
968 state->entropy[state->entropy_got++] = lParam;
969 state->entropy[state->entropy_got++] = GetMessageTime();
970 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
971 state->entropy_got, 0);
972 if (state->entropy_got >= state->entropy_required) {
973 struct rsa_key_thread_params *params;
977 * Seed the entropy pool
979 random_add_heavynoise(state->entropy, state->entropy_size);
980 smemclr(state->entropy, state->entropy_size);
981 sfree(state->entropy);
982 state->collecting_entropy = FALSE;
984 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
985 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
986 MAKELPARAM(0, PROGRESSRANGE));
987 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
989 params = snew(struct rsa_key_thread_params);
990 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
991 params->dialog = hwnd;
992 params->keysize = state->keysize;
993 params->is_dsa = state->is_dsa;
994 params->key = &state->key;
995 params->dsskey = &state->dsskey;
997 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
998 params, 0, &threadid)) {
999 MessageBox(hwnd, "Out of thread resources",
1000 "Key generation error",
1001 MB_OK | MB_ICONERROR);
1004 state->generation_thread_exists = TRUE;
1010 switch (LOWORD(wParam)) {
1012 case IDC_KEYSSH2RSA:
1013 case IDC_KEYSSH2DSA:
1015 state = (struct MainDlgState *)
1016 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1017 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1018 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1020 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1021 LOWORD(wParam), MF_BYCOMMAND);
1025 PostMessage(hwnd, WM_CLOSE, 0, 0);
1027 case IDC_COMMENTEDIT:
1028 if (HIWORD(wParam) == EN_CHANGE) {
1029 state = (struct MainDlgState *)
1030 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1031 if (state->key_exists) {
1032 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1033 int len = GetWindowTextLength(editctl);
1034 if (*state->commentptr)
1035 sfree(*state->commentptr);
1036 *state->commentptr = snewn(len + 1, char);
1037 GetWindowText(editctl, *state->commentptr, len + 1);
1039 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1042 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1049 EnableWindow(hwnd, 0);
1050 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1051 EnableWindow(hwnd, 1);
1052 SetActiveWindow(hwnd);
1055 if (HIWORD(wParam) == BN_CLICKED ||
1056 HIWORD(wParam) == BN_DOUBLECLICKED) {
1057 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1061 if (HIWORD(wParam) != BN_CLICKED &&
1062 HIWORD(wParam) != BN_DOUBLECLICKED)
1065 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1066 if (!state->generation_thread_exists) {
1068 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1070 state->keysize = DEFAULT_KEYSIZE;
1071 /* If we ever introduce a new key type, check it here! */
1072 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1073 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1074 if (state->keysize < 256) {
1075 int ret = MessageBox(hwnd,
1076 "PuTTYgen will not generate a key"
1077 " smaller than 256 bits.\n"
1078 "Key length reset to 256. Continue?",
1080 MB_ICONWARNING | MB_OKCANCEL);
1083 state->keysize = 256;
1084 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1086 ui_set_state(hwnd, state, 1);
1087 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1088 state->key_exists = FALSE;
1089 state->collecting_entropy = TRUE;
1092 * My brief statistical tests on mouse movements
1093 * suggest that there are about 2.5 bits of
1094 * randomness in the x position, 2.5 in the y
1095 * position, and 1.7 in the message time, making
1096 * 5.7 bits of unpredictability per mouse movement.
1097 * However, other people have told me it's far less
1098 * than that, so I'm going to be stupidly cautious
1099 * and knock that down to a nice round 2. With this
1100 * method, we require two words per mouse movement,
1101 * so with 2 bits per mouse movement we expect 2
1102 * bits every 2 words.
1104 state->entropy_required = (state->keysize / 2) * 2;
1105 state->entropy_got = 0;
1106 state->entropy_size = (state->entropy_required *
1108 state->entropy = snewn(state->entropy_required, unsigned);
1110 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1111 MAKELPARAM(0, state->entropy_required));
1112 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1116 case IDC_EXPORT_OPENSSH:
1117 case IDC_EXPORT_SSHCOM:
1118 if (HIWORD(wParam) != BN_CLICKED)
1121 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1122 if (state->key_exists) {
1123 char filename[FILENAME_MAX];
1124 char *passphrase, *passphrase2;
1128 realtype = SSH_KEYTYPE_SSH2;
1130 realtype = SSH_KEYTYPE_SSH1;
1132 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1133 type = SSH_KEYTYPE_OPENSSH;
1134 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1135 type = SSH_KEYTYPE_SSHCOM;
1139 if (type != realtype &&
1140 import_target_type(type) != realtype) {
1142 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1143 " format", (state->ssh2 ? 2 : 1),
1144 (state->ssh2 ? 1 : 2));
1145 MessageBox(hwnd, msg,
1146 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1150 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1151 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1152 if (strcmp(passphrase, passphrase2)) {
1154 "The two passphrases given do not match.",
1155 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1156 burnstr(passphrase);
1157 burnstr(passphrase2);
1160 burnstr(passphrase2);
1163 ret = MessageBox(hwnd,
1164 "Are you sure you want to save this key\n"
1165 "without a passphrase to protect it?",
1167 MB_YESNO | MB_ICONWARNING);
1169 burnstr(passphrase);
1173 if (prompt_keyfile(hwnd, "Save private key as:",
1174 filename, 1, (type == realtype))) {
1176 FILE *fp = fopen(filename, "r");
1180 buffer = dupprintf("Overwrite existing file\n%s?",
1182 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1183 MB_YESNO | MB_ICONWARNING);
1186 burnstr(passphrase);
1192 Filename *fn = filename_from_str(filename);
1193 if (type != realtype)
1194 ret = export_ssh2(fn, type, &state->ssh2key,
1195 *passphrase ? passphrase : NULL);
1197 ret = ssh2_save_userkey(fn, &state->ssh2key,
1198 *passphrase ? passphrase :
1202 Filename *fn = filename_from_str(filename);
1203 if (type != realtype)
1204 ret = export_ssh1(fn, type, &state->key,
1205 *passphrase ? passphrase : NULL);
1207 ret = saversakey(fn, &state->key,
1208 *passphrase ? passphrase : NULL);
1212 MessageBox(hwnd, "Unable to save key file",
1213 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1216 burnstr(passphrase);
1220 if (HIWORD(wParam) != BN_CLICKED)
1223 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1224 if (state->key_exists) {
1225 char filename[FILENAME_MAX];
1226 if (prompt_keyfile(hwnd, "Save public key as:",
1229 FILE *fp = fopen(filename, "r");
1233 buffer = dupprintf("Overwrite existing file\n%s?",
1235 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1236 MB_YESNO | MB_ICONWARNING);
1242 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1244 ret = save_ssh1_pubkey(filename, &state->key);
1247 MessageBox(hwnd, "Unable to save key file",
1248 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1255 if (HIWORD(wParam) != BN_CLICKED)
1258 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1259 if (!state->generation_thread_exists) {
1260 char filename[FILENAME_MAX];
1261 if (prompt_keyfile(hwnd, "Load private key:",
1262 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1263 Filename *fn = filename_from_str(filename);
1264 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1272 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1273 state->generation_thread_exists = FALSE;
1274 state->key_exists = TRUE;
1275 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1276 MAKELPARAM(0, PROGRESSRANGE));
1277 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1279 if (state->is_dsa) {
1280 state->ssh2key.data = &state->dsskey;
1281 state->ssh2key.alg = &ssh_dss;
1283 state->ssh2key.data = &state->key;
1284 state->ssh2key.alg = &ssh_rsa;
1286 state->commentptr = &state->ssh2key.comment;
1288 state->commentptr = &state->key.comment;
1291 * Invent a comment for the key. We'll do this by including
1292 * the date in it. This will be so horrifyingly ugly that
1293 * the user will immediately want to change it, which is
1296 *state->commentptr = snewn(30, char);
1301 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1303 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1307 * Now update the key controls with all the key data.
1312 * Blank passphrase, initially. This isn't dangerous,
1313 * because we will warn (Are You Sure?) before allowing
1314 * the user to save an unprotected private key.
1316 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1317 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1321 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1323 * Set the key fingerprint.
1325 savecomment = *state->commentptr;
1326 *state->commentptr = NULL;
1329 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1330 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1334 rsa_fingerprint(buf, sizeof(buf), &state->key);
1335 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1337 *state->commentptr = savecomment;
1339 * Construct a decimal representation of the key, for
1340 * pasting into .ssh/authorized_keys or
1341 * .ssh/authorized_keys2 on a Unix box.
1344 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1345 IDC_PKSTATIC, &state->ssh2key);
1347 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1348 IDC_PKSTATIC, &state->key);
1352 * Finally, hide the progress bar and show the key data.
1354 ui_set_state(hwnd, state, 2);
1358 int id = ((LPHELPINFO)lParam)->iCtrlId;
1361 case IDC_GENERATING:
1365 topic = WINHELP_CTX_puttygen_generate; break;
1367 case IDC_KEYDISPLAY:
1368 topic = WINHELP_CTX_puttygen_pastekey; break;
1370 case IDC_FINGERPRINT:
1371 topic = WINHELP_CTX_puttygen_fingerprint; break;
1372 case IDC_COMMENTSTATIC:
1373 case IDC_COMMENTEDIT:
1374 topic = WINHELP_CTX_puttygen_comment; break;
1375 case IDC_PASSPHRASE1STATIC:
1376 case IDC_PASSPHRASE1EDIT:
1377 case IDC_PASSPHRASE2STATIC:
1378 case IDC_PASSPHRASE2EDIT:
1379 topic = WINHELP_CTX_puttygen_passphrase; break;
1380 case IDC_LOADSTATIC:
1382 topic = WINHELP_CTX_puttygen_load; break;
1383 case IDC_SAVESTATIC:
1385 topic = WINHELP_CTX_puttygen_savepriv; break;
1387 topic = WINHELP_CTX_puttygen_savepub; break;
1388 case IDC_TYPESTATIC:
1390 case IDC_KEYSSH2RSA:
1391 case IDC_KEYSSH2DSA:
1392 topic = WINHELP_CTX_puttygen_keytype; break;
1393 case IDC_BITSSTATIC:
1395 topic = WINHELP_CTX_puttygen_bits; break;
1397 case IDC_EXPORT_OPENSSH:
1398 case IDC_EXPORT_SSHCOM:
1399 topic = WINHELP_CTX_puttygen_conversions; break;
1402 launch_help(hwnd, topic);
1409 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1418 void cleanup_exit(int code)
1424 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1430 InitCommonControls();
1435 * See if we can find our Help file.
1439 split_into_argv(cmdline, &argc, &argv, NULL);
1442 if (!strcmp(argv[0], "-pgpfp")) {
1447 * Assume the first argument to be a private key file, and
1448 * attempt to load it.
1450 cmdline_keyfile = argv[0];
1455 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1458 return ret; /* just in case optimiser complains */