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");
549 menu1
= CreateMenu();
550 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
551 "Export &OpenSSH key");
553 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
554 "Export &ssh.com key");
557 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
560 menu1
= CreateMenu();
561 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
563 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
565 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
573 { /* centre the window */
577 hw
= GetDesktopWindow();
578 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
580 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
581 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
582 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
585 state
= smalloc(sizeof(*state
));
586 state
->generation_thread_exists
= FALSE
;
587 state
->collecting_entropy
= FALSE
;
588 state
->entropy
= NULL
;
589 state
->key_exists
= FALSE
;
590 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
592 struct ctlpos cp
, cp2
;
594 /* Accelerators used: acglops1rbd */
596 ctlposinit(&cp
, hwnd
, 4, 4, 4);
597 beginbox(&cp
, "Key", IDC_BOX_KEY
);
599 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
601 statictext(&cp2
, "", 1, IDC_GENERATING
);
602 progressbar(&cp2
, IDC_PROGRESS
);
604 "&Public key for pasting into authorized_keys file:",
605 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
606 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
607 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
608 IDC_FINGERPRINT
, 75);
609 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
611 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
612 IDC_COMMENTEDIT
, 75);
613 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
614 IDC_PASSPHRASE1EDIT
, 75);
615 staticpassedit(&cp
, "C&onfirm passphrase:",
616 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
618 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
619 staticbtn(&cp
, "Generate a public/private key pair",
620 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
621 staticbtn(&cp
, "Load an existing private key file",
622 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
623 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
624 "Save p&ublic key", IDC_SAVEPUB
,
625 "&Save private key", IDC_SAVE
);
627 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
628 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
629 "SSH&1 (RSA)", IDC_KEYSSH1
,
630 "SSH2 &RSA", IDC_KEYSSH2RSA
,
631 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
632 staticedit(&cp
, "Number of &bits in a generated key:",
633 IDC_BITSSTATIC
, IDC_BITS
, 20);
636 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
637 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
640 * Initially, hide the progress bar and the key display,
641 * and show the no-key display. Also disable the Save
642 * buttons, because with no key we obviously can't save
645 hidemany(hwnd
, nokey_ids
, FALSE
);
646 hidemany(hwnd
, generating_ids
, TRUE
);
647 hidemany(hwnd
, gotkey_ids
, TRUE
);
648 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
649 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
653 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
654 if (state
->collecting_entropy
&&
655 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
656 state
->entropy
[state
->entropy_got
++] = lParam
;
657 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
658 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
659 state
->entropy_got
, 0);
660 if (state
->entropy_got
>= state
->entropy_required
) {
661 struct rsa_key_thread_params
*params
;
665 * Seed the entropy pool
667 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
668 memset(state
->entropy
, 0, state
->entropy_size
);
669 sfree(state
->entropy
);
670 state
->collecting_entropy
= FALSE
;
672 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
673 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
674 MAKELPARAM(0, PROGRESSRANGE
));
675 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
677 params
= smalloc(sizeof(*params
));
678 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
679 params
->dialog
= hwnd
;
680 params
->keysize
= state
->keysize
;
681 params
->is_dsa
= state
->is_dsa
;
682 params
->key
= &state
->key
;
683 params
->dsskey
= &state
->dsskey
;
685 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
686 params
, 0, &threadid
)) {
687 MessageBox(hwnd
, "Out of thread resources",
688 "Key generation error",
689 MB_OK
| MB_ICONERROR
);
692 state
->generation_thread_exists
= TRUE
;
698 switch (LOWORD(wParam
)) {
699 case IDC_COMMENTEDIT
:
700 if (HIWORD(wParam
) == EN_CHANGE
) {
701 state
= (struct MainDlgState
*)
702 GetWindowLong(hwnd
, GWL_USERDATA
);
703 if (state
->key_exists
) {
704 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
705 int len
= GetWindowTextLength(editctl
);
706 if (*state
->commentptr
)
707 sfree(*state
->commentptr
);
708 *state
->commentptr
= smalloc(len
+ 1);
709 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
711 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
714 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
721 EnableWindow(hwnd
, 0);
722 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
723 EnableWindow(hwnd
, 1);
724 SetActiveWindow(hwnd
);
727 if (HIWORD(wParam
) == BN_CLICKED
||
728 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
730 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
731 (DWORD
)"JI(`',`puttygen.general')");
732 requested_help
= TRUE
;
738 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
739 if (!state
->generation_thread_exists
) {
741 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
743 state
->keysize
= DEFAULT_KEYSIZE
;
744 /* If we ever introduce a new key type, check it here! */
745 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
746 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
747 if (state
->keysize
< 256) {
748 int ret
= MessageBox(hwnd
,
749 "PuTTYgen will not generate a key"
750 " smaller than 256 bits.\n"
751 "Key length reset to 256. Continue?",
753 MB_ICONWARNING
| MB_OKCANCEL
);
756 state
->keysize
= 256;
757 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
759 hidemany(hwnd
, nokey_ids
, TRUE
);
760 hidemany(hwnd
, generating_ids
, FALSE
);
761 hidemany(hwnd
, gotkey_ids
, TRUE
);
762 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
763 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
764 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
765 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
766 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
767 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
768 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
769 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
770 state
->key_exists
= FALSE
;
771 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
772 state
->collecting_entropy
= TRUE
;
775 * My brief statistical tests on mouse movements
776 * suggest that there are about 2.5 bits of
777 * randomness in the x position, 2.5 in the y
778 * position, and 1.7 in the message time, making
779 * 5.7 bits of unpredictability per mouse movement.
780 * However, other people have told me it's far less
781 * than that, so I'm going to be stupidly cautious
782 * and knock that down to a nice round 2. With this
783 * method, we require two words per mouse movement,
784 * so with 2 bits per mouse movement we expect 2
785 * bits every 2 words.
787 state
->entropy_required
= (state
->keysize
/ 2) * 2;
788 state
->entropy_got
= 0;
789 state
->entropy_size
= (state
->entropy_required
*
790 sizeof(*state
->entropy
));
791 state
->entropy
= smalloc(state
->entropy_size
);
793 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
794 MAKELPARAM(0, state
->entropy_required
));
795 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
799 case IDC_EXPORT_OPENSSH
:
800 case IDC_EXPORT_SSHCOM
:
802 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
803 if (state
->key_exists
) {
804 char filename
[FILENAME_MAX
];
805 char passphrase
[PASSPHRASE_MAXLEN
];
806 char passphrase2
[PASSPHRASE_MAXLEN
];
810 realtype
= SSH_KEYTYPE_SSH2
;
812 realtype
= SSH_KEYTYPE_SSH1
;
814 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
815 type
= SSH_KEYTYPE_OPENSSH
;
816 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
817 type
= SSH_KEYTYPE_SSHCOM
;
821 if (type
!= realtype
&&
822 import_target_type(type
) != realtype
) {
824 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
825 " format", (state
->ssh2 ?
2 : 1),
826 (state
->ssh2 ?
1 : 2));
827 MessageBox(hwnd
, msg
,
828 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
832 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
833 passphrase
, sizeof(passphrase
));
834 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
835 passphrase2
, sizeof(passphrase2
));
836 if (strcmp(passphrase
, passphrase2
)) {
838 "The two passphrases given do not match.",
839 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
844 ret
= MessageBox(hwnd
,
845 "Are you sure you want to save this key\n"
846 "without a passphrase to protect it?",
848 MB_YESNO
| MB_ICONWARNING
);
852 if (prompt_keyfile(hwnd
, "Save private key as:",
855 FILE *fp
= fopen(filename
, "r");
857 char buffer
[FILENAME_MAX
+ 80];
859 sprintf(buffer
, "Overwrite existing file\n%.*s?",
860 FILENAME_MAX
, filename
);
861 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
862 MB_YESNO
| MB_ICONWARNING
);
868 if (type
!= realtype
)
869 ret
= export_ssh2(filename
, type
, &state
->ssh2key
,
870 *passphrase ? passphrase
: NULL
);
872 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
873 *passphrase ? passphrase
:
876 if (type
!= realtype
)
877 ret
= export_ssh1(filename
, type
, &state
->key
,
878 *passphrase ? passphrase
: NULL
);
880 ret
= saversakey(filename
, &state
->key
,
881 *passphrase ? passphrase
: NULL
);
884 MessageBox(hwnd
, "Unable to save key file",
885 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
892 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
893 if (state
->key_exists
) {
894 char filename
[FILENAME_MAX
];
895 if (prompt_keyfile(hwnd
, "Save public key as:",
898 FILE *fp
= fopen(filename
, "r");
900 char buffer
[FILENAME_MAX
+ 80];
902 sprintf(buffer
, "Overwrite existing file\n%.*s?",
903 FILENAME_MAX
, filename
);
904 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
905 MB_YESNO
| MB_ICONWARNING
);
910 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
912 ret
= save_ssh1_pubkey(filename
, &state
->key
);
915 MessageBox(hwnd
, "Unable to save key file",
916 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
923 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
924 if (!state
->generation_thread_exists
) {
925 char filename
[FILENAME_MAX
];
926 if (prompt_keyfile(hwnd
, "Load private key:", filename
, 0)) {
927 char passphrase
[PASSPHRASE_MAXLEN
];
932 struct PassphraseProcStruct pps
;
933 struct RSAKey newkey1
;
934 struct ssh2_userkey
*newkey2
= NULL
;
936 type
= realtype
= key_type(filename
);
937 if (type
!= SSH_KEYTYPE_SSH1
&&
938 type
!= SSH_KEYTYPE_SSH2
&&
939 !import_possible(type
)) {
941 sprintf(msg
, "Couldn't load private key (%s)",
942 key_type_to_str(type
));
943 MessageBox(NULL
, msg
,
944 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
948 if (type
!= SSH_KEYTYPE_SSH1
&&
949 type
!= SSH_KEYTYPE_SSH2
) {
951 type
= import_target_type(type
);
955 if (realtype
== SSH_KEYTYPE_SSH1
)
956 needs_pass
= rsakey_encrypted(filename
, &comment
);
957 else if (realtype
== SSH_KEYTYPE_SSH2
)
959 ssh2_userkey_encrypted(filename
, &comment
);
961 needs_pass
= import_encrypted(filename
, realtype
,
963 pps
.passphrase
= passphrase
;
964 pps
.comment
= comment
;
968 dlgret
= DialogBoxParam(hinst
,
969 MAKEINTRESOURCE(210),
970 NULL
, PassphraseProc
,
978 if (type
== SSH_KEYTYPE_SSH1
) {
979 if (realtype
== type
)
980 ret
= loadrsakey(filename
, &newkey1
,
983 ret
= import_ssh1(filename
, realtype
,
984 &newkey1
, passphrase
);
986 if (realtype
== type
)
987 newkey2
= ssh2_load_userkey(filename
,
990 newkey2
= import_ssh2(filename
, realtype
,
992 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
1003 MessageBox(NULL
, "Couldn't load private key.",
1004 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1005 } else if (ret
== 1) {
1006 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
1007 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
1008 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
1009 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
1010 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
1011 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
1012 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
1013 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
1015 * Now update the key controls with all the
1019 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1021 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1023 if (type
== SSH_KEYTYPE_SSH1
) {
1027 state
->ssh2
= FALSE
;
1028 state
->commentptr
= &state
->key
.comment
;
1029 state
->key
= newkey1
;
1032 * Set the key fingerprint.
1034 savecomment
= state
->key
.comment
;
1035 state
->key
.comment
= NULL
;
1036 rsa_fingerprint(buf
, sizeof(buf
),
1038 state
->key
.comment
= savecomment
;
1040 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1042 * Construct a decimal representation
1043 * of the key, for pasting into
1044 * .ssh/authorized_keys on a Unix box.
1046 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1047 IDC_PKSTATIC
, &state
->key
);
1054 &state
->ssh2key
.comment
;
1055 state
->ssh2key
= *newkey2
; /* structure copy */
1058 savecomment
= state
->ssh2key
.comment
;
1059 state
->ssh2key
.comment
= NULL
;
1061 state
->ssh2key
.alg
->
1062 fingerprint(state
->ssh2key
.data
);
1063 state
->ssh2key
.comment
= savecomment
;
1065 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1068 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1069 IDC_PKSTATIC
, &state
->ssh2key
);
1071 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
1072 *state
->commentptr
);
1075 * Finally, hide the progress bar and show
1078 hidemany(hwnd
, nokey_ids
, TRUE
);
1079 hidemany(hwnd
, generating_ids
, TRUE
);
1080 hidemany(hwnd
, gotkey_ids
, FALSE
);
1081 state
->key_exists
= TRUE
;
1089 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1090 state
->generation_thread_exists
= FALSE
;
1091 state
->key_exists
= TRUE
;
1092 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1093 MAKELPARAM(0, PROGRESSRANGE
));
1094 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1095 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
1096 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
1097 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
1098 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
1099 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
1100 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
1101 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
1102 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
1104 if (state
->is_dsa
) {
1105 state
->ssh2key
.data
= &state
->dsskey
;
1106 state
->ssh2key
.alg
= &ssh_dss
;
1108 state
->ssh2key
.data
= &state
->key
;
1109 state
->ssh2key
.alg
= &ssh_rsa
;
1111 state
->commentptr
= &state
->ssh2key
.comment
;
1113 state
->commentptr
= &state
->key
.comment
;
1116 * Invent a comment for the key. We'll do this by including
1117 * the date in it. This will be so horrifyingly ugly that
1118 * the user will immediately want to change it, which is
1121 *state
->commentptr
= smalloc(30);
1128 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1130 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1134 * Now update the key controls with all the key data.
1139 * Blank passphrase, initially. This isn't dangerous,
1140 * because we will warn (Are You Sure?) before allowing
1141 * the user to save an unprotected private key.
1143 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1144 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1148 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1150 * Set the key fingerprint.
1152 savecomment
= *state
->commentptr
;
1153 *state
->commentptr
= NULL
;
1156 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1157 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1161 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1162 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1164 *state
->commentptr
= savecomment
;
1166 * Construct a decimal representation of the key, for
1167 * pasting into .ssh/authorized_keys or
1168 * .ssh/authorized_keys2 on a Unix box.
1171 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1172 IDC_PKSTATIC
, &state
->ssh2key
);
1174 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1175 IDC_PKSTATIC
, &state
->key
);
1179 * Finally, hide the progress bar and show the key data.
1181 hidemany(hwnd
, nokey_ids
, TRUE
);
1182 hidemany(hwnd
, generating_ids
, TRUE
);
1183 hidemany(hwnd
, gotkey_ids
, FALSE
);
1187 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1190 case IDC_GENERATING
:
1194 cmd
= "JI(`',`puttygen.generate')"; break;
1196 case IDC_KEYDISPLAY
:
1197 cmd
= "JI(`',`puttygen.pastekey')"; break;
1199 case IDC_FINGERPRINT
:
1200 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1201 case IDC_COMMENTSTATIC
:
1202 case IDC_COMMENTEDIT
:
1203 cmd
= "JI(`',`puttygen.comment')"; break;
1204 case IDC_PASSPHRASE1STATIC
:
1205 case IDC_PASSPHRASE1EDIT
:
1206 case IDC_PASSPHRASE2STATIC
:
1207 case IDC_PASSPHRASE2EDIT
:
1208 cmd
= "JI(`',`puttygen.passphrase')"; break;
1209 case IDC_LOADSTATIC
:
1211 cmd
= "JI(`',`puttygen.load')"; break;
1212 case IDC_SAVESTATIC
:
1214 cmd
= "JI(`',`puttygen.savepriv')"; break;
1216 cmd
= "JI(`',`puttygen.savepub')"; break;
1217 case IDC_TYPESTATIC
:
1219 case IDC_KEYSSH2RSA
:
1220 case IDC_KEYSSH2DSA
:
1221 cmd
= "JI(`',`puttygen.keytype')"; break;
1222 case IDC_BITSSTATIC
:
1224 cmd
= "JI(`',`puttygen.bits')"; break;
1227 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1228 requested_help
= TRUE
;
1235 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1237 if (requested_help
) {
1238 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1239 requested_help
= FALSE
;
1247 void cleanup_exit(int code
) { exit(code
); }
1249 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1251 InitCommonControls();
1255 * See if we can find our Help file.
1258 char b
[2048], *p
, *q
, *r
;
1260 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1262 p
= strrchr(b
, '\\');
1263 if (p
&& p
>= r
) r
= p
+1;
1264 q
= strrchr(b
, ':');
1265 if (q
&& q
>= r
) r
= q
+1;
1266 strcpy(r
, "putty.hlp");
1267 if ( (fp
= fopen(b
, "r")) != NULL
) {
1268 help_path
= dupstr(b
);
1275 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1276 MainDlgProc
) != IDOK
;