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