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