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