Added export of ssh.com key files.
[sgt/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
9dda6459 549 menu1 = CreateMenu();
550 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
551 "Export &OpenSSH key");
552 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
553 "Export &ssh.com key");
554
555 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
556 "&Export");
9dda6459 557
558 menu1 = CreateMenu();
559 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
560 if (help_path)
561 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
562
563 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
564
565 SetMenu(hwnd, menu);
566 }
567
65a22376 568 /*
569 * Centre the window.
570 */
571 { /* centre the window */
572 RECT rs, rd;
573 HWND hw;
574
575 hw = GetDesktopWindow();
32874aea 576 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
577 MoveWindow(hwnd,
578 (rs.right + rs.left + rd.left - rd.right) / 2,
579 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
580 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 581 }
582
32874aea 583 state = smalloc(sizeof(*state));
584 state->generation_thread_exists = FALSE;
585 state->collecting_entropy = FALSE;
586 state->entropy = NULL;
587 state->key_exists = FALSE;
588 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
589 {
590 struct ctlpos cp, cp2;
6e522441 591
5c72ca61 592 /* Accelerators used: acglops1rbd */
a5470c60 593
e52455b1 594 ctlposinit(&cp, hwnd, 4, 4, 4);
32874aea 595 beginbox(&cp, "Key", IDC_BOX_KEY);
596 cp2 = cp;
ca68ebca 597 statictext(&cp2, "No key.", 1, IDC_NOKEY);
32874aea 598 cp2 = cp;
ca68ebca 599 statictext(&cp2, "", 1, IDC_GENERATING);
32874aea 600 progressbar(&cp2, IDC_PROGRESS);
601 bigeditctrl(&cp,
602 "&Public key for pasting into authorized_keys file:",
e52455b1 603 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
32874aea 604 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
605 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
606 IDC_FINGERPRINT, 75);
607 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
608 0);
609 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
610 IDC_COMMENTEDIT, 75);
611 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
612 IDC_PASSPHRASE1EDIT, 75);
613 staticpassedit(&cp, "C&onfirm passphrase:",
614 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
615 endbox(&cp);
616 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
617 staticbtn(&cp, "Generate a public/private key pair",
618 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
619 staticbtn(&cp, "Load an existing private key file",
620 IDC_LOADSTATIC, "&Load", IDC_LOAD);
af282e3b 621 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
622 "Save p&ublic key", IDC_SAVEPUB,
623 "&Save private key", IDC_SAVE);
32874aea 624 endbox(&cp);
625 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
5c72ca61 626 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
32874aea 627 "SSH&1 (RSA)", IDC_KEYSSH1,
5c72ca61 628 "SSH2 &RSA", IDC_KEYSSH2RSA,
629 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
32874aea 630 staticedit(&cp, "Number of &bits in a generated key:",
0c2986d0 631 IDC_BITSSTATIC, IDC_BITS, 20);
32874aea 632 endbox(&cp);
633 }
00060c72 634 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
0c2986d0 635 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
636
32874aea 637 /*
638 * Initially, hide the progress bar and the key display,
639 * and show the no-key display. Also disable the Save
af282e3b 640 * buttons, because with no key we obviously can't save
32874aea 641 * anything.
642 */
643 hidemany(hwnd, nokey_ids, FALSE);
644 hidemany(hwnd, generating_ids, TRUE);
645 hidemany(hwnd, gotkey_ids, TRUE);
646 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
af282e3b 647 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
6e522441 648
649 return 1;
650 case WM_MOUSEMOVE:
32874aea 651 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
652 if (state->collecting_entropy &&
653 state->entropy && state->entropy_got < state->entropy_required) {
654 state->entropy[state->entropy_got++] = lParam;
655 state->entropy[state->entropy_got++] = GetMessageTime();
656 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
657 state->entropy_got, 0);
658 if (state->entropy_got >= state->entropy_required) {
659 struct rsa_key_thread_params *params;
660 DWORD threadid;
661
662 /*
663 * Seed the entropy pool
664 */
665 random_add_heavynoise(state->entropy, state->entropy_size);
666 memset(state->entropy, 0, state->entropy_size);
667 sfree(state->entropy);
668 state->collecting_entropy = FALSE;
669
670 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
671 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
672 MAKELPARAM(0, PROGRESSRANGE));
673 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
674
675 params = smalloc(sizeof(*params));
676 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
677 params->dialog = hwnd;
0c2986d0 678 params->keysize = state->keysize;
5c72ca61 679 params->is_dsa = state->is_dsa;
32874aea 680 params->key = &state->key;
5c72ca61 681 params->dsskey = &state->dsskey;
32874aea 682
683 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
684 params, 0, &threadid)) {
685 MessageBox(hwnd, "Out of thread resources",
686 "Key generation error",
687 MB_OK | MB_ICONERROR);
688 sfree(params);
689 } else {
690 state->generation_thread_exists = TRUE;
691 }
692 }
693 }
694 break;
6e522441 695 case WM_COMMAND:
696 switch (LOWORD(wParam)) {
697 case IDC_COMMENTEDIT:
698 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 699 state = (struct MainDlgState *)
700 GetWindowLong(hwnd, GWL_USERDATA);
701 if (state->key_exists) {
702 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
703 int len = GetWindowTextLength(editctl);
704 if (*state->commentptr)
705 sfree(*state->commentptr);
706 *state->commentptr = smalloc(len + 1);
707 GetWindowText(editctl, *state->commentptr, len + 1);
2e8324fc 708 if (state->ssh2) {
af282e3b 709 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 710 &state->ssh2key);
2e8324fc 711 } else {
af282e3b 712 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
713 &state->key);
2e8324fc 714 }
715 }
716 }
6e522441 717 break;
718 case IDC_ABOUT:
719 EnableWindow(hwnd, 0);
32874aea 720 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
6e522441 721 EnableWindow(hwnd, 1);
32874aea 722 SetActiveWindow(hwnd);
6e522441 723 return 0;
9dda6459 724 case IDC_GIVEHELP:
725 if (HIWORD(wParam) == BN_CLICKED ||
726 HIWORD(wParam) == BN_DOUBLECLICKED) {
727 if (help_path) {
728 WinHelp(hwnd, help_path, HELP_COMMAND,
729 (DWORD)"JI(`',`puttygen.general')");
730 requested_help = TRUE;
731 }
732 }
733 return 0;
32874aea 734 case IDC_GENERATE:
735 state =
736 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
737 if (!state->generation_thread_exists) {
738 BOOL ok;
739 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
740 if (!ok)
741 state->keysize = DEFAULT_KEYSIZE;
65a22376 742 /* If we ever introduce a new key type, check it here! */
743 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 744 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 745 if (state->keysize < 256) {
746 int ret = MessageBox(hwnd,
747 "PuTTYgen will not generate a key"
748 " smaller than 256 bits.\n"
749 "Key length reset to 256. Continue?",
750 "PuTTYgen Warning",
751 MB_ICONWARNING | MB_OKCANCEL);
752 if (ret != IDOK)
753 break;
754 state->keysize = 256;
755 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
756 }
757 hidemany(hwnd, nokey_ids, TRUE);
758 hidemany(hwnd, generating_ids, FALSE);
759 hidemany(hwnd, gotkey_ids, TRUE);
760 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
761 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
762 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
af282e3b 763 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
764 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
765 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
00060c72 766 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
af282e3b 767 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
32874aea 768 state->key_exists = FALSE;
769 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
770 state->collecting_entropy = TRUE;
771
772 /*
773 * My brief statistical tests on mouse movements
774 * suggest that there are about 2.5 bits of
775 * randomness in the x position, 2.5 in the y
776 * position, and 1.7 in the message time, making
777 * 5.7 bits of unpredictability per mouse movement.
778 * However, other people have told me it's far less
779 * than that, so I'm going to be stupidly cautious
780 * and knock that down to a nice round 2. With this
781 * method, we require two words per mouse movement,
782 * so with 2 bits per mouse movement we expect 2
783 * bits every 2 words.
784 */
785 state->entropy_required = (state->keysize / 2) * 2;
786 state->entropy_got = 0;
787 state->entropy_size = (state->entropy_required *
788 sizeof(*state->entropy));
789 state->entropy = smalloc(state->entropy_size);
790
791 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
792 MAKELPARAM(0, state->entropy_required));
793 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
794 }
795 break;
796 case IDC_SAVE:
4801c01c 797 case IDC_EXPORT_OPENSSH:
798 case IDC_EXPORT_SSHCOM:
32874aea 799 state =
800 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
801 if (state->key_exists) {
802 char filename[FILENAME_MAX];
803 char passphrase[PASSPHRASE_MAXLEN];
804 char passphrase2[PASSPHRASE_MAXLEN];
4801c01c 805 int type, realtype;
806
807 if (state->ssh2)
808 realtype = SSH_KEYTYPE_SSH2;
809 else
810 realtype = SSH_KEYTYPE_SSH1;
811
812 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
813 type = SSH_KEYTYPE_OPENSSH;
814 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
815 type = SSH_KEYTYPE_SSHCOM;
816 else
817 type = realtype;
818
819 if (type != realtype &&
820 import_target_type(type) != realtype) {
821 char msg[256];
822 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
823 " format", (state->ssh2 ? 2 : 1),
824 (state->ssh2 ? 1 : 2));
825 MessageBox(hwnd, msg,
826 "PuTTYgen Error", MB_OK | MB_ICONERROR);
827 break;
828 }
829
32874aea 830 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
831 passphrase, sizeof(passphrase));
832 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
833 passphrase2, sizeof(passphrase2));
a5470c60 834 if (strcmp(passphrase, passphrase2)) {
32874aea 835 MessageBox(hwnd,
a5470c60 836 "The two passphrases given do not match.",
32874aea 837 "PuTTYgen Error", MB_OK | MB_ICONERROR);
a5470c60 838 break;
839 }
32874aea 840 if (!*passphrase) {
841 int ret;
842 ret = MessageBox(hwnd,
843 "Are you sure you want to save this key\n"
844 "without a passphrase to protect it?",
845 "PuTTYgen Warning",
846 MB_YESNO | MB_ICONWARNING);
847 if (ret != IDYES)
848 break;
849 }
850 if (prompt_keyfile(hwnd, "Save private key as:",
851 filename, 1)) {
bdea0743 852 int ret;
18790478 853 FILE *fp = fopen(filename, "r");
854 if (fp) {
32874aea 855 char buffer[FILENAME_MAX + 80];
18790478 856 fclose(fp);
857 sprintf(buffer, "Overwrite existing file\n%.*s?",
858 FILENAME_MAX, filename);
859 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
860 MB_YESNO | MB_ICONWARNING);
861 if (ret != IDYES)
862 break;
863 }
4801c01c 864
65a22376 865 if (state->ssh2) {
4801c01c 866 if (type != realtype)
867 ret = export_ssh2(filename, type, &state->ssh2key,
868 *passphrase ? passphrase : NULL);
869 else
870 ret = ssh2_save_userkey(filename, &state->ssh2key,
871 *passphrase ? passphrase :
872 NULL);
65a22376 873 } else {
4801c01c 874 if (type != realtype)
875 ret = export_ssh1(filename, type, &state->key,
876 *passphrase ? passphrase : NULL);
877 else
878 ret = saversakey(filename, &state->key,
879 *passphrase ? passphrase : NULL);
65a22376 880 }
bdea0743 881 if (ret <= 0) {
882 MessageBox(hwnd, "Unable to save key file",
32874aea 883 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 884 }
32874aea 885 }
886 }
887 break;
af282e3b 888 case IDC_SAVEPUB:
889 state =
890 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
891 if (state->key_exists) {
892 char filename[FILENAME_MAX];
893 if (prompt_keyfile(hwnd, "Save public key as:",
894 filename, 1)) {
895 int ret;
896 FILE *fp = fopen(filename, "r");
897 if (fp) {
898 char buffer[FILENAME_MAX + 80];
899 fclose(fp);
900 sprintf(buffer, "Overwrite existing file\n%.*s?",
901 FILENAME_MAX, filename);
902 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
903 MB_YESNO | MB_ICONWARNING);
904 if (ret != IDYES)
905 break;
906 }
907 if (state->ssh2) {
908 ret = save_ssh2_pubkey(filename, &state->ssh2key);
909 } else {
910 ret = save_ssh1_pubkey(filename, &state->key);
911 }
912 if (ret <= 0) {
913 MessageBox(hwnd, "Unable to save key file",
914 "PuTTYgen Error", MB_OK | MB_ICONERROR);
915 }
916 }
917 }
918 break;
32874aea 919 case IDC_LOAD:
920 state =
921 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
922 if (!state->generation_thread_exists) {
923 char filename[FILENAME_MAX];
924 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
925 char passphrase[PASSPHRASE_MAXLEN];
926 int needs_pass;
9dda6459 927 int type, realtype;
32874aea 928 int ret;
929 char *comment;
930 struct PassphraseProcStruct pps;
931 struct RSAKey newkey1;
2d466ffd 932 struct ssh2_userkey *newkey2 = NULL;
65a22376 933
9dda6459 934 type = realtype = key_type(filename);
935 if (type != SSH_KEYTYPE_SSH1 &&
936 type != SSH_KEYTYPE_SSH2 &&
937 !import_possible(type)) {
231ee168 938 char msg[256];
939 sprintf(msg, "Couldn't load private key (%s)",
940 key_type_to_str(type));
941 MessageBox(NULL, msg,
32874aea 942 "PuTTYgen Error", MB_OK | MB_ICONERROR);
65a22376 943 break;
944 }
6e522441 945
9dda6459 946 if (type != SSH_KEYTYPE_SSH1 &&
947 type != SSH_KEYTYPE_SSH2) {
948 realtype = type;
949 type = import_target_type(type);
950 }
951
65a22376 952 comment = NULL;
9dda6459 953 if (realtype == SSH_KEYTYPE_SSH1)
65a22376 954 needs_pass = rsakey_encrypted(filename, &comment);
9dda6459 955 else if (realtype == SSH_KEYTYPE_SSH2)
32874aea 956 needs_pass =
957 ssh2_userkey_encrypted(filename, &comment);
9dda6459 958 else
959 needs_pass = import_encrypted(filename, realtype,
960 &comment);
32874aea 961 pps.passphrase = passphrase;
962 pps.comment = comment;
963 do {
964 if (needs_pass) {
965 int dlgret;
966 dlgret = DialogBoxParam(hinst,
967 MAKEINTRESOURCE(210),
968 NULL, PassphraseProc,
969 (LPARAM) & pps);
970 if (!dlgret) {
971 ret = -2;
972 break;
973 }
974 } else
975 *passphrase = '\0';
9dda6459 976 if (type == SSH_KEYTYPE_SSH1) {
977 if (realtype == type)
978 ret = loadrsakey(filename, &newkey1,
979 passphrase);
980 else
981 ret = import_ssh1(filename, realtype,
982 &newkey1, passphrase);
983 } else {
984 if (realtype == type)
985 newkey2 = ssh2_load_userkey(filename,
986 passphrase);
987 else
988 newkey2 = import_ssh2(filename, realtype,
989 passphrase);
65a22376 990 if (newkey2 == SSH2_WRONG_PASSPHRASE)
991 ret = -1;
992 else if (!newkey2)
993 ret = 0;
994 else
995 ret = 1;
996 }
32874aea 997 } while (ret == -1);
998 if (comment)
999 sfree(comment);
1000 if (ret == 0) {
1001 MessageBox(NULL, "Couldn't load private key.",
1002 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1003 } else if (ret == 1) {
1004 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1005 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1006 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
af282e3b 1007 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1008 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1009 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
00060c72 1010 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
af282e3b 1011 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
32874aea 1012 /*
1013 * Now update the key controls with all the
1014 * key data.
1015 */
1016 {
1017 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1018 passphrase);
1019 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1020 passphrase);
231ee168 1021 if (type == SSH_KEYTYPE_SSH1) {
65a22376 1022 char buf[128];
1023 char *savecomment;
1024
1025 state->ssh2 = FALSE;
1026 state->commentptr = &state->key.comment;
1027 state->key = newkey1;
1028
1029 /*
1030 * Set the key fingerprint.
1031 */
1032 savecomment = state->key.comment;
32874aea 1033 state->key.comment = NULL;
1034 rsa_fingerprint(buf, sizeof(buf),
1035 &state->key);
1036 state->key.comment = savecomment;
65a22376 1037
1038 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1039 /*
1040 * Construct a decimal representation
1041 * of the key, for pasting into
1042 * .ssh/authorized_keys on a Unix box.
1043 */
32874aea 1044 setupbigedit1(hwnd, IDC_KEYDISPLAY,
af282e3b 1045 IDC_PKSTATIC, &state->key);
65a22376 1046 } else {
1047 char *fp;
1048 char *savecomment;
1049
1050 state->ssh2 = TRUE;
32874aea 1051 state->commentptr =
1052 &state->ssh2key.comment;
1053 state->ssh2key = *newkey2; /* structure copy */
65a22376 1054 sfree(newkey2);
1055
1056 savecomment = state->ssh2key.comment;
32874aea 1057 state->ssh2key.comment = NULL;
1058 fp =
1059 state->ssh2key.alg->
1060 fingerprint(state->ssh2key.data);
1061 state->ssh2key.comment = savecomment;
65a22376 1062
1063 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1064 sfree(fp);
1065
32874aea 1066 setupbigedit2(hwnd, IDC_KEYDISPLAY,
af282e3b 1067 IDC_PKSTATIC, &state->ssh2key);
65a22376 1068 }
32874aea 1069 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1070 *state->commentptr);
1071 }
1072 /*
1073 * Finally, hide the progress bar and show
1074 * the key data.
1075 */
1076 hidemany(hwnd, nokey_ids, TRUE);
1077 hidemany(hwnd, generating_ids, TRUE);
1078 hidemany(hwnd, gotkey_ids, FALSE);
1079 state->key_exists = TRUE;
1080 }
1081 }
1082 }
1083 break;
6e522441 1084 }
1085 return 0;
1086 case WM_DONEKEY:
32874aea 1087 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1088 state->generation_thread_exists = FALSE;
1089 state->key_exists = TRUE;
5c72ca61 1090 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1091 MAKELPARAM(0, PROGRESSRANGE));
1092 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
32874aea 1093 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
1094 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
1095 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
af282e3b 1096 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
1097 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
1098 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
00060c72 1099 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
af282e3b 1100 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
3b4c5899 1101 if (state->ssh2) {
5c72ca61 1102 if (state->is_dsa) {
1103 state->ssh2key.data = &state->dsskey;
1104 state->ssh2key.alg = &ssh_dss;
1105 } else {
1106 state->ssh2key.data = &state->key;
1107 state->ssh2key.alg = &ssh_rsa;
1108 }
65a22376 1109 state->commentptr = &state->ssh2key.comment;
3b4c5899 1110 } else {
65a22376 1111 state->commentptr = &state->key.comment;
3b4c5899 1112 }
32874aea 1113 /*
1114 * Invent a comment for the key. We'll do this by including
1115 * the date in it. This will be so horrifyingly ugly that
1116 * the user will immediately want to change it, which is
1117 * what we want :-)
1118 */
1119 *state->commentptr = smalloc(30);
1120 {
1121 time_t t;
1122 struct tm *tm;
1123 time(&t);
1124 tm = localtime(&t);
5c72ca61 1125 if (state->is_dsa)
1126 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1127 else
1128 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
32874aea 1129 }
1130
1131 /*
1132 * Now update the key controls with all the key data.
1133 */
1134 {
65a22376 1135 char *savecomment;
32874aea 1136 /*
1137 * Blank passphrase, initially. This isn't dangerous,
1138 * because we will warn (Are You Sure?) before allowing
1139 * the user to save an unprotected private key.
1140 */
1141 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1142 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1143 /*
1144 * Set the comment.
1145 */
1146 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1147 /*
1148 * Set the key fingerprint.
1149 */
65a22376 1150 savecomment = *state->commentptr;
1151 *state->commentptr = NULL;
32874aea 1152 if (state->ssh2) {
65a22376 1153 char *fp;
32874aea 1154 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1155 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1156 sfree(fp);
1157 } else {
1158 char buf[128];
32874aea 1159 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1160 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1161 }
65a22376 1162 *state->commentptr = savecomment;
32874aea 1163 /*
1164 * Construct a decimal representation of the key, for
af282e3b 1165 * pasting into .ssh/authorized_keys or
1166 * .ssh/authorized_keys2 on a Unix box.
32874aea 1167 */
65a22376 1168 if (state->ssh2) {
af282e3b 1169 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1170 IDC_PKSTATIC, &state->ssh2key);
65a22376 1171 } else {
af282e3b 1172 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1173 IDC_PKSTATIC, &state->key);
65a22376 1174 }
32874aea 1175 }
1176 /*
1177 * Finally, hide the progress bar and show the key data.
1178 */
1179 hidemany(hwnd, nokey_ids, TRUE);
1180 hidemany(hwnd, generating_ids, TRUE);
1181 hidemany(hwnd, gotkey_ids, FALSE);
1182 break;
0906628e 1183 case WM_HELP:
1184 if (help_path) {
1185 int id = ((LPHELPINFO)lParam)->iCtrlId;
1186 char *cmd = NULL;
1187 switch (id) {
1188 case IDC_GENERATING:
1189 case IDC_PROGRESS:
1190 case IDC_GENSTATIC:
1191 case IDC_GENERATE:
1192 cmd = "JI(`',`puttygen.generate')"; break;
1193 case IDC_PKSTATIC:
1194 case IDC_KEYDISPLAY:
1195 cmd = "JI(`',`puttygen.pastekey')"; break;
1196 case IDC_FPSTATIC:
1197 case IDC_FINGERPRINT:
1198 cmd = "JI(`',`puttygen.fingerprint')"; break;
1199 case IDC_COMMENTSTATIC:
1200 case IDC_COMMENTEDIT:
1201 cmd = "JI(`',`puttygen.comment')"; break;
1202 case IDC_PASSPHRASE1STATIC:
1203 case IDC_PASSPHRASE1EDIT:
1204 case IDC_PASSPHRASE2STATIC:
1205 case IDC_PASSPHRASE2EDIT:
1206 cmd = "JI(`',`puttygen.passphrase')"; break;
1207 case IDC_LOADSTATIC:
1208 case IDC_LOAD:
1209 cmd = "JI(`',`puttygen.load')"; break;
1210 case IDC_SAVESTATIC:
1211 case IDC_SAVE:
1212 cmd = "JI(`',`puttygen.savepriv')"; break;
1213 case IDC_SAVEPUB:
1214 cmd = "JI(`',`puttygen.savepub')"; break;
1215 case IDC_TYPESTATIC:
1216 case IDC_KEYSSH1:
1217 case IDC_KEYSSH2RSA:
1218 case IDC_KEYSSH2DSA:
1219 cmd = "JI(`',`puttygen.keytype')"; break;
1220 case IDC_BITSSTATIC:
1221 case IDC_BITS:
1222 cmd = "JI(`',`puttygen.bits')"; break;
1223 }
1224 if (cmd) {
1225 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1226 requested_help = TRUE;
1227 } else {
1228 MessageBeep(0);
1229 }
1230 }
1231 break;
6e522441 1232 case WM_CLOSE:
32874aea 1233 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1234 sfree(state);
0906628e 1235 if (requested_help) {
1236 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1237 requested_help = FALSE;
1238 }
32874aea 1239 EndDialog(hwnd, 1);
6e522441 1240 return 0;
1241 }
1242 return 0;
1243}
1244
93b581bd 1245void cleanup_exit(int code) { exit(code); }
1246
32874aea 1247int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1248{
8b8cf871 1249 InitCommonControls();
6e522441 1250 hinst = inst;
0906628e 1251
1252 /*
1253 * See if we can find our Help file.
1254 */
1255 {
1256 char b[2048], *p, *q, *r;
1257 FILE *fp;
1258 GetModuleFileName(NULL, b, sizeof(b) - 1);
1259 r = b;
1260 p = strrchr(b, '\\');
1261 if (p && p >= r) r = p+1;
1262 q = strrchr(b, ':');
1263 if (q && q >= r) r = q+1;
1264 strcpy(r, "putty.hlp");
1265 if ( (fp = fopen(b, "r")) != NULL) {
1266 help_path = dupstr(b);
1267 fclose(fp);
1268 } else
1269 help_path = NULL;
1270 }
1271
6e522441 1272 random_init();
32874aea 1273 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1274 MainDlgProc) != IDOK;
6e522441 1275}