2 * PuTTY key generation front end.
11 #define PUTTY_DO_GLOBALS
17 #define WM_DONEKEY (WM_XUSER + 1)
19 #define DEFAULT_KEYSIZE 1024
21 static int requested_help;
23 static char *cmdline_keyfile = NULL;
26 * Print a modal (Really Bad) message box and perform a fatal exit.
28 void modalfatalbox(char *fmt, ...)
34 stuff = dupvprintf(fmt, ap);
36 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
37 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
42 /* ----------------------------------------------------------------------
43 * Progress report code. This is really horrible :-)
45 #define PROGRESSRANGE 65535
51 unsigned startpoint, total;
52 unsigned param, current, n; /* if exponential */
53 unsigned mult; /* if linear */
55 unsigned total, divisor, range;
59 static void progress_update(void *param, int action, int phase, int iprogress)
61 struct progress *p = (struct progress *) param;
62 unsigned progress = iprogress;
65 if (action < PROGFN_READY && p->nphases < phase)
68 case PROGFN_INITIALISE:
71 case PROGFN_LIN_PHASE:
72 p->phases[phase-1].exponential = 0;
73 p->phases[phase-1].mult = p->phases[phase].total / progress;
75 case PROGFN_EXP_PHASE:
76 p->phases[phase-1].exponential = 1;
77 p->phases[phase-1].param = 0x10000 + progress;
78 p->phases[phase-1].current = p->phases[phase-1].total;
79 p->phases[phase-1].n = 0;
81 case PROGFN_PHASE_EXTENT:
82 p->phases[phase-1].total = progress;
88 for (i = 0; i < p->nphases; i++) {
89 p->phases[i].startpoint = total;
90 total += p->phases[i].total;
93 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
94 p->range = p->total / p->divisor;
95 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
99 if (p->phases[phase-1].exponential) {
100 while (p->phases[phase-1].n < progress) {
101 p->phases[phase-1].n++;
102 p->phases[phase-1].current *= p->phases[phase-1].param;
103 p->phases[phase-1].current /= 0x10000;
105 position = (p->phases[phase-1].startpoint +
106 p->phases[phase-1].total - p->phases[phase-1].current);
108 position = (p->phases[phase-1].startpoint +
109 progress * p->phases[phase-1].mult);
111 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
118 #define PASSPHRASE_MAXLEN 512
120 struct PassphraseProcStruct {
126 * Dialog-box function for the passphrase box.
128 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
129 WPARAM wParam, LPARAM lParam)
131 static char *passphrase = NULL;
132 struct PassphraseProcStruct *p;
136 SetForegroundWindow(hwnd);
137 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
138 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
143 { /* centre the window */
147 hw = GetDesktopWindow();
148 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
150 (rs.right + rs.left + rd.left - rd.right) / 2,
151 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
152 rd.right - rd.left, rd.bottom - rd.top, TRUE);
155 p = (struct PassphraseProcStruct *) lParam;
156 passphrase = p->passphrase;
158 SetDlgItemText(hwnd, 101, p->comment);
160 SetDlgItemText(hwnd, 102, passphrase);
163 switch (LOWORD(wParam)) {
173 case 102: /* edit box */
174 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
175 GetDlgItemText(hwnd, 102, passphrase,
176 PASSPHRASE_MAXLEN - 1);
177 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
190 * Prompt for a key file. Assumes the filename buffer is of size
193 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
194 char *filename, int save, int ppk)
197 memset(&of, 0, sizeof(of));
198 #ifdef OPENFILENAME_SIZE_VERSION_400
199 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
201 of.lStructSize = sizeof(of);
205 of.lpstrFilter = "PuTTY Private Key Files\0*.PPK\0All Files\0*\0\0\0";
206 of.lpstrDefExt = ".ppk";
208 of.lpstrFilter = "All Files\0*\0\0\0";
210 of.lpstrCustomFilter = NULL;
212 of.lpstrFile = filename;
214 of.nMaxFile = FILENAME_MAX;
215 of.lpstrFileTitle = NULL;
216 of.lpstrInitialDir = NULL;
217 of.lpstrTitle = dlgtitle;
220 return GetSaveFileName(&of);
222 return GetOpenFileName(&of);
226 * Dialog-box function for the Licence box.
228 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
229 WPARAM wParam, LPARAM lParam)
236 { /* centre the window */
240 hw = GetDesktopWindow();
241 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
243 (rs.right + rs.left + rd.left - rd.right) / 2,
244 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
245 rd.right - rd.left, rd.bottom - rd.top, TRUE);
250 switch (LOWORD(wParam)) {
264 * Dialog-box function for the About box.
266 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
267 WPARAM wParam, LPARAM lParam)
274 { /* centre the window */
278 hw = GetDesktopWindow();
279 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
281 (rs.right + rs.left + rd.left - rd.right) / 2,
282 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
283 rd.right - rd.left, rd.bottom - rd.top, TRUE);
286 SetDlgItemText(hwnd, 100, ver);
289 switch (LOWORD(wParam)) {
294 EnableWindow(hwnd, 0);
295 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
296 EnableWindow(hwnd, 1);
297 SetActiveWindow(hwnd);
309 * Thread to generate a key.
311 struct rsa_key_thread_params {
312 HWND progressbar; /* notify this with progress */
313 HWND dialog; /* notify this on completion */
314 int keysize; /* bits in key */
317 struct dss_key *dsskey;
319 static DWORD WINAPI generate_rsa_key_thread(void *param)
321 struct rsa_key_thread_params *params =
322 (struct rsa_key_thread_params *) param;
323 struct progress prog;
324 prog.progbar = params->progressbar;
326 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
329 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
331 rsa_generate(params->key, params->keysize, progress_update, &prog);
333 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
339 struct MainDlgState {
340 int collecting_entropy;
341 int generation_thread_exists;
343 int entropy_got, entropy_required, entropy_size;
346 char **commentptr; /* points to key.comment or ssh2key.comment */
347 struct ssh2_userkey ssh2key;
350 struct dss_key dsskey;
351 HMENU filemenu, keymenu, cvtmenu;
354 static void hidemany(HWND hwnd, const int *ids, int hideit)
357 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
361 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
366 dec1 = bignum_decimal(key->exponent);
367 dec2 = bignum_decimal(key->modulus);
368 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
369 dec1, dec2, key->comment);
370 SetDlgItemText(hwnd, id, buffer);
371 SetDlgItemText(hwnd, idstatic,
372 "&Public key for pasting into authorized_keys file:");
378 static void setupbigedit2(HWND hwnd, int id, int idstatic,
379 struct ssh2_userkey *key)
381 unsigned char *pub_blob;
386 pub_blob = key->alg->public_blob(key->data, &pub_len);
387 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
388 strlen(key->comment) + 3);
389 strcpy(buffer, key->alg->name);
390 p = buffer + strlen(buffer);
393 while (i < pub_len) {
394 int n = (pub_len - i < 3 ? pub_len - i : 3);
395 base64_encode_atom(pub_blob + i, n, p);
400 strcpy(p, key->comment);
401 SetDlgItemText(hwnd, id, buffer);
402 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
403 "OpenSSH authorized_keys2 file:");
408 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
413 dec1 = bignum_decimal(key->exponent);
414 dec2 = bignum_decimal(key->modulus);
415 fp = fopen(filename, "wb");
418 fprintf(fp, "%d %s %s %s\n",
419 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
427 * Warn about the obsolescent key file format.
429 void old_keyfile_warning(void)
431 static const char mbtitle[] = "PuTTY Key File Warning";
432 static const char message[] =
433 "You are loading an SSH 2 private key which has an\n"
434 "old version of the file format. This means your key\n"
435 "file is not fully tamperproof. Future versions of\n"
436 "PuTTY may stop supporting this private key format,\n"
437 "so we recommend you convert your key to the new\n"
440 "Once the key is loaded into PuTTYgen, you can perform\n"
441 "this conversion simply by saving it again.";
443 MessageBox(NULL, message, mbtitle, MB_OK);
446 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
448 unsigned char *pub_blob;
454 pub_blob = key->alg->public_blob(key->data, &pub_len);
456 fp = fopen(filename, "wb");
460 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
462 fprintf(fp, "Comment: \"");
463 for (p = key->comment; *p; p++) {
464 if (*p == '\\' || *p == '\"')
472 while (i < pub_len) {
474 int n = (pub_len - i < 3 ? pub_len - i : 3);
475 base64_encode_atom(pub_blob + i, n, buf);
479 if (++column >= 16) {
487 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
494 controlidstart = 100,
501 IDC_PKSTATIC, IDC_KEYDISPLAY,
502 IDC_FPSTATIC, IDC_FINGERPRINT,
503 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
504 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
505 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
507 IDC_GENSTATIC, IDC_GENERATE,
508 IDC_LOADSTATIC, IDC_LOAD,
509 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
511 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
512 IDC_BITSSTATIC, IDC_BITS,
515 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
518 static const int nokey_ids[] = { IDC_NOKEY, 0 };
519 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
520 static const int gotkey_ids[] = {
521 IDC_PKSTATIC, IDC_KEYDISPLAY,
522 IDC_FPSTATIC, IDC_FINGERPRINT,
523 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
524 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
525 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
529 * Small UI helper function to switch the state of the main dialog
530 * by enabling and disabling controls and menu items.
532 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
538 hidemany(hwnd, nokey_ids, FALSE);
539 hidemany(hwnd, generating_ids, TRUE);
540 hidemany(hwnd, gotkey_ids, TRUE);
541 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
542 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
543 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
544 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
545 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
546 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
547 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
548 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
549 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
550 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
551 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
552 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
553 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
554 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
555 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
556 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
557 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
558 MF_GRAYED|MF_BYCOMMAND);
559 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
560 MF_GRAYED|MF_BYCOMMAND);
562 case 1: /* generating key */
563 hidemany(hwnd, nokey_ids, TRUE);
564 hidemany(hwnd, generating_ids, FALSE);
565 hidemany(hwnd, gotkey_ids, TRUE);
566 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
567 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
568 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
569 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
570 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
571 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
572 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
573 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
574 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
575 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
576 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
577 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
578 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
579 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
580 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
581 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
582 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
583 MF_GRAYED|MF_BYCOMMAND);
584 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
585 MF_GRAYED|MF_BYCOMMAND);
588 hidemany(hwnd, nokey_ids, TRUE);
589 hidemany(hwnd, generating_ids, TRUE);
590 hidemany(hwnd, gotkey_ids, FALSE);
591 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
592 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
593 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
594 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
595 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
596 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
597 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
598 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
599 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
600 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
601 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
602 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
603 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
604 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
605 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
608 * Enable export menu items if and only if the key type
609 * supports this kind of export.
611 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
612 #define do_export_menuitem(x,y) \
613 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
614 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
615 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
616 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
617 #undef do_export_menuitem
622 void load_key_file(HWND hwnd, struct MainDlgState *state,
623 char *filename, int was_import_cmd)
625 char passphrase[PASSPHRASE_MAXLEN];
630 struct PassphraseProcStruct pps;
631 struct RSAKey newkey1;
632 struct ssh2_userkey *newkey2 = NULL;
634 type = realtype = key_type(filename);
635 if (type != SSH_KEYTYPE_SSH1 &&
636 type != SSH_KEYTYPE_SSH2 &&
637 !import_possible(type)) {
639 sprintf(msg, "Couldn't load private key (%s)",
640 key_type_to_str(type));
641 MessageBox(NULL, msg,
642 "PuTTYgen Error", MB_OK | MB_ICONERROR);
646 if (type != SSH_KEYTYPE_SSH1 &&
647 type != SSH_KEYTYPE_SSH2) {
649 type = import_target_type(type);
653 if (realtype == SSH_KEYTYPE_SSH1)
654 needs_pass = rsakey_encrypted(filename, &comment);
655 else if (realtype == SSH_KEYTYPE_SSH2)
657 ssh2_userkey_encrypted(filename, &comment);
659 needs_pass = import_encrypted(filename, realtype,
661 pps.passphrase = passphrase;
662 pps.comment = comment;
666 dlgret = DialogBoxParam(hinst,
667 MAKEINTRESOURCE(210),
668 NULL, PassphraseProc,
676 if (type == SSH_KEYTYPE_SSH1) {
677 if (realtype == type)
678 ret = loadrsakey(filename, &newkey1,
681 ret = import_ssh1(filename, realtype,
682 &newkey1, passphrase);
684 if (realtype == type)
685 newkey2 = ssh2_load_userkey(filename,
688 newkey2 = import_ssh2(filename, realtype,
690 if (newkey2 == SSH2_WRONG_PASSPHRASE)
701 MessageBox(NULL, "Couldn't load private key.",
702 "PuTTYgen Error", MB_OK | MB_ICONERROR);
703 } else if (ret == 1) {
705 * Now update the key controls with all the
709 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
711 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
713 if (type == SSH_KEYTYPE_SSH1) {
718 state->commentptr = &state->key.comment;
719 state->key = newkey1;
722 * Set the key fingerprint.
724 savecomment = state->key.comment;
725 state->key.comment = NULL;
726 rsa_fingerprint(buf, sizeof(buf),
728 state->key.comment = savecomment;
730 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
732 * Construct a decimal representation
733 * of the key, for pasting into
734 * .ssh/authorized_keys on a Unix box.
736 setupbigedit1(hwnd, IDC_KEYDISPLAY,
737 IDC_PKSTATIC, &state->key);
744 &state->ssh2key.comment;
745 state->ssh2key = *newkey2; /* structure copy */
748 savecomment = state->ssh2key.comment;
749 state->ssh2key.comment = NULL;
752 fingerprint(state->ssh2key.data);
753 state->ssh2key.comment = savecomment;
755 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
758 setupbigedit2(hwnd, IDC_KEYDISPLAY,
759 IDC_PKSTATIC, &state->ssh2key);
761 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
765 * Finally, hide the progress bar and show
768 ui_set_state(hwnd, state, 2);
769 state->key_exists = TRUE;
772 * If the user has imported a foreign key
773 * using the Load command, let them know.
774 * If they've used the Import command, be
777 if (realtype != type && !was_import_cmd) {
779 sprintf(msg, "Successfully imported foreign key\n"
781 "To use this key with PuTTY, you need to\n"
782 "use the \"Save private key\" command to\n"
783 "save it in PuTTY's own format.",
784 key_type_to_str(realtype));
785 MessageBox(NULL, msg, "PuTTYgen Notice",
786 MB_OK | MB_ICONINFORMATION);
792 * Dialog-box function for the main PuTTYgen dialog box.
794 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
795 WPARAM wParam, LPARAM lParam)
797 static const char generating_msg[] =
798 "Please wait while a key is generated...";
799 static const char entropy_msg[] =
800 "Please generate some randomness by moving the mouse over the blank area.";
801 struct MainDlgState *state;
806 SetWindowLong(hwnd, GWL_EXSTYLE,
807 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
810 * If we add a Help button, this is where we destroy it
811 * if the help file isn't present.
814 requested_help = FALSE;
816 state = smalloc(sizeof(*state));
817 state->generation_thread_exists = FALSE;
818 state->collecting_entropy = FALSE;
819 state->entropy = NULL;
820 state->key_exists = FALSE;
821 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
827 menu1 = CreateMenu();
828 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
829 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
830 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
831 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
832 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
833 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
834 state->filemenu = menu1;
836 menu1 = CreateMenu();
837 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
838 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
839 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
840 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
841 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
842 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
843 state->keymenu = menu1;
845 menu1 = CreateMenu();
846 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
847 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
848 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
849 "Export &OpenSSH key");
850 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
851 "Export &ssh.com key");
852 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
854 state->cvtmenu = menu1;
856 menu1 = CreateMenu();
857 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
859 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
860 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
868 { /* centre the window */
872 hw = GetDesktopWindow();
873 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
875 (rs.right + rs.left + rd.left - rd.right) / 2,
876 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
877 rd.right - rd.left, rd.bottom - rd.top, TRUE);
881 struct ctlpos cp, cp2;
883 /* Accelerators used: acglops1rbd */
885 ctlposinit(&cp, hwnd, 4, 4, 4);
886 beginbox(&cp, "Key", IDC_BOX_KEY);
888 statictext(&cp2, "No key.", 1, IDC_NOKEY);
890 statictext(&cp2, "", 1, IDC_GENERATING);
891 progressbar(&cp2, IDC_PROGRESS);
893 "&Public key for pasting into authorized_keys file:",
894 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
895 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
896 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
897 IDC_FINGERPRINT, 75);
898 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
900 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
901 IDC_COMMENTEDIT, 75);
902 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
903 IDC_PASSPHRASE1EDIT, 75);
904 staticpassedit(&cp, "C&onfirm passphrase:",
905 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
907 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
908 staticbtn(&cp, "Generate a public/private key pair",
909 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
910 staticbtn(&cp, "Load an existing private key file",
911 IDC_LOADSTATIC, "&Load", IDC_LOAD);
912 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
913 "Save p&ublic key", IDC_SAVEPUB,
914 "&Save private key", IDC_SAVE);
916 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
917 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
918 "SSH&1 (RSA)", IDC_KEYSSH1,
919 "SSH2 &RSA", IDC_KEYSSH2RSA,
920 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
921 staticedit(&cp, "Number of &bits in a generated key:",
922 IDC_BITSSTATIC, IDC_BITS, 20);
925 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
926 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
927 IDC_KEYSSH1, MF_BYCOMMAND);
928 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
931 * Initially, hide the progress bar and the key display,
932 * and show the no-key display. Also disable the Save
933 * buttons, because with no key we obviously can't save
936 ui_set_state(hwnd, state, 0);
939 * Load a key file if one was provided on the command line.
942 load_key_file(hwnd, state, cmdline_keyfile, 0);
946 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
947 if (state->collecting_entropy &&
948 state->entropy && state->entropy_got < state->entropy_required) {
949 state->entropy[state->entropy_got++] = lParam;
950 state->entropy[state->entropy_got++] = GetMessageTime();
951 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
952 state->entropy_got, 0);
953 if (state->entropy_got >= state->entropy_required) {
954 struct rsa_key_thread_params *params;
958 * Seed the entropy pool
960 random_add_heavynoise(state->entropy, state->entropy_size);
961 memset(state->entropy, 0, state->entropy_size);
962 sfree(state->entropy);
963 state->collecting_entropy = FALSE;
965 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
966 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
967 MAKELPARAM(0, PROGRESSRANGE));
968 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
970 params = smalloc(sizeof(*params));
971 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
972 params->dialog = hwnd;
973 params->keysize = state->keysize;
974 params->is_dsa = state->is_dsa;
975 params->key = &state->key;
976 params->dsskey = &state->dsskey;
978 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
979 params, 0, &threadid)) {
980 MessageBox(hwnd, "Out of thread resources",
981 "Key generation error",
982 MB_OK | MB_ICONERROR);
985 state->generation_thread_exists = TRUE;
991 switch (LOWORD(wParam)) {
996 state = (struct MainDlgState *)
997 GetWindowLong(hwnd, GWL_USERDATA);
998 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
999 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1001 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1002 LOWORD(wParam), MF_BYCOMMAND);
1006 PostMessage(hwnd, WM_CLOSE, 0, 0);
1008 case IDC_COMMENTEDIT:
1009 if (HIWORD(wParam) == EN_CHANGE) {
1010 state = (struct MainDlgState *)
1011 GetWindowLong(hwnd, GWL_USERDATA);
1012 if (state->key_exists) {
1013 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1014 int len = GetWindowTextLength(editctl);
1015 if (*state->commentptr)
1016 sfree(*state->commentptr);
1017 *state->commentptr = smalloc(len + 1);
1018 GetWindowText(editctl, *state->commentptr, len + 1);
1020 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1023 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1030 EnableWindow(hwnd, 0);
1031 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
1032 EnableWindow(hwnd, 1);
1033 SetActiveWindow(hwnd);
1036 if (HIWORD(wParam) == BN_CLICKED ||
1037 HIWORD(wParam) == BN_DOUBLECLICKED) {
1039 WinHelp(hwnd, help_path, HELP_COMMAND,
1040 (DWORD)"JI(`',`puttygen.general')");
1041 requested_help = TRUE;
1047 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1048 if (!state->generation_thread_exists) {
1050 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1052 state->keysize = DEFAULT_KEYSIZE;
1053 /* If we ever introduce a new key type, check it here! */
1054 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1055 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1056 if (state->keysize < 256) {
1057 int ret = MessageBox(hwnd,
1058 "PuTTYgen will not generate a key"
1059 " smaller than 256 bits.\n"
1060 "Key length reset to 256. Continue?",
1062 MB_ICONWARNING | MB_OKCANCEL);
1065 state->keysize = 256;
1066 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1068 ui_set_state(hwnd, state, 1);
1069 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1070 state->key_exists = FALSE;
1071 state->collecting_entropy = TRUE;
1074 * My brief statistical tests on mouse movements
1075 * suggest that there are about 2.5 bits of
1076 * randomness in the x position, 2.5 in the y
1077 * position, and 1.7 in the message time, making
1078 * 5.7 bits of unpredictability per mouse movement.
1079 * However, other people have told me it's far less
1080 * than that, so I'm going to be stupidly cautious
1081 * and knock that down to a nice round 2. With this
1082 * method, we require two words per mouse movement,
1083 * so with 2 bits per mouse movement we expect 2
1084 * bits every 2 words.
1086 state->entropy_required = (state->keysize / 2) * 2;
1087 state->entropy_got = 0;
1088 state->entropy_size = (state->entropy_required *
1089 sizeof(*state->entropy));
1090 state->entropy = smalloc(state->entropy_size);
1092 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1093 MAKELPARAM(0, state->entropy_required));
1094 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1098 case IDC_EXPORT_OPENSSH:
1099 case IDC_EXPORT_SSHCOM:
1101 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1102 if (state->key_exists) {
1103 char filename[FILENAME_MAX];
1104 char passphrase[PASSPHRASE_MAXLEN];
1105 char passphrase2[PASSPHRASE_MAXLEN];
1109 realtype = SSH_KEYTYPE_SSH2;
1111 realtype = SSH_KEYTYPE_SSH1;
1113 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1114 type = SSH_KEYTYPE_OPENSSH;
1115 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1116 type = SSH_KEYTYPE_SSHCOM;
1120 if (type != realtype &&
1121 import_target_type(type) != realtype) {
1123 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1124 " format", (state->ssh2 ? 2 : 1),
1125 (state->ssh2 ? 1 : 2));
1126 MessageBox(hwnd, msg,
1127 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1131 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1132 passphrase, sizeof(passphrase));
1133 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1134 passphrase2, sizeof(passphrase2));
1135 if (strcmp(passphrase, passphrase2)) {
1137 "The two passphrases given do not match.",
1138 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1143 ret = MessageBox(hwnd,
1144 "Are you sure you want to save this key\n"
1145 "without a passphrase to protect it?",
1147 MB_YESNO | MB_ICONWARNING);
1151 if (prompt_keyfile(hwnd, "Save private key as:",
1152 filename, 1, (type == realtype))) {
1154 FILE *fp = fopen(filename, "r");
1158 buffer = dupprintf("Overwrite existing file\n%s?",
1160 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1161 MB_YESNO | MB_ICONWARNING);
1168 if (type != realtype)
1169 ret = export_ssh2(filename, type, &state->ssh2key,
1170 *passphrase ? passphrase : NULL);
1172 ret = ssh2_save_userkey(filename, &state->ssh2key,
1173 *passphrase ? passphrase :
1176 if (type != realtype)
1177 ret = export_ssh1(filename, type, &state->key,
1178 *passphrase ? passphrase : NULL);
1180 ret = saversakey(filename, &state->key,
1181 *passphrase ? passphrase : NULL);
1184 MessageBox(hwnd, "Unable to save key file",
1185 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1192 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1193 if (state->key_exists) {
1194 char filename[FILENAME_MAX];
1195 if (prompt_keyfile(hwnd, "Save public key as:",
1198 FILE *fp = fopen(filename, "r");
1202 buffer = dupprintf("Overwrite existing file\n%s?",
1204 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1205 MB_YESNO | MB_ICONWARNING);
1211 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1213 ret = save_ssh1_pubkey(filename, &state->key);
1216 MessageBox(hwnd, "Unable to save key file",
1217 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1225 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1226 if (!state->generation_thread_exists) {
1227 char filename[FILENAME_MAX];
1228 if (prompt_keyfile(hwnd, "Load private key:",
1229 filename, 0, LOWORD(wParam)==IDC_LOAD))
1230 load_key_file(hwnd, state, filename,
1231 LOWORD(wParam) != IDC_LOAD);
1237 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1238 state->generation_thread_exists = FALSE;
1239 state->key_exists = TRUE;
1240 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1241 MAKELPARAM(0, PROGRESSRANGE));
1242 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1244 if (state->is_dsa) {
1245 state->ssh2key.data = &state->dsskey;
1246 state->ssh2key.alg = &ssh_dss;
1248 state->ssh2key.data = &state->key;
1249 state->ssh2key.alg = &ssh_rsa;
1251 state->commentptr = &state->ssh2key.comment;
1253 state->commentptr = &state->key.comment;
1256 * Invent a comment for the key. We'll do this by including
1257 * the date in it. This will be so horrifyingly ugly that
1258 * the user will immediately want to change it, which is
1261 *state->commentptr = smalloc(30);
1268 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1270 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1274 * Now update the key controls with all the key data.
1279 * Blank passphrase, initially. This isn't dangerous,
1280 * because we will warn (Are You Sure?) before allowing
1281 * the user to save an unprotected private key.
1283 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1284 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1288 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1290 * Set the key fingerprint.
1292 savecomment = *state->commentptr;
1293 *state->commentptr = NULL;
1296 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1297 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1301 rsa_fingerprint(buf, sizeof(buf), &state->key);
1302 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1304 *state->commentptr = savecomment;
1306 * Construct a decimal representation of the key, for
1307 * pasting into .ssh/authorized_keys or
1308 * .ssh/authorized_keys2 on a Unix box.
1311 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1312 IDC_PKSTATIC, &state->ssh2key);
1314 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1315 IDC_PKSTATIC, &state->key);
1319 * Finally, hide the progress bar and show the key data.
1321 ui_set_state(hwnd, state, 2);
1325 int id = ((LPHELPINFO)lParam)->iCtrlId;
1328 case IDC_GENERATING:
1332 cmd = "JI(`',`puttygen.generate')"; break;
1334 case IDC_KEYDISPLAY:
1335 cmd = "JI(`',`puttygen.pastekey')"; break;
1337 case IDC_FINGERPRINT:
1338 cmd = "JI(`',`puttygen.fingerprint')"; break;
1339 case IDC_COMMENTSTATIC:
1340 case IDC_COMMENTEDIT:
1341 cmd = "JI(`',`puttygen.comment')"; break;
1342 case IDC_PASSPHRASE1STATIC:
1343 case IDC_PASSPHRASE1EDIT:
1344 case IDC_PASSPHRASE2STATIC:
1345 case IDC_PASSPHRASE2EDIT:
1346 cmd = "JI(`',`puttygen.passphrase')"; break;
1347 case IDC_LOADSTATIC:
1349 cmd = "JI(`',`puttygen.load')"; break;
1350 case IDC_SAVESTATIC:
1352 cmd = "JI(`',`puttygen.savepriv')"; break;
1354 cmd = "JI(`',`puttygen.savepub')"; break;
1355 case IDC_TYPESTATIC:
1357 case IDC_KEYSSH2RSA:
1358 case IDC_KEYSSH2DSA:
1359 cmd = "JI(`',`puttygen.keytype')"; break;
1360 case IDC_BITSSTATIC:
1362 cmd = "JI(`',`puttygen.bits')"; break;
1364 case IDC_EXPORT_OPENSSH:
1365 case IDC_EXPORT_SSHCOM:
1366 cmd = "JI(`',`puttygen.conversions')"; break;
1369 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1370 requested_help = TRUE;
1377 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1379 if (requested_help) {
1380 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1381 requested_help = FALSE;
1389 void cleanup_exit(int code) { exit(code); }
1391 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1396 split_into_argv(cmdline, &argc, &argv, NULL);
1400 * Assume the first argument to be a private key file, and
1401 * attempt to load it.
1403 cmdline_keyfile = argv[0];
1406 InitCommonControls();
1410 * See if we can find our Help file.
1413 char b[2048], *p, *q, *r;
1415 GetModuleFileName(NULL, b, sizeof(b) - 1);
1417 p = strrchr(b, '\\');
1418 if (p && p >= r) r = p+1;
1419 q = strrchr(b, ':');
1420 if (q && q >= r) r = q+1;
1421 strcpy(r, "putty.hlp");
1422 if ( (fp = fopen(b, "r")) != NULL) {
1423 help_path = dupstr(b);
1430 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1431 MainDlgProc) != IDOK;