2 * PuTTY key generation front end.
10 #define PUTTY_DO_GLOBALS
16 #define WM_DONEKEY (WM_XUSER + 1)
18 #define DEFAULT_KEYSIZE 1024
20 /* ----------------------------------------------------------------------
21 * Progress report code. This is really horrible :-)
23 #define PHASE1TOTAL 0x10000
24 #define PHASE2TOTAL 0x10000
25 #define PHASE3TOTAL 0x04000
27 #define PHASE2START (PHASE1TOTAL)
28 #define PHASE3START (PHASE1TOTAL + PHASE2TOTAL)
29 #define TOTALTOTAL (PHASE1TOTAL + PHASE2TOTAL + PHASE3TOTAL)
30 #define PROGRESSBIGRANGE 65535
31 #define DIVISOR ((TOTALTOTAL + PROGRESSBIGRANGE - 1) / PROGRESSBIGRANGE)
32 #define PROGRESSRANGE (TOTALTOTAL / DIVISOR)
34 unsigned phase1param
, phase1current
, phase1n
;
35 unsigned phase2param
, phase2current
, phase2n
;
40 static void progress_update(void *param
, int phase
, int iprogress
) {
41 struct progress
*p
= (struct progress
*)param
;
42 unsigned progress
= iprogress
;
47 p
->phase1param
= 0x10000 + progress
;
48 p
->phase1current
= 0x10000; p
->phase1n
= 0;
51 p
->phase2param
= 0x10000 + progress
;
52 p
->phase2current
= 0x10000; p
->phase2n
= 0;
55 p
->phase3mult
= PHASE3TOTAL
/ progress
;
58 while (p
->phase1n
< progress
) {
60 p
->phase1current
*= p
->phase1param
;
61 p
->phase1current
/= 0x10000;
63 position
= PHASE1START
+ 0x10000 - p
->phase1current
;
66 while (p
->phase2n
< progress
) {
68 p
->phase2current
*= p
->phase2param
;
69 p
->phase2current
/= 0x10000;
71 position
= PHASE2START
+ 0x10000 - p
->phase2current
;
74 position
= PHASE3START
+ progress
* p
->phase3mult
;
78 SendMessage(p
->progbar
, PBM_SETPOS
, position
/ DIVISOR
, 0);
83 #define PASSPHRASE_MAXLEN 512
85 struct PassphraseProcStruct
{
91 * Dialog-box function for the passphrase box.
93 static int CALLBACK
PassphraseProc(HWND hwnd
, UINT msg
,
94 WPARAM wParam
, LPARAM lParam
) {
95 static char *passphrase
;
96 struct PassphraseProcStruct
*p
;
100 SetForegroundWindow(hwnd
);
101 SetWindowPos (hwnd
, HWND_TOP
, 0, 0, 0, 0,
102 SWP_NOMOVE
| SWP_NOSIZE
| SWP_SHOWWINDOW
);
103 p
= (struct PassphraseProcStruct
*)lParam
;
104 passphrase
= p
->passphrase
;
106 SetDlgItemText(hwnd
, 101, p
->comment
);
110 switch (LOWORD(wParam
)) {
120 case 102: /* edit box */
121 if (HIWORD(wParam
) == EN_CHANGE
) {
122 GetDlgItemText (hwnd
, 102, passphrase
, PASSPHRASE_MAXLEN
-1);
123 passphrase
[PASSPHRASE_MAXLEN
-1] = '\0';
136 * Prompt for a key file. Assumes the filename buffer is of size
139 static int prompt_keyfile(HWND hwnd
, char *dlgtitle
,
140 char *filename
, int save
) {
142 memset(&of
, 0, sizeof(of
));
143 #ifdef OPENFILENAME_SIZE_VERSION_400
144 of
.lStructSize
= OPENFILENAME_SIZE_VERSION_400
;
146 of
.lStructSize
= sizeof(of
);
149 of
.lpstrFilter
= "All Files\0*\0\0\0";
150 of
.lpstrCustomFilter
= NULL
;
152 of
.lpstrFile
= filename
; *filename
= '\0';
153 of
.nMaxFile
= FILENAME_MAX
;
154 of
.lpstrFileTitle
= NULL
;
155 of
.lpstrInitialDir
= NULL
;
156 of
.lpstrTitle
= dlgtitle
;
159 return GetSaveFileName(&of
);
161 return GetOpenFileName(&of
);
165 * This function is needed to link with the DES code. We need not
166 * have it do anything at all.
168 void logevent(char *msg
) {
172 * Dialog-box function for the Licence box.
174 static int CALLBACK
LicenceProc (HWND hwnd
, UINT msg
,
175 WPARAM wParam
, LPARAM lParam
) {
180 switch (LOWORD(wParam
)) {
194 * Dialog-box function for the About box.
196 static int CALLBACK
AboutProc (HWND hwnd
, UINT msg
,
197 WPARAM wParam
, LPARAM lParam
) {
200 SetDlgItemText (hwnd
, 100, ver
);
203 switch (LOWORD(wParam
)) {
208 EnableWindow(hwnd
, 0);
209 DialogBox (hinst
, MAKEINTRESOURCE(214), NULL
, LicenceProc
);
210 EnableWindow(hwnd
, 1);
211 SetActiveWindow(hwnd
);
223 * Thread to generate a key.
225 struct rsa_key_thread_params
{
226 HWND progressbar
; /* notify this with progress */
227 HWND dialog
; /* notify this on completion */
228 int keysize
; /* bits in key */
232 static DWORD WINAPI
generate_rsa_key_thread(void *param
) {
233 struct rsa_key_thread_params
*params
=
234 (struct rsa_key_thread_params
*)param
;
235 struct progress prog
;
236 prog
.progbar
= params
->progressbar
;
238 rsa_generate(params
->key
, params
->aux
,
239 params
->keysize
, progress_update
, &prog
);
241 PostMessage(params
->dialog
, WM_DONEKEY
, 0, 0);
247 struct MainDlgState
{
248 int collecting_entropy
;
249 int generation_thread_exists
;
251 int entropy_got
, entropy_required
, entropy_size
;
258 static void hidemany(HWND hwnd
, const int *ids
, int hideit
) {
260 ShowWindow(GetDlgItem(hwnd
, *ids
++), (hideit ? SW_HIDE
: SW_SHOW
));
264 static void setupbigedit(HWND hwnd
, int id
, struct RSAKey
*key
) {
268 dec1
= bignum_decimal(key
->exponent
);
269 dec2
= bignum_decimal(key
->modulus
);
270 buffer
= malloc(strlen(dec1
)+strlen(dec2
)+
271 strlen(key
->comment
)+30);
272 sprintf(buffer
, "%d %s %s %s",
273 ssh1_bignum_bitcount(key
->modulus
),
274 dec1
, dec2
, key
->comment
);
275 SetDlgItemText(hwnd
, id
, buffer
);
282 * Dialog-box function for the main PuTTYgen dialog box.
284 static int CALLBACK
MainDlgProc (HWND hwnd
, UINT msg
,
285 WPARAM wParam
, LPARAM lParam
) {
287 controlidstart
= 100,
289 IDC_BOX_KEY
, IDC_BOXT_KEY
,
293 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
294 IDC_FPSTATIC
, IDC_FINGERPRINT
,
295 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
296 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
297 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
,
298 IDC_BOX_ACTIONS
, IDC_BOXT_ACTIONS
,
299 IDC_GENSTATIC
, IDC_GENERATE
,
300 IDC_LOADSTATIC
, IDC_LOAD
,
301 IDC_SAVESTATIC
, IDC_SAVE
,
302 IDC_BOX_PARAMS
, IDC_BOXT_PARAMS
,
303 IDC_BITSSTATIC
, IDC_BITS
,
306 static const int nokey_ids
[] = { IDC_NOKEY
, 0 };
307 static const int generating_ids
[] = { IDC_GENERATING
, IDC_PROGRESS
, 0 };
308 static const int gotkey_ids
[] = {
309 IDC_PKSTATIC
, IDC_KEYDISPLAY
,
310 IDC_FPSTATIC
, IDC_FINGERPRINT
,
311 IDC_COMMENTSTATIC
, IDC_COMMENTEDIT
,
312 IDC_PASSPHRASE1STATIC
, IDC_PASSPHRASE1EDIT
,
313 IDC_PASSPHRASE2STATIC
, IDC_PASSPHRASE2EDIT
, 0 };
314 static const char generating_msg
[] =
315 "Please wait while a key is generated...";
316 static const char entropy_msg
[] =
317 "Please generate some randomness by moving the mouse over the blank area.";
318 struct MainDlgState
*state
;
322 state
= malloc(sizeof(*state
));
323 state
->generation_thread_exists
= FALSE
;
324 state
->key_exists
= FALSE
;
325 SetWindowLong(hwnd
, GWL_USERDATA
, (LONG
)state
);
327 struct ctlpos cp
, cp2
;
329 /* Accelerators used: acglops */
331 ctlposinit(&cp
, hwnd
, 10, 10, 10);
332 bartitle(&cp
, "Public and private key generation for PuTTY",
335 IDC_BOX_KEY
, IDC_BOXT_KEY
);
337 statictext(&cp2
, "No key.", IDC_NOKEY
);
341 progressbar(&cp2
, IDC_PROGRESS
);
343 "&Public key for pasting into authorized_keys file:",
344 IDC_PKSTATIC
, IDC_KEYDISPLAY
, 7);
345 SendDlgItemMessage(hwnd
, IDC_KEYDISPLAY
, EM_SETREADONLY
, 1, 0);
346 staticedit(&cp
, "Key fingerprint:", IDC_FPSTATIC
,
347 IDC_FINGERPRINT
, 70);
348 SendDlgItemMessage(hwnd
, IDC_FINGERPRINT
, EM_SETREADONLY
, 1, 0);
349 staticedit(&cp
, "Key &comment:", IDC_COMMENTSTATIC
,
350 IDC_COMMENTEDIT
, 70);
351 staticpassedit(&cp
, "Key p&assphrase:", IDC_PASSPHRASE1STATIC
,
352 IDC_PASSPHRASE1EDIT
, 70);
353 staticpassedit(&cp
, "C&onfirm passphrase:", IDC_PASSPHRASE2STATIC
,
354 IDC_PASSPHRASE2EDIT
, 70);
356 beginbox(&cp
, "Actions",
357 IDC_BOX_ACTIONS
, IDC_BOXT_ACTIONS
);
358 staticbtn(&cp
, "Generate a public/private key pair",
359 IDC_GENSTATIC
, "&Generate", IDC_GENERATE
);
360 staticbtn(&cp
, "Load an existing private key file",
361 IDC_LOADSTATIC
, "&Load", IDC_LOAD
);
362 staticbtn(&cp
, "Save the generated key to a new file",
363 IDC_SAVESTATIC
, "&Save", IDC_SAVE
);
365 beginbox(&cp
, "Parameters",
366 IDC_BOX_PARAMS
, IDC_BOXT_PARAMS
);
367 staticedit(&cp
, "Number of &bits in a generated key:",
368 IDC_BITSSTATIC
, IDC_BITS
, 20);
371 SetDlgItemInt(hwnd
, IDC_BITS
, DEFAULT_KEYSIZE
, FALSE
);
374 * Initially, hide the progress bar and the key display,
375 * and show the no-key display. Also disable the Save
376 * button, because with no key we obviously can't save
379 hidemany(hwnd
, nokey_ids
, FALSE
);
380 hidemany(hwnd
, generating_ids
, TRUE
);
381 hidemany(hwnd
, gotkey_ids
, TRUE
);
382 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
386 state
= (struct MainDlgState
*)GetWindowLong(hwnd
, GWL_USERDATA
);
387 if (state
->collecting_entropy
) {
388 state
->entropy
[state
->entropy_got
++] = lParam
;
389 state
->entropy
[state
->entropy_got
++] = GetMessageTime();
390 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
,
391 state
->entropy_got
, 0);
392 if (state
->entropy_got
>= state
->entropy_required
) {
393 struct rsa_key_thread_params
*params
;
397 * Seed the entropy pool
399 random_add_heavynoise(state
->entropy
, state
->entropy_size
);
400 memset(state
->entropy
, 0, state
->entropy_size
);
401 free(state
->entropy
);
403 SetDlgItemText(hwnd
, IDC_GENERATING
, generating_msg
);
404 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
405 MAKELPARAM(0, PROGRESSRANGE
));
406 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
408 params
= malloc(sizeof(*params
));
409 params
->progressbar
= GetDlgItem(hwnd
, IDC_PROGRESS
);
410 params
->dialog
= hwnd
;
411 params
->keysize
= state
->keysize
;
412 params
->key
= &state
->key
;
413 params
->aux
= &state
->aux
;
415 if (!CreateThread(NULL
, 0, generate_rsa_key_thread
,
416 params
, 0, &threadid
)) {
417 MessageBox(hwnd
, "Out of thread resources",
418 "Key generation error",
419 MB_OK
| MB_ICONERROR
);
422 state
->generation_thread_exists
= TRUE
;
423 state
->collecting_entropy
= FALSE
;
429 switch (LOWORD(wParam
)) {
430 case IDC_COMMENTEDIT
:
431 if (HIWORD(wParam
) == EN_CHANGE
) {
432 state
= (struct MainDlgState
*)
433 GetWindowLong(hwnd
, GWL_USERDATA
);
434 if (state
->key_exists
) {
435 HWND editctl
= GetDlgItem(hwnd
, IDC_COMMENTEDIT
);
436 int len
= GetWindowTextLength(editctl
);
437 if (state
->key
.comment
)
438 free(state
->key
.comment
);
439 state
->key
.comment
= malloc(len
+1);
440 GetWindowText(editctl
, state
->key
.comment
, len
+1);
445 EnableWindow(hwnd
, 0);
446 DialogBox (hinst
, MAKEINTRESOURCE(213), NULL
, AboutProc
);
447 EnableWindow(hwnd
, 1);
448 SetActiveWindow(hwnd
);
451 state
= (struct MainDlgState
*)GetWindowLong(hwnd
, GWL_USERDATA
);
452 if (!state
->generation_thread_exists
) {
453 hidemany(hwnd
, nokey_ids
, TRUE
);
454 hidemany(hwnd
, generating_ids
, FALSE
);
455 hidemany(hwnd
, gotkey_ids
, TRUE
);
456 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 0);
457 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 0);
458 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 0);
459 state
->key_exists
= FALSE
;
460 SetDlgItemText(hwnd
, IDC_GENERATING
, entropy_msg
);
461 state
->collecting_entropy
= TRUE
;
464 state
->keysize
= GetDlgItemInt(hwnd
, IDC_BITS
,
466 if (!ok
) state
->keysize
= DEFAULT_KEYSIZE
;
470 * My brief statistical tests on mouse movements
471 * suggest that there are about 2.5 bits of
472 * randomness in the x position, 2.5 in the y
473 * position, and 1.7 in the message time, making
474 * 5.7 bits of unpredictability per mouse movement.
475 * However, other people have told me it's far less
476 * than that, so I'm going to be stupidly cautious
477 * and knock that down to a nice round 2. With this
478 * method, we require two words per mouse movement,
479 * so with 2 bits per mouse movement we expect 2
480 * bits every 2 words.
482 state
->entropy_required
= (state
->keysize
/2) * 2;
483 state
->entropy_got
= 0;
484 state
->entropy_size
= (state
->entropy_required
*
485 sizeof(*state
->entropy
));
486 state
->entropy
= malloc(state
->entropy_size
);
488 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
489 MAKELPARAM(0, state
->entropy_required
));
490 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
494 state
= (struct MainDlgState
*)GetWindowLong(hwnd
, GWL_USERDATA
);
495 if (state
->key_exists
) {
496 char filename
[FILENAME_MAX
];
497 char passphrase
[PASSPHRASE_MAXLEN
];
498 char passphrase2
[PASSPHRASE_MAXLEN
];
499 GetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
500 passphrase
, sizeof(passphrase
));
501 GetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
502 passphrase2
, sizeof(passphrase2
));
503 if (strcmp(passphrase
, passphrase2
)) {
505 "The two passphrases given do not match.",
507 MB_OK
| MB_ICONERROR
);
512 ret
= MessageBox(hwnd
,
513 "Are you sure you want to save this key\n"
514 "without a passphrase to protect it?",
516 MB_YESNO
| MB_ICONWARNING
);
520 if (prompt_keyfile(hwnd
, "Save private key as:",
523 FILE *fp
= fopen(filename
, "r");
525 char buffer
[FILENAME_MAX
+80];
527 sprintf(buffer
, "Overwrite existing file\n%.*s?",
528 FILENAME_MAX
, filename
);
529 ret
= MessageBox(hwnd
, buffer
, "PuTTYgen Warning",
530 MB_YESNO
| MB_ICONWARNING
);
534 ret
= saversakey(filename
, &state
->key
, &state
->aux
,
535 *passphrase ? passphrase
: NULL
);
537 MessageBox(hwnd
, "Unable to save key file",
539 MB_OK
| MB_ICONERROR
);
545 state
= (struct MainDlgState
*)GetWindowLong(hwnd
, GWL_USERDATA
);
546 if (!state
->generation_thread_exists
) {
547 char filename
[FILENAME_MAX
];
548 if (prompt_keyfile(hwnd
, "Load private key:",
550 char passphrase
[PASSPHRASE_MAXLEN
];
554 struct PassphraseProcStruct pps
;
555 struct RSAKey newkey
;
556 struct RSAAux newaux
;
558 needs_pass
= rsakey_encrypted(filename
, &comment
);
559 pps
.passphrase
= passphrase
;
560 pps
.comment
= comment
;
564 dlgret
= DialogBoxParam(hinst
,
565 MAKEINTRESOURCE(210),
566 NULL
, PassphraseProc
,
574 ret
= loadrsakey(filename
, &newkey
, &newaux
,
577 if (comment
) free(comment
);
579 MessageBox(NULL
, "Couldn't load private key.",
580 "PuTTYgen Error", MB_OK
| MB_ICONERROR
);
581 } else if (ret
== 1) {
585 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
586 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
587 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
589 * Now update the key controls with all the
594 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
,
596 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
,
598 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
,
601 * Set the key fingerprint.
604 char *savecomment
= state
->key
.comment
;
605 state
->key
.comment
= NULL
;
606 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
607 state
->key
.comment
= savecomment
;
609 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
611 * Construct a decimal representation
612 * of the key, for pasting into
613 * .ssh/authorized_keys on a Unix box.
615 setupbigedit(hwnd
, IDC_KEYDISPLAY
, &state
->key
);
618 * Finally, hide the progress bar and show
621 hidemany(hwnd
, nokey_ids
, TRUE
);
622 hidemany(hwnd
, generating_ids
, TRUE
);
623 hidemany(hwnd
, gotkey_ids
, FALSE
);
624 state
->key_exists
= TRUE
;
632 state
= (struct MainDlgState
*)GetWindowLong(hwnd
, GWL_USERDATA
);
633 state
->generation_thread_exists
= FALSE
;
634 state
->key_exists
= TRUE
;
635 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, PROGRESSRANGE
, 0);
636 EnableWindow(GetDlgItem(hwnd
, IDC_GENERATE
), 1);
637 EnableWindow(GetDlgItem(hwnd
, IDC_LOAD
), 1);
638 EnableWindow(GetDlgItem(hwnd
, IDC_SAVE
), 1);
640 * Invent a comment for the key. We'll do this by including
641 * the date in it. This will be so horrifyingly ugly that
642 * the user will immediately want to change it, which is
645 state
->key
.comment
= malloc(30);
651 strftime(state
->key
.comment
, 30, "rsa-key-%Y%m%d", tm
);
655 * Now update the key controls with all the key data.
660 * Blank passphrase, initially. This isn't dangerous,
661 * because we will warn (Are You Sure?) before allowing
662 * the user to save an unprotected private key.
664 SetDlgItemText(hwnd
, IDC_PASSPHRASE1EDIT
, "");
665 SetDlgItemText(hwnd
, IDC_PASSPHRASE2EDIT
, "");
669 SetDlgItemText(hwnd
, IDC_COMMENTEDIT
, state
->key
.comment
);
671 * Set the key fingerprint.
674 char *savecomment
= state
->key
.comment
;
675 state
->key
.comment
= NULL
;
676 rsa_fingerprint(buf
, sizeof(buf
), &state
->key
);
677 state
->key
.comment
= savecomment
;
679 SetDlgItemText(hwnd
, IDC_FINGERPRINT
, buf
);
681 * Construct a decimal representation of the key, for
682 * pasting into .ssh/authorized_keys on a Unix box.
684 setupbigedit(hwnd
, IDC_KEYDISPLAY
, &state
->key
);
687 * Finally, hide the progress bar and show the key data.
689 hidemany(hwnd
, nokey_ids
, TRUE
);
690 hidemany(hwnd
, generating_ids
, TRUE
);
691 hidemany(hwnd
, gotkey_ids
, FALSE
);
694 state
= (struct MainDlgState
*)GetWindowLong(hwnd
, GWL_USERDATA
);
702 int WINAPI
WinMain(HINSTANCE inst
, HINSTANCE prev
, LPSTR cmdline
, int show
) {
703 InitCommonControls();
706 return DialogBox(hinst
, MAKEINTRESOURCE(201), NULL
, MainDlgProc
) != IDOK
;