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