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 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
506 static const int generating_ids
[] =
507 { IDC_GENERATING
, IDC_PROGRESS
, 0 };
508 static const int gotkey_ids
[] = {
509 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
510 IDC_FPSTATIC
, IDC_FINGERPRINT
,
511 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
512 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
513 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
515 static const char generating_msg
[] =
516 "Please wait while a key is generated...";
517 static const char entropy_msg
[] =
518 "Please generate some randomness by moving the mouse over the blank area.";
519 struct MainDlgState
*state
;
524 SetWindowLong(hwnd
, GWL_EXSTYLE
,
525 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
528 * If we add a Help button, this is where we destroy it
529 * if the help file isn't present.
532 requested_help
= FALSE
;
537 { /* centre the window */
541 hw
= GetDesktopWindow();
542 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
544 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
545 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
546 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
549 state
= smalloc(sizeof(*state
));
550 state
->generation_thread_exists
= FALSE
;
551 state
->collecting_entropy
= FALSE
;
552 state
->entropy
= NULL
;
553 state
->key_exists
= FALSE
;
554 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
556 struct ctlpos cp
, cp2
;
558 /* Accelerators used: acglops1rbd */
560 ctlposinit(&cp
, hwnd
, 4, 4, 4);
561 bartitle(&cp
, "Public and private key generation for PuTTY",
563 beginbox(&cp
, "Key", IDC_BOX_KEY
);
565 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
567 statictext(&cp2
, "", 1, IDC_GENERATING
);
568 progressbar(&cp2
, IDC_PROGRESS
);
570 "&Public key for pasting into authorized_keys file:",
571 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
572 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
573 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
574 IDC_FINGERPRINT
, 75);
575 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
577 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
578 IDC_COMMENTEDIT
, 75);
579 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
580 IDC_PASSPHRASE1EDIT
, 75);
581 staticpassedit(&cp
, "C&onfirm passphrase:",
582 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
584 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
585 staticbtn(&cp
, "Generate a public/private key pair",
586 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
587 staticbtn(&cp
, "Load an existing private key file",
588 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
589 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
590 "Save p&ublic key", IDC_SAVEPUB
,
591 "&Save private key", IDC_SAVE
);
593 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
594 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
595 "SSH&1 (RSA)", IDC_KEYSSH1
,
596 "SSH2 &RSA", IDC_KEYSSH2RSA
,
597 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
598 staticedit(&cp
, "Number of &bits in a generated key:",
599 IDC_BITSSTATIC
, IDC_BITS
, 20);
602 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
603 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
606 * Initially, hide the progress bar and the key display,
607 * and show the no-key display. Also disable the Save
608 * buttons, because with no key we obviously can't save
611 hidemany(hwnd
, nokey_ids
, FALSE
);
612 hidemany(hwnd
, generating_ids
, TRUE
);
613 hidemany(hwnd
, gotkey_ids
, TRUE
);
614 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
615 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
619 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
620 if (state
->collecting_entropy
&&
621 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
622 state
->entropy
[state
->entropy_got
++] = lParam
;
623 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
624 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
625 state
->entropy_got
, 0);
626 if (state
->entropy_got
>= state
->entropy_required
) {
627 struct rsa_key_thread_params
*params
;
631 * Seed the entropy pool
633 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
634 memset(state
->entropy
, 0, state
->entropy_size
);
635 sfree(state
->entropy
);
636 state
->collecting_entropy
= FALSE
;
638 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
639 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
640 MAKELPARAM(0, PROGRESSRANGE
));
641 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
643 params
= smalloc(sizeof(*params
));
644 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
645 params
->dialog
= hwnd
;
646 params
->keysize
= state
->keysize
;
647 params
->is_dsa
= state
->is_dsa
;
648 params
->key
= &state
->key
;
649 params
->dsskey
= &state
->dsskey
;
651 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
652 params
, 0, &threadid
)) {
653 MessageBox(hwnd
, "Out of thread resources",
654 "Key generation error",
655 MB_OK
| MB_ICONERROR
);
658 state
->generation_thread_exists
= TRUE
;
664 switch (LOWORD(wParam
)) {
665 case IDC_COMMENTEDIT
:
666 if (HIWORD(wParam
) == EN_CHANGE
) {
667 state
= (struct MainDlgState
*)
668 GetWindowLong(hwnd
, GWL_USERDATA
);
669 if (state
->key_exists
) {
670 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
671 int len
= GetWindowTextLength(editctl
);
672 if (*state
->commentptr
)
673 sfree(*state
->commentptr
);
674 *state
->commentptr
= smalloc(len
+ 1);
675 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
677 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
680 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
687 EnableWindow(hwnd
, 0);
688 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
689 EnableWindow(hwnd
, 1);
690 SetActiveWindow(hwnd
);
694 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
695 if (!state
->generation_thread_exists
) {
697 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
699 state
->keysize
= DEFAULT_KEYSIZE
;
700 /* If we ever introduce a new key type, check it here! */
701 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
702 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
703 if (state
->keysize
< 256) {
704 int ret
= MessageBox(hwnd
,
705 "PuTTYgen will not generate a key"
706 " smaller than 256 bits.\n"
707 "Key length reset to 256. Continue?",
709 MB_ICONWARNING
| MB_OKCANCEL
);
712 state
->keysize
= 256;
713 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
715 hidemany(hwnd
, nokey_ids
, TRUE
);
716 hidemany(hwnd
, generating_ids
, FALSE
);
717 hidemany(hwnd
, gotkey_ids
, TRUE
);
718 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
719 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
720 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
721 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
722 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
723 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
724 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
725 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
726 state
->key_exists
= FALSE
;
727 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
728 state
->collecting_entropy
= TRUE
;
731 * My brief statistical tests on mouse movements
732 * suggest that there are about 2.5 bits of
733 * randomness in the x position, 2.5 in the y
734 * position, and 1.7 in the message time, making
735 * 5.7 bits of unpredictability per mouse movement.
736 * However, other people have told me it's far less
737 * than that, so I'm going to be stupidly cautious
738 * and knock that down to a nice round 2. With this
739 * method, we require two words per mouse movement,
740 * so with 2 bits per mouse movement we expect 2
741 * bits every 2 words.
743 state
->entropy_required
= (state
->keysize
/ 2) * 2;
744 state
->entropy_got
= 0;
745 state
->entropy_size
= (state
->entropy_required
*
746 sizeof(*state
->entropy
));
747 state
->entropy
= smalloc(state
->entropy_size
);
749 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
750 MAKELPARAM(0, state
->entropy_required
));
751 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
756 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
757 if (state
->key_exists
) {
758 char filename
[FILENAME_MAX
];
759 char passphrase
[PASSPHRASE_MAXLEN
];
760 char passphrase2
[PASSPHRASE_MAXLEN
];
761 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
762 passphrase
, sizeof(passphrase
));
763 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
764 passphrase2
, sizeof(passphrase2
));
765 if (strcmp(passphrase
, passphrase2
)) {
767 "The two passphrases given do not match.",
768 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
773 ret
= MessageBox(hwnd
,
774 "Are you sure you want to save this key\n"
775 "without a passphrase to protect it?",
777 MB_YESNO
| MB_ICONWARNING
);
781 if (prompt_keyfile(hwnd
, "Save private key as:",
784 FILE *fp
= fopen(filename
, "r");
786 char buffer
[FILENAME_MAX
+ 80];
788 sprintf(buffer
, "Overwrite existing file\n%.*s?",
789 FILENAME_MAX
, filename
);
790 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
791 MB_YESNO
| MB_ICONWARNING
);
796 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
797 *passphrase ? passphrase
:
800 ret
= saversakey(filename
, &state
->key
,
801 *passphrase ? passphrase
: NULL
);
804 MessageBox(hwnd
, "Unable to save key file",
805 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
812 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
813 if (state
->key_exists
) {
814 char filename
[FILENAME_MAX
];
815 if (prompt_keyfile(hwnd
, "Save public key as:",
818 FILE *fp
= fopen(filename
, "r");
820 char buffer
[FILENAME_MAX
+ 80];
822 sprintf(buffer
, "Overwrite existing file\n%.*s?",
823 FILENAME_MAX
, filename
);
824 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
825 MB_YESNO
| MB_ICONWARNING
);
830 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
832 ret
= save_ssh1_pubkey(filename
, &state
->key
);
835 MessageBox(hwnd
, "Unable to save key file",
836 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
843 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
844 if (!state
->generation_thread_exists
) {
845 char filename
[FILENAME_MAX
];
846 if (prompt_keyfile(hwnd
, "Load private key:", filename
, 0)) {
847 char passphrase
[PASSPHRASE_MAXLEN
];
852 struct PassphraseProcStruct pps
;
853 struct RSAKey newkey1
;
854 struct ssh2_userkey
*newkey2
= NULL
;
856 type
= key_type(filename
);
857 if (type
!= SSH_KEYTYPE_SSH1
&& type
!= SSH_KEYTYPE_SSH2
) {
859 sprintf(msg
, "Couldn't load private key (%s)",
860 key_type_to_str(type
));
861 MessageBox(NULL
, msg
,
862 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
867 if (type
== SSH_KEYTYPE_SSH1
)
868 needs_pass
= rsakey_encrypted(filename
, &comment
);
871 ssh2_userkey_encrypted(filename
, &comment
);
872 pps
.passphrase
= passphrase
;
873 pps
.comment
= comment
;
877 dlgret
= DialogBoxParam(hinst
,
878 MAKEINTRESOURCE(210),
879 NULL
, PassphraseProc
,
887 if (type
== SSH_KEYTYPE_SSH1
)
889 loadrsakey(filename
, &newkey1
, passphrase
);
892 ssh2_load_userkey(filename
, passphrase
);
893 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
904 MessageBox(NULL
, "Couldn't load private key.",
905 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
906 } else if (ret
== 1) {
907 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
908 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
909 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
910 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
911 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
912 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
913 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
914 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
916 * Now update the key controls with all the
920 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
922 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
924 if (type
== SSH_KEYTYPE_SSH1
) {
929 state
->commentptr
= &state
->key
.comment
;
930 state
->key
= newkey1
;
933 * Set the key fingerprint.
935 savecomment
= state
->key
.comment
;
936 state
->key
.comment
= NULL
;
937 rsa_fingerprint(buf
, sizeof(buf
),
939 state
->key
.comment
= savecomment
;
941 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
943 * Construct a decimal representation
944 * of the key, for pasting into
945 * .ssh/authorized_keys on a Unix box.
947 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
948 IDC_PKSTATIC
, &state
->key
);
955 &state
->ssh2key
.comment
;
956 state
->ssh2key
= *newkey2
; /* structure copy */
959 savecomment
= state
->ssh2key
.comment
;
960 state
->ssh2key
.comment
= NULL
;
963 fingerprint(state
->ssh2key
.data
);
964 state
->ssh2key
.comment
= savecomment
;
966 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
969 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
970 IDC_PKSTATIC
, &state
->ssh2key
);
972 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
976 * Finally, hide the progress bar and show
979 hidemany(hwnd
, nokey_ids
, TRUE
);
980 hidemany(hwnd
, generating_ids
, TRUE
);
981 hidemany(hwnd
, gotkey_ids
, FALSE
);
982 state
->key_exists
= TRUE
;
990 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
991 state
->generation_thread_exists
= FALSE
;
992 state
->key_exists
= TRUE
;
993 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
994 MAKELPARAM(0, PROGRESSRANGE
));
995 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
996 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
997 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
998 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
999 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
1000 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
1001 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
1002 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
1003 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
1005 if (state
->is_dsa
) {
1006 state
->ssh2key
.data
= &state
->dsskey
;
1007 state
->ssh2key
.alg
= &ssh_dss
;
1009 state
->ssh2key
.data
= &state
->key
;
1010 state
->ssh2key
.alg
= &ssh_rsa
;
1012 state
->commentptr
= &state
->ssh2key
.comment
;
1014 state
->commentptr
= &state
->key
.comment
;
1017 * Invent a comment for the key. We'll do this by including
1018 * the date in it. This will be so horrifyingly ugly that
1019 * the user will immediately want to change it, which is
1022 *state
->commentptr
= smalloc(30);
1029 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1031 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1035 * Now update the key controls with all the key data.
1040 * Blank passphrase, initially. This isn't dangerous,
1041 * because we will warn (Are You Sure?) before allowing
1042 * the user to save an unprotected private key.
1044 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1045 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1049 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1051 * Set the key fingerprint.
1053 savecomment
= *state
->commentptr
;
1054 *state
->commentptr
= NULL
;
1057 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1058 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1062 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1063 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1065 *state
->commentptr
= savecomment
;
1067 * Construct a decimal representation of the key, for
1068 * pasting into .ssh/authorized_keys or
1069 * .ssh/authorized_keys2 on a Unix box.
1072 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1073 IDC_PKSTATIC
, &state
->ssh2key
);
1075 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1076 IDC_PKSTATIC
, &state
->key
);
1080 * Finally, hide the progress bar and show the key data.
1082 hidemany(hwnd
, nokey_ids
, TRUE
);
1083 hidemany(hwnd
, generating_ids
, TRUE
);
1084 hidemany(hwnd
, gotkey_ids
, FALSE
);
1088 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1091 case IDC_GENERATING
:
1095 cmd
= "JI(`',`puttygen.generate')"; break;
1097 case IDC_KEYDISPLAY
:
1098 cmd
= "JI(`',`puttygen.pastekey')"; break;
1100 case IDC_FINGERPRINT
:
1101 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1102 case IDC_COMMENTSTATIC
:
1103 case IDC_COMMENTEDIT
:
1104 cmd
= "JI(`',`puttygen.comment')"; break;
1105 case IDC_PASSPHRASE1STATIC
:
1106 case IDC_PASSPHRASE1EDIT
:
1107 case IDC_PASSPHRASE2STATIC
:
1108 case IDC_PASSPHRASE2EDIT
:
1109 cmd
= "JI(`',`puttygen.passphrase')"; break;
1110 case IDC_LOADSTATIC
:
1112 cmd
= "JI(`',`puttygen.load')"; break;
1113 case IDC_SAVESTATIC
:
1115 cmd
= "JI(`',`puttygen.savepriv')"; break;
1117 cmd
= "JI(`',`puttygen.savepub')"; break;
1118 case IDC_TYPESTATIC
:
1120 case IDC_KEYSSH2RSA
:
1121 case IDC_KEYSSH2DSA
:
1122 cmd
= "JI(`',`puttygen.keytype')"; break;
1123 case IDC_BITSSTATIC
:
1125 cmd
= "JI(`',`puttygen.bits')"; break;
1128 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1129 requested_help
= TRUE
;
1136 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1138 if (requested_help
) {
1139 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1140 requested_help
= FALSE
;
1148 void cleanup_exit(int code
) { exit(code
); }
1150 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1152 InitCommonControls();
1156 * See if we can find our Help file.
1159 char b
[2048], *p
, *q
, *r
;
1161 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1163 p
= strrchr(b
, '\\');
1164 if (p
&& p
>= r
) r
= p
+1;
1165 q
= strrchr(b
, ':');
1166 if (q
&& q
>= r
) r
= q
+1;
1167 strcpy(r
, "putty.hlp");
1168 if ( (fp
= fopen(b
, "r")) != NULL
) {
1169 help_path
= dupstr(b
);
1176 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1177 MainDlgProc
) != IDOK
;