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