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