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
, cvtmenu
;
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
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
543 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
544 MF_GRAYED
|MF_BYCOMMAND
);
545 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
546 MF_GRAYED
|MF_BYCOMMAND
);
548 case 1: /* generating key */
549 hidemany(hwnd
, nokey_ids
, TRUE
);
550 hidemany(hwnd
, generating_ids
, FALSE
);
551 hidemany(hwnd
, gotkey_ids
, TRUE
);
552 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
553 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
554 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
555 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 0);
556 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 0);
557 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 0);
558 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 0);
559 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 0);
560 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_GRAYED
|MF_BYCOMMAND
);
561 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_GRAYED
|MF_BYCOMMAND
);
562 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_GRAYED
|MF_BYCOMMAND
);
563 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_GRAYED
|MF_BYCOMMAND
);
564 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_GRAYED
|MF_BYCOMMAND
);
565 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
, MF_GRAYED
|MF_BYCOMMAND
);
566 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
, MF_GRAYED
|MF_BYCOMMAND
);
567 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_GRAYED
|MF_BYCOMMAND
);
568 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_OPENSSH
,
569 MF_GRAYED
|MF_BYCOMMAND
);
570 EnableMenuItem(state
->cvtmenu
, IDC_EXPORT_SSHCOM
,
571 MF_GRAYED
|MF_BYCOMMAND
);
574 hidemany(hwnd
, nokey_ids
, TRUE
);
575 hidemany(hwnd
, generating_ids
, TRUE
);
576 hidemany(hwnd
, gotkey_ids
, FALSE
);
577 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
578 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
579 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
580 EnableWindow(GetDlgItem(hwnd
, IDC_SAVEPUB
), 1);
581 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH1
), 1);
582 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2RSA
), 1);
583 EnableWindow(GetDlgItem(hwnd
, IDC_KEYSSH2DSA
), 1);
584 EnableWindow(GetDlgItem(hwnd
, IDC_BITS
), 1);
585 EnableMenuItem(state
->filemenu
, IDC_LOAD
, MF_ENABLED
|MF_BYCOMMAND
);
586 EnableMenuItem(state
->filemenu
, IDC_SAVE
, MF_ENABLED
|MF_BYCOMMAND
);
587 EnableMenuItem(state
->filemenu
, IDC_SAVEPUB
, MF_ENABLED
|MF_BYCOMMAND
);
588 EnableMenuItem(state
->keymenu
, IDC_GENERATE
, MF_ENABLED
|MF_BYCOMMAND
);
589 EnableMenuItem(state
->keymenu
, IDC_KEYSSH1
, MF_ENABLED
|MF_BYCOMMAND
);
590 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2RSA
,MF_ENABLED
|MF_BYCOMMAND
);
591 EnableMenuItem(state
->keymenu
, IDC_KEYSSH2DSA
,MF_ENABLED
|MF_BYCOMMAND
);
592 EnableMenuItem(state
->cvtmenu
, IDC_IMPORT
, MF_ENABLED
|MF_BYCOMMAND
);
594 * Enable export menu items if and only if the key type
595 * supports this kind of export.
597 type
= state
->ssh2 ? SSH_KEYTYPE_SSH2
: SSH_KEYTYPE_SSH1
;
598 #define do_export_menuitem(x,y) \
599 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
600 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
601 do_export_menuitem(IDC_EXPORT_OPENSSH
, SSH_KEYTYPE_OPENSSH
);
602 do_export_menuitem(IDC_EXPORT_SSHCOM
, SSH_KEYTYPE_SSHCOM
);
603 #undef do_export_menuitem
609 * Dialog-box function for the main PuTTYgen dialog box.
611 static int CALLBACK
MainDlgProc(HWND hwnd
, UINT msg
,
612 WPARAM wParam
, LPARAM lParam
)
614 static const char generating_msg
[] =
615 "Please wait while a key is generated...";
616 static const char entropy_msg
[] =
617 "Please generate some randomness by moving the mouse over the blank area.";
618 struct MainDlgState
*state
;
623 SetWindowLong(hwnd
, GWL_EXSTYLE
,
624 GetWindowLong(hwnd
, GWL_EXSTYLE
) | WS_EX_CONTEXTHELP
);
627 * If we add a Help button, this is where we destroy it
628 * if the help file isn't present.
631 requested_help
= FALSE
;
633 state
= smalloc(sizeof(*state
));
634 state
->generation_thread_exists
= FALSE
;
635 state
->collecting_entropy
= FALSE
;
636 state
->entropy
= NULL
;
637 state
->key_exists
= FALSE
;
638 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
) state
);
644 menu1
= CreateMenu();
645 AppendMenu(menu1
, MF_ENABLED
, IDC_LOAD
, "&Load private key");
646 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVEPUB
, "Save p&ublic key");
647 AppendMenu(menu1
, MF_ENABLED
, IDC_SAVE
, "&Save private key");
648 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
649 AppendMenu(menu1
, MF_ENABLED
, IDC_QUIT
, "E&xit");
650 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&File");
651 state
->filemenu
= menu1
;
653 menu1
= CreateMenu();
654 AppendMenu(menu1
, MF_ENABLED
, IDC_GENERATE
, "&Generate key pair");
655 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
656 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH1
, "SSH&1 key (RSA)");
657 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2RSA
, "SSH2 &RSA key");
658 AppendMenu(menu1
, MF_ENABLED
, IDC_KEYSSH2DSA
, "SSH2 &DSA key");
659 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Key");
660 state
->keymenu
= menu1
;
662 menu1
= CreateMenu();
663 AppendMenu(menu1
, MF_ENABLED
, IDC_IMPORT
, "&Import key");
664 AppendMenu(menu1
, MF_SEPARATOR
, 0, 0);
665 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_OPENSSH
,
666 "Export &OpenSSH key");
667 AppendMenu(menu1
, MF_ENABLED
, IDC_EXPORT_SSHCOM
,
668 "Export &ssh.com key");
669 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
,
671 state
->cvtmenu
= menu1
;
673 menu1
= CreateMenu();
674 AppendMenu(menu1
, MF_ENABLED
, IDC_ABOUT
, "&About");
676 AppendMenu(menu1
, MF_ENABLED
, IDC_GIVEHELP
, "&Help");
677 AppendMenu(menu
, MF_POPUP
| MF_ENABLED
, (UINT
) menu1
, "&Help");
685 { /* centre the window */
689 hw
= GetDesktopWindow();
690 if (GetWindowRect(hw
, &rs
) && GetWindowRect(hwnd
, &rd
))
692 (rs
.right
+ rs
.left
+ rd
.left
- rd
.right
) / 2,
693 (rs
.bottom
+ rs
.top
+ rd
.top
- rd
.bottom
) / 2,
694 rd
.right
- rd
.left
, rd
.bottom
- rd
.top
, TRUE
);
698 struct ctlpos cp
, cp2
;
700 /* Accelerators used: acglops1rbd */
702 ctlposinit(&cp
, hwnd
, 4, 4, 4);
703 beginbox(&cp
, "Key", IDC_BOX_KEY
);
705 statictext(&cp2
, "No key.", 1, IDC_NOKEY
);
707 statictext(&cp2
, "", 1, IDC_GENERATING
);
708 progressbar(&cp2
, IDC_PROGRESS
);
710 "&Public key for pasting into authorized_keys file:",
711 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 5);
712 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
713 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
714 IDC_FINGERPRINT
, 75);
715 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1,
717 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
718 IDC_COMMENTEDIT
, 75);
719 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
720 IDC_PASSPHRASE1EDIT
, 75);
721 staticpassedit(&cp
, "C&onfirm passphrase:",
722 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 75);
724 beginbox(&cp
, "Actions", IDC_BOX_ACTIONS
);
725 staticbtn(&cp
, "Generate a public/private key pair",
726 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
727 staticbtn(&cp
, "Load an existing private key file",
728 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
729 static2btn(&cp
, "Save the generated key", IDC_SAVESTATIC
,
730 "Save p&ublic key", IDC_SAVEPUB
,
731 "&Save private key", IDC_SAVE
);
733 beginbox(&cp
, "Parameters", IDC_BOX_PARAMS
);
734 radioline(&cp
, "Type of key to generate:", IDC_TYPESTATIC
, 3,
735 "SSH&1 (RSA)", IDC_KEYSSH1
,
736 "SSH2 &RSA", IDC_KEYSSH2RSA
,
737 "SSH2 &DSA", IDC_KEYSSH2DSA
, NULL
);
738 staticedit(&cp
, "Number of &bits in a generated key:",
739 IDC_BITSSTATIC
, IDC_BITS
, 20);
742 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
, IDC_KEYSSH1
);
743 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
744 IDC_KEYSSH1
, MF_BYCOMMAND
);
745 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
748 * Initially, hide the progress bar and the key display,
749 * and show the no-key display. Also disable the Save
750 * buttons, because with no key we obviously can't save
753 ui_set_state(hwnd
, state
, 0);
757 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
758 if (state
->collecting_entropy
&&
759 state
->entropy
&& state
->entropy_got
< state
->entropy_required
) {
760 state
->entropy
[state
->entropy_got
++] = lParam
;
761 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
762 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
763 state
->entropy_got
, 0);
764 if (state
->entropy_got
>= state
->entropy_required
) {
765 struct rsa_key_thread_params
*params
;
769 * Seed the entropy pool
771 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
772 memset(state
->entropy
, 0, state
->entropy_size
);
773 sfree(state
->entropy
);
774 state
->collecting_entropy
= FALSE
;
776 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
777 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
778 MAKELPARAM(0, PROGRESSRANGE
));
779 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
781 params
= smalloc(sizeof(*params
));
782 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
783 params
->dialog
= hwnd
;
784 params
->keysize
= state
->keysize
;
785 params
->is_dsa
= state
->is_dsa
;
786 params
->key
= &state
->key
;
787 params
->dsskey
= &state
->dsskey
;
789 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
790 params
, 0, &threadid
)) {
791 MessageBox(hwnd
, "Out of thread resources",
792 "Key generation error",
793 MB_OK
| MB_ICONERROR
);
796 state
->generation_thread_exists
= TRUE
;
802 switch (LOWORD(wParam
)) {
807 state
= (struct MainDlgState
*)
808 GetWindowLong(hwnd
, GWL_USERDATA
);
809 if (!IsDlgButtonChecked(hwnd
, LOWORD(wParam
)))
810 CheckRadioButton(hwnd
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
812 CheckMenuRadioItem(state
->keymenu
, IDC_KEYSSH1
, IDC_KEYSSH2DSA
,
813 LOWORD(wParam
), MF_BYCOMMAND
);
817 PostMessage(hwnd
, WM_CLOSE
, 0, 0);
819 case IDC_COMMENTEDIT
:
820 if (HIWORD(wParam
) == EN_CHANGE
) {
821 state
= (struct MainDlgState
*)
822 GetWindowLong(hwnd
, GWL_USERDATA
);
823 if (state
->key_exists
) {
824 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
825 int len
= GetWindowTextLength(editctl
);
826 if (*state
->commentptr
)
827 sfree(*state
->commentptr
);
828 *state
->commentptr
= smalloc(len
+ 1);
829 GetWindowText(editctl
, *state
->commentptr
, len
+ 1);
831 setupbigedit2(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
834 setupbigedit1(hwnd
, IDC_KEYDISPLAY
, IDC_PKSTATIC
,
841 EnableWindow(hwnd
, 0);
842 DialogBox(hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
843 EnableWindow(hwnd
, 1);
844 SetActiveWindow(hwnd
);
847 if (HIWORD(wParam
) == BN_CLICKED
||
848 HIWORD(wParam
) == BN_DOUBLECLICKED
) {
850 WinHelp(hwnd
, help_path
, HELP_COMMAND
,
851 (DWORD
)"JI(`',`puttygen.general')");
852 requested_help
= TRUE
;
858 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
859 if (!state
->generation_thread_exists
) {
861 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
, &ok
, FALSE
);
863 state
->keysize
= DEFAULT_KEYSIZE
;
864 /* If we ever introduce a new key type, check it here! */
865 state
->ssh2
= !IsDlgButtonChecked(hwnd
, IDC_KEYSSH1
);
866 state
->is_dsa
= IsDlgButtonChecked(hwnd
, IDC_KEYSSH2DSA
);
867 if (state
->keysize
< 256) {
868 int ret
= MessageBox(hwnd
,
869 "PuTTYgen will not generate a key"
870 " smaller than 256 bits.\n"
871 "Key length reset to 256. Continue?",
873 MB_ICONWARNING
| MB_OKCANCEL
);
876 state
->keysize
= 256;
877 SetDlgItemInt(hwnd
, IDC_BITS
, 256, FALSE
);
879 ui_set_state(hwnd
, state
, 1);
880 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
881 state
->key_exists
= FALSE
;
882 state
->collecting_entropy
= TRUE
;
885 * My brief statistical tests on mouse movements
886 * suggest that there are about 2.5 bits of
887 * randomness in the x position, 2.5 in the y
888 * position, and 1.7 in the message time, making
889 * 5.7 bits of unpredictability per mouse movement.
890 * However, other people have told me it's far less
891 * than that, so I'm going to be stupidly cautious
892 * and knock that down to a nice round 2. With this
893 * method, we require two words per mouse movement,
894 * so with 2 bits per mouse movement we expect 2
895 * bits every 2 words.
897 state
->entropy_required
= (state
->keysize
/ 2) * 2;
898 state
->entropy_got
= 0;
899 state
->entropy_size
= (state
->entropy_required
*
900 sizeof(*state
->entropy
));
901 state
->entropy
= smalloc(state
->entropy_size
);
903 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
904 MAKELPARAM(0, state
->entropy_required
));
905 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
909 case IDC_EXPORT_OPENSSH
:
910 case IDC_EXPORT_SSHCOM
:
912 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
913 if (state
->key_exists
) {
914 char filename
[FILENAME_MAX
];
915 char passphrase
[PASSPHRASE_MAXLEN
];
916 char passphrase2
[PASSPHRASE_MAXLEN
];
920 realtype
= SSH_KEYTYPE_SSH2
;
922 realtype
= SSH_KEYTYPE_SSH1
;
924 if (LOWORD(wParam
) == IDC_EXPORT_OPENSSH
)
925 type
= SSH_KEYTYPE_OPENSSH
;
926 else if (LOWORD(wParam
) == IDC_EXPORT_SSHCOM
)
927 type
= SSH_KEYTYPE_SSHCOM
;
931 if (type
!= realtype
&&
932 import_target_type(type
) != realtype
) {
934 sprintf(msg
, "Cannot export an SSH%d key in an SSH%d"
935 " format", (state
->ssh2 ?
2 : 1),
936 (state
->ssh2 ?
1 : 2));
937 MessageBox(hwnd
, msg
,
938 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
942 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
943 passphrase
, sizeof(passphrase
));
944 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
945 passphrase2
, sizeof(passphrase2
));
946 if (strcmp(passphrase
, passphrase2
)) {
948 "The two passphrases given do not match.",
949 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
954 ret
= MessageBox(hwnd
,
955 "Are you sure you want to save this key\n"
956 "without a passphrase to protect it?",
958 MB_YESNO
| MB_ICONWARNING
);
962 if (prompt_keyfile(hwnd
, "Save private key as:",
965 FILE *fp
= fopen(filename
, "r");
967 char buffer
[FILENAME_MAX
+ 80];
969 sprintf(buffer
, "Overwrite existing file\n%.*s?",
970 FILENAME_MAX
, filename
);
971 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
972 MB_YESNO
| MB_ICONWARNING
);
978 if (type
!= realtype
)
979 ret
= export_ssh2(filename
, type
, &state
->ssh2key
,
980 *passphrase ? passphrase
: NULL
);
982 ret
= ssh2_save_userkey(filename
, &state
->ssh2key
,
983 *passphrase ? passphrase
:
986 if (type
!= realtype
)
987 ret
= export_ssh1(filename
, type
, &state
->key
,
988 *passphrase ? passphrase
: NULL
);
990 ret
= saversakey(filename
, &state
->key
,
991 *passphrase ? passphrase
: NULL
);
994 MessageBox(hwnd
, "Unable to save key file",
995 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1002 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1003 if (state
->key_exists
) {
1004 char filename
[FILENAME_MAX
];
1005 if (prompt_keyfile(hwnd
, "Save public key as:",
1008 FILE *fp
= fopen(filename
, "r");
1010 char buffer
[FILENAME_MAX
+ 80];
1012 sprintf(buffer
, "Overwrite existing file\n%.*s?",
1013 FILENAME_MAX
, filename
);
1014 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
1015 MB_YESNO
| MB_ICONWARNING
);
1020 ret
= save_ssh2_pubkey(filename
, &state
->ssh2key
);
1022 ret
= save_ssh1_pubkey(filename
, &state
->key
);
1025 MessageBox(hwnd
, "Unable to save key file",
1026 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1034 (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1035 if (!state
->generation_thread_exists
) {
1036 char filename
[FILENAME_MAX
];
1037 if (prompt_keyfile(hwnd
, "Load private key:", filename
, 0)) {
1038 char passphrase
[PASSPHRASE_MAXLEN
];
1043 struct PassphraseProcStruct pps
;
1044 struct RSAKey newkey1
;
1045 struct ssh2_userkey
*newkey2
= NULL
;
1047 type
= realtype
= key_type(filename
);
1048 if (type
!= SSH_KEYTYPE_SSH1
&&
1049 type
!= SSH_KEYTYPE_SSH2
&&
1050 !import_possible(type
)) {
1052 sprintf(msg
, "Couldn't load private key (%s)",
1053 key_type_to_str(type
));
1054 MessageBox(NULL
, msg
,
1055 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1059 if (type
!= SSH_KEYTYPE_SSH1
&&
1060 type
!= SSH_KEYTYPE_SSH2
) {
1062 type
= import_target_type(type
);
1066 if (realtype
== SSH_KEYTYPE_SSH1
)
1067 needs_pass
= rsakey_encrypted(filename
, &comment
);
1068 else if (realtype
== SSH_KEYTYPE_SSH2
)
1070 ssh2_userkey_encrypted(filename
, &comment
);
1072 needs_pass
= import_encrypted(filename
, realtype
,
1074 pps
.passphrase
= passphrase
;
1075 pps
.comment
= comment
;
1079 dlgret
= DialogBoxParam(hinst
,
1080 MAKEINTRESOURCE(210),
1081 NULL
, PassphraseProc
,
1089 if (type
== SSH_KEYTYPE_SSH1
) {
1090 if (realtype
== type
)
1091 ret
= loadrsakey(filename
, &newkey1
,
1094 ret
= import_ssh1(filename
, realtype
,
1095 &newkey1
, passphrase
);
1097 if (realtype
== type
)
1098 newkey2
= ssh2_load_userkey(filename
,
1101 newkey2
= import_ssh2(filename
, realtype
,
1103 if (newkey2
== SSH2_WRONG_PASSPHRASE
)
1110 } while (ret
== -1);
1114 MessageBox(NULL
, "Couldn't load private key.",
1115 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
1116 } else if (ret
== 1) {
1118 * Now update the key controls with all the
1122 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
1124 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
1126 if (type
== SSH_KEYTYPE_SSH1
) {
1130 state
->ssh2
= FALSE
;
1131 state
->commentptr
= &state
->key
.comment
;
1132 state
->key
= newkey1
;
1135 * Set the key fingerprint.
1137 savecomment
= state
->key
.comment
;
1138 state
->key
.comment
= NULL
;
1139 rsa_fingerprint(buf
, sizeof(buf
),
1141 state
->key
.comment
= savecomment
;
1143 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1145 * Construct a decimal representation
1146 * of the key, for pasting into
1147 * .ssh/authorized_keys on a Unix box.
1149 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1150 IDC_PKSTATIC
, &state
->key
);
1157 &state
->ssh2key
.comment
;
1158 state
->ssh2key
= *newkey2
; /* structure copy */
1161 savecomment
= state
->ssh2key
.comment
;
1162 state
->ssh2key
.comment
= NULL
;
1164 state
->ssh2key
.alg
->
1165 fingerprint(state
->ssh2key
.data
);
1166 state
->ssh2key
.comment
= savecomment
;
1168 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1171 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1172 IDC_PKSTATIC
, &state
->ssh2key
);
1174 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
1175 *state
->commentptr
);
1178 * Finally, hide the progress bar and show
1181 ui_set_state(hwnd
, state
, 2);
1182 state
->key_exists
= TRUE
;
1185 * If the user has imported a foreign key
1186 * using the Load command, let them know.
1187 * If they've used the Import command, be
1190 if (realtype
!= type
&& LOWORD(wParam
) == IDC_LOAD
) {
1192 sprintf(msg
, "Successfully imported foreign key\n"
1194 "To use this key with PuTTY, you need to\n"
1195 "use the \"Save private key\" command to\n"
1196 "save it in PuTTY's own format.",
1197 key_type_to_str(realtype
));
1198 MessageBox(NULL
, msg
, "PuTTYgen Notice",
1199 MB_OK
| MB_ICONINFORMATION
);
1208 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1209 state
->generation_thread_exists
= FALSE
;
1210 state
->key_exists
= TRUE
;
1211 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
1212 MAKELPARAM(0, PROGRESSRANGE
));
1213 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
1215 if (state
->is_dsa
) {
1216 state
->ssh2key
.data
= &state
->dsskey
;
1217 state
->ssh2key
.alg
= &ssh_dss
;
1219 state
->ssh2key
.data
= &state
->key
;
1220 state
->ssh2key
.alg
= &ssh_rsa
;
1222 state
->commentptr
= &state
->ssh2key
.comment
;
1224 state
->commentptr
= &state
->key
.comment
;
1227 * Invent a comment for the key. We'll do this by including
1228 * the date in it. This will be so horrifyingly ugly that
1229 * the user will immediately want to change it, which is
1232 *state
->commentptr
= smalloc(30);
1239 strftime(*state
->commentptr
, 30, "dsa-key-%Y%m%d", tm
);
1241 strftime(*state
->commentptr
, 30, "rsa-key-%Y%m%d", tm
);
1245 * Now update the key controls with all the key data.
1250 * Blank passphrase, initially. This isn't dangerous,
1251 * because we will warn (Are You Sure?) before allowing
1252 * the user to save an unprotected private key.
1254 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
1255 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
1259 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, *state
->commentptr
);
1261 * Set the key fingerprint.
1263 savecomment
= *state
->commentptr
;
1264 *state
->commentptr
= NULL
;
1267 fp
= state
->ssh2key
.alg
->fingerprint(state
->ssh2key
.data
);
1268 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, fp
);
1272 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
1273 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
1275 *state
->commentptr
= savecomment
;
1277 * Construct a decimal representation of the key, for
1278 * pasting into .ssh/authorized_keys or
1279 * .ssh/authorized_keys2 on a Unix box.
1282 setupbigedit2(hwnd
, IDC_KEYDISPLAY
,
1283 IDC_PKSTATIC
, &state
->ssh2key
);
1285 setupbigedit1(hwnd
, IDC_KEYDISPLAY
,
1286 IDC_PKSTATIC
, &state
->key
);
1290 * Finally, hide the progress bar and show the key data.
1292 ui_set_state(hwnd
, state
, 2);
1296 int id
= ((LPHELPINFO
)lParam
)->iCtrlId
;
1299 case IDC_GENERATING
:
1303 cmd
= "JI(`',`puttygen.generate')"; break;
1305 case IDC_KEYDISPLAY
:
1306 cmd
= "JI(`',`puttygen.pastekey')"; break;
1308 case IDC_FINGERPRINT
:
1309 cmd
= "JI(`',`puttygen.fingerprint')"; break;
1310 case IDC_COMMENTSTATIC
:
1311 case IDC_COMMENTEDIT
:
1312 cmd
= "JI(`',`puttygen.comment')"; break;
1313 case IDC_PASSPHRASE1STATIC
:
1314 case IDC_PASSPHRASE1EDIT
:
1315 case IDC_PASSPHRASE2STATIC
:
1316 case IDC_PASSPHRASE2EDIT
:
1317 cmd
= "JI(`',`puttygen.passphrase')"; break;
1318 case IDC_LOADSTATIC
:
1320 cmd
= "JI(`',`puttygen.load')"; break;
1321 case IDC_SAVESTATIC
:
1323 cmd
= "JI(`',`puttygen.savepriv')"; break;
1325 cmd
= "JI(`',`puttygen.savepub')"; break;
1326 case IDC_TYPESTATIC
:
1328 case IDC_KEYSSH2RSA
:
1329 case IDC_KEYSSH2DSA
:
1330 cmd
= "JI(`',`puttygen.keytype')"; break;
1331 case IDC_BITSSTATIC
:
1333 cmd
= "JI(`',`puttygen.bits')"; break;
1335 case IDC_EXPORT_OPENSSH
:
1336 case IDC_EXPORT_SSHCOM
:
1337 cmd
= "JI(`',`puttygen.conversions')"; break;
1340 WinHelp(hwnd
, help_path
, HELP_COMMAND
, (DWORD
)cmd
);
1341 requested_help
= TRUE
;
1348 state
= (struct MainDlgState
*) GetWindowLong(hwnd
, GWL_USERDATA
);
1350 if (requested_help
) {
1351 WinHelp(hwnd
, help_path
, HELP_QUIT
, 0);
1352 requested_help
= FALSE
;
1360 void cleanup_exit(int code
) { exit(code
); }
1362 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
)
1364 InitCommonControls();
1368 * See if we can find our Help file.
1371 char b
[2048], *p
, *q
, *r
;
1373 GetModuleFileName(NULL
, b
, sizeof(b
) - 1);
1375 p
= strrchr(b
, '\\');
1376 if (p
&& p
>= r
) r
= p
+1;
1377 q
= strrchr(b
, ':');
1378 if (q
&& q
>= r
) r
= q
+1;
1379 strcpy(r
, "putty.hlp");
1380 if ( (fp
= fopen(b
, "r")) != NULL
) {
1381 help_path
= dupstr(b
);
1388 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
,
1389 MainDlgProc
) != IDOK
;