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