2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
22 #define WM_DONEKEY (WM_APP + 1)
24 #define DEFAULT_KEYSIZE 2048
26 static char *cmdline_keyfile = NULL;
29 * Print a modal (Really Bad) message box and perform a fatal exit.
31 void modalfatalbox(char *fmt, ...)
37 stuff = dupvprintf(fmt, ap);
39 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
40 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
46 * Print a non-fatal message box and do not exit.
48 void nonfatal(char *fmt, ...)
54 stuff = dupvprintf(fmt, ap);
56 MessageBox(NULL, stuff, "PuTTYgen Error",
57 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
61 /* ----------------------------------------------------------------------
62 * Progress report code. This is really horrible :-)
64 #define PROGRESSRANGE 65535
70 unsigned startpoint, total;
71 unsigned param, current, n; /* if exponential */
72 unsigned mult; /* if linear */
74 unsigned total, divisor, range;
78 static void progress_update(void *param, int action, int phase, int iprogress)
80 struct progress *p = (struct progress *) param;
81 unsigned progress = iprogress;
84 if (action < PROGFN_READY && p->nphases < phase)
87 case PROGFN_INITIALISE:
90 case PROGFN_LIN_PHASE:
91 p->phases[phase-1].exponential = 0;
92 p->phases[phase-1].mult = p->phases[phase].total / progress;
94 case PROGFN_EXP_PHASE:
95 p->phases[phase-1].exponential = 1;
96 p->phases[phase-1].param = 0x10000 + progress;
97 p->phases[phase-1].current = p->phases[phase-1].total;
98 p->phases[phase-1].n = 0;
100 case PROGFN_PHASE_EXTENT:
101 p->phases[phase-1].total = progress;
107 for (i = 0; i < p->nphases; i++) {
108 p->phases[i].startpoint = total;
109 total += p->phases[i].total;
112 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
113 p->range = p->total / p->divisor;
114 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
117 case PROGFN_PROGRESS:
118 if (p->phases[phase-1].exponential) {
119 while (p->phases[phase-1].n < progress) {
120 p->phases[phase-1].n++;
121 p->phases[phase-1].current *= p->phases[phase-1].param;
122 p->phases[phase-1].current /= 0x10000;
124 position = (p->phases[phase-1].startpoint +
125 p->phases[phase-1].total - p->phases[phase-1].current);
127 position = (p->phases[phase-1].startpoint +
128 progress * p->phases[phase-1].mult);
130 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
137 struct PassphraseProcStruct {
143 * Dialog-box function for the passphrase box.
145 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
146 WPARAM wParam, LPARAM lParam)
148 static char **passphrase = NULL;
149 struct PassphraseProcStruct *p;
153 SetForegroundWindow(hwnd);
154 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
155 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
160 { /* centre the window */
164 hw = GetDesktopWindow();
165 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
167 (rs.right + rs.left + rd.left - rd.right) / 2,
168 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
169 rd.right - rd.left, rd.bottom - rd.top, TRUE);
172 p = (struct PassphraseProcStruct *) lParam;
173 passphrase = p->passphrase;
175 SetDlgItemText(hwnd, 101, p->comment);
176 burnstr(*passphrase);
177 *passphrase = dupstr("");
178 SetDlgItemText(hwnd, 102, *passphrase);
181 switch (LOWORD(wParam)) {
191 case 102: /* edit box */
192 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
193 burnstr(*passphrase);
194 *passphrase = GetDlgItemText_alloc(hwnd, 102);
207 * Prompt for a key file. Assumes the filename buffer is of size
210 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
211 char *filename, int save, int ppk)
214 memset(&of, 0, sizeof(of));
217 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
218 "All Files (*.*)\0*\0\0\0";
219 of.lpstrDefExt = ".ppk";
221 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
223 of.lpstrCustomFilter = NULL;
225 of.lpstrFile = filename;
227 of.nMaxFile = FILENAME_MAX;
228 of.lpstrFileTitle = NULL;
229 of.lpstrTitle = dlgtitle;
231 return request_file(NULL, &of, FALSE, save);
235 * Dialog-box function for the Licence box.
237 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
238 WPARAM wParam, LPARAM lParam)
245 { /* centre the window */
249 hw = GetDesktopWindow();
250 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
252 (rs.right + rs.left + rd.left - rd.right) / 2,
253 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
254 rd.right - rd.left, rd.bottom - rd.top, TRUE);
257 SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n"));
260 switch (LOWORD(wParam)) {
275 * Dialog-box function for the About box.
277 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
278 WPARAM wParam, LPARAM lParam)
285 { /* centre the window */
289 hw = GetDesktopWindow();
290 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
292 (rs.right + rs.left + rd.left - rd.right) / 2,
293 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
294 rd.right - rd.left, rd.bottom - rd.top, TRUE);
298 char *text = dupprintf
299 ("Pageant\r\n\r\n%s\r\n\r\n%s",
301 "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved.");
302 SetDlgItemText(hwnd, 1000, text);
307 switch (LOWORD(wParam)) {
313 EnableWindow(hwnd, 0);
314 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
315 EnableWindow(hwnd, 1);
316 SetActiveWindow(hwnd);
328 * Thread to generate a key.
330 struct rsa_key_thread_params {
331 HWND progressbar; /* notify this with progress */
332 HWND dialog; /* notify this on completion */
333 int keysize; /* bits in key */
336 struct dss_key *dsskey;
338 static DWORD WINAPI generate_rsa_key_thread(void *param)
340 struct rsa_key_thread_params *params =
341 (struct rsa_key_thread_params *) param;
342 struct progress prog;
343 prog.progbar = params->progressbar;
345 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
348 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
350 rsa_generate(params->key, params->keysize, progress_update, &prog);
352 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
358 struct MainDlgState {
359 int collecting_entropy;
360 int generation_thread_exists;
362 int entropy_got, entropy_required, entropy_size;
365 char **commentptr; /* points to key.comment or ssh2key.comment */
366 struct ssh2_userkey ssh2key;
369 struct dss_key dsskey;
370 HMENU filemenu, keymenu, cvtmenu;
373 static void hidemany(HWND hwnd, const int *ids, int hideit)
376 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
380 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
385 dec1 = bignum_decimal(key->exponent);
386 dec2 = bignum_decimal(key->modulus);
387 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
388 dec1, dec2, key->comment);
389 SetDlgItemText(hwnd, id, buffer);
390 SetDlgItemText(hwnd, idstatic,
391 "&Public key for pasting into authorized_keys file:");
397 static void setupbigedit2(HWND hwnd, int id, int idstatic,
398 struct ssh2_userkey *key)
400 unsigned char *pub_blob;
405 pub_blob = key->alg->public_blob(key->data, &pub_len);
406 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
407 strlen(key->comment) + 3, char);
408 strcpy(buffer, key->alg->name);
409 p = buffer + strlen(buffer);
412 while (i < pub_len) {
413 int n = (pub_len - i < 3 ? pub_len - i : 3);
414 base64_encode_atom(pub_blob + i, n, p);
419 strcpy(p, key->comment);
420 SetDlgItemText(hwnd, id, buffer);
421 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
422 "OpenSSH authorized_keys file:");
427 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
432 fp = fopen(filename, "wb");
435 dec1 = bignum_decimal(key->exponent);
436 dec2 = bignum_decimal(key->modulus);
437 fprintf(fp, "%d %s %s %s\n",
438 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
446 * Warn about the obsolescent key file format.
448 void old_keyfile_warning(void)
450 static const char mbtitle[] = "PuTTY Key File Warning";
451 static const char message[] =
452 "You are loading an SSH-2 private key which has an\n"
453 "old version of the file format. This means your key\n"
454 "file is not fully tamperproof. Future versions of\n"
455 "PuTTY may stop supporting this private key format,\n"
456 "so we recommend you convert your key to the new\n"
459 "Once the key is loaded into PuTTYgen, you can perform\n"
460 "this conversion simply by saving it again.";
462 MessageBox(NULL, message, mbtitle, MB_OK);
465 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
467 unsigned char *pub_blob;
473 pub_blob = key->alg->public_blob(key->data, &pub_len);
475 fp = fopen(filename, "wb");
479 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
481 fprintf(fp, "Comment: \"");
482 for (p = key->comment; *p; p++) {
483 if (*p == '\\' || *p == '\"')
491 while (i < pub_len) {
493 int n = (pub_len - i < 3 ? pub_len - i : 3);
494 base64_encode_atom(pub_blob + i, n, buf);
498 if (++column >= 16) {
506 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
513 controlidstart = 100,
520 IDC_PKSTATIC, IDC_KEYDISPLAY,
521 IDC_FPSTATIC, IDC_FINGERPRINT,
522 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
523 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
524 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
526 IDC_GENSTATIC, IDC_GENERATE,
527 IDC_LOADSTATIC, IDC_LOAD,
528 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
530 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
531 IDC_BITSSTATIC, IDC_BITS,
534 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
537 static const int nokey_ids[] = { IDC_NOKEY, 0 };
538 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
539 static const int gotkey_ids[] = {
540 IDC_PKSTATIC, IDC_KEYDISPLAY,
541 IDC_FPSTATIC, IDC_FINGERPRINT,
542 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
543 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
544 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
548 * Small UI helper function to switch the state of the main dialog
549 * by enabling and disabling controls and menu items.
551 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
557 hidemany(hwnd, nokey_ids, FALSE);
558 hidemany(hwnd, generating_ids, TRUE);
559 hidemany(hwnd, gotkey_ids, TRUE);
560 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
561 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
562 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
563 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
564 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
565 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
566 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
567 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
568 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
569 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
570 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
571 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
572 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
573 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
574 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
575 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
576 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
577 MF_GRAYED|MF_BYCOMMAND);
578 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
579 MF_GRAYED|MF_BYCOMMAND);
581 case 1: /* generating key */
582 hidemany(hwnd, nokey_ids, TRUE);
583 hidemany(hwnd, generating_ids, FALSE);
584 hidemany(hwnd, gotkey_ids, TRUE);
585 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
586 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
587 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
588 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
589 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
590 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
591 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
592 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
593 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
594 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
595 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
596 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
597 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
598 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
599 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
600 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
601 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
602 MF_GRAYED|MF_BYCOMMAND);
603 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
604 MF_GRAYED|MF_BYCOMMAND);
607 hidemany(hwnd, nokey_ids, TRUE);
608 hidemany(hwnd, generating_ids, TRUE);
609 hidemany(hwnd, gotkey_ids, FALSE);
610 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
611 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
612 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
613 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
614 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
615 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
616 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
617 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
618 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
619 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
620 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
621 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
622 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
623 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
624 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
625 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
627 * Enable export menu items if and only if the key type
628 * supports this kind of export.
630 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
631 #define do_export_menuitem(x,y) \
632 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
633 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
634 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
635 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
636 #undef do_export_menuitem
641 void load_key_file(HWND hwnd, struct MainDlgState *state,
642 Filename *filename, int was_import_cmd)
648 const char *errmsg = NULL;
650 struct RSAKey newkey1;
651 struct ssh2_userkey *newkey2 = NULL;
653 type = realtype = key_type(filename);
654 if (type != SSH_KEYTYPE_SSH1 &&
655 type != SSH_KEYTYPE_SSH2 &&
656 !import_possible(type)) {
657 char *msg = dupprintf("Couldn't load private key (%s)",
658 key_type_to_str(type));
659 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
660 HELPCTXID(errors_cantloadkey));
665 if (type != SSH_KEYTYPE_SSH1 &&
666 type != SSH_KEYTYPE_SSH2) {
668 type = import_target_type(type);
673 if (realtype == SSH_KEYTYPE_SSH1)
674 needs_pass = rsakey_encrypted(filename, &comment);
675 else if (realtype == SSH_KEYTYPE_SSH2)
676 needs_pass = ssh2_userkey_encrypted(filename, &comment);
678 needs_pass = import_encrypted(filename, realtype, &comment);
685 struct PassphraseProcStruct pps;
686 pps.passphrase = &passphrase;
687 pps.comment = comment;
688 dlgret = DialogBoxParam(hinst,
689 MAKEINTRESOURCE(210),
690 NULL, PassphraseProc,
696 assert(passphrase != NULL);
698 passphrase = dupstr("");
699 if (type == SSH_KEYTYPE_SSH1) {
700 if (realtype == type)
701 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
703 ret = import_ssh1(filename, realtype, &newkey1,
704 passphrase, &errmsg);
706 if (realtype == type)
707 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
709 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
710 if (newkey2 == SSH2_WRONG_PASSPHRASE)
721 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
722 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
723 HELPCTXID(errors_cantloadkey));
725 } else if (ret == 1) {
727 * Now update the key controls with all the
731 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
733 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
735 if (type == SSH_KEYTYPE_SSH1) {
740 state->commentptr = &state->key.comment;
741 state->key = newkey1;
744 * Set the key fingerprint.
746 savecomment = state->key.comment;
747 state->key.comment = NULL;
748 rsa_fingerprint(buf, sizeof(buf),
750 state->key.comment = savecomment;
752 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
754 * Construct a decimal representation
755 * of the key, for pasting into
756 * .ssh/authorized_keys on a Unix box.
758 setupbigedit1(hwnd, IDC_KEYDISPLAY,
759 IDC_PKSTATIC, &state->key);
766 &state->ssh2key.comment;
767 state->ssh2key = *newkey2; /* structure copy */
770 savecomment = state->ssh2key.comment;
771 state->ssh2key.comment = NULL;
774 fingerprint(state->ssh2key.data);
775 state->ssh2key.comment = savecomment;
777 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
780 setupbigedit2(hwnd, IDC_KEYDISPLAY,
781 IDC_PKSTATIC, &state->ssh2key);
783 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
787 * Finally, hide the progress bar and show
790 ui_set_state(hwnd, state, 2);
791 state->key_exists = TRUE;
794 * If the user has imported a foreign key
795 * using the Load command, let them know.
796 * If they've used the Import command, be
799 if (realtype != type && !was_import_cmd) {
801 sprintf(msg, "Successfully imported foreign key\n"
803 "To use this key with PuTTY, you need to\n"
804 "use the \"Save private key\" command to\n"
805 "save it in PuTTY's own format.",
806 key_type_to_str(realtype));
807 MessageBox(NULL, msg, "PuTTYgen Notice",
808 MB_OK | MB_ICONINFORMATION);
815 * Dialog-box function for the main PuTTYgen dialog box.
817 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
818 WPARAM wParam, LPARAM lParam)
820 static const char generating_msg[] =
821 "Please wait while a key is generated...";
822 static const char entropy_msg[] =
823 "Please generate some randomness by moving the mouse over the blank area.";
824 struct MainDlgState *state;
829 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
830 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
834 * If we add a Help button, this is where we destroy it
835 * if the help file isn't present.
838 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
839 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
841 state = snew(struct MainDlgState);
842 state->generation_thread_exists = FALSE;
843 state->collecting_entropy = FALSE;
844 state->entropy = NULL;
845 state->key_exists = FALSE;
846 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
852 menu1 = CreateMenu();
853 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
854 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
855 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
856 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
857 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
858 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
859 state->filemenu = menu1;
861 menu1 = CreateMenu();
862 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
863 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
864 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
865 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
866 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
867 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
868 state->keymenu = menu1;
870 menu1 = CreateMenu();
871 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
872 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
873 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
874 "Export &OpenSSH key");
875 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
876 "Export &ssh.com key");
877 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
879 state->cvtmenu = menu1;
881 menu1 = CreateMenu();
882 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
884 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
885 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
893 { /* centre the window */
897 hw = GetDesktopWindow();
898 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
900 (rs.right + rs.left + rd.left - rd.right) / 2,
901 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
902 rd.right - rd.left, rd.bottom - rd.top, TRUE);
906 struct ctlpos cp, cp2;
908 /* Accelerators used: acglops1rbd */
910 ctlposinit(&cp, hwnd, 4, 4, 4);
911 beginbox(&cp, "Key", IDC_BOX_KEY);
913 statictext(&cp2, "No key.", 1, IDC_NOKEY);
915 statictext(&cp2, "", 1, IDC_GENERATING);
916 progressbar(&cp2, IDC_PROGRESS);
918 "&Public key for pasting into authorized_keys file:",
919 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
920 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
921 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
922 IDC_FINGERPRINT, 75);
923 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
925 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
926 IDC_COMMENTEDIT, 75);
927 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
928 IDC_PASSPHRASE1EDIT, 75);
929 staticpassedit(&cp, "C&onfirm passphrase:",
930 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
932 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
933 staticbtn(&cp, "Generate a public/private key pair",
934 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
935 staticbtn(&cp, "Load an existing private key file",
936 IDC_LOADSTATIC, "&Load", IDC_LOAD);
937 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
938 "Save p&ublic key", IDC_SAVEPUB,
939 "&Save private key", IDC_SAVE);
941 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
942 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
943 "SSH-&1 (RSA)", IDC_KEYSSH1,
944 "SSH-2 &RSA", IDC_KEYSSH2RSA,
945 "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
946 staticedit(&cp, "Number of &bits in a generated key:",
947 IDC_BITSSTATIC, IDC_BITS, 20);
950 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
951 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
952 IDC_KEYSSH2RSA, MF_BYCOMMAND);
953 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
956 * Initially, hide the progress bar and the key display,
957 * and show the no-key display. Also disable the Save
958 * buttons, because with no key we obviously can't save
961 ui_set_state(hwnd, state, 0);
964 * Load a key file if one was provided on the command line.
966 if (cmdline_keyfile) {
967 Filename *fn = filename_from_str(cmdline_keyfile);
968 load_key_file(hwnd, state, fn, 0);
974 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
975 if (state->collecting_entropy &&
976 state->entropy && state->entropy_got < state->entropy_required) {
977 state->entropy[state->entropy_got++] = lParam;
978 state->entropy[state->entropy_got++] = GetMessageTime();
979 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
980 state->entropy_got, 0);
981 if (state->entropy_got >= state->entropy_required) {
982 struct rsa_key_thread_params *params;
986 * Seed the entropy pool
988 random_add_heavynoise(state->entropy, state->entropy_size);
989 smemclr(state->entropy, state->entropy_size);
990 sfree(state->entropy);
991 state->collecting_entropy = FALSE;
993 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
994 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
995 MAKELPARAM(0, PROGRESSRANGE));
996 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
998 params = snew(struct rsa_key_thread_params);
999 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1000 params->dialog = hwnd;
1001 params->keysize = state->keysize;
1002 params->is_dsa = state->is_dsa;
1003 params->key = &state->key;
1004 params->dsskey = &state->dsskey;
1006 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
1007 params, 0, &threadid)) {
1008 MessageBox(hwnd, "Out of thread resources",
1009 "Key generation error",
1010 MB_OK | MB_ICONERROR);
1013 state->generation_thread_exists = TRUE;
1019 switch (LOWORD(wParam)) {
1021 case IDC_KEYSSH2RSA:
1022 case IDC_KEYSSH2DSA:
1024 state = (struct MainDlgState *)
1025 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1026 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1027 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1029 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1030 LOWORD(wParam), MF_BYCOMMAND);
1034 PostMessage(hwnd, WM_CLOSE, 0, 0);
1036 case IDC_COMMENTEDIT:
1037 if (HIWORD(wParam) == EN_CHANGE) {
1038 state = (struct MainDlgState *)
1039 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1040 if (state->key_exists) {
1041 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1042 int len = GetWindowTextLength(editctl);
1043 if (*state->commentptr)
1044 sfree(*state->commentptr);
1045 *state->commentptr = snewn(len + 1, char);
1046 GetWindowText(editctl, *state->commentptr, len + 1);
1048 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1051 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1058 EnableWindow(hwnd, 0);
1059 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1060 EnableWindow(hwnd, 1);
1061 SetActiveWindow(hwnd);
1064 if (HIWORD(wParam) == BN_CLICKED ||
1065 HIWORD(wParam) == BN_DOUBLECLICKED) {
1066 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1070 if (HIWORD(wParam) != BN_CLICKED &&
1071 HIWORD(wParam) != BN_DOUBLECLICKED)
1074 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1075 if (!state->generation_thread_exists) {
1077 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1079 state->keysize = DEFAULT_KEYSIZE;
1080 /* If we ever introduce a new key type, check it here! */
1081 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1082 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1083 if (state->keysize < 256) {
1084 int ret = MessageBox(hwnd,
1085 "PuTTYgen will not generate a key"
1086 " smaller than 256 bits.\n"
1087 "Key length reset to 256. Continue?",
1089 MB_ICONWARNING | MB_OKCANCEL);
1092 state->keysize = 256;
1093 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1095 ui_set_state(hwnd, state, 1);
1096 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1097 state->key_exists = FALSE;
1098 state->collecting_entropy = TRUE;
1101 * My brief statistical tests on mouse movements
1102 * suggest that there are about 2.5 bits of
1103 * randomness in the x position, 2.5 in the y
1104 * position, and 1.7 in the message time, making
1105 * 5.7 bits of unpredictability per mouse movement.
1106 * However, other people have told me it's far less
1107 * than that, so I'm going to be stupidly cautious
1108 * and knock that down to a nice round 2. With this
1109 * method, we require two words per mouse movement,
1110 * so with 2 bits per mouse movement we expect 2
1111 * bits every 2 words.
1113 state->entropy_required = (state->keysize / 2) * 2;
1114 state->entropy_got = 0;
1115 state->entropy_size = (state->entropy_required *
1117 state->entropy = snewn(state->entropy_required, unsigned);
1119 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1120 MAKELPARAM(0, state->entropy_required));
1121 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1125 case IDC_EXPORT_OPENSSH:
1126 case IDC_EXPORT_SSHCOM:
1127 if (HIWORD(wParam) != BN_CLICKED)
1130 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1131 if (state->key_exists) {
1132 char filename[FILENAME_MAX];
1133 char *passphrase, *passphrase2;
1137 realtype = SSH_KEYTYPE_SSH2;
1139 realtype = SSH_KEYTYPE_SSH1;
1141 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1142 type = SSH_KEYTYPE_OPENSSH;
1143 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1144 type = SSH_KEYTYPE_SSHCOM;
1148 if (type != realtype &&
1149 import_target_type(type) != realtype) {
1151 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1152 " format", (state->ssh2 ? 2 : 1),
1153 (state->ssh2 ? 1 : 2));
1154 MessageBox(hwnd, msg,
1155 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1159 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1160 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1161 if (strcmp(passphrase, passphrase2)) {
1163 "The two passphrases given do not match.",
1164 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1165 burnstr(passphrase);
1166 burnstr(passphrase2);
1169 burnstr(passphrase2);
1172 ret = MessageBox(hwnd,
1173 "Are you sure you want to save this key\n"
1174 "without a passphrase to protect it?",
1176 MB_YESNO | MB_ICONWARNING);
1178 burnstr(passphrase);
1182 if (prompt_keyfile(hwnd, "Save private key as:",
1183 filename, 1, (type == realtype))) {
1185 FILE *fp = fopen(filename, "r");
1189 buffer = dupprintf("Overwrite existing file\n%s?",
1191 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1192 MB_YESNO | MB_ICONWARNING);
1195 burnstr(passphrase);
1201 Filename *fn = filename_from_str(filename);
1202 if (type != realtype)
1203 ret = export_ssh2(fn, type, &state->ssh2key,
1204 *passphrase ? passphrase : NULL);
1206 ret = ssh2_save_userkey(fn, &state->ssh2key,
1207 *passphrase ? passphrase :
1211 Filename *fn = filename_from_str(filename);
1212 if (type != realtype)
1213 ret = export_ssh1(fn, type, &state->key,
1214 *passphrase ? passphrase : NULL);
1216 ret = saversakey(fn, &state->key,
1217 *passphrase ? passphrase : NULL);
1221 MessageBox(hwnd, "Unable to save key file",
1222 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1225 burnstr(passphrase);
1229 if (HIWORD(wParam) != BN_CLICKED)
1232 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1233 if (state->key_exists) {
1234 char filename[FILENAME_MAX];
1235 if (prompt_keyfile(hwnd, "Save public key as:",
1238 FILE *fp = fopen(filename, "r");
1242 buffer = dupprintf("Overwrite existing file\n%s?",
1244 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1245 MB_YESNO | MB_ICONWARNING);
1251 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1253 ret = save_ssh1_pubkey(filename, &state->key);
1256 MessageBox(hwnd, "Unable to save key file",
1257 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1264 if (HIWORD(wParam) != BN_CLICKED)
1267 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1268 if (!state->generation_thread_exists) {
1269 char filename[FILENAME_MAX];
1270 if (prompt_keyfile(hwnd, "Load private key:",
1271 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1272 Filename *fn = filename_from_str(filename);
1273 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1281 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1282 state->generation_thread_exists = FALSE;
1283 state->key_exists = TRUE;
1284 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1285 MAKELPARAM(0, PROGRESSRANGE));
1286 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1288 if (state->is_dsa) {
1289 state->ssh2key.data = &state->dsskey;
1290 state->ssh2key.alg = &ssh_dss;
1292 state->ssh2key.data = &state->key;
1293 state->ssh2key.alg = &ssh_rsa;
1295 state->commentptr = &state->ssh2key.comment;
1297 state->commentptr = &state->key.comment;
1300 * Invent a comment for the key. We'll do this by including
1301 * the date in it. This will be so horrifyingly ugly that
1302 * the user will immediately want to change it, which is
1305 *state->commentptr = snewn(30, char);
1310 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1312 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1316 * Now update the key controls with all the key data.
1321 * Blank passphrase, initially. This isn't dangerous,
1322 * because we will warn (Are You Sure?) before allowing
1323 * the user to save an unprotected private key.
1325 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1326 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1330 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1332 * Set the key fingerprint.
1334 savecomment = *state->commentptr;
1335 *state->commentptr = NULL;
1338 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1339 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1343 rsa_fingerprint(buf, sizeof(buf), &state->key);
1344 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1346 *state->commentptr = savecomment;
1348 * Construct a decimal representation of the key, for
1349 * pasting into .ssh/authorized_keys or
1350 * .ssh/authorized_keys2 on a Unix box.
1353 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1354 IDC_PKSTATIC, &state->ssh2key);
1356 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1357 IDC_PKSTATIC, &state->key);
1361 * Finally, hide the progress bar and show the key data.
1363 ui_set_state(hwnd, state, 2);
1367 int id = ((LPHELPINFO)lParam)->iCtrlId;
1370 case IDC_GENERATING:
1374 topic = WINHELP_CTX_puttygen_generate; break;
1376 case IDC_KEYDISPLAY:
1377 topic = WINHELP_CTX_puttygen_pastekey; break;
1379 case IDC_FINGERPRINT:
1380 topic = WINHELP_CTX_puttygen_fingerprint; break;
1381 case IDC_COMMENTSTATIC:
1382 case IDC_COMMENTEDIT:
1383 topic = WINHELP_CTX_puttygen_comment; break;
1384 case IDC_PASSPHRASE1STATIC:
1385 case IDC_PASSPHRASE1EDIT:
1386 case IDC_PASSPHRASE2STATIC:
1387 case IDC_PASSPHRASE2EDIT:
1388 topic = WINHELP_CTX_puttygen_passphrase; break;
1389 case IDC_LOADSTATIC:
1391 topic = WINHELP_CTX_puttygen_load; break;
1392 case IDC_SAVESTATIC:
1394 topic = WINHELP_CTX_puttygen_savepriv; break;
1396 topic = WINHELP_CTX_puttygen_savepub; break;
1397 case IDC_TYPESTATIC:
1399 case IDC_KEYSSH2RSA:
1400 case IDC_KEYSSH2DSA:
1401 topic = WINHELP_CTX_puttygen_keytype; break;
1402 case IDC_BITSSTATIC:
1404 topic = WINHELP_CTX_puttygen_bits; break;
1406 case IDC_EXPORT_OPENSSH:
1407 case IDC_EXPORT_SSHCOM:
1408 topic = WINHELP_CTX_puttygen_conversions; break;
1411 launch_help(hwnd, topic);
1418 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1427 void cleanup_exit(int code)
1433 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1439 InitCommonControls();
1444 * See if we can find our Help file.
1448 split_into_argv(cmdline, &argc, &argv, NULL);
1451 if (!strcmp(argv[0], "-pgpfp")) {
1456 * Assume the first argument to be a private key file, and
1457 * attempt to load it.
1459 cmdline_keyfile = argv[0];
1464 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1467 return ret; /* just in case optimiser complains */