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