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
;
335 HMENU filemenu
, keymenu
, exportmenu
;
338 static void hidemany(HWND hwnd
, const int *ids
, int hideit
)
341 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
345 static void setupbigedit1(HWND hwnd
, int id
, int idstatic
, struct RSAKey
*key
)
350 dec1
= bignum_decimal(key
->exponent
);
351 dec2
= bignum_decimal(key
->modulus
);
352 buffer
= smalloc(strlen(dec1
) + strlen(dec2
) +
353 strlen(key
->comment
) + 30);
354 sprintf(buffer
, "%d %s %s %s",
355 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
356 SetDlgItemText(hwnd
, id
, buffer
);
357 SetDlgItemText(hwnd
, idstatic
,
358 "&Public key for pasting into authorized_keys file:");
364 static void setupbigedit2(HWND hwnd
, int id
, int idstatic
,
365 struct ssh2_userkey
*key
)
367 unsigned char *pub_blob
;
372 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
373 buffer
= smalloc(strlen(key
->alg
->name
) + 4 * ((pub_len
+ 2) / 3) +
374 strlen(key
->comment
) + 3);
375 strcpy(buffer
, key
->alg
->name
);
376 p
= buffer
+ strlen(buffer
);
379 while (i
< pub_len
) {
380 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
381 base64_encode_atom(pub_blob
+ i
, n
, p
);
386 strcpy(p
, key
->comment
);
387 SetDlgItemText(hwnd
, id
, buffer
);
388 SetDlgItemText(hwnd
, idstatic
, "&Public key for pasting into "
389 "OpenSSH authorized_keys2 file:");
394 static int save_ssh1_pubkey(char *filename
, struct RSAKey
*key
)
399 dec1
= bignum_decimal(key
->exponent
);
400 dec2
= bignum_decimal(key
->modulus
);
401 fp
= fopen(filename
, "wb");
404 fprintf(fp
, "%d %s %s %s\n",
405 bignum_bitcount(key
->modulus
), dec1
, dec2
, key
->comment
);
413 * Warn about the obsolescent key file format.
415 void old_keyfile_warning(void)
417 static const char mbtitle
[] = "PuTTY Key File Warning";
418 static const char message
[] =
419 "You are loading an SSH 2 private key which has an\n"
420 "old version of the file format. This means your key\n"
421 "file is not fully tamperproof. Future versions of\n"
422 "PuTTY may stop supporting this private key format,\n"
423 "so we recommend you convert your key to the new\n"
426 "Once the key is loaded into PuTTYgen, you can perform\n"
427 "this conversion simply by saving it again.";
429 MessageBox(NULL
, message
, mbtitle
, MB_OK
);
432 static int save_ssh2_pubkey(char *filename
, struct ssh2_userkey
*key
)
434 unsigned char *pub_blob
;
440 pub_blob
= key
->alg
->public_blob(key
->data
, &pub_len
);
442 fp
= fopen(filename
, "wb");
446 fprintf(fp
, "---- BEGIN SSH2 PUBLIC KEY ----\n");
448 fprintf(fp
, "Comment: \"");
449 for (p
= key
->comment
; *p
; p
++) {
450 if (*p
== '\\' || *p
== '\"')
458 while (i
< pub_len
) {
460 int n
= (pub_len
- i
< 3 ? pub_len
- i
: 3);
461 base64_encode_atom(pub_blob
+ i
, n
, buf
);
465 if (++column
>= 16) {
473 fprintf(fp
, "---- END SSH2 PUBLIC KEY ----\n");
480 controlidstart
= 100,
487 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
488 IDC_FPSTATIC
, IDC_FINGERPRINT
,
489 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
490 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
491 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
493 IDC_GENSTATIC
, IDC_GENERATE
,
494 IDC_LOADSTATIC
, IDC_LOAD
,
495 IDC_SAVESTATIC
, IDC_SAVE
, IDC_SAVEPUB
,
497 IDC_TYPESTATIC
, IDC_KEYSSH1
, IDC_KEYSSH2RSA
, IDC_KEYSSH2DSA
,
498 IDC_BITSSTATIC
, IDC_BITS
,
501 IDC_IMPORT
, IDC_EXPORT_OPENSSH
, IDC_EXPORT_SSHCOM
504 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
505 static const int generating_ids
[] = { IDC_GENERATING
, IDC_PROGRESS
, 0 };
506 static const int gotkey_ids
[] = {
507 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
508 IDC_FPSTATIC
, IDC_FINGERPRINT
,
509 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
510 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
511 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0
515 * Small UI helper function to switch the state of the main dialog
516 * by enabling and disabling controls and menu items.
518 void ui_set_state(HWND hwnd
, struct MainDlgState
*state
, int status
)
524 hidemany(hwnd
, nokey_ids
, FALSE
);
525 hidemany(hwnd
, generating_ids
, TRUE
);
526 hidemany(hwnd
, gotkey_ids
, TRUE
);
527 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
528 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
529 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
530 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
531 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
532 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
533 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
534 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
535 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
536 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
537 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
538 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
539 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
540 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_ENABLED
|MF_BYCOMMAND
);
541 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_ENABLED
|MF_BYCOMMAND
);
542 EnableMenuItem(state
->exportmenu
, IDC_EXPORT_OPENSSH
,
543 MF_GRAYED
|MF_BYCOMMAND
);
544 EnableMenuItem(state
->exportmenu
, IDC_EXPORT_SSHCOM
,
545 MF_GRAYED
|MF_BYCOMMAND
);
547 case 1: /* generating key */
548 hidemany(hwnd
, nokey_ids
, TRUE
);
549 hidemany(hwnd
, generating_ids
, FALSE
);
550 hidemany(hwnd
, gotkey_ids
, TRUE
);
551 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
552 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
553 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
554 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
555 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
556 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
557 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
558 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
559 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_GRAYED
|MF_BYCOMMAND
);
560 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
561 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
562 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_GRAYED
|MF_BYCOMMAND
);
563 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_GRAYED
|MF_BYCOMMAND
);
564 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_GRAYED
|MF_BYCOMMAND
);
565 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_GRAYED
|MF_BYCOMMAND
);
566 EnableMenuItem(state
->exportmenu
, IDC_EXPORT_OPENSSH
,
567 MF_GRAYED
|MF_BYCOMMAND
);
568 EnableMenuItem(state
->exportmenu
, IDC_EXPORT_SSHCOM
,
569 MF_GRAYED
|MF_BYCOMMAND
);
572 hidemany(hwnd
, nokey_ids
, TRUE
);
573 hidemany(hwnd
, generating_ids
, TRUE
);
574 hidemany(hwnd
, gotkey_ids
, FALSE
);
575 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
576 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
577 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
578 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
579 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
580 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
581 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
582 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
583 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
584 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_ENABLED
|MF_BYCOMMAND
);
585 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_ENABLED
|MF_BYCOMMAND
);
586 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
587 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
588 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
,MF_ENABLED
|MF_BYCOMMAND
);
589 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
,MF_ENABLED
|MF_BYCOMMAND
);
591 * Enable export menu items if and only if the key type
592 * supports this kind of export.
594 type
= state
->ssh2 ? SSH_KEYTYPE_SSH2
: SSH_KEYTYPE_SSH1
;
595 #define do_export_menuitem(x,y) \
596 EnableMenuItem(state->exportmenu, x, MF_BYCOMMAND | \
597 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
598 do_export_menuitem(IDC_EXPORT_OPENSSH
, SSH_KEYTYPE_OPENSSH
);
599 do_export_menuitem(IDC_EXPORT_SSHCOM
, SSH_KEYTYPE_SSHCOM
);
600 #undef do_export_menuitem
606 * Dialog-box function for the main PuTTYgen dialog box.
608 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
609 WPARAM wParam
, LPARAM lParam
)
611 static const char generating_msg
[] =
612 "Please wait while a key is generated...";
613 static const char entropy_msg
[] =
614 "Please generate some randomness by moving the mouse over the blank area.";
615 struct MainDlgState
*state
;
620 SetWindowLong(hwnd
, GWL_EXSTYLE
,
621 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
624 * If we add a Help button, this is where we destroy it
625 * if the help file isn't present.
628 requested_help
= FALSE
;
630 state
= smalloc(sizeof(*state
));
631 state
->generation_thread_exists
= FALSE
;
632 state
->collecting_entropy
= FALSE
;
633 state
->entropy
= NULL
;
634 state
->key_exists
= FALSE
;
635 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
641 menu1
= CreateMenu();
642 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
643 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
644 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
645 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
646 AppendMenu(menu1
, MF_ENABLED
, IDC_QUIT
, "E&xit");
647 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
648 state
->filemenu
= menu1
;
650 menu1
= CreateMenu();
651 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
652 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
653 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH1
, "SSH&1 key (RSA)");
654 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2RSA
, "SSH2 &RSA key");
655 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2DSA
, "SSH2 &DSA key");
656 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Key");
657 state
->keymenu
= menu1
;
659 menu1
= CreateMenu();
660 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
661 "Export &OpenSSH key");
662 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
663 "Export &ssh.com key");
664 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
666 state
->exportmenu
= menu1
;
668 menu1
= CreateMenu();
669 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
671 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
672 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
680 { /* centre the window */
684 hw
= GetDesktopWindow();
685 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
687 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
688 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
689 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
693 struct ctlpos cp
, cp2
;
695 /* Accelerators used: acglops1rbd */
697 ctlposinit(&cp
, hwnd
, 4, 4, 4);
698 beginbox(&cp
, "Key", IDC_BOX_KEY
);
700 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
702 statictext(&cp2
, "", 1, IDC_GENERATING
);
703 progressbar(&cp2
, IDC_PROGRESS
);
705 "&Public key for pasting into authorized_keys file:",
706 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
707 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
708 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
709 IDC_FINGERPRINT
, 75);
710 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
712 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
713 IDC_COMMENTEDIT
, 75);
714 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
715 IDC_PASSPHRASE1EDIT
, 75);
716 staticpassedit(&cp
, "C&onfirm passphrase:",
717 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
719 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
720 staticbtn(&cp
, "Generate a public/private key pair",
721 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
722 staticbtn(&cp
, "Load an existing private key file",
723 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
724 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
725 "Save p&ublic key", IDC_SAVEPUB
,
726 "&Save private key", IDC_SAVE
);
728 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
729 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
730 "SSH&1 (RSA)", IDC_KEYSSH1
,
731 "SSH2 &RSA", IDC_KEYSSH2RSA
,
732 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
733 staticedit(&cp
, "Number of &bits in a generated key:",
734 IDC_BITSSTATIC
, IDC_BITS
, 20);
737 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
738 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
739 IDC_KEYSSH1
, MF_BYCOMMAND
);
740 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
743 * Initially, hide the progress bar and the key display,
744 * and show the no-key display. Also disable the Save
745 * buttons, because with no key we obviously can't save
748 ui_set_state(hwnd
, state
, 0);
752 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
753 if (state
->collecting_entropy
&&
754 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
755 state
->entropy
[state
->entropy_got
++] = lParam
;
756 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
757 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
758 state
->entropy_got
, 0);
759 if (state
->entropy_got
>= state
->entropy_required
) {
760 struct rsa_key_thread_params
*params
;
764 * Seed the entropy pool
766 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
767 memset(state
->entropy
, 0, state
->entropy_size
);
768 sfree(state
->entropy
);
769 state
->collecting_entropy
= FALSE
;
771 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
772 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
773 MAKELPARAM(0, PROGRESSRANGE
));
774 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
776 params
= smalloc(sizeof(*params
));
777 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
778 params
->dialog
= hwnd
;
779 params
->keysize
= state
->keysize
;
780 params
->is_dsa
= state
->is_dsa
;
781 params
->key
= &state
->key
;
782 params
->dsskey
= &state
->dsskey
;
784 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
785 params
, 0, &threadid
)) {
786 MessageBox(hwnd
, "Out of thread resources",
787 "Key generation error",
788 MB_OK
| MB_ICONERROR
);
791 state
->generation_thread_exists
= TRUE
;
797 switch (LOWORD(wParam
)) {
802 state
= (struct MainDlgState
*)
803 GetWindowLong(hwnd
, GWL_USERDATA
);
804 if (!IsDlgButtonChecked(hwnd
, LOWORD(wParam
)))
805 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
807 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
808 LOWORD(wParam
), MF_BYCOMMAND
);
812 PostMessage(hwnd
, WM_CLOSE
, 0, 0);
814 case IDC_COMMENTEDIT
:
815 if (HIWORD(wParam
) == EN_CHANGE
) {
816 state
= (struct MainDlgState
*)
817 GetWindowLong(hwnd
, GWL_USERDATA
);
818 if (state
->key_exists
) {
819 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
820 int len
= GetWindowTextLength(editctl
);
821 if (*state
->commentptr
)
822 sfree(*state
->commentptr
);
823 *state
->commentptr
= smalloc(len
+ 1);
824 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
826 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
829 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
836 EnableWindow(hwnd
, 0);
837 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
838 EnableWindow(hwnd
, 1);
839 SetActiveWindow(hwnd
);
842 if (HIWORD(wParam
) == BN_CLICKED
||
843 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
845 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
846 (DWORD
)"JI(`',`puttygen.general')");
847 requested_help
= TRUE
;
853 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
854 if (!state
->generation_thread_exists
) {
856 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
858 state
->keysize
= DEFAULT_KEYSIZE
;
859 /* If we ever introduce a new key type, check it here! */
860 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
861 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
862 if (state
->keysize
< 256) {
863 int ret
= MessageBox(hwnd
,
864 "PuTTYgen will not generate a key"
865 " smaller than 256 bits.\n"
866 "Key length reset to 256. Continue?",
868 MB_ICONWARNING
| MB_OKCANCEL
);
871 state
->keysize
= 256;
872 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
874 ui_set_state(hwnd
, state
, 1);
875 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
876 state
->key_exists
= FALSE
;
877 state
->collecting_entropy
= TRUE
;
880 * My brief statistical tests on mouse movements
881 * suggest that there are about 2.5 bits of
882 * randomness in the x position, 2.5 in the y
883 * position, and 1.7 in the message time, making
884 * 5.7 bits of unpredictability per mouse movement.
885 * However, other people have told me it's far less
886 * than that, so I'm going to be stupidly cautious
887 * and knock that down to a nice round 2. With this
888 * method, we require two words per mouse movement,
889 * so with 2 bits per mouse movement we expect 2
890 * bits every 2 words.
892 state
->entropy_required
= (state
->keysize
/ 2) * 2;
893 state
->entropy_got
= 0;
894 state
->entropy_size
= (state
->entropy_required
*
895 sizeof(*state
->entropy
));
896 state
->entropy
= smalloc(state
->entropy_size
);
898 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
899 MAKELPARAM(0, state
->entropy_required
));
900 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
904 case IDC_EXPORT_OPENSSH
:
905 case IDC_EXPORT_SSHCOM
:
907 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
908 if (state
->key_exists
) {
909 char filename
[FILENAME_MAX
];
910 char passphrase
[PASSPHRASE_MAXLEN
];
911 char passphrase2
[PASSPHRASE_MAXLEN
];
915 realtype
= SSH_KEYTYPE_SSH2
;
917 realtype
= SSH_KEYTYPE_SSH1
;
919 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
920 type
= SSH_KEYTYPE_OPENSSH
;
921 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
922 type
= SSH_KEYTYPE_SSHCOM
;
926 if (type
!= realtype
&&
927 import_target_type(type
) != realtype
) {
929 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
930 " format", (state
->ssh2 ?
2 : 1),
931 (state
->ssh2 ?
1 : 2));
932 MessageBox(hwnd
, msg
,
933 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
937 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
938 passphrase
, sizeof(passphrase
));
939 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
940 passphrase2
, sizeof(passphrase2
));
941 if (strcmp(passphrase
, passphrase2
)) {
943 "The two passphrases given do not match.",
944 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
949 ret
= MessageBox(hwnd
,
950 "Are you sure you want to save this key\n"
951 "without a passphrase to protect it?",
953 MB_YESNO
| MB_ICONWARNING
);
957 if (prompt_keyfile(hwnd
, "Save private key as:",
960 FILE *fp
= fopen(filename
, "r");
962 char buffer
[FILENAME_MAX
+ 80];
964 sprintf(buffer
, "Overwrite existing file\n%.*s?",
965 FILENAME_MAX
, filename
);
966 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
967 MB_YESNO
| MB_ICONWARNING
);
973 if (type
!= realtype
)
974 ret
= export_ssh2(filename
, type
, &state
->ssh2key
,
975 *passphrase ? passphrase
: NULL
);
977 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
978 *passphrase ? passphrase
:
981 if (type
!= realtype
)
982 ret
= export_ssh1(filename
, type
, &state
->key
,
983 *passphrase ? passphrase
: NULL
);
985 ret
= saversakey(filename
, &state
->key
,
986 *passphrase ? passphrase
: NULL
);
989 MessageBox(hwnd
, "Unable to save key file",
990 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
997 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
998 if (state
->key_exists
) {
999 char filename
[FILENAME_MAX
];
1000 if (prompt_keyfile(hwnd
, "Save public key as:",
1003 FILE *fp
= fopen(filename
, "r");
1005 char buffer
[FILENAME_MAX
+ 80];
1007 sprintf(buffer
, "Overwrite existing file\n%.*s?",
1008 FILENAME_MAX
, filename
);
1009 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1010 MB_YESNO
| MB_ICONWARNING
);
1015 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
1017 ret
= save_ssh1_pubkey(filename
, &state
->key
);
1020 MessageBox(hwnd
, "Unable to save key file",
1021 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1028 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1029 if (!state
->generation_thread_exists
) {
1030 char filename
[FILENAME_MAX
];
1031 if (prompt_keyfile(hwnd
, "Load private key:", filename
, 0)) {
1032 char passphrase
[PASSPHRASE_MAXLEN
];
1037 struct PassphraseProcStruct pps
;
1038 struct RSAKey newkey1
;
1039 struct ssh2_userkey
*newkey2
= NULL
;
1041 type
= realtype
= key_type(filename
);
1042 if (type
!= SSH_KEYTYPE_SSH1
&&
1043 type
!= SSH_KEYTYPE_SSH2
&&
1044 !import_possible(type
)) {
1046 sprintf(msg
, "Couldn't load private key (%s)",
1047 key_type_to_str(type
));
1048 MessageBox(NULL
, msg
,
1049 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1053 if (type
!= SSH_KEYTYPE_SSH1
&&
1054 type
!= SSH_KEYTYPE_SSH2
) {
1056 type
= import_target_type(type
);
1060 if (realtype
== SSH_KEYTYPE_SSH1
)
1061 needs_pass
= rsakey_encrypted(filename
, &comment
);
1062 else if (realtype
== SSH_KEYTYPE_SSH2
)
1064 ssh2_userkey_encrypted(filename
, &comment
);
1066 needs_pass
= import_encrypted(filename
, realtype
,
1068 pps
.passphrase
= passphrase
;
1069 pps
.comment
= comment
;
1073 dlgret
= DialogBoxParam(hinst
,
1074 MAKEINTRESOURCE(210),
1075 NULL
, PassphraseProc
,
1083 if (type
== SSH_KEYTYPE_SSH1
) {
1084 if (realtype
== type
)
1085 ret
= loadrsakey(filename
, &newkey1
,
1088 ret
= import_ssh1(filename
, realtype
,
1089 &newkey1
, passphrase
);
1091 if (realtype
== type
)
1092 newkey2
= ssh2_load_userkey(filename
,
1095 newkey2
= import_ssh2(filename
, realtype
,
1097 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
1104 } while (ret
== -1);
1108 MessageBox(NULL
, "Couldn't load private key.",
1109 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1110 } else if (ret
== 1) {
1112 * Now update the key controls with all the
1116 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1118 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1120 if (type
== SSH_KEYTYPE_SSH1
) {
1124 state
->ssh2
= FALSE
;
1125 state
->commentptr
= &state
->key
.comment
;
1126 state
->key
= newkey1
;
1129 * Set the key fingerprint.
1131 savecomment
= state
->key
.comment
;
1132 state
->key
.comment
= NULL
;
1133 rsa_fingerprint(buf
, sizeof(buf
),
1135 state
->key
.comment
= savecomment
;
1137 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1139 * Construct a decimal representation
1140 * of the key, for pasting into
1141 * .ssh/authorized_keys on a Unix box.
1143 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1144 IDC_PKSTATIC
, &state
->key
);
1151 &state
->ssh2key
.comment
;
1152 state
->ssh2key
= *newkey2
; /* structure copy */
1155 savecomment
= state
->ssh2key
.comment
;
1156 state
->ssh2key
.comment
= NULL
;
1158 state
->ssh2key
.alg
->
1159 fingerprint(state
->ssh2key
.data
);
1160 state
->ssh2key
.comment
= savecomment
;
1162 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1165 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1166 IDC_PKSTATIC
, &state
->ssh2key
);
1168 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
1169 *state
->commentptr
);
1172 * Finally, hide the progress bar and show
1175 ui_set_state(hwnd
, state
, 2);
1176 state
->key_exists
= TRUE
;
1184 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1185 state
->generation_thread_exists
= FALSE
;
1186 state
->key_exists
= TRUE
;
1187 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1188 MAKELPARAM(0, PROGRESSRANGE
));
1189 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1191 if (state
->is_dsa
) {
1192 state
->ssh2key
.data
= &state
->dsskey
;
1193 state
->ssh2key
.alg
= &ssh_dss
;
1195 state
->ssh2key
.data
= &state
->key
;
1196 state
->ssh2key
.alg
= &ssh_rsa
;
1198 state
->commentptr
= &state
->ssh2key
.comment
;
1200 state
->commentptr
= &state
->key
.comment
;
1203 * Invent a comment for the key. We'll do this by including
1204 * the date in it. This will be so horrifyingly ugly that
1205 * the user will immediately want to change it, which is
1208 *state
->commentptr
= smalloc(30);
1215 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1217 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1221 * Now update the key controls with all the key data.
1226 * Blank passphrase, initially. This isn't dangerous,
1227 * because we will warn (Are You Sure?) before allowing
1228 * the user to save an unprotected private key.
1230 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1231 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1235 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1237 * Set the key fingerprint.
1239 savecomment
= *state
->commentptr
;
1240 *state
->commentptr
= NULL
;
1243 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1244 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1248 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1249 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1251 *state
->commentptr
= savecomment
;
1253 * Construct a decimal representation of the key, for
1254 * pasting into .ssh/authorized_keys or
1255 * .ssh/authorized_keys2 on a Unix box.
1258 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1259 IDC_PKSTATIC
, &state
->ssh2key
);
1261 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1262 IDC_PKSTATIC
, &state
->key
);
1266 * Finally, hide the progress bar and show the key data.
1268 ui_set_state(hwnd
, state
, 2);
1272 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1275 case IDC_GENERATING
:
1279 cmd
= "JI(`',`puttygen.generate')"; break;
1281 case IDC_KEYDISPLAY
:
1282 cmd
= "JI(`',`puttygen.pastekey')"; break;
1284 case IDC_FINGERPRINT
:
1285 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1286 case IDC_COMMENTSTATIC
:
1287 case IDC_COMMENTEDIT
:
1288 cmd
= "JI(`',`puttygen.comment')"; break;
1289 case IDC_PASSPHRASE1STATIC
:
1290 case IDC_PASSPHRASE1EDIT
:
1291 case IDC_PASSPHRASE2STATIC
:
1292 case IDC_PASSPHRASE2EDIT
:
1293 cmd
= "JI(`',`puttygen.passphrase')"; break;
1294 case IDC_LOADSTATIC
:
1296 cmd
= "JI(`',`puttygen.load')"; break;
1297 case IDC_SAVESTATIC
:
1299 cmd
= "JI(`',`puttygen.savepriv')"; break;
1301 cmd
= "JI(`',`puttygen.savepub')"; break;
1302 case IDC_TYPESTATIC
:
1304 case IDC_KEYSSH2RSA
:
1305 case IDC_KEYSSH2DSA
:
1306 cmd
= "JI(`',`puttygen.keytype')"; break;
1307 case IDC_BITSSTATIC
:
1309 cmd
= "JI(`',`puttygen.bits')"; break;
1310 case IDC_EXPORT_OPENSSH
:
1311 case IDC_EXPORT_SSHCOM
:
1312 cmd
= "JI(`',`puttygen.export')"; break;
1315 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1316 requested_help
= TRUE
;
1323 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1325 if (requested_help
) {
1326 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1327 requested_help
= FALSE
;
1335 void cleanup_exit(int code
) { exit(code
); }
1337 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1339 InitCommonControls();
1343 * See if we can find our Help file.
1346 char b
[2048], *p
, *q
, *r
;
1348 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1350 p
= strrchr(b
, '\\');
1351 if (p
&& p
>= r
) r
= p
+1;
1352 q
= strrchr(b
, ':');
1353 if (q
&& q
>= r
) r
= q
+1;
1354 strcpy(r
, "putty.hlp");
1355 if ( (fp
= fopen(b
, "r")) != NULL
) {
1356 help_path
= dupstr(b
);
1363 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1364 MainDlgProc
) != IDOK
;