Disable username switching between SSH2 auth attempts, and add a
[u/mdw/putty] / puttygen.c
CommitLineData
6e522441 1/*
2 * PuTTY key generation front end.
3 */
4
5#include <windows.h>
6#include <commctrl.h>
7#include <time.h>
6e522441 8#include <stdio.h>
49bad831 9#include <stdlib.h>
6e522441 10
11#define PUTTY_DO_GLOBALS
12
13#include "putty.h"
14#include "ssh.h"
15#include "winstuff.h"
16
17#define WM_DONEKEY (WM_XUSER + 1)
18
0c2986d0 19#define DEFAULT_KEYSIZE 1024
6e522441 20
6e522441 21/* ----------------------------------------------------------------------
22 * Progress report code. This is really horrible :-)
23 */
5c72ca61 24#define PROGRESSRANGE 65535
25#define MAXPHASE 5
6e522441 26struct progress {
5c72ca61 27 int nphases;
28 struct {
29 int exponential;
30 unsigned startpoint, total;
31 unsigned param, current, n; /* if exponential */
32 unsigned mult; /* if linear */
33 } phases[MAXPHASE];
34 unsigned total, divisor, range;
6e522441 35 HWND progbar;
36};
37
5c72ca61 38static void progress_update(void *param, int action, int phase, int iprogress)
32874aea 39{
40 struct progress *p = (struct progress *) param;
6e522441 41 unsigned progress = iprogress;
42 int position;
43
5c72ca61 44 if (action < PROGFN_READY && p->nphases < phase)
45 p->nphases = phase;
46 switch (action) {
7c7f6893 47 case PROGFN_INITIALISE:
48 p->nphases = 0;
49 break;
5c72ca61 50 case PROGFN_LIN_PHASE:
51 p->phases[phase-1].exponential = 0;
52 p->phases[phase-1].mult = p->phases[phase].total / progress;
53 break;
54 case PROGFN_EXP_PHASE:
55 p->phases[phase-1].exponential = 1;
56 p->phases[phase-1].param = 0x10000 + progress;
57 p->phases[phase-1].current = p->phases[phase-1].total;
58 p->phases[phase-1].n = 0;
32874aea 59 break;
5c72ca61 60 case PROGFN_PHASE_EXTENT:
61 p->phases[phase-1].total = progress;
62 break;
63 case PROGFN_READY:
64 {
65 unsigned total = 0;
66 int i;
67 for (i = 0; i < p->nphases; i++) {
68 p->phases[i].startpoint = total;
69 total += p->phases[i].total;
70 }
71 p->total = total;
72 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
73 p->range = p->total / p->divisor;
74 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
32874aea 75 }
32874aea 76 break;
5c72ca61 77 case PROGFN_PROGRESS:
78 if (p->phases[phase-1].exponential) {
79 while (p->phases[phase-1].n < progress) {
80 p->phases[phase-1].n++;
81 p->phases[phase-1].current *= p->phases[phase-1].param;
82 p->phases[phase-1].current /= 0x10000;
83 }
84 position = (p->phases[phase-1].startpoint +
85 p->phases[phase-1].total - p->phases[phase-1].current);
86 } else {
87 position = (p->phases[phase-1].startpoint +
88 progress * p->phases[phase-1].mult);
89 }
90 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
32874aea 91 break;
6e522441 92 }
6e522441 93}
94
95extern char ver[];
96
97#define PASSPHRASE_MAXLEN 512
98
99struct PassphraseProcStruct {
100 char *passphrase;
101 char *comment;
102};
103
104/*
105 * Dialog-box function for the passphrase box.
106 */
107static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
32874aea 108 WPARAM wParam, LPARAM lParam)
109{
8e1feb27 110 static char *passphrase = NULL;
6e522441 111 struct PassphraseProcStruct *p;
112
113 switch (msg) {
114 case WM_INITDIALOG:
32874aea 115 SetForegroundWindow(hwnd);
116 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
117 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
65a22376 118
119 /*
120 * Centre the window.
121 */
122 { /* centre the window */
123 RECT rs, rd;
124 HWND hw;
125
126 hw = GetDesktopWindow();
32874aea 127 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
128 MoveWindow(hwnd,
129 (rs.right + rs.left + rd.left - rd.right) / 2,
130 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
131 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 132 }
133
32874aea 134 p = (struct PassphraseProcStruct *) lParam;
135 passphrase = p->passphrase;
136 if (p->comment)
137 SetDlgItemText(hwnd, 101, p->comment);
138 *passphrase = 0;
139 SetDlgItemText(hwnd, 102, passphrase);
140 return 0;
6e522441 141 case WM_COMMAND:
142 switch (LOWORD(wParam)) {
143 case IDOK:
144 if (*passphrase)
32874aea 145 EndDialog(hwnd, 1);
6e522441 146 else
32874aea 147 MessageBeep(0);
6e522441 148 return 0;
149 case IDCANCEL:
32874aea 150 EndDialog(hwnd, 0);
6e522441 151 return 0;
32874aea 152 case 102: /* edit box */
8e1feb27 153 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
32874aea 154 GetDlgItemText(hwnd, 102, passphrase,
155 PASSPHRASE_MAXLEN - 1);
156 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
157 }
158 return 0;
6e522441 159 }
160 return 0;
161 case WM_CLOSE:
32874aea 162 EndDialog(hwnd, 0);
6e522441 163 return 0;
164 }
165 return 0;
166}
167
168/*
169 * Prompt for a key file. Assumes the filename buffer is of size
170 * FILENAME_MAX.
171 */
172static int prompt_keyfile(HWND hwnd, char *dlgtitle,
32874aea 173 char *filename, int save)
174{
6e522441 175 OPENFILENAME of;
176 memset(&of, 0, sizeof(of));
177#ifdef OPENFILENAME_SIZE_VERSION_400
178 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
179#else
180 of.lStructSize = sizeof(of);
181#endif
182 of.hwndOwner = hwnd;
183 of.lpstrFilter = "All Files\0*\0\0\0";
184 of.lpstrCustomFilter = NULL;
185 of.nFilterIndex = 1;
32874aea 186 of.lpstrFile = filename;
187 *filename = '\0';
6e522441 188 of.nMaxFile = FILENAME_MAX;
189 of.lpstrFileTitle = NULL;
190 of.lpstrInitialDir = NULL;
191 of.lpstrTitle = dlgtitle;
192 of.Flags = 0;
193 if (save)
32874aea 194 return GetSaveFileName(&of);
6e522441 195 else
32874aea 196 return GetOpenFileName(&of);
6e522441 197}
198
199/*
200 * This function is needed to link with the DES code. We need not
201 * have it do anything at all.
202 */
32874aea 203void logevent(char *msg)
204{
6e522441 205}
206
207/*
208 * Dialog-box function for the Licence box.
209 */
32874aea 210static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
211 WPARAM wParam, LPARAM lParam)
212{
6e522441 213 switch (msg) {
214 case WM_INITDIALOG:
65a22376 215 /*
216 * Centre the window.
217 */
218 { /* centre the window */
219 RECT rs, rd;
220 HWND hw;
221
222 hw = GetDesktopWindow();
32874aea 223 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
224 MoveWindow(hwnd,
225 (rs.right + rs.left + rd.left - rd.right) / 2,
226 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
227 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 228 }
229
6e522441 230 return 1;
231 case WM_COMMAND:
232 switch (LOWORD(wParam)) {
233 case IDOK:
32874aea 234 EndDialog(hwnd, 1);
6e522441 235 return 0;
236 }
237 return 0;
238 case WM_CLOSE:
239 EndDialog(hwnd, 1);
240 return 0;
241 }
242 return 0;
243}
244
245/*
246 * Dialog-box function for the About box.
247 */
32874aea 248static int CALLBACK AboutProc(HWND hwnd, UINT msg,
249 WPARAM wParam, LPARAM lParam)
250{
6e522441 251 switch (msg) {
252 case WM_INITDIALOG:
65a22376 253 /*
254 * Centre the window.
255 */
256 { /* centre the window */
257 RECT rs, rd;
258 HWND hw;
259
260 hw = GetDesktopWindow();
32874aea 261 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
262 MoveWindow(hwnd,
263 (rs.right + rs.left + rd.left - rd.right) / 2,
264 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
265 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 266 }
267
32874aea 268 SetDlgItemText(hwnd, 100, ver);
6e522441 269 return 1;
270 case WM_COMMAND:
271 switch (LOWORD(wParam)) {
272 case IDOK:
32874aea 273 EndDialog(hwnd, 1);
6e522441 274 return 0;
275 case 101:
276 EnableWindow(hwnd, 0);
32874aea 277 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
6e522441 278 EnableWindow(hwnd, 1);
32874aea 279 SetActiveWindow(hwnd);
6e522441 280 return 0;
281 }
282 return 0;
283 case WM_CLOSE:
32874aea 284 EndDialog(hwnd, 1);
6e522441 285 return 0;
286 }
287 return 0;
288}
289
290/*
291 * Thread to generate a key.
292 */
293struct rsa_key_thread_params {
32874aea 294 HWND progressbar; /* notify this with progress */
295 HWND dialog; /* notify this on completion */
0c2986d0 296 int keysize; /* bits in key */
5c72ca61 297 int is_dsa;
6e522441 298 struct RSAKey *key;
5c72ca61 299 struct dss_key *dsskey;
6e522441 300};
32874aea 301static DWORD WINAPI generate_rsa_key_thread(void *param)
302{
6e522441 303 struct rsa_key_thread_params *params =
32874aea 304 (struct rsa_key_thread_params *) param;
6e522441 305 struct progress prog;
306 prog.progbar = params->progressbar;
307
7c7f6893 308 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
309
5c72ca61 310 if (params->is_dsa)
311 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
312 else
313 rsa_generate(params->key, params->keysize, progress_update, &prog);
6e522441 314
315 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
316
dcbde236 317 sfree(params);
6e522441 318 return 0;
319}
320
321struct MainDlgState {
322 int collecting_entropy;
323 int generation_thread_exists;
324 int key_exists;
325 int entropy_got, entropy_required, entropy_size;
0c2986d0 326 int keysize;
5c72ca61 327 int ssh2, is_dsa;
65a22376 328 char **commentptr; /* points to key.comment or ssh2key.comment */
329 struct ssh2_userkey ssh2key;
6e522441 330 unsigned *entropy;
331 struct RSAKey key;
5c72ca61 332 struct dss_key dsskey;
6e522441 333};
334
32874aea 335static void hidemany(HWND hwnd, const int *ids, int hideit)
336{
6e522441 337 while (*ids) {
32874aea 338 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
6e522441 339 }
340}
341
af282e3b 342static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
32874aea 343{
6e522441 344 char *buffer;
345 char *dec1, *dec2;
346
347 dec1 = bignum_decimal(key->exponent);
348 dec2 = bignum_decimal(key->modulus);
32874aea 349 buffer = smalloc(strlen(dec1) + strlen(dec2) +
350 strlen(key->comment) + 30);
6e522441 351 sprintf(buffer, "%d %s %s %s",
32874aea 352 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
6e522441 353 SetDlgItemText(hwnd, id, buffer);
af282e3b 354 SetDlgItemText(hwnd, idstatic,
355 "&Public key for pasting into authorized_keys file:");
dcbde236 356 sfree(dec1);
357 sfree(dec2);
358 sfree(buffer);
6e522441 359}
360
af282e3b 361static void setupbigedit2(HWND hwnd, int id, int idstatic,
362 struct ssh2_userkey *key)
32874aea 363{
65a22376 364 unsigned char *pub_blob;
365 char *buffer, *p;
44fb739e 366 int pub_len;
65a22376 367 int i;
368
369 pub_blob = key->alg->public_blob(key->data, &pub_len);
32874aea 370 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
65a22376 371 strlen(key->comment) + 3);
372 strcpy(buffer, key->alg->name);
373 p = buffer + strlen(buffer);
374 *p++ = ' ';
375 i = 0;
376 while (i < pub_len) {
32874aea 377 int n = (pub_len - i < 3 ? pub_len - i : 3);
378 base64_encode_atom(pub_blob + i, n, p);
65a22376 379 i += n;
380 p += 4;
381 }
382 *p++ = ' ';
383 strcpy(p, key->comment);
384 SetDlgItemText(hwnd, id, buffer);
af282e3b 385 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
386 "OpenSSH authorized_keys2 file:");
65a22376 387 sfree(pub_blob);
32874aea 388 sfree(buffer);
65a22376 389}
390
af282e3b 391static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
392{
393 char *dec1, *dec2;
394 FILE *fp;
395
396 dec1 = bignum_decimal(key->exponent);
397 dec2 = bignum_decimal(key->modulus);
398 fp = fopen(filename, "wb");
399 if (!fp)
400 return 0;
401 fprintf(fp, "%d %s %s %s\n",
402 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
403 fclose(fp);
404 sfree(dec1);
405 sfree(dec2);
406 return 1;
407}
408
7bedb13c 409/*
410 * Warn about the obsolescent key file format.
411 */
412void old_keyfile_warning(void)
413{
414 static const char mbtitle[] = "PuTTY Key File Warning";
415 static const char message[] =
416 "You are loading an SSH 2 private key which has an\n"
417 "old version of the file format. This means your key\n"
418 "file is not fully tamperproof. Future versions of\n"
419 "PuTTY may stop supporting this private key format,\n"
420 "so we recommend you convert your key to the new\n"
421 "format.\n"
422 "\n"
423 "Once the key is loaded into PuTTYgen, you can perform\n"
424 "this conversion simply by saving it again.";
425
426 MessageBox(NULL, message, mbtitle, MB_OK);
427}
428
af282e3b 429static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
430{
431 unsigned char *pub_blob;
432 char *p;
433 int pub_len;
434 int i, column;
435 FILE *fp;
436
437 pub_blob = key->alg->public_blob(key->data, &pub_len);
438
439 fp = fopen(filename, "wb");
440 if (!fp)
441 return 0;
442
443 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
444
445 fprintf(fp, "Comment: \"");
446 for (p = key->comment; *p; p++) {
447 if (*p == '\\' || *p == '\"')
448 fputc('\\', fp);
449 fputc(*p, fp);
450 }
451 fprintf(fp, "\"\n");
452
453 i = 0;
454 column = 0;
455 while (i < pub_len) {
456 char buf[5];
457 int n = (pub_len - i < 3 ? pub_len - i : 3);
458 base64_encode_atom(pub_blob + i, n, buf);
459 i += n;
460 buf[4] = '\0';
461 fputs(buf, fp);
462 if (++column >= 16) {
463 fputc('\n', fp);
464 column = 0;
465 }
466 }
467 if (column > 0)
468 fputc('\n', fp);
469
470 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
471 fclose(fp);
472 sfree(pub_blob);
473 return 1;
474}
475
6e522441 476/*
477 * Dialog-box function for the main PuTTYgen dialog box.
478 */
32874aea 479static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
480 WPARAM wParam, LPARAM lParam)
481{
6e522441 482 enum {
32874aea 483 controlidstart = 100,
484 IDC_TITLE,
485 IDC_BOX_KEY,
486 IDC_NOKEY,
487 IDC_GENERATING,
488 IDC_PROGRESS,
489 IDC_PKSTATIC, IDC_KEYDISPLAY,
490 IDC_FPSTATIC, IDC_FINGERPRINT,
491 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
492 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
493 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT,
494 IDC_BOX_ACTIONS,
495 IDC_GENSTATIC, IDC_GENERATE,
496 IDC_LOADSTATIC, IDC_LOAD,
af282e3b 497 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
32874aea 498 IDC_BOX_PARAMS,
5c72ca61 499 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
32874aea 500 IDC_BITSSTATIC, IDC_BITS,
501 IDC_ABOUT,
6e522441 502 };
503 static const int nokey_ids[] = { IDC_NOKEY, 0 };
32874aea 504 static const int generating_ids[] =
505 { IDC_GENERATING, IDC_PROGRESS, 0 };
6e522441 506 static const int gotkey_ids[] = {
32874aea 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
512 };
6e522441 513 static const char generating_msg[] =
32874aea 514 "Please wait while a key is generated...";
6e522441 515 static const char entropy_msg[] =
32874aea 516 "Please generate some randomness by moving the mouse over the blank area.";
6e522441 517 struct MainDlgState *state;
518
519 switch (msg) {
520 case WM_INITDIALOG:
65a22376 521 /*
522 * Centre the window.
523 */
524 { /* centre the window */
525 RECT rs, rd;
526 HWND hw;
527
528 hw = GetDesktopWindow();
32874aea 529 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
530 MoveWindow(hwnd,
531 (rs.right + rs.left + rd.left - rd.right) / 2,
532 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
533 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 534 }
535
32874aea 536 state = smalloc(sizeof(*state));
537 state->generation_thread_exists = FALSE;
538 state->collecting_entropy = FALSE;
539 state->entropy = NULL;
540 state->key_exists = FALSE;
541 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
542 {
543 struct ctlpos cp, cp2;
6e522441 544
5c72ca61 545 /* Accelerators used: acglops1rbd */
a5470c60 546
e52455b1 547 ctlposinit(&cp, hwnd, 4, 4, 4);
32874aea 548 bartitle(&cp, "Public and private key generation for PuTTY",
549 IDC_TITLE);
550 beginbox(&cp, "Key", IDC_BOX_KEY);
551 cp2 = cp;
ca68ebca 552 statictext(&cp2, "No key.", 1, IDC_NOKEY);
32874aea 553 cp2 = cp;
ca68ebca 554 statictext(&cp2, "", 1, IDC_GENERATING);
32874aea 555 progressbar(&cp2, IDC_PROGRESS);
556 bigeditctrl(&cp,
557 "&Public key for pasting into authorized_keys file:",
e52455b1 558 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
32874aea 559 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
560 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
561 IDC_FINGERPRINT, 75);
562 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
563 0);
564 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
565 IDC_COMMENTEDIT, 75);
566 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
567 IDC_PASSPHRASE1EDIT, 75);
568 staticpassedit(&cp, "C&onfirm passphrase:",
569 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
570 endbox(&cp);
571 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
572 staticbtn(&cp, "Generate a public/private key pair",
573 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
574 staticbtn(&cp, "Load an existing private key file",
575 IDC_LOADSTATIC, "&Load", IDC_LOAD);
af282e3b 576 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
577 "Save p&ublic key", IDC_SAVEPUB,
578 "&Save private key", IDC_SAVE);
32874aea 579 endbox(&cp);
580 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
5c72ca61 581 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
32874aea 582 "SSH&1 (RSA)", IDC_KEYSSH1,
5c72ca61 583 "SSH2 &RSA", IDC_KEYSSH2RSA,
584 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
32874aea 585 staticedit(&cp, "Number of &bits in a generated key:",
0c2986d0 586 IDC_BITSSTATIC, IDC_BITS, 20);
32874aea 587 endbox(&cp);
588 }
00060c72 589 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
0c2986d0 590 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
591
32874aea 592 /*
593 * Initially, hide the progress bar and the key display,
594 * and show the no-key display. Also disable the Save
af282e3b 595 * buttons, because with no key we obviously can't save
32874aea 596 * anything.
597 */
598 hidemany(hwnd, nokey_ids, FALSE);
599 hidemany(hwnd, generating_ids, TRUE);
600 hidemany(hwnd, gotkey_ids, TRUE);
601 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
af282e3b 602 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
6e522441 603
604 return 1;
605 case WM_MOUSEMOVE:
32874aea 606 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
607 if (state->collecting_entropy &&
608 state->entropy && state->entropy_got < state->entropy_required) {
609 state->entropy[state->entropy_got++] = lParam;
610 state->entropy[state->entropy_got++] = GetMessageTime();
611 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
612 state->entropy_got, 0);
613 if (state->entropy_got >= state->entropy_required) {
614 struct rsa_key_thread_params *params;
615 DWORD threadid;
616
617 /*
618 * Seed the entropy pool
619 */
620 random_add_heavynoise(state->entropy, state->entropy_size);
621 memset(state->entropy, 0, state->entropy_size);
622 sfree(state->entropy);
623 state->collecting_entropy = FALSE;
624
625 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
626 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
627 MAKELPARAM(0, PROGRESSRANGE));
628 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
629
630 params = smalloc(sizeof(*params));
631 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
632 params->dialog = hwnd;
0c2986d0 633 params->keysize = state->keysize;
5c72ca61 634 params->is_dsa = state->is_dsa;
32874aea 635 params->key = &state->key;
5c72ca61 636 params->dsskey = &state->dsskey;
32874aea 637
638 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
639 params, 0, &threadid)) {
640 MessageBox(hwnd, "Out of thread resources",
641 "Key generation error",
642 MB_OK | MB_ICONERROR);
643 sfree(params);
644 } else {
645 state->generation_thread_exists = TRUE;
646 }
647 }
648 }
649 break;
6e522441 650 case WM_COMMAND:
651 switch (LOWORD(wParam)) {
652 case IDC_COMMENTEDIT:
653 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 654 state = (struct MainDlgState *)
655 GetWindowLong(hwnd, GWL_USERDATA);
656 if (state->key_exists) {
657 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
658 int len = GetWindowTextLength(editctl);
659 if (*state->commentptr)
660 sfree(*state->commentptr);
661 *state->commentptr = smalloc(len + 1);
662 GetWindowText(editctl, *state->commentptr, len + 1);
2e8324fc 663 if (state->ssh2) {
af282e3b 664 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 665 &state->ssh2key);
2e8324fc 666 } else {
af282e3b 667 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
668 &state->key);
2e8324fc 669 }
670 }
671 }
6e522441 672 break;
673 case IDC_ABOUT:
674 EnableWindow(hwnd, 0);
32874aea 675 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
6e522441 676 EnableWindow(hwnd, 1);
32874aea 677 SetActiveWindow(hwnd);
6e522441 678 return 0;
32874aea 679 case IDC_GENERATE:
680 state =
681 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
682 if (!state->generation_thread_exists) {
683 BOOL ok;
684 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
685 if (!ok)
686 state->keysize = DEFAULT_KEYSIZE;
65a22376 687 /* If we ever introduce a new key type, check it here! */
688 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 689 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 690 if (state->keysize < 256) {
691 int ret = MessageBox(hwnd,
692 "PuTTYgen will not generate a key"
693 " smaller than 256 bits.\n"
694 "Key length reset to 256. Continue?",
695 "PuTTYgen Warning",
696 MB_ICONWARNING | MB_OKCANCEL);
697 if (ret != IDOK)
698 break;
699 state->keysize = 256;
700 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
701 }
702 hidemany(hwnd, nokey_ids, TRUE);
703 hidemany(hwnd, generating_ids, FALSE);
704 hidemany(hwnd, gotkey_ids, TRUE);
705 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
706 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
707 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
af282e3b 708 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
709 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
710 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
00060c72 711 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
af282e3b 712 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
32874aea 713 state->key_exists = FALSE;
714 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
715 state->collecting_entropy = TRUE;
716
717 /*
718 * My brief statistical tests on mouse movements
719 * suggest that there are about 2.5 bits of
720 * randomness in the x position, 2.5 in the y
721 * position, and 1.7 in the message time, making
722 * 5.7 bits of unpredictability per mouse movement.
723 * However, other people have told me it's far less
724 * than that, so I'm going to be stupidly cautious
725 * and knock that down to a nice round 2. With this
726 * method, we require two words per mouse movement,
727 * so with 2 bits per mouse movement we expect 2
728 * bits every 2 words.
729 */
730 state->entropy_required = (state->keysize / 2) * 2;
731 state->entropy_got = 0;
732 state->entropy_size = (state->entropy_required *
733 sizeof(*state->entropy));
734 state->entropy = smalloc(state->entropy_size);
735
736 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
737 MAKELPARAM(0, state->entropy_required));
738 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
739 }
740 break;
741 case IDC_SAVE:
742 state =
743 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
744 if (state->key_exists) {
745 char filename[FILENAME_MAX];
746 char passphrase[PASSPHRASE_MAXLEN];
747 char passphrase2[PASSPHRASE_MAXLEN];
748 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
749 passphrase, sizeof(passphrase));
750 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
751 passphrase2, sizeof(passphrase2));
a5470c60 752 if (strcmp(passphrase, passphrase2)) {
32874aea 753 MessageBox(hwnd,
a5470c60 754 "The two passphrases given do not match.",
32874aea 755 "PuTTYgen Error", MB_OK | MB_ICONERROR);
a5470c60 756 break;
757 }
32874aea 758 if (!*passphrase) {
759 int ret;
760 ret = MessageBox(hwnd,
761 "Are you sure you want to save this key\n"
762 "without a passphrase to protect it?",
763 "PuTTYgen Warning",
764 MB_YESNO | MB_ICONWARNING);
765 if (ret != IDYES)
766 break;
767 }
768 if (prompt_keyfile(hwnd, "Save private key as:",
769 filename, 1)) {
bdea0743 770 int ret;
18790478 771 FILE *fp = fopen(filename, "r");
772 if (fp) {
32874aea 773 char buffer[FILENAME_MAX + 80];
18790478 774 fclose(fp);
775 sprintf(buffer, "Overwrite existing file\n%.*s?",
776 FILENAME_MAX, filename);
777 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
778 MB_YESNO | MB_ICONWARNING);
779 if (ret != IDYES)
780 break;
781 }
65a22376 782 if (state->ssh2) {
783 ret = ssh2_save_userkey(filename, &state->ssh2key,
32874aea 784 *passphrase ? passphrase :
785 NULL);
65a22376 786 } else {
787 ret = saversakey(filename, &state->key,
788 *passphrase ? passphrase : NULL);
789 }
bdea0743 790 if (ret <= 0) {
791 MessageBox(hwnd, "Unable to save key file",
32874aea 792 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 793 }
32874aea 794 }
795 }
796 break;
af282e3b 797 case IDC_SAVEPUB:
798 state =
799 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
800 if (state->key_exists) {
801 char filename[FILENAME_MAX];
802 if (prompt_keyfile(hwnd, "Save public key as:",
803 filename, 1)) {
804 int ret;
805 FILE *fp = fopen(filename, "r");
806 if (fp) {
807 char buffer[FILENAME_MAX + 80];
808 fclose(fp);
809 sprintf(buffer, "Overwrite existing file\n%.*s?",
810 FILENAME_MAX, filename);
811 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
812 MB_YESNO | MB_ICONWARNING);
813 if (ret != IDYES)
814 break;
815 }
816 if (state->ssh2) {
817 ret = save_ssh2_pubkey(filename, &state->ssh2key);
818 } else {
819 ret = save_ssh1_pubkey(filename, &state->key);
820 }
821 if (ret <= 0) {
822 MessageBox(hwnd, "Unable to save key file",
823 "PuTTYgen Error", MB_OK | MB_ICONERROR);
824 }
825 }
826 }
827 break;
32874aea 828 case IDC_LOAD:
829 state =
830 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
831 if (!state->generation_thread_exists) {
832 char filename[FILENAME_MAX];
833 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
834 char passphrase[PASSPHRASE_MAXLEN];
835 int needs_pass;
65a22376 836 int ver;
32874aea 837 int ret;
838 char *comment;
839 struct PassphraseProcStruct pps;
840 struct RSAKey newkey1;
2d466ffd 841 struct ssh2_userkey *newkey2 = NULL;
65a22376 842
843 ver = keyfile_version(filename);
844 if (ver == 0) {
32874aea 845 MessageBox(NULL, "Couldn't load private key.",
846 "PuTTYgen Error", MB_OK | MB_ICONERROR);
65a22376 847 break;
848 }
6e522441 849
65a22376 850 comment = NULL;
851 if (ver == 1)
852 needs_pass = rsakey_encrypted(filename, &comment);
853 else
32874aea 854 needs_pass =
855 ssh2_userkey_encrypted(filename, &comment);
856 pps.passphrase = passphrase;
857 pps.comment = comment;
858 do {
859 if (needs_pass) {
860 int dlgret;
861 dlgret = DialogBoxParam(hinst,
862 MAKEINTRESOURCE(210),
863 NULL, PassphraseProc,
864 (LPARAM) & pps);
865 if (!dlgret) {
866 ret = -2;
867 break;
868 }
869 } else
870 *passphrase = '\0';
65a22376 871 if (ver == 1)
32874aea 872 ret =
873 loadrsakey(filename, &newkey1, passphrase);
65a22376 874 else {
32874aea 875 newkey2 =
876 ssh2_load_userkey(filename, passphrase);
65a22376 877 if (newkey2 == SSH2_WRONG_PASSPHRASE)
878 ret = -1;
879 else if (!newkey2)
880 ret = 0;
881 else
882 ret = 1;
883 }
32874aea 884 } while (ret == -1);
885 if (comment)
886 sfree(comment);
887 if (ret == 0) {
888 MessageBox(NULL, "Couldn't load private key.",
889 "PuTTYgen Error", MB_OK | MB_ICONERROR);
890 } else if (ret == 1) {
891 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
892 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
893 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
af282e3b 894 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
895 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
896 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
00060c72 897 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
af282e3b 898 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
32874aea 899 /*
900 * Now update the key controls with all the
901 * key data.
902 */
903 {
904 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
905 passphrase);
906 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
907 passphrase);
65a22376 908 if (ver == 1) {
909 char buf[128];
910 char *savecomment;
911
912 state->ssh2 = FALSE;
913 state->commentptr = &state->key.comment;
914 state->key = newkey1;
915
916 /*
917 * Set the key fingerprint.
918 */
919 savecomment = state->key.comment;
32874aea 920 state->key.comment = NULL;
921 rsa_fingerprint(buf, sizeof(buf),
922 &state->key);
923 state->key.comment = savecomment;
65a22376 924
925 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
926 /*
927 * Construct a decimal representation
928 * of the key, for pasting into
929 * .ssh/authorized_keys on a Unix box.
930 */
32874aea 931 setupbigedit1(hwnd, IDC_KEYDISPLAY,
af282e3b 932 IDC_PKSTATIC, &state->key);
65a22376 933 } else {
934 char *fp;
935 char *savecomment;
936
937 state->ssh2 = TRUE;
32874aea 938 state->commentptr =
939 &state->ssh2key.comment;
940 state->ssh2key = *newkey2; /* structure copy */
65a22376 941 sfree(newkey2);
942
943 savecomment = state->ssh2key.comment;
32874aea 944 state->ssh2key.comment = NULL;
945 fp =
946 state->ssh2key.alg->
947 fingerprint(state->ssh2key.data);
948 state->ssh2key.comment = savecomment;
65a22376 949
950 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
951 sfree(fp);
952
32874aea 953 setupbigedit2(hwnd, IDC_KEYDISPLAY,
af282e3b 954 IDC_PKSTATIC, &state->ssh2key);
65a22376 955 }
32874aea 956 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
957 *state->commentptr);
958 }
959 /*
960 * Finally, hide the progress bar and show
961 * the key data.
962 */
963 hidemany(hwnd, nokey_ids, TRUE);
964 hidemany(hwnd, generating_ids, TRUE);
965 hidemany(hwnd, gotkey_ids, FALSE);
966 state->key_exists = TRUE;
967 }
968 }
969 }
970 break;
6e522441 971 }
972 return 0;
973 case WM_DONEKEY:
32874aea 974 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
975 state->generation_thread_exists = FALSE;
976 state->key_exists = TRUE;
5c72ca61 977 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
978 MAKELPARAM(0, PROGRESSRANGE));
979 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
32874aea 980 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
981 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
982 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
af282e3b 983 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
984 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
985 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
00060c72 986 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
af282e3b 987 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
3b4c5899 988 if (state->ssh2) {
5c72ca61 989 if (state->is_dsa) {
990 state->ssh2key.data = &state->dsskey;
991 state->ssh2key.alg = &ssh_dss;
992 } else {
993 state->ssh2key.data = &state->key;
994 state->ssh2key.alg = &ssh_rsa;
995 }
65a22376 996 state->commentptr = &state->ssh2key.comment;
3b4c5899 997 } else {
65a22376 998 state->commentptr = &state->key.comment;
3b4c5899 999 }
32874aea 1000 /*
1001 * Invent a comment for the key. We'll do this by including
1002 * the date in it. This will be so horrifyingly ugly that
1003 * the user will immediately want to change it, which is
1004 * what we want :-)
1005 */
1006 *state->commentptr = smalloc(30);
1007 {
1008 time_t t;
1009 struct tm *tm;
1010 time(&t);
1011 tm = localtime(&t);
5c72ca61 1012 if (state->is_dsa)
1013 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1014 else
1015 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
32874aea 1016 }
1017
1018 /*
1019 * Now update the key controls with all the key data.
1020 */
1021 {
65a22376 1022 char *savecomment;
32874aea 1023 /*
1024 * Blank passphrase, initially. This isn't dangerous,
1025 * because we will warn (Are You Sure?) before allowing
1026 * the user to save an unprotected private key.
1027 */
1028 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1029 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1030 /*
1031 * Set the comment.
1032 */
1033 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1034 /*
1035 * Set the key fingerprint.
1036 */
65a22376 1037 savecomment = *state->commentptr;
1038 *state->commentptr = NULL;
32874aea 1039 if (state->ssh2) {
65a22376 1040 char *fp;
32874aea 1041 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1042 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1043 sfree(fp);
1044 } else {
1045 char buf[128];
32874aea 1046 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1047 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1048 }
65a22376 1049 *state->commentptr = savecomment;
32874aea 1050 /*
1051 * Construct a decimal representation of the key, for
af282e3b 1052 * pasting into .ssh/authorized_keys or
1053 * .ssh/authorized_keys2 on a Unix box.
32874aea 1054 */
65a22376 1055 if (state->ssh2) {
af282e3b 1056 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1057 IDC_PKSTATIC, &state->ssh2key);
65a22376 1058 } else {
af282e3b 1059 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1060 IDC_PKSTATIC, &state->key);
65a22376 1061 }
32874aea 1062 }
1063 /*
1064 * Finally, hide the progress bar and show the key data.
1065 */
1066 hidemany(hwnd, nokey_ids, TRUE);
1067 hidemany(hwnd, generating_ids, TRUE);
1068 hidemany(hwnd, gotkey_ids, FALSE);
1069 break;
6e522441 1070 case WM_CLOSE:
32874aea 1071 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1072 sfree(state);
1073 EndDialog(hwnd, 1);
6e522441 1074 return 0;
1075 }
1076 return 0;
1077}
1078
32874aea 1079int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1080{
8b8cf871 1081 InitCommonControls();
6e522441 1082 hinst = inst;
1083 random_init();
32874aea 1084 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1085 MainDlgProc) != IDOK;
6e522441 1086}