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