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
= "PuTTY Private Key Files\0*.PPK\0AllFiles\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
.lpstrDefExt
= ".ppk";
194 of
.lpstrTitle
= dlgtitle
;
197 return GetSaveFileName(&of
);
199 return GetOpenFileName(&of
);
203 * This function is needed to link with the DES code. We need not
204 * have it do anything at all.
206 void logevent(char *msg
)
211 * Dialog-box function for the Licence box.
213 static int CALLBACK
LicenceProc(HWND hwnd
, UINT msg
,
214 WPARAM wParam
, LPARAM lParam
)
221 { /* centre the window */
225 hw
= GetDesktopWindow();
226 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
228 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
229 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
230 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
235 switch (LOWORD(wParam
)) {
249 * Dialog-box function for the About box.
251 static int CALLBACK
AboutProc(HWND hwnd
, UINT msg
,
252 WPARAM wParam
, LPARAM lParam
)
259 { /* centre the window */
263 hw
= GetDesktopWindow();
264 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
266 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
267 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
268 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
271 SetDlgItemText(hwnd
, 100, ver
);
274 switch (LOWORD(wParam
)) {
279 EnableWindow(hwnd
, 0);
280 DialogBox(hinst
, MAKEINTRESOURCE(214), NULL
, LicenceProc
);
281 EnableWindow(hwnd
, 1);
282 SetActiveWindow(hwnd
);
294 * Thread to generate a key.
296 struct rsa_key_thread_params
{
297 HWND progressbar
; /* notify this with progress */
298 HWND dialog
; /* notify this on completion */
299 int keysize
; /* bits in key */
302 struct dss_key
*dsskey
;
304 static DWORD WINAPI
generate_rsa_key_thread(void *param
)
306 struct rsa_key_thread_params
*params
=
307 (struct rsa_key_thread_params
*) param
;
308 struct progress prog
;
309 prog
.progbar
= params
->progressbar
;
311 progress_update(&prog
, PROGFN_INITIALISE
, 0, 0);
314 dsa_generate(params
->dsskey
, params
->keysize
, progress_update
, &prog
);
316 rsa_generate(params
->key
, params
->keysize
, progress_update
, &prog
);
318 PostMessage(params
->dialog
, WM_DONEKEY
, 0, 0);
324 struct MainDlgState
{
325 int collecting_entropy
;
326 int generation_thread_exists
;
328 int entropy_got
, entropy_required
, entropy_size
;
331 char **commentptr
; /* points to key.comment or ssh2key.comment */
332 struct ssh2_userkey ssh2key
;
335 struct dss_key dsskey
;
336 HMENU filemenu
, keymenu
, cvtmenu
;
339 static void hidemany(HWND hwnd
, const int *ids
, int hideit
)
342 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
346 static void setupbigedit1(HWND hwnd
, int id
, int idstatic
, struct RSAKey
*key
)
351 dec1
= bignum_decimal(key
->exponent
);
352 dec2
= bignum_decimal(key
->modulus
);
353 buffer
= smalloc(strlen(dec1
) + strlen(dec2
) +
354 strlen(key
->comment
) + 30);
355 sprintf(buffer
, "%d %s %s %s",
356 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
357 SetDlgItemText(hwnd
, id
, buffer
);
358 SetDlgItemText(hwnd
, idstatic
,
359 "&Public key for pasting into authorized_keys file:");
365 static void setupbigedit2(HWND hwnd
, int id
, int idstatic
,
366 struct ssh2_userkey
*key
)
368 unsigned char *pub_blob
;
373 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
374 buffer
= smalloc(strlen(key
->alg
->name
) + 4 * ((pub_len
+ 2) / 3) +
375 strlen(key
->comment
) + 3);
376 strcpy(buffer
, key
->alg
->name
);
377 p
= buffer
+ strlen(buffer
);
380 while (i
< pub_len
) {
381 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
382 base64_encode_atom(pub_blob
+ i
, n
, p
);
387 strcpy(p
, key
->comment
);
388 SetDlgItemText(hwnd
, id
, buffer
);
389 SetDlgItemText(hwnd
, idstatic
, "&Public key for pasting into "
390 "OpenSSH authorized_keys2 file:");
395 static int save_ssh1_pubkey(char *filename
, struct RSAKey
*key
)
400 dec1
= bignum_decimal(key
->exponent
);
401 dec2
= bignum_decimal(key
->modulus
);
402 fp
= fopen(filename
, "wb");
405 fprintf(fp
, "%d %s %s %s\n",
406 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
414 * Warn about the obsolescent key file format.
416 void old_keyfile_warning(void)
418 static const char mbtitle
[] = "PuTTY Key File Warning";
419 static const char message
[] =
420 "You are loading an SSH 2 private key which has an\n"
421 "old version of the file format. This means your key\n"
422 "file is not fully tamperproof. Future versions of\n"
423 "PuTTY may stop supporting this private key format,\n"
424 "so we recommend you convert your key to the new\n"
427 "Once the key is loaded into PuTTYgen, you can perform\n"
428 "this conversion simply by saving it again.";
430 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
433 static int save_ssh2_pubkey(char *filename
, struct ssh2_userkey
*key
)
435 unsigned char *pub_blob
;
441 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
443 fp
= fopen(filename
, "wb");
447 fprintf(fp
, "---- BEGIN SSH2 PUBLIC KEY ----\n");
449 fprintf(fp
, "Comment: \"");
450 for (p
= key
->comment
; *p
; p
++) {
451 if (*p
== '\\' || *p
== '\"')
459 while (i
< pub_len
) {
461 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
462 base64_encode_atom(pub_blob
+ i
, n
, buf
);
466 if (++column
>= 16) {
474 fprintf(fp
, "---- END SSH2 PUBLIC KEY ----\n");
481 controlidstart
= 100,
488 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
489 IDC_FPSTATIC
, IDC_FINGERPRINT
,
490 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
491 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
492 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
494 IDC_GENSTATIC
, IDC_GENERATE
,
495 IDC_LOADSTATIC
, IDC_LOAD
,
496 IDC_SAVESTATIC
, IDC_SAVE
, IDC_SAVEPUB
,
498 IDC_TYPESTATIC
, IDC_KEYSSH1
, IDC_KEYSSH2RSA
, IDC_KEYSSH2DSA
,
499 IDC_BITSSTATIC
, IDC_BITS
,
502 IDC_IMPORT
, IDC_EXPORT_OPENSSH
, IDC_EXPORT_SSHCOM
505 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
506 static const int generating_ids
[] = { IDC_GENERATING
, IDC_PROGRESS
, 0 };
507 static const int gotkey_ids
[] = {
508 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
509 IDC_FPSTATIC
, IDC_FINGERPRINT
,
510 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
511 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
512 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
516 * Small UI helper function to switch the state of the main dialog
517 * by enabling and disabling controls and menu items.
519 void ui_set_state(HWND hwnd
, struct MainDlgState
*state
, int status
)
525 hidemany(hwnd
, nokey_ids
, FALSE
);
526 hidemany(hwnd
, generating_ids
, TRUE
);
527 hidemany(hwnd
, gotkey_ids
, TRUE
);
528 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
529 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
530 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
531 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
532 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
533 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
534 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
535 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
536 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
537 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
538 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
539 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
540 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
541 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_ENABLED
|MF_BYCOMMAND
);
542 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_ENABLED
|MF_BYCOMMAND
);
543 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
544 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
545 MF_GRAYED
|MF_BYCOMMAND
);
546 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
547 MF_GRAYED
|MF_BYCOMMAND
);
549 case 1: /* generating key */
550 hidemany(hwnd
, nokey_ids
, TRUE
);
551 hidemany(hwnd
, generating_ids
, FALSE
);
552 hidemany(hwnd
, gotkey_ids
, TRUE
);
553 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
554 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
555 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
556 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
557 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
558 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
559 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
560 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
561 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_GRAYED
|MF_BYCOMMAND
);
562 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
563 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
564 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_GRAYED
|MF_BYCOMMAND
);
565 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_GRAYED
|MF_BYCOMMAND
);
566 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_GRAYED
|MF_BYCOMMAND
);
567 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_GRAYED
|MF_BYCOMMAND
);
568 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_GRAYED
|MF_BYCOMMAND
);
569 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
570 MF_GRAYED
|MF_BYCOMMAND
);
571 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
572 MF_GRAYED
|MF_BYCOMMAND
);
575 hidemany(hwnd
, nokey_ids
, TRUE
);
576 hidemany(hwnd
, generating_ids
, TRUE
);
577 hidemany(hwnd
, gotkey_ids
, FALSE
);
578 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
579 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
580 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
581 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
582 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
583 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
584 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
585 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
586 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
587 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_ENABLED
|MF_BYCOMMAND
);
588 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_ENABLED
|MF_BYCOMMAND
);
589 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
590 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
591 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
,MF_ENABLED
|MF_BYCOMMAND
);
592 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
,MF_ENABLED
|MF_BYCOMMAND
);
593 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
595 * Enable export menu items if and only if the key type
596 * supports this kind of export.
598 type
= state
->ssh2 ? SSH_KEYTYPE_SSH2
: SSH_KEYTYPE_SSH1
;
599 #define do_export_menuitem(x,y) \
600 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
601 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
602 do_export_menuitem(IDC_EXPORT_OPENSSH
, SSH_KEYTYPE_OPENSSH
);
603 do_export_menuitem(IDC_EXPORT_SSHCOM
, SSH_KEYTYPE_SSHCOM
);
604 #undef do_export_menuitem
610 * Dialog-box function for the main PuTTYgen dialog box.
612 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
613 WPARAM wParam
, LPARAM lParam
)
615 static const char generating_msg
[] =
616 "Please wait while a key is generated...";
617 static const char entropy_msg
[] =
618 "Please generate some randomness by moving the mouse over the blank area.";
619 struct MainDlgState
*state
;
624 SetWindowLong(hwnd
, GWL_EXSTYLE
,
625 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
628 * If we add a Help button, this is where we destroy it
629 * if the help file isn't present.
632 requested_help
= FALSE
;
634 state
= smalloc(sizeof(*state
));
635 state
->generation_thread_exists
= FALSE
;
636 state
->collecting_entropy
= FALSE
;
637 state
->entropy
= NULL
;
638 state
->key_exists
= FALSE
;
639 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
645 menu1
= CreateMenu();
646 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
647 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
648 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
649 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
650 AppendMenu(menu1
, MF_ENABLED
, IDC_QUIT
, "E&xit");
651 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
652 state
->filemenu
= menu1
;
654 menu1
= CreateMenu();
655 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
656 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
657 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH1
, "SSH&1 key (RSA)");
658 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2RSA
, "SSH2 &RSA key");
659 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2DSA
, "SSH2 &DSA key");
660 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Key");
661 state
->keymenu
= menu1
;
663 menu1
= CreateMenu();
664 AppendMenu(menu1
, MF_ENABLED
, IDC_IMPORT
, "&Import key");
665 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
666 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
667 "Export &OpenSSH key");
668 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
669 "Export &ssh.com key");
670 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
672 state
->cvtmenu
= menu1
;
674 menu1
= CreateMenu();
675 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
677 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
678 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
686 { /* centre the window */
690 hw
= GetDesktopWindow();
691 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
693 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
694 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
695 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
699 struct ctlpos cp
, cp2
;
701 /* Accelerators used: acglops1rbd */
703 ctlposinit(&cp
, hwnd
, 4, 4, 4);
704 beginbox(&cp
, "Key", IDC_BOX_KEY
);
706 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
708 statictext(&cp2
, "", 1, IDC_GENERATING
);
709 progressbar(&cp2
, IDC_PROGRESS
);
711 "&Public key for pasting into authorized_keys file:",
712 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
713 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
714 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
715 IDC_FINGERPRINT
, 75);
716 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
718 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
719 IDC_COMMENTEDIT
, 75);
720 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
721 IDC_PASSPHRASE1EDIT
, 75);
722 staticpassedit(&cp
, "C&onfirm passphrase:",
723 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
725 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
726 staticbtn(&cp
, "Generate a public/private key pair",
727 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
728 staticbtn(&cp
, "Load an existing private key file",
729 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
730 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
731 "Save p&ublic key", IDC_SAVEPUB
,
732 "&Save private key", IDC_SAVE
);
734 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
735 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
736 "SSH&1 (RSA)", IDC_KEYSSH1
,
737 "SSH2 &RSA", IDC_KEYSSH2RSA
,
738 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
739 staticedit(&cp
, "Number of &bits in a generated key:",
740 IDC_BITSSTATIC
, IDC_BITS
, 20);
743 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
744 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
745 IDC_KEYSSH1
, MF_BYCOMMAND
);
746 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
749 * Initially, hide the progress bar and the key display,
750 * and show the no-key display. Also disable the Save
751 * buttons, because with no key we obviously can't save
754 ui_set_state(hwnd
, state
, 0);
758 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
759 if (state
->collecting_entropy
&&
760 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
761 state
->entropy
[state
->entropy_got
++] = lParam
;
762 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
763 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
764 state
->entropy_got
, 0);
765 if (state
->entropy_got
>= state
->entropy_required
) {
766 struct rsa_key_thread_params
*params
;
770 * Seed the entropy pool
772 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
773 memset(state
->entropy
, 0, state
->entropy_size
);
774 sfree(state
->entropy
);
775 state
->collecting_entropy
= FALSE
;
777 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
778 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
779 MAKELPARAM(0, PROGRESSRANGE
));
780 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
782 params
= smalloc(sizeof(*params
));
783 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
784 params
->dialog
= hwnd
;
785 params
->keysize
= state
->keysize
;
786 params
->is_dsa
= state
->is_dsa
;
787 params
->key
= &state
->key
;
788 params
->dsskey
= &state
->dsskey
;
790 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
791 params
, 0, &threadid
)) {
792 MessageBox(hwnd
, "Out of thread resources",
793 "Key generation error",
794 MB_OK
| MB_ICONERROR
);
797 state
->generation_thread_exists
= TRUE
;
803 switch (LOWORD(wParam
)) {
808 state
= (struct MainDlgState
*)
809 GetWindowLong(hwnd
, GWL_USERDATA
);
810 if (!IsDlgButtonChecked(hwnd
, LOWORD(wParam
)))
811 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
813 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
814 LOWORD(wParam
), MF_BYCOMMAND
);
818 PostMessage(hwnd
, WM_CLOSE
, 0, 0);
820 case IDC_COMMENTEDIT
:
821 if (HIWORD(wParam
) == EN_CHANGE
) {
822 state
= (struct MainDlgState
*)
823 GetWindowLong(hwnd
, GWL_USERDATA
);
824 if (state
->key_exists
) {
825 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
826 int len
= GetWindowTextLength(editctl
);
827 if (*state
->commentptr
)
828 sfree(*state
->commentptr
);
829 *state
->commentptr
= smalloc(len
+ 1);
830 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
832 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
835 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
842 EnableWindow(hwnd
, 0);
843 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
844 EnableWindow(hwnd
, 1);
845 SetActiveWindow(hwnd
);
848 if (HIWORD(wParam
) == BN_CLICKED
||
849 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
851 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
852 (DWORD
)"JI(`',`puttygen.general')");
853 requested_help
= TRUE
;
859 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
860 if (!state
->generation_thread_exists
) {
862 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
864 state
->keysize
= DEFAULT_KEYSIZE
;
865 /* If we ever introduce a new key type, check it here! */
866 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
867 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
868 if (state
->keysize
< 256) {
869 int ret
= MessageBox(hwnd
,
870 "PuTTYgen will not generate a key"
871 " smaller than 256 bits.\n"
872 "Key length reset to 256. Continue?",
874 MB_ICONWARNING
| MB_OKCANCEL
);
877 state
->keysize
= 256;
878 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
880 ui_set_state(hwnd
, state
, 1);
881 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
882 state
->key_exists
= FALSE
;
883 state
->collecting_entropy
= TRUE
;
886 * My brief statistical tests on mouse movements
887 * suggest that there are about 2.5 bits of
888 * randomness in the x position, 2.5 in the y
889 * position, and 1.7 in the message time, making
890 * 5.7 bits of unpredictability per mouse movement.
891 * However, other people have told me it's far less
892 * than that, so I'm going to be stupidly cautious
893 * and knock that down to a nice round 2. With this
894 * method, we require two words per mouse movement,
895 * so with 2 bits per mouse movement we expect 2
896 * bits every 2 words.
898 state
->entropy_required
= (state
->keysize
/ 2) * 2;
899 state
->entropy_got
= 0;
900 state
->entropy_size
= (state
->entropy_required
*
901 sizeof(*state
->entropy
));
902 state
->entropy
= smalloc(state
->entropy_size
);
904 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
905 MAKELPARAM(0, state
->entropy_required
));
906 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
910 case IDC_EXPORT_OPENSSH
:
911 case IDC_EXPORT_SSHCOM
:
913 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
914 if (state
->key_exists
) {
915 char filename
[FILENAME_MAX
];
916 char passphrase
[PASSPHRASE_MAXLEN
];
917 char passphrase2
[PASSPHRASE_MAXLEN
];
921 realtype
= SSH_KEYTYPE_SSH2
;
923 realtype
= SSH_KEYTYPE_SSH1
;
925 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
926 type
= SSH_KEYTYPE_OPENSSH
;
927 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
928 type
= SSH_KEYTYPE_SSHCOM
;
932 if (type
!= realtype
&&
933 import_target_type(type
) != realtype
) {
935 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
936 " format", (state
->ssh2 ?
2 : 1),
937 (state
->ssh2 ?
1 : 2));
938 MessageBox(hwnd
, msg
,
939 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
943 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
944 passphrase
, sizeof(passphrase
));
945 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
946 passphrase2
, sizeof(passphrase2
));
947 if (strcmp(passphrase
, passphrase2
)) {
949 "The two passphrases given do not match.",
950 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
955 ret
= MessageBox(hwnd
,
956 "Are you sure you want to save this key\n"
957 "without a passphrase to protect it?",
959 MB_YESNO
| MB_ICONWARNING
);
963 if (prompt_keyfile(hwnd
, "Save private key as:",
966 FILE *fp
= fopen(filename
, "r");
968 char buffer
[FILENAME_MAX
+ 80];
970 sprintf(buffer
, "Overwrite existing file\n%.*s?",
971 FILENAME_MAX
, filename
);
972 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
973 MB_YESNO
| MB_ICONWARNING
);
979 if (type
!= realtype
)
980 ret
= export_ssh2(filename
, type
, &state
->ssh2key
,
981 *passphrase ? passphrase
: NULL
);
983 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
984 *passphrase ? passphrase
:
987 if (type
!= realtype
)
988 ret
= export_ssh1(filename
, type
, &state
->key
,
989 *passphrase ? passphrase
: NULL
);
991 ret
= saversakey(filename
, &state
->key
,
992 *passphrase ? passphrase
: NULL
);
995 MessageBox(hwnd
, "Unable to save key file",
996 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1003 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1004 if (state
->key_exists
) {
1005 char filename
[FILENAME_MAX
];
1006 if (prompt_keyfile(hwnd
, "Save public key as:",
1009 FILE *fp
= fopen(filename
, "r");
1011 char buffer
[FILENAME_MAX
+ 80];
1013 sprintf(buffer
, "Overwrite existing file\n%.*s?",
1014 FILENAME_MAX
, filename
);
1015 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1016 MB_YESNO
| MB_ICONWARNING
);
1021 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
1023 ret
= save_ssh1_pubkey(filename
, &state
->key
);
1026 MessageBox(hwnd
, "Unable to save key file",
1027 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1035 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1036 if (!state
->generation_thread_exists
) {
1037 char filename
[FILENAME_MAX
];
1038 if (prompt_keyfile(hwnd
, "Load private key:", filename
, 0)) {
1039 char passphrase
[PASSPHRASE_MAXLEN
];
1044 struct PassphraseProcStruct pps
;
1045 struct RSAKey newkey1
;
1046 struct ssh2_userkey
*newkey2
= NULL
;
1048 type
= realtype
= key_type(filename
);
1049 if (type
!= SSH_KEYTYPE_SSH1
&&
1050 type
!= SSH_KEYTYPE_SSH2
&&
1051 !import_possible(type
)) {
1053 sprintf(msg
, "Couldn't load private key (%s)",
1054 key_type_to_str(type
));
1055 MessageBox(NULL
, msg
,
1056 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1060 if (type
!= SSH_KEYTYPE_SSH1
&&
1061 type
!= SSH_KEYTYPE_SSH2
) {
1063 type
= import_target_type(type
);
1067 if (realtype
== SSH_KEYTYPE_SSH1
)
1068 needs_pass
= rsakey_encrypted(filename
, &comment
);
1069 else if (realtype
== SSH_KEYTYPE_SSH2
)
1071 ssh2_userkey_encrypted(filename
, &comment
);
1073 needs_pass
= import_encrypted(filename
, realtype
,
1075 pps
.passphrase
= passphrase
;
1076 pps
.comment
= comment
;
1080 dlgret
= DialogBoxParam(hinst
,
1081 MAKEINTRESOURCE(210),
1082 NULL
, PassphraseProc
,
1090 if (type
== SSH_KEYTYPE_SSH1
) {
1091 if (realtype
== type
)
1092 ret
= loadrsakey(filename
, &newkey1
,
1095 ret
= import_ssh1(filename
, realtype
,
1096 &newkey1
, passphrase
);
1098 if (realtype
== type
)
1099 newkey2
= ssh2_load_userkey(filename
,
1102 newkey2
= import_ssh2(filename
, realtype
,
1104 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
1111 } while (ret
== -1);
1115 MessageBox(NULL
, "Couldn't load private key.",
1116 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1117 } else if (ret
== 1) {
1119 * Now update the key controls with all the
1123 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1125 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1127 if (type
== SSH_KEYTYPE_SSH1
) {
1131 state
->ssh2
= FALSE
;
1132 state
->commentptr
= &state
->key
.comment
;
1133 state
->key
= newkey1
;
1136 * Set the key fingerprint.
1138 savecomment
= state
->key
.comment
;
1139 state
->key
.comment
= NULL
;
1140 rsa_fingerprint(buf
, sizeof(buf
),
1142 state
->key
.comment
= savecomment
;
1144 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1146 * Construct a decimal representation
1147 * of the key, for pasting into
1148 * .ssh/authorized_keys on a Unix box.
1150 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1151 IDC_PKSTATIC
, &state
->key
);
1158 &state
->ssh2key
.comment
;
1159 state
->ssh2key
= *newkey2
; /* structure copy */
1162 savecomment
= state
->ssh2key
.comment
;
1163 state
->ssh2key
.comment
= NULL
;
1165 state
->ssh2key
.alg
->
1166 fingerprint(state
->ssh2key
.data
);
1167 state
->ssh2key
.comment
= savecomment
;
1169 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1172 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1173 IDC_PKSTATIC
, &state
->ssh2key
);
1175 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
1176 *state
->commentptr
);
1179 * Finally, hide the progress bar and show
1182 ui_set_state(hwnd
, state
, 2);
1183 state
->key_exists
= TRUE
;
1186 * If the user has imported a foreign key
1187 * using the Load command, let them know.
1188 * If they've used the Import command, be
1191 if (realtype
!= type
&& LOWORD(wParam
) == IDC_LOAD
) {
1193 sprintf(msg
, "Successfully imported foreign key\n"
1195 "To use this key with PuTTY, you need to\n"
1196 "use the \"Save private key\" command to\n"
1197 "save it in PuTTY's own format.",
1198 key_type_to_str(realtype
));
1199 MessageBox(NULL
, msg
, "PuTTYgen Notice",
1200 MB_OK
| MB_ICONINFORMATION
);
1209 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1210 state
->generation_thread_exists
= FALSE
;
1211 state
->key_exists
= TRUE
;
1212 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1213 MAKELPARAM(0, PROGRESSRANGE
));
1214 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1216 if (state
->is_dsa
) {
1217 state
->ssh2key
.data
= &state
->dsskey
;
1218 state
->ssh2key
.alg
= &ssh_dss
;
1220 state
->ssh2key
.data
= &state
->key
;
1221 state
->ssh2key
.alg
= &ssh_rsa
;
1223 state
->commentptr
= &state
->ssh2key
.comment
;
1225 state
->commentptr
= &state
->key
.comment
;
1228 * Invent a comment for the key. We'll do this by including
1229 * the date in it. This will be so horrifyingly ugly that
1230 * the user will immediately want to change it, which is
1233 *state
->commentptr
= smalloc(30);
1240 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1242 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1246 * Now update the key controls with all the key data.
1251 * Blank passphrase, initially. This isn't dangerous,
1252 * because we will warn (Are You Sure?) before allowing
1253 * the user to save an unprotected private key.
1255 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1256 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1260 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1262 * Set the key fingerprint.
1264 savecomment
= *state
->commentptr
;
1265 *state
->commentptr
= NULL
;
1268 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1269 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1273 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1274 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1276 *state
->commentptr
= savecomment
;
1278 * Construct a decimal representation of the key, for
1279 * pasting into .ssh/authorized_keys or
1280 * .ssh/authorized_keys2 on a Unix box.
1283 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1284 IDC_PKSTATIC
, &state
->ssh2key
);
1286 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1287 IDC_PKSTATIC
, &state
->key
);
1291 * Finally, hide the progress bar and show the key data.
1293 ui_set_state(hwnd
, state
, 2);
1297 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1300 case IDC_GENERATING
:
1304 cmd
= "JI(`',`puttygen.generate')"; break;
1306 case IDC_KEYDISPLAY
:
1307 cmd
= "JI(`',`puttygen.pastekey')"; break;
1309 case IDC_FINGERPRINT
:
1310 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1311 case IDC_COMMENTSTATIC
:
1312 case IDC_COMMENTEDIT
:
1313 cmd
= "JI(`',`puttygen.comment')"; break;
1314 case IDC_PASSPHRASE1STATIC
:
1315 case IDC_PASSPHRASE1EDIT
:
1316 case IDC_PASSPHRASE2STATIC
:
1317 case IDC_PASSPHRASE2EDIT
:
1318 cmd
= "JI(`',`puttygen.passphrase')"; break;
1319 case IDC_LOADSTATIC
:
1321 cmd
= "JI(`',`puttygen.load')"; break;
1322 case IDC_SAVESTATIC
:
1324 cmd
= "JI(`',`puttygen.savepriv')"; break;
1326 cmd
= "JI(`',`puttygen.savepub')"; break;
1327 case IDC_TYPESTATIC
:
1329 case IDC_KEYSSH2RSA
:
1330 case IDC_KEYSSH2DSA
:
1331 cmd
= "JI(`',`puttygen.keytype')"; break;
1332 case IDC_BITSSTATIC
:
1334 cmd
= "JI(`',`puttygen.bits')"; break;
1336 case IDC_EXPORT_OPENSSH
:
1337 case IDC_EXPORT_SSHCOM
:
1338 cmd
= "JI(`',`puttygen.conversions')"; break;
1341 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1342 requested_help
= TRUE
;
1349 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1351 if (requested_help
) {
1352 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1353 requested_help
= FALSE
;
1361 void cleanup_exit(int code
) { exit(code
); }
1363 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1365 InitCommonControls();
1369 * See if we can find our Help file.
1372 char b
[2048], *p
, *q
, *r
;
1374 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1376 p
= strrchr(b
, '\\');
1377 if (p
&& p
>= r
) r
= p
+1;
1378 q
= strrchr(b
, ':');
1379 if (q
&& q
>= r
) r
= q
+1;
1380 strcpy(r
, "putty.hlp");
1381 if ( (fp
= fopen(b
, "r")) != NULL
) {
1382 help_path
= dupstr(b
);
1389 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1390 MainDlgProc
) != IDOK
;