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 vsprintf(stuff, fmt, ap);
36 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
37 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
41 /* ----------------------------------------------------------------------
42 * Progress report code. This is really horrible :-)
44 #define PROGRESSRANGE 65535
50 unsigned startpoint, total;
51 unsigned param, current, n; /* if exponential */
52 unsigned mult; /* if linear */
54 unsigned total, divisor, range;
58 static void progress_update(void *param, int action, int phase, int iprogress)
60 struct progress *p = (struct progress *) param;
61 unsigned progress = iprogress;
64 if (action < PROGFN_READY && p->nphases < phase)
67 case PROGFN_INITIALISE:
70 case PROGFN_LIN_PHASE:
71 p->phases[phase-1].exponential = 0;
72 p->phases[phase-1].mult = p->phases[phase].total / progress;
74 case PROGFN_EXP_PHASE:
75 p->phases[phase-1].exponential = 1;
76 p->phases[phase-1].param = 0x10000 + progress;
77 p->phases[phase-1].current = p->phases[phase-1].total;
78 p->phases[phase-1].n = 0;
80 case PROGFN_PHASE_EXTENT:
81 p->phases[phase-1].total = progress;
87 for (i = 0; i < p->nphases; i++) {
88 p->phases[i].startpoint = total;
89 total += p->phases[i].total;
92 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
93 p->range = p->total / p->divisor;
94 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
98 if (p->phases[phase-1].exponential) {
99 while (p->phases[phase-1].n < progress) {
100 p->phases[phase-1].n++;
101 p->phases[phase-1].current *= p->phases[phase-1].param;
102 p->phases[phase-1].current /= 0x10000;
104 position = (p->phases[phase-1].startpoint +
105 p->phases[phase-1].total - p->phases[phase-1].current);
107 position = (p->phases[phase-1].startpoint +
108 progress * p->phases[phase-1].mult);
110 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
117 #define PASSPHRASE_MAXLEN 512
119 struct PassphraseProcStruct {
125 * Dialog-box function for the passphrase box.
127 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
128 WPARAM wParam, LPARAM lParam)
130 static char *passphrase = NULL;
131 struct PassphraseProcStruct *p;
135 SetForegroundWindow(hwnd);
136 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
137 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
142 { /* centre the window */
146 hw = GetDesktopWindow();
147 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
149 (rs.right + rs.left + rd.left - rd.right) / 2,
150 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
151 rd.right - rd.left, rd.bottom - rd.top, TRUE);
154 p = (struct PassphraseProcStruct *) lParam;
155 passphrase = p->passphrase;
157 SetDlgItemText(hwnd, 101, p->comment);
159 SetDlgItemText(hwnd, 102, passphrase);
162 switch (LOWORD(wParam)) {
172 case 102: /* edit box */
173 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
174 GetDlgItemText(hwnd, 102, passphrase,
175 PASSPHRASE_MAXLEN - 1);
176 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
189 * Prompt for a key file. Assumes the filename buffer is of size
192 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
193 char *filename, int save, int ppk)
196 memset(&of, 0, sizeof(of));
197 #ifdef OPENFILENAME_SIZE_VERSION_400
198 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
200 of.lStructSize = sizeof(of);
204 of.lpstrFilter = "PuTTY Private Key Files\0*.PPK\0All Files\0*\0\0\0";
205 of.lpstrDefExt = ".ppk";
207 of.lpstrFilter = "All Files\0*\0\0\0";
209 of.lpstrCustomFilter = NULL;
211 of.lpstrFile = filename;
213 of.nMaxFile = FILENAME_MAX;
214 of.lpstrFileTitle = NULL;
215 of.lpstrInitialDir = NULL;
216 of.lpstrTitle = dlgtitle;
219 return GetSaveFileName(&of);
221 return GetOpenFileName(&of);
225 * This function is needed to link with the DES code. We need not
226 * have it do anything at all.
228 void logevent(char *msg)
233 * Dialog-box function for the Licence box.
235 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
236 WPARAM wParam, LPARAM lParam)
243 { /* centre the window */
247 hw = GetDesktopWindow();
248 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
250 (rs.right + rs.left + rd.left - rd.right) / 2,
251 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
252 rd.right - rd.left, rd.bottom - rd.top, TRUE);
257 switch (LOWORD(wParam)) {
271 * Dialog-box function for the About box.
273 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
274 WPARAM wParam, LPARAM lParam)
281 { /* centre the window */
285 hw = GetDesktopWindow();
286 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
288 (rs.right + rs.left + rd.left - rd.right) / 2,
289 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
290 rd.right - rd.left, rd.bottom - rd.top, TRUE);
293 SetDlgItemText(hwnd, 100, ver);
296 switch (LOWORD(wParam)) {
301 EnableWindow(hwnd, 0);
302 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
303 EnableWindow(hwnd, 1);
304 SetActiveWindow(hwnd);
316 * Thread to generate a key.
318 struct rsa_key_thread_params {
319 HWND progressbar; /* notify this with progress */
320 HWND dialog; /* notify this on completion */
321 int keysize; /* bits in key */
324 struct dss_key *dsskey;
326 static DWORD WINAPI generate_rsa_key_thread(void *param)
328 struct rsa_key_thread_params *params =
329 (struct rsa_key_thread_params *) param;
330 struct progress prog;
331 prog.progbar = params->progressbar;
333 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
336 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
338 rsa_generate(params->key, params->keysize, progress_update, &prog);
340 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
346 struct MainDlgState {
347 int collecting_entropy;
348 int generation_thread_exists;
350 int entropy_got, entropy_required, entropy_size;
353 char **commentptr; /* points to key.comment or ssh2key.comment */
354 struct ssh2_userkey ssh2key;
357 struct dss_key dsskey;
358 HMENU filemenu, keymenu, cvtmenu;
361 static void hidemany(HWND hwnd, const int *ids, int hideit)
364 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
368 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
373 dec1 = bignum_decimal(key->exponent);
374 dec2 = bignum_decimal(key->modulus);
375 buffer = smalloc(strlen(dec1) + strlen(dec2) +
376 strlen(key->comment) + 30);
377 sprintf(buffer, "%d %s %s %s",
378 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
379 SetDlgItemText(hwnd, id, buffer);
380 SetDlgItemText(hwnd, idstatic,
381 "&Public key for pasting into authorized_keys file:");
387 static void setupbigedit2(HWND hwnd, int id, int idstatic,
388 struct ssh2_userkey *key)
390 unsigned char *pub_blob;
395 pub_blob = key->alg->public_blob(key->data, &pub_len);
396 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
397 strlen(key->comment) + 3);
398 strcpy(buffer, key->alg->name);
399 p = buffer + strlen(buffer);
402 while (i < pub_len) {
403 int n = (pub_len - i < 3 ? pub_len - i : 3);
404 base64_encode_atom(pub_blob + i, n, p);
409 strcpy(p, key->comment);
410 SetDlgItemText(hwnd, id, buffer);
411 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
412 "OpenSSH authorized_keys2 file:");
417 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
422 dec1 = bignum_decimal(key->exponent);
423 dec2 = bignum_decimal(key->modulus);
424 fp = fopen(filename, "wb");
427 fprintf(fp, "%d %s %s %s\n",
428 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
436 * Warn about the obsolescent key file format.
438 void old_keyfile_warning(void)
440 static const char mbtitle[] = "PuTTY Key File Warning";
441 static const char message[] =
442 "You are loading an SSH 2 private key which has an\n"
443 "old version of the file format. This means your key\n"
444 "file is not fully tamperproof. Future versions of\n"
445 "PuTTY may stop supporting this private key format,\n"
446 "so we recommend you convert your key to the new\n"
449 "Once the key is loaded into PuTTYgen, you can perform\n"
450 "this conversion simply by saving it again.";
452 MessageBox(NULL, message, mbtitle, MB_OK);
455 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
457 unsigned char *pub_blob;
463 pub_blob = key->alg->public_blob(key->data, &pub_len);
465 fp = fopen(filename, "wb");
469 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
471 fprintf(fp, "Comment: \"");
472 for (p = key->comment; *p; p++) {
473 if (*p == '\\' || *p == '\"')
481 while (i < pub_len) {
483 int n = (pub_len - i < 3 ? pub_len - i : 3);
484 base64_encode_atom(pub_blob + i, n, buf);
488 if (++column >= 16) {
496 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
503 controlidstart = 100,
510 IDC_PKSTATIC, IDC_KEYDISPLAY,
511 IDC_FPSTATIC, IDC_FINGERPRINT,
512 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
513 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
514 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
516 IDC_GENSTATIC, IDC_GENERATE,
517 IDC_LOADSTATIC, IDC_LOAD,
518 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
520 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
521 IDC_BITSSTATIC, IDC_BITS,
524 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
527 static const int nokey_ids[] = { IDC_NOKEY, 0 };
528 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
529 static const int gotkey_ids[] = {
530 IDC_PKSTATIC, IDC_KEYDISPLAY,
531 IDC_FPSTATIC, IDC_FINGERPRINT,
532 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
533 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
534 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
538 * Small UI helper function to switch the state of the main dialog
539 * by enabling and disabling controls and menu items.
541 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
547 hidemany(hwnd, nokey_ids, FALSE);
548 hidemany(hwnd, generating_ids, TRUE);
549 hidemany(hwnd, gotkey_ids, TRUE);
550 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
551 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
553 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
554 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
555 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
556 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
557 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
558 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
559 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
560 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
561 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
562 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
563 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
564 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
565 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
566 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
567 MF_GRAYED|MF_BYCOMMAND);
568 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
569 MF_GRAYED|MF_BYCOMMAND);
571 case 1: /* generating key */
572 hidemany(hwnd, nokey_ids, TRUE);
573 hidemany(hwnd, generating_ids, FALSE);
574 hidemany(hwnd, gotkey_ids, TRUE);
575 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
576 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
577 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
578 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
579 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
580 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
581 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
582 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
583 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
584 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
587 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
588 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
589 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
590 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
591 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
592 MF_GRAYED|MF_BYCOMMAND);
593 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
594 MF_GRAYED|MF_BYCOMMAND);
597 hidemany(hwnd, nokey_ids, TRUE);
598 hidemany(hwnd, generating_ids, TRUE);
599 hidemany(hwnd, gotkey_ids, FALSE);
600 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
601 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
602 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
603 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
604 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
605 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
606 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
607 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
608 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
609 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
610 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
611 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
612 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
613 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
614 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
615 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
617 * Enable export menu items if and only if the key type
618 * supports this kind of export.
620 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
621 #define do_export_menuitem(x,y) \
622 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
623 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
624 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
625 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
626 #undef do_export_menuitem
631 void load_key_file(HWND hwnd, struct MainDlgState *state,
632 char *filename, int was_import_cmd)
634 char passphrase[PASSPHRASE_MAXLEN];
639 struct PassphraseProcStruct pps;
640 struct RSAKey newkey1;
641 struct ssh2_userkey *newkey2 = NULL;
643 type = realtype = key_type(filename);
644 if (type != SSH_KEYTYPE_SSH1 &&
645 type != SSH_KEYTYPE_SSH2 &&
646 !import_possible(type)) {
648 sprintf(msg, "Couldn't load private key (%s)",
649 key_type_to_str(type));
650 MessageBox(NULL, msg,
651 "PuTTYgen Error", MB_OK | MB_ICONERROR);
655 if (type != SSH_KEYTYPE_SSH1 &&
656 type != SSH_KEYTYPE_SSH2) {
658 type = import_target_type(type);
662 if (realtype == SSH_KEYTYPE_SSH1)
663 needs_pass = rsakey_encrypted(filename, &comment);
664 else if (realtype == SSH_KEYTYPE_SSH2)
666 ssh2_userkey_encrypted(filename, &comment);
668 needs_pass = import_encrypted(filename, realtype,
670 pps.passphrase = passphrase;
671 pps.comment = comment;
675 dlgret = DialogBoxParam(hinst,
676 MAKEINTRESOURCE(210),
677 NULL, PassphraseProc,
685 if (type == SSH_KEYTYPE_SSH1) {
686 if (realtype == type)
687 ret = loadrsakey(filename, &newkey1,
690 ret = import_ssh1(filename, realtype,
691 &newkey1, passphrase);
693 if (realtype == type)
694 newkey2 = ssh2_load_userkey(filename,
697 newkey2 = import_ssh2(filename, realtype,
699 if (newkey2 == SSH2_WRONG_PASSPHRASE)
710 MessageBox(NULL, "Couldn't load private key.",
711 "PuTTYgen Error", MB_OK | MB_ICONERROR);
712 } else if (ret == 1) {
714 * Now update the key controls with all the
718 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
720 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
722 if (type == SSH_KEYTYPE_SSH1) {
727 state->commentptr = &state->key.comment;
728 state->key = newkey1;
731 * Set the key fingerprint.
733 savecomment = state->key.comment;
734 state->key.comment = NULL;
735 rsa_fingerprint(buf, sizeof(buf),
737 state->key.comment = savecomment;
739 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
741 * Construct a decimal representation
742 * of the key, for pasting into
743 * .ssh/authorized_keys on a Unix box.
745 setupbigedit1(hwnd, IDC_KEYDISPLAY,
746 IDC_PKSTATIC, &state->key);
753 &state->ssh2key.comment;
754 state->ssh2key = *newkey2; /* structure copy */
757 savecomment = state->ssh2key.comment;
758 state->ssh2key.comment = NULL;
761 fingerprint(state->ssh2key.data);
762 state->ssh2key.comment = savecomment;
764 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
767 setupbigedit2(hwnd, IDC_KEYDISPLAY,
768 IDC_PKSTATIC, &state->ssh2key);
770 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
774 * Finally, hide the progress bar and show
777 ui_set_state(hwnd, state, 2);
778 state->key_exists = TRUE;
781 * If the user has imported a foreign key
782 * using the Load command, let them know.
783 * If they've used the Import command, be
786 if (realtype != type && !was_import_cmd) {
788 sprintf(msg, "Successfully imported foreign key\n"
790 "To use this key with PuTTY, you need to\n"
791 "use the \"Save private key\" command to\n"
792 "save it in PuTTY's own format.",
793 key_type_to_str(realtype));
794 MessageBox(NULL, msg, "PuTTYgen Notice",
795 MB_OK | MB_ICONINFORMATION);
801 * Dialog-box function for the main PuTTYgen dialog box.
803 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
804 WPARAM wParam, LPARAM lParam)
806 static const char generating_msg[] =
807 "Please wait while a key is generated...";
808 static const char entropy_msg[] =
809 "Please generate some randomness by moving the mouse over the blank area.";
810 struct MainDlgState *state;
815 SetWindowLong(hwnd, GWL_EXSTYLE,
816 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
819 * If we add a Help button, this is where we destroy it
820 * if the help file isn't present.
823 requested_help = FALSE;
825 state = smalloc(sizeof(*state));
826 state->generation_thread_exists = FALSE;
827 state->collecting_entropy = FALSE;
828 state->entropy = NULL;
829 state->key_exists = FALSE;
830 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
836 menu1 = CreateMenu();
837 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
838 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
839 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
840 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
841 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
842 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
843 state->filemenu = menu1;
845 menu1 = CreateMenu();
846 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
847 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
848 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
849 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
850 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
851 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
852 state->keymenu = menu1;
854 menu1 = CreateMenu();
855 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
856 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
857 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
858 "Export &OpenSSH key");
859 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
860 "Export &ssh.com key");
861 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
863 state->cvtmenu = menu1;
865 menu1 = CreateMenu();
866 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
868 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
869 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
877 { /* centre the window */
881 hw = GetDesktopWindow();
882 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
884 (rs.right + rs.left + rd.left - rd.right) / 2,
885 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
886 rd.right - rd.left, rd.bottom - rd.top, TRUE);
890 struct ctlpos cp, cp2;
892 /* Accelerators used: acglops1rbd */
894 ctlposinit(&cp, hwnd, 4, 4, 4);
895 beginbox(&cp, "Key", IDC_BOX_KEY);
897 statictext(&cp2, "No key.", 1, IDC_NOKEY);
899 statictext(&cp2, "", 1, IDC_GENERATING);
900 progressbar(&cp2, IDC_PROGRESS);
902 "&Public key for pasting into authorized_keys file:",
903 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
904 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
905 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
906 IDC_FINGERPRINT, 75);
907 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
909 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
910 IDC_COMMENTEDIT, 75);
911 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
912 IDC_PASSPHRASE1EDIT, 75);
913 staticpassedit(&cp, "C&onfirm passphrase:",
914 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
916 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
917 staticbtn(&cp, "Generate a public/private key pair",
918 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
919 staticbtn(&cp, "Load an existing private key file",
920 IDC_LOADSTATIC, "&Load", IDC_LOAD);
921 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
922 "Save p&ublic key", IDC_SAVEPUB,
923 "&Save private key", IDC_SAVE);
925 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
926 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
927 "SSH&1 (RSA)", IDC_KEYSSH1,
928 "SSH2 &RSA", IDC_KEYSSH2RSA,
929 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
930 staticedit(&cp, "Number of &bits in a generated key:",
931 IDC_BITSSTATIC, IDC_BITS, 20);
934 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
935 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
936 IDC_KEYSSH1, MF_BYCOMMAND);
937 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
940 * Initially, hide the progress bar and the key display,
941 * and show the no-key display. Also disable the Save
942 * buttons, because with no key we obviously can't save
945 ui_set_state(hwnd, state, 0);
948 * Load a key file if one was provided on the command line.
951 load_key_file(hwnd, state, cmdline_keyfile, 0);
955 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
956 if (state->collecting_entropy &&
957 state->entropy && state->entropy_got < state->entropy_required) {
958 state->entropy[state->entropy_got++] = lParam;
959 state->entropy[state->entropy_got++] = GetMessageTime();
960 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
961 state->entropy_got, 0);
962 if (state->entropy_got >= state->entropy_required) {
963 struct rsa_key_thread_params *params;
967 * Seed the entropy pool
969 random_add_heavynoise(state->entropy, state->entropy_size);
970 memset(state->entropy, 0, state->entropy_size);
971 sfree(state->entropy);
972 state->collecting_entropy = FALSE;
974 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
975 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
976 MAKELPARAM(0, PROGRESSRANGE));
977 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
979 params = smalloc(sizeof(*params));
980 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
981 params->dialog = hwnd;
982 params->keysize = state->keysize;
983 params->is_dsa = state->is_dsa;
984 params->key = &state->key;
985 params->dsskey = &state->dsskey;
987 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
988 params, 0, &threadid)) {
989 MessageBox(hwnd, "Out of thread resources",
990 "Key generation error",
991 MB_OK | MB_ICONERROR);
994 state->generation_thread_exists = TRUE;
1000 switch (LOWORD(wParam)) {
1002 case IDC_KEYSSH2RSA:
1003 case IDC_KEYSSH2DSA:
1005 state = (struct MainDlgState *)
1006 GetWindowLong(hwnd, GWL_USERDATA);
1007 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1008 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1010 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1011 LOWORD(wParam), MF_BYCOMMAND);
1015 PostMessage(hwnd, WM_CLOSE, 0, 0);
1017 case IDC_COMMENTEDIT:
1018 if (HIWORD(wParam) == EN_CHANGE) {
1019 state = (struct MainDlgState *)
1020 GetWindowLong(hwnd, GWL_USERDATA);
1021 if (state->key_exists) {
1022 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1023 int len = GetWindowTextLength(editctl);
1024 if (*state->commentptr)
1025 sfree(*state->commentptr);
1026 *state->commentptr = smalloc(len + 1);
1027 GetWindowText(editctl, *state->commentptr, len + 1);
1029 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1032 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1039 EnableWindow(hwnd, 0);
1040 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
1041 EnableWindow(hwnd, 1);
1042 SetActiveWindow(hwnd);
1045 if (HIWORD(wParam) == BN_CLICKED ||
1046 HIWORD(wParam) == BN_DOUBLECLICKED) {
1048 WinHelp(hwnd, help_path, HELP_COMMAND,
1049 (DWORD)"JI(`',`puttygen.general')");
1050 requested_help = TRUE;
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 *
1098 sizeof(*state->entropy));
1099 state->entropy = smalloc(state->entropy_size);
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:
1110 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1111 if (state->key_exists) {
1112 char filename[FILENAME_MAX];
1113 char passphrase[PASSPHRASE_MAXLEN];
1114 char passphrase2[PASSPHRASE_MAXLEN];
1118 realtype = SSH_KEYTYPE_SSH2;
1120 realtype = SSH_KEYTYPE_SSH1;
1122 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1123 type = SSH_KEYTYPE_OPENSSH;
1124 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1125 type = SSH_KEYTYPE_SSHCOM;
1129 if (type != realtype &&
1130 import_target_type(type) != realtype) {
1132 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1133 " format", (state->ssh2 ? 2 : 1),
1134 (state->ssh2 ? 1 : 2));
1135 MessageBox(hwnd, msg,
1136 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1140 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1141 passphrase, sizeof(passphrase));
1142 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1143 passphrase2, sizeof(passphrase2));
1144 if (strcmp(passphrase, passphrase2)) {
1146 "The two passphrases given do not match.",
1147 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1152 ret = MessageBox(hwnd,
1153 "Are you sure you want to save this key\n"
1154 "without a passphrase to protect it?",
1156 MB_YESNO | MB_ICONWARNING);
1160 if (prompt_keyfile(hwnd, "Save private key as:",
1161 filename, 1, (type == realtype))) {
1163 FILE *fp = fopen(filename, "r");
1165 char buffer[FILENAME_MAX + 80];
1167 sprintf(buffer, "Overwrite existing file\n%.*s?",
1168 FILENAME_MAX, filename);
1169 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1170 MB_YESNO | MB_ICONWARNING);
1176 if (type != realtype)
1177 ret = export_ssh2(filename, type, &state->ssh2key,
1178 *passphrase ? passphrase : NULL);
1180 ret = ssh2_save_userkey(filename, &state->ssh2key,
1181 *passphrase ? passphrase :
1184 if (type != realtype)
1185 ret = export_ssh1(filename, type, &state->key,
1186 *passphrase ? passphrase : NULL);
1188 ret = saversakey(filename, &state->key,
1189 *passphrase ? passphrase : NULL);
1192 MessageBox(hwnd, "Unable to save key file",
1193 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1200 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1201 if (state->key_exists) {
1202 char filename[FILENAME_MAX];
1203 if (prompt_keyfile(hwnd, "Save public key as:",
1206 FILE *fp = fopen(filename, "r");
1208 char buffer[FILENAME_MAX + 80];
1210 sprintf(buffer, "Overwrite existing file\n%.*s?",
1211 FILENAME_MAX, filename);
1212 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1213 MB_YESNO | MB_ICONWARNING);
1218 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1220 ret = save_ssh1_pubkey(filename, &state->key);
1223 MessageBox(hwnd, "Unable to save key file",
1224 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1232 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1233 if (!state->generation_thread_exists) {
1234 char filename[FILENAME_MAX];
1235 if (prompt_keyfile(hwnd, "Load private key:",
1236 filename, 0, LOWORD(wParam)==IDC_LOAD))
1237 load_key_file(hwnd, state, filename,
1238 LOWORD(wParam) != IDC_LOAD);
1244 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1245 state->generation_thread_exists = FALSE;
1246 state->key_exists = TRUE;
1247 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1248 MAKELPARAM(0, PROGRESSRANGE));
1249 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1251 if (state->is_dsa) {
1252 state->ssh2key.data = &state->dsskey;
1253 state->ssh2key.alg = &ssh_dss;
1255 state->ssh2key.data = &state->key;
1256 state->ssh2key.alg = &ssh_rsa;
1258 state->commentptr = &state->ssh2key.comment;
1260 state->commentptr = &state->key.comment;
1263 * Invent a comment for the key. We'll do this by including
1264 * the date in it. This will be so horrifyingly ugly that
1265 * the user will immediately want to change it, which is
1268 *state->commentptr = smalloc(30);
1275 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1277 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1281 * Now update the key controls with all the key data.
1286 * Blank passphrase, initially. This isn't dangerous,
1287 * because we will warn (Are You Sure?) before allowing
1288 * the user to save an unprotected private key.
1290 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1291 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1295 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1297 * Set the key fingerprint.
1299 savecomment = *state->commentptr;
1300 *state->commentptr = NULL;
1303 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1304 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1308 rsa_fingerprint(buf, sizeof(buf), &state->key);
1309 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1311 *state->commentptr = savecomment;
1313 * Construct a decimal representation of the key, for
1314 * pasting into .ssh/authorized_keys or
1315 * .ssh/authorized_keys2 on a Unix box.
1318 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1319 IDC_PKSTATIC, &state->ssh2key);
1321 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1322 IDC_PKSTATIC, &state->key);
1326 * Finally, hide the progress bar and show the key data.
1328 ui_set_state(hwnd, state, 2);
1332 int id = ((LPHELPINFO)lParam)->iCtrlId;
1335 case IDC_GENERATING:
1339 cmd = "JI(`',`puttygen.generate')"; break;
1341 case IDC_KEYDISPLAY:
1342 cmd = "JI(`',`puttygen.pastekey')"; break;
1344 case IDC_FINGERPRINT:
1345 cmd = "JI(`',`puttygen.fingerprint')"; break;
1346 case IDC_COMMENTSTATIC:
1347 case IDC_COMMENTEDIT:
1348 cmd = "JI(`',`puttygen.comment')"; break;
1349 case IDC_PASSPHRASE1STATIC:
1350 case IDC_PASSPHRASE1EDIT:
1351 case IDC_PASSPHRASE2STATIC:
1352 case IDC_PASSPHRASE2EDIT:
1353 cmd = "JI(`',`puttygen.passphrase')"; break;
1354 case IDC_LOADSTATIC:
1356 cmd = "JI(`',`puttygen.load')"; break;
1357 case IDC_SAVESTATIC:
1359 cmd = "JI(`',`puttygen.savepriv')"; break;
1361 cmd = "JI(`',`puttygen.savepub')"; break;
1362 case IDC_TYPESTATIC:
1364 case IDC_KEYSSH2RSA:
1365 case IDC_KEYSSH2DSA:
1366 cmd = "JI(`',`puttygen.keytype')"; break;
1367 case IDC_BITSSTATIC:
1369 cmd = "JI(`',`puttygen.bits')"; break;
1371 case IDC_EXPORT_OPENSSH:
1372 case IDC_EXPORT_SSHCOM:
1373 cmd = "JI(`',`puttygen.conversions')"; break;
1376 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1377 requested_help = TRUE;
1384 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1386 if (requested_help) {
1387 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1388 requested_help = FALSE;
1396 void cleanup_exit(int code) { exit(code); }
1398 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1403 split_into_argv(cmdline, &argc, &argv, NULL);
1407 * Assume the first argument to be a private key file, and
1408 * attempt to load it.
1410 cmdline_keyfile = argv[0];
1413 InitCommonControls();
1417 * See if we can find our Help file.
1420 char b[2048], *p, *q, *r;
1422 GetModuleFileName(NULL, b, sizeof(b) - 1);
1424 p = strrchr(b, '\\');
1425 if (p && p >= r) r = p+1;
1426 q = strrchr(b, ':');
1427 if (q && q >= r) r = q+1;
1428 strcpy(r, "putty.hlp");
1429 if ( (fp = fopen(b, "r")) != NULL) {
1430 help_path = dupstr(b);
1437 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1438 MainDlgProc) != IDOK;