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 static char *cmdline_keyfile
= NULL
;
25 /* ----------------------------------------------------------------------
26 * Progress report code. This is really horrible :-)
28 #define PROGRESSRANGE 65535
34 unsigned startpoint
, total
;
35 unsigned param
, current
, n
; /* if exponential */
36 unsigned mult
; /* if linear */
38 unsigned total
, divisor
, range
;
42 static void progress_update(void *param
, int action
, int phase
, int iprogress
)
44 struct progress
*p
= (struct progress
*) param
;
45 unsigned progress
= iprogress
;
48 if (action
< PROGFN_READY
&& p
->nphases
< phase
)
51 case PROGFN_INITIALISE
:
54 case PROGFN_LIN_PHASE
:
55 p
->phases
[phase
-1].exponential
= 0;
56 p
->phases
[phase
-1].mult
= p
->phases
[phase
].total
/ progress
;
58 case PROGFN_EXP_PHASE
:
59 p
->phases
[phase
-1].exponential
= 1;
60 p
->phases
[phase
-1].param
= 0x10000 + progress
;
61 p
->phases
[phase
-1].current
= p
->phases
[phase
-1].total
;
62 p
->phases
[phase
-1].n
= 0;
64 case PROGFN_PHASE_EXTENT
:
65 p
->phases
[phase
-1].total
= progress
;
71 for (i
= 0; i
< p
->nphases
; i
++) {
72 p
->phases
[i
].startpoint
= total
;
73 total
+= p
->phases
[i
].total
;
76 p
->divisor
= ((p
->total
+ PROGRESSRANGE
- 1) / PROGRESSRANGE
);
77 p
->range
= p
->total
/ p
->divisor
;
78 SendMessage(p
->progbar
, PBM_SETRANGE
, 0, MAKELPARAM(0, p
->range
));
82 if (p
->phases
[phase
-1].exponential
) {
83 while (p
->phases
[phase
-1].n
< progress
) {
84 p
->phases
[phase
-1].n
++;
85 p
->phases
[phase
-1].current
*= p
->phases
[phase
-1].param
;
86 p
->phases
[phase
-1].current
/= 0x10000;
88 position
= (p
->phases
[phase
-1].startpoint
+
89 p
->phases
[phase
-1].total
- p
->phases
[phase
-1].current
);
91 position
= (p
->phases
[phase
-1].startpoint
+
92 progress
* p
->phases
[phase
-1].mult
);
94 SendMessage(p
->progbar
, PBM_SETPOS
, position
/ p
->divisor
, 0);
101 #define PASSPHRASE_MAXLEN 512
103 struct PassphraseProcStruct
{
109 * Dialog-box function for the passphrase box.
111 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
112 WPARAM wParam
, LPARAM lParam
)
114 static char *passphrase
= NULL
;
115 struct PassphraseProcStruct
*p
;
119 SetForegroundWindow(hwnd
);
120 SetWindowPos(hwnd
, HWND_TOP
, 0, 0, 0, 0,
121 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
126 { /* centre the window */
130 hw
= GetDesktopWindow();
131 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
133 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
134 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
135 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
138 p
= (struct PassphraseProcStruct
*) lParam
;
139 passphrase
= p
->passphrase
;
141 SetDlgItemText(hwnd
, 101, p
->comment
);
143 SetDlgItemText(hwnd
, 102, passphrase
);
146 switch (LOWORD(wParam
)) {
156 case 102: /* edit box */
157 if ((HIWORD(wParam
) == EN_CHANGE
) && passphrase
) {
158 GetDlgItemText(hwnd
, 102, passphrase
,
159 PASSPHRASE_MAXLEN
- 1);
160 passphrase
[PASSPHRASE_MAXLEN
- 1] = '\0';
173 * Prompt for a key file. Assumes the filename buffer is of size
176 static int prompt_keyfile(HWND hwnd
, char *dlgtitle
,
177 char *filename
, int save
, int ppk
)
180 memset(&of
, 0, sizeof(of
));
181 #ifdef OPENFILENAME_SIZE_VERSION_400
182 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
184 of
.lStructSize
= sizeof(of
);
188 of
.lpstrFilter
= "PuTTY Private Key Files\0*.PPK\0All Files\0*\0\0\0";
189 of
.lpstrDefExt
= ".ppk";
191 of
.lpstrFilter
= "All Files\0*\0\0\0";
193 of
.lpstrCustomFilter
= NULL
;
195 of
.lpstrFile
= filename
;
197 of
.nMaxFile
= FILENAME_MAX
;
198 of
.lpstrFileTitle
= NULL
;
199 of
.lpstrInitialDir
= NULL
;
200 of
.lpstrTitle
= dlgtitle
;
203 return GetSaveFileName(&of
);
205 return GetOpenFileName(&of
);
209 * This function is needed to link with the DES code. We need not
210 * have it do anything at all.
212 void logevent(char *msg
)
217 * Dialog-box function for the Licence box.
219 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
220 WPARAM wParam
, LPARAM lParam
)
227 { /* centre the window */
231 hw
= GetDesktopWindow();
232 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
234 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
235 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
236 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
241 switch (LOWORD(wParam
)) {
255 * Dialog-box function for the About box.
257 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
258 WPARAM wParam
, LPARAM lParam
)
265 { /* centre the window */
269 hw
= GetDesktopWindow();
270 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
272 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
273 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
274 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
277 SetDlgItemText(hwnd
, 100, ver
);
280 switch (LOWORD(wParam
)) {
285 EnableWindow(hwnd
, 0);
286 DialogBox(hinst
, MAKEINTRESOURCE(214), NULL
, LicenceProc
);
287 EnableWindow(hwnd
, 1);
288 SetActiveWindow(hwnd
);
300 * Thread to generate a key.
302 struct rsa_key_thread_params
{
303 HWND progressbar
; /* notify this with progress */
304 HWND dialog
; /* notify this on completion */
305 int keysize
; /* bits in key */
308 struct dss_key
*dsskey
;
310 static DWORD WINAPI
generate_rsa_key_thread(void *param
)
312 struct rsa_key_thread_params
*params
=
313 (struct rsa_key_thread_params
*) param
;
314 struct progress prog
;
315 prog
.progbar
= params
->progressbar
;
317 progress_update(&prog
, PROGFN_INITIALISE
, 0, 0);
320 dsa_generate(params
->dsskey
, params
->keysize
, progress_update
, &prog
);
322 rsa_generate(params
->key
, params
->keysize
, progress_update
, &prog
);
324 PostMessage(params
->dialog
, WM_DONEKEY
, 0, 0);
330 struct MainDlgState
{
331 int collecting_entropy
;
332 int generation_thread_exists
;
334 int entropy_got
, entropy_required
, entropy_size
;
337 char **commentptr
; /* points to key.comment or ssh2key.comment */
338 struct ssh2_userkey ssh2key
;
341 struct dss_key dsskey
;
342 HMENU filemenu
, keymenu
, cvtmenu
;
345 static void hidemany(HWND hwnd
, const int *ids
, int hideit
)
348 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
352 static void setupbigedit1(HWND hwnd
, int id
, int idstatic
, struct RSAKey
*key
)
357 dec1
= bignum_decimal(key
->exponent
);
358 dec2
= bignum_decimal(key
->modulus
);
359 buffer
= smalloc(strlen(dec1
) + strlen(dec2
) +
360 strlen(key
->comment
) + 30);
361 sprintf(buffer
, "%d %s %s %s",
362 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
363 SetDlgItemText(hwnd
, id
, buffer
);
364 SetDlgItemText(hwnd
, idstatic
,
365 "&Public key for pasting into authorized_keys file:");
371 static void setupbigedit2(HWND hwnd
, int id
, int idstatic
,
372 struct ssh2_userkey
*key
)
374 unsigned char *pub_blob
;
379 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
380 buffer
= smalloc(strlen(key
->alg
->name
) + 4 * ((pub_len
+ 2) / 3) +
381 strlen(key
->comment
) + 3);
382 strcpy(buffer
, key
->alg
->name
);
383 p
= buffer
+ strlen(buffer
);
386 while (i
< pub_len
) {
387 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
388 base64_encode_atom(pub_blob
+ i
, n
, p
);
393 strcpy(p
, key
->comment
);
394 SetDlgItemText(hwnd
, id
, buffer
);
395 SetDlgItemText(hwnd
, idstatic
, "&Public key for pasting into "
396 "OpenSSH authorized_keys2 file:");
401 static int save_ssh1_pubkey(char *filename
, struct RSAKey
*key
)
406 dec1
= bignum_decimal(key
->exponent
);
407 dec2
= bignum_decimal(key
->modulus
);
408 fp
= fopen(filename
, "wb");
411 fprintf(fp
, "%d %s %s %s\n",
412 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
420 * Warn about the obsolescent key file format.
422 void old_keyfile_warning(void)
424 static const char mbtitle
[] = "PuTTY Key File Warning";
425 static const char message
[] =
426 "You are loading an SSH 2 private key which has an\n"
427 "old version of the file format. This means your key\n"
428 "file is not fully tamperproof. Future versions of\n"
429 "PuTTY may stop supporting this private key format,\n"
430 "so we recommend you convert your key to the new\n"
433 "Once the key is loaded into PuTTYgen, you can perform\n"
434 "this conversion simply by saving it again.";
436 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
439 static int save_ssh2_pubkey(char *filename
, struct ssh2_userkey
*key
)
441 unsigned char *pub_blob
;
447 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
449 fp
= fopen(filename
, "wb");
453 fprintf(fp
, "---- BEGIN SSH2 PUBLIC KEY ----\n");
455 fprintf(fp
, "Comment: \"");
456 for (p
= key
->comment
; *p
; p
++) {
457 if (*p
== '\\' || *p
== '\"')
465 while (i
< pub_len
) {
467 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
468 base64_encode_atom(pub_blob
+ i
, n
, buf
);
472 if (++column
>= 16) {
480 fprintf(fp
, "---- END SSH2 PUBLIC KEY ----\n");
487 controlidstart
= 100,
494 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
495 IDC_FPSTATIC
, IDC_FINGERPRINT
,
496 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
497 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
498 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
500 IDC_GENSTATIC
, IDC_GENERATE
,
501 IDC_LOADSTATIC
, IDC_LOAD
,
502 IDC_SAVESTATIC
, IDC_SAVE
, IDC_SAVEPUB
,
504 IDC_TYPESTATIC
, IDC_KEYSSH1
, IDC_KEYSSH2RSA
, IDC_KEYSSH2DSA
,
505 IDC_BITSSTATIC
, IDC_BITS
,
508 IDC_IMPORT
, IDC_EXPORT_OPENSSH
, IDC_EXPORT_SSHCOM
511 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
512 static const int generating_ids
[] = { IDC_GENERATING
, IDC_PROGRESS
, 0 };
513 static const int gotkey_ids
[] = {
514 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
515 IDC_FPSTATIC
, IDC_FINGERPRINT
,
516 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
517 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
518 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
522 * Small UI helper function to switch the state of the main dialog
523 * by enabling and disabling controls and menu items.
525 void ui_set_state(HWND hwnd
, struct MainDlgState
*state
, int status
)
531 hidemany(hwnd
, nokey_ids
, FALSE
);
532 hidemany(hwnd
, generating_ids
, TRUE
);
533 hidemany(hwnd
, gotkey_ids
, TRUE
);
534 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
535 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
536 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
537 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
538 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
539 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
540 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
541 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
542 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
543 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
544 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
545 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
546 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
547 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_ENABLED
|MF_BYCOMMAND
);
548 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_ENABLED
|MF_BYCOMMAND
);
549 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
550 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
551 MF_GRAYED
|MF_BYCOMMAND
);
552 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
553 MF_GRAYED
|MF_BYCOMMAND
);
555 case 1: /* generating key */
556 hidemany(hwnd
, nokey_ids
, TRUE
);
557 hidemany(hwnd
, generating_ids
, FALSE
);
558 hidemany(hwnd
, gotkey_ids
, TRUE
);
559 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
560 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
561 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
562 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
563 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
564 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
565 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
566 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
567 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_GRAYED
|MF_BYCOMMAND
);
568 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
569 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
570 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_GRAYED
|MF_BYCOMMAND
);
571 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_GRAYED
|MF_BYCOMMAND
);
572 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_GRAYED
|MF_BYCOMMAND
);
573 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_GRAYED
|MF_BYCOMMAND
);
574 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_GRAYED
|MF_BYCOMMAND
);
575 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
576 MF_GRAYED
|MF_BYCOMMAND
);
577 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
578 MF_GRAYED
|MF_BYCOMMAND
);
581 hidemany(hwnd
, nokey_ids
, TRUE
);
582 hidemany(hwnd
, generating_ids
, TRUE
);
583 hidemany(hwnd
, gotkey_ids
, FALSE
);
584 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
585 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
586 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
587 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
588 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
589 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
590 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
591 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
592 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
593 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_ENABLED
|MF_BYCOMMAND
);
594 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_ENABLED
|MF_BYCOMMAND
);
595 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
596 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
597 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
,MF_ENABLED
|MF_BYCOMMAND
);
598 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
,MF_ENABLED
|MF_BYCOMMAND
);
599 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
601 * Enable export menu items if and only if the key type
602 * supports this kind of export.
604 type
= state
->ssh2 ? SSH_KEYTYPE_SSH2
: SSH_KEYTYPE_SSH1
;
605 #define do_export_menuitem(x,y) \
606 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
607 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
608 do_export_menuitem(IDC_EXPORT_OPENSSH
, SSH_KEYTYPE_OPENSSH
);
609 do_export_menuitem(IDC_EXPORT_SSHCOM
, SSH_KEYTYPE_SSHCOM
);
610 #undef do_export_menuitem
615 void load_key_file(HWND hwnd
, struct MainDlgState
*state
,
616 char *filename
, int was_import_cmd
)
618 char passphrase
[PASSPHRASE_MAXLEN
];
623 struct PassphraseProcStruct pps
;
624 struct RSAKey newkey1
;
625 struct ssh2_userkey
*newkey2
= NULL
;
627 type
= realtype
= key_type(filename
);
628 if (type
!= SSH_KEYTYPE_SSH1
&&
629 type
!= SSH_KEYTYPE_SSH2
&&
630 !import_possible(type
)) {
632 sprintf(msg
, "Couldn't load private key (%s)",
633 key_type_to_str(type
));
634 MessageBox(NULL
, msg
,
635 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
639 if (type
!= SSH_KEYTYPE_SSH1
&&
640 type
!= SSH_KEYTYPE_SSH2
) {
642 type
= import_target_type(type
);
646 if (realtype
== SSH_KEYTYPE_SSH1
)
647 needs_pass
= rsakey_encrypted(filename
, &comment
);
648 else if (realtype
== SSH_KEYTYPE_SSH2
)
650 ssh2_userkey_encrypted(filename
, &comment
);
652 needs_pass
= import_encrypted(filename
, realtype
,
654 pps
.passphrase
= passphrase
;
655 pps
.comment
= comment
;
659 dlgret
= DialogBoxParam(hinst
,
660 MAKEINTRESOURCE(210),
661 NULL
, PassphraseProc
,
669 if (type
== SSH_KEYTYPE_SSH1
) {
670 if (realtype
== type
)
671 ret
= loadrsakey(filename
, &newkey1
,
674 ret
= import_ssh1(filename
, realtype
,
675 &newkey1
, passphrase
);
677 if (realtype
== type
)
678 newkey2
= ssh2_load_userkey(filename
,
681 newkey2
= import_ssh2(filename
, realtype
,
683 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
694 MessageBox(NULL
, "Couldn't load private key.",
695 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
696 } else if (ret
== 1) {
698 * Now update the key controls with all the
702 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
704 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
706 if (type
== SSH_KEYTYPE_SSH1
) {
711 state
->commentptr
= &state
->key
.comment
;
712 state
->key
= newkey1
;
715 * Set the key fingerprint.
717 savecomment
= state
->key
.comment
;
718 state
->key
.comment
= NULL
;
719 rsa_fingerprint(buf
, sizeof(buf
),
721 state
->key
.comment
= savecomment
;
723 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
725 * Construct a decimal representation
726 * of the key, for pasting into
727 * .ssh/authorized_keys on a Unix box.
729 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
730 IDC_PKSTATIC
, &state
->key
);
737 &state
->ssh2key
.comment
;
738 state
->ssh2key
= *newkey2
; /* structure copy */
741 savecomment
= state
->ssh2key
.comment
;
742 state
->ssh2key
.comment
= NULL
;
745 fingerprint(state
->ssh2key
.data
);
746 state
->ssh2key
.comment
= savecomment
;
748 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
751 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
752 IDC_PKSTATIC
, &state
->ssh2key
);
754 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
758 * Finally, hide the progress bar and show
761 ui_set_state(hwnd
, state
, 2);
762 state
->key_exists
= TRUE
;
765 * If the user has imported a foreign key
766 * using the Load command, let them know.
767 * If they've used the Import command, be
770 if (realtype
!= type
&& !was_import_cmd
) {
772 sprintf(msg
, "Successfully imported foreign key\n"
774 "To use this key with PuTTY, you need to\n"
775 "use the \"Save private key\" command to\n"
776 "save it in PuTTY's own format.",
777 key_type_to_str(realtype
));
778 MessageBox(NULL
, msg
, "PuTTYgen Notice",
779 MB_OK
| MB_ICONINFORMATION
);
785 * Dialog-box function for the main PuTTYgen dialog box.
787 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
788 WPARAM wParam
, LPARAM lParam
)
790 static const char generating_msg
[] =
791 "Please wait while a key is generated...";
792 static const char entropy_msg
[] =
793 "Please generate some randomness by moving the mouse over the blank area.";
794 struct MainDlgState
*state
;
799 SetWindowLong(hwnd
, GWL_EXSTYLE
,
800 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
803 * If we add a Help button, this is where we destroy it
804 * if the help file isn't present.
807 requested_help
= FALSE
;
809 state
= smalloc(sizeof(*state
));
810 state
->generation_thread_exists
= FALSE
;
811 state
->collecting_entropy
= FALSE
;
812 state
->entropy
= NULL
;
813 state
->key_exists
= FALSE
;
814 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
820 menu1
= CreateMenu();
821 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
822 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
823 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
824 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
825 AppendMenu(menu1
, MF_ENABLED
, IDC_QUIT
, "E&xit");
826 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
827 state
->filemenu
= menu1
;
829 menu1
= CreateMenu();
830 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
831 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
832 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH1
, "SSH&1 key (RSA)");
833 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2RSA
, "SSH2 &RSA key");
834 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2DSA
, "SSH2 &DSA key");
835 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Key");
836 state
->keymenu
= menu1
;
838 menu1
= CreateMenu();
839 AppendMenu(menu1
, MF_ENABLED
, IDC_IMPORT
, "&Import key");
840 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
841 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
842 "Export &OpenSSH key");
843 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
844 "Export &ssh.com key");
845 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
847 state
->cvtmenu
= menu1
;
849 menu1
= CreateMenu();
850 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
852 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
853 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
861 { /* centre the window */
865 hw
= GetDesktopWindow();
866 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
868 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
869 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
870 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
874 struct ctlpos cp
, cp2
;
876 /* Accelerators used: acglops1rbd */
878 ctlposinit(&cp
, hwnd
, 4, 4, 4);
879 beginbox(&cp
, "Key", IDC_BOX_KEY
);
881 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
883 statictext(&cp2
, "", 1, IDC_GENERATING
);
884 progressbar(&cp2
, IDC_PROGRESS
);
886 "&Public key for pasting into authorized_keys file:",
887 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
888 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
889 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
890 IDC_FINGERPRINT
, 75);
891 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
893 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
894 IDC_COMMENTEDIT
, 75);
895 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
896 IDC_PASSPHRASE1EDIT
, 75);
897 staticpassedit(&cp
, "C&onfirm passphrase:",
898 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
900 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
901 staticbtn(&cp
, "Generate a public/private key pair",
902 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
903 staticbtn(&cp
, "Load an existing private key file",
904 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
905 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
906 "Save p&ublic key", IDC_SAVEPUB
,
907 "&Save private key", IDC_SAVE
);
909 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
910 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
911 "SSH&1 (RSA)", IDC_KEYSSH1
,
912 "SSH2 &RSA", IDC_KEYSSH2RSA
,
913 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
914 staticedit(&cp
, "Number of &bits in a generated key:",
915 IDC_BITSSTATIC
, IDC_BITS
, 20);
918 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
919 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
920 IDC_KEYSSH1
, MF_BYCOMMAND
);
921 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
924 * Initially, hide the progress bar and the key display,
925 * and show the no-key display. Also disable the Save
926 * buttons, because with no key we obviously can't save
929 ui_set_state(hwnd
, state
, 0);
932 * Load a key file if one was provided on the command line.
935 load_key_file(hwnd
, state
, cmdline_keyfile
, 0);
939 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
940 if (state
->collecting_entropy
&&
941 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
942 state
->entropy
[state
->entropy_got
++] = lParam
;
943 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
944 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
945 state
->entropy_got
, 0);
946 if (state
->entropy_got
>= state
->entropy_required
) {
947 struct rsa_key_thread_params
*params
;
951 * Seed the entropy pool
953 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
954 memset(state
->entropy
, 0, state
->entropy_size
);
955 sfree(state
->entropy
);
956 state
->collecting_entropy
= FALSE
;
958 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
959 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
960 MAKELPARAM(0, PROGRESSRANGE
));
961 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
963 params
= smalloc(sizeof(*params
));
964 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
965 params
->dialog
= hwnd
;
966 params
->keysize
= state
->keysize
;
967 params
->is_dsa
= state
->is_dsa
;
968 params
->key
= &state
->key
;
969 params
->dsskey
= &state
->dsskey
;
971 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
972 params
, 0, &threadid
)) {
973 MessageBox(hwnd
, "Out of thread resources",
974 "Key generation error",
975 MB_OK
| MB_ICONERROR
);
978 state
->generation_thread_exists
= TRUE
;
984 switch (LOWORD(wParam
)) {
989 state
= (struct MainDlgState
*)
990 GetWindowLong(hwnd
, GWL_USERDATA
);
991 if (!IsDlgButtonChecked(hwnd
, LOWORD(wParam
)))
992 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
994 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
995 LOWORD(wParam
), MF_BYCOMMAND
);
999 PostMessage(hwnd
, WM_CLOSE
, 0, 0);
1001 case IDC_COMMENTEDIT
:
1002 if (HIWORD(wParam
) == EN_CHANGE
) {
1003 state
= (struct MainDlgState
*)
1004 GetWindowLong(hwnd
, GWL_USERDATA
);
1005 if (state
->key_exists
) {
1006 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
1007 int len
= GetWindowTextLength(editctl
);
1008 if (*state
->commentptr
)
1009 sfree(*state
->commentptr
);
1010 *state
->commentptr
= smalloc(len
+ 1);
1011 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
1013 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
1016 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
1023 EnableWindow(hwnd
, 0);
1024 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
1025 EnableWindow(hwnd
, 1);
1026 SetActiveWindow(hwnd
);
1029 if (HIWORD(wParam
) == BN_CLICKED
||
1030 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
1032 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
1033 (DWORD
)"JI(`',`puttygen.general')");
1034 requested_help
= TRUE
;
1040 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1041 if (!state
->generation_thread_exists
) {
1043 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
1045 state
->keysize
= DEFAULT_KEYSIZE
;
1046 /* If we ever introduce a new key type, check it here! */
1047 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
1048 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
1049 if (state
->keysize
< 256) {
1050 int ret
= MessageBox(hwnd
,
1051 "PuTTYgen will not generate a key"
1052 " smaller than 256 bits.\n"
1053 "Key length reset to 256. Continue?",
1055 MB_ICONWARNING
| MB_OKCANCEL
);
1058 state
->keysize
= 256;
1059 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
1061 ui_set_state(hwnd
, state
, 1);
1062 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
1063 state
->key_exists
= FALSE
;
1064 state
->collecting_entropy
= TRUE
;
1067 * My brief statistical tests on mouse movements
1068 * suggest that there are about 2.5 bits of
1069 * randomness in the x position, 2.5 in the y
1070 * position, and 1.7 in the message time, making
1071 * 5.7 bits of unpredictability per mouse movement.
1072 * However, other people have told me it's far less
1073 * than that, so I'm going to be stupidly cautious
1074 * and knock that down to a nice round 2. With this
1075 * method, we require two words per mouse movement,
1076 * so with 2 bits per mouse movement we expect 2
1077 * bits every 2 words.
1079 state
->entropy_required
= (state
->keysize
/ 2) * 2;
1080 state
->entropy_got
= 0;
1081 state
->entropy_size
= (state
->entropy_required
*
1082 sizeof(*state
->entropy
));
1083 state
->entropy
= smalloc(state
->entropy_size
);
1085 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1086 MAKELPARAM(0, state
->entropy_required
));
1087 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
1091 case IDC_EXPORT_OPENSSH
:
1092 case IDC_EXPORT_SSHCOM
:
1094 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1095 if (state
->key_exists
) {
1096 char filename
[FILENAME_MAX
];
1097 char passphrase
[PASSPHRASE_MAXLEN
];
1098 char passphrase2
[PASSPHRASE_MAXLEN
];
1102 realtype
= SSH_KEYTYPE_SSH2
;
1104 realtype
= SSH_KEYTYPE_SSH1
;
1106 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
1107 type
= SSH_KEYTYPE_OPENSSH
;
1108 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
1109 type
= SSH_KEYTYPE_SSHCOM
;
1113 if (type
!= realtype
&&
1114 import_target_type(type
) != realtype
) {
1116 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
1117 " format", (state
->ssh2 ?
2 : 1),
1118 (state
->ssh2 ?
1 : 2));
1119 MessageBox(hwnd
, msg
,
1120 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1124 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1125 passphrase
, sizeof(passphrase
));
1126 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1127 passphrase2
, sizeof(passphrase2
));
1128 if (strcmp(passphrase
, passphrase2
)) {
1130 "The two passphrases given do not match.",
1131 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1136 ret
= MessageBox(hwnd
,
1137 "Are you sure you want to save this key\n"
1138 "without a passphrase to protect it?",
1140 MB_YESNO
| MB_ICONWARNING
);
1144 if (prompt_keyfile(hwnd
, "Save private key as:",
1145 filename
, 1, (type
== realtype
))) {
1147 FILE *fp
= fopen(filename
, "r");
1149 char buffer
[FILENAME_MAX
+ 80];
1151 sprintf(buffer
, "Overwrite existing file\n%.*s?",
1152 FILENAME_MAX
, filename
);
1153 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1154 MB_YESNO
| MB_ICONWARNING
);
1160 if (type
!= realtype
)
1161 ret
= export_ssh2(filename
, type
, &state
->ssh2key
,
1162 *passphrase ? passphrase
: NULL
);
1164 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
1165 *passphrase ? passphrase
:
1168 if (type
!= realtype
)
1169 ret
= export_ssh1(filename
, type
, &state
->key
,
1170 *passphrase ? passphrase
: NULL
);
1172 ret
= saversakey(filename
, &state
->key
,
1173 *passphrase ? passphrase
: NULL
);
1176 MessageBox(hwnd
, "Unable to save key file",
1177 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1184 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1185 if (state
->key_exists
) {
1186 char filename
[FILENAME_MAX
];
1187 if (prompt_keyfile(hwnd
, "Save public key as:",
1190 FILE *fp
= fopen(filename
, "r");
1192 char buffer
[FILENAME_MAX
+ 80];
1194 sprintf(buffer
, "Overwrite existing file\n%.*s?",
1195 FILENAME_MAX
, filename
);
1196 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1197 MB_YESNO
| MB_ICONWARNING
);
1202 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
1204 ret
= save_ssh1_pubkey(filename
, &state
->key
);
1207 MessageBox(hwnd
, "Unable to save key file",
1208 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1216 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1217 if (!state
->generation_thread_exists
) {
1218 char filename
[FILENAME_MAX
];
1219 if (prompt_keyfile(hwnd
, "Load private key:",
1220 filename
, 0, LOWORD(wParam
)==IDC_LOAD
))
1221 load_key_file(hwnd
, state
, filename
,
1222 LOWORD(wParam
) != IDC_LOAD
);
1228 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1229 state
->generation_thread_exists
= FALSE
;
1230 state
->key_exists
= TRUE
;
1231 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1232 MAKELPARAM(0, PROGRESSRANGE
));
1233 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1235 if (state
->is_dsa
) {
1236 state
->ssh2key
.data
= &state
->dsskey
;
1237 state
->ssh2key
.alg
= &ssh_dss
;
1239 state
->ssh2key
.data
= &state
->key
;
1240 state
->ssh2key
.alg
= &ssh_rsa
;
1242 state
->commentptr
= &state
->ssh2key
.comment
;
1244 state
->commentptr
= &state
->key
.comment
;
1247 * Invent a comment for the key. We'll do this by including
1248 * the date in it. This will be so horrifyingly ugly that
1249 * the user will immediately want to change it, which is
1252 *state
->commentptr
= smalloc(30);
1259 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1261 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1265 * Now update the key controls with all the key data.
1270 * Blank passphrase, initially. This isn't dangerous,
1271 * because we will warn (Are You Sure?) before allowing
1272 * the user to save an unprotected private key.
1274 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1275 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1279 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1281 * Set the key fingerprint.
1283 savecomment
= *state
->commentptr
;
1284 *state
->commentptr
= NULL
;
1287 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1288 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1292 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1293 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1295 *state
->commentptr
= savecomment
;
1297 * Construct a decimal representation of the key, for
1298 * pasting into .ssh/authorized_keys or
1299 * .ssh/authorized_keys2 on a Unix box.
1302 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1303 IDC_PKSTATIC
, &state
->ssh2key
);
1305 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1306 IDC_PKSTATIC
, &state
->key
);
1310 * Finally, hide the progress bar and show the key data.
1312 ui_set_state(hwnd
, state
, 2);
1316 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1319 case IDC_GENERATING
:
1323 cmd
= "JI(`',`puttygen.generate')"; break;
1325 case IDC_KEYDISPLAY
:
1326 cmd
= "JI(`',`puttygen.pastekey')"; break;
1328 case IDC_FINGERPRINT
:
1329 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1330 case IDC_COMMENTSTATIC
:
1331 case IDC_COMMENTEDIT
:
1332 cmd
= "JI(`',`puttygen.comment')"; break;
1333 case IDC_PASSPHRASE1STATIC
:
1334 case IDC_PASSPHRASE1EDIT
:
1335 case IDC_PASSPHRASE2STATIC
:
1336 case IDC_PASSPHRASE2EDIT
:
1337 cmd
= "JI(`',`puttygen.passphrase')"; break;
1338 case IDC_LOADSTATIC
:
1340 cmd
= "JI(`',`puttygen.load')"; break;
1341 case IDC_SAVESTATIC
:
1343 cmd
= "JI(`',`puttygen.savepriv')"; break;
1345 cmd
= "JI(`',`puttygen.savepub')"; break;
1346 case IDC_TYPESTATIC
:
1348 case IDC_KEYSSH2RSA
:
1349 case IDC_KEYSSH2DSA
:
1350 cmd
= "JI(`',`puttygen.keytype')"; break;
1351 case IDC_BITSSTATIC
:
1353 cmd
= "JI(`',`puttygen.bits')"; break;
1355 case IDC_EXPORT_OPENSSH
:
1356 case IDC_EXPORT_SSHCOM
:
1357 cmd
= "JI(`',`puttygen.conversions')"; break;
1360 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1361 requested_help
= TRUE
;
1368 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1370 if (requested_help
) {
1371 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1372 requested_help
= FALSE
;
1380 void cleanup_exit(int code
) { exit(code
); }
1382 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1387 split_into_argv(cmdline
, &argc
, &argv
, NULL
);
1391 * Assume the first argument to be a private key file, and
1392 * attempt to load it.
1394 cmdline_keyfile
= argv
[0];
1397 InitCommonControls();
1401 * See if we can find our Help file.
1404 char b
[2048], *p
, *q
, *r
;
1406 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1408 p
= strrchr(b
, '\\');
1409 if (p
&& p
>= r
) r
= p
+1;
1410 q
= strrchr(b
, ':');
1411 if (q
&& q
>= r
) r
= q
+1;
1412 strcpy(r
, "putty.hlp");
1413 if ( (fp
= fopen(b
, "r")) != NULL
) {
1414 help_path
= dupstr(b
);
1421 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1422 MainDlgProc
) != IDOK
;