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