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