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 * Dialog-box function for the Licence box.
227 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
228 WPARAM wParam, LPARAM lParam)
235 { /* centre the window */
239 hw = GetDesktopWindow();
240 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
242 (rs.right + rs.left + rd.left - rd.right) / 2,
243 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
244 rd.right - rd.left, rd.bottom - rd.top, TRUE);
249 switch (LOWORD(wParam)) {
263 * Dialog-box function for the About box.
265 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
266 WPARAM wParam, LPARAM lParam)
273 { /* centre the window */
277 hw = GetDesktopWindow();
278 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
280 (rs.right + rs.left + rd.left - rd.right) / 2,
281 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
282 rd.right - rd.left, rd.bottom - rd.top, TRUE);
285 SetDlgItemText(hwnd, 100, ver);
288 switch (LOWORD(wParam)) {
293 EnableWindow(hwnd, 0);
294 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
295 EnableWindow(hwnd, 1);
296 SetActiveWindow(hwnd);
308 * Thread to generate a key.
310 struct rsa_key_thread_params {
311 HWND progressbar; /* notify this with progress */
312 HWND dialog; /* notify this on completion */
313 int keysize; /* bits in key */
316 struct dss_key *dsskey;
318 static DWORD WINAPI generate_rsa_key_thread(void *param)
320 struct rsa_key_thread_params *params =
321 (struct rsa_key_thread_params *) param;
322 struct progress prog;
323 prog.progbar = params->progressbar;
325 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
328 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
330 rsa_generate(params->key, params->keysize, progress_update, &prog);
332 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
338 struct MainDlgState {
339 int collecting_entropy;
340 int generation_thread_exists;
342 int entropy_got, entropy_required, entropy_size;
345 char **commentptr; /* points to key.comment or ssh2key.comment */
346 struct ssh2_userkey ssh2key;
349 struct dss_key dsskey;
350 HMENU filemenu, keymenu, cvtmenu;
353 static void hidemany(HWND hwnd, const int *ids, int hideit)
356 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
360 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
365 dec1 = bignum_decimal(key->exponent);
366 dec2 = bignum_decimal(key->modulus);
367 buffer = smalloc(strlen(dec1) + strlen(dec2) +
368 strlen(key->comment) + 30);
369 sprintf(buffer, "%d %s %s %s",
370 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
371 SetDlgItemText(hwnd, id, buffer);
372 SetDlgItemText(hwnd, idstatic,
373 "&Public key for pasting into authorized_keys file:");
379 static void setupbigedit2(HWND hwnd, int id, int idstatic,
380 struct ssh2_userkey *key)
382 unsigned char *pub_blob;
387 pub_blob = key->alg->public_blob(key->data, &pub_len);
388 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
389 strlen(key->comment) + 3);
390 strcpy(buffer, key->alg->name);
391 p = buffer + strlen(buffer);
394 while (i < pub_len) {
395 int n = (pub_len - i < 3 ? pub_len - i : 3);
396 base64_encode_atom(pub_blob + i, n, p);
401 strcpy(p, key->comment);
402 SetDlgItemText(hwnd, id, buffer);
403 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
404 "OpenSSH authorized_keys2 file:");
409 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
414 dec1 = bignum_decimal(key->exponent);
415 dec2 = bignum_decimal(key->modulus);
416 fp = fopen(filename, "wb");
419 fprintf(fp, "%d %s %s %s\n",
420 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
428 * Warn about the obsolescent key file format.
430 void old_keyfile_warning(void)
432 static const char mbtitle[] = "PuTTY Key File Warning";
433 static const char message[] =
434 "You are loading an SSH 2 private key which has an\n"
435 "old version of the file format. This means your key\n"
436 "file is not fully tamperproof. Future versions of\n"
437 "PuTTY may stop supporting this private key format,\n"
438 "so we recommend you convert your key to the new\n"
441 "Once the key is loaded into PuTTYgen, you can perform\n"
442 "this conversion simply by saving it again.";
444 MessageBox(NULL, message, mbtitle, MB_OK);
447 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
449 unsigned char *pub_blob;
455 pub_blob = key->alg->public_blob(key->data, &pub_len);
457 fp = fopen(filename, "wb");
461 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
463 fprintf(fp, "Comment: \"");
464 for (p = key->comment; *p; p++) {
465 if (*p == '\\' || *p == '\"')
473 while (i < pub_len) {
475 int n = (pub_len - i < 3 ? pub_len - i : 3);
476 base64_encode_atom(pub_blob + i, n, buf);
480 if (++column >= 16) {
488 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
495 controlidstart = 100,
502 IDC_PKSTATIC, IDC_KEYDISPLAY,
503 IDC_FPSTATIC, IDC_FINGERPRINT,
504 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
505 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
506 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
508 IDC_GENSTATIC, IDC_GENERATE,
509 IDC_LOADSTATIC, IDC_LOAD,
510 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
512 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
513 IDC_BITSSTATIC, IDC_BITS,
516 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
519 static const int nokey_ids[] = { IDC_NOKEY, 0 };
520 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
521 static const int gotkey_ids[] = {
522 IDC_PKSTATIC, IDC_KEYDISPLAY,
523 IDC_FPSTATIC, IDC_FINGERPRINT,
524 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
525 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
526 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
530 * Small UI helper function to switch the state of the main dialog
531 * by enabling and disabling controls and menu items.
533 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
539 hidemany(hwnd, nokey_ids, FALSE);
540 hidemany(hwnd, generating_ids, TRUE);
541 hidemany(hwnd, gotkey_ids, TRUE);
542 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
543 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
544 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
545 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
546 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
547 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
548 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
549 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
550 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
551 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
552 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
553 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
554 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
555 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
556 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
557 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
558 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
559 MF_GRAYED|MF_BYCOMMAND);
560 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
561 MF_GRAYED|MF_BYCOMMAND);
563 case 1: /* generating key */
564 hidemany(hwnd, nokey_ids, TRUE);
565 hidemany(hwnd, generating_ids, FALSE);
566 hidemany(hwnd, gotkey_ids, TRUE);
567 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
568 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
569 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
570 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
571 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
572 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
573 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
574 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
575 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
576 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
577 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
578 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
579 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
580 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
581 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
582 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
583 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
584 MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
586 MF_GRAYED|MF_BYCOMMAND);
589 hidemany(hwnd, nokey_ids, TRUE);
590 hidemany(hwnd, generating_ids, TRUE);
591 hidemany(hwnd, gotkey_ids, FALSE);
592 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
593 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
594 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
595 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
596 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
597 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
598 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
599 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
600 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
601 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
602 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
603 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
604 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
605 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
607 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
609 * Enable export menu items if and only if the key type
610 * supports this kind of export.
612 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
613 #define do_export_menuitem(x,y) \
614 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
615 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
616 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
617 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
618 #undef do_export_menuitem
623 void load_key_file(HWND hwnd, struct MainDlgState *state,
624 char *filename, int was_import_cmd)
626 char passphrase[PASSPHRASE_MAXLEN];
631 struct PassphraseProcStruct pps;
632 struct RSAKey newkey1;
633 struct ssh2_userkey *newkey2 = NULL;
635 type = realtype = key_type(filename);
636 if (type != SSH_KEYTYPE_SSH1 &&
637 type != SSH_KEYTYPE_SSH2 &&
638 !import_possible(type)) {
640 sprintf(msg, "Couldn't load private key (%s)",
641 key_type_to_str(type));
642 MessageBox(NULL, msg,
643 "PuTTYgen Error", MB_OK | MB_ICONERROR);
647 if (type != SSH_KEYTYPE_SSH1 &&
648 type != SSH_KEYTYPE_SSH2) {
650 type = import_target_type(type);
654 if (realtype == SSH_KEYTYPE_SSH1)
655 needs_pass = rsakey_encrypted(filename, &comment);
656 else if (realtype == SSH_KEYTYPE_SSH2)
658 ssh2_userkey_encrypted(filename, &comment);
660 needs_pass = import_encrypted(filename, realtype,
662 pps.passphrase = passphrase;
663 pps.comment = comment;
667 dlgret = DialogBoxParam(hinst,
668 MAKEINTRESOURCE(210),
669 NULL, PassphraseProc,
677 if (type == SSH_KEYTYPE_SSH1) {
678 if (realtype == type)
679 ret = loadrsakey(filename, &newkey1,
682 ret = import_ssh1(filename, realtype,
683 &newkey1, passphrase);
685 if (realtype == type)
686 newkey2 = ssh2_load_userkey(filename,
689 newkey2 = import_ssh2(filename, realtype,
691 if (newkey2 == SSH2_WRONG_PASSPHRASE)
702 MessageBox(NULL, "Couldn't load private key.",
703 "PuTTYgen Error", MB_OK | MB_ICONERROR);
704 } else if (ret == 1) {
706 * Now update the key controls with all the
710 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
712 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
714 if (type == SSH_KEYTYPE_SSH1) {
719 state->commentptr = &state->key.comment;
720 state->key = newkey1;
723 * Set the key fingerprint.
725 savecomment = state->key.comment;
726 state->key.comment = NULL;
727 rsa_fingerprint(buf, sizeof(buf),
729 state->key.comment = savecomment;
731 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
733 * Construct a decimal representation
734 * of the key, for pasting into
735 * .ssh/authorized_keys on a Unix box.
737 setupbigedit1(hwnd, IDC_KEYDISPLAY,
738 IDC_PKSTATIC, &state->key);
745 &state->ssh2key.comment;
746 state->ssh2key = *newkey2; /* structure copy */
749 savecomment = state->ssh2key.comment;
750 state->ssh2key.comment = NULL;
753 fingerprint(state->ssh2key.data);
754 state->ssh2key.comment = savecomment;
756 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
759 setupbigedit2(hwnd, IDC_KEYDISPLAY,
760 IDC_PKSTATIC, &state->ssh2key);
762 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
766 * Finally, hide the progress bar and show
769 ui_set_state(hwnd, state, 2);
770 state->key_exists = TRUE;
773 * If the user has imported a foreign key
774 * using the Load command, let them know.
775 * If they've used the Import command, be
778 if (realtype != type && !was_import_cmd) {
780 sprintf(msg, "Successfully imported foreign key\n"
782 "To use this key with PuTTY, you need to\n"
783 "use the \"Save private key\" command to\n"
784 "save it in PuTTY's own format.",
785 key_type_to_str(realtype));
786 MessageBox(NULL, msg, "PuTTYgen Notice",
787 MB_OK | MB_ICONINFORMATION);
793 * Dialog-box function for the main PuTTYgen dialog box.
795 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
796 WPARAM wParam, LPARAM lParam)
798 static const char generating_msg[] =
799 "Please wait while a key is generated...";
800 static const char entropy_msg[] =
801 "Please generate some randomness by moving the mouse over the blank area.";
802 struct MainDlgState *state;
807 SetWindowLong(hwnd, GWL_EXSTYLE,
808 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
811 * If we add a Help button, this is where we destroy it
812 * if the help file isn't present.
815 requested_help = FALSE;
817 state = smalloc(sizeof(*state));
818 state->generation_thread_exists = FALSE;
819 state->collecting_entropy = FALSE;
820 state->entropy = NULL;
821 state->key_exists = FALSE;
822 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
828 menu1 = CreateMenu();
829 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
830 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
831 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
832 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
833 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
834 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
835 state->filemenu = menu1;
837 menu1 = CreateMenu();
838 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
839 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
840 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
841 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
842 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
843 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
844 state->keymenu = menu1;
846 menu1 = CreateMenu();
847 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
848 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
849 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
850 "Export &OpenSSH key");
851 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
852 "Export &ssh.com key");
853 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
855 state->cvtmenu = menu1;
857 menu1 = CreateMenu();
858 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
860 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
861 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
869 { /* centre the window */
873 hw = GetDesktopWindow();
874 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
876 (rs.right + rs.left + rd.left - rd.right) / 2,
877 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
878 rd.right - rd.left, rd.bottom - rd.top, TRUE);
882 struct ctlpos cp, cp2;
884 /* Accelerators used: acglops1rbd */
886 ctlposinit(&cp, hwnd, 4, 4, 4);
887 beginbox(&cp, "Key", IDC_BOX_KEY);
889 statictext(&cp2, "No key.", 1, IDC_NOKEY);
891 statictext(&cp2, "", 1, IDC_GENERATING);
892 progressbar(&cp2, IDC_PROGRESS);
894 "&Public key for pasting into authorized_keys file:",
895 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
896 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
897 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
898 IDC_FINGERPRINT, 75);
899 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
901 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
902 IDC_COMMENTEDIT, 75);
903 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
904 IDC_PASSPHRASE1EDIT, 75);
905 staticpassedit(&cp, "C&onfirm passphrase:",
906 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
908 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
909 staticbtn(&cp, "Generate a public/private key pair",
910 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
911 staticbtn(&cp, "Load an existing private key file",
912 IDC_LOADSTATIC, "&Load", IDC_LOAD);
913 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
914 "Save p&ublic key", IDC_SAVEPUB,
915 "&Save private key", IDC_SAVE);
917 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
918 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
919 "SSH&1 (RSA)", IDC_KEYSSH1,
920 "SSH2 &RSA", IDC_KEYSSH2RSA,
921 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
922 staticedit(&cp, "Number of &bits in a generated key:",
923 IDC_BITSSTATIC, IDC_BITS, 20);
926 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
927 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
928 IDC_KEYSSH1, MF_BYCOMMAND);
929 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
932 * Initially, hide the progress bar and the key display,
933 * and show the no-key display. Also disable the Save
934 * buttons, because with no key we obviously can't save
937 ui_set_state(hwnd, state, 0);
940 * Load a key file if one was provided on the command line.
943 load_key_file(hwnd, state, cmdline_keyfile, 0);
947 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
948 if (state->collecting_entropy &&
949 state->entropy && state->entropy_got < state->entropy_required) {
950 state->entropy[state->entropy_got++] = lParam;
951 state->entropy[state->entropy_got++] = GetMessageTime();
952 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
953 state->entropy_got, 0);
954 if (state->entropy_got >= state->entropy_required) {
955 struct rsa_key_thread_params *params;
959 * Seed the entropy pool
961 random_add_heavynoise(state->entropy, state->entropy_size);
962 memset(state->entropy, 0, state->entropy_size);
963 sfree(state->entropy);
964 state->collecting_entropy = FALSE;
966 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
967 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
968 MAKELPARAM(0, PROGRESSRANGE));
969 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
971 params = smalloc(sizeof(*params));
972 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
973 params->dialog = hwnd;
974 params->keysize = state->keysize;
975 params->is_dsa = state->is_dsa;
976 params->key = &state->key;
977 params->dsskey = &state->dsskey;
979 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
980 params, 0, &threadid)) {
981 MessageBox(hwnd, "Out of thread resources",
982 "Key generation error",
983 MB_OK | MB_ICONERROR);
986 state->generation_thread_exists = TRUE;
992 switch (LOWORD(wParam)) {
997 state = (struct MainDlgState *)
998 GetWindowLong(hwnd, GWL_USERDATA);
999 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1000 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1002 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1003 LOWORD(wParam), MF_BYCOMMAND);
1007 PostMessage(hwnd, WM_CLOSE, 0, 0);
1009 case IDC_COMMENTEDIT:
1010 if (HIWORD(wParam) == EN_CHANGE) {
1011 state = (struct MainDlgState *)
1012 GetWindowLong(hwnd, GWL_USERDATA);
1013 if (state->key_exists) {
1014 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1015 int len = GetWindowTextLength(editctl);
1016 if (*state->commentptr)
1017 sfree(*state->commentptr);
1018 *state->commentptr = smalloc(len + 1);
1019 GetWindowText(editctl, *state->commentptr, len + 1);
1021 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1024 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1031 EnableWindow(hwnd, 0);
1032 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
1033 EnableWindow(hwnd, 1);
1034 SetActiveWindow(hwnd);
1037 if (HIWORD(wParam) == BN_CLICKED ||
1038 HIWORD(wParam) == BN_DOUBLECLICKED) {
1040 WinHelp(hwnd, help_path, HELP_COMMAND,
1041 (DWORD)"JI(`',`puttygen.general')");
1042 requested_help = TRUE;
1048 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1049 if (!state->generation_thread_exists) {
1051 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1053 state->keysize = DEFAULT_KEYSIZE;
1054 /* If we ever introduce a new key type, check it here! */
1055 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1056 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1057 if (state->keysize < 256) {
1058 int ret = MessageBox(hwnd,
1059 "PuTTYgen will not generate a key"
1060 " smaller than 256 bits.\n"
1061 "Key length reset to 256. Continue?",
1063 MB_ICONWARNING | MB_OKCANCEL);
1066 state->keysize = 256;
1067 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1069 ui_set_state(hwnd, state, 1);
1070 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1071 state->key_exists = FALSE;
1072 state->collecting_entropy = TRUE;
1075 * My brief statistical tests on mouse movements
1076 * suggest that there are about 2.5 bits of
1077 * randomness in the x position, 2.5 in the y
1078 * position, and 1.7 in the message time, making
1079 * 5.7 bits of unpredictability per mouse movement.
1080 * However, other people have told me it's far less
1081 * than that, so I'm going to be stupidly cautious
1082 * and knock that down to a nice round 2. With this
1083 * method, we require two words per mouse movement,
1084 * so with 2 bits per mouse movement we expect 2
1085 * bits every 2 words.
1087 state->entropy_required = (state->keysize / 2) * 2;
1088 state->entropy_got = 0;
1089 state->entropy_size = (state->entropy_required *
1090 sizeof(*state->entropy));
1091 state->entropy = smalloc(state->entropy_size);
1093 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1094 MAKELPARAM(0, state->entropy_required));
1095 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1099 case IDC_EXPORT_OPENSSH:
1100 case IDC_EXPORT_SSHCOM:
1102 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1103 if (state->key_exists) {
1104 char filename[FILENAME_MAX];
1105 char passphrase[PASSPHRASE_MAXLEN];
1106 char passphrase2[PASSPHRASE_MAXLEN];
1110 realtype = SSH_KEYTYPE_SSH2;
1112 realtype = SSH_KEYTYPE_SSH1;
1114 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1115 type = SSH_KEYTYPE_OPENSSH;
1116 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1117 type = SSH_KEYTYPE_SSHCOM;
1121 if (type != realtype &&
1122 import_target_type(type) != realtype) {
1124 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1125 " format", (state->ssh2 ? 2 : 1),
1126 (state->ssh2 ? 1 : 2));
1127 MessageBox(hwnd, msg,
1128 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1132 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1133 passphrase, sizeof(passphrase));
1134 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1135 passphrase2, sizeof(passphrase2));
1136 if (strcmp(passphrase, passphrase2)) {
1138 "The two passphrases given do not match.",
1139 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1144 ret = MessageBox(hwnd,
1145 "Are you sure you want to save this key\n"
1146 "without a passphrase to protect it?",
1148 MB_YESNO | MB_ICONWARNING);
1152 if (prompt_keyfile(hwnd, "Save private key as:",
1153 filename, 1, (type == realtype))) {
1155 FILE *fp = fopen(filename, "r");
1157 char buffer[FILENAME_MAX + 80];
1159 sprintf(buffer, "Overwrite existing file\n%.*s?",
1160 FILENAME_MAX, filename);
1161 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1162 MB_YESNO | MB_ICONWARNING);
1168 if (type != realtype)
1169 ret = export_ssh2(filename, type, &state->ssh2key,
1170 *passphrase ? passphrase : NULL);
1172 ret = ssh2_save_userkey(filename, &state->ssh2key,
1173 *passphrase ? passphrase :
1176 if (type != realtype)
1177 ret = export_ssh1(filename, type, &state->key,
1178 *passphrase ? passphrase : NULL);
1180 ret = saversakey(filename, &state->key,
1181 *passphrase ? passphrase : NULL);
1184 MessageBox(hwnd, "Unable to save key file",
1185 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1192 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1193 if (state->key_exists) {
1194 char filename[FILENAME_MAX];
1195 if (prompt_keyfile(hwnd, "Save public key as:",
1198 FILE *fp = fopen(filename, "r");
1200 char buffer[FILENAME_MAX + 80];
1202 sprintf(buffer, "Overwrite existing file\n%.*s?",
1203 FILENAME_MAX, filename);
1204 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1205 MB_YESNO | MB_ICONWARNING);
1210 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1212 ret = save_ssh1_pubkey(filename, &state->key);
1215 MessageBox(hwnd, "Unable to save key file",
1216 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1224 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1225 if (!state->generation_thread_exists) {
1226 char filename[FILENAME_MAX];
1227 if (prompt_keyfile(hwnd, "Load private key:",
1228 filename, 0, LOWORD(wParam)==IDC_LOAD))
1229 load_key_file(hwnd, state, filename,
1230 LOWORD(wParam) != IDC_LOAD);
1236 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1237 state->generation_thread_exists = FALSE;
1238 state->key_exists = TRUE;
1239 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1240 MAKELPARAM(0, PROGRESSRANGE));
1241 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1243 if (state->is_dsa) {
1244 state->ssh2key.data = &state->dsskey;
1245 state->ssh2key.alg = &ssh_dss;
1247 state->ssh2key.data = &state->key;
1248 state->ssh2key.alg = &ssh_rsa;
1250 state->commentptr = &state->ssh2key.comment;
1252 state->commentptr = &state->key.comment;
1255 * Invent a comment for the key. We'll do this by including
1256 * the date in it. This will be so horrifyingly ugly that
1257 * the user will immediately want to change it, which is
1260 *state->commentptr = smalloc(30);
1267 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1269 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1273 * Now update the key controls with all the key data.
1278 * Blank passphrase, initially. This isn't dangerous,
1279 * because we will warn (Are You Sure?) before allowing
1280 * the user to save an unprotected private key.
1282 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1283 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1287 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1289 * Set the key fingerprint.
1291 savecomment = *state->commentptr;
1292 *state->commentptr = NULL;
1295 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1296 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1300 rsa_fingerprint(buf, sizeof(buf), &state->key);
1301 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1303 *state->commentptr = savecomment;
1305 * Construct a decimal representation of the key, for
1306 * pasting into .ssh/authorized_keys or
1307 * .ssh/authorized_keys2 on a Unix box.
1310 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1311 IDC_PKSTATIC, &state->ssh2key);
1313 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1314 IDC_PKSTATIC, &state->key);
1318 * Finally, hide the progress bar and show the key data.
1320 ui_set_state(hwnd, state, 2);
1324 int id = ((LPHELPINFO)lParam)->iCtrlId;
1327 case IDC_GENERATING:
1331 cmd = "JI(`',`puttygen.generate')"; break;
1333 case IDC_KEYDISPLAY:
1334 cmd = "JI(`',`puttygen.pastekey')"; break;
1336 case IDC_FINGERPRINT:
1337 cmd = "JI(`',`puttygen.fingerprint')"; break;
1338 case IDC_COMMENTSTATIC:
1339 case IDC_COMMENTEDIT:
1340 cmd = "JI(`',`puttygen.comment')"; break;
1341 case IDC_PASSPHRASE1STATIC:
1342 case IDC_PASSPHRASE1EDIT:
1343 case IDC_PASSPHRASE2STATIC:
1344 case IDC_PASSPHRASE2EDIT:
1345 cmd = "JI(`',`puttygen.passphrase')"; break;
1346 case IDC_LOADSTATIC:
1348 cmd = "JI(`',`puttygen.load')"; break;
1349 case IDC_SAVESTATIC:
1351 cmd = "JI(`',`puttygen.savepriv')"; break;
1353 cmd = "JI(`',`puttygen.savepub')"; break;
1354 case IDC_TYPESTATIC:
1356 case IDC_KEYSSH2RSA:
1357 case IDC_KEYSSH2DSA:
1358 cmd = "JI(`',`puttygen.keytype')"; break;
1359 case IDC_BITSSTATIC:
1361 cmd = "JI(`',`puttygen.bits')"; break;
1363 case IDC_EXPORT_OPENSSH:
1364 case IDC_EXPORT_SSHCOM:
1365 cmd = "JI(`',`puttygen.conversions')"; break;
1368 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1369 requested_help = TRUE;
1376 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1378 if (requested_help) {
1379 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1380 requested_help = FALSE;
1388 void cleanup_exit(int code) { exit(code); }
1390 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1395 split_into_argv(cmdline, &argc, &argv, NULL);
1399 * Assume the first argument to be a private key file, and
1400 * attempt to load it.
1402 cmdline_keyfile = argv[0];
1405 InitCommonControls();
1409 * See if we can find our Help file.
1412 char b[2048], *p, *q, *r;
1414 GetModuleFileName(NULL, b, sizeof(b) - 1);
1416 p = strrchr(b, '\\');
1417 if (p && p >= r) r = p+1;
1418 q = strrchr(b, ':');
1419 if (q && q >= r) r = q+1;
1420 strcpy(r, "putty.hlp");
1421 if ( (fp = fopen(b, "r")) != NULL) {
1422 help_path = dupstr(b);
1429 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1430 MainDlgProc) != IDOK;