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