Fix for `pageant-dirhandle': a new wrapper functions `request_file()' maintains
[sgt/putty] / windows / winpgen.c
CommitLineData
6e522441 1/*
f4ff9455 2 * PuTTY key generation front end (Windows).
6e522441 3 */
4
6e522441 5#include <time.h>
6e522441 6#include <stdio.h>
49bad831 7#include <stdlib.h>
6e522441 8
9#define PUTTY_DO_GLOBALS
10
11#include "putty.h"
12#include "ssh.h"
7440fd44 13
14#include <commctrl.h>
6e522441 15
336ec53c 16#ifdef MSVC4
17#define ICON_BIG 1
18#endif
19
6e522441 20#define WM_DONEKEY (WM_XUSER + 1)
21
0c2986d0 22#define DEFAULT_KEYSIZE 1024
6e522441 23
0906628e 24static int requested_help;
25
1a418691 26static char *cmdline_keyfile = NULL;
27
1709795f 28/*
29 * Print a modal (Really Bad) message box and perform a fatal exit.
30 */
31void modalfatalbox(char *fmt, ...)
32{
33 va_list ap;
57356d63 34 char *stuff;
1709795f 35
36 va_start(ap, fmt);
57356d63 37 stuff = dupvprintf(fmt, ap);
1709795f 38 va_end(ap);
39 MessageBox(NULL, stuff, "PuTTYgen Fatal Error",
40 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
57356d63 41 sfree(stuff);
1709795f 42 exit(1);
43}
44
6e522441 45/* ----------------------------------------------------------------------
46 * Progress report code. This is really horrible :-)
47 */
5c72ca61 48#define PROGRESSRANGE 65535
49#define MAXPHASE 5
6e522441 50struct progress {
5c72ca61 51 int nphases;
52 struct {
53 int exponential;
54 unsigned startpoint, total;
55 unsigned param, current, n; /* if exponential */
56 unsigned mult; /* if linear */
57 } phases[MAXPHASE];
58 unsigned total, divisor, range;
6e522441 59 HWND progbar;
60};
61
5c72ca61 62static void progress_update(void *param, int action, int phase, int iprogress)
32874aea 63{
64 struct progress *p = (struct progress *) param;
6e522441 65 unsigned progress = iprogress;
66 int position;
67
5c72ca61 68 if (action < PROGFN_READY && p->nphases < phase)
69 p->nphases = phase;
70 switch (action) {
7c7f6893 71 case PROGFN_INITIALISE:
72 p->nphases = 0;
73 break;
5c72ca61 74 case PROGFN_LIN_PHASE:
75 p->phases[phase-1].exponential = 0;
76 p->phases[phase-1].mult = p->phases[phase].total / progress;
77 break;
78 case PROGFN_EXP_PHASE:
79 p->phases[phase-1].exponential = 1;
80 p->phases[phase-1].param = 0x10000 + progress;
81 p->phases[phase-1].current = p->phases[phase-1].total;
82 p->phases[phase-1].n = 0;
32874aea 83 break;
5c72ca61 84 case PROGFN_PHASE_EXTENT:
85 p->phases[phase-1].total = progress;
86 break;
87 case PROGFN_READY:
88 {
89 unsigned total = 0;
90 int i;
91 for (i = 0; i < p->nphases; i++) {
92 p->phases[i].startpoint = total;
93 total += p->phases[i].total;
94 }
95 p->total = total;
96 p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
97 p->range = p->total / p->divisor;
98 SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
32874aea 99 }
32874aea 100 break;
5c72ca61 101 case PROGFN_PROGRESS:
102 if (p->phases[phase-1].exponential) {
103 while (p->phases[phase-1].n < progress) {
104 p->phases[phase-1].n++;
105 p->phases[phase-1].current *= p->phases[phase-1].param;
106 p->phases[phase-1].current /= 0x10000;
107 }
108 position = (p->phases[phase-1].startpoint +
109 p->phases[phase-1].total - p->phases[phase-1].current);
110 } else {
111 position = (p->phases[phase-1].startpoint +
112 progress * p->phases[phase-1].mult);
113 }
114 SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
32874aea 115 break;
6e522441 116 }
6e522441 117}
118
119extern char ver[];
120
121#define PASSPHRASE_MAXLEN 512
122
123struct PassphraseProcStruct {
124 char *passphrase;
125 char *comment;
126};
127
128/*
129 * Dialog-box function for the passphrase box.
130 */
131static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
32874aea 132 WPARAM wParam, LPARAM lParam)
133{
8e1feb27 134 static char *passphrase = NULL;
6e522441 135 struct PassphraseProcStruct *p;
136
137 switch (msg) {
138 case WM_INITDIALOG:
32874aea 139 SetForegroundWindow(hwnd);
140 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
141 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
65a22376 142
143 /*
144 * Centre the window.
145 */
146 { /* centre the window */
147 RECT rs, rd;
148 HWND hw;
149
150 hw = GetDesktopWindow();
32874aea 151 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
152 MoveWindow(hwnd,
153 (rs.right + rs.left + rd.left - rd.right) / 2,
154 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
155 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 156 }
157
32874aea 158 p = (struct PassphraseProcStruct *) lParam;
159 passphrase = p->passphrase;
160 if (p->comment)
161 SetDlgItemText(hwnd, 101, p->comment);
162 *passphrase = 0;
163 SetDlgItemText(hwnd, 102, passphrase);
164 return 0;
6e522441 165 case WM_COMMAND:
166 switch (LOWORD(wParam)) {
167 case IDOK:
168 if (*passphrase)
32874aea 169 EndDialog(hwnd, 1);
6e522441 170 else
32874aea 171 MessageBeep(0);
6e522441 172 return 0;
173 case IDCANCEL:
32874aea 174 EndDialog(hwnd, 0);
6e522441 175 return 0;
32874aea 176 case 102: /* edit box */
8e1feb27 177 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
32874aea 178 GetDlgItemText(hwnd, 102, passphrase,
179 PASSPHRASE_MAXLEN - 1);
180 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
181 }
182 return 0;
6e522441 183 }
184 return 0;
185 case WM_CLOSE:
32874aea 186 EndDialog(hwnd, 0);
6e522441 187 return 0;
188 }
189 return 0;
190}
191
192/*
193 * Prompt for a key file. Assumes the filename buffer is of size
194 * FILENAME_MAX.
195 */
196static int prompt_keyfile(HWND hwnd, char *dlgtitle,
06980e9d 197 char *filename, int save, int ppk)
32874aea 198{
6e522441 199 OPENFILENAME of;
200 memset(&of, 0, sizeof(of));
201#ifdef OPENFILENAME_SIZE_VERSION_400
202 of.lStructSize = OPENFILENAME_SIZE_VERSION_400;
203#else
204 of.lStructSize = sizeof(of);
205#endif
206 of.hwndOwner = hwnd;
06980e9d 207 if (ppk) {
b1ccd98e 208 of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0"
209 "All Files (*.*)\0*\0\0\0";
06980e9d 210 of.lpstrDefExt = ".ppk";
211 } else {
b1ccd98e 212 of.lpstrFilter = "All Files (*.*)\0*\0\0\0";
06980e9d 213 }
6e522441 214 of.lpstrCustomFilter = NULL;
215 of.nFilterIndex = 1;
32874aea 216 of.lpstrFile = filename;
217 *filename = '\0';
6e522441 218 of.nMaxFile = FILENAME_MAX;
219 of.lpstrFileTitle = NULL;
220 of.lpstrInitialDir = NULL;
221 of.lpstrTitle = dlgtitle;
222 of.Flags = 0;
223 if (save)
32874aea 224 return GetSaveFileName(&of);
6e522441 225 else
32874aea 226 return GetOpenFileName(&of);
6e522441 227}
228
229/*
6e522441 230 * Dialog-box function for the Licence box.
231 */
32874aea 232static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
233 WPARAM wParam, LPARAM lParam)
234{
6e522441 235 switch (msg) {
236 case WM_INITDIALOG:
65a22376 237 /*
238 * Centre the window.
239 */
240 { /* centre the window */
241 RECT rs, rd;
242 HWND hw;
243
244 hw = GetDesktopWindow();
32874aea 245 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
246 MoveWindow(hwnd,
247 (rs.right + rs.left + rd.left - rd.right) / 2,
248 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
249 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 250 }
251
6e522441 252 return 1;
253 case WM_COMMAND:
254 switch (LOWORD(wParam)) {
255 case IDOK:
c4c04fac 256 case IDCANCEL:
32874aea 257 EndDialog(hwnd, 1);
6e522441 258 return 0;
259 }
260 return 0;
261 case WM_CLOSE:
262 EndDialog(hwnd, 1);
263 return 0;
264 }
265 return 0;
266}
267
268/*
269 * Dialog-box function for the About box.
270 */
32874aea 271static int CALLBACK AboutProc(HWND hwnd, UINT msg,
272 WPARAM wParam, LPARAM lParam)
273{
6e522441 274 switch (msg) {
275 case WM_INITDIALOG:
65a22376 276 /*
277 * Centre the window.
278 */
279 { /* centre the window */
280 RECT rs, rd;
281 HWND hw;
282
283 hw = GetDesktopWindow();
32874aea 284 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
285 MoveWindow(hwnd,
286 (rs.right + rs.left + rd.left - rd.right) / 2,
287 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
288 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 289 }
290
32874aea 291 SetDlgItemText(hwnd, 100, ver);
6e522441 292 return 1;
293 case WM_COMMAND:
294 switch (LOWORD(wParam)) {
295 case IDOK:
c4c04fac 296 case IDCANCEL:
32874aea 297 EndDialog(hwnd, 1);
6e522441 298 return 0;
299 case 101:
300 EnableWindow(hwnd, 0);
cd9778e2 301 DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc);
6e522441 302 EnableWindow(hwnd, 1);
32874aea 303 SetActiveWindow(hwnd);
6e522441 304 return 0;
305 }
306 return 0;
307 case WM_CLOSE:
32874aea 308 EndDialog(hwnd, 1);
6e522441 309 return 0;
310 }
311 return 0;
312}
313
314/*
315 * Thread to generate a key.
316 */
317struct rsa_key_thread_params {
32874aea 318 HWND progressbar; /* notify this with progress */
319 HWND dialog; /* notify this on completion */
0c2986d0 320 int keysize; /* bits in key */
5c72ca61 321 int is_dsa;
6e522441 322 struct RSAKey *key;
5c72ca61 323 struct dss_key *dsskey;
6e522441 324};
32874aea 325static DWORD WINAPI generate_rsa_key_thread(void *param)
326{
6e522441 327 struct rsa_key_thread_params *params =
32874aea 328 (struct rsa_key_thread_params *) param;
6e522441 329 struct progress prog;
330 prog.progbar = params->progressbar;
331
7c7f6893 332 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
333
5c72ca61 334 if (params->is_dsa)
335 dsa_generate(params->dsskey, params->keysize, progress_update, &prog);
336 else
337 rsa_generate(params->key, params->keysize, progress_update, &prog);
6e522441 338
339 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
340
dcbde236 341 sfree(params);
6e522441 342 return 0;
343}
344
345struct MainDlgState {
346 int collecting_entropy;
347 int generation_thread_exists;
348 int key_exists;
349 int entropy_got, entropy_required, entropy_size;
0c2986d0 350 int keysize;
5c72ca61 351 int ssh2, is_dsa;
65a22376 352 char **commentptr; /* points to key.comment or ssh2key.comment */
353 struct ssh2_userkey ssh2key;
6e522441 354 unsigned *entropy;
355 struct RSAKey key;
5c72ca61 356 struct dss_key dsskey;
b723338c 357 HMENU filemenu, keymenu, cvtmenu;
6e522441 358};
359
32874aea 360static void hidemany(HWND hwnd, const int *ids, int hideit)
361{
6e522441 362 while (*ids) {
32874aea 363 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
6e522441 364 }
365}
366
af282e3b 367static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
32874aea 368{
6e522441 369 char *buffer;
370 char *dec1, *dec2;
371
372 dec1 = bignum_decimal(key->exponent);
373 dec2 = bignum_decimal(key->modulus);
57356d63 374 buffer = dupprintf("%d %s %s %s", bignum_bitcount(key->modulus),
375 dec1, dec2, key->comment);
6e522441 376 SetDlgItemText(hwnd, id, buffer);
af282e3b 377 SetDlgItemText(hwnd, idstatic,
378 "&Public key for pasting into authorized_keys file:");
dcbde236 379 sfree(dec1);
380 sfree(dec2);
381 sfree(buffer);
6e522441 382}
383
af282e3b 384static void setupbigedit2(HWND hwnd, int id, int idstatic,
385 struct ssh2_userkey *key)
32874aea 386{
65a22376 387 unsigned char *pub_blob;
388 char *buffer, *p;
44fb739e 389 int pub_len;
65a22376 390 int i;
391
392 pub_blob = key->alg->public_blob(key->data, &pub_len);
3d88e64d 393 buffer = snewn(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
394 strlen(key->comment) + 3, char);
65a22376 395 strcpy(buffer, key->alg->name);
396 p = buffer + strlen(buffer);
397 *p++ = ' ';
398 i = 0;
399 while (i < pub_len) {
32874aea 400 int n = (pub_len - i < 3 ? pub_len - i : 3);
401 base64_encode_atom(pub_blob + i, n, p);
65a22376 402 i += n;
403 p += 4;
404 }
405 *p++ = ' ';
406 strcpy(p, key->comment);
407 SetDlgItemText(hwnd, id, buffer);
af282e3b 408 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
eecca84d 409 "OpenSSH authorized_keys file:");
65a22376 410 sfree(pub_blob);
32874aea 411 sfree(buffer);
65a22376 412}
413
af282e3b 414static int save_ssh1_pubkey(char *filename, struct RSAKey *key)
415{
416 char *dec1, *dec2;
417 FILE *fp;
418
419 dec1 = bignum_decimal(key->exponent);
420 dec2 = bignum_decimal(key->modulus);
421 fp = fopen(filename, "wb");
422 if (!fp)
423 return 0;
424 fprintf(fp, "%d %s %s %s\n",
425 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
426 fclose(fp);
427 sfree(dec1);
428 sfree(dec2);
429 return 1;
430}
431
7bedb13c 432/*
433 * Warn about the obsolescent key file format.
434 */
435void old_keyfile_warning(void)
436{
437 static const char mbtitle[] = "PuTTY Key File Warning";
438 static const char message[] =
439 "You are loading an SSH 2 private key which has an\n"
440 "old version of the file format. This means your key\n"
441 "file is not fully tamperproof. Future versions of\n"
442 "PuTTY may stop supporting this private key format,\n"
443 "so we recommend you convert your key to the new\n"
444 "format.\n"
445 "\n"
446 "Once the key is loaded into PuTTYgen, you can perform\n"
447 "this conversion simply by saving it again.";
448
449 MessageBox(NULL, message, mbtitle, MB_OK);
450}
451
af282e3b 452static int save_ssh2_pubkey(char *filename, struct ssh2_userkey *key)
453{
454 unsigned char *pub_blob;
455 char *p;
456 int pub_len;
457 int i, column;
458 FILE *fp;
459
460 pub_blob = key->alg->public_blob(key->data, &pub_len);
461
462 fp = fopen(filename, "wb");
463 if (!fp)
464 return 0;
465
466 fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n");
467
468 fprintf(fp, "Comment: \"");
469 for (p = key->comment; *p; p++) {
470 if (*p == '\\' || *p == '\"')
471 fputc('\\', fp);
472 fputc(*p, fp);
473 }
474 fprintf(fp, "\"\n");
475
476 i = 0;
477 column = 0;
478 while (i < pub_len) {
479 char buf[5];
480 int n = (pub_len - i < 3 ? pub_len - i : 3);
481 base64_encode_atom(pub_blob + i, n, buf);
482 i += n;
483 buf[4] = '\0';
484 fputs(buf, fp);
485 if (++column >= 16) {
486 fputc('\n', fp);
487 column = 0;
488 }
489 }
490 if (column > 0)
491 fputc('\n', fp);
492
493 fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n");
494 fclose(fp);
495 sfree(pub_blob);
496 return 1;
497}
498
9d07b2db 499enum {
500 controlidstart = 100,
501 IDC_QUIT,
502 IDC_TITLE,
503 IDC_BOX_KEY,
504 IDC_NOKEY,
505 IDC_GENERATING,
506 IDC_PROGRESS,
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,
512 IDC_BOX_ACTIONS,
513 IDC_GENSTATIC, IDC_GENERATE,
514 IDC_LOADSTATIC, IDC_LOAD,
515 IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB,
516 IDC_BOX_PARAMS,
517 IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA,
518 IDC_BITSSTATIC, IDC_BITS,
519 IDC_ABOUT,
520 IDC_GIVEHELP,
521 IDC_IMPORT, IDC_EXPORT_OPENSSH, IDC_EXPORT_SSHCOM
522};
523
524static const int nokey_ids[] = { IDC_NOKEY, 0 };
525static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
526static const int gotkey_ids[] = {
527 IDC_PKSTATIC, IDC_KEYDISPLAY,
528 IDC_FPSTATIC, IDC_FINGERPRINT,
529 IDC_COMMENTSTATIC, IDC_COMMENTEDIT,
530 IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT,
531 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0
532};
533
534/*
535 * Small UI helper function to switch the state of the main dialog
536 * by enabling and disabling controls and menu items.
537 */
538void ui_set_state(HWND hwnd, struct MainDlgState *state, int status)
539{
540 int type;
541
542 switch (status) {
543 case 0: /* no key */
544 hidemany(hwnd, nokey_ids, FALSE);
545 hidemany(hwnd, generating_ids, TRUE);
546 hidemany(hwnd, gotkey_ids, TRUE);
547 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
548 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
549 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
550 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
551 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
552 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
553 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
554 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
555 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
556 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
557 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
558 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
559 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
560 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND);
561 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND);
b723338c 562 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
563 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
9d07b2db 564 MF_GRAYED|MF_BYCOMMAND);
b723338c 565 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
9d07b2db 566 MF_GRAYED|MF_BYCOMMAND);
567 break;
568 case 1: /* generating key */
569 hidemany(hwnd, nokey_ids, TRUE);
570 hidemany(hwnd, generating_ids, FALSE);
571 hidemany(hwnd, gotkey_ids, TRUE);
572 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
573 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
574 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
575 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
576 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
577 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
578 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
579 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
580 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
581 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
582 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
583 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
584 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
585 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
586 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
b723338c 587 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
588 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
9d07b2db 589 MF_GRAYED|MF_BYCOMMAND);
b723338c 590 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
9d07b2db 591 MF_GRAYED|MF_BYCOMMAND);
592 break;
593 case 2:
594 hidemany(hwnd, nokey_ids, TRUE);
595 hidemany(hwnd, generating_ids, TRUE);
596 hidemany(hwnd, gotkey_ids, FALSE);
597 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
598 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
599 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
600 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
601 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
602 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
603 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
604 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
605 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
606 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
607 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
608 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
609 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
610 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
611 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
b723338c 612 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
9d07b2db 613 /*
614 * Enable export menu items if and only if the key type
615 * supports this kind of export.
616 */
617 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
618#define do_export_menuitem(x,y) \
b723338c 619 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
9d07b2db 620 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
621 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
622 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
623#undef do_export_menuitem
624 break;
625 }
626}
627
1a418691 628void load_key_file(HWND hwnd, struct MainDlgState *state,
9a30e26b 629 Filename filename, int was_import_cmd)
1a418691 630{
631 char passphrase[PASSPHRASE_MAXLEN];
632 int needs_pass;
633 int type, realtype;
634 int ret;
1e87cce5 635 const char *errmsg = NULL;
1a418691 636 char *comment;
637 struct PassphraseProcStruct pps;
638 struct RSAKey newkey1;
639 struct ssh2_userkey *newkey2 = NULL;
640
9a30e26b 641 type = realtype = key_type(&filename);
1a418691 642 if (type != SSH_KEYTYPE_SSH1 &&
643 type != SSH_KEYTYPE_SSH2 &&
644 !import_possible(type)) {
1e87cce5 645 char *msg = dupprintf("Couldn't load private key (%s)",
646 key_type_to_str(type));
1a418691 647 MessageBox(NULL, msg,
648 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1e87cce5 649 sfree(msg);
1a418691 650 return;
651 }
652
653 if (type != SSH_KEYTYPE_SSH1 &&
654 type != SSH_KEYTYPE_SSH2) {
655 realtype = type;
656 type = import_target_type(type);
657 }
658
659 comment = NULL;
660 if (realtype == SSH_KEYTYPE_SSH1)
9a30e26b 661 needs_pass = rsakey_encrypted(&filename, &comment);
1a418691 662 else if (realtype == SSH_KEYTYPE_SSH2)
663 needs_pass =
9a30e26b 664 ssh2_userkey_encrypted(&filename, &comment);
1a418691 665 else
9a30e26b 666 needs_pass = import_encrypted(&filename, realtype,
1a418691 667 &comment);
668 pps.passphrase = passphrase;
669 pps.comment = comment;
670 do {
671 if (needs_pass) {
672 int dlgret;
673 dlgret = DialogBoxParam(hinst,
674 MAKEINTRESOURCE(210),
675 NULL, PassphraseProc,
676 (LPARAM) & pps);
677 if (!dlgret) {
678 ret = -2;
679 break;
680 }
681 } else
682 *passphrase = '\0';
683 if (type == SSH_KEYTYPE_SSH1) {
684 if (realtype == type)
9a30e26b 685 ret = loadrsakey(&filename, &newkey1,
1e87cce5 686 passphrase, &errmsg);
1a418691 687 else
9a30e26b 688 ret = import_ssh1(&filename, realtype,
1e87cce5 689 &newkey1, passphrase, &errmsg);
1a418691 690 } else {
691 if (realtype == type)
9a30e26b 692 newkey2 = ssh2_load_userkey(&filename,
1e87cce5 693 passphrase, &errmsg);
1a418691 694 else
9a30e26b 695 newkey2 = import_ssh2(&filename, realtype,
1e87cce5 696 passphrase, &errmsg);
1a418691 697 if (newkey2 == SSH2_WRONG_PASSPHRASE)
698 ret = -1;
699 else if (!newkey2)
700 ret = 0;
701 else
702 ret = 1;
703 }
704 } while (ret == -1);
705 if (comment)
706 sfree(comment);
707 if (ret == 0) {
1e87cce5 708 char *msg = dupprintf("Couldn't load private key (%s)", errmsg);
709 MessageBox(NULL, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR);
710 sfree(msg);
1a418691 711 } else if (ret == 1) {
712 /*
713 * Now update the key controls with all the
714 * key data.
715 */
716 {
717 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
718 passphrase);
719 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
720 passphrase);
721 if (type == SSH_KEYTYPE_SSH1) {
722 char buf[128];
723 char *savecomment;
724
725 state->ssh2 = FALSE;
726 state->commentptr = &state->key.comment;
727 state->key = newkey1;
728
729 /*
730 * Set the key fingerprint.
731 */
732 savecomment = state->key.comment;
733 state->key.comment = NULL;
734 rsa_fingerprint(buf, sizeof(buf),
735 &state->key);
736 state->key.comment = savecomment;
737
738 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
739 /*
740 * Construct a decimal representation
741 * of the key, for pasting into
742 * .ssh/authorized_keys on a Unix box.
743 */
744 setupbigedit1(hwnd, IDC_KEYDISPLAY,
745 IDC_PKSTATIC, &state->key);
746 } else {
747 char *fp;
748 char *savecomment;
749
750 state->ssh2 = TRUE;
751 state->commentptr =
752 &state->ssh2key.comment;
753 state->ssh2key = *newkey2; /* structure copy */
754 sfree(newkey2);
755
756 savecomment = state->ssh2key.comment;
757 state->ssh2key.comment = NULL;
758 fp =
759 state->ssh2key.alg->
760 fingerprint(state->ssh2key.data);
761 state->ssh2key.comment = savecomment;
762
763 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
764 sfree(fp);
765
766 setupbigedit2(hwnd, IDC_KEYDISPLAY,
767 IDC_PKSTATIC, &state->ssh2key);
768 }
769 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
770 *state->commentptr);
771 }
772 /*
773 * Finally, hide the progress bar and show
774 * the key data.
775 */
776 ui_set_state(hwnd, state, 2);
777 state->key_exists = TRUE;
778
779 /*
780 * If the user has imported a foreign key
781 * using the Load command, let them know.
782 * If they've used the Import command, be
783 * silent.
784 */
785 if (realtype != type && !was_import_cmd) {
786 char msg[512];
787 sprintf(msg, "Successfully imported foreign key\n"
788 "(%s).\n"
789 "To use this key with PuTTY, you need to\n"
790 "use the \"Save private key\" command to\n"
791 "save it in PuTTY's own format.",
792 key_type_to_str(realtype));
793 MessageBox(NULL, msg, "PuTTYgen Notice",
794 MB_OK | MB_ICONINFORMATION);
795 }
796 }
797}
798
6e522441 799/*
800 * Dialog-box function for the main PuTTYgen dialog box.
801 */
32874aea 802static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
803 WPARAM wParam, LPARAM lParam)
804{
6e522441 805 static const char generating_msg[] =
32874aea 806 "Please wait while a key is generated...";
6e522441 807 static const char entropy_msg[] =
32874aea 808 "Please generate some randomness by moving the mouse over the blank area.";
6e522441 809 struct MainDlgState *state;
810
811 switch (msg) {
812 case WM_INITDIALOG:
0906628e 813 if (help_path)
814 SetWindowLong(hwnd, GWL_EXSTYLE,
815 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
816 else {
817 /*
818 * If we add a Help button, this is where we destroy it
819 * if the help file isn't present.
820 */
821 }
822 requested_help = FALSE;
336ec53c 823 SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG,
824 (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200)));
0906628e 825
3d88e64d 826 state = snew(struct MainDlgState);
9d07b2db 827 state->generation_thread_exists = FALSE;
828 state->collecting_entropy = FALSE;
829 state->entropy = NULL;
830 state->key_exists = FALSE;
831 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
9dda6459 832 {
833 HMENU menu, menu1;
834
835 menu = CreateMenu();
836
837 menu1 = CreateMenu();
9dda6459 838 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
839 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
840 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
9d07b2db 841 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
842 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
9dda6459 843 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
9d07b2db 844 state->filemenu = menu1;
845
846 menu1 = CreateMenu();
847 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
848 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
849 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
850 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
851 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
852 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
853 state->keymenu = menu1;
9dda6459 854
9dda6459 855 menu1 = CreateMenu();
b723338c 856 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
857 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
9dda6459 858 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
859 "Export &OpenSSH key");
860 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
861 "Export &ssh.com key");
9dda6459 862 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
03245025 863 "Con&versions");
b723338c 864 state->cvtmenu = menu1;
9dda6459 865
866 menu1 = CreateMenu();
867 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
868 if (help_path)
869 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
9dda6459 870 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
871
872 SetMenu(hwnd, menu);
873 }
874
65a22376 875 /*
876 * Centre the window.
877 */
878 { /* centre the window */
879 RECT rs, rd;
880 HWND hw;
881
882 hw = GetDesktopWindow();
32874aea 883 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
884 MoveWindow(hwnd,
885 (rs.right + rs.left + rd.left - rd.right) / 2,
886 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
887 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 888 }
889
32874aea 890 {
891 struct ctlpos cp, cp2;
6e522441 892
5c72ca61 893 /* Accelerators used: acglops1rbd */
a5470c60 894
e52455b1 895 ctlposinit(&cp, hwnd, 4, 4, 4);
32874aea 896 beginbox(&cp, "Key", IDC_BOX_KEY);
897 cp2 = cp;
ca68ebca 898 statictext(&cp2, "No key.", 1, IDC_NOKEY);
32874aea 899 cp2 = cp;
ca68ebca 900 statictext(&cp2, "", 1, IDC_GENERATING);
32874aea 901 progressbar(&cp2, IDC_PROGRESS);
902 bigeditctrl(&cp,
903 "&Public key for pasting into authorized_keys file:",
e52455b1 904 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
32874aea 905 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
03245025 906 staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC,
32874aea 907 IDC_FINGERPRINT, 75);
908 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
909 0);
910 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
911 IDC_COMMENTEDIT, 75);
912 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
913 IDC_PASSPHRASE1EDIT, 75);
914 staticpassedit(&cp, "C&onfirm passphrase:",
915 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
916 endbox(&cp);
917 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
918 staticbtn(&cp, "Generate a public/private key pair",
919 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
920 staticbtn(&cp, "Load an existing private key file",
921 IDC_LOADSTATIC, "&Load", IDC_LOAD);
af282e3b 922 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
923 "Save p&ublic key", IDC_SAVEPUB,
924 "&Save private key", IDC_SAVE);
32874aea 925 endbox(&cp);
926 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
5c72ca61 927 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
32874aea 928 "SSH&1 (RSA)", IDC_KEYSSH1,
5c72ca61 929 "SSH2 &RSA", IDC_KEYSSH2RSA,
930 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
32874aea 931 staticedit(&cp, "Number of &bits in a generated key:",
0c2986d0 932 IDC_BITSSTATIC, IDC_BITS, 20);
32874aea 933 endbox(&cp);
934 }
3b2db109 935 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH2RSA);
9d07b2db 936 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
2b7c6acd 937 IDC_KEYSSH2RSA, MF_BYCOMMAND);
0c2986d0 938 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
939
32874aea 940 /*
941 * Initially, hide the progress bar and the key display,
942 * and show the no-key display. Also disable the Save
af282e3b 943 * buttons, because with no key we obviously can't save
32874aea 944 * anything.
945 */
9d07b2db 946 ui_set_state(hwnd, state, 0);
6e522441 947
1a418691 948 /*
949 * Load a key file if one was provided on the command line.
950 */
951 if (cmdline_keyfile)
9a30e26b 952 load_key_file(hwnd, state, filename_from_str(cmdline_keyfile), 0);
1a418691 953
6e522441 954 return 1;
955 case WM_MOUSEMOVE:
32874aea 956 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
957 if (state->collecting_entropy &&
958 state->entropy && state->entropy_got < state->entropy_required) {
959 state->entropy[state->entropy_got++] = lParam;
960 state->entropy[state->entropy_got++] = GetMessageTime();
961 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
962 state->entropy_got, 0);
963 if (state->entropy_got >= state->entropy_required) {
964 struct rsa_key_thread_params *params;
965 DWORD threadid;
966
967 /*
968 * Seed the entropy pool
969 */
970 random_add_heavynoise(state->entropy, state->entropy_size);
971 memset(state->entropy, 0, state->entropy_size);
972 sfree(state->entropy);
973 state->collecting_entropy = FALSE;
974
975 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
976 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
977 MAKELPARAM(0, PROGRESSRANGE));
978 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
979
3d88e64d 980 params = snew(struct rsa_key_thread_params);
32874aea 981 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
982 params->dialog = hwnd;
0c2986d0 983 params->keysize = state->keysize;
5c72ca61 984 params->is_dsa = state->is_dsa;
32874aea 985 params->key = &state->key;
5c72ca61 986 params->dsskey = &state->dsskey;
32874aea 987
988 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
989 params, 0, &threadid)) {
990 MessageBox(hwnd, "Out of thread resources",
991 "Key generation error",
992 MB_OK | MB_ICONERROR);
993 sfree(params);
994 } else {
995 state->generation_thread_exists = TRUE;
996 }
997 }
998 }
999 break;
6e522441 1000 case WM_COMMAND:
1001 switch (LOWORD(wParam)) {
9d07b2db 1002 case IDC_KEYSSH1:
1003 case IDC_KEYSSH2RSA:
1004 case IDC_KEYSSH2DSA:
1005 {
1006 state = (struct MainDlgState *)
1007 GetWindowLong(hwnd, GWL_USERDATA);
1008 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1009 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1010 LOWORD(wParam));
1011 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1012 LOWORD(wParam), MF_BYCOMMAND);
1013 }
1014 break;
1015 case IDC_QUIT:
1016 PostMessage(hwnd, WM_CLOSE, 0, 0);
1017 break;
6e522441 1018 case IDC_COMMENTEDIT:
1019 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 1020 state = (struct MainDlgState *)
1021 GetWindowLong(hwnd, GWL_USERDATA);
1022 if (state->key_exists) {
1023 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1024 int len = GetWindowTextLength(editctl);
1025 if (*state->commentptr)
1026 sfree(*state->commentptr);
3d88e64d 1027 *state->commentptr = snewn(len + 1, char);
32874aea 1028 GetWindowText(editctl, *state->commentptr, len + 1);
2e8324fc 1029 if (state->ssh2) {
af282e3b 1030 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 1031 &state->ssh2key);
2e8324fc 1032 } else {
af282e3b 1033 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1034 &state->key);
2e8324fc 1035 }
1036 }
1037 }
6e522441 1038 break;
1039 case IDC_ABOUT:
1040 EnableWindow(hwnd, 0);
cd9778e2 1041 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
6e522441 1042 EnableWindow(hwnd, 1);
32874aea 1043 SetActiveWindow(hwnd);
6e522441 1044 return 0;
9dda6459 1045 case IDC_GIVEHELP:
1046 if (HIWORD(wParam) == BN_CLICKED ||
1047 HIWORD(wParam) == BN_DOUBLECLICKED) {
1048 if (help_path) {
1049 WinHelp(hwnd, help_path, HELP_COMMAND,
1050 (DWORD)"JI(`',`puttygen.general')");
1051 requested_help = TRUE;
1052 }
1053 }
1054 return 0;
32874aea 1055 case IDC_GENERATE:
a06a0632 1056 if (HIWORD(wParam) != BN_CLICKED &&
1057 HIWORD(wParam) != BN_DOUBLECLICKED)
1058 break;
32874aea 1059 state =
1060 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1061 if (!state->generation_thread_exists) {
1062 BOOL ok;
1063 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1064 if (!ok)
1065 state->keysize = DEFAULT_KEYSIZE;
65a22376 1066 /* If we ever introduce a new key type, check it here! */
1067 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 1068 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 1069 if (state->keysize < 256) {
1070 int ret = MessageBox(hwnd,
1071 "PuTTYgen will not generate a key"
1072 " smaller than 256 bits.\n"
1073 "Key length reset to 256. Continue?",
1074 "PuTTYgen Warning",
1075 MB_ICONWARNING | MB_OKCANCEL);
1076 if (ret != IDOK)
1077 break;
1078 state->keysize = 256;
1079 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1080 }
9d07b2db 1081 ui_set_state(hwnd, state, 1);
32874aea 1082 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
9d07b2db 1083 state->key_exists = FALSE;
32874aea 1084 state->collecting_entropy = TRUE;
1085
1086 /*
1087 * My brief statistical tests on mouse movements
1088 * suggest that there are about 2.5 bits of
1089 * randomness in the x position, 2.5 in the y
1090 * position, and 1.7 in the message time, making
1091 * 5.7 bits of unpredictability per mouse movement.
1092 * However, other people have told me it's far less
1093 * than that, so I'm going to be stupidly cautious
1094 * and knock that down to a nice round 2. With this
1095 * method, we require two words per mouse movement,
1096 * so with 2 bits per mouse movement we expect 2
1097 * bits every 2 words.
1098 */
1099 state->entropy_required = (state->keysize / 2) * 2;
1100 state->entropy_got = 0;
1101 state->entropy_size = (state->entropy_required *
3d88e64d 1102 sizeof(unsigned));
1103 state->entropy = snewn(state->entropy_required, unsigned);
32874aea 1104
1105 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1106 MAKELPARAM(0, state->entropy_required));
1107 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1108 }
1109 break;
1110 case IDC_SAVE:
4801c01c 1111 case IDC_EXPORT_OPENSSH:
1112 case IDC_EXPORT_SSHCOM:
f3165540 1113 if (HIWORD(wParam) != BN_CLICKED)
1114 break;
32874aea 1115 state =
1116 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1117 if (state->key_exists) {
1118 char filename[FILENAME_MAX];
1119 char passphrase[PASSPHRASE_MAXLEN];
1120 char passphrase2[PASSPHRASE_MAXLEN];
4801c01c 1121 int type, realtype;
1122
1123 if (state->ssh2)
1124 realtype = SSH_KEYTYPE_SSH2;
1125 else
1126 realtype = SSH_KEYTYPE_SSH1;
1127
1128 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1129 type = SSH_KEYTYPE_OPENSSH;
1130 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1131 type = SSH_KEYTYPE_SSHCOM;
1132 else
1133 type = realtype;
1134
1135 if (type != realtype &&
1136 import_target_type(type) != realtype) {
1137 char msg[256];
1138 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
1139 " format", (state->ssh2 ? 2 : 1),
1140 (state->ssh2 ? 1 : 2));
1141 MessageBox(hwnd, msg,
1142 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1143 break;
1144 }
1145
32874aea 1146 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1147 passphrase, sizeof(passphrase));
1148 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1149 passphrase2, sizeof(passphrase2));
a5470c60 1150 if (strcmp(passphrase, passphrase2)) {
32874aea 1151 MessageBox(hwnd,
a5470c60 1152 "The two passphrases given do not match.",
32874aea 1153 "PuTTYgen Error", MB_OK | MB_ICONERROR);
a5470c60 1154 break;
1155 }
32874aea 1156 if (!*passphrase) {
1157 int ret;
1158 ret = MessageBox(hwnd,
1159 "Are you sure you want to save this key\n"
1160 "without a passphrase to protect it?",
1161 "PuTTYgen Warning",
1162 MB_YESNO | MB_ICONWARNING);
1163 if (ret != IDYES)
1164 break;
1165 }
1166 if (prompt_keyfile(hwnd, "Save private key as:",
06980e9d 1167 filename, 1, (type == realtype))) {
bdea0743 1168 int ret;
18790478 1169 FILE *fp = fopen(filename, "r");
1170 if (fp) {
57356d63 1171 char *buffer;
18790478 1172 fclose(fp);
57356d63 1173 buffer = dupprintf("Overwrite existing file\n%s?",
1174 filename);
18790478 1175 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1176 MB_YESNO | MB_ICONWARNING);
57356d63 1177 sfree(buffer);
18790478 1178 if (ret != IDYES)
1179 break;
1180 }
4801c01c 1181
65a22376 1182 if (state->ssh2) {
9a30e26b 1183 Filename fn = filename_from_str(filename);
4801c01c 1184 if (type != realtype)
9a30e26b 1185 ret = export_ssh2(&fn, type, &state->ssh2key,
4801c01c 1186 *passphrase ? passphrase : NULL);
1187 else
9a30e26b 1188 ret = ssh2_save_userkey(&fn, &state->ssh2key,
4801c01c 1189 *passphrase ? passphrase :
1190 NULL);
65a22376 1191 } else {
9a30e26b 1192 Filename fn = filename_from_str(filename);
4801c01c 1193 if (type != realtype)
9a30e26b 1194 ret = export_ssh1(&fn, type, &state->key,
4801c01c 1195 *passphrase ? passphrase : NULL);
1196 else
9a30e26b 1197 ret = saversakey(&fn, &state->key,
4801c01c 1198 *passphrase ? passphrase : NULL);
65a22376 1199 }
bdea0743 1200 if (ret <= 0) {
1201 MessageBox(hwnd, "Unable to save key file",
32874aea 1202 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 1203 }
32874aea 1204 }
1205 }
1206 break;
af282e3b 1207 case IDC_SAVEPUB:
f3165540 1208 if (HIWORD(wParam) != BN_CLICKED)
1209 break;
af282e3b 1210 state =
1211 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1212 if (state->key_exists) {
1213 char filename[FILENAME_MAX];
1214 if (prompt_keyfile(hwnd, "Save public key as:",
06980e9d 1215 filename, 1, 0)) {
af282e3b 1216 int ret;
1217 FILE *fp = fopen(filename, "r");
1218 if (fp) {
57356d63 1219 char *buffer;
af282e3b 1220 fclose(fp);
57356d63 1221 buffer = dupprintf("Overwrite existing file\n%s?",
1222 filename);
af282e3b 1223 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1224 MB_YESNO | MB_ICONWARNING);
57356d63 1225 sfree(buffer);
af282e3b 1226 if (ret != IDYES)
1227 break;
1228 }
1229 if (state->ssh2) {
1230 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1231 } else {
1232 ret = save_ssh1_pubkey(filename, &state->key);
1233 }
1234 if (ret <= 0) {
1235 MessageBox(hwnd, "Unable to save key file",
1236 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1237 }
1238 }
1239 }
1240 break;
32874aea 1241 case IDC_LOAD:
b723338c 1242 case IDC_IMPORT:
f3165540 1243 if (HIWORD(wParam) != BN_CLICKED)
1244 break;
32874aea 1245 state =
1246 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1247 if (!state->generation_thread_exists) {
1248 char filename[FILENAME_MAX];
06980e9d 1249 if (prompt_keyfile(hwnd, "Load private key:",
1a418691 1250 filename, 0, LOWORD(wParam)==IDC_LOAD))
9a30e26b 1251 load_key_file(hwnd, state, filename_from_str(filename),
1a418691 1252 LOWORD(wParam) != IDC_LOAD);
32874aea 1253 }
1254 break;
6e522441 1255 }
1256 return 0;
1257 case WM_DONEKEY:
32874aea 1258 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1259 state->generation_thread_exists = FALSE;
1260 state->key_exists = TRUE;
5c72ca61 1261 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1262 MAKELPARAM(0, PROGRESSRANGE));
1263 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
3b4c5899 1264 if (state->ssh2) {
5c72ca61 1265 if (state->is_dsa) {
1266 state->ssh2key.data = &state->dsskey;
1267 state->ssh2key.alg = &ssh_dss;
1268 } else {
1269 state->ssh2key.data = &state->key;
1270 state->ssh2key.alg = &ssh_rsa;
1271 }
65a22376 1272 state->commentptr = &state->ssh2key.comment;
3b4c5899 1273 } else {
65a22376 1274 state->commentptr = &state->key.comment;
3b4c5899 1275 }
32874aea 1276 /*
1277 * Invent a comment for the key. We'll do this by including
1278 * the date in it. This will be so horrifyingly ugly that
1279 * the user will immediately want to change it, which is
1280 * what we want :-)
1281 */
3d88e64d 1282 *state->commentptr = snewn(30, char);
32874aea 1283 {
aca589d9 1284 struct tm tm;
1285 tm = ltime();
5c72ca61 1286 if (state->is_dsa)
aca589d9 1287 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
5c72ca61 1288 else
aca589d9 1289 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
32874aea 1290 }
1291
1292 /*
1293 * Now update the key controls with all the key data.
1294 */
1295 {
65a22376 1296 char *savecomment;
32874aea 1297 /*
1298 * Blank passphrase, initially. This isn't dangerous,
1299 * because we will warn (Are You Sure?) before allowing
1300 * the user to save an unprotected private key.
1301 */
1302 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1303 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1304 /*
1305 * Set the comment.
1306 */
1307 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1308 /*
1309 * Set the key fingerprint.
1310 */
65a22376 1311 savecomment = *state->commentptr;
1312 *state->commentptr = NULL;
32874aea 1313 if (state->ssh2) {
65a22376 1314 char *fp;
32874aea 1315 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1316 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1317 sfree(fp);
1318 } else {
1319 char buf[128];
32874aea 1320 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1321 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1322 }
65a22376 1323 *state->commentptr = savecomment;
32874aea 1324 /*
1325 * Construct a decimal representation of the key, for
af282e3b 1326 * pasting into .ssh/authorized_keys or
1327 * .ssh/authorized_keys2 on a Unix box.
32874aea 1328 */
65a22376 1329 if (state->ssh2) {
af282e3b 1330 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1331 IDC_PKSTATIC, &state->ssh2key);
65a22376 1332 } else {
af282e3b 1333 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1334 IDC_PKSTATIC, &state->key);
65a22376 1335 }
32874aea 1336 }
1337 /*
1338 * Finally, hide the progress bar and show the key data.
1339 */
9d07b2db 1340 ui_set_state(hwnd, state, 2);
32874aea 1341 break;
0906628e 1342 case WM_HELP:
1343 if (help_path) {
1344 int id = ((LPHELPINFO)lParam)->iCtrlId;
a9dc49af 1345 char *topic = NULL;
0906628e 1346 switch (id) {
1347 case IDC_GENERATING:
1348 case IDC_PROGRESS:
1349 case IDC_GENSTATIC:
1350 case IDC_GENERATE:
a9dc49af 1351 topic = "puttygen.generate"; break;
0906628e 1352 case IDC_PKSTATIC:
1353 case IDC_KEYDISPLAY:
a9dc49af 1354 topic = "puttygen.pastekey"; break;
0906628e 1355 case IDC_FPSTATIC:
1356 case IDC_FINGERPRINT:
a9dc49af 1357 topic = "puttygen.fingerprint"; break;
0906628e 1358 case IDC_COMMENTSTATIC:
1359 case IDC_COMMENTEDIT:
a9dc49af 1360 topic = "puttygen.comment"; break;
0906628e 1361 case IDC_PASSPHRASE1STATIC:
1362 case IDC_PASSPHRASE1EDIT:
1363 case IDC_PASSPHRASE2STATIC:
1364 case IDC_PASSPHRASE2EDIT:
a9dc49af 1365 topic = "puttygen.passphrase"; break;
0906628e 1366 case IDC_LOADSTATIC:
1367 case IDC_LOAD:
a9dc49af 1368 topic = "puttygen.load"; break;
0906628e 1369 case IDC_SAVESTATIC:
1370 case IDC_SAVE:
a9dc49af 1371 topic = "puttygen.savepriv"; break;
0906628e 1372 case IDC_SAVEPUB:
a9dc49af 1373 topic = "puttygen.savepub"; break;
0906628e 1374 case IDC_TYPESTATIC:
1375 case IDC_KEYSSH1:
1376 case IDC_KEYSSH2RSA:
1377 case IDC_KEYSSH2DSA:
a9dc49af 1378 topic = "puttygen.keytype"; break;
0906628e 1379 case IDC_BITSSTATIC:
1380 case IDC_BITS:
a9dc49af 1381 topic = "puttygen.bits"; break;
b723338c 1382 case IDC_IMPORT:
9d07b2db 1383 case IDC_EXPORT_OPENSSH:
1384 case IDC_EXPORT_SSHCOM:
a9dc49af 1385 topic = "puttygen.conversions"; break;
0906628e 1386 }
a9dc49af 1387 if (topic) {
1388 char *cmd = dupprintf("JI(`',`%s')", topic);
0906628e 1389 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
a9dc49af 1390 sfree(cmd);
0906628e 1391 requested_help = TRUE;
1392 } else {
1393 MessageBeep(0);
1394 }
1395 }
1396 break;
6e522441 1397 case WM_CLOSE:
32874aea 1398 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1399 sfree(state);
0906628e 1400 if (requested_help) {
1401 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1402 requested_help = FALSE;
1403 }
32874aea 1404 EndDialog(hwnd, 1);
6e522441 1405 return 0;
1406 }
1407 return 0;
1408}
1409
93b581bd 1410void cleanup_exit(int code) { exit(code); }
1411
32874aea 1412int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1413{
1a418691 1414 int argc;
1415 char **argv;
1416
d3a1a808 1417 split_into_argv(cmdline, &argc, &argv, NULL);
1a418691 1418
1419 if (argc > 0) {
1420 /*
1421 * Assume the first argument to be a private key file, and
1422 * attempt to load it.
1423 */
1424 cmdline_keyfile = argv[0];
1425 }
1426
8b8cf871 1427 InitCommonControls();
6e522441 1428 hinst = inst;
0906628e 1429
1430 /*
1431 * See if we can find our Help file.
1432 */
1433 {
1434 char b[2048], *p, *q, *r;
1435 FILE *fp;
1436 GetModuleFileName(NULL, b, sizeof(b) - 1);
1437 r = b;
1438 p = strrchr(b, '\\');
1439 if (p && p >= r) r = p+1;
1440 q = strrchr(b, ':');
1441 if (q && q >= r) r = q+1;
a9dc49af 1442 strcpy(r, PUTTY_HELP_FILE);
0906628e 1443 if ( (fp = fopen(b, "r")) != NULL) {
1444 help_path = dupstr(b);
1445 fclose(fp);
1446 } else
1447 help_path = NULL;
1448 }
1449
5d17ccfc 1450 random_ref();
32874aea 1451 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1452 MainDlgProc) != IDOK;
6e522441 1453}