2 * PuTTY key generation front end.
11 #define PUTTY_DO_GLOBALS
17 #define WM_DONEKEY (WM_XUSER + 1)
19 #define DEFAULT_KEYSIZE 1024
21 static int requested_help
;
23 /* ----------------------------------------------------------------------
24 * Progress report code. This is really horrible :-)
26 #define PROGRESSRANGE 65535
32 unsigned startpoint
, total
;
33 unsigned param
, current
, n
; /* if exponential */
34 unsigned mult
; /* if linear */
36 unsigned total
, divisor
, range
;
40 static void progress_update(void *param
, int action
, int phase
, int iprogress
)
42 struct progress
*p
= (struct progress
*) param
;
43 unsigned progress
= iprogress
;
46 if (action
< PROGFN_READY
&& p
->nphases
< phase
)
49 case PROGFN_INITIALISE
:
52 case PROGFN_LIN_PHASE
:
53 p
->phases
[phase
-1].exponential
= 0;
54 p
->phases
[phase
-1].mult
= p
->phases
[phase
].total
/ progress
;
56 case PROGFN_EXP_PHASE
:
57 p
->phases
[phase
-1].exponential
= 1;
58 p
->phases
[phase
-1].param
= 0x10000 + progress
;
59 p
->phases
[phase
-1].current
= p
->phases
[phase
-1].total
;
60 p
->phases
[phase
-1].n
= 0;
62 case PROGFN_PHASE_EXTENT
:
63 p
->phases
[phase
-1].total
= progress
;
69 for (i
= 0; i
< p
->nphases
; i
++) {
70 p
->phases
[i
].startpoint
= total
;
71 total
+= p
->phases
[i
].total
;
74 p
->divisor
= ((p
->total
+ PROGRESSRANGE
- 1) / PROGRESSRANGE
);
75 p
->range
= p
->total
/ p
->divisor
;
76 SendMessage(p
->progbar
, PBM_SETRANGE
, 0, MAKELPARAM(0, p
->range
));
80 if (p
->phases
[phase
-1].exponential
) {
81 while (p
->phases
[phase
-1].n
< progress
) {
82 p
->phases
[phase
-1].n
++;
83 p
->phases
[phase
-1].current
*= p
->phases
[phase
-1].param
;
84 p
->phases
[phase
-1].current
/= 0x10000;
86 position
= (p
->phases
[phase
-1].startpoint
+
87 p
->phases
[phase
-1].total
- p
->phases
[phase
-1].current
);
89 position
= (p
->phases
[phase
-1].startpoint
+
90 progress
* p
->phases
[phase
-1].mult
);
92 SendMessage(p
->progbar
, PBM_SETPOS
, position
/ p
->divisor
, 0);
99 #define PASSPHRASE_MAXLEN 512
101 struct PassphraseProcStruct
{
107 * Dialog-box function for the passphrase box.
109 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
110 WPARAM wParam
, LPARAM lParam
)
112 static char *passphrase
= NULL
;
113 struct PassphraseProcStruct
*p
;
117 SetForegroundWindow(hwnd
);
118 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
119 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
124 { /* centre the window */
128 hw
= GetDesktopWindow();
129 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
131 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
132 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
133 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
136 p
= (struct PassphraseProcStruct
*) lParam
;
137 passphrase
= p
->passphrase
;
139 SetDlgItemText(hwnd
, 101, p
->comment
);
141 SetDlgItemText(hwnd
, 102, passphrase
);
144 switch (LOWORD(wParam
)) {
154 case 102: /* edit box */
155 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
156 GetDlgItemText(hwnd
, 102, passphrase
,
157 PASSPHRASE_MAXLEN
- 1);
158 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
171 * Prompt for a key file. Assumes the filename buffer is of size
174 static int prompt_keyfile(HWND hwnd
, char *dlgtitle
,
175 char *filename
, int save
)
178 memset(&of
, 0, sizeof(of
));
179 #ifdef OPENFILENAME_SIZE_VERSION_400
180 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
182 of
.lStructSize
= sizeof(of
);
185 of
.lpstrFilter
= "All Files\0*\0\0\0";
186 of
.lpstrCustomFilter
= NULL
;
188 of
.lpstrFile
= filename
;
190 of
.nMaxFile
= FILENAME_MAX
;
191 of
.lpstrFileTitle
= NULL
;
192 of
.lpstrInitialDir
= NULL
;
193 of
.lpstrTitle
= dlgtitle
;
196 return GetSaveFileName(&of
);
198 return GetOpenFileName(&of
);
202 * This function is needed to link with the DES code. We need not
203 * have it do anything at all.
205 void logevent(char *msg
)
210 * Dialog-box function for the Licence box.
212 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
213 WPARAM wParam
, LPARAM lParam
)
220 { /* centre the window */
224 hw
= GetDesktopWindow();
225 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
227 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
228 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
229 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
234 switch (LOWORD(wParam
)) {
248 * Dialog-box function for the About box.
250 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
251 WPARAM wParam
, LPARAM lParam
)
258 { /* centre the window */
262 hw
= GetDesktopWindow();
263 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
265 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
266 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
267 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
270 SetDlgItemText(hwnd
, 100, ver
);
273 switch (LOWORD(wParam
)) {
278 EnableWindow(hwnd
, 0);
279 DialogBox(hinst
, MAKEINTRESOURCE(214), NULL
, LicenceProc
);
280 EnableWindow(hwnd
, 1);
281 SetActiveWindow(hwnd
);
293 * Thread to generate a key.
295 struct rsa_key_thread_params
{
296 HWND progressbar
; /* notify this with progress */
297 HWND dialog
; /* notify this on completion */
298 int keysize
; /* bits in key */
301 struct dss_key
*dsskey
;
303 static DWORD WINAPI
generate_rsa_key_thread(void *param
)
305 struct rsa_key_thread_params
*params
=
306 (struct rsa_key_thread_params
*) param
;
307 struct progress prog
;
308 prog
.progbar
= params
->progressbar
;
310 progress_update(&prog
, PROGFN_INITIALISE
, 0, 0);
313 dsa_generate(params
->dsskey
, params
->keysize
, progress_update
, &prog
);
315 rsa_generate(params
->key
, params
->keysize
, progress_update
, &prog
);
317 PostMessage(params
->dialog
, WM_DONEKEY
, 0, 0);
323 struct MainDlgState
{
324 int collecting_entropy
;
325 int generation_thread_exists
;
327 int entropy_got
, entropy_required
, entropy_size
;
330 char **commentptr
; /* points to key.comment or ssh2key.comment */
331 struct ssh2_userkey ssh2key
;
334 struct dss_key dsskey
;
337 static void hidemany(HWND hwnd
, const int *ids
, int hideit
)
340 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
344 static void setupbigedit1(HWND hwnd
, int id
, int idstatic
, struct RSAKey
*key
)
349 dec1
= bignum_decimal(key
->exponent
);
350 dec2
= bignum_decimal(key
->modulus
);
351 buffer
= smalloc(strlen(dec1
) + strlen(dec2
) +
352 strlen(key
->comment
) + 30);
353 sprintf(buffer
, "%d %s %s %s",
354 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
355 SetDlgItemText(hwnd
, id
, buffer
);
356 SetDlgItemText(hwnd
, idstatic
,
357 "&Public key for pasting into authorized_keys file:");
363 static void setupbigedit2(HWND hwnd
, int id
, int idstatic
,
364 struct ssh2_userkey
*key
)
366 unsigned char *pub_blob
;
371 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
372 buffer
= smalloc(strlen(key
->alg
->name
) + 4 * ((pub_len
+ 2) / 3) +
373 strlen(key
->comment
) + 3);
374 strcpy(buffer
, key
->alg
->name
);
375 p
= buffer
+ strlen(buffer
);
378 while (i
< pub_len
) {
379 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
380 base64_encode_atom(pub_blob
+ i
, n
, p
);
385 strcpy(p
, key
->comment
);
386 SetDlgItemText(hwnd
, id
, buffer
);
387 SetDlgItemText(hwnd
, idstatic
, "&Public key for pasting into "
388 "OpenSSH authorized_keys2 file:");
393 static int save_ssh1_pubkey(char *filename
, struct RSAKey
*key
)
398 dec1
= bignum_decimal(key
->exponent
);
399 dec2
= bignum_decimal(key
->modulus
);
400 fp
= fopen(filename
, "wb");
403 fprintf(fp
, "%d %s %s %s\n",
404 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
412 * Warn about the obsolescent key file format.
414 void old_keyfile_warning(void)
416 static const char mbtitle
[] = "PuTTY Key File Warning";
417 static const char message
[] =
418 "You are loading an SSH 2 private key which has an\n"
419 "old version of the file format. This means your key\n"
420 "file is not fully tamperproof. Future versions of\n"
421 "PuTTY may stop supporting this private key format,\n"
422 "so we recommend you convert your key to the new\n"
425 "Once the key is loaded into PuTTYgen, you can perform\n"
426 "this conversion simply by saving it again.";
428 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
431 static int save_ssh2_pubkey(char *filename
, struct ssh2_userkey
*key
)
433 unsigned char *pub_blob
;
439 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
441 fp
= fopen(filename
, "wb");
445 fprintf(fp
, "---- BEGIN SSH2 PUBLIC KEY ----\n");
447 fprintf(fp
, "Comment: \"");
448 for (p
= key
->comment
; *p
; p
++) {
449 if (*p
== '\\' || *p
== '\"')
457 while (i
< pub_len
) {
459 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
460 base64_encode_atom(pub_blob
+ i
, n
, buf
);
464 if (++column
>= 16) {
472 fprintf(fp
, "---- END SSH2 PUBLIC KEY ----\n");
479 * Dialog-box function for the main PuTTYgen dialog box.
481 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
482 WPARAM wParam
, LPARAM lParam
)
485 controlidstart
= 100,
491 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
492 IDC_FPSTATIC
, IDC_FINGERPRINT
,
493 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
494 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
495 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
497 IDC_GENSTATIC
, IDC_GENERATE
,
498 IDC_LOADSTATIC
, IDC_LOAD
,
499 IDC_SAVESTATIC
, IDC_SAVE
, IDC_SAVEPUB
,
501 IDC_TYPESTATIC
, IDC_KEYSSH1
, IDC_KEYSSH2RSA
, IDC_KEYSSH2DSA
,
502 IDC_BITSSTATIC
, IDC_BITS
,
505 IDC_IMPORT
, IDC_EXPORT_OPENSSH
, IDC_EXPORT_SSHCOM
507 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
508 static const int generating_ids
[] =
509 { IDC_GENERATING
, IDC_PROGRESS
, 0 };
510 static const int gotkey_ids
[] = {
511 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
512 IDC_FPSTATIC
, IDC_FINGERPRINT
,
513 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
514 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
515 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
517 static const char generating_msg
[] =
518 "Please wait while a key is generated...";
519 static const char entropy_msg
[] =
520 "Please generate some randomness by moving the mouse over the blank area.";
521 struct MainDlgState
*state
;
526 SetWindowLong(hwnd
, GWL_EXSTYLE
,
527 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
530 * If we add a Help button, this is where we destroy it
531 * if the help file isn't present.
534 requested_help
= FALSE
;
541 menu1
= CreateMenu();
542 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
543 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
544 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
545 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
547 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
551 * Exporting not yet supported, but when we do it we
552 * should just put this lot back in.
554 menu1
= CreateMenu();
555 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
556 "Export &OpenSSH key");
557 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
558 "Export &ssh.com key");
560 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
564 menu1
= CreateMenu();
565 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
567 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
569 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
577 { /* centre the window */
581 hw
= GetDesktopWindow();
582 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
584 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
585 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
586 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
589 state
= smalloc(sizeof(*state
));
590 state
->generation_thread_exists
= FALSE
;
591 state
->collecting_entropy
= FALSE
;
592 state
->entropy
= NULL
;
593 state
->key_exists
= FALSE
;
594 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
596 struct ctlpos cp
, cp2
;
598 /* Accelerators used: acglops1rbd */
600 ctlposinit(&cp
, hwnd
, 4, 4, 4);
601 beginbox(&cp
, "Key", IDC_BOX_KEY
);
603 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
605 statictext(&cp2
, "", 1, IDC_GENERATING
);
606 progressbar(&cp2
, IDC_PROGRESS
);
608 "&Public key for pasting into authorized_keys file:",
609 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
610 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
611 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
612 IDC_FINGERPRINT
, 75);
613 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
615 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
616 IDC_COMMENTEDIT
, 75);
617 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
618 IDC_PASSPHRASE1EDIT
, 75);
619 staticpassedit(&cp
, "C&onfirm passphrase:",
620 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
622 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
623 staticbtn(&cp
, "Generate a public/private key pair",
624 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
625 staticbtn(&cp
, "Load an existing private key file",
626 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
627 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
628 "Save p&ublic key", IDC_SAVEPUB
,
629 "&Save private key", IDC_SAVE
);
631 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
632 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
633 "SSH&1 (RSA)", IDC_KEYSSH1
,
634 "SSH2 &RSA", IDC_KEYSSH2RSA
,
635 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
636 staticedit(&cp
, "Number of &bits in a generated key:",
637 IDC_BITSSTATIC
, IDC_BITS
, 20);
640 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
641 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
644 * Initially, hide the progress bar and the key display,
645 * and show the no-key display. Also disable the Save
646 * buttons, because with no key we obviously can't save
649 hidemany(hwnd
, nokey_ids
, FALSE
);
650 hidemany(hwnd
, generating_ids
, TRUE
);
651 hidemany(hwnd
, gotkey_ids
, TRUE
);
652 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
653 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
657 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
658 if (state
->collecting_entropy
&&
659 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
660 state
->entropy
[state
->entropy_got
++] = lParam
;
661 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
662 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
663 state
->entropy_got
, 0);
664 if (state
->entropy_got
>= state
->entropy_required
) {
665 struct rsa_key_thread_params
*params
;
669 * Seed the entropy pool
671 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
672 memset(state
->entropy
, 0, state
->entropy_size
);
673 sfree(state
->entropy
);
674 state
->collecting_entropy
= FALSE
;
676 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
677 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
678 MAKELPARAM(0, PROGRESSRANGE
));
679 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
681 params
= smalloc(sizeof(*params
));
682 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
683 params
->dialog
= hwnd
;
684 params
->keysize
= state
->keysize
;
685 params
->is_dsa
= state
->is_dsa
;
686 params
->key
= &state
->key
;
687 params
->dsskey
= &state
->dsskey
;
689 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
690 params
, 0, &threadid
)) {
691 MessageBox(hwnd
, "Out of thread resources",
692 "Key generation error",
693 MB_OK
| MB_ICONERROR
);
696 state
->generation_thread_exists
= TRUE
;
702 switch (LOWORD(wParam
)) {
703 case IDC_COMMENTEDIT
:
704 if (HIWORD(wParam
) == EN_CHANGE
) {
705 state
= (struct MainDlgState
*)
706 GetWindowLong(hwnd
, GWL_USERDATA
);
707 if (state
->key_exists
) {
708 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
709 int len
= GetWindowTextLength(editctl
);
710 if (*state
->commentptr
)
711 sfree(*state
->commentptr
);
712 *state
->commentptr
= smalloc(len
+ 1);
713 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
715 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
718 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
725 EnableWindow(hwnd
, 0);
726 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
727 EnableWindow(hwnd
, 1);
728 SetActiveWindow(hwnd
);
731 if (HIWORD(wParam
) == BN_CLICKED
||
732 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
734 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
735 (DWORD
)"JI(`',`puttygen.general')");
736 requested_help
= TRUE
;
742 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
743 if (!state
->generation_thread_exists
) {
745 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
747 state
->keysize
= DEFAULT_KEYSIZE
;
748 /* If we ever introduce a new key type, check it here! */
749 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
750 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
751 if (state
->keysize
< 256) {
752 int ret
= MessageBox(hwnd
,
753 "PuTTYgen will not generate a key"
754 " smaller than 256 bits.\n"
755 "Key length reset to 256. Continue?",
757 MB_ICONWARNING
| MB_OKCANCEL
);
760 state
->keysize
= 256;
761 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
763 hidemany(hwnd
, nokey_ids
, TRUE
);
764 hidemany(hwnd
, generating_ids
, FALSE
);
765 hidemany(hwnd
, gotkey_ids
, TRUE
);
766 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
767 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
768 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
769 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
770 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
771 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
772 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
773 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
774 state
->key_exists
= FALSE
;
775 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
776 state
->collecting_entropy
= TRUE
;
779 * My brief statistical tests on mouse movements
780 * suggest that there are about 2.5 bits of
781 * randomness in the x position, 2.5 in the y
782 * position, and 1.7 in the message time, making
783 * 5.7 bits of unpredictability per mouse movement.
784 * However, other people have told me it's far less
785 * than that, so I'm going to be stupidly cautious
786 * and knock that down to a nice round 2. With this
787 * method, we require two words per mouse movement,
788 * so with 2 bits per mouse movement we expect 2
789 * bits every 2 words.
791 state
->entropy_required
= (state
->keysize
/ 2) * 2;
792 state
->entropy_got
= 0;
793 state
->entropy_size
= (state
->entropy_required
*
794 sizeof(*state
->entropy
));
795 state
->entropy
= smalloc(state
->entropy_size
);
797 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
798 MAKELPARAM(0, state
->entropy_required
));
799 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
803 case IDC_EXPORT_OPENSSH
:
804 case IDC_EXPORT_SSHCOM
:
806 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
807 if (state
->key_exists
) {
808 char filename
[FILENAME_MAX
];
809 char passphrase
[PASSPHRASE_MAXLEN
];
810 char passphrase2
[PASSPHRASE_MAXLEN
];
814 realtype
= SSH_KEYTYPE_SSH2
;
816 realtype
= SSH_KEYTYPE_SSH1
;
818 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
819 type
= SSH_KEYTYPE_OPENSSH
;
820 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
821 type
= SSH_KEYTYPE_SSHCOM
;
825 if (type
!= realtype
&&
826 import_target_type(type
) != realtype
) {
828 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
829 " format", (state
->ssh2 ?
2 : 1),
830 (state
->ssh2 ?
1 : 2));
831 MessageBox(hwnd
, msg
,
832 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
836 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
837 passphrase
, sizeof(passphrase
));
838 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
839 passphrase2
, sizeof(passphrase2
));
840 if (strcmp(passphrase
, passphrase2
)) {
842 "The two passphrases given do not match.",
843 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
848 ret
= MessageBox(hwnd
,
849 "Are you sure you want to save this key\n"
850 "without a passphrase to protect it?",
852 MB_YESNO
| MB_ICONWARNING
);
856 if (prompt_keyfile(hwnd
, "Save private key as:",
859 FILE *fp
= fopen(filename
, "r");
861 char buffer
[FILENAME_MAX
+ 80];
863 sprintf(buffer
, "Overwrite existing file\n%.*s?",
864 FILENAME_MAX
, filename
);
865 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
866 MB_YESNO
| MB_ICONWARNING
);
872 if (type
!= realtype
)
873 ret
= export_ssh2(filename
, type
, &state
->ssh2key
,
874 *passphrase ? passphrase
: NULL
);
876 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
877 *passphrase ? passphrase
:
880 if (type
!= realtype
)
881 ret
= export_ssh1(filename
, type
, &state
->key
,
882 *passphrase ? passphrase
: NULL
);
884 ret
= saversakey(filename
, &state
->key
,
885 *passphrase ? passphrase
: NULL
);
888 MessageBox(hwnd
, "Unable to save key file",
889 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
896 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
897 if (state
->key_exists
) {
898 char filename
[FILENAME_MAX
];
899 if (prompt_keyfile(hwnd
, "Save public key as:",
902 FILE *fp
= fopen(filename
, "r");
904 char buffer
[FILENAME_MAX
+ 80];
906 sprintf(buffer
, "Overwrite existing file\n%.*s?",
907 FILENAME_MAX
, filename
);
908 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
909 MB_YESNO
| MB_ICONWARNING
);
914 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
916 ret
= save_ssh1_pubkey(filename
, &state
->key
);
919 MessageBox(hwnd
, "Unable to save key file",
920 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
927 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
928 if (!state
->generation_thread_exists
) {
929 char filename
[FILENAME_MAX
];
930 if (prompt_keyfile(hwnd
, "Load private key:", filename
, 0)) {
931 char passphrase
[PASSPHRASE_MAXLEN
];
936 struct PassphraseProcStruct pps
;
937 struct RSAKey newkey1
;
938 struct ssh2_userkey
*newkey2
= NULL
;
940 type
= realtype
= key_type(filename
);
941 if (type
!= SSH_KEYTYPE_SSH1
&&
942 type
!= SSH_KEYTYPE_SSH2
&&
943 !import_possible(type
)) {
945 sprintf(msg
, "Couldn't load private key (%s)",
946 key_type_to_str(type
));
947 MessageBox(NULL
, msg
,
948 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
952 if (type
!= SSH_KEYTYPE_SSH1
&&
953 type
!= SSH_KEYTYPE_SSH2
) {
955 type
= import_target_type(type
);
959 if (realtype
== SSH_KEYTYPE_SSH1
)
960 needs_pass
= rsakey_encrypted(filename
, &comment
);
961 else if (realtype
== SSH_KEYTYPE_SSH2
)
963 ssh2_userkey_encrypted(filename
, &comment
);
965 needs_pass
= import_encrypted(filename
, realtype
,
967 pps
.passphrase
= passphrase
;
968 pps
.comment
= comment
;
972 dlgret
= DialogBoxParam(hinst
,
973 MAKEINTRESOURCE(210),
974 NULL
, PassphraseProc
,
982 if (type
== SSH_KEYTYPE_SSH1
) {
983 if (realtype
== type
)
984 ret
= loadrsakey(filename
, &newkey1
,
987 ret
= import_ssh1(filename
, realtype
,
988 &newkey1
, passphrase
);
990 if (realtype
== type
)
991 newkey2
= ssh2_load_userkey(filename
,
994 newkey2
= import_ssh2(filename
, realtype
,
996 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
1003 } while (ret
== -1);
1007 MessageBox(NULL
, "Couldn't load private key.",
1008 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1009 } else if (ret
== 1) {
1010 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
1011 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
1012 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
1013 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
1014 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
1015 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
1016 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
1017 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
1019 * Now update the key controls with all the
1023 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1025 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1027 if (type
== SSH_KEYTYPE_SSH1
) {
1031 state
->ssh2
= FALSE
;
1032 state
->commentptr
= &state
->key
.comment
;
1033 state
->key
= newkey1
;
1036 * Set the key fingerprint.
1038 savecomment
= state
->key
.comment
;
1039 state
->key
.comment
= NULL
;
1040 rsa_fingerprint(buf
, sizeof(buf
),
1042 state
->key
.comment
= savecomment
;
1044 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1046 * Construct a decimal representation
1047 * of the key, for pasting into
1048 * .ssh/authorized_keys on a Unix box.
1050 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1051 IDC_PKSTATIC
, &state
->key
);
1058 &state
->ssh2key
.comment
;
1059 state
->ssh2key
= *newkey2
; /* structure copy */
1062 savecomment
= state
->ssh2key
.comment
;
1063 state
->ssh2key
.comment
= NULL
;
1065 state
->ssh2key
.alg
->
1066 fingerprint(state
->ssh2key
.data
);
1067 state
->ssh2key
.comment
= savecomment
;
1069 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1072 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1073 IDC_PKSTATIC
, &state
->ssh2key
);
1075 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
1076 *state
->commentptr
);
1079 * Finally, hide the progress bar and show
1082 hidemany(hwnd
, nokey_ids
, TRUE
);
1083 hidemany(hwnd
, generating_ids
, TRUE
);
1084 hidemany(hwnd
, gotkey_ids
, FALSE
);
1085 state
->key_exists
= TRUE
;
1093 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1094 state
->generation_thread_exists
= FALSE
;
1095 state
->key_exists
= TRUE
;
1096 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1097 MAKELPARAM(0, PROGRESSRANGE
));
1098 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1099 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
1100 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
1101 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
1102 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
1103 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
1104 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
1105 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
1106 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
1108 if (state
->is_dsa
) {
1109 state
->ssh2key
.data
= &state
->dsskey
;
1110 state
->ssh2key
.alg
= &ssh_dss
;
1112 state
->ssh2key
.data
= &state
->key
;
1113 state
->ssh2key
.alg
= &ssh_rsa
;
1115 state
->commentptr
= &state
->ssh2key
.comment
;
1117 state
->commentptr
= &state
->key
.comment
;
1120 * Invent a comment for the key. We'll do this by including
1121 * the date in it. This will be so horrifyingly ugly that
1122 * the user will immediately want to change it, which is
1125 *state
->commentptr
= smalloc(30);
1132 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1134 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1138 * Now update the key controls with all the key data.
1143 * Blank passphrase, initially. This isn't dangerous,
1144 * because we will warn (Are You Sure?) before allowing
1145 * the user to save an unprotected private key.
1147 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1148 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1152 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1154 * Set the key fingerprint.
1156 savecomment
= *state
->commentptr
;
1157 *state
->commentptr
= NULL
;
1160 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1161 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1165 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1166 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1168 *state
->commentptr
= savecomment
;
1170 * Construct a decimal representation of the key, for
1171 * pasting into .ssh/authorized_keys or
1172 * .ssh/authorized_keys2 on a Unix box.
1175 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1176 IDC_PKSTATIC
, &state
->ssh2key
);
1178 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1179 IDC_PKSTATIC
, &state
->key
);
1183 * Finally, hide the progress bar and show the key data.
1185 hidemany(hwnd
, nokey_ids
, TRUE
);
1186 hidemany(hwnd
, generating_ids
, TRUE
);
1187 hidemany(hwnd
, gotkey_ids
, FALSE
);
1191 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1194 case IDC_GENERATING
:
1198 cmd
= "JI(`',`puttygen.generate')"; break;
1200 case IDC_KEYDISPLAY
:
1201 cmd
= "JI(`',`puttygen.pastekey')"; break;
1203 case IDC_FINGERPRINT
:
1204 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1205 case IDC_COMMENTSTATIC
:
1206 case IDC_COMMENTEDIT
:
1207 cmd
= "JI(`',`puttygen.comment')"; break;
1208 case IDC_PASSPHRASE1STATIC
:
1209 case IDC_PASSPHRASE1EDIT
:
1210 case IDC_PASSPHRASE2STATIC
:
1211 case IDC_PASSPHRASE2EDIT
:
1212 cmd
= "JI(`',`puttygen.passphrase')"; break;
1213 case IDC_LOADSTATIC
:
1215 cmd
= "JI(`',`puttygen.load')"; break;
1216 case IDC_SAVESTATIC
:
1218 cmd
= "JI(`',`puttygen.savepriv')"; break;
1220 cmd
= "JI(`',`puttygen.savepub')"; break;
1221 case IDC_TYPESTATIC
:
1223 case IDC_KEYSSH2RSA
:
1224 case IDC_KEYSSH2DSA
:
1225 cmd
= "JI(`',`puttygen.keytype')"; break;
1226 case IDC_BITSSTATIC
:
1228 cmd
= "JI(`',`puttygen.bits')"; break;
1231 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1232 requested_help
= TRUE
;
1239 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1241 if (requested_help
) {
1242 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1243 requested_help
= FALSE
;
1251 void cleanup_exit(int code
) { exit(code
); }
1253 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1255 InitCommonControls();
1259 * See if we can find our Help file.
1262 char b
[2048], *p
, *q
, *r
;
1264 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1266 p
= strrchr(b
, '\\');
1267 if (p
&& p
>= r
) r
= p
+1;
1268 q
= strrchr(b
, ':');
1269 if (q
&& q
>= r
) r
= q
+1;
1270 strcpy(r
, "putty.hlp");
1271 if ( (fp
= fopen(b
, "r")) != NULL
) {
1272 help_path
= dupstr(b
);
1279 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1280 MainDlgProc
) != IDOK
;