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