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 /* ----------------------------------------------------------------------
24 * Progress report code. This is really horrible :-)
26 #define PROGRESSRANGE 65535
32 unsigned startpoint, total;
33 unsigned param, current, n; /* if exponential */
34 unsigned mult; /* if linear */
36 unsigned total, divisor, range;
40 static void progress_update(void *param, int action, int phase, int iprogress)
42 struct progress *p = (struct progress *) param;
43 unsigned progress = iprogress;
46 if (action < PROGFN_READY && p->nphases < phase)
49 case PROGFN_INITIALISE:
52 case PROGFN_LIN_PHASE:
53 p->phases[phase-1].exponential = 0;
54 p->phases[phase-1].mult = p->phases[phase].total / progress;
56 case PROGFN_EXP_PHASE:
57 p->phases[phase-1].exponential = 1;
58 p->phases[phase-1].param = 0x10000 + progress;
59 p->phases[phase-1].current = p->phases[phase-1].total;
60 p->phases[phase-1].n = 0;
62 case PROGFN_PHASE_EXTENT:
63 p->phases[phase-1].total = progress;
69 for (i = 0; i < p->nphases; i++) {
70 p->phases[i].startpoint = total;
71 total += p->phases[i].total;
74 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
75 p->range = p->total / p->divisor;
76 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
80 if (p->phases[phase-1].exponential) {
81 while (p->phases[phase-1].n < progress) {
82 p->phases[phase-1].n++;
83 p->phases[phase-1].current *= p->phases[phase-1].param;
84 p->phases[phase-1].current /= 0x10000;
86 position = (p->phases[phase-1].startpoint +
87 p->phases[phase-1].total - p->phases[phase-1].current);
89 position = (p->phases[phase-1].startpoint +
90 progress * p->phases[phase-1].mult);
92 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
99 #define PASSPHRASE_MAXLEN 512
101 struct PassphraseProcStruct {
107 * Dialog-box function for the passphrase box.
109 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
110 WPARAM wParam, LPARAM lParam)
112 static char *passphrase = NULL;
113 struct PassphraseProcStruct *p;
117 SetForegroundWindow(hwnd);
118 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
119 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
124 { /* centre the window */
128 hw = GetDesktopWindow();
129 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
131 (rs.right + rs.left + rd.left - rd.right) / 2,
132 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
133 rd.right - rd.left, rd.bottom - rd.top, TRUE);
136 p = (struct PassphraseProcStruct *) lParam;
137 passphrase = p->passphrase;
139 SetDlgItemText(hwnd, 101, p->comment);
141 SetDlgItemText(hwnd, 102, passphrase);
144 switch (LOWORD(wParam)) {
154 case 102: /* edit box */
155 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
156 GetDlgItemText(hwnd, 102, passphrase,
157 PASSPHRASE_MAXLEN - 1);
158 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
171 * Prompt for a key file. Assumes the filename buffer is of size
174 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
175 char *filename, int save)
178 memset(&of, 0, sizeof(of));
179 #ifdef OPENFILENAME_SIZE_VERSION_400
180 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
182 of.lStructSize = sizeof(of);
185 of.lpstrFilter = "All Files\0*\0\0\0";
186 of.lpstrCustomFilter = NULL;
188 of.lpstrFile = filename;
190 of.nMaxFile = FILENAME_MAX;
191 of.lpstrFileTitle = NULL;
192 of.lpstrInitialDir = NULL;
193 of.lpstrTitle = dlgtitle;
196 return GetSaveFileName(&of);
198 return GetOpenFileName(&of);
202 * This function is needed to link with the DES code. We need not
203 * have it do anything at all.
205 void logevent(char *msg)
210 * Dialog-box function for the Licence box.
212 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
213 WPARAM wParam, LPARAM lParam)
220 { /* centre the window */
224 hw = GetDesktopWindow();
225 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
227 (rs.right + rs.left + rd.left - rd.right) / 2,
228 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
229 rd.right - rd.left, rd.bottom - rd.top, TRUE);
234 switch (LOWORD(wParam)) {
248 * Dialog-box function for the About box.
250 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
251 WPARAM wParam, LPARAM lParam)
258 { /* centre the window */
262 hw = GetDesktopWindow();
263 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
265 (rs.right + rs.left + rd.left - rd.right) / 2,
266 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
267 rd.right - rd.left, rd.bottom - rd.top, TRUE);
270 SetDlgItemText(hwnd, 100, ver);
273 switch (LOWORD(wParam)) {
278 EnableWindow(hwnd, 0);
279 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
280 EnableWindow(hwnd, 1);
281 SetActiveWindow(hwnd);
293 * Thread to generate a key.
295 struct rsa_key_thread_params {
296 HWND progressbar; /* notify this with progress */
297 HWND dialog; /* notify this on completion */
298 int keysize; /* bits in key */
301 struct dss_key *dsskey;
303 static DWORD WINAPI generate_rsa_key_thread(void *param)
305 struct rsa_key_thread_params *params =
306 (struct rsa_key_thread_params *) param;
307 struct progress prog;
308 prog.progbar = params->progressbar;
310 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
313 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
315 rsa_generate(params->key, params->keysize, progress_update, &prog);
317 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
323 struct MainDlgState {
324 int collecting_entropy;
325 int generation_thread_exists;
327 int entropy_got, entropy_required, entropy_size;
330 char **commentptr; /* points to key.comment or ssh2key.comment */
331 struct ssh2_userkey ssh2key;
334 struct dss_key dsskey;
337 static void hidemany(HWND hwnd, const int *ids, int hideit)
340 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
344 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
349 dec1 = bignum_decimal(key->exponent);
350 dec2 = bignum_decimal(key->modulus);
351 buffer = smalloc(strlen(dec1) + strlen(dec2) +
352 strlen(key->comment) + 30);
353 sprintf(buffer, "%d %s %s %s",
354 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
355 SetDlgItemText(hwnd, id, buffer);
356 SetDlgItemText(hwnd, idstatic,
357 "&Public key for pasting into authorized_keys file:");
363 static void setupbigedit2(HWND hwnd, int id, int idstatic,
364 struct ssh2_userkey *key)
366 unsigned char *pub_blob;
371 pub_blob = key->alg->public_blob(key->data, &pub_len);
372 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
373 strlen(key->comment) + 3);
374 strcpy(buffer, key->alg->name);
375 p = buffer + strlen(buffer);
378 while (i < pub_len) {
379 int n = (pub_len - i < 3 ? pub_len - i : 3);
380 base64_encode_atom(pub_blob + i, n, p);
385 strcpy(p, key->comment);
386 SetDlgItemText(hwnd, id, buffer);
387 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
388 "OpenSSH authorized_keys2 file:");
393 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
398 dec1 = bignum_decimal(key->exponent);
399 dec2 = bignum_decimal(key->modulus);
400 fp = fopen(filename, "wb");
403 fprintf(fp, "%d %s %s %s\n",
404 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
412 * Warn about the obsolescent key file format.
414 void old_keyfile_warning(void)
416 static const char mbtitle[] = "PuTTY Key File Warning";
417 static const char message[] =
418 "You are loading an SSH 2 private key which has an\n"
419 "old version of the file format. This means your key\n"
420 "file is not fully tamperproof. Future versions of\n"
421 "PuTTY may stop supporting this private key format,\n"
422 "so we recommend you convert your key to the new\n"
425 "Once the key is loaded into PuTTYgen, you can perform\n"
426 "this conversion simply by saving it again.";
428 MessageBox(NULL, message, mbtitle, MB_OK);
431 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
433 unsigned char *pub_blob;
439 pub_blob = key->alg->public_blob(key->data, &pub_len);
441 fp = fopen(filename, "wb");
445 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
447 fprintf(fp, "Comment: \"");
448 for (p = key->comment; *p; p++) {
449 if (*p == '\\' || *p == '\"')
457 while (i < pub_len) {
459 int n = (pub_len - i < 3 ? pub_len - i : 3);
460 base64_encode_atom(pub_blob + i, n, buf);
464 if (++column >= 16) {
472 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
479 * Dialog-box function for the main PuTTYgen dialog box.
481 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
482 WPARAM wParam, LPARAM lParam)
485 controlidstart = 100,
491 IDC_PKSTATIC, IDC_KEYDISPLAY,
492 IDC_FPSTATIC, IDC_FINGERPRINT,
493 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
494 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
495 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
497 IDC_GENSTATIC, IDC_GENERATE,
498 IDC_LOADSTATIC, IDC_LOAD,
499 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
501 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
502 IDC_BITSSTATIC, IDC_BITS,
505 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
507 static const int nokey_ids[] = { IDC_NOKEY, 0 };
508 static const int generating_ids[] =
509 { IDC_GENERATING, IDC_PROGRESS, 0 };
510 static const int gotkey_ids[] = {
511 IDC_PKSTATIC, IDC_KEYDISPLAY,
512 IDC_FPSTATIC, IDC_FINGERPRINT,
513 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
514 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
515 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
517 static const char generating_msg[] =
518 "Please wait while a key is generated...";
519 static const char entropy_msg[] =
520 "Please generate some randomness by moving the mouse over the blank area.";
521 struct MainDlgState *state;
526 SetWindowLong(hwnd, GWL_EXSTYLE,
527 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
530 * If we add a Help button, this is where we destroy it
531 * if the help file isn't present.
534 requested_help = FALSE;
541 menu1 = CreateMenu();
542 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
543 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
544 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
545 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
547 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
549 menu1 = CreateMenu();
550 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
551 "Export &OpenSSH key");
552 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
553 "Export &ssh.com key");
555 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
558 menu1 = CreateMenu();
559 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
561 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
563 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
571 { /* centre the window */
575 hw = GetDesktopWindow();
576 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
578 (rs.right + rs.left + rd.left - rd.right) / 2,
579 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
580 rd.right - rd.left, rd.bottom - rd.top, TRUE);
583 state = smalloc(sizeof(*state));
584 state->generation_thread_exists = FALSE;
585 state->collecting_entropy = FALSE;
586 state->entropy = NULL;
587 state->key_exists = FALSE;
588 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
590 struct ctlpos cp, cp2;
592 /* Accelerators used: acglops1rbd */
594 ctlposinit(&cp, hwnd, 4, 4, 4);
595 beginbox(&cp, "Key", IDC_BOX_KEY);
597 statictext(&cp2, "No key.", 1, IDC_NOKEY);
599 statictext(&cp2, "", 1, IDC_GENERATING);
600 progressbar(&cp2, IDC_PROGRESS);
602 "&Public key for pasting into authorized_keys file:",
603 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
604 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
605 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
606 IDC_FINGERPRINT, 75);
607 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
609 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
610 IDC_COMMENTEDIT, 75);
611 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
612 IDC_PASSPHRASE1EDIT, 75);
613 staticpassedit(&cp, "C&onfirm passphrase:",
614 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
616 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
617 staticbtn(&cp, "Generate a public/private key pair",
618 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
619 staticbtn(&cp, "Load an existing private key file",
620 IDC_LOADSTATIC, "&Load", IDC_LOAD);
621 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
622 "Save p&ublic key", IDC_SAVEPUB,
623 "&Save private key", IDC_SAVE);
625 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
626 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
627 "SSH&1 (RSA)", IDC_KEYSSH1,
628 "SSH2 &RSA", IDC_KEYSSH2RSA,
629 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
630 staticedit(&cp, "Number of &bits in a generated key:",
631 IDC_BITSSTATIC, IDC_BITS, 20);
634 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
635 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
638 * Initially, hide the progress bar and the key display,
639 * and show the no-key display. Also disable the Save
640 * buttons, because with no key we obviously can't save
643 hidemany(hwnd, nokey_ids, FALSE);
644 hidemany(hwnd, generating_ids, TRUE);
645 hidemany(hwnd, gotkey_ids, TRUE);
646 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
647 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
651 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
652 if (state->collecting_entropy &&
653 state->entropy && state->entropy_got < state->entropy_required) {
654 state->entropy[state->entropy_got++] = lParam;
655 state->entropy[state->entropy_got++] = GetMessageTime();
656 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
657 state->entropy_got, 0);
658 if (state->entropy_got >= state->entropy_required) {
659 struct rsa_key_thread_params *params;
663 * Seed the entropy pool
665 random_add_heavynoise(state->entropy, state->entropy_size);
666 memset(state->entropy, 0, state->entropy_size);
667 sfree(state->entropy);
668 state->collecting_entropy = FALSE;
670 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
671 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
672 MAKELPARAM(0, PROGRESSRANGE));
673 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
675 params = smalloc(sizeof(*params));
676 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
677 params->dialog = hwnd;
678 params->keysize = state->keysize;
679 params->is_dsa = state->is_dsa;
680 params->key = &state->key;
681 params->dsskey = &state->dsskey;
683 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
684 params, 0, &threadid)) {
685 MessageBox(hwnd, "Out of thread resources",
686 "Key generation error",
687 MB_OK | MB_ICONERROR);
690 state->generation_thread_exists = TRUE;
696 switch (LOWORD(wParam)) {
697 case IDC_COMMENTEDIT:
698 if (HIWORD(wParam) == EN_CHANGE) {
699 state = (struct MainDlgState *)
700 GetWindowLong(hwnd, GWL_USERDATA);
701 if (state->key_exists) {
702 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
703 int len = GetWindowTextLength(editctl);
704 if (*state->commentptr)
705 sfree(*state->commentptr);
706 *state->commentptr = smalloc(len + 1);
707 GetWindowText(editctl, *state->commentptr, len + 1);
709 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
712 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
719 EnableWindow(hwnd, 0);
720 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
721 EnableWindow(hwnd, 1);
722 SetActiveWindow(hwnd);
725 if (HIWORD(wParam) == BN_CLICKED ||
726 HIWORD(wParam) == BN_DOUBLECLICKED) {
728 WinHelp(hwnd, help_path, HELP_COMMAND,
729 (DWORD)"JI(`',`puttygen.general')");
730 requested_help = TRUE;
736 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
737 if (!state->generation_thread_exists) {
739 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
741 state->keysize = DEFAULT_KEYSIZE;
742 /* If we ever introduce a new key type, check it here! */
743 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
744 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
745 if (state->keysize < 256) {
746 int ret = MessageBox(hwnd,
747 "PuTTYgen will not generate a key"
748 " smaller than 256 bits.\n"
749 "Key length reset to 256. Continue?",
751 MB_ICONWARNING | MB_OKCANCEL);
754 state->keysize = 256;
755 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
757 hidemany(hwnd, nokey_ids, TRUE);
758 hidemany(hwnd, generating_ids, FALSE);
759 hidemany(hwnd, gotkey_ids, TRUE);
760 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
761 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
762 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
763 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
764 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
765 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
766 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
767 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
768 state->key_exists = FALSE;
769 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
770 state->collecting_entropy = TRUE;
773 * My brief statistical tests on mouse movements
774 * suggest that there are about 2.5 bits of
775 * randomness in the x position, 2.5 in the y
776 * position, and 1.7 in the message time, making
777 * 5.7 bits of unpredictability per mouse movement.
778 * However, other people have told me it's far less
779 * than that, so I'm going to be stupidly cautious
780 * and knock that down to a nice round 2. With this
781 * method, we require two words per mouse movement,
782 * so with 2 bits per mouse movement we expect 2
783 * bits every 2 words.
785 state->entropy_required = (state->keysize / 2) * 2;
786 state->entropy_got = 0;
787 state->entropy_size = (state->entropy_required *
788 sizeof(*state->entropy));
789 state->entropy = smalloc(state->entropy_size);
791 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
792 MAKELPARAM(0, state->entropy_required));
793 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
797 case IDC_EXPORT_OPENSSH:
798 case IDC_EXPORT_SSHCOM:
800 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
801 if (state->key_exists) {
802 char filename[FILENAME_MAX];
803 char passphrase[PASSPHRASE_MAXLEN];
804 char passphrase2[PASSPHRASE_MAXLEN];
808 realtype = SSH_KEYTYPE_SSH2;
810 realtype = SSH_KEYTYPE_SSH1;
812 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
813 type = SSH_KEYTYPE_OPENSSH;
814 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
815 type = SSH_KEYTYPE_SSHCOM;
819 if (type != realtype &&
820 import_target_type(type) != realtype) {
822 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
823 " format", (state->ssh2 ? 2 : 1),
824 (state->ssh2 ? 1 : 2));
825 MessageBox(hwnd, msg,
826 "PuTTYgen Error", MB_OK | MB_ICONERROR);
830 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
831 passphrase, sizeof(passphrase));
832 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
833 passphrase2, sizeof(passphrase2));
834 if (strcmp(passphrase, passphrase2)) {
836 "The two passphrases given do not match.",
837 "PuTTYgen Error", MB_OK | MB_ICONERROR);
842 ret = MessageBox(hwnd,
843 "Are you sure you want to save this key\n"
844 "without a passphrase to protect it?",
846 MB_YESNO | MB_ICONWARNING);
850 if (prompt_keyfile(hwnd, "Save private key as:",
853 FILE *fp = fopen(filename, "r");
855 char buffer[FILENAME_MAX + 80];
857 sprintf(buffer, "Overwrite existing file\n%.*s?",
858 FILENAME_MAX, filename);
859 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
860 MB_YESNO | MB_ICONWARNING);
866 if (type != realtype)
867 ret = export_ssh2(filename, type, &state->ssh2key,
868 *passphrase ? passphrase : NULL);
870 ret = ssh2_save_userkey(filename, &state->ssh2key,
871 *passphrase ? passphrase :
874 if (type != realtype)
875 ret = export_ssh1(filename, type, &state->key,
876 *passphrase ? passphrase : NULL);
878 ret = saversakey(filename, &state->key,
879 *passphrase ? passphrase : NULL);
882 MessageBox(hwnd, "Unable to save key file",
883 "PuTTYgen Error", MB_OK | MB_ICONERROR);
890 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
891 if (state->key_exists) {
892 char filename[FILENAME_MAX];
893 if (prompt_keyfile(hwnd, "Save public key as:",
896 FILE *fp = fopen(filename, "r");
898 char buffer[FILENAME_MAX + 80];
900 sprintf(buffer, "Overwrite existing file\n%.*s?",
901 FILENAME_MAX, filename);
902 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
903 MB_YESNO | MB_ICONWARNING);
908 ret = save_ssh2_pubkey(filename, &state->ssh2key);
910 ret = save_ssh1_pubkey(filename, &state->key);
913 MessageBox(hwnd, "Unable to save key file",
914 "PuTTYgen Error", MB_OK | MB_ICONERROR);
921 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
922 if (!state->generation_thread_exists) {
923 char filename[FILENAME_MAX];
924 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
925 char passphrase[PASSPHRASE_MAXLEN];
930 struct PassphraseProcStruct pps;
931 struct RSAKey newkey1;
932 struct ssh2_userkey *newkey2 = NULL;
934 type = realtype = key_type(filename);
935 if (type != SSH_KEYTYPE_SSH1 &&
936 type != SSH_KEYTYPE_SSH2 &&
937 !import_possible(type)) {
939 sprintf(msg, "Couldn't load private key (%s)",
940 key_type_to_str(type));
941 MessageBox(NULL, msg,
942 "PuTTYgen Error", MB_OK | MB_ICONERROR);
946 if (type != SSH_KEYTYPE_SSH1 &&
947 type != SSH_KEYTYPE_SSH2) {
949 type = import_target_type(type);
953 if (realtype == SSH_KEYTYPE_SSH1)
954 needs_pass = rsakey_encrypted(filename, &comment);
955 else if (realtype == SSH_KEYTYPE_SSH2)
957 ssh2_userkey_encrypted(filename, &comment);
959 needs_pass = import_encrypted(filename, realtype,
961 pps.passphrase = passphrase;
962 pps.comment = comment;
966 dlgret = DialogBoxParam(hinst,
967 MAKEINTRESOURCE(210),
968 NULL, PassphraseProc,
976 if (type == SSH_KEYTYPE_SSH1) {
977 if (realtype == type)
978 ret = loadrsakey(filename, &newkey1,
981 ret = import_ssh1(filename, realtype,
982 &newkey1, passphrase);
984 if (realtype == type)
985 newkey2 = ssh2_load_userkey(filename,
988 newkey2 = import_ssh2(filename, realtype,
990 if (newkey2 == SSH2_WRONG_PASSPHRASE)
1001 MessageBox(NULL, "Couldn't load private key.",
1002 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1003 } else if (ret == 1) {
1004 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1005 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1006 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
1007 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1008 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1009 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
1010 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1011 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1013 * Now update the key controls with all the
1017 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1019 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1021 if (type == SSH_KEYTYPE_SSH1) {
1025 state->ssh2 = FALSE;
1026 state->commentptr = &state->key.comment;
1027 state->key = newkey1;
1030 * Set the key fingerprint.
1032 savecomment = state->key.comment;
1033 state->key.comment = NULL;
1034 rsa_fingerprint(buf, sizeof(buf),
1036 state->key.comment = savecomment;
1038 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1040 * Construct a decimal representation
1041 * of the key, for pasting into
1042 * .ssh/authorized_keys on a Unix box.
1044 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1045 IDC_PKSTATIC, &state->key);
1052 &state->ssh2key.comment;
1053 state->ssh2key = *newkey2; /* structure copy */
1056 savecomment = state->ssh2key.comment;
1057 state->ssh2key.comment = NULL;
1059 state->ssh2key.alg->
1060 fingerprint(state->ssh2key.data);
1061 state->ssh2key.comment = savecomment;
1063 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1066 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1067 IDC_PKSTATIC, &state->ssh2key);
1069 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1070 *state->commentptr);
1073 * Finally, hide the progress bar and show
1076 hidemany(hwnd, nokey_ids, TRUE);
1077 hidemany(hwnd, generating_ids, TRUE);
1078 hidemany(hwnd, gotkey_ids, FALSE);
1079 state->key_exists = TRUE;
1087 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1088 state->generation_thread_exists = FALSE;
1089 state->key_exists = TRUE;
1090 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1091 MAKELPARAM(0, PROGRESSRANGE));
1092 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1093 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1094 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1095 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
1096 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1097 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1098 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
1099 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1100 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1102 if (state->is_dsa) {
1103 state->ssh2key.data = &state->dsskey;
1104 state->ssh2key.alg = &ssh_dss;
1106 state->ssh2key.data = &state->key;
1107 state->ssh2key.alg = &ssh_rsa;
1109 state->commentptr = &state->ssh2key.comment;
1111 state->commentptr = &state->key.comment;
1114 * Invent a comment for the key. We'll do this by including
1115 * the date in it. This will be so horrifyingly ugly that
1116 * the user will immediately want to change it, which is
1119 *state->commentptr = smalloc(30);
1126 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1128 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1132 * Now update the key controls with all the key data.
1137 * Blank passphrase, initially. This isn't dangerous,
1138 * because we will warn (Are You Sure?) before allowing
1139 * the user to save an unprotected private key.
1141 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1142 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1146 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1148 * Set the key fingerprint.
1150 savecomment = *state->commentptr;
1151 *state->commentptr = NULL;
1154 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1155 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1159 rsa_fingerprint(buf, sizeof(buf), &state->key);
1160 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1162 *state->commentptr = savecomment;
1164 * Construct a decimal representation of the key, for
1165 * pasting into .ssh/authorized_keys or
1166 * .ssh/authorized_keys2 on a Unix box.
1169 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1170 IDC_PKSTATIC, &state->ssh2key);
1172 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1173 IDC_PKSTATIC, &state->key);
1177 * Finally, hide the progress bar and show the key data.
1179 hidemany(hwnd, nokey_ids, TRUE);
1180 hidemany(hwnd, generating_ids, TRUE);
1181 hidemany(hwnd, gotkey_ids, FALSE);
1185 int id = ((LPHELPINFO)lParam)->iCtrlId;
1188 case IDC_GENERATING:
1192 cmd = "JI(`',`puttygen.generate')"; break;
1194 case IDC_KEYDISPLAY:
1195 cmd = "JI(`',`puttygen.pastekey')"; break;
1197 case IDC_FINGERPRINT:
1198 cmd = "JI(`',`puttygen.fingerprint')"; break;
1199 case IDC_COMMENTSTATIC:
1200 case IDC_COMMENTEDIT:
1201 cmd = "JI(`',`puttygen.comment')"; break;
1202 case IDC_PASSPHRASE1STATIC:
1203 case IDC_PASSPHRASE1EDIT:
1204 case IDC_PASSPHRASE2STATIC:
1205 case IDC_PASSPHRASE2EDIT:
1206 cmd = "JI(`',`puttygen.passphrase')"; break;
1207 case IDC_LOADSTATIC:
1209 cmd = "JI(`',`puttygen.load')"; break;
1210 case IDC_SAVESTATIC:
1212 cmd = "JI(`',`puttygen.savepriv')"; break;
1214 cmd = "JI(`',`puttygen.savepub')"; break;
1215 case IDC_TYPESTATIC:
1217 case IDC_KEYSSH2RSA:
1218 case IDC_KEYSSH2DSA:
1219 cmd = "JI(`',`puttygen.keytype')"; break;
1220 case IDC_BITSSTATIC:
1222 cmd = "JI(`',`puttygen.bits')"; break;
1225 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1226 requested_help = TRUE;
1233 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1235 if (requested_help) {
1236 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1237 requested_help = FALSE;
1245 void cleanup_exit(int code) { exit(code); }
1247 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1249 InitCommonControls();
1253 * See if we can find our Help file.
1256 char b[2048], *p, *q, *r;
1258 GetModuleFileName(NULL, b, sizeof(b) - 1);
1260 p = strrchr(b, '\\');
1261 if (p && p >= r) r = p+1;
1262 q = strrchr(b, ':');
1263 if (q && q >= r) r = q+1;
1264 strcpy(r, "putty.hlp");
1265 if ( (fp = fopen(b, "r")) != NULL) {
1266 help_path = dupstr(b);
1273 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1274 MainDlgProc) != IDOK;