2 * PuTTY key generation front end.
11 #define PUTTY_DO_GLOBALS
21 #define WM_DONEKEY (WM_XUSER + 1)
23 #define DEFAULT_KEYSIZE 1024
25 static int requested_help;
27 static char *cmdline_keyfile = NULL;
30 * Print a modal (Really Bad) message box and perform a fatal exit.
32 void modalfatalbox(char *fmt, ...)
38 stuff = dupvprintf(fmt, ap);
40 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
41 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
46 /* ----------------------------------------------------------------------
47 * Progress report code. This is really horrible :-)
49 #define PROGRESSRANGE 65535
55 unsigned startpoint, total;
56 unsigned param, current, n; /* if exponential */
57 unsigned mult; /* if linear */
59 unsigned total, divisor, range;
63 static void progress_update(void *param, int action, int phase, int iprogress)
65 struct progress *p = (struct progress *) param;
66 unsigned progress = iprogress;
69 if (action < PROGFN_READY && p->nphases < phase)
72 case PROGFN_INITIALISE:
75 case PROGFN_LIN_PHASE:
76 p->phases[phase-1].exponential = 0;
77 p->phases[phase-1].mult = p->phases[phase].total / progress;
79 case PROGFN_EXP_PHASE:
80 p->phases[phase-1].exponential = 1;
81 p->phases[phase-1].param = 0x10000 + progress;
82 p->phases[phase-1].current = p->phases[phase-1].total;
83 p->phases[phase-1].n = 0;
85 case PROGFN_PHASE_EXTENT:
86 p->phases[phase-1].total = progress;
92 for (i = 0; i < p->nphases; i++) {
93 p->phases[i].startpoint = total;
94 total += p->phases[i].total;
97 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
98 p->range = p->total / p->divisor;
99 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
102 case PROGFN_PROGRESS:
103 if (p->phases[phase-1].exponential) {
104 while (p->phases[phase-1].n < progress) {
105 p->phases[phase-1].n++;
106 p->phases[phase-1].current *= p->phases[phase-1].param;
107 p->phases[phase-1].current /= 0x10000;
109 position = (p->phases[phase-1].startpoint +
110 p->phases[phase-1].total - p->phases[phase-1].current);
112 position = (p->phases[phase-1].startpoint +
113 progress * p->phases[phase-1].mult);
115 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
122 #define PASSPHRASE_MAXLEN 512
124 struct PassphraseProcStruct {
130 * Dialog-box function for the passphrase box.
132 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
133 WPARAM wParam, LPARAM lParam)
135 static char *passphrase = NULL;
136 struct PassphraseProcStruct *p;
140 SetForegroundWindow(hwnd);
141 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
142 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
147 { /* centre the window */
151 hw = GetDesktopWindow();
152 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
154 (rs.right + rs.left + rd.left - rd.right) / 2,
155 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
156 rd.right - rd.left, rd.bottom - rd.top, TRUE);
159 p = (struct PassphraseProcStruct *) lParam;
160 passphrase = p->passphrase;
162 SetDlgItemText(hwnd, 101, p->comment);
164 SetDlgItemText(hwnd, 102, passphrase);
167 switch (LOWORD(wParam)) {
177 case 102: /* edit box */
178 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
179 GetDlgItemText(hwnd, 102, passphrase,
180 PASSPHRASE_MAXLEN - 1);
181 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
194 * Prompt for a key file. Assumes the filename buffer is of size
197 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
198 char *filename, int save, int ppk)
201 memset(&of, 0, sizeof(of));
202 #ifdef OPENFILENAME_SIZE_VERSION_400
203 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
205 of.lStructSize = sizeof(of);
209 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
210 "All Files (*.*)\0*\0\0\0";
211 of.lpstrDefExt = ".ppk";
213 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
215 of.lpstrCustomFilter = NULL;
217 of.lpstrFile = filename;
219 of.nMaxFile = FILENAME_MAX;
220 of.lpstrFileTitle = NULL;
221 of.lpstrInitialDir = NULL;
222 of.lpstrTitle = dlgtitle;
225 return GetSaveFileName(&of);
227 return GetOpenFileName(&of);
231 * Dialog-box function for the Licence box.
233 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
234 WPARAM wParam, LPARAM lParam)
241 { /* centre the window */
245 hw = GetDesktopWindow();
246 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
248 (rs.right + rs.left + rd.left - rd.right) / 2,
249 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
250 rd.right - rd.left, rd.bottom - rd.top, TRUE);
255 switch (LOWORD(wParam)) {
269 * Dialog-box function for the About box.
271 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
272 WPARAM wParam, LPARAM lParam)
279 { /* centre the window */
283 hw = GetDesktopWindow();
284 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
286 (rs.right + rs.left + rd.left - rd.right) / 2,
287 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
288 rd.right - rd.left, rd.bottom - rd.top, TRUE);
291 SetDlgItemText(hwnd, 100, ver);
294 switch (LOWORD(wParam)) {
299 EnableWindow(hwnd, 0);
300 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
301 EnableWindow(hwnd, 1);
302 SetActiveWindow(hwnd);
314 * Thread to generate a key.
316 struct rsa_key_thread_params {
317 HWND progressbar; /* notify this with progress */
318 HWND dialog; /* notify this on completion */
319 int keysize; /* bits in key */
322 struct dss_key *dsskey;
324 static DWORD WINAPI generate_rsa_key_thread(void *param)
326 struct rsa_key_thread_params *params =
327 (struct rsa_key_thread_params *) param;
328 struct progress prog;
329 prog.progbar = params->progressbar;
331 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
334 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
336 rsa_generate(params->key, params->keysize, progress_update, &prog);
338 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
344 struct MainDlgState {
345 int collecting_entropy;
346 int generation_thread_exists;
348 int entropy_got, entropy_required, entropy_size;
351 char **commentptr; /* points to key.comment or ssh2key.comment */
352 struct ssh2_userkey ssh2key;
355 struct dss_key dsskey;
356 HMENU filemenu, keymenu, cvtmenu;
359 static void hidemany(HWND hwnd, const int *ids, int hideit)
362 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
366 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
371 dec1 = bignum_decimal(key->exponent);
372 dec2 = bignum_decimal(key->modulus);
373 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
374 dec1, dec2, key->comment);
375 SetDlgItemText(hwnd, id, buffer);
376 SetDlgItemText(hwnd, idstatic,
377 "&Public key for pasting into authorized_keys file:");
383 static void setupbigedit2(HWND hwnd, int id, int idstatic,
384 struct ssh2_userkey *key)
386 unsigned char *pub_blob;
391 pub_blob = key->alg->public_blob(key->data, &pub_len);
392 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
393 strlen(key->comment) + 3, char);
394 strcpy(buffer, key->alg->name);
395 p = buffer + strlen(buffer);
398 while (i < pub_len) {
399 int n = (pub_len - i < 3 ? pub_len - i : 3);
400 base64_encode_atom(pub_blob + i, n, p);
405 strcpy(p, key->comment);
406 SetDlgItemText(hwnd, id, buffer);
407 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
408 "OpenSSH authorized_keys2 file:");
413 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
418 dec1 = bignum_decimal(key->exponent);
419 dec2 = bignum_decimal(key->modulus);
420 fp = fopen(filename, "wb");
423 fprintf(fp, "%d %s %s %s\n",
424 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
432 * Warn about the obsolescent key file format.
434 void old_keyfile_warning(void)
436 static const char mbtitle[] = "PuTTY Key File Warning";
437 static const char message[] =
438 "You are loading an SSH 2 private key which has an\n"
439 "old version of the file format. This means your key\n"
440 "file is not fully tamperproof. Future versions of\n"
441 "PuTTY may stop supporting this private key format,\n"
442 "so we recommend you convert your key to the new\n"
445 "Once the key is loaded into PuTTYgen, you can perform\n"
446 "this conversion simply by saving it again.";
448 MessageBox(NULL, message, mbtitle, MB_OK);
451 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
453 unsigned char *pub_blob;
459 pub_blob = key->alg->public_blob(key->data, &pub_len);
461 fp = fopen(filename, "wb");
465 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
467 fprintf(fp, "Comment: \"");
468 for (p = key->comment; *p; p++) {
469 if (*p == '\\' || *p == '\"')
477 while (i < pub_len) {
479 int n = (pub_len - i < 3 ? pub_len - i : 3);
480 base64_encode_atom(pub_blob + i, n, buf);
484 if (++column >= 16) {
492 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
499 controlidstart = 100,
506 IDC_PKSTATIC, IDC_KEYDISPLAY,
507 IDC_FPSTATIC, IDC_FINGERPRINT,
508 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
509 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
510 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
512 IDC_GENSTATIC, IDC_GENERATE,
513 IDC_LOADSTATIC, IDC_LOAD,
514 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
516 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
517 IDC_BITSSTATIC, IDC_BITS,
520 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
523 static const int nokey_ids[] = { IDC_NOKEY, 0 };
524 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
525 static const int gotkey_ids[] = {
526 IDC_PKSTATIC, IDC_KEYDISPLAY,
527 IDC_FPSTATIC, IDC_FINGERPRINT,
528 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
529 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
530 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
534 * Small UI helper function to switch the state of the main dialog
535 * by enabling and disabling controls and menu items.
537 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
543 hidemany(hwnd, nokey_ids, FALSE);
544 hidemany(hwnd, generating_ids, TRUE);
545 hidemany(hwnd, gotkey_ids, TRUE);
546 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
547 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
548 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
549 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
550 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
551 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
553 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
554 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
555 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
556 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
557 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
558 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
559 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
560 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
561 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
562 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
563 MF_GRAYED|MF_BYCOMMAND);
564 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
565 MF_GRAYED|MF_BYCOMMAND);
567 case 1: /* generating key */
568 hidemany(hwnd, nokey_ids, TRUE);
569 hidemany(hwnd, generating_ids, FALSE);
570 hidemany(hwnd, gotkey_ids, TRUE);
571 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
572 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
573 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
574 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
575 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
576 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
577 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
578 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
579 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
580 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
581 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
582 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
583 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
584 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
587 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
588 MF_GRAYED|MF_BYCOMMAND);
589 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
590 MF_GRAYED|MF_BYCOMMAND);
593 hidemany(hwnd, nokey_ids, TRUE);
594 hidemany(hwnd, generating_ids, TRUE);
595 hidemany(hwnd, gotkey_ids, FALSE);
596 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
597 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
598 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
599 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
600 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
601 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
602 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
603 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
604 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
605 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
607 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
608 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
609 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
610 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
611 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
613 * Enable export menu items if and only if the key type
614 * supports this kind of export.
616 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
617 #define do_export_menuitem(x,y) \
618 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
619 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
620 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
621 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
622 #undef do_export_menuitem
627 void load_key_file(HWND hwnd, struct MainDlgState *state,
628 Filename filename, int was_import_cmd)
630 char passphrase[PASSPHRASE_MAXLEN];
635 struct PassphraseProcStruct pps;
636 struct RSAKey newkey1;
637 struct ssh2_userkey *newkey2 = NULL;
639 type = realtype = key_type(&filename);
640 if (type != SSH_KEYTYPE_SSH1 &&
641 type != SSH_KEYTYPE_SSH2 &&
642 !import_possible(type)) {
644 sprintf(msg, "Couldn't load private key (%s)",
645 key_type_to_str(type));
646 MessageBox(NULL, msg,
647 "PuTTYgen Error", MB_OK | MB_ICONERROR);
651 if (type != SSH_KEYTYPE_SSH1 &&
652 type != SSH_KEYTYPE_SSH2) {
654 type = import_target_type(type);
658 if (realtype == SSH_KEYTYPE_SSH1)
659 needs_pass = rsakey_encrypted(&filename, &comment);
660 else if (realtype == SSH_KEYTYPE_SSH2)
662 ssh2_userkey_encrypted(&filename, &comment);
664 needs_pass = import_encrypted(&filename, realtype,
666 pps.passphrase = passphrase;
667 pps.comment = comment;
671 dlgret = DialogBoxParam(hinst,
672 MAKEINTRESOURCE(210),
673 NULL, PassphraseProc,
681 if (type == SSH_KEYTYPE_SSH1) {
682 if (realtype == type)
683 ret = loadrsakey(&filename, &newkey1,
686 ret = import_ssh1(&filename, realtype,
687 &newkey1, passphrase);
689 if (realtype == type)
690 newkey2 = ssh2_load_userkey(&filename,
693 newkey2 = import_ssh2(&filename, realtype,
695 if (newkey2 == SSH2_WRONG_PASSPHRASE)
706 MessageBox(NULL, "Couldn't load private key.",
707 "PuTTYgen Error", MB_OK | MB_ICONERROR);
708 } else if (ret == 1) {
710 * Now update the key controls with all the
714 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
716 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
718 if (type == SSH_KEYTYPE_SSH1) {
723 state->commentptr = &state->key.comment;
724 state->key = newkey1;
727 * Set the key fingerprint.
729 savecomment = state->key.comment;
730 state->key.comment = NULL;
731 rsa_fingerprint(buf, sizeof(buf),
733 state->key.comment = savecomment;
735 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
737 * Construct a decimal representation
738 * of the key, for pasting into
739 * .ssh/authorized_keys on a Unix box.
741 setupbigedit1(hwnd, IDC_KEYDISPLAY,
742 IDC_PKSTATIC, &state->key);
749 &state->ssh2key.comment;
750 state->ssh2key = *newkey2; /* structure copy */
753 savecomment = state->ssh2key.comment;
754 state->ssh2key.comment = NULL;
757 fingerprint(state->ssh2key.data);
758 state->ssh2key.comment = savecomment;
760 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
763 setupbigedit2(hwnd, IDC_KEYDISPLAY,
764 IDC_PKSTATIC, &state->ssh2key);
766 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
770 * Finally, hide the progress bar and show
773 ui_set_state(hwnd, state, 2);
774 state->key_exists = TRUE;
777 * If the user has imported a foreign key
778 * using the Load command, let them know.
779 * If they've used the Import command, be
782 if (realtype != type && !was_import_cmd) {
784 sprintf(msg, "Successfully imported foreign key\n"
786 "To use this key with PuTTY, you need to\n"
787 "use the \"Save private key\" command to\n"
788 "save it in PuTTY's own format.",
789 key_type_to_str(realtype));
790 MessageBox(NULL, msg, "PuTTYgen Notice",
791 MB_OK | MB_ICONINFORMATION);
797 * Dialog-box function for the main PuTTYgen dialog box.
799 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
800 WPARAM wParam, LPARAM lParam)
802 static const char generating_msg[] =
803 "Please wait while a key is generated...";
804 static const char entropy_msg[] =
805 "Please generate some randomness by moving the mouse over the blank area.";
806 struct MainDlgState *state;
811 SetWindowLong(hwnd, GWL_EXSTYLE,
812 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
815 * If we add a Help button, this is where we destroy it
816 * if the help file isn't present.
819 requested_help = FALSE;
820 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
821 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
823 state = snew(struct MainDlgState);
824 state->generation_thread_exists = FALSE;
825 state->collecting_entropy = FALSE;
826 state->entropy = NULL;
827 state->key_exists = FALSE;
828 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
834 menu1 = CreateMenu();
835 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
836 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
837 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
838 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
839 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
840 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
841 state->filemenu = menu1;
843 menu1 = CreateMenu();
844 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
845 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
846 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
847 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
848 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
849 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
850 state->keymenu = menu1;
852 menu1 = CreateMenu();
853 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
854 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
855 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
856 "Export &OpenSSH key");
857 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
858 "Export &ssh.com key");
859 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
861 state->cvtmenu = menu1;
863 menu1 = CreateMenu();
864 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
866 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
867 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
875 { /* centre the window */
879 hw = GetDesktopWindow();
880 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
882 (rs.right + rs.left + rd.left - rd.right) / 2,
883 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
884 rd.right - rd.left, rd.bottom - rd.top, TRUE);
888 struct ctlpos cp, cp2;
890 /* Accelerators used: acglops1rbd */
892 ctlposinit(&cp, hwnd, 4, 4, 4);
893 beginbox(&cp, "Key", IDC_BOX_KEY);
895 statictext(&cp2, "No key.", 1, IDC_NOKEY);
897 statictext(&cp2, "", 1, IDC_GENERATING);
898 progressbar(&cp2, IDC_PROGRESS);
900 "&Public key for pasting into authorized_keys file:",
901 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
902 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
903 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
904 IDC_FINGERPRINT, 75);
905 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
907 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
908 IDC_COMMENTEDIT, 75);
909 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
910 IDC_PASSPHRASE1EDIT, 75);
911 staticpassedit(&cp, "C&onfirm passphrase:",
912 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
914 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
915 staticbtn(&cp, "Generate a public/private key pair",
916 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
917 staticbtn(&cp, "Load an existing private key file",
918 IDC_LOADSTATIC, "&Load", IDC_LOAD);
919 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
920 "Save p&ublic key", IDC_SAVEPUB,
921 "&Save private key", IDC_SAVE);
923 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
924 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
925 "SSH&1 (RSA)", IDC_KEYSSH1,
926 "SSH2 &RSA", IDC_KEYSSH2RSA,
927 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
928 staticedit(&cp, "Number of &bits in a generated key:",
929 IDC_BITSSTATIC, IDC_BITS, 20);
932 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
933 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
934 IDC_KEYSSH1, MF_BYCOMMAND);
935 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
938 * Initially, hide the progress bar and the key display,
939 * and show the no-key display. Also disable the Save
940 * buttons, because with no key we obviously can't save
943 ui_set_state(hwnd, state, 0);
946 * Load a key file if one was provided on the command line.
949 load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
953 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
954 if (state->collecting_entropy &&
955 state->entropy && state->entropy_got < state->entropy_required) {
956 state->entropy[state->entropy_got++] = lParam;
957 state->entropy[state->entropy_got++] = GetMessageTime();
958 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
959 state->entropy_got, 0);
960 if (state->entropy_got >= state->entropy_required) {
961 struct rsa_key_thread_params *params;
965 * Seed the entropy pool
967 random_add_heavynoise(state->entropy, state->entropy_size);
968 memset(state->entropy, 0, state->entropy_size);
969 sfree(state->entropy);
970 state->collecting_entropy = FALSE;
972 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
973 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
974 MAKELPARAM(0, PROGRESSRANGE));
975 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
977 params = snew(struct rsa_key_thread_params);
978 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
979 params->dialog = hwnd;
980 params->keysize = state->keysize;
981 params->is_dsa = state->is_dsa;
982 params->key = &state->key;
983 params->dsskey = &state->dsskey;
985 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
986 params, 0, &threadid)) {
987 MessageBox(hwnd, "Out of thread resources",
988 "Key generation error",
989 MB_OK | MB_ICONERROR);
992 state->generation_thread_exists = TRUE;
998 switch (LOWORD(wParam)) {
1000 case IDC_KEYSSH2RSA:
1001 case IDC_KEYSSH2DSA:
1003 state = (struct MainDlgState *)
1004 GetWindowLong(hwnd, GWL_USERDATA);
1005 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1006 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1008 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1009 LOWORD(wParam), MF_BYCOMMAND);
1013 PostMessage(hwnd, WM_CLOSE, 0, 0);
1015 case IDC_COMMENTEDIT:
1016 if (HIWORD(wParam) == EN_CHANGE) {
1017 state = (struct MainDlgState *)
1018 GetWindowLong(hwnd, GWL_USERDATA);
1019 if (state->key_exists) {
1020 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1021 int len = GetWindowTextLength(editctl);
1022 if (*state->commentptr)
1023 sfree(*state->commentptr);
1024 *state->commentptr = snewn(len + 1, char);
1025 GetWindowText(editctl, *state->commentptr, len + 1);
1027 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1030 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1037 EnableWindow(hwnd, 0);
1038 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1039 EnableWindow(hwnd, 1);
1040 SetActiveWindow(hwnd);
1043 if (HIWORD(wParam) == BN_CLICKED ||
1044 HIWORD(wParam) == BN_DOUBLECLICKED) {
1046 WinHelp(hwnd, help_path, HELP_COMMAND,
1047 (DWORD)"JI(`',`puttygen.general')");
1048 requested_help = TRUE;
1053 if (HIWORD(wParam) != BN_CLICKED &&
1054 HIWORD(wParam) != BN_DOUBLECLICKED)
1057 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1058 if (!state->generation_thread_exists) {
1060 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1062 state->keysize = DEFAULT_KEYSIZE;
1063 /* If we ever introduce a new key type, check it here! */
1064 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1065 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1066 if (state->keysize < 256) {
1067 int ret = MessageBox(hwnd,
1068 "PuTTYgen will not generate a key"
1069 " smaller than 256 bits.\n"
1070 "Key length reset to 256. Continue?",
1072 MB_ICONWARNING | MB_OKCANCEL);
1075 state->keysize = 256;
1076 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1078 ui_set_state(hwnd, state, 1);
1079 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1080 state->key_exists = FALSE;
1081 state->collecting_entropy = TRUE;
1084 * My brief statistical tests on mouse movements
1085 * suggest that there are about 2.5 bits of
1086 * randomness in the x position, 2.5 in the y
1087 * position, and 1.7 in the message time, making
1088 * 5.7 bits of unpredictability per mouse movement.
1089 * However, other people have told me it's far less
1090 * than that, so I'm going to be stupidly cautious
1091 * and knock that down to a nice round 2. With this
1092 * method, we require two words per mouse movement,
1093 * so with 2 bits per mouse movement we expect 2
1094 * bits every 2 words.
1096 state->entropy_required = (state->keysize / 2) * 2;
1097 state->entropy_got = 0;
1098 state->entropy_size = (state->entropy_required *
1100 state->entropy = snewn(state->entropy_required, unsigned);
1102 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1103 MAKELPARAM(0, state->entropy_required));
1104 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1108 case IDC_EXPORT_OPENSSH:
1109 case IDC_EXPORT_SSHCOM:
1111 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1112 if (state->key_exists) {
1113 char filename[FILENAME_MAX];
1114 char passphrase[PASSPHRASE_MAXLEN];
1115 char passphrase2[PASSPHRASE_MAXLEN];
1119 realtype = SSH_KEYTYPE_SSH2;
1121 realtype = SSH_KEYTYPE_SSH1;
1123 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1124 type = SSH_KEYTYPE_OPENSSH;
1125 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1126 type = SSH_KEYTYPE_SSHCOM;
1130 if (type != realtype &&
1131 import_target_type(type) != realtype) {
1133 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1134 " format", (state->ssh2 ? 2 : 1),
1135 (state->ssh2 ? 1 : 2));
1136 MessageBox(hwnd, msg,
1137 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1141 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1142 passphrase, sizeof(passphrase));
1143 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1144 passphrase2, sizeof(passphrase2));
1145 if (strcmp(passphrase, passphrase2)) {
1147 "The two passphrases given do not match.",
1148 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1153 ret = MessageBox(hwnd,
1154 "Are you sure you want to save this key\n"
1155 "without a passphrase to protect it?",
1157 MB_YESNO | MB_ICONWARNING);
1161 if (prompt_keyfile(hwnd, "Save private key as:",
1162 filename, 1, (type == realtype))) {
1164 FILE *fp = fopen(filename, "r");
1168 buffer = dupprintf("Overwrite existing file\n%s?",
1170 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1171 MB_YESNO | MB_ICONWARNING);
1178 Filename fn = filename_from_str(filename);
1179 if (type != realtype)
1180 ret = export_ssh2(&fn, type, &state->ssh2key,
1181 *passphrase ? passphrase : NULL);
1183 ret = ssh2_save_userkey(&fn, &state->ssh2key,
1184 *passphrase ? passphrase :
1187 Filename fn = filename_from_str(filename);
1188 if (type != realtype)
1189 ret = export_ssh1(&fn, type, &state->key,
1190 *passphrase ? passphrase : NULL);
1192 ret = saversakey(&fn, &state->key,
1193 *passphrase ? passphrase : NULL);
1196 MessageBox(hwnd, "Unable to save key file",
1197 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1204 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1205 if (state->key_exists) {
1206 char filename[FILENAME_MAX];
1207 if (prompt_keyfile(hwnd, "Save public key as:",
1210 FILE *fp = fopen(filename, "r");
1214 buffer = dupprintf("Overwrite existing file\n%s?",
1216 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1217 MB_YESNO | MB_ICONWARNING);
1223 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1225 ret = save_ssh1_pubkey(filename, &state->key);
1228 MessageBox(hwnd, "Unable to save key file",
1229 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1237 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1238 if (!state->generation_thread_exists) {
1239 char filename[FILENAME_MAX];
1240 if (prompt_keyfile(hwnd, "Load private key:",
1241 filename, 0, LOWORD(wParam)==IDC_LOAD))
1242 load_key_file(hwnd, state, filename_from_str(filename),
1243 LOWORD(wParam) != IDC_LOAD);
1249 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1250 state->generation_thread_exists = FALSE;
1251 state->key_exists = TRUE;
1252 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1253 MAKELPARAM(0, PROGRESSRANGE));
1254 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1256 if (state->is_dsa) {
1257 state->ssh2key.data = &state->dsskey;
1258 state->ssh2key.alg = &ssh_dss;
1260 state->ssh2key.data = &state->key;
1261 state->ssh2key.alg = &ssh_rsa;
1263 state->commentptr = &state->ssh2key.comment;
1265 state->commentptr = &state->key.comment;
1268 * Invent a comment for the key. We'll do this by including
1269 * the date in it. This will be so horrifyingly ugly that
1270 * the user will immediately want to change it, which is
1273 *state->commentptr = snewn(30, char);
1280 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1282 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
1286 * Now update the key controls with all the key data.
1291 * Blank passphrase, initially. This isn't dangerous,
1292 * because we will warn (Are You Sure?) before allowing
1293 * the user to save an unprotected private key.
1295 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1296 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1300 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1302 * Set the key fingerprint.
1304 savecomment = *state->commentptr;
1305 *state->commentptr = NULL;
1308 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1309 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1313 rsa_fingerprint(buf, sizeof(buf), &state->key);
1314 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1316 *state->commentptr = savecomment;
1318 * Construct a decimal representation of the key, for
1319 * pasting into .ssh/authorized_keys or
1320 * .ssh/authorized_keys2 on a Unix box.
1323 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1324 IDC_PKSTATIC, &state->ssh2key);
1326 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1327 IDC_PKSTATIC, &state->key);
1331 * Finally, hide the progress bar and show the key data.
1333 ui_set_state(hwnd, state, 2);
1337 int id = ((LPHELPINFO)lParam)->iCtrlId;
1340 case IDC_GENERATING:
1344 cmd = "JI(`',`puttygen.generate')"; break;
1346 case IDC_KEYDISPLAY:
1347 cmd = "JI(`',`puttygen.pastekey')"; break;
1349 case IDC_FINGERPRINT:
1350 cmd = "JI(`',`puttygen.fingerprint')"; break;
1351 case IDC_COMMENTSTATIC:
1352 case IDC_COMMENTEDIT:
1353 cmd = "JI(`',`puttygen.comment')"; break;
1354 case IDC_PASSPHRASE1STATIC:
1355 case IDC_PASSPHRASE1EDIT:
1356 case IDC_PASSPHRASE2STATIC:
1357 case IDC_PASSPHRASE2EDIT:
1358 cmd = "JI(`',`puttygen.passphrase')"; break;
1359 case IDC_LOADSTATIC:
1361 cmd = "JI(`',`puttygen.load')"; break;
1362 case IDC_SAVESTATIC:
1364 cmd = "JI(`',`puttygen.savepriv')"; break;
1366 cmd = "JI(`',`puttygen.savepub')"; break;
1367 case IDC_TYPESTATIC:
1369 case IDC_KEYSSH2RSA:
1370 case IDC_KEYSSH2DSA:
1371 cmd = "JI(`',`puttygen.keytype')"; break;
1372 case IDC_BITSSTATIC:
1374 cmd = "JI(`',`puttygen.bits')"; break;
1376 case IDC_EXPORT_OPENSSH:
1377 case IDC_EXPORT_SSHCOM:
1378 cmd = "JI(`',`puttygen.conversions')"; break;
1381 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1382 requested_help = TRUE;
1389 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1391 if (requested_help) {
1392 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1393 requested_help = FALSE;
1401 void cleanup_exit(int code) { exit(code); }
1403 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1408 split_into_argv(cmdline, &argc, &argv, NULL);
1412 * Assume the first argument to be a private key file, and
1413 * attempt to load it.
1415 cmdline_keyfile = argv[0];
1418 InitCommonControls();
1422 * See if we can find our Help file.
1425 char b[2048], *p, *q, *r;
1427 GetModuleFileName(NULL, b, sizeof(b) - 1);
1429 p = strrchr(b, '\\');
1430 if (p && p >= r) r = p+1;
1431 q = strrchr(b, ':');
1432 if (q && q >= r) r = q+1;
1433 strcpy(r, "putty.hlp");
1434 if ( (fp = fopen(b, "r")) != NULL) {
1435 help_path = dupstr(b);
1442 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1443 MainDlgProc) != IDOK;