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