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