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