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 static const int nokey_ids[] = { IDC_NOKEY, 0 };
506 static const int generating_ids[] =
507 { IDC_GENERATING, IDC_PROGRESS, 0 };
508 static const int gotkey_ids[] = {
509 IDC_PKSTATIC, IDC_KEYDISPLAY,
510 IDC_FPSTATIC, IDC_FINGERPRINT,
511 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
512 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
513 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
515 static const char generating_msg[] =
516 "Please wait while a key is generated...";
517 static const char entropy_msg[] =
518 "Please generate some randomness by moving the mouse over the blank area.";
519 struct MainDlgState *state;
524 SetWindowLong(hwnd, GWL_EXSTYLE,
525 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
528 * If we add a Help button, this is where we destroy it
529 * if the help file isn't present.
532 requested_help = FALSE;
537 { /* centre the window */
541 hw = GetDesktopWindow();
542 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
544 (rs.right + rs.left + rd.left - rd.right) / 2,
545 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
546 rd.right - rd.left, rd.bottom - rd.top, TRUE);
549 state = smalloc(sizeof(*state));
550 state->generation_thread_exists = FALSE;
551 state->collecting_entropy = FALSE;
552 state->entropy = NULL;
553 state->key_exists = FALSE;
554 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
556 struct ctlpos cp, cp2;
558 /* Accelerators used: acglops1rbd */
560 ctlposinit(&cp, hwnd, 4, 4, 4);
561 bartitle(&cp, "Public and private key generation for PuTTY",
563 beginbox(&cp, "Key", IDC_BOX_KEY);
565 statictext(&cp2, "No key.", 1, IDC_NOKEY);
567 statictext(&cp2, "", 1, IDC_GENERATING);
568 progressbar(&cp2, IDC_PROGRESS);
570 "&Public key for pasting into authorized_keys file:",
571 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
572 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
573 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
574 IDC_FINGERPRINT, 75);
575 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
577 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
578 IDC_COMMENTEDIT, 75);
579 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
580 IDC_PASSPHRASE1EDIT, 75);
581 staticpassedit(&cp, "C&onfirm passphrase:",
582 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
584 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
585 staticbtn(&cp, "Generate a public/private key pair",
586 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
587 staticbtn(&cp, "Load an existing private key file",
588 IDC_LOADSTATIC, "&Load", IDC_LOAD);
589 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
590 "Save p&ublic key", IDC_SAVEPUB,
591 "&Save private key", IDC_SAVE);
593 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
594 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
595 "SSH&1 (RSA)", IDC_KEYSSH1,
596 "SSH2 &RSA", IDC_KEYSSH2RSA,
597 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
598 staticedit(&cp, "Number of &bits in a generated key:",
599 IDC_BITSSTATIC, IDC_BITS, 20);
602 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
603 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
606 * Initially, hide the progress bar and the key display,
607 * and show the no-key display. Also disable the Save
608 * buttons, because with no key we obviously can't save
611 hidemany(hwnd, nokey_ids, FALSE);
612 hidemany(hwnd, generating_ids, TRUE);
613 hidemany(hwnd, gotkey_ids, TRUE);
614 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
615 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
619 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
620 if (state->collecting_entropy &&
621 state->entropy && state->entropy_got < state->entropy_required) {
622 state->entropy[state->entropy_got++] = lParam;
623 state->entropy[state->entropy_got++] = GetMessageTime();
624 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
625 state->entropy_got, 0);
626 if (state->entropy_got >= state->entropy_required) {
627 struct rsa_key_thread_params *params;
631 * Seed the entropy pool
633 random_add_heavynoise(state->entropy, state->entropy_size);
634 memset(state->entropy, 0, state->entropy_size);
635 sfree(state->entropy);
636 state->collecting_entropy = FALSE;
638 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
639 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
640 MAKELPARAM(0, PROGRESSRANGE));
641 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
643 params = smalloc(sizeof(*params));
644 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
645 params->dialog = hwnd;
646 params->keysize = state->keysize;
647 params->is_dsa = state->is_dsa;
648 params->key = &state->key;
649 params->dsskey = &state->dsskey;
651 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
652 params, 0, &threadid)) {
653 MessageBox(hwnd, "Out of thread resources",
654 "Key generation error",
655 MB_OK | MB_ICONERROR);
658 state->generation_thread_exists = TRUE;
664 switch (LOWORD(wParam)) {
665 case IDC_COMMENTEDIT:
666 if (HIWORD(wParam) == EN_CHANGE) {
667 state = (struct MainDlgState *)
668 GetWindowLong(hwnd, GWL_USERDATA);
669 if (state->key_exists) {
670 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
671 int len = GetWindowTextLength(editctl);
672 if (*state->commentptr)
673 sfree(*state->commentptr);
674 *state->commentptr = smalloc(len + 1);
675 GetWindowText(editctl, *state->commentptr, len + 1);
677 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
680 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
687 EnableWindow(hwnd, 0);
688 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
689 EnableWindow(hwnd, 1);
690 SetActiveWindow(hwnd);
694 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
695 if (!state->generation_thread_exists) {
697 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
699 state->keysize = DEFAULT_KEYSIZE;
700 /* If we ever introduce a new key type, check it here! */
701 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
702 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
703 if (state->keysize < 256) {
704 int ret = MessageBox(hwnd,
705 "PuTTYgen will not generate a key"
706 " smaller than 256 bits.\n"
707 "Key length reset to 256. Continue?",
709 MB_ICONWARNING | MB_OKCANCEL);
712 state->keysize = 256;
713 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
715 hidemany(hwnd, nokey_ids, TRUE);
716 hidemany(hwnd, generating_ids, FALSE);
717 hidemany(hwnd, gotkey_ids, TRUE);
718 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
719 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
720 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
721 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
722 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
723 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
724 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
725 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
726 state->key_exists = FALSE;
727 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
728 state->collecting_entropy = TRUE;
731 * My brief statistical tests on mouse movements
732 * suggest that there are about 2.5 bits of
733 * randomness in the x position, 2.5 in the y
734 * position, and 1.7 in the message time, making
735 * 5.7 bits of unpredictability per mouse movement.
736 * However, other people have told me it's far less
737 * than that, so I'm going to be stupidly cautious
738 * and knock that down to a nice round 2. With this
739 * method, we require two words per mouse movement,
740 * so with 2 bits per mouse movement we expect 2
741 * bits every 2 words.
743 state->entropy_required = (state->keysize / 2) * 2;
744 state->entropy_got = 0;
745 state->entropy_size = (state->entropy_required *
746 sizeof(*state->entropy));
747 state->entropy = smalloc(state->entropy_size);
749 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
750 MAKELPARAM(0, state->entropy_required));
751 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
756 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
757 if (state->key_exists) {
758 char filename[FILENAME_MAX];
759 char passphrase[PASSPHRASE_MAXLEN];
760 char passphrase2[PASSPHRASE_MAXLEN];
761 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
762 passphrase, sizeof(passphrase));
763 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
764 passphrase2, sizeof(passphrase2));
765 if (strcmp(passphrase, passphrase2)) {
767 "The two passphrases given do not match.",
768 "PuTTYgen Error", MB_OK | MB_ICONERROR);
773 ret = MessageBox(hwnd,
774 "Are you sure you want to save this key\n"
775 "without a passphrase to protect it?",
777 MB_YESNO | MB_ICONWARNING);
781 if (prompt_keyfile(hwnd, "Save private key as:",
784 FILE *fp = fopen(filename, "r");
786 char buffer[FILENAME_MAX + 80];
788 sprintf(buffer, "Overwrite existing file\n%.*s?",
789 FILENAME_MAX, filename);
790 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
791 MB_YESNO | MB_ICONWARNING);
796 ret = ssh2_save_userkey(filename, &state->ssh2key,
797 *passphrase ? passphrase :
800 ret = saversakey(filename, &state->key,
801 *passphrase ? passphrase : NULL);
804 MessageBox(hwnd, "Unable to save key file",
805 "PuTTYgen Error", MB_OK | MB_ICONERROR);
812 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
813 if (state->key_exists) {
814 char filename[FILENAME_MAX];
815 if (prompt_keyfile(hwnd, "Save public key as:",
818 FILE *fp = fopen(filename, "r");
820 char buffer[FILENAME_MAX + 80];
822 sprintf(buffer, "Overwrite existing file\n%.*s?",
823 FILENAME_MAX, filename);
824 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
825 MB_YESNO | MB_ICONWARNING);
830 ret = save_ssh2_pubkey(filename, &state->ssh2key);
832 ret = save_ssh1_pubkey(filename, &state->key);
835 MessageBox(hwnd, "Unable to save key file",
836 "PuTTYgen Error", MB_OK | MB_ICONERROR);
843 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
844 if (!state->generation_thread_exists) {
845 char filename[FILENAME_MAX];
846 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
847 char passphrase[PASSPHRASE_MAXLEN];
852 struct PassphraseProcStruct pps;
853 struct RSAKey newkey1;
854 struct ssh2_userkey *newkey2 = NULL;
856 ver = keyfile_version(filename);
858 MessageBox(NULL, "Couldn't load private key.",
859 "PuTTYgen Error", MB_OK | MB_ICONERROR);
865 needs_pass = rsakey_encrypted(filename, &comment);
868 ssh2_userkey_encrypted(filename, &comment);
869 pps.passphrase = passphrase;
870 pps.comment = comment;
874 dlgret = DialogBoxParam(hinst,
875 MAKEINTRESOURCE(210),
876 NULL, PassphraseProc,
886 loadrsakey(filename, &newkey1, passphrase);
889 ssh2_load_userkey(filename, passphrase);
890 if (newkey2 == SSH2_WRONG_PASSPHRASE)
901 MessageBox(NULL, "Couldn't load private key.",
902 "PuTTYgen Error", MB_OK | MB_ICONERROR);
903 } else if (ret == 1) {
904 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
905 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
906 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
907 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
908 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
909 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
910 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
911 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
913 * Now update the key controls with all the
917 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
919 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
926 state->commentptr = &state->key.comment;
927 state->key = newkey1;
930 * Set the key fingerprint.
932 savecomment = state->key.comment;
933 state->key.comment = NULL;
934 rsa_fingerprint(buf, sizeof(buf),
936 state->key.comment = savecomment;
938 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
940 * Construct a decimal representation
941 * of the key, for pasting into
942 * .ssh/authorized_keys on a Unix box.
944 setupbigedit1(hwnd, IDC_KEYDISPLAY,
945 IDC_PKSTATIC, &state->key);
952 &state->ssh2key.comment;
953 state->ssh2key = *newkey2; /* structure copy */
956 savecomment = state->ssh2key.comment;
957 state->ssh2key.comment = NULL;
960 fingerprint(state->ssh2key.data);
961 state->ssh2key.comment = savecomment;
963 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
966 setupbigedit2(hwnd, IDC_KEYDISPLAY,
967 IDC_PKSTATIC, &state->ssh2key);
969 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
973 * Finally, hide the progress bar and show
976 hidemany(hwnd, nokey_ids, TRUE);
977 hidemany(hwnd, generating_ids, TRUE);
978 hidemany(hwnd, gotkey_ids, FALSE);
979 state->key_exists = TRUE;
987 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
988 state->generation_thread_exists = FALSE;
989 state->key_exists = TRUE;
990 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
991 MAKELPARAM(0, PROGRESSRANGE));
992 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
993 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
994 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
995 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
996 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
997 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
998 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
999 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
1000 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
1002 if (state->is_dsa) {
1003 state->ssh2key.data = &state->dsskey;
1004 state->ssh2key.alg = &ssh_dss;
1006 state->ssh2key.data = &state->key;
1007 state->ssh2key.alg = &ssh_rsa;
1009 state->commentptr = &state->ssh2key.comment;
1011 state->commentptr = &state->key.comment;
1014 * Invent a comment for the key. We'll do this by including
1015 * the date in it. This will be so horrifyingly ugly that
1016 * the user will immediately want to change it, which is
1019 *state->commentptr = smalloc(30);
1026 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1028 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1032 * Now update the key controls with all the key data.
1037 * Blank passphrase, initially. This isn't dangerous,
1038 * because we will warn (Are You Sure?) before allowing
1039 * the user to save an unprotected private key.
1041 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1042 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1046 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1048 * Set the key fingerprint.
1050 savecomment = *state->commentptr;
1051 *state->commentptr = NULL;
1054 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1055 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1059 rsa_fingerprint(buf, sizeof(buf), &state->key);
1060 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1062 *state->commentptr = savecomment;
1064 * Construct a decimal representation of the key, for
1065 * pasting into .ssh/authorized_keys or
1066 * .ssh/authorized_keys2 on a Unix box.
1069 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1070 IDC_PKSTATIC, &state->ssh2key);
1072 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1073 IDC_PKSTATIC, &state->key);
1077 * Finally, hide the progress bar and show the key data.
1079 hidemany(hwnd, nokey_ids, TRUE);
1080 hidemany(hwnd, generating_ids, TRUE);
1081 hidemany(hwnd, gotkey_ids, FALSE);
1085 int id = ((LPHELPINFO)lParam)->iCtrlId;
1088 case IDC_GENERATING:
1092 cmd = "JI(`',`puttygen.generate')"; break;
1094 case IDC_KEYDISPLAY:
1095 cmd = "JI(`',`puttygen.pastekey')"; break;
1097 case IDC_FINGERPRINT:
1098 cmd = "JI(`',`puttygen.fingerprint')"; break;
1099 case IDC_COMMENTSTATIC:
1100 case IDC_COMMENTEDIT:
1101 cmd = "JI(`',`puttygen.comment')"; break;
1102 case IDC_PASSPHRASE1STATIC:
1103 case IDC_PASSPHRASE1EDIT:
1104 case IDC_PASSPHRASE2STATIC:
1105 case IDC_PASSPHRASE2EDIT:
1106 cmd = "JI(`',`puttygen.passphrase')"; break;
1107 case IDC_LOADSTATIC:
1109 cmd = "JI(`',`puttygen.load')"; break;
1110 case IDC_SAVESTATIC:
1112 cmd = "JI(`',`puttygen.savepriv')"; break;
1114 cmd = "JI(`',`puttygen.savepub')"; break;
1115 case IDC_TYPESTATIC:
1117 case IDC_KEYSSH2RSA:
1118 case IDC_KEYSSH2DSA:
1119 cmd = "JI(`',`puttygen.keytype')"; break;
1120 case IDC_BITSSTATIC:
1122 cmd = "JI(`',`puttygen.bits')"; break;
1125 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1126 requested_help = TRUE;
1133 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1135 if (requested_help) {
1136 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1137 requested_help = FALSE;
1145 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1147 InitCommonControls();
1151 * See if we can find our Help file.
1154 char b[2048], *p, *q, *r;
1156 GetModuleFileName(NULL, b, sizeof(b) - 1);
1158 p = strrchr(b, '\\');
1159 if (p && p >= r) r = p+1;
1160 q = strrchr(b, ':');
1161 if (q && q >= r) r = q+1;
1162 strcpy(r, "putty.hlp");
1163 if ( (fp = fopen(b, "r")) != NULL) {
1164 help_path = dupstr(b);
1171 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1172 MainDlgProc) != IDOK;