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);
318 typedef enum {RSA, DSA, ECDSA} keytype;
321 * Thread to generate a key.
323 struct rsa_key_thread_params {
324 HWND progressbar; /* notify this with progress */
325 HWND dialog; /* notify this on completion */
326 int keysize; /* bits in key */
330 struct dss_key *dsskey;
333 static DWORD WINAPI generate_rsa_key_thread(void *param)
335 struct rsa_key_thread_params *params =
336 (struct rsa_key_thread_params *) param;
337 struct progress prog;
338 prog.progbar = params->progressbar;
340 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
342 if (params->keytype == DSA)
343 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
345 rsa_generate(params->key, params->keysize, progress_update, &prog);
347 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
353 struct MainDlgState {
354 int collecting_entropy;
355 int generation_thread_exists;
357 int entropy_got, entropy_required, entropy_size;
361 char **commentptr; /* points to key.comment or ssh2key.comment */
362 struct ssh2_userkey ssh2key;
366 struct dss_key dsskey;
368 HMENU filemenu, keymenu, cvtmenu;
371 static void hidemany(HWND hwnd, const int *ids, int hideit)
374 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
378 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
383 dec1 = bignum_decimal(key->exponent);
384 dec2 = bignum_decimal(key->modulus);
385 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
386 dec1, dec2, key->comment);
387 SetDlgItemText(hwnd, id, buffer);
388 SetDlgItemText(hwnd, idstatic,
389 "&Public key for pasting into authorized_keys file:");
395 static void setupbigedit2(HWND hwnd, int id, int idstatic,
396 struct ssh2_userkey *key)
398 unsigned char *pub_blob;
403 pub_blob = key->alg->public_blob(key->data, &pub_len);
404 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
405 strlen(key->comment) + 3, char);
406 strcpy(buffer, key->alg->name);
407 p = buffer + strlen(buffer);
410 while (i < pub_len) {
411 int n = (pub_len - i < 3 ? pub_len - i : 3);
412 base64_encode_atom(pub_blob + i, n, p);
417 strcpy(p, key->comment);
418 SetDlgItemText(hwnd, id, buffer);
419 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
420 "OpenSSH authorized_keys file:");
425 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
430 fp = fopen(filename, "wb");
433 dec1 = bignum_decimal(key->exponent);
434 dec2 = bignum_decimal(key->modulus);
435 fprintf(fp, "%d %s %s %s\n",
436 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
444 * Warn about the obsolescent key file format.
446 void old_keyfile_warning(void)
448 static const char mbtitle[] = "PuTTY Key File Warning";
449 static const char message[] =
450 "You are loading an SSH-2 private key which has an\n"
451 "old version of the file format. This means your key\n"
452 "file is not fully tamperproof. Future versions of\n"
453 "PuTTY may stop supporting this private key format,\n"
454 "so we recommend you convert your key to the new\n"
457 "Once the key is loaded into PuTTYgen, you can perform\n"
458 "this conversion simply by saving it again.";
460 MessageBox(NULL, message, mbtitle, MB_OK);
463 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
465 unsigned char *pub_blob;
471 pub_blob = key->alg->public_blob(key->data, &pub_len);
473 fp = fopen(filename, "wb");
477 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
479 fprintf(fp, "Comment: \"");
480 for (p = key->comment; *p; p++) {
481 if (*p == '\\' || *p == '\"')
489 while (i < pub_len) {
491 int n = (pub_len - i < 3 ? pub_len - i : 3);
492 base64_encode_atom(pub_blob + i, n, buf);
496 if (++column >= 16) {
504 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
511 controlidstart = 100,
518 IDC_PKSTATIC, IDC_KEYDISPLAY,
519 IDC_FPSTATIC, IDC_FINGERPRINT,
520 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
521 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
522 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
524 IDC_GENSTATIC, IDC_GENERATE,
525 IDC_LOADSTATIC, IDC_LOAD,
526 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
528 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
529 IDC_BITSSTATIC, IDC_BITS,
532 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
535 static const int nokey_ids[] = { IDC_NOKEY, 0 };
536 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
537 static const int gotkey_ids[] = {
538 IDC_PKSTATIC, IDC_KEYDISPLAY,
539 IDC_FPSTATIC, IDC_FINGERPRINT,
540 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
541 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
542 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
546 * Small UI helper function to switch the state of the main dialog
547 * by enabling and disabling controls and menu items.
549 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
555 hidemany(hwnd, nokey_ids, FALSE);
556 hidemany(hwnd, generating_ids, TRUE);
557 hidemany(hwnd, gotkey_ids, TRUE);
558 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
559 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
560 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
561 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
562 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
563 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
564 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
565 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
566 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
567 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
568 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
569 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
570 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
571 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
572 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
573 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
574 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
575 MF_GRAYED|MF_BYCOMMAND);
576 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
577 MF_GRAYED|MF_BYCOMMAND);
579 case 1: /* generating key */
580 hidemany(hwnd, nokey_ids, TRUE);
581 hidemany(hwnd, generating_ids, FALSE);
582 hidemany(hwnd, gotkey_ids, TRUE);
583 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
584 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
585 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
586 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
587 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
588 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
589 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
590 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
591 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
592 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
593 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
594 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
595 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
596 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
597 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
598 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
599 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
600 MF_GRAYED|MF_BYCOMMAND);
601 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
602 MF_GRAYED|MF_BYCOMMAND);
605 hidemany(hwnd, nokey_ids, TRUE);
606 hidemany(hwnd, generating_ids, TRUE);
607 hidemany(hwnd, gotkey_ids, FALSE);
608 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
609 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
610 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
611 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
612 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
613 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
614 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
615 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
616 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
617 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
618 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
619 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
620 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
621 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
622 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
623 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
625 * Enable export menu items if and only if the key type
626 * supports this kind of export.
628 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
629 #define do_export_menuitem(x,y) \
630 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
631 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
632 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
633 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
634 #undef do_export_menuitem
639 void load_key_file(HWND hwnd, struct MainDlgState *state,
640 Filename *filename, int was_import_cmd)
646 const char *errmsg = NULL;
648 struct RSAKey newkey1;
649 struct ssh2_userkey *newkey2 = NULL;
651 type = realtype = key_type(filename);
652 if (type != SSH_KEYTYPE_SSH1 &&
653 type != SSH_KEYTYPE_SSH2 &&
654 !import_possible(type)) {
655 char *msg = dupprintf("Couldn't load private key (%s)",
656 key_type_to_str(type));
657 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
658 HELPCTXID(errors_cantloadkey));
663 if (type != SSH_KEYTYPE_SSH1 &&
664 type != SSH_KEYTYPE_SSH2) {
666 type = import_target_type(type);
671 if (realtype == SSH_KEYTYPE_SSH1)
672 needs_pass = rsakey_encrypted(filename, &comment);
673 else if (realtype == SSH_KEYTYPE_SSH2)
674 needs_pass = ssh2_userkey_encrypted(filename, &comment);
676 needs_pass = import_encrypted(filename, realtype, &comment);
683 struct PassphraseProcStruct pps;
684 pps.passphrase = &passphrase;
685 pps.comment = comment;
686 dlgret = DialogBoxParam(hinst,
687 MAKEINTRESOURCE(210),
688 NULL, PassphraseProc,
694 assert(passphrase != NULL);
696 passphrase = dupstr("");
697 if (type == SSH_KEYTYPE_SSH1) {
698 if (realtype == type)
699 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
701 ret = import_ssh1(filename, realtype, &newkey1,
702 passphrase, &errmsg);
704 if (realtype == type)
705 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
707 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
708 if (newkey2 == SSH2_WRONG_PASSPHRASE)
719 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
720 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
721 HELPCTXID(errors_cantloadkey));
723 } else if (ret == 1) {
725 * Now update the key controls with all the
729 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
731 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
733 if (type == SSH_KEYTYPE_SSH1) {
738 state->commentptr = &state->key.comment;
739 state->key = newkey1;
742 * Set the key fingerprint.
744 savecomment = state->key.comment;
745 state->key.comment = NULL;
746 rsa_fingerprint(buf, sizeof(buf),
748 state->key.comment = savecomment;
750 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
752 * Construct a decimal representation
753 * of the key, for pasting into
754 * .ssh/authorized_keys on a Unix box.
756 setupbigedit1(hwnd, IDC_KEYDISPLAY,
757 IDC_PKSTATIC, &state->key);
764 &state->ssh2key.comment;
765 state->ssh2key = *newkey2; /* structure copy */
768 savecomment = state->ssh2key.comment;
769 state->ssh2key.comment = NULL;
772 fingerprint(state->ssh2key.data);
773 state->ssh2key.comment = savecomment;
775 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
778 setupbigedit2(hwnd, IDC_KEYDISPLAY,
779 IDC_PKSTATIC, &state->ssh2key);
781 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
785 * Finally, hide the progress bar and show
788 ui_set_state(hwnd, state, 2);
789 state->key_exists = TRUE;
792 * If the user has imported a foreign key
793 * using the Load command, let them know.
794 * If they've used the Import command, be
797 if (realtype != type && !was_import_cmd) {
799 sprintf(msg, "Successfully imported foreign key\n"
801 "To use this key with PuTTY, you need to\n"
802 "use the \"Save private key\" command to\n"
803 "save it in PuTTY's own format.",
804 key_type_to_str(realtype));
805 MessageBox(NULL, msg, "PuTTYgen Notice",
806 MB_OK | MB_ICONINFORMATION);
813 * Dialog-box function for the main PuTTYgen dialog box.
815 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
816 WPARAM wParam, LPARAM lParam)
818 static const char generating_msg[] =
819 "Please wait while a key is generated...";
820 static const char entropy_msg[] =
821 "Please generate some randomness by moving the mouse over the blank area.";
822 struct MainDlgState *state;
827 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
828 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
832 * If we add a Help button, this is where we destroy it
833 * if the help file isn't present.
836 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
837 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
839 state = snew(struct MainDlgState);
840 state->generation_thread_exists = FALSE;
841 state->collecting_entropy = FALSE;
842 state->entropy = NULL;
843 state->key_exists = FALSE;
844 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
850 menu1 = CreateMenu();
851 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
852 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
853 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
854 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
855 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
856 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
857 state->filemenu = menu1;
859 menu1 = CreateMenu();
860 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
861 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
862 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
863 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
864 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
865 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
866 state->keymenu = menu1;
868 menu1 = CreateMenu();
869 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
870 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
871 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
872 "Export &OpenSSH key");
873 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
874 "Export &ssh.com key");
875 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
877 state->cvtmenu = menu1;
879 menu1 = CreateMenu();
880 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
882 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
883 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
891 { /* centre the window */
895 hw = GetDesktopWindow();
896 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
898 (rs.right + rs.left + rd.left - rd.right) / 2,
899 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
900 rd.right - rd.left, rd.bottom - rd.top, TRUE);
904 struct ctlpos cp, cp2;
906 /* Accelerators used: acglops1rbd */
908 ctlposinit(&cp, hwnd, 4, 4, 4);
909 beginbox(&cp, "Key", IDC_BOX_KEY);
911 statictext(&cp2, "No key.", 1, IDC_NOKEY);
913 statictext(&cp2, "", 1, IDC_GENERATING);
914 progressbar(&cp2, IDC_PROGRESS);
916 "&Public key for pasting into authorized_keys file:",
917 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
918 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
919 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
920 IDC_FINGERPRINT, 75);
921 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
923 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
924 IDC_COMMENTEDIT, 75);
925 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
926 IDC_PASSPHRASE1EDIT, 75);
927 staticpassedit(&cp, "C&onfirm passphrase:",
928 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
930 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
931 staticbtn(&cp, "Generate a public/private key pair",
932 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
933 staticbtn(&cp, "Load an existing private key file",
934 IDC_LOADSTATIC, "&Load", IDC_LOAD);
935 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
936 "Save p&ublic key", IDC_SAVEPUB,
937 "&Save private key", IDC_SAVE);
939 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
940 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
941 "SSH-&1 (RSA)", IDC_KEYSSH1,
942 "SSH-2 &RSA", IDC_KEYSSH2RSA,
943 "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
944 staticedit(&cp, "Number of &bits in a generated key:",
945 IDC_BITSSTATIC, IDC_BITS, 20);
948 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
949 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
950 IDC_KEYSSH2RSA, MF_BYCOMMAND);
951 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
954 * Initially, hide the progress bar and the key display,
955 * and show the no-key display. Also disable the Save
956 * buttons, because with no key we obviously can't save
959 ui_set_state(hwnd, state, 0);
962 * Load a key file if one was provided on the command line.
964 if (cmdline_keyfile) {
965 Filename *fn = filename_from_str(cmdline_keyfile);
966 load_key_file(hwnd, state, fn, 0);
972 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
973 if (state->collecting_entropy &&
974 state->entropy && state->entropy_got < state->entropy_required) {
975 state->entropy[state->entropy_got++] = lParam;
976 state->entropy[state->entropy_got++] = GetMessageTime();
977 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
978 state->entropy_got, 0);
979 if (state->entropy_got >= state->entropy_required) {
980 struct rsa_key_thread_params *params;
984 * Seed the entropy pool
986 random_add_heavynoise(state->entropy, state->entropy_size);
987 smemclr(state->entropy, state->entropy_size);
988 sfree(state->entropy);
989 state->collecting_entropy = FALSE;
991 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
992 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
993 MAKELPARAM(0, PROGRESSRANGE));
994 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
996 params = snew(struct rsa_key_thread_params);
997 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
998 params->dialog = hwnd;
999 params->keysize = state->keysize;
1000 params->keytype = state->keytype;
1001 params->key = &state->key;
1002 params->dsskey = &state->dsskey;
1004 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
1005 params, 0, &threadid)) {
1006 MessageBox(hwnd, "Out of thread resources",
1007 "Key generation error",
1008 MB_OK | MB_ICONERROR);
1011 state->generation_thread_exists = TRUE;
1017 switch (LOWORD(wParam)) {
1019 case IDC_KEYSSH2RSA:
1020 case IDC_KEYSSH2DSA:
1022 state = (struct MainDlgState *)
1023 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1024 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1025 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1027 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1028 LOWORD(wParam), MF_BYCOMMAND);
1032 PostMessage(hwnd, WM_CLOSE, 0, 0);
1034 case IDC_COMMENTEDIT:
1035 if (HIWORD(wParam) == EN_CHANGE) {
1036 state = (struct MainDlgState *)
1037 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1038 if (state->key_exists) {
1039 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1040 int len = GetWindowTextLength(editctl);
1041 if (*state->commentptr)
1042 sfree(*state->commentptr);
1043 *state->commentptr = snewn(len + 1, char);
1044 GetWindowText(editctl, *state->commentptr, len + 1);
1046 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1049 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1056 EnableWindow(hwnd, 0);
1057 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1058 EnableWindow(hwnd, 1);
1059 SetActiveWindow(hwnd);
1062 if (HIWORD(wParam) == BN_CLICKED ||
1063 HIWORD(wParam) == BN_DOUBLECLICKED) {
1064 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1068 if (HIWORD(wParam) != BN_CLICKED &&
1069 HIWORD(wParam) != BN_DOUBLECLICKED)
1072 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1073 if (!state->generation_thread_exists) {
1075 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1077 state->keysize = DEFAULT_KEYSIZE;
1078 /* If we ever introduce a new key type, check it here! */
1079 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1080 state->keytype = RSA;
1081 if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) {
1082 state->keytype = DSA;
1084 if (state->keysize < 256) {
1085 int ret = MessageBox(hwnd,
1086 "PuTTYgen will not generate a key"
1087 " smaller than 256 bits.\n"
1088 "Key length reset to 256. Continue?",
1090 MB_ICONWARNING | MB_OKCANCEL);
1093 state->keysize = 256;
1094 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1096 ui_set_state(hwnd, state, 1);
1097 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1098 state->key_exists = FALSE;
1099 state->collecting_entropy = TRUE;
1102 * My brief statistical tests on mouse movements
1103 * suggest that there are about 2.5 bits of
1104 * randomness in the x position, 2.5 in the y
1105 * position, and 1.7 in the message time, making
1106 * 5.7 bits of unpredictability per mouse movement.
1107 * However, other people have told me it's far less
1108 * than that, so I'm going to be stupidly cautious
1109 * and knock that down to a nice round 2. With this
1110 * method, we require two words per mouse movement,
1111 * so with 2 bits per mouse movement we expect 2
1112 * bits every 2 words.
1114 state->entropy_required = (state->keysize / 2) * 2;
1115 state->entropy_got = 0;
1116 state->entropy_size = (state->entropy_required *
1118 state->entropy = snewn(state->entropy_required, unsigned);
1120 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1121 MAKELPARAM(0, state->entropy_required));
1122 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1126 case IDC_EXPORT_OPENSSH:
1127 case IDC_EXPORT_SSHCOM:
1128 if (HIWORD(wParam) != BN_CLICKED)
1131 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1132 if (state->key_exists) {
1133 char filename[FILENAME_MAX];
1134 char *passphrase, *passphrase2;
1138 realtype = SSH_KEYTYPE_SSH2;
1140 realtype = SSH_KEYTYPE_SSH1;
1142 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1143 type = SSH_KEYTYPE_OPENSSH;
1144 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1145 type = SSH_KEYTYPE_SSHCOM;
1149 if (type != realtype &&
1150 import_target_type(type) != realtype) {
1152 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1153 " format", (state->ssh2 ? 2 : 1),
1154 (state->ssh2 ? 1 : 2));
1155 MessageBox(hwnd, msg,
1156 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1160 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1161 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1162 if (strcmp(passphrase, passphrase2)) {
1164 "The two passphrases given do not match.",
1165 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1166 burnstr(passphrase);
1167 burnstr(passphrase2);
1170 burnstr(passphrase2);
1173 ret = MessageBox(hwnd,
1174 "Are you sure you want to save this key\n"
1175 "without a passphrase to protect it?",
1177 MB_YESNO | MB_ICONWARNING);
1179 burnstr(passphrase);
1183 if (prompt_keyfile(hwnd, "Save private key as:",
1184 filename, 1, (type == realtype))) {
1186 FILE *fp = fopen(filename, "r");
1190 buffer = dupprintf("Overwrite existing file\n%s?",
1192 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1193 MB_YESNO | MB_ICONWARNING);
1196 burnstr(passphrase);
1202 Filename *fn = filename_from_str(filename);
1203 if (type != realtype)
1204 ret = export_ssh2(fn, type, &state->ssh2key,
1205 *passphrase ? passphrase : NULL);
1207 ret = ssh2_save_userkey(fn, &state->ssh2key,
1208 *passphrase ? passphrase :
1212 Filename *fn = filename_from_str(filename);
1213 if (type != realtype)
1214 ret = export_ssh1(fn, type, &state->key,
1215 *passphrase ? passphrase : NULL);
1217 ret = saversakey(fn, &state->key,
1218 *passphrase ? passphrase : NULL);
1222 MessageBox(hwnd, "Unable to save key file",
1223 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1226 burnstr(passphrase);
1230 if (HIWORD(wParam) != BN_CLICKED)
1233 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1234 if (state->key_exists) {
1235 char filename[FILENAME_MAX];
1236 if (prompt_keyfile(hwnd, "Save public key as:",
1239 FILE *fp = fopen(filename, "r");
1243 buffer = dupprintf("Overwrite existing file\n%s?",
1245 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1246 MB_YESNO | MB_ICONWARNING);
1252 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1254 ret = save_ssh1_pubkey(filename, &state->key);
1257 MessageBox(hwnd, "Unable to save key file",
1258 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1265 if (HIWORD(wParam) != BN_CLICKED)
1268 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1269 if (!state->generation_thread_exists) {
1270 char filename[FILENAME_MAX];
1271 if (prompt_keyfile(hwnd, "Load private key:",
1272 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1273 Filename *fn = filename_from_str(filename);
1274 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1282 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1283 state->generation_thread_exists = FALSE;
1284 state->key_exists = TRUE;
1285 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1286 MAKELPARAM(0, PROGRESSRANGE));
1287 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1289 if (state->keytype == DSA) {
1290 state->ssh2key.data = &state->dsskey;
1291 state->ssh2key.alg = &ssh_dss;
1293 state->ssh2key.data = &state->key;
1294 state->ssh2key.alg = &ssh_rsa;
1296 state->commentptr = &state->ssh2key.comment;
1298 state->commentptr = &state->key.comment;
1301 * Invent a comment for the key. We'll do this by including
1302 * the date in it. This will be so horrifyingly ugly that
1303 * the user will immediately want to change it, which is
1306 *state->commentptr = snewn(30, char);
1310 if (state->keytype == DSA)
1311 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1313 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1317 * Now update the key controls with all the key data.
1322 * Blank passphrase, initially. This isn't dangerous,
1323 * because we will warn (Are You Sure?) before allowing
1324 * the user to save an unprotected private key.
1326 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1327 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1331 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1333 * Set the key fingerprint.
1335 savecomment = *state->commentptr;
1336 *state->commentptr = NULL;
1339 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1340 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1344 rsa_fingerprint(buf, sizeof(buf), &state->key);
1345 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1347 *state->commentptr = savecomment;
1349 * Construct a decimal representation of the key, for
1350 * pasting into .ssh/authorized_keys or
1351 * .ssh/authorized_keys2 on a Unix box.
1354 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1355 IDC_PKSTATIC, &state->ssh2key);
1357 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1358 IDC_PKSTATIC, &state->key);
1362 * Finally, hide the progress bar and show the key data.
1364 ui_set_state(hwnd, state, 2);
1368 int id = ((LPHELPINFO)lParam)->iCtrlId;
1371 case IDC_GENERATING:
1375 topic = WINHELP_CTX_puttygen_generate; break;
1377 case IDC_KEYDISPLAY:
1378 topic = WINHELP_CTX_puttygen_pastekey; break;
1380 case IDC_FINGERPRINT:
1381 topic = WINHELP_CTX_puttygen_fingerprint; break;
1382 case IDC_COMMENTSTATIC:
1383 case IDC_COMMENTEDIT:
1384 topic = WINHELP_CTX_puttygen_comment; break;
1385 case IDC_PASSPHRASE1STATIC:
1386 case IDC_PASSPHRASE1EDIT:
1387 case IDC_PASSPHRASE2STATIC:
1388 case IDC_PASSPHRASE2EDIT:
1389 topic = WINHELP_CTX_puttygen_passphrase; break;
1390 case IDC_LOADSTATIC:
1392 topic = WINHELP_CTX_puttygen_load; break;
1393 case IDC_SAVESTATIC:
1395 topic = WINHELP_CTX_puttygen_savepriv; break;
1397 topic = WINHELP_CTX_puttygen_savepub; break;
1398 case IDC_TYPESTATIC:
1400 case IDC_KEYSSH2RSA:
1401 case IDC_KEYSSH2DSA:
1402 topic = WINHELP_CTX_puttygen_keytype; break;
1403 case IDC_BITSSTATIC:
1405 topic = WINHELP_CTX_puttygen_bits; break;
1407 case IDC_EXPORT_OPENSSH:
1408 case IDC_EXPORT_SSHCOM:
1409 topic = WINHELP_CTX_puttygen_conversions; break;
1412 launch_help(hwnd, topic);
1419 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1428 void cleanup_exit(int code)
1434 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1440 InitCommonControls();
1445 * See if we can find our Help file.
1449 split_into_argv(cmdline, &argc, &argv, NULL);
1452 if (!strcmp(argv[0], "-pgpfp")) {
1457 * Assume the first argument to be a private key file, and
1458 * attempt to load it.
1460 cmdline_keyfile = argv[0];
1465 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1468 return ret; /* just in case optimiser complains */