Fix a few more memory and resource leaks.
[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>
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 */
af723f29 957 if (cmdline_keyfile) {
958 Filename *fn = filename_from_str(cmdline_keyfile);
959 load_key_file(hwnd, state, fn, 0);
960 filename_free(fn);
961 }
1a418691 962
6e522441 963 return 1;
964 case WM_MOUSEMOVE:
1e5eefb6 965 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 966 if (state->collecting_entropy &&
967 state->entropy && state->entropy_got < state->entropy_required) {
968 state->entropy[state->entropy_got++] = lParam;
969 state->entropy[state->entropy_got++] = GetMessageTime();
970 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
971 state->entropy_got, 0);
972 if (state->entropy_got >= state->entropy_required) {
973 struct rsa_key_thread_params *params;
974 DWORD threadid;
975
976 /*
977 * Seed the entropy pool
978 */
979 random_add_heavynoise(state->entropy, state->entropy_size);
dfb88efd 980 smemclr(state->entropy, state->entropy_size);
32874aea 981 sfree(state->entropy);
982 state->collecting_entropy = FALSE;
983
984 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
985 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
986 MAKELPARAM(0, PROGRESSRANGE));
987 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
988
3d88e64d 989 params = snew(struct rsa_key_thread_params);
32874aea 990 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
991 params->dialog = hwnd;
0c2986d0 992 params->keysize = state->keysize;
5c72ca61 993 params->is_dsa = state->is_dsa;
32874aea 994 params->key = &state->key;
5c72ca61 995 params->dsskey = &state->dsskey;
32874aea 996
997 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
998 params, 0, &threadid)) {
999 MessageBox(hwnd, "Out of thread resources",
1000 "Key generation error",
1001 MB_OK | MB_ICONERROR);
1002 sfree(params);
1003 } else {
1004 state->generation_thread_exists = TRUE;
1005 }
1006 }
1007 }
1008 break;
6e522441 1009 case WM_COMMAND:
1010 switch (LOWORD(wParam)) {
9d07b2db 1011 case IDC_KEYSSH1:
1012 case IDC_KEYSSH2RSA:
1013 case IDC_KEYSSH2DSA:
1014 {
1015 state = (struct MainDlgState *)
1e5eefb6 1016 GetWindowLongPtr(hwnd, GWLP_USERDATA);
9d07b2db 1017 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
1018 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1019 LOWORD(wParam));
1020 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
1021 LOWORD(wParam), MF_BYCOMMAND);
1022 }
1023 break;
1024 case IDC_QUIT:
1025 PostMessage(hwnd, WM_CLOSE, 0, 0);
1026 break;
6e522441 1027 case IDC_COMMENTEDIT:
1028 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 1029 state = (struct MainDlgState *)
1e5eefb6 1030 GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 1031 if (state->key_exists) {
1032 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
1033 int len = GetWindowTextLength(editctl);
1034 if (*state->commentptr)
1035 sfree(*state->commentptr);
3d88e64d 1036 *state->commentptr = snewn(len + 1, char);
32874aea 1037 GetWindowText(editctl, *state->commentptr, len + 1);
2e8324fc 1038 if (state->ssh2) {
af282e3b 1039 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 1040 &state->ssh2key);
2e8324fc 1041 } else {
af282e3b 1042 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1043 &state->key);
2e8324fc 1044 }
1045 }
1046 }
6e522441 1047 break;
1048 case IDC_ABOUT:
1049 EnableWindow(hwnd, 0);
cd9778e2 1050 DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc);
6e522441 1051 EnableWindow(hwnd, 1);
32874aea 1052 SetActiveWindow(hwnd);
6e522441 1053 return 0;
9dda6459 1054 case IDC_GIVEHELP:
1055 if (HIWORD(wParam) == BN_CLICKED ||
1056 HIWORD(wParam) == BN_DOUBLECLICKED) {
cb2708d3 1057 launch_help(hwnd, WINHELP_CTX_puttygen_general);
9dda6459 1058 }
1059 return 0;
32874aea 1060 case IDC_GENERATE:
a06a0632 1061 if (HIWORD(wParam) != BN_CLICKED &&
1062 HIWORD(wParam) != BN_DOUBLECLICKED)
1063 break;
32874aea 1064 state =
1e5eefb6 1065 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 1066 if (!state->generation_thread_exists) {
1067 BOOL ok;
1068 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
1069 if (!ok)
1070 state->keysize = DEFAULT_KEYSIZE;
65a22376 1071 /* If we ever introduce a new key type, check it here! */
1072 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 1073 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 1074 if (state->keysize < 256) {
1075 int ret = MessageBox(hwnd,
1076 "PuTTYgen will not generate a key"
1077 " smaller than 256 bits.\n"
1078 "Key length reset to 256. Continue?",
1079 "PuTTYgen Warning",
1080 MB_ICONWARNING | MB_OKCANCEL);
1081 if (ret != IDOK)
1082 break;
1083 state->keysize = 256;
1084 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
1085 }
9d07b2db 1086 ui_set_state(hwnd, state, 1);
32874aea 1087 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
9d07b2db 1088 state->key_exists = FALSE;
32874aea 1089 state->collecting_entropy = TRUE;
1090
1091 /*
1092 * My brief statistical tests on mouse movements
1093 * suggest that there are about 2.5 bits of
1094 * randomness in the x position, 2.5 in the y
1095 * position, and 1.7 in the message time, making
1096 * 5.7 bits of unpredictability per mouse movement.
1097 * However, other people have told me it's far less
1098 * than that, so I'm going to be stupidly cautious
1099 * and knock that down to a nice round 2. With this
1100 * method, we require two words per mouse movement,
1101 * so with 2 bits per mouse movement we expect 2
1102 * bits every 2 words.
1103 */
1104 state->entropy_required = (state->keysize / 2) * 2;
1105 state->entropy_got = 0;
1106 state->entropy_size = (state->entropy_required *
3d88e64d 1107 sizeof(unsigned));
1108 state->entropy = snewn(state->entropy_required, unsigned);
32874aea 1109
1110 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1111 MAKELPARAM(0, state->entropy_required));
1112 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
1113 }
1114 break;
1115 case IDC_SAVE:
4801c01c 1116 case IDC_EXPORT_OPENSSH:
1117 case IDC_EXPORT_SSHCOM:
f3165540 1118 if (HIWORD(wParam) != BN_CLICKED)
1119 break;
32874aea 1120 state =
1e5eefb6 1121 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 1122 if (state->key_exists) {
1123 char filename[FILENAME_MAX];
274186e8 1124 char *passphrase, *passphrase2;
4801c01c 1125 int type, realtype;
1126
1127 if (state->ssh2)
1128 realtype = SSH_KEYTYPE_SSH2;
1129 else
1130 realtype = SSH_KEYTYPE_SSH1;
1131
1132 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
1133 type = SSH_KEYTYPE_OPENSSH;
1134 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
1135 type = SSH_KEYTYPE_SSHCOM;
1136 else
1137 type = realtype;
1138
1139 if (type != realtype &&
1140 import_target_type(type) != realtype) {
1141 char msg[256];
2e85c969 1142 sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d"
4801c01c 1143 " format", (state->ssh2 ? 2 : 1),
1144 (state->ssh2 ? 1 : 2));
1145 MessageBox(hwnd, msg,
1146 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1147 break;
1148 }
1149
274186e8 1150 passphrase = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE1EDIT);
1151 passphrase2 = GetDlgItemText_alloc(hwnd, IDC_PASSPHRASE2EDIT);
a5470c60 1152 if (strcmp(passphrase, passphrase2)) {
32874aea 1153 MessageBox(hwnd,
a5470c60 1154 "The two passphrases given do not match.",
32874aea 1155 "PuTTYgen Error", MB_OK | MB_ICONERROR);
274186e8 1156 burnstr(passphrase);
1157 burnstr(passphrase2);
a5470c60 1158 break;
1159 }
274186e8 1160 burnstr(passphrase2);
32874aea 1161 if (!*passphrase) {
1162 int ret;
1163 ret = MessageBox(hwnd,
1164 "Are you sure you want to save this key\n"
1165 "without a passphrase to protect it?",
1166 "PuTTYgen Warning",
1167 MB_YESNO | MB_ICONWARNING);
274186e8 1168 if (ret != IDYES) {
1169 burnstr(passphrase);
1170 break;
1171 }
32874aea 1172 }
1173 if (prompt_keyfile(hwnd, "Save private key as:",
06980e9d 1174 filename, 1, (type == realtype))) {
bdea0743 1175 int ret;
18790478 1176 FILE *fp = fopen(filename, "r");
1177 if (fp) {
57356d63 1178 char *buffer;
18790478 1179 fclose(fp);
57356d63 1180 buffer = dupprintf("Overwrite existing file\n%s?",
1181 filename);
18790478 1182 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1183 MB_YESNO | MB_ICONWARNING);
57356d63 1184 sfree(buffer);
274186e8 1185 if (ret != IDYES) {
1186 burnstr(passphrase);
18790478 1187 break;
274186e8 1188 }
18790478 1189 }
4801c01c 1190
65a22376 1191 if (state->ssh2) {
962468d4 1192 Filename *fn = filename_from_str(filename);
4801c01c 1193 if (type != realtype)
962468d4 1194 ret = export_ssh2(fn, type, &state->ssh2key,
4801c01c 1195 *passphrase ? passphrase : NULL);
1196 else
962468d4 1197 ret = ssh2_save_userkey(fn, &state->ssh2key,
4801c01c 1198 *passphrase ? passphrase :
1199 NULL);
962468d4 1200 filename_free(fn);
65a22376 1201 } else {
962468d4 1202 Filename *fn = filename_from_str(filename);
4801c01c 1203 if (type != realtype)
962468d4 1204 ret = export_ssh1(fn, type, &state->key,
4801c01c 1205 *passphrase ? passphrase : NULL);
1206 else
962468d4 1207 ret = saversakey(fn, &state->key,
4801c01c 1208 *passphrase ? passphrase : NULL);
962468d4 1209 filename_free(fn);
65a22376 1210 }
bdea0743 1211 if (ret <= 0) {
1212 MessageBox(hwnd, "Unable to save key file",
32874aea 1213 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 1214 }
32874aea 1215 }
274186e8 1216 burnstr(passphrase);
32874aea 1217 }
1218 break;
af282e3b 1219 case IDC_SAVEPUB:
f3165540 1220 if (HIWORD(wParam) != BN_CLICKED)
1221 break;
af282e3b 1222 state =
1e5eefb6 1223 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
af282e3b 1224 if (state->key_exists) {
1225 char filename[FILENAME_MAX];
1226 if (prompt_keyfile(hwnd, "Save public key as:",
06980e9d 1227 filename, 1, 0)) {
af282e3b 1228 int ret;
1229 FILE *fp = fopen(filename, "r");
1230 if (fp) {
57356d63 1231 char *buffer;
af282e3b 1232 fclose(fp);
57356d63 1233 buffer = dupprintf("Overwrite existing file\n%s?",
1234 filename);
af282e3b 1235 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1236 MB_YESNO | MB_ICONWARNING);
57356d63 1237 sfree(buffer);
af282e3b 1238 if (ret != IDYES)
1239 break;
1240 }
1241 if (state->ssh2) {
1242 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1243 } else {
1244 ret = save_ssh1_pubkey(filename, &state->key);
1245 }
1246 if (ret <= 0) {
1247 MessageBox(hwnd, "Unable to save key file",
1248 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1249 }
1250 }
1251 }
1252 break;
32874aea 1253 case IDC_LOAD:
b723338c 1254 case IDC_IMPORT:
f3165540 1255 if (HIWORD(wParam) != BN_CLICKED)
1256 break;
32874aea 1257 state =
1e5eefb6 1258 (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 1259 if (!state->generation_thread_exists) {
1260 char filename[FILENAME_MAX];
06980e9d 1261 if (prompt_keyfile(hwnd, "Load private key:",
0c33d3a6 1262 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
1263 Filename *fn = filename_from_str(filename);
1264 load_key_file(hwnd, state, fn, LOWORD(wParam) != IDC_LOAD);
1265 filename_free(fn);
1266 }
32874aea 1267 }
1268 break;
6e522441 1269 }
1270 return 0;
1271 case WM_DONEKEY:
1e5eefb6 1272 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 1273 state->generation_thread_exists = FALSE;
1274 state->key_exists = TRUE;
5c72ca61 1275 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1276 MAKELPARAM(0, PROGRESSRANGE));
1277 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
3b4c5899 1278 if (state->ssh2) {
5c72ca61 1279 if (state->is_dsa) {
1280 state->ssh2key.data = &state->dsskey;
1281 state->ssh2key.alg = &ssh_dss;
1282 } else {
1283 state->ssh2key.data = &state->key;
1284 state->ssh2key.alg = &ssh_rsa;
1285 }
65a22376 1286 state->commentptr = &state->ssh2key.comment;
3b4c5899 1287 } else {
65a22376 1288 state->commentptr = &state->key.comment;
3b4c5899 1289 }
32874aea 1290 /*
1291 * Invent a comment for the key. We'll do this by including
1292 * the date in it. This will be so horrifyingly ugly that
1293 * the user will immediately want to change it, which is
1294 * what we want :-)
1295 */
3d88e64d 1296 *state->commentptr = snewn(30, char);
32874aea 1297 {
aca589d9 1298 struct tm tm;
1299 tm = ltime();
5c72ca61 1300 if (state->is_dsa)
aca589d9 1301 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm);
5c72ca61 1302 else
aca589d9 1303 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm);
32874aea 1304 }
1305
1306 /*
1307 * Now update the key controls with all the key data.
1308 */
1309 {
65a22376 1310 char *savecomment;
32874aea 1311 /*
1312 * Blank passphrase, initially. This isn't dangerous,
1313 * because we will warn (Are You Sure?) before allowing
1314 * the user to save an unprotected private key.
1315 */
1316 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1317 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1318 /*
1319 * Set the comment.
1320 */
1321 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1322 /*
1323 * Set the key fingerprint.
1324 */
65a22376 1325 savecomment = *state->commentptr;
1326 *state->commentptr = NULL;
32874aea 1327 if (state->ssh2) {
65a22376 1328 char *fp;
32874aea 1329 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1330 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1331 sfree(fp);
1332 } else {
1333 char buf[128];
32874aea 1334 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1335 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1336 }
65a22376 1337 *state->commentptr = savecomment;
32874aea 1338 /*
1339 * Construct a decimal representation of the key, for
af282e3b 1340 * pasting into .ssh/authorized_keys or
1341 * .ssh/authorized_keys2 on a Unix box.
32874aea 1342 */
65a22376 1343 if (state->ssh2) {
af282e3b 1344 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1345 IDC_PKSTATIC, &state->ssh2key);
65a22376 1346 } else {
af282e3b 1347 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1348 IDC_PKSTATIC, &state->key);
65a22376 1349 }
32874aea 1350 }
1351 /*
1352 * Finally, hide the progress bar and show the key data.
1353 */
9d07b2db 1354 ui_set_state(hwnd, state, 2);
32874aea 1355 break;
0906628e 1356 case WM_HELP:
cb2708d3 1357 {
0906628e 1358 int id = ((LPHELPINFO)lParam)->iCtrlId;
a9dc49af 1359 char *topic = NULL;
0906628e 1360 switch (id) {
1361 case IDC_GENERATING:
1362 case IDC_PROGRESS:
1363 case IDC_GENSTATIC:
1364 case IDC_GENERATE:
cb2708d3 1365 topic = WINHELP_CTX_puttygen_generate; break;
0906628e 1366 case IDC_PKSTATIC:
1367 case IDC_KEYDISPLAY:
cb2708d3 1368 topic = WINHELP_CTX_puttygen_pastekey; break;
0906628e 1369 case IDC_FPSTATIC:
1370 case IDC_FINGERPRINT:
cb2708d3 1371 topic = WINHELP_CTX_puttygen_fingerprint; break;
0906628e 1372 case IDC_COMMENTSTATIC:
1373 case IDC_COMMENTEDIT:
cb2708d3 1374 topic = WINHELP_CTX_puttygen_comment; break;
0906628e 1375 case IDC_PASSPHRASE1STATIC:
1376 case IDC_PASSPHRASE1EDIT:
1377 case IDC_PASSPHRASE2STATIC:
1378 case IDC_PASSPHRASE2EDIT:
cb2708d3 1379 topic = WINHELP_CTX_puttygen_passphrase; break;
0906628e 1380 case IDC_LOADSTATIC:
1381 case IDC_LOAD:
cb2708d3 1382 topic = WINHELP_CTX_puttygen_load; break;
0906628e 1383 case IDC_SAVESTATIC:
1384 case IDC_SAVE:
cb2708d3 1385 topic = WINHELP_CTX_puttygen_savepriv; break;
0906628e 1386 case IDC_SAVEPUB:
cb2708d3 1387 topic = WINHELP_CTX_puttygen_savepub; break;
0906628e 1388 case IDC_TYPESTATIC:
1389 case IDC_KEYSSH1:
1390 case IDC_KEYSSH2RSA:
1391 case IDC_KEYSSH2DSA:
cb2708d3 1392 topic = WINHELP_CTX_puttygen_keytype; break;
0906628e 1393 case IDC_BITSSTATIC:
1394 case IDC_BITS:
cb2708d3 1395 topic = WINHELP_CTX_puttygen_bits; break;
b723338c 1396 case IDC_IMPORT:
9d07b2db 1397 case IDC_EXPORT_OPENSSH:
1398 case IDC_EXPORT_SSHCOM:
cb2708d3 1399 topic = WINHELP_CTX_puttygen_conversions; break;
0906628e 1400 }
a9dc49af 1401 if (topic) {
cb2708d3 1402 launch_help(hwnd, topic);
0906628e 1403 } else {
1404 MessageBeep(0);
1405 }
1406 }
1407 break;
6e522441 1408 case WM_CLOSE:
1e5eefb6 1409 state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
32874aea 1410 sfree(state);
cb2708d3 1411 quit_help(hwnd);
32874aea 1412 EndDialog(hwnd, 1);
6e522441 1413 return 0;
1414 }
1415 return 0;
1416}
1417
cb2708d3 1418void cleanup_exit(int code)
1419{
1420 shutdown_help();
1421 exit(code);
1422}
93b581bd 1423
32874aea 1424int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1425{
1a418691 1426 int argc;
1427 char **argv;
cb2708d3 1428 int ret;
1a418691 1429
8b8cf871 1430 InitCommonControls();
6e522441 1431 hinst = inst;
28339579 1432 hwnd = NULL;
0906628e 1433
1434 /*
1435 * See if we can find our Help file.
1436 */
cb2708d3 1437 init_help();
0906628e 1438
2285d016 1439 split_into_argv(cmdline, &argc, &argv, NULL);
1440
1441 if (argc > 0) {
1442 if (!strcmp(argv[0], "-pgpfp")) {
1443 pgp_fingerprints();
1444 exit(1);
1445 } else {
1446 /*
1447 * Assume the first argument to be a private key file, and
1448 * attempt to load it.
1449 */
1450 cmdline_keyfile = argv[0];
1451 }
1452 }
1453
5d17ccfc 1454 random_ref();
cb2708d3 1455 ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK;
1456
1457 cleanup_exit(ret);
1458 return ret; /* just in case optimiser complains */
6e522441 1459}