2 * PuTTY key generation front end.
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)) {
268 * Dialog-box function for the About box.
270 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
271 WPARAM wParam, LPARAM lParam)
278 { /* centre the window */
282 hw = GetDesktopWindow();
283 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
285 (rs.right + rs.left + rd.left - rd.right) / 2,
286 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
287 rd.right - rd.left, rd.bottom - rd.top, TRUE);
290 SetDlgItemText(hwnd, 100, ver);
293 switch (LOWORD(wParam)) {
298 EnableWindow(hwnd, 0);
299 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
300 EnableWindow(hwnd, 1);
301 SetActiveWindow(hwnd);
313 * Thread to generate a key.
315 struct rsa_key_thread_params {
316 HWND progressbar; /* notify this with progress */
317 HWND dialog; /* notify this on completion */
318 int keysize; /* bits in key */
321 struct dss_key *dsskey;
323 static DWORD WINAPI generate_rsa_key_thread(void *param)
325 struct rsa_key_thread_params *params =
326 (struct rsa_key_thread_params *) param;
327 struct progress prog;
328 prog.progbar = params->progressbar;
330 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
333 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
335 rsa_generate(params->key, params->keysize, progress_update, &prog);
337 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
343 struct MainDlgState {
344 int collecting_entropy;
345 int generation_thread_exists;
347 int entropy_got, entropy_required, entropy_size;
350 char **commentptr; /* points to key.comment or ssh2key.comment */
351 struct ssh2_userkey ssh2key;
354 struct dss_key dsskey;
355 HMENU filemenu, keymenu, cvtmenu;
358 static void hidemany(HWND hwnd, const int *ids, int hideit)
361 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
365 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
370 dec1 = bignum_decimal(key->exponent);
371 dec2 = bignum_decimal(key->modulus);
372 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
373 dec1, dec2, key->comment);
374 SetDlgItemText(hwnd, id, buffer);
375 SetDlgItemText(hwnd, idstatic,
376 "&Public key for pasting into authorized_keys file:");
382 static void setupbigedit2(HWND hwnd, int id, int idstatic,
383 struct ssh2_userkey *key)
385 unsigned char *pub_blob;
390 pub_blob = key->alg->public_blob(key->data, &pub_len);
391 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
392 strlen(key->comment) + 3, char);
393 strcpy(buffer, key->alg->name);
394 p = buffer + strlen(buffer);
397 while (i < pub_len) {
398 int n = (pub_len - i < 3 ? pub_len - i : 3);
399 base64_encode_atom(pub_blob + i, n, p);
404 strcpy(p, key->comment);
405 SetDlgItemText(hwnd, id, buffer);
406 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
407 "OpenSSH authorized_keys file:");
412 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
417 dec1 = bignum_decimal(key->exponent);
418 dec2 = bignum_decimal(key->modulus);
419 fp = fopen(filename, "wb");
422 fprintf(fp, "%d %s %s %s\n",
423 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
431 * Warn about the obsolescent key file format.
433 void old_keyfile_warning(void)
435 static const char mbtitle[] = "PuTTY Key File Warning";
436 static const char message[] =
437 "You are loading an SSH 2 private key which has an\n"
438 "old version of the file format. This means your key\n"
439 "file is not fully tamperproof. Future versions of\n"
440 "PuTTY may stop supporting this private key format,\n"
441 "so we recommend you convert your key to the new\n"
444 "Once the key is loaded into PuTTYgen, you can perform\n"
445 "this conversion simply by saving it again.";
447 MessageBox(NULL, message, mbtitle, MB_OK);
450 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
452 unsigned char *pub_blob;
458 pub_blob = key->alg->public_blob(key->data, &pub_len);
460 fp = fopen(filename, "wb");
464 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
466 fprintf(fp, "Comment: \"");
467 for (p = key->comment; *p; p++) {
468 if (*p == '\\' || *p == '\"')
476 while (i < pub_len) {
478 int n = (pub_len - i < 3 ? pub_len - i : 3);
479 base64_encode_atom(pub_blob + i, n, buf);
483 if (++column >= 16) {
491 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
498 controlidstart = 100,
505 IDC_PKSTATIC, IDC_KEYDISPLAY,
506 IDC_FPSTATIC, IDC_FINGERPRINT,
507 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
508 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
509 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
511 IDC_GENSTATIC, IDC_GENERATE,
512 IDC_LOADSTATIC, IDC_LOAD,
513 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
515 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
516 IDC_BITSSTATIC, IDC_BITS,
519 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
522 static const int nokey_ids[] = { IDC_NOKEY, 0 };
523 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
524 static const int gotkey_ids[] = {
525 IDC_PKSTATIC, IDC_KEYDISPLAY,
526 IDC_FPSTATIC, IDC_FINGERPRINT,
527 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
528 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
529 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
533 * Small UI helper function to switch the state of the main dialog
534 * by enabling and disabling controls and menu items.
536 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
542 hidemany(hwnd, nokey_ids, FALSE);
543 hidemany(hwnd, generating_ids, TRUE);
544 hidemany(hwnd, gotkey_ids, TRUE);
545 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
546 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
547 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
548 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
549 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
550 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
551 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
553 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
554 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
555 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
556 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
557 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
558 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
559 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
560 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
561 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
562 MF_GRAYED|MF_BYCOMMAND);
563 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
564 MF_GRAYED|MF_BYCOMMAND);
566 case 1: /* generating key */
567 hidemany(hwnd, nokey_ids, TRUE);
568 hidemany(hwnd, generating_ids, FALSE);
569 hidemany(hwnd, gotkey_ids, TRUE);
570 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
571 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
572 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
573 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
574 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
575 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
576 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
577 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
578 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
579 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
580 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
581 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
582 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
583 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
584 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
587 MF_GRAYED|MF_BYCOMMAND);
588 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
589 MF_GRAYED|MF_BYCOMMAND);
592 hidemany(hwnd, nokey_ids, TRUE);
593 hidemany(hwnd, generating_ids, TRUE);
594 hidemany(hwnd, gotkey_ids, FALSE);
595 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
596 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
597 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
598 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
599 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
600 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
601 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
602 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
603 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
604 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
605 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
607 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
608 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
609 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
610 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
612 * Enable export menu items if and only if the key type
613 * supports this kind of export.
615 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
616 #define do_export_menuitem(x,y) \
617 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
618 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
619 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
620 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
621 #undef do_export_menuitem
626 void load_key_file(HWND hwnd, struct MainDlgState *state,
627 Filename filename, int was_import_cmd)
629 char passphrase[PASSPHRASE_MAXLEN];
634 struct PassphraseProcStruct pps;
635 struct RSAKey newkey1;
636 struct ssh2_userkey *newkey2 = NULL;
638 type = realtype = key_type(&filename);
639 if (type != SSH_KEYTYPE_SSH1 &&
640 type != SSH_KEYTYPE_SSH2 &&
641 !import_possible(type)) {
643 sprintf(msg, "Couldn't load private key (%s)",
644 key_type_to_str(type));
645 MessageBox(NULL, msg,
646 "PuTTYgen Error", MB_OK | MB_ICONERROR);
650 if (type != SSH_KEYTYPE_SSH1 &&
651 type != SSH_KEYTYPE_SSH2) {
653 type = import_target_type(type);
657 if (realtype == SSH_KEYTYPE_SSH1)
658 needs_pass = rsakey_encrypted(&filename, &comment);
659 else if (realtype == SSH_KEYTYPE_SSH2)
661 ssh2_userkey_encrypted(&filename, &comment);
663 needs_pass = import_encrypted(&filename, realtype,
665 pps.passphrase = passphrase;
666 pps.comment = comment;
670 dlgret = DialogBoxParam(hinst,
671 MAKEINTRESOURCE(210),
672 NULL, PassphraseProc,
680 if (type == SSH_KEYTYPE_SSH1) {
681 if (realtype == type)
682 ret = loadrsakey(&filename, &newkey1,
685 ret = import_ssh1(&filename, realtype,
686 &newkey1, passphrase);
688 if (realtype == type)
689 newkey2 = ssh2_load_userkey(&filename,
692 newkey2 = import_ssh2(&filename, realtype,
694 if (newkey2 == SSH2_WRONG_PASSPHRASE)
705 MessageBox(NULL, "Couldn't load private key.",
706 "PuTTYgen Error", MB_OK | MB_ICONERROR);
707 } else if (ret == 1) {
709 * Now update the key controls with all the
713 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
715 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
717 if (type == SSH_KEYTYPE_SSH1) {
722 state->commentptr = &state->key.comment;
723 state->key = newkey1;
726 * Set the key fingerprint.
728 savecomment = state->key.comment;
729 state->key.comment = NULL;
730 rsa_fingerprint(buf, sizeof(buf),
732 state->key.comment = savecomment;
734 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
736 * Construct a decimal representation
737 * of the key, for pasting into
738 * .ssh/authorized_keys on a Unix box.
740 setupbigedit1(hwnd, IDC_KEYDISPLAY,
741 IDC_PKSTATIC, &state->key);
748 &state->ssh2key.comment;
749 state->ssh2key = *newkey2; /* structure copy */
752 savecomment = state->ssh2key.comment;
753 state->ssh2key.comment = NULL;
756 fingerprint(state->ssh2key.data);
757 state->ssh2key.comment = savecomment;
759 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
762 setupbigedit2(hwnd, IDC_KEYDISPLAY,
763 IDC_PKSTATIC, &state->ssh2key);
765 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
769 * Finally, hide the progress bar and show
772 ui_set_state(hwnd, state, 2);
773 state->key_exists = TRUE;
776 * If the user has imported a foreign key
777 * using the Load command, let them know.
778 * If they've used the Import command, be
781 if (realtype != type && !was_import_cmd) {
783 sprintf(msg, "Successfully imported foreign key\n"
785 "To use this key with PuTTY, you need to\n"
786 "use the \"Save private key\" command to\n"
787 "save it in PuTTY's own format.",
788 key_type_to_str(realtype));
789 MessageBox(NULL, msg, "PuTTYgen Notice",
790 MB_OK | MB_ICONINFORMATION);
796 * Dialog-box function for the main PuTTYgen dialog box.
798 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
799 WPARAM wParam, LPARAM lParam)
801 static const char generating_msg[] =
802 "Please wait while a key is generated...";
803 static const char entropy_msg[] =
804 "Please generate some randomness by moving the mouse over the blank area.";
805 struct MainDlgState *state;
810 SetWindowLong(hwnd, GWL_EXSTYLE,
811 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
814 * If we add a Help button, this is where we destroy it
815 * if the help file isn't present.
818 requested_help = FALSE;
819 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
820 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
822 state = snew(struct MainDlgState);
823 state->generation_thread_exists = FALSE;
824 state->collecting_entropy = FALSE;
825 state->entropy = NULL;
826 state->key_exists = FALSE;
827 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
833 menu1 = CreateMenu();
834 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
835 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
836 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
837 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
838 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
839 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
840 state->filemenu = menu1;
842 menu1 = CreateMenu();
843 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
844 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
845 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
846 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
847 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
848 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
849 state->keymenu = menu1;
851 menu1 = CreateMenu();
852 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
853 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
854 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
855 "Export &OpenSSH key");
856 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
857 "Export &ssh.com key");
858 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
860 state->cvtmenu = menu1;
862 menu1 = CreateMenu();
863 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
865 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
866 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
874 { /* centre the window */
878 hw = GetDesktopWindow();
879 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
881 (rs.right + rs.left + rd.left - rd.right) / 2,
882 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
883 rd.right - rd.left, rd.bottom - rd.top, TRUE);
887 struct ctlpos cp, cp2;
889 /* Accelerators used: acglops1rbd */
891 ctlposinit(&cp, hwnd, 4, 4, 4);
892 beginbox(&cp, "Key", IDC_BOX_KEY);
894 statictext(&cp2, "No key.", 1, IDC_NOKEY);
896 statictext(&cp2, "", 1, IDC_GENERATING);
897 progressbar(&cp2, IDC_PROGRESS);
899 "&Public key for pasting into authorized_keys file:",
900 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
901 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
902 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
903 IDC_FINGERPRINT, 75);
904 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
906 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
907 IDC_COMMENTEDIT, 75);
908 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
909 IDC_PASSPHRASE1EDIT, 75);
910 staticpassedit(&cp, "C&onfirm passphrase:",
911 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
913 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
914 staticbtn(&cp, "Generate a public/private key pair",
915 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
916 staticbtn(&cp, "Load an existing private key file",
917 IDC_LOADSTATIC, "&Load", IDC_LOAD);
918 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
919 "Save p&ublic key", IDC_SAVEPUB,
920 "&Save private key", IDC_SAVE);
922 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
923 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
924 "SSH&1 (RSA)", IDC_KEYSSH1,
925 "SSH2 &RSA", IDC_KEYSSH2RSA,
926 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
927 staticedit(&cp, "Number of &bits in a generated key:",
928 IDC_BITSSTATIC, IDC_BITS, 20);
931 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
932 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
933 IDC_KEYSSH1, MF_BYCOMMAND);
934 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
937 * Initially, hide the progress bar and the key display,
938 * and show the no-key display. Also disable the Save
939 * buttons, because with no key we obviously can't save
942 ui_set_state(hwnd, state, 0);
945 * Load a key file if one was provided on the command line.
948 load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
952 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
953 if (state->collecting_entropy &&
954 state->entropy && state->entropy_got < state->entropy_required) {
955 state->entropy[state->entropy_got++] = lParam;
956 state->entropy[state->entropy_got++] = GetMessageTime();
957 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
958 state->entropy_got, 0);
959 if (state->entropy_got >= state->entropy_required) {
960 struct rsa_key_thread_params *params;
964 * Seed the entropy pool
966 random_add_heavynoise(state->entropy, state->entropy_size);
967 memset(state->entropy, 0, state->entropy_size);
968 sfree(state->entropy);
969 state->collecting_entropy = FALSE;
971 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
972 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
973 MAKELPARAM(0, PROGRESSRANGE));
974 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
976 params = snew(struct rsa_key_thread_params);
977 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
978 params->dialog = hwnd;
979 params->keysize = state->keysize;
980 params->is_dsa = state->is_dsa;
981 params->key = &state->key;
982 params->dsskey = &state->dsskey;
984 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
985 params, 0, &threadid)) {
986 MessageBox(hwnd, "Out of thread resources",
987 "Key generation error",
988 MB_OK | MB_ICONERROR);
991 state->generation_thread_exists = TRUE;
997 switch (LOWORD(wParam)) {
1000 case IDC_KEYSSH2DSA:
1002 state = (struct MainDlgState *)
1003 GetWindowLong(hwnd, GWL_USERDATA);
1004 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1005 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1007 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1008 LOWORD(wParam), MF_BYCOMMAND);
1012 PostMessage(hwnd, WM_CLOSE, 0, 0);
1014 case IDC_COMMENTEDIT:
1015 if (HIWORD(wParam) == EN_CHANGE) {
1016 state = (struct MainDlgState *)
1017 GetWindowLong(hwnd, GWL_USERDATA);
1018 if (state->key_exists) {
1019 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1020 int len = GetWindowTextLength(editctl);
1021 if (*state->commentptr)
1022 sfree(*state->commentptr);
1023 *state->commentptr = snewn(len + 1, char);
1024 GetWindowText(editctl, *state->commentptr, len + 1);
1026 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1029 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1036 EnableWindow(hwnd, 0);
1037 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1038 EnableWindow(hwnd, 1);
1039 SetActiveWindow(hwnd);
1042 if (HIWORD(wParam) == BN_CLICKED ||
1043 HIWORD(wParam) == BN_DOUBLECLICKED) {
1045 WinHelp(hwnd, help_path, HELP_COMMAND,
1046 (DWORD)"JI(`',`puttygen.general')");
1047 requested_help = TRUE;
1052 if (HIWORD(wParam) != BN_CLICKED &&
1053 HIWORD(wParam) != BN_DOUBLECLICKED)
1056 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1057 if (!state->generation_thread_exists) {
1059 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1061 state->keysize = DEFAULT_KEYSIZE;
1062 /* If we ever introduce a new key type, check it here! */
1063 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1064 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1065 if (state->keysize < 256) {
1066 int ret = MessageBox(hwnd,
1067 "PuTTYgen will not generate a key"
1068 " smaller than 256 bits.\n"
1069 "Key length reset to 256. Continue?",
1071 MB_ICONWARNING | MB_OKCANCEL);
1074 state->keysize = 256;
1075 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1077 ui_set_state(hwnd, state, 1);
1078 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1079 state->key_exists = FALSE;
1080 state->collecting_entropy = TRUE;
1083 * My brief statistical tests on mouse movements
1084 * suggest that there are about 2.5 bits of
1085 * randomness in the x position, 2.5 in the y
1086 * position, and 1.7 in the message time, making
1087 * 5.7 bits of unpredictability per mouse movement.
1088 * However, other people have told me it's far less
1089 * than that, so I'm going to be stupidly cautious
1090 * and knock that down to a nice round 2. With this
1091 * method, we require two words per mouse movement,
1092 * so with 2 bits per mouse movement we expect 2
1093 * bits every 2 words.
1095 state->entropy_required = (state->keysize / 2) * 2;
1096 state->entropy_got = 0;
1097 state->entropy_size = (state->entropy_required *
1099 state->entropy = snewn(state->entropy_required, unsigned);
1101 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1102 MAKELPARAM(0, state->entropy_required));
1103 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1107 case IDC_EXPORT_OPENSSH:
1108 case IDC_EXPORT_SSHCOM:
1109 if (HIWORD(wParam) != BN_CLICKED)
1112 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1113 if (state->key_exists) {
1114 char filename[FILENAME_MAX];
1115 char passphrase[PASSPHRASE_MAXLEN];
1116 char passphrase2[PASSPHRASE_MAXLEN];
1120 realtype = SSH_KEYTYPE_SSH2;
1122 realtype = SSH_KEYTYPE_SSH1;
1124 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1125 type = SSH_KEYTYPE_OPENSSH;
1126 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1127 type = SSH_KEYTYPE_SSHCOM;
1131 if (type != realtype &&
1132 import_target_type(type) != realtype) {
1134 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1135 " format", (state->ssh2 ? 2 : 1),
1136 (state->ssh2 ? 1 : 2));
1137 MessageBox(hwnd, msg,
1138 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1142 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1143 passphrase, sizeof(passphrase));
1144 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1145 passphrase2, sizeof(passphrase2));
1146 if (strcmp(passphrase, passphrase2)) {
1148 "The two passphrases given do not match.",
1149 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1154 ret = MessageBox(hwnd,
1155 "Are you sure you want to save this key\n"
1156 "without a passphrase to protect it?",
1158 MB_YESNO | MB_ICONWARNING);
1162 if (prompt_keyfile(hwnd, "Save private key as:",
1163 filename, 1, (type == realtype))) {
1165 FILE *fp = fopen(filename, "r");
1169 buffer = dupprintf("Overwrite existing file\n%s?",
1171 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1172 MB_YESNO | MB_ICONWARNING);
1179 Filename fn = filename_from_str(filename);
1180 if (type != realtype)
1181 ret = export_ssh2(&fn, type, &state->ssh2key,
1182 *passphrase ? passphrase : NULL);
1184 ret = ssh2_save_userkey(&fn, &state->ssh2key,
1185 *passphrase ? passphrase :
1188 Filename fn = filename_from_str(filename);
1189 if (type != realtype)
1190 ret = export_ssh1(&fn, type, &state->key,
1191 *passphrase ? passphrase : NULL);
1193 ret = saversakey(&fn, &state->key,
1194 *passphrase ? passphrase : NULL);
1197 MessageBox(hwnd, "Unable to save key file",
1198 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1204 if (HIWORD(wParam) != BN_CLICKED)
1207 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1208 if (state->key_exists) {
1209 char filename[FILENAME_MAX];
1210 if (prompt_keyfile(hwnd, "Save public key as:",
1213 FILE *fp = fopen(filename, "r");
1217 buffer = dupprintf("Overwrite existing file\n%s?",
1219 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1220 MB_YESNO | MB_ICONWARNING);
1226 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1228 ret = save_ssh1_pubkey(filename, &state->key);
1231 MessageBox(hwnd, "Unable to save key file",
1232 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1239 if (HIWORD(wParam) != BN_CLICKED)
1242 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1243 if (!state->generation_thread_exists) {
1244 char filename[FILENAME_MAX];
1245 if (prompt_keyfile(hwnd, "Load private key:",
1246 filename, 0, LOWORD(wParam)==IDC_LOAD))
1247 load_key_file(hwnd, state, filename_from_str(filename),
1248 LOWORD(wParam) != IDC_LOAD);
1254 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1255 state->generation_thread_exists = FALSE;
1256 state->key_exists = TRUE;
1257 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1258 MAKELPARAM(0, PROGRESSRANGE));
1259 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1261 if (state->is_dsa) {
1262 state->ssh2key.data = &state->dsskey;
1263 state->ssh2key.alg = &ssh_dss;
1265 state->ssh2key.data = &state->key;
1266 state->ssh2key.alg = &ssh_rsa;
1268 state->commentptr = &state->ssh2key.comment;
1270 state->commentptr = &state->key.comment;
1273 * Invent a comment for the key. We'll do this by including
1274 * the date in it. This will be so horrifyingly ugly that
1275 * the user will immediately want to change it, which is
1278 *state->commentptr = snewn(30, char);
1285 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1287 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1291 * Now update the key controls with all the key data.
1296 * Blank passphrase, initially. This isn't dangerous,
1297 * because we will warn (Are You Sure?) before allowing
1298 * the user to save an unprotected private key.
1300 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1301 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1305 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1307 * Set the key fingerprint.
1309 savecomment = *state->commentptr;
1310 *state->commentptr = NULL;
1313 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1314 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1318 rsa_fingerprint(buf, sizeof(buf), &state->key);
1319 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1321 *state->commentptr = savecomment;
1323 * Construct a decimal representation of the key, for
1324 * pasting into .ssh/authorized_keys or
1325 * .ssh/authorized_keys2 on a Unix box.
1328 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1329 IDC_PKSTATIC, &state->ssh2key);
1331 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1332 IDC_PKSTATIC, &state->key);
1336 * Finally, hide the progress bar and show the key data.
1338 ui_set_state(hwnd, state, 2);
1342 int id = ((LPHELPINFO)lParam)->iCtrlId;
1345 case IDC_GENERATING:
1349 cmd = "JI(`',`puttygen.generate')"; break;
1351 case IDC_KEYDISPLAY:
1352 cmd = "JI(`',`puttygen.pastekey')"; break;
1354 case IDC_FINGERPRINT:
1355 cmd = "JI(`',`puttygen.fingerprint')"; break;
1356 case IDC_COMMENTSTATIC:
1357 case IDC_COMMENTEDIT:
1358 cmd = "JI(`',`puttygen.comment')"; break;
1359 case IDC_PASSPHRASE1STATIC:
1360 case IDC_PASSPHRASE1EDIT:
1361 case IDC_PASSPHRASE2STATIC:
1362 case IDC_PASSPHRASE2EDIT:
1363 cmd = "JI(`',`puttygen.passphrase')"; break;
1364 case IDC_LOADSTATIC:
1366 cmd = "JI(`',`puttygen.load')"; break;
1367 case IDC_SAVESTATIC:
1369 cmd = "JI(`',`puttygen.savepriv')"; break;
1371 cmd = "JI(`',`puttygen.savepub')"; break;
1372 case IDC_TYPESTATIC:
1374 case IDC_KEYSSH2RSA:
1375 case IDC_KEYSSH2DSA:
1376 cmd = "JI(`',`puttygen.keytype')"; break;
1377 case IDC_BITSSTATIC:
1379 cmd = "JI(`',`puttygen.bits')"; break;
1381 case IDC_EXPORT_OPENSSH:
1382 case IDC_EXPORT_SSHCOM:
1383 cmd = "JI(`',`puttygen.conversions')"; break;
1386 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1387 requested_help = TRUE;
1394 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1396 if (requested_help) {
1397 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1398 requested_help = FALSE;
1406 void cleanup_exit(int code) { exit(code); }
1408 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1413 split_into_argv(cmdline, &argc, &argv, NULL);
1417 * Assume the first argument to be a private key file, and
1418 * attempt to load it.
1420 cmdline_keyfile = argv[0];
1423 InitCommonControls();
1427 * See if we can find our Help file.
1430 char b[2048], *p, *q, *r;
1432 GetModuleFileName(NULL, b, sizeof(b) - 1);
1434 p = strrchr(b, '\\');
1435 if (p && p >= r) r = p+1;
1436 q = strrchr(b, ':');
1437 if (q && q >= r) r = q+1;
1438 strcpy(r, "putty.hlp");
1439 if ( (fp = fopen(b, "r")) != NULL) {
1440 help_path = dupstr(b);
1447 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1448 MainDlgProc) != IDOK;