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