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