2 * PuTTY key generation front end (Windows).
9 #define PUTTY_DO_GLOBALS
20 #define WM_DONEKEY (WM_XUSER + 1)
22 #define DEFAULT_KEYSIZE 1024
24 static int requested_help;
26 static char *cmdline_keyfile = NULL;
29 * Print a modal (Really Bad) message box and perform a fatal exit.
31 void modalfatalbox(char *fmt, ...)
37 stuff = dupvprintf(fmt, ap);
39 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
40 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
45 /* ----------------------------------------------------------------------
46 * Progress report code. This is really horrible :-)
48 #define PROGRESSRANGE 65535
54 unsigned startpoint, total;
55 unsigned param, current, n; /* if exponential */
56 unsigned mult; /* if linear */
58 unsigned total, divisor, range;
62 static void progress_update(void *param, int action, int phase, int iprogress)
64 struct progress *p = (struct progress *) param;
65 unsigned progress = iprogress;
68 if (action < PROGFN_READY && p->nphases < phase)
71 case PROGFN_INITIALISE:
74 case PROGFN_LIN_PHASE:
75 p->phases[phase-1].exponential = 0;
76 p->phases[phase-1].mult = p->phases[phase].total / progress;
78 case PROGFN_EXP_PHASE:
79 p->phases[phase-1].exponential = 1;
80 p->phases[phase-1].param = 0x10000 + progress;
81 p->phases[phase-1].current = p->phases[phase-1].total;
82 p->phases[phase-1].n = 0;
84 case PROGFN_PHASE_EXTENT:
85 p->phases[phase-1].total = progress;
91 for (i = 0; i < p->nphases; i++) {
92 p->phases[i].startpoint = total;
93 total += p->phases[i].total;
96 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
97 p->range = p->total / p->divisor;
98 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
101 case PROGFN_PROGRESS:
102 if (p->phases[phase-1].exponential) {
103 while (p->phases[phase-1].n < progress) {
104 p->phases[phase-1].n++;
105 p->phases[phase-1].current *= p->phases[phase-1].param;
106 p->phases[phase-1].current /= 0x10000;
108 position = (p->phases[phase-1].startpoint +
109 p->phases[phase-1].total - p->phases[phase-1].current);
111 position = (p->phases[phase-1].startpoint +
112 progress * p->phases[phase-1].mult);
114 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
121 #define PASSPHRASE_MAXLEN 512
123 struct PassphraseProcStruct {
129 * Dialog-box function for the passphrase box.
131 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
132 WPARAM wParam, LPARAM lParam)
134 static char *passphrase = NULL;
135 struct PassphraseProcStruct *p;
139 SetForegroundWindow(hwnd);
140 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
141 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
146 { /* centre the window */
150 hw = GetDesktopWindow();
151 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
153 (rs.right + rs.left + rd.left - rd.right) / 2,
154 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
155 rd.right - rd.left, rd.bottom - rd.top, TRUE);
158 p = (struct PassphraseProcStruct *) lParam;
159 passphrase = p->passphrase;
161 SetDlgItemText(hwnd, 101, p->comment);
163 SetDlgItemText(hwnd, 102, passphrase);
166 switch (LOWORD(wParam)) {
176 case 102: /* edit box */
177 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
178 GetDlgItemText(hwnd, 102, passphrase,
179 PASSPHRASE_MAXLEN - 1);
180 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
193 * Prompt for a key file. Assumes the filename buffer is of size
196 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
197 char *filename, int save, int ppk)
200 memset(&of, 0, sizeof(of));
201 #ifdef OPENFILENAME_SIZE_VERSION_400
202 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
204 of.lStructSize = sizeof(of);
208 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
209 "All Files (*.*)\0*\0\0\0";
210 of.lpstrDefExt = ".ppk";
212 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
214 of.lpstrCustomFilter = NULL;
216 of.lpstrFile = filename;
218 of.nMaxFile = FILENAME_MAX;
219 of.lpstrFileTitle = NULL;
220 of.lpstrInitialDir = NULL;
221 of.lpstrTitle = dlgtitle;
224 return GetSaveFileName(&of);
226 return GetOpenFileName(&of);
230 * Dialog-box function for the Licence box.
232 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
233 WPARAM wParam, LPARAM lParam)
240 { /* centre the window */
244 hw = GetDesktopWindow();
245 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
247 (rs.right + rs.left + rd.left - rd.right) / 2,
248 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
249 rd.right - rd.left, rd.bottom - rd.top, TRUE);
254 switch (LOWORD(wParam)) {
269 * Dialog-box function for the About box.
271 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
272 WPARAM wParam, LPARAM lParam)
279 { /* centre the window */
283 hw = GetDesktopWindow();
284 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
286 (rs.right + rs.left + rd.left - rd.right) / 2,
287 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
288 rd.right - rd.left, rd.bottom - rd.top, TRUE);
291 SetDlgItemText(hwnd, 100, ver);
294 switch (LOWORD(wParam)) {
300 EnableWindow(hwnd, 0);
301 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
302 EnableWindow(hwnd, 1);
303 SetActiveWindow(hwnd);
315 * Thread to generate a key.
317 struct rsa_key_thread_params {
318 HWND progressbar; /* notify this with progress */
319 HWND dialog; /* notify this on completion */
320 int keysize; /* bits in key */
323 struct dss_key *dsskey;
325 static DWORD WINAPI generate_rsa_key_thread(void *param)
327 struct rsa_key_thread_params *params =
328 (struct rsa_key_thread_params *) param;
329 struct progress prog;
330 prog.progbar = params->progressbar;
332 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
335 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
337 rsa_generate(params->key, params->keysize, progress_update, &prog);
339 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
345 struct MainDlgState {
346 int collecting_entropy;
347 int generation_thread_exists;
349 int entropy_got, entropy_required, entropy_size;
352 char **commentptr; /* points to key.comment or ssh2key.comment */
353 struct ssh2_userkey ssh2key;
356 struct dss_key dsskey;
357 HMENU filemenu, keymenu, cvtmenu;
360 static void hidemany(HWND hwnd, const int *ids, int hideit)
363 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
367 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
372 dec1 = bignum_decimal(key->exponent);
373 dec2 = bignum_decimal(key->modulus);
374 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
375 dec1, dec2, key->comment);
376 SetDlgItemText(hwnd, id, buffer);
377 SetDlgItemText(hwnd, idstatic,
378 "&Public key for pasting into authorized_keys file:");
384 static void setupbigedit2(HWND hwnd, int id, int idstatic,
385 struct ssh2_userkey *key)
387 unsigned char *pub_blob;
392 pub_blob = key->alg->public_blob(key->data, &pub_len);
393 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
394 strlen(key->comment) + 3, char);
395 strcpy(buffer, key->alg->name);
396 p = buffer + strlen(buffer);
399 while (i < pub_len) {
400 int n = (pub_len - i < 3 ? pub_len - i : 3);
401 base64_encode_atom(pub_blob + i, n, p);
406 strcpy(p, key->comment);
407 SetDlgItemText(hwnd, id, buffer);
408 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
409 "OpenSSH authorized_keys file:");
414 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
419 dec1 = bignum_decimal(key->exponent);
420 dec2 = bignum_decimal(key->modulus);
421 fp = fopen(filename, "wb");
424 fprintf(fp, "%d %s %s %s\n",
425 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
433 * Warn about the obsolescent key file format.
435 void old_keyfile_warning(void)
437 static const char mbtitle[] = "PuTTY Key File Warning";
438 static const char message[] =
439 "You are loading an SSH 2 private key which has an\n"
440 "old version of the file format. This means your key\n"
441 "file is not fully tamperproof. Future versions of\n"
442 "PuTTY may stop supporting this private key format,\n"
443 "so we recommend you convert your key to the new\n"
446 "Once the key is loaded into PuTTYgen, you can perform\n"
447 "this conversion simply by saving it again.";
449 MessageBox(NULL, message, mbtitle, MB_OK);
452 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
454 unsigned char *pub_blob;
460 pub_blob = key->alg->public_blob(key->data, &pub_len);
462 fp = fopen(filename, "wb");
466 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
468 fprintf(fp, "Comment: \"");
469 for (p = key->comment; *p; p++) {
470 if (*p == '\\' || *p == '\"')
478 while (i < pub_len) {
480 int n = (pub_len - i < 3 ? pub_len - i : 3);
481 base64_encode_atom(pub_blob + i, n, buf);
485 if (++column >= 16) {
493 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
500 controlidstart = 100,
507 IDC_PKSTATIC, IDC_KEYDISPLAY,
508 IDC_FPSTATIC, IDC_FINGERPRINT,
509 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
510 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
511 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
513 IDC_GENSTATIC, IDC_GENERATE,
514 IDC_LOADSTATIC, IDC_LOAD,
515 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
517 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
518 IDC_BITSSTATIC, IDC_BITS,
521 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
524 static const int nokey_ids[] = { IDC_NOKEY, 0 };
525 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
526 static const int gotkey_ids[] = {
527 IDC_PKSTATIC, IDC_KEYDISPLAY,
528 IDC_FPSTATIC, IDC_FINGERPRINT,
529 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
530 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
531 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
535 * Small UI helper function to switch the state of the main dialog
536 * by enabling and disabling controls and menu items.
538 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
544 hidemany(hwnd, nokey_ids, FALSE);
545 hidemany(hwnd, generating_ids, TRUE);
546 hidemany(hwnd, gotkey_ids, TRUE);
547 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
548 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
549 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
550 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
551 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
553 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
554 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
555 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
556 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
557 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
558 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
559 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
560 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
561 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
562 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
563 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
564 MF_GRAYED|MF_BYCOMMAND);
565 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
566 MF_GRAYED|MF_BYCOMMAND);
568 case 1: /* generating key */
569 hidemany(hwnd, nokey_ids, TRUE);
570 hidemany(hwnd, generating_ids, FALSE);
571 hidemany(hwnd, gotkey_ids, TRUE);
572 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
573 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
574 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
575 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
576 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
577 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
578 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
579 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
580 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
581 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
582 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
583 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
584 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
587 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
588 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
589 MF_GRAYED|MF_BYCOMMAND);
590 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
591 MF_GRAYED|MF_BYCOMMAND);
594 hidemany(hwnd, nokey_ids, TRUE);
595 hidemany(hwnd, generating_ids, TRUE);
596 hidemany(hwnd, gotkey_ids, FALSE);
597 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
598 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
599 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
600 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
601 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
602 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
603 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
604 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
605 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
607 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
608 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
609 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
610 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
611 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
612 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
614 * Enable export menu items if and only if the key type
615 * supports this kind of export.
617 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
618 #define do_export_menuitem(x,y) \
619 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
620 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
621 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
622 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
623 #undef do_export_menuitem
628 void load_key_file(HWND hwnd, struct MainDlgState *state,
629 Filename filename, int was_import_cmd)
631 char passphrase[PASSPHRASE_MAXLEN];
635 const char *errmsg = NULL;
637 struct PassphraseProcStruct pps;
638 struct RSAKey newkey1;
639 struct ssh2_userkey *newkey2 = NULL;
641 type = realtype = key_type(&filename);
642 if (type != SSH_KEYTYPE_SSH1 &&
643 type != SSH_KEYTYPE_SSH2 &&
644 !import_possible(type)) {
645 char *msg = dupprintf("Couldn't load private key (%s)",
646 key_type_to_str(type));
647 MessageBox(NULL, msg,
648 "PuTTYgen Error", MB_OK | MB_ICONERROR);
653 if (type != SSH_KEYTYPE_SSH1 &&
654 type != SSH_KEYTYPE_SSH2) {
656 type = import_target_type(type);
660 if (realtype == SSH_KEYTYPE_SSH1)
661 needs_pass = rsakey_encrypted(&filename, &comment);
662 else if (realtype == SSH_KEYTYPE_SSH2)
664 ssh2_userkey_encrypted(&filename, &comment);
666 needs_pass = import_encrypted(&filename, realtype,
668 pps.passphrase = passphrase;
669 pps.comment = comment;
673 dlgret = DialogBoxParam(hinst,
674 MAKEINTRESOURCE(210),
675 NULL, PassphraseProc,
683 if (type == SSH_KEYTYPE_SSH1) {
684 if (realtype == type)
685 ret = loadrsakey(&filename, &newkey1,
686 passphrase, &errmsg);
688 ret = import_ssh1(&filename, realtype,
689 &newkey1, passphrase, &errmsg);
691 if (realtype == type)
692 newkey2 = ssh2_load_userkey(&filename,
693 passphrase, &errmsg);
695 newkey2 = import_ssh2(&filename, realtype,
696 passphrase, &errmsg);
697 if (newkey2 == SSH2_WRONG_PASSPHRASE)
708 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
709 MessageBox(NULL, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR);
711 } else if (ret == 1) {
713 * Now update the key controls with all the
717 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
719 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
721 if (type == SSH_KEYTYPE_SSH1) {
726 state->commentptr = &state->key.comment;
727 state->key = newkey1;
730 * Set the key fingerprint.
732 savecomment = state->key.comment;
733 state->key.comment = NULL;
734 rsa_fingerprint(buf, sizeof(buf),
736 state->key.comment = savecomment;
738 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
740 * Construct a decimal representation
741 * of the key, for pasting into
742 * .ssh/authorized_keys on a Unix box.
744 setupbigedit1(hwnd, IDC_KEYDISPLAY,
745 IDC_PKSTATIC, &state->key);
752 &state->ssh2key.comment;
753 state->ssh2key = *newkey2; /* structure copy */
756 savecomment = state->ssh2key.comment;
757 state->ssh2key.comment = NULL;
760 fingerprint(state->ssh2key.data);
761 state->ssh2key.comment = savecomment;
763 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
766 setupbigedit2(hwnd, IDC_KEYDISPLAY,
767 IDC_PKSTATIC, &state->ssh2key);
769 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
773 * Finally, hide the progress bar and show
776 ui_set_state(hwnd, state, 2);
777 state->key_exists = TRUE;
780 * If the user has imported a foreign key
781 * using the Load command, let them know.
782 * If they've used the Import command, be
785 if (realtype != type && !was_import_cmd) {
787 sprintf(msg, "Successfully imported foreign key\n"
789 "To use this key with PuTTY, you need to\n"
790 "use the \"Save private key\" command to\n"
791 "save it in PuTTY's own format.",
792 key_type_to_str(realtype));
793 MessageBox(NULL, msg, "PuTTYgen Notice",
794 MB_OK | MB_ICONINFORMATION);
800 * Dialog-box function for the main PuTTYgen dialog box.
802 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
803 WPARAM wParam, LPARAM lParam)
805 static const char generating_msg[] =
806 "Please wait while a key is generated...";
807 static const char entropy_msg[] =
808 "Please generate some randomness by moving the mouse over the blank area.";
809 struct MainDlgState *state;
814 SetWindowLong(hwnd, GWL_EXSTYLE,
815 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
818 * If we add a Help button, this is where we destroy it
819 * if the help file isn't present.
822 requested_help = FALSE;
823 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
824 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
826 state = snew(struct MainDlgState);
827 state->generation_thread_exists = FALSE;
828 state->collecting_entropy = FALSE;
829 state->entropy = NULL;
830 state->key_exists = FALSE;
831 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
837 menu1 = CreateMenu();
838 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
839 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
840 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
841 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
842 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
843 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
844 state->filemenu = menu1;
846 menu1 = CreateMenu();
847 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
848 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
849 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
850 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
851 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
852 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
853 state->keymenu = menu1;
855 menu1 = CreateMenu();
856 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
857 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
858 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
859 "Export &OpenSSH key");
860 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
861 "Export &ssh.com key");
862 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
864 state->cvtmenu = menu1;
866 menu1 = CreateMenu();
867 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
869 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
870 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
878 { /* centre the window */
882 hw = GetDesktopWindow();
883 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
885 (rs.right + rs.left + rd.left - rd.right) / 2,
886 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
887 rd.right - rd.left, rd.bottom - rd.top, TRUE);
891 struct ctlpos cp, cp2;
893 /* Accelerators used: acglops1rbd */
895 ctlposinit(&cp, hwnd, 4, 4, 4);
896 beginbox(&cp, "Key", IDC_BOX_KEY);
898 statictext(&cp2, "No key.", 1, IDC_NOKEY);
900 statictext(&cp2, "", 1, IDC_GENERATING);
901 progressbar(&cp2, IDC_PROGRESS);
903 "&Public key for pasting into authorized_keys file:",
904 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
905 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
906 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
907 IDC_FINGERPRINT, 75);
908 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
910 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
911 IDC_COMMENTEDIT, 75);
912 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
913 IDC_PASSPHRASE1EDIT, 75);
914 staticpassedit(&cp, "C&onfirm passphrase:",
915 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
917 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
918 staticbtn(&cp, "Generate a public/private key pair",
919 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
920 staticbtn(&cp, "Load an existing private key file",
921 IDC_LOADSTATIC, "&Load", IDC_LOAD);
922 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
923 "Save p&ublic key", IDC_SAVEPUB,
924 "&Save private key", IDC_SAVE);
926 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
927 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
928 "SSH&1 (RSA)", IDC_KEYSSH1,
929 "SSH2 &RSA", IDC_KEYSSH2RSA,
930 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
931 staticedit(&cp, "Number of &bits in a generated key:",
932 IDC_BITSSTATIC, IDC_BITS, 20);
935 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
936 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
937 IDC_KEYSSH2RSA, MF_BYCOMMAND);
938 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
941 * Initially, hide the progress bar and the key display,
942 * and show the no-key display. Also disable the Save
943 * buttons, because with no key we obviously can't save
946 ui_set_state(hwnd, state, 0);
949 * Load a key file if one was provided on the command line.
952 load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
956 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
957 if (state->collecting_entropy &&
958 state->entropy && state->entropy_got < state->entropy_required) {
959 state->entropy[state->entropy_got++] = lParam;
960 state->entropy[state->entropy_got++] = GetMessageTime();
961 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
962 state->entropy_got, 0);
963 if (state->entropy_got >= state->entropy_required) {
964 struct rsa_key_thread_params *params;
968 * Seed the entropy pool
970 random_add_heavynoise(state->entropy, state->entropy_size);
971 memset(state->entropy, 0, state->entropy_size);
972 sfree(state->entropy);
973 state->collecting_entropy = FALSE;
975 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
976 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
977 MAKELPARAM(0, PROGRESSRANGE));
978 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
980 params = snew(struct rsa_key_thread_params);
981 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
982 params->dialog = hwnd;
983 params->keysize = state->keysize;
984 params->is_dsa = state->is_dsa;
985 params->key = &state->key;
986 params->dsskey = &state->dsskey;
988 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
989 params, 0, &threadid)) {
990 MessageBox(hwnd, "Out of thread resources",
991 "Key generation error",
992 MB_OK | MB_ICONERROR);
995 state->generation_thread_exists = TRUE;
1001 switch (LOWORD(wParam)) {
1003 case IDC_KEYSSH2RSA:
1004 case IDC_KEYSSH2DSA:
1006 state = (struct MainDlgState *)
1007 GetWindowLong(hwnd, GWL_USERDATA);
1008 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1009 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1011 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1012 LOWORD(wParam), MF_BYCOMMAND);
1016 PostMessage(hwnd, WM_CLOSE, 0, 0);
1018 case IDC_COMMENTEDIT:
1019 if (HIWORD(wParam) == EN_CHANGE) {
1020 state = (struct MainDlgState *)
1021 GetWindowLong(hwnd, GWL_USERDATA);
1022 if (state->key_exists) {
1023 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1024 int len = GetWindowTextLength(editctl);
1025 if (*state->commentptr)
1026 sfree(*state->commentptr);
1027 *state->commentptr = snewn(len + 1, char);
1028 GetWindowText(editctl, *state->commentptr, len + 1);
1030 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1033 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1040 EnableWindow(hwnd, 0);
1041 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1042 EnableWindow(hwnd, 1);
1043 SetActiveWindow(hwnd);
1046 if (HIWORD(wParam) == BN_CLICKED ||
1047 HIWORD(wParam) == BN_DOUBLECLICKED) {
1049 WinHelp(hwnd, help_path, HELP_COMMAND,
1050 (DWORD)"JI(`',`puttygen.general')");
1051 requested_help = TRUE;
1056 if (HIWORD(wParam) != BN_CLICKED &&
1057 HIWORD(wParam) != BN_DOUBLECLICKED)
1060 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1061 if (!state->generation_thread_exists) {
1063 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1065 state->keysize = DEFAULT_KEYSIZE;
1066 /* If we ever introduce a new key type, check it here! */
1067 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1068 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1069 if (state->keysize < 256) {
1070 int ret = MessageBox(hwnd,
1071 "PuTTYgen will not generate a key"
1072 " smaller than 256 bits.\n"
1073 "Key length reset to 256. Continue?",
1075 MB_ICONWARNING | MB_OKCANCEL);
1078 state->keysize = 256;
1079 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1081 ui_set_state(hwnd, state, 1);
1082 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1083 state->key_exists = FALSE;
1084 state->collecting_entropy = TRUE;
1087 * My brief statistical tests on mouse movements
1088 * suggest that there are about 2.5 bits of
1089 * randomness in the x position, 2.5 in the y
1090 * position, and 1.7 in the message time, making
1091 * 5.7 bits of unpredictability per mouse movement.
1092 * However, other people have told me it's far less
1093 * than that, so I'm going to be stupidly cautious
1094 * and knock that down to a nice round 2. With this
1095 * method, we require two words per mouse movement,
1096 * so with 2 bits per mouse movement we expect 2
1097 * bits every 2 words.
1099 state->entropy_required = (state->keysize / 2) * 2;
1100 state->entropy_got = 0;
1101 state->entropy_size = (state->entropy_required *
1103 state->entropy = snewn(state->entropy_required, unsigned);
1105 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1106 MAKELPARAM(0, state->entropy_required));
1107 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1111 case IDC_EXPORT_OPENSSH:
1112 case IDC_EXPORT_SSHCOM:
1113 if (HIWORD(wParam) != BN_CLICKED)
1116 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1117 if (state->key_exists) {
1118 char filename[FILENAME_MAX];
1119 char passphrase[PASSPHRASE_MAXLEN];
1120 char passphrase2[PASSPHRASE_MAXLEN];
1124 realtype = SSH_KEYTYPE_SSH2;
1126 realtype = SSH_KEYTYPE_SSH1;
1128 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1129 type = SSH_KEYTYPE_OPENSSH;
1130 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1131 type = SSH_KEYTYPE_SSHCOM;
1135 if (type != realtype &&
1136 import_target_type(type) != realtype) {
1138 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1139 " format", (state->ssh2 ? 2 : 1),
1140 (state->ssh2 ? 1 : 2));
1141 MessageBox(hwnd, msg,
1142 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1146 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1147 passphrase, sizeof(passphrase));
1148 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1149 passphrase2, sizeof(passphrase2));
1150 if (strcmp(passphrase, passphrase2)) {
1152 "The two passphrases given do not match.",
1153 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1158 ret = MessageBox(hwnd,
1159 "Are you sure you want to save this key\n"
1160 "without a passphrase to protect it?",
1162 MB_YESNO | MB_ICONWARNING);
1166 if (prompt_keyfile(hwnd, "Save private key as:",
1167 filename, 1, (type == realtype))) {
1169 FILE *fp = fopen(filename, "r");
1173 buffer = dupprintf("Overwrite existing file\n%s?",
1175 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1176 MB_YESNO | MB_ICONWARNING);
1183 Filename fn = filename_from_str(filename);
1184 if (type != realtype)
1185 ret = export_ssh2(&fn, type, &state->ssh2key,
1186 *passphrase ? passphrase : NULL);
1188 ret = ssh2_save_userkey(&fn, &state->ssh2key,
1189 *passphrase ? passphrase :
1192 Filename fn = filename_from_str(filename);
1193 if (type != realtype)
1194 ret = export_ssh1(&fn, type, &state->key,
1195 *passphrase ? passphrase : NULL);
1197 ret = saversakey(&fn, &state->key,
1198 *passphrase ? passphrase : NULL);
1201 MessageBox(hwnd, "Unable to save key file",
1202 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1208 if (HIWORD(wParam) != BN_CLICKED)
1211 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1212 if (state->key_exists) {
1213 char filename[FILENAME_MAX];
1214 if (prompt_keyfile(hwnd, "Save public key as:",
1217 FILE *fp = fopen(filename, "r");
1221 buffer = dupprintf("Overwrite existing file\n%s?",
1223 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1224 MB_YESNO | MB_ICONWARNING);
1230 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1232 ret = save_ssh1_pubkey(filename, &state->key);
1235 MessageBox(hwnd, "Unable to save key file",
1236 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1243 if (HIWORD(wParam) != BN_CLICKED)
1246 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1247 if (!state->generation_thread_exists) {
1248 char filename[FILENAME_MAX];
1249 if (prompt_keyfile(hwnd, "Load private key:",
1250 filename, 0, LOWORD(wParam)==IDC_LOAD))
1251 load_key_file(hwnd, state, filename_from_str(filename),
1252 LOWORD(wParam) != IDC_LOAD);
1258 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1259 state->generation_thread_exists = FALSE;
1260 state->key_exists = TRUE;
1261 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1262 MAKELPARAM(0, PROGRESSRANGE));
1263 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1265 if (state->is_dsa) {
1266 state->ssh2key.data = &state->dsskey;
1267 state->ssh2key.alg = &ssh_dss;
1269 state->ssh2key.data = &state->key;
1270 state->ssh2key.alg = &ssh_rsa;
1272 state->commentptr = &state->ssh2key.comment;
1274 state->commentptr = &state->key.comment;
1277 * Invent a comment for the key. We'll do this by including
1278 * the date in it. This will be so horrifyingly ugly that
1279 * the user will immediately want to change it, which is
1282 *state->commentptr = snewn(30, char);
1287 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1289 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1293 * Now update the key controls with all the key data.
1298 * Blank passphrase, initially. This isn't dangerous,
1299 * because we will warn (Are You Sure?) before allowing
1300 * the user to save an unprotected private key.
1302 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1303 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1307 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1309 * Set the key fingerprint.
1311 savecomment = *state->commentptr;
1312 *state->commentptr = NULL;
1315 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1316 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1320 rsa_fingerprint(buf, sizeof(buf), &state->key);
1321 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1323 *state->commentptr = savecomment;
1325 * Construct a decimal representation of the key, for
1326 * pasting into .ssh/authorized_keys or
1327 * .ssh/authorized_keys2 on a Unix box.
1330 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1331 IDC_PKSTATIC, &state->ssh2key);
1333 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1334 IDC_PKSTATIC, &state->key);
1338 * Finally, hide the progress bar and show the key data.
1340 ui_set_state(hwnd, state, 2);
1344 int id = ((LPHELPINFO)lParam)->iCtrlId;
1347 case IDC_GENERATING:
1351 topic = "puttygen.generate"; break;
1353 case IDC_KEYDISPLAY:
1354 topic = "puttygen.pastekey"; break;
1356 case IDC_FINGERPRINT:
1357 topic = "puttygen.fingerprint"; break;
1358 case IDC_COMMENTSTATIC:
1359 case IDC_COMMENTEDIT:
1360 topic = "puttygen.comment"; break;
1361 case IDC_PASSPHRASE1STATIC:
1362 case IDC_PASSPHRASE1EDIT:
1363 case IDC_PASSPHRASE2STATIC:
1364 case IDC_PASSPHRASE2EDIT:
1365 topic = "puttygen.passphrase"; break;
1366 case IDC_LOADSTATIC:
1368 topic = "puttygen.load"; break;
1369 case IDC_SAVESTATIC:
1371 topic = "puttygen.savepriv"; break;
1373 topic = "puttygen.savepub"; break;
1374 case IDC_TYPESTATIC:
1376 case IDC_KEYSSH2RSA:
1377 case IDC_KEYSSH2DSA:
1378 topic = "puttygen.keytype"; break;
1379 case IDC_BITSSTATIC:
1381 topic = "puttygen.bits"; break;
1383 case IDC_EXPORT_OPENSSH:
1384 case IDC_EXPORT_SSHCOM:
1385 topic = "puttygen.conversions"; break;
1388 char *cmd = dupprintf("JI(`',`%s')", topic);
1389 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1391 requested_help = TRUE;
1398 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1400 if (requested_help) {
1401 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1402 requested_help = FALSE;
1410 void cleanup_exit(int code) { exit(code); }
1412 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1417 split_into_argv(cmdline, &argc, &argv, NULL);
1421 * Assume the first argument to be a private key file, and
1422 * attempt to load it.
1424 cmdline_keyfile = argv[0];
1427 InitCommonControls();
1431 * See if we can find our Help file.
1434 char b[2048], *p, *q, *r;
1436 GetModuleFileName(NULL, b, sizeof(b) - 1);
1438 p = strrchr(b, '\\');
1439 if (p && p >= r) r = p+1;
1440 q = strrchr(b, ':');
1441 if (q && q >= r) r = q+1;
1442 strcpy(r, PUTTY_HELP_FILE);
1443 if ( (fp = fopen(b, "r")) != NULL) {
1444 help_path = dupstr(b);
1451 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1452 MainDlgProc) != IDOK;