2 * PuTTY key generation front end (Windows).
10 #define PUTTY_DO_GLOBALS
21 #define WM_DONEKEY (WM_APP + 1)
23 #define DEFAULT_KEYSIZE 2048
25 static char *cmdline_keyfile = NULL;
28 * Print a modal (Really Bad) message box and perform a fatal exit.
30 void modalfatalbox(char *fmt, ...)
36 stuff = dupvprintf(fmt, ap);
38 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
39 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
45 * Print a non-fatal message box and do not exit.
47 void nonfatal(char *fmt, ...)
53 stuff = dupvprintf(fmt, ap);
55 MessageBox(NULL, stuff, "PuTTYgen Error",
56 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
60 /* ----------------------------------------------------------------------
61 * Progress report code. This is really horrible :-)
63 #define PROGRESSRANGE 65535
69 unsigned startpoint, total;
70 unsigned param, current, n; /* if exponential */
71 unsigned mult; /* if linear */
73 unsigned total, divisor, range;
77 static void progress_update(void *param, int action, int phase, int iprogress)
79 struct progress *p = (struct progress *) param;
80 unsigned progress = iprogress;
83 if (action < PROGFN_READY && p->nphases < phase)
86 case PROGFN_INITIALISE:
89 case PROGFN_LIN_PHASE:
90 p->phases[phase-1].exponential = 0;
91 p->phases[phase-1].mult = p->phases[phase].total / progress;
93 case PROGFN_EXP_PHASE:
94 p->phases[phase-1].exponential = 1;
95 p->phases[phase-1].param = 0x10000 + progress;
96 p->phases[phase-1].current = p->phases[phase-1].total;
97 p->phases[phase-1].n = 0;
99 case PROGFN_PHASE_EXTENT:
100 p->phases[phase-1].total = progress;
106 for (i = 0; i < p->nphases; i++) {
107 p->phases[i].startpoint = total;
108 total += p->phases[i].total;
111 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
112 p->range = p->total / p->divisor;
113 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
116 case PROGFN_PROGRESS:
117 if (p->phases[phase-1].exponential) {
118 while (p->phases[phase-1].n < progress) {
119 p->phases[phase-1].n++;
120 p->phases[phase-1].current *= p->phases[phase-1].param;
121 p->phases[phase-1].current /= 0x10000;
123 position = (p->phases[phase-1].startpoint +
124 p->phases[phase-1].total - p->phases[phase-1].current);
126 position = (p->phases[phase-1].startpoint +
127 progress * p->phases[phase-1].mult);
129 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
136 struct PassphraseProcStruct {
142 * Dialog-box function for the passphrase box.
144 static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
145 WPARAM wParam, LPARAM lParam)
147 static char **passphrase = NULL;
148 struct PassphraseProcStruct *p;
152 SetForegroundWindow(hwnd);
153 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
154 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
159 { /* centre the window */
163 hw = GetDesktopWindow();
164 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
166 (rs.right + rs.left + rd.left - rd.right) / 2,
167 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
168 rd.right - rd.left, rd.bottom - rd.top, TRUE);
171 p = (struct PassphraseProcStruct *) lParam;
172 passphrase = p->passphrase;
174 SetDlgItemText(hwnd, 101, p->comment);
175 burnstr(*passphrase);
176 *passphrase = dupstr("");
177 SetDlgItemText(hwnd, 102, *passphrase);
180 switch (LOWORD(wParam)) {
190 case 102: /* edit box */
191 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
192 burnstr(*passphrase);
193 *passphrase = GetDlgItemText_alloc(hwnd, 102);
206 * Prompt for a key file. Assumes the filename buffer is of size
209 static int prompt_keyfile(HWND hwnd, char *dlgtitle,
210 char *filename, int save, int ppk)
213 memset(&of, 0, sizeof(of));
216 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
217 "All Files (*.*)\0*\0\0\0";
218 of.lpstrDefExt = ".ppk";
220 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
222 of.lpstrCustomFilter = NULL;
224 of.lpstrFile = filename;
226 of.nMaxFile = FILENAME_MAX;
227 of.lpstrFileTitle = NULL;
228 of.lpstrTitle = dlgtitle;
230 return request_file(NULL, &of, FALSE, save);
234 * Dialog-box function for the Licence box.
236 static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
237 WPARAM wParam, LPARAM lParam)
244 { /* centre the window */
248 hw = GetDesktopWindow();
249 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
251 (rs.right + rs.left + rd.left - rd.right) / 2,
252 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
253 rd.right - rd.left, rd.bottom - rd.top, TRUE);
256 SetDlgItemText(hwnd, 1000,
257 "Copyright 1997-2015 Simon Tatham.\r\n\r\n"
259 "Portions copyright Robert de Bath, Joris van Rantwijk, Delian "
260 "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas "
261 "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, "
262 "Markus Kuhn, Colin Watson, Christopher Staite, and CORE SDI S.A.\r\n\r\n"
264 "Permission is hereby granted, free of charge, to any person "
265 "obtaining a copy of this software and associated documentation "
266 "files (the ""Software""), to deal in the Software without restriction, "
267 "including without limitation the rights to use, copy, modify, merge, "
268 "publish, distribute, sublicense, and/or sell copies of the Software, "
269 "and to permit persons to whom the Software is furnished to do so, "
270 "subject to the following conditions:\r\n\r\n"
272 "The above copyright notice and this permission notice shall be "
273 "included in all copies or substantial portions of the Software.\r\n\r\n"
275 "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT "
276 "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, "
277 "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF "
278 "MERCHANTABILITY, FITNESS FOR A PARTICULAR "
279 "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
280 "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES "
281 "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, "
282 "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN "
283 "CONNECTION WITH THE SOFTWARE OR THE USE OR "
284 "OTHER DEALINGS IN THE SOFTWARE."
288 switch (LOWORD(wParam)) {
303 * Dialog-box function for the About box.
305 static int CALLBACK AboutProc(HWND hwnd, UINT msg,
306 WPARAM wParam, LPARAM lParam)
313 { /* centre the window */
317 hw = GetDesktopWindow();
318 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
320 (rs.right + rs.left + rd.left - rd.right) / 2,
321 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
322 rd.right - rd.left, rd.bottom - rd.top, TRUE);
326 char *text = dupprintf
327 ("Pageant\r\n\r\n%s\r\n\r\n%s",
329 "\251 1997-2015 Simon Tatham. All rights reserved.");
330 SetDlgItemText(hwnd, 1000, text);
335 switch (LOWORD(wParam)) {
341 EnableWindow(hwnd, 0);
342 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
343 EnableWindow(hwnd, 1);
344 SetActiveWindow(hwnd);
356 * Thread to generate a key.
358 struct rsa_key_thread_params {
359 HWND progressbar; /* notify this with progress */
360 HWND dialog; /* notify this on completion */
361 int keysize; /* bits in key */
364 struct dss_key *dsskey;
366 static DWORD WINAPI generate_rsa_key_thread(void *param)
368 struct rsa_key_thread_params *params =
369 (struct rsa_key_thread_params *) param;
370 struct progress prog;
371 prog.progbar = params->progressbar;
373 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
376 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
378 rsa_generate(params->key, params->keysize, progress_update, &prog);
380 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
386 struct MainDlgState {
387 int collecting_entropy;
388 int generation_thread_exists;
390 int entropy_got, entropy_required, entropy_size;
393 char **commentptr; /* points to key.comment or ssh2key.comment */
394 struct ssh2_userkey ssh2key;
397 struct dss_key dsskey;
398 HMENU filemenu, keymenu, cvtmenu;
401 static void hidemany(HWND hwnd, const int *ids, int hideit)
404 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
408 static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
413 dec1 = bignum_decimal(key->exponent);
414 dec2 = bignum_decimal(key->modulus);
415 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
416 dec1, dec2, key->comment);
417 SetDlgItemText(hwnd, id, buffer);
418 SetDlgItemText(hwnd, idstatic,
419 "&Public key for pasting into authorized_keys file:");
425 static void setupbigedit2(HWND hwnd, int id, int idstatic,
426 struct ssh2_userkey *key)
428 unsigned char *pub_blob;
433 pub_blob = key->alg->public_blob(key->data, &pub_len);
434 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
435 strlen(key->comment) + 3, char);
436 strcpy(buffer, key->alg->name);
437 p = buffer + strlen(buffer);
440 while (i < pub_len) {
441 int n = (pub_len - i < 3 ? pub_len - i : 3);
442 base64_encode_atom(pub_blob + i, n, p);
447 strcpy(p, key->comment);
448 SetDlgItemText(hwnd, id, buffer);
449 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
450 "OpenSSH authorized_keys file:");
455 static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
460 fp = fopen(filename, "wb");
463 dec1 = bignum_decimal(key->exponent);
464 dec2 = bignum_decimal(key->modulus);
465 fprintf(fp, "%d %s %s %s\n",
466 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
474 * Warn about the obsolescent key file format.
476 void old_keyfile_warning(void)
478 static const char mbtitle[] = "PuTTY Key File Warning";
479 static const char message[] =
480 "You are loading an SSH-2 private key which has an\n"
481 "old version of the file format. This means your key\n"
482 "file is not fully tamperproof. Future versions of\n"
483 "PuTTY may stop supporting this private key format,\n"
484 "so we recommend you convert your key to the new\n"
487 "Once the key is loaded into PuTTYgen, you can perform\n"
488 "this conversion simply by saving it again.";
490 MessageBox(NULL, message, mbtitle, MB_OK);
493 static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
495 unsigned char *pub_blob;
501 pub_blob = key->alg->public_blob(key->data, &pub_len);
503 fp = fopen(filename, "wb");
507 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
509 fprintf(fp, "Comment: \"");
510 for (p = key->comment; *p; p++) {
511 if (*p == '\\' || *p == '\"')
519 while (i < pub_len) {
521 int n = (pub_len - i < 3 ? pub_len - i : 3);
522 base64_encode_atom(pub_blob + i, n, buf);
526 if (++column >= 16) {
534 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
541 controlidstart = 100,
548 IDC_PKSTATIC, IDC_KEYDISPLAY,
549 IDC_FPSTATIC, IDC_FINGERPRINT,
550 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
551 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
552 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
554 IDC_GENSTATIC, IDC_GENERATE,
555 IDC_LOADSTATIC, IDC_LOAD,
556 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
558 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
559 IDC_BITSSTATIC, IDC_BITS,
562 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
565 static const int nokey_ids[] = { IDC_NOKEY, 0 };
566 static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
567 static const int gotkey_ids[] = {
568 IDC_PKSTATIC, IDC_KEYDISPLAY,
569 IDC_FPSTATIC, IDC_FINGERPRINT,
570 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
571 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
572 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
576 * Small UI helper function to switch the state of the main dialog
577 * by enabling and disabling controls and menu items.
579 void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
585 hidemany(hwnd, nokey_ids, FALSE);
586 hidemany(hwnd, generating_ids, TRUE);
587 hidemany(hwnd, gotkey_ids, TRUE);
588 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
589 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
590 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
591 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
592 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
593 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
594 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
595 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
596 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
597 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
598 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
599 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
600 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
601 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
602 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
603 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
604 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
605 MF_GRAYED|MF_BYCOMMAND);
606 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
607 MF_GRAYED|MF_BYCOMMAND);
609 case 1: /* generating key */
610 hidemany(hwnd, nokey_ids, TRUE);
611 hidemany(hwnd, generating_ids, FALSE);
612 hidemany(hwnd, gotkey_ids, TRUE);
613 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
614 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
615 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
616 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
617 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
618 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
619 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
620 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
621 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
622 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
623 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
624 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
625 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
626 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
627 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
628 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
629 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
630 MF_GRAYED|MF_BYCOMMAND);
631 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
632 MF_GRAYED|MF_BYCOMMAND);
635 hidemany(hwnd, nokey_ids, TRUE);
636 hidemany(hwnd, generating_ids, TRUE);
637 hidemany(hwnd, gotkey_ids, FALSE);
638 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
639 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
640 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
641 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
642 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
643 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
644 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
645 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
646 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
647 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
648 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
649 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
650 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
651 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
652 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
653 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
655 * Enable export menu items if and only if the key type
656 * supports this kind of export.
658 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
659 #define do_export_menuitem(x,y) \
660 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
661 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
662 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
663 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
664 #undef do_export_menuitem
669 void load_key_file(HWND hwnd, struct MainDlgState *state,
670 Filename *filename, int was_import_cmd)
676 const char *errmsg = NULL;
678 struct RSAKey newkey1;
679 struct ssh2_userkey *newkey2 = NULL;
681 type = realtype = key_type(filename);
682 if (type != SSH_KEYTYPE_SSH1 &&
683 type != SSH_KEYTYPE_SSH2 &&
684 !import_possible(type)) {
685 char *msg = dupprintf("Couldn't load private key (%s)",
686 key_type_to_str(type));
687 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
688 HELPCTXID(errors_cantloadkey));
693 if (type != SSH_KEYTYPE_SSH1 &&
694 type != SSH_KEYTYPE_SSH2) {
696 type = import_target_type(type);
701 if (realtype == SSH_KEYTYPE_SSH1)
702 needs_pass = rsakey_encrypted(filename, &comment);
703 else if (realtype == SSH_KEYTYPE_SSH2)
704 needs_pass = ssh2_userkey_encrypted(filename, &comment);
706 needs_pass = import_encrypted(filename, realtype, &comment);
713 struct PassphraseProcStruct pps;
714 pps.passphrase = &passphrase;
715 pps.comment = comment;
716 dlgret = DialogBoxParam(hinst,
717 MAKEINTRESOURCE(210),
718 NULL, PassphraseProc,
724 assert(passphrase != NULL);
726 passphrase = dupstr("");
727 if (type == SSH_KEYTYPE_SSH1) {
728 if (realtype == type)
729 ret = loadrsakey(filename, &newkey1, passphrase, &errmsg);
731 ret = import_ssh1(filename, realtype, &newkey1,
732 passphrase, &errmsg);
734 if (realtype == type)
735 newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg);
737 newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg);
738 if (newkey2 == SSH2_WRONG_PASSPHRASE)
749 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
750 message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR,
751 HELPCTXID(errors_cantloadkey));
753 } else if (ret == 1) {
755 * Now update the key controls with all the
759 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
761 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
763 if (type == SSH_KEYTYPE_SSH1) {
768 state->commentptr = &state->key.comment;
769 state->key = newkey1;
772 * Set the key fingerprint.
774 savecomment = state->key.comment;
775 state->key.comment = NULL;
776 rsa_fingerprint(buf, sizeof(buf),
778 state->key.comment = savecomment;
780 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
782 * Construct a decimal representation
783 * of the key, for pasting into
784 * .ssh/authorized_keys on a Unix box.
786 setupbigedit1(hwnd, IDC_KEYDISPLAY,
787 IDC_PKSTATIC, &state->key);
794 &state->ssh2key.comment;
795 state->ssh2key = *newkey2; /* structure copy */
798 savecomment = state->ssh2key.comment;
799 state->ssh2key.comment = NULL;
802 fingerprint(state->ssh2key.data);
803 state->ssh2key.comment = savecomment;
805 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
808 setupbigedit2(hwnd, IDC_KEYDISPLAY,
809 IDC_PKSTATIC, &state->ssh2key);
811 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
815 * Finally, hide the progress bar and show
818 ui_set_state(hwnd, state, 2);
819 state->key_exists = TRUE;
822 * If the user has imported a foreign key
823 * using the Load command, let them know.
824 * If they've used the Import command, be
827 if (realtype != type && !was_import_cmd) {
829 sprintf(msg, "Successfully imported foreign key\n"
831 "To use this key with PuTTY, you need to\n"
832 "use the \"Save private key\" command to\n"
833 "save it in PuTTY's own format.",
834 key_type_to_str(realtype));
835 MessageBox(NULL, msg, "PuTTYgen Notice",
836 MB_OK | MB_ICONINFORMATION);
843 * Dialog-box function for the main PuTTYgen dialog box.
845 static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
846 WPARAM wParam, LPARAM lParam)
848 static const char generating_msg[] =
849 "Please wait while a key is generated...";
850 static const char entropy_msg[] =
851 "Please generate some randomness by moving the mouse over the blank area.";
852 struct MainDlgState *state;
857 SetWindowLongPtr(hwnd, GWL_EXSTYLE,
858 GetWindowLongPtr(hwnd, GWL_EXSTYLE) |
862 * If we add a Help button, this is where we destroy it
863 * if the help file isn't present.
866 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
867 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
869 state = snew(struct MainDlgState);
870 state->generation_thread_exists = FALSE;
871 state->collecting_entropy = FALSE;
872 state->entropy = NULL;
873 state->key_exists = FALSE;
874 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state);
880 menu1 = CreateMenu();
881 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
882 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
883 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
884 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
885 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
886 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
887 state->filemenu = menu1;
889 menu1 = CreateMenu();
890 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
891 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
892 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)");
893 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key");
894 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key");
895 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
896 state->keymenu = menu1;
898 menu1 = CreateMenu();
899 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
900 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
901 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
902 "Export &OpenSSH key");
903 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
904 "Export &ssh.com key");
905 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
907 state->cvtmenu = menu1;
909 menu1 = CreateMenu();
910 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
912 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
913 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
921 { /* centre the window */
925 hw = GetDesktopWindow();
926 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
928 (rs.right + rs.left + rd.left - rd.right) / 2,
929 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
930 rd.right - rd.left, rd.bottom - rd.top, TRUE);
934 struct ctlpos cp, cp2;
936 /* Accelerators used: acglops1rbd */
938 ctlposinit(&cp, hwnd, 4, 4, 4);
939 beginbox(&cp, "Key", IDC_BOX_KEY);
941 statictext(&cp2, "No key.", 1, IDC_NOKEY);
943 statictext(&cp2, "", 1, IDC_GENERATING);
944 progressbar(&cp2, IDC_PROGRESS);
946 "&Public key for pasting into authorized_keys file:",
947 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
948 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
949 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
950 IDC_FINGERPRINT, 75);
951 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
953 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
954 IDC_COMMENTEDIT, 75);
955 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
956 IDC_PASSPHRASE1EDIT, 75);
957 staticpassedit(&cp, "C&onfirm passphrase:",
958 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
960 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
961 staticbtn(&cp, "Generate a public/private key pair",
962 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
963 staticbtn(&cp, "Load an existing private key file",
964 IDC_LOADSTATIC, "&Load", IDC_LOAD);
965 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
966 "Save p&ublic key", IDC_SAVEPUB,
967 "&Save private key", IDC_SAVE);
969 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
970 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
971 "SSH-&1 (RSA)", IDC_KEYSSH1,
972 "SSH-2 &RSA", IDC_KEYSSH2RSA,
973 "SSH-2 &DSA", IDC_KEYSSH2DSA, NULL);
974 staticedit(&cp, "Number of &bits in a generated key:",
975 IDC_BITSSTATIC, IDC_BITS, 20);
978 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
979 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
980 IDC_KEYSSH2RSA, MF_BYCOMMAND);
981 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
984 * Initially, hide the progress bar and the key display,
985 * and show the no-key display. Also disable the Save
986 * buttons, because with no key we obviously can't save
989 ui_set_state(hwnd, state, 0);
992 * Load a key file if one was provided on the command line.
994 if (cmdline_keyfile) {
995 Filename *fn = filename_from_str(cmdline_keyfile);
996 load_key_file(hwnd, state, fn, 0);
1002 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1003 if (state->collecting_entropy &&
1004 state->entropy && state->entropy_got < state->entropy_required) {
1005 state->entropy[state->entropy_got++] = lParam;
1006 state->entropy[state->entropy_got++] = GetMessageTime();
1007 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
1008 state->entropy_got, 0);
1009 if (state->entropy_got >= state->entropy_required) {
1010 struct rsa_key_thread_params *params;
1014 * Seed the entropy pool
1016 random_add_heavynoise(state->entropy, state->entropy_size);
1017 smemclr(state->entropy, state->entropy_size);
1018 sfree(state->entropy);
1019 state->collecting_entropy = FALSE;
1021 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
1022 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1023 MAKELPARAM(0, PROGRESSRANGE));
1024 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1026 params = snew(struct rsa_key_thread_params);
1027 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
1028 params->dialog = hwnd;
1029 params->keysize = state->keysize;
1030 params->is_dsa = state->is_dsa;
1031 params->key = &state->key;
1032 params->dsskey = &state->dsskey;
1034 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
1035 params, 0, &threadid)) {
1036 MessageBox(hwnd, "Out of thread resources",
1037 "Key generation error",
1038 MB_OK | MB_ICONERROR);
1041 state->generation_thread_exists = TRUE;
1047 switch (LOWORD(wParam)) {
1049 case IDC_KEYSSH2RSA:
1050 case IDC_KEYSSH2DSA:
1052 state = (struct MainDlgState *)
1053 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1054 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1055 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1057 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1058 LOWORD(wParam), MF_BYCOMMAND);
1062 PostMessage(hwnd, WM_CLOSE, 0, 0);
1064 case IDC_COMMENTEDIT:
1065 if (HIWORD(wParam) == EN_CHANGE) {
1066 state = (struct MainDlgState *)
1067 GetWindowLongPtr(hwnd, GWLP_USERDATA);
1068 if (state->key_exists) {
1069 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1070 int len = GetWindowTextLength(editctl);
1071 if (*state->commentptr)
1072 sfree(*state->commentptr);
1073 *state->commentptr = snewn(len + 1, char);
1074 GetWindowText(editctl, *state->commentptr, len + 1);
1076 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1079 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1086 EnableWindow(hwnd, 0);
1087 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
1088 EnableWindow(hwnd, 1);
1089 SetActiveWindow(hwnd);
1092 if (HIWORD(wParam) == BN_CLICKED ||
1093 HIWORD(wParam) == BN_DOUBLECLICKED) {
1094 launch_help(hwnd, WINHELP_CTX_puttygen_general);
1098 if (HIWORD(wParam) != BN_CLICKED &&
1099 HIWORD(wParam) != BN_DOUBLECLICKED)
1102 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1103 if (!state->generation_thread_exists) {
1105 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1107 state->keysize = DEFAULT_KEYSIZE;
1108 /* If we ever introduce a new key type, check it here! */
1109 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
1110 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
1111 if (state->keysize < 256) {
1112 int ret = MessageBox(hwnd,
1113 "PuTTYgen will not generate a key"
1114 " smaller than 256 bits.\n"
1115 "Key length reset to 256. Continue?",
1117 MB_ICONWARNING | MB_OKCANCEL);
1120 state->keysize = 256;
1121 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1123 ui_set_state(hwnd, state, 1);
1124 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
1125 state->key_exists = FALSE;
1126 state->collecting_entropy = TRUE;
1129 * My brief statistical tests on mouse movements
1130 * suggest that there are about 2.5 bits of
1131 * randomness in the x position, 2.5 in the y
1132 * position, and 1.7 in the message time, making
1133 * 5.7 bits of unpredictability per mouse movement.
1134 * However, other people have told me it's far less
1135 * than that, so I'm going to be stupidly cautious
1136 * and knock that down to a nice round 2. With this
1137 * method, we require two words per mouse movement,
1138 * so with 2 bits per mouse movement we expect 2
1139 * bits every 2 words.
1141 state->entropy_required = (state->keysize / 2) * 2;
1142 state->entropy_got = 0;
1143 state->entropy_size = (state->entropy_required *
1145 state->entropy = snewn(state->entropy_required, unsigned);
1147 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1148 MAKELPARAM(0, state->entropy_required));
1149 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1153 case IDC_EXPORT_OPENSSH:
1154 case IDC_EXPORT_SSHCOM:
1155 if (HIWORD(wParam) != BN_CLICKED)
1158 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1159 if (state->key_exists) {
1160 char filename[FILENAME_MAX];
1161 char *passphrase, *passphrase2;
1165 realtype = SSH_KEYTYPE_SSH2;
1167 realtype = SSH_KEYTYPE_SSH1;
1169 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1170 type = SSH_KEYTYPE_OPENSSH;
1171 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1172 type = SSH_KEYTYPE_SSHCOM;
1176 if (type != realtype &&
1177 import_target_type(type) != realtype) {
1179 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
1180 " format", (state->ssh2 ? 2 : 1),
1181 (state->ssh2 ? 1 : 2));
1182 MessageBox(hwnd, msg,
1183 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1187 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1188 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
1189 if (strcmp(passphrase, passphrase2)) {
1191 "The two passphrases given do not match.",
1192 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1193 burnstr(passphrase);
1194 burnstr(passphrase2);
1197 burnstr(passphrase2);
1200 ret = MessageBox(hwnd,
1201 "Are you sure you want to save this key\n"
1202 "without a passphrase to protect it?",
1204 MB_YESNO | MB_ICONWARNING);
1206 burnstr(passphrase);
1210 if (prompt_keyfile(hwnd, "Save private key as:",
1211 filename, 1, (type == realtype))) {
1213 FILE *fp = fopen(filename, "r");
1217 buffer = dupprintf("Overwrite existing file\n%s?",
1219 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1220 MB_YESNO | MB_ICONWARNING);
1223 burnstr(passphrase);
1229 Filename *fn = filename_from_str(filename);
1230 if (type != realtype)
1231 ret = export_ssh2(fn, type, &state->ssh2key,
1232 *passphrase ? passphrase : NULL);
1234 ret = ssh2_save_userkey(fn, &state->ssh2key,
1235 *passphrase ? passphrase :
1239 Filename *fn = filename_from_str(filename);
1240 if (type != realtype)
1241 ret = export_ssh1(fn, type, &state->key,
1242 *passphrase ? passphrase : NULL);
1244 ret = saversakey(fn, &state->key,
1245 *passphrase ? passphrase : NULL);
1249 MessageBox(hwnd, "Unable to save key file",
1250 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1253 burnstr(passphrase);
1257 if (HIWORD(wParam) != BN_CLICKED)
1260 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1261 if (state->key_exists) {
1262 char filename[FILENAME_MAX];
1263 if (prompt_keyfile(hwnd, "Save public key as:",
1266 FILE *fp = fopen(filename, "r");
1270 buffer = dupprintf("Overwrite existing file\n%s?",
1272 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1273 MB_YESNO | MB_ICONWARNING);
1279 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1281 ret = save_ssh1_pubkey(filename, &state->key);
1284 MessageBox(hwnd, "Unable to save key file",
1285 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1292 if (HIWORD(wParam) != BN_CLICKED)
1295 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1296 if (!state->generation_thread_exists) {
1297 char filename[FILENAME_MAX];
1298 if (prompt_keyfile(hwnd, "Load private key:",
1299 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1300 Filename *fn = filename_from_str(filename);
1301 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1309 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1310 state->generation_thread_exists = FALSE;
1311 state->key_exists = TRUE;
1312 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1313 MAKELPARAM(0, PROGRESSRANGE));
1314 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
1316 if (state->is_dsa) {
1317 state->ssh2key.data = &state->dsskey;
1318 state->ssh2key.alg = &ssh_dss;
1320 state->ssh2key.data = &state->key;
1321 state->ssh2key.alg = &ssh_rsa;
1323 state->commentptr = &state->ssh2key.comment;
1325 state->commentptr = &state->key.comment;
1328 * Invent a comment for the key. We'll do this by including
1329 * the date in it. This will be so horrifyingly ugly that
1330 * the user will immediately want to change it, which is
1333 *state->commentptr = snewn(30, char);
1338 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
1340 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
1344 * Now update the key controls with all the key data.
1349 * Blank passphrase, initially. This isn't dangerous,
1350 * because we will warn (Are You Sure?) before allowing
1351 * the user to save an unprotected private key.
1353 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1354 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1358 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1360 * Set the key fingerprint.
1362 savecomment = *state->commentptr;
1363 *state->commentptr = NULL;
1366 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
1367 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1371 rsa_fingerprint(buf, sizeof(buf), &state->key);
1372 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1374 *state->commentptr = savecomment;
1376 * Construct a decimal representation of the key, for
1377 * pasting into .ssh/authorized_keys or
1378 * .ssh/authorized_keys2 on a Unix box.
1381 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1382 IDC_PKSTATIC, &state->ssh2key);
1384 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1385 IDC_PKSTATIC, &state->key);
1389 * Finally, hide the progress bar and show the key data.
1391 ui_set_state(hwnd, state, 2);
1395 int id = ((LPHELPINFO)lParam)->iCtrlId;
1398 case IDC_GENERATING:
1402 topic = WINHELP_CTX_puttygen_generate; break;
1404 case IDC_KEYDISPLAY:
1405 topic = WINHELP_CTX_puttygen_pastekey; break;
1407 case IDC_FINGERPRINT:
1408 topic = WINHELP_CTX_puttygen_fingerprint; break;
1409 case IDC_COMMENTSTATIC:
1410 case IDC_COMMENTEDIT:
1411 topic = WINHELP_CTX_puttygen_comment; break;
1412 case IDC_PASSPHRASE1STATIC:
1413 case IDC_PASSPHRASE1EDIT:
1414 case IDC_PASSPHRASE2STATIC:
1415 case IDC_PASSPHRASE2EDIT:
1416 topic = WINHELP_CTX_puttygen_passphrase; break;
1417 case IDC_LOADSTATIC:
1419 topic = WINHELP_CTX_puttygen_load; break;
1420 case IDC_SAVESTATIC:
1422 topic = WINHELP_CTX_puttygen_savepriv; break;
1424 topic = WINHELP_CTX_puttygen_savepub; break;
1425 case IDC_TYPESTATIC:
1427 case IDC_KEYSSH2RSA:
1428 case IDC_KEYSSH2DSA:
1429 topic = WINHELP_CTX_puttygen_keytype; break;
1430 case IDC_BITSSTATIC:
1432 topic = WINHELP_CTX_puttygen_bits; break;
1434 case IDC_EXPORT_OPENSSH:
1435 case IDC_EXPORT_SSHCOM:
1436 topic = WINHELP_CTX_puttygen_conversions; break;
1439 launch_help(hwnd, topic);
1446 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
1455 void cleanup_exit(int code)
1461 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1467 InitCommonControls();
1472 * See if we can find our Help file.
1476 split_into_argv(cmdline, &argc, &argv, NULL);
1479 if (!strcmp(argv[0], "-pgpfp")) {
1484 * Assume the first argument to be a private key file, and
1485 * attempt to load it.
1487 cmdline_keyfile = argv[0];
1492 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1495 return ret; /* just in case optimiser complains */