Oops. That is to say, only loading and saving of PuTTY private keys
[sgt/putty] / puttygen.c
CommitLineData
6e522441 1/*
2 * PuTTY key generation front end.
3 */
4
5#include <windows.h>
6#include <commctrl.h>
7#include <time.h>
6e522441 8#include <stdio.h>
49bad831 9#include <stdlib.h>
6e522441 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
0c2986d0 19#define DEFAULT_KEYSIZE 1024
6e522441 20
0906628e 21static int requested_help;
22
6e522441 23/* ----------------------------------------------------------------------
24 * Progress report code. This is really horrible :-)
25 */
5c72ca61 26#define PROGRESSRANGE 65535
27#define MAXPHASE 5
6e522441 28struct progress {
5c72ca61 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;
6e522441 37 HWND progbar;
38};
39
5c72ca61 40static void progress_update(void *param, int action, int phase, int iprogress)
32874aea 41{
42 struct progress *p = (struct progress *) param;
6e522441 43 unsigned progress = iprogress;
44 int position;
45
5c72ca61 46 if (action < PROGFN_READY && p->nphases < phase)
47 p->nphases = phase;
48 switch (action) {
7c7f6893 49 case PROGFN_INITIALISE:
50 p->nphases = 0;
51 break;
5c72ca61 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;
32874aea 61 break;
5c72ca61 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));
32874aea 77 }
32874aea 78 break;
5c72ca61 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);
32874aea 93 break;
6e522441 94 }
6e522441 95}
96
97extern char ver[];
98
99#define PASSPHRASE_MAXLEN 512
100
101struct PassphraseProcStruct {
102 char *passphrase;
103 char *comment;
104};
105
106/*
107 * Dialog-box function for the passphrase box.
108 */
109static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
32874aea 110 WPARAM wParam, LPARAM lParam)
111{
8e1feb27 112 static char *passphrase = NULL;
6e522441 113 struct PassphraseProcStruct *p;
114
115 switch (msg) {
116 case WM_INITDIALOG:
32874aea 117 SetForegroundWindow(hwnd);
118 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
119 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
65a22376 120
121 /*
122 * Centre the window.
123 */
124 { /* centre the window */
125 RECT rs, rd;
126 HWND hw;
127
128 hw = GetDesktopWindow();
32874aea 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);
65a22376 134 }
135
32874aea 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;
6e522441 143 case WM_COMMAND:
144 switch (LOWORD(wParam)) {
145 case IDOK:
146 if (*passphrase)
32874aea 147 EndDialog(hwnd, 1);
6e522441 148 else
32874aea 149 MessageBeep(0);
6e522441 150 return 0;
151 case IDCANCEL:
32874aea 152 EndDialog(hwnd, 0);
6e522441 153 return 0;
32874aea 154 case 102: /* edit box */
8e1feb27 155 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
32874aea 156 GetDlgItemText(hwnd, 102, passphrase,
157 PASSPHRASE_MAXLEN - 1);
158 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
159 }
160 return 0;
6e522441 161 }
162 return 0;
163 case WM_CLOSE:
32874aea 164 EndDialog(hwnd, 0);
6e522441 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 */
174static int prompt_keyfile(HWND hwnd, char *dlgtitle,
06980e9d 175 char *filename, int save, int ppk)
32874aea 176{
6e522441 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;
06980e9d 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 }
6e522441 191 of.lpstrCustomFilter = NULL;
192 of.nFilterIndex = 1;
32874aea 193 of.lpstrFile = filename;
194 *filename = '\0';
6e522441 195 of.nMaxFile = FILENAME_MAX;
196 of.lpstrFileTitle = NULL;
197 of.lpstrInitialDir = NULL;
198 of.lpstrTitle = dlgtitle;
199 of.Flags = 0;
200 if (save)
32874aea 201 return GetSaveFileName(&of);
6e522441 202 else
32874aea 203 return GetOpenFileName(&of);
6e522441 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 */
32874aea 210void logevent(char *msg)
211{
6e522441 212}
213
214/*
215 * Dialog-box function for the Licence box.
216 */
32874aea 217static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
218 WPARAM wParam, LPARAM lParam)
219{
6e522441 220 switch (msg) {
221 case WM_INITDIALOG:
65a22376 222 /*
223 * Centre the window.
224 */
225 { /* centre the window */
226 RECT rs, rd;
227 HWND hw;
228
229 hw = GetDesktopWindow();
32874aea 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);
65a22376 235 }
236
6e522441 237 return 1;
238 case WM_COMMAND:
239 switch (LOWORD(wParam)) {
240 case IDOK:
32874aea 241 EndDialog(hwnd, 1);
6e522441 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 */
32874aea 255static int CALLBACK AboutProc(HWND hwnd, UINT msg,
256 WPARAM wParam, LPARAM lParam)
257{
6e522441 258 switch (msg) {
259 case WM_INITDIALOG:
65a22376 260 /*
261 * Centre the window.
262 */
263 { /* centre the window */
264 RECT rs, rd;
265 HWND hw;
266
267 hw = GetDesktopWindow();
32874aea 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);
65a22376 273 }
274
32874aea 275 SetDlgItemText(hwnd, 100, ver);
6e522441 276 return 1;
277 case WM_COMMAND:
278 switch (LOWORD(wParam)) {
279 case IDOK:
32874aea 280 EndDialog(hwnd, 1);
6e522441 281 return 0;
282 case 101:
283 EnableWindow(hwnd, 0);
32874aea 284 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
6e522441 285 EnableWindow(hwnd, 1);
32874aea 286 SetActiveWindow(hwnd);
6e522441 287 return 0;
288 }
289 return 0;
290 case WM_CLOSE:
32874aea 291 EndDialog(hwnd, 1);
6e522441 292 return 0;
293 }
294 return 0;
295}
296
297/*
298 * Thread to generate a key.
299 */
300struct rsa_key_thread_params {
32874aea 301 HWND progressbar; /* notify this with progress */
302 HWND dialog; /* notify this on completion */
0c2986d0 303 int keysize; /* bits in key */
5c72ca61 304 int is_dsa;
6e522441 305 struct RSAKey *key;
5c72ca61 306 struct dss_key *dsskey;
6e522441 307};
32874aea 308static DWORD WINAPI generate_rsa_key_thread(void *param)
309{
6e522441 310 struct rsa_key_thread_params *params =
32874aea 311 (struct rsa_key_thread_params *) param;
6e522441 312 struct progress prog;
313 prog.progbar = params->progressbar;
314
7c7f6893 315 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
316
5c72ca61 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);
6e522441 321
322 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
323
dcbde236 324 sfree(params);
6e522441 325 return 0;
326}
327
328struct MainDlgState {
329 int collecting_entropy;
330 int generation_thread_exists;
331 int key_exists;
332 int entropy_got, entropy_required, entropy_size;
0c2986d0 333 int keysize;
5c72ca61 334 int ssh2, is_dsa;
65a22376 335 char **commentptr; /* points to key.comment or ssh2key.comment */
336 struct ssh2_userkey ssh2key;
6e522441 337 unsigned *entropy;
338 struct RSAKey key;
5c72ca61 339 struct dss_key dsskey;
b723338c 340 HMENU filemenu, keymenu, cvtmenu;
6e522441 341};
342
32874aea 343static void hidemany(HWND hwnd, const int *ids, int hideit)
344{
6e522441 345 while (*ids) {
32874aea 346 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
6e522441 347 }
348}
349
af282e3b 350static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
32874aea 351{
6e522441 352 char *buffer;
353 char *dec1, *dec2;
354
355 dec1 = bignum_decimal(key->exponent);
356 dec2 = bignum_decimal(key->modulus);
32874aea 357 buffer = smalloc(strlen(dec1) + strlen(dec2) +
358 strlen(key->comment) + 30);
6e522441 359 sprintf(buffer, "%d %s %s %s",
32874aea 360 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
6e522441 361 SetDlgItemText(hwnd, id, buffer);
af282e3b 362 SetDlgItemText(hwnd, idstatic,
363 "&Public key for pasting into authorized_keys file:");
dcbde236 364 sfree(dec1);
365 sfree(dec2);
366 sfree(buffer);
6e522441 367}
368
af282e3b 369static void setupbigedit2(HWND hwnd, int id, int idstatic,
370 struct ssh2_userkey *key)
32874aea 371{
65a22376 372 unsigned char *pub_blob;
373 char *buffer, *p;
44fb739e 374 int pub_len;
65a22376 375 int i;
376
377 pub_blob = key->alg->public_blob(key->data, &pub_len);
32874aea 378 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
65a22376 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) {
32874aea 385 int n = (pub_len - i < 3 ? pub_len - i : 3);
386 base64_encode_atom(pub_blob + i, n, p);
65a22376 387 i += n;
388 p += 4;
389 }
390 *p++ = ' ';
391 strcpy(p, key->comment);
392 SetDlgItemText(hwnd, id, buffer);
af282e3b 393 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
394 "OpenSSH authorized_keys2 file:");
65a22376 395 sfree(pub_blob);
32874aea 396 sfree(buffer);
65a22376 397}
398
af282e3b 399static 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
7bedb13c 417/*
418 * Warn about the obsolescent key file format.
419 */
420void 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
af282e3b 437static 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
9d07b2db 484enum {
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
509static const int nokey_ids[] = { IDC_NOKEY, 0 };
510static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
511static 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 */
523void 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);
b723338c 547 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
548 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
9d07b2db 549 MF_GRAYED|MF_BYCOMMAND);
b723338c 550 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
9d07b2db 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);
b723338c 572 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
573 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
9d07b2db 574 MF_GRAYED|MF_BYCOMMAND);
b723338c 575 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
9d07b2db 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);
b723338c 597 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
9d07b2db 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) \
b723338c 604 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
9d07b2db 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
6e522441 613/*
614 * Dialog-box function for the main PuTTYgen dialog box.
615 */
32874aea 616static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
617 WPARAM wParam, LPARAM lParam)
618{
6e522441 619 static const char generating_msg[] =
32874aea 620 "Please wait while a key is generated...";
6e522441 621 static const char entropy_msg[] =
32874aea 622 "Please generate some randomness by moving the mouse over the blank area.";
6e522441 623 struct MainDlgState *state;
624
625 switch (msg) {
626 case WM_INITDIALOG:
0906628e 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
9d07b2db 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);
9dda6459 644 {
645 HMENU menu, menu1;
646
647 menu = CreateMenu();
648
649 menu1 = CreateMenu();
9dda6459 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");
9d07b2db 653 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
654 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
9dda6459 655 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
9d07b2db 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;
9dda6459 666
9dda6459 667 menu1 = CreateMenu();
b723338c 668 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
669 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
9dda6459 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");
9dda6459 674 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
b723338c 675 "&Conversions");
676 state->cvtmenu = menu1;
9dda6459 677
678 menu1 = CreateMenu();
679 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
680 if (help_path)
681 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
9dda6459 682 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
683
684 SetMenu(hwnd, menu);
685 }
686
65a22376 687 /*
688 * Centre the window.
689 */
690 { /* centre the window */
691 RECT rs, rd;
692 HWND hw;
693
694 hw = GetDesktopWindow();
32874aea 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);
65a22376 700 }
701
32874aea 702 {
703 struct ctlpos cp, cp2;
6e522441 704
5c72ca61 705 /* Accelerators used: acglops1rbd */
a5470c60 706
e52455b1 707 ctlposinit(&cp, hwnd, 4, 4, 4);
32874aea 708 beginbox(&cp, "Key", IDC_BOX_KEY);
709 cp2 = cp;
ca68ebca 710 statictext(&cp2, "No key.", 1, IDC_NOKEY);
32874aea 711 cp2 = cp;
ca68ebca 712 statictext(&cp2, "", 1, IDC_GENERATING);
32874aea 713 progressbar(&cp2, IDC_PROGRESS);
714 bigeditctrl(&cp,
715 "&Public key for pasting into authorized_keys file:",
e52455b1 716 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
32874aea 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);
af282e3b 734 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
735 "Save p&ublic key", IDC_SAVEPUB,
736 "&Save private key", IDC_SAVE);
32874aea 737 endbox(&cp);
738 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
5c72ca61 739 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
32874aea 740 "SSH&1 (RSA)", IDC_KEYSSH1,
5c72ca61 741 "SSH2 &RSA", IDC_KEYSSH2RSA,
742 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
32874aea 743 staticedit(&cp, "Number of &bits in a generated key:",
0c2986d0 744 IDC_BITSSTATIC, IDC_BITS, 20);
32874aea 745 endbox(&cp);
746 }
00060c72 747 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
9d07b2db 748 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
749 IDC_KEYSSH1, MF_BYCOMMAND);
0c2986d0 750 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
751
32874aea 752 /*
753 * Initially, hide the progress bar and the key display,
754 * and show the no-key display. Also disable the Save
af282e3b 755 * buttons, because with no key we obviously can't save
32874aea 756 * anything.
757 */
9d07b2db 758 ui_set_state(hwnd, state, 0);
6e522441 759
760 return 1;
761 case WM_MOUSEMOVE:
32874aea 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;
0c2986d0 789 params->keysize = state->keysize;
5c72ca61 790 params->is_dsa = state->is_dsa;
32874aea 791 params->key = &state->key;
5c72ca61 792 params->dsskey = &state->dsskey;
32874aea 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;
6e522441 806 case WM_COMMAND:
807 switch (LOWORD(wParam)) {
9d07b2db 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;
6e522441 824 case IDC_COMMENTEDIT:
825 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 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);
2e8324fc 835 if (state->ssh2) {
af282e3b 836 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 837 &state->ssh2key);
2e8324fc 838 } else {
af282e3b 839 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
840 &state->key);
2e8324fc 841 }
842 }
843 }
6e522441 844 break;
845 case IDC_ABOUT:
846 EnableWindow(hwnd, 0);
32874aea 847 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
6e522441 848 EnableWindow(hwnd, 1);
32874aea 849 SetActiveWindow(hwnd);
6e522441 850 return 0;
9dda6459 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;
32874aea 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;
65a22376 869 /* If we ever introduce a new key type, check it here! */
870 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 871 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 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 }
9d07b2db 884 ui_set_state(hwnd, state, 1);
32874aea 885 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
9d07b2db 886 state->key_exists = FALSE;
32874aea 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:
4801c01c 914 case IDC_EXPORT_OPENSSH:
915 case IDC_EXPORT_SSHCOM:
32874aea 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];
4801c01c 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
32874aea 947 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
948 passphrase, sizeof(passphrase));
949 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
950 passphrase2, sizeof(passphrase2));
a5470c60 951 if (strcmp(passphrase, passphrase2)) {
32874aea 952 MessageBox(hwnd,
a5470c60 953 "The two passphrases given do not match.",
32874aea 954 "PuTTYgen Error", MB_OK | MB_ICONERROR);
a5470c60 955 break;
956 }
32874aea 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:",
06980e9d 968 filename, 1, (type == realtype))) {
bdea0743 969 int ret;
18790478 970 FILE *fp = fopen(filename, "r");
971 if (fp) {
32874aea 972 char buffer[FILENAME_MAX + 80];
18790478 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 }
4801c01c 981
65a22376 982 if (state->ssh2) {
4801c01c 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);
65a22376 990 } else {
4801c01c 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);
65a22376 997 }
bdea0743 998 if (ret <= 0) {
999 MessageBox(hwnd, "Unable to save key file",
32874aea 1000 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 1001 }
32874aea 1002 }
1003 }
1004 break;
af282e3b 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:",
06980e9d 1011 filename, 1, 0)) {
af282e3b 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;
32874aea 1036 case IDC_LOAD:
b723338c 1037 case IDC_IMPORT:
32874aea 1038 state =
1039 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1040 if (!state->generation_thread_exists) {
1041 char filename[FILENAME_MAX];
06980e9d 1042 if (prompt_keyfile(hwnd, "Load private key:",
1043 filename, 0, LOWORD(wParam)==IDC_LOAD)) {
32874aea 1044 char passphrase[PASSPHRASE_MAXLEN];
1045 int needs_pass;
9dda6459 1046 int type, realtype;
32874aea 1047 int ret;
1048 char *comment;
1049 struct PassphraseProcStruct pps;
1050 struct RSAKey newkey1;
2d466ffd 1051 struct ssh2_userkey *newkey2 = NULL;
65a22376 1052
9dda6459 1053 type = realtype = key_type(filename);
1054 if (type != SSH_KEYTYPE_SSH1 &&
1055 type != SSH_KEYTYPE_SSH2 &&
1056 !import_possible(type)) {
231ee168 1057 char msg[256];
1058 sprintf(msg, "Couldn't load private key (%s)",
1059 key_type_to_str(type));
1060 MessageBox(NULL, msg,
32874aea 1061 "PuTTYgen Error", MB_OK | MB_ICONERROR);
65a22376 1062 break;
1063 }
6e522441 1064
9dda6459 1065 if (type != SSH_KEYTYPE_SSH1 &&
1066 type != SSH_KEYTYPE_SSH2) {
1067 realtype = type;
1068 type = import_target_type(type);
1069 }
1070
65a22376 1071 comment = NULL;
9dda6459 1072 if (realtype == SSH_KEYTYPE_SSH1)
65a22376 1073 needs_pass = rsakey_encrypted(filename, &comment);
9dda6459 1074 else if (realtype == SSH_KEYTYPE_SSH2)
32874aea 1075 needs_pass =
1076 ssh2_userkey_encrypted(filename, &comment);
9dda6459 1077 else
1078 needs_pass = import_encrypted(filename, realtype,
1079 &comment);
32874aea 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';
9dda6459 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);
65a22376 1109 if (newkey2 == SSH2_WRONG_PASSPHRASE)
1110 ret = -1;
1111 else if (!newkey2)
1112 ret = 0;
1113 else
1114 ret = 1;
1115 }
32874aea 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) {
32874aea 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);
231ee168 1132 if (type == SSH_KEYTYPE_SSH1) {
65a22376 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;
32874aea 1144 state->key.comment = NULL;
1145 rsa_fingerprint(buf, sizeof(buf),
1146 &state->key);
1147 state->key.comment = savecomment;
65a22376 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 */
32874aea 1155 setupbigedit1(hwnd, IDC_KEYDISPLAY,
af282e3b 1156 IDC_PKSTATIC, &state->key);
65a22376 1157 } else {
1158 char *fp;
1159 char *savecomment;
1160
1161 state->ssh2 = TRUE;
32874aea 1162 state->commentptr =
1163 &state->ssh2key.comment;
1164 state->ssh2key = *newkey2; /* structure copy */
65a22376 1165 sfree(newkey2);
1166
1167 savecomment = state->ssh2key.comment;
32874aea 1168 state->ssh2key.comment = NULL;
1169 fp =
1170 state->ssh2key.alg->
1171 fingerprint(state->ssh2key.data);
1172 state->ssh2key.comment = savecomment;
65a22376 1173
1174 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1175 sfree(fp);
1176
32874aea 1177 setupbigedit2(hwnd, IDC_KEYDISPLAY,
af282e3b 1178 IDC_PKSTATIC, &state->ssh2key);
65a22376 1179 }
32874aea 1180 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1181 *state->commentptr);
1182 }
1183 /*
1184 * Finally, hide the progress bar and show
1185 * the key data.
1186 */
9d07b2db 1187 ui_set_state(hwnd, state, 2);
32874aea 1188 state->key_exists = TRUE;
b723338c 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 }
32874aea 1207 }
1208 }
1209 }
1210 break;
6e522441 1211 }
1212 return 0;
1213 case WM_DONEKEY:
32874aea 1214 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1215 state->generation_thread_exists = FALSE;
1216 state->key_exists = TRUE;
5c72ca61 1217 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1218 MAKELPARAM(0, PROGRESSRANGE));
1219 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
3b4c5899 1220 if (state->ssh2) {
5c72ca61 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 }
65a22376 1228 state->commentptr = &state->ssh2key.comment;
3b4c5899 1229 } else {
65a22376 1230 state->commentptr = &state->key.comment;
3b4c5899 1231 }
32874aea 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);
5c72ca61 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);
32874aea 1248 }
1249
1250 /*
1251 * Now update the key controls with all the key data.
1252 */
1253 {
65a22376 1254 char *savecomment;
32874aea 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 */
65a22376 1269 savecomment = *state->commentptr;
1270 *state->commentptr = NULL;
32874aea 1271 if (state->ssh2) {
65a22376 1272 char *fp;
32874aea 1273 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1274 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1275 sfree(fp);
1276 } else {
1277 char buf[128];
32874aea 1278 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1279 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1280 }
65a22376 1281 *state->commentptr = savecomment;
32874aea 1282 /*
1283 * Construct a decimal representation of the key, for
af282e3b 1284 * pasting into .ssh/authorized_keys or
1285 * .ssh/authorized_keys2 on a Unix box.
32874aea 1286 */
65a22376 1287 if (state->ssh2) {
af282e3b 1288 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1289 IDC_PKSTATIC, &state->ssh2key);
65a22376 1290 } else {
af282e3b 1291 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1292 IDC_PKSTATIC, &state->key);
65a22376 1293 }
32874aea 1294 }
1295 /*
1296 * Finally, hide the progress bar and show the key data.
1297 */
9d07b2db 1298 ui_set_state(hwnd, state, 2);
32874aea 1299 break;
0906628e 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;
b723338c 1340 case IDC_IMPORT:
9d07b2db 1341 case IDC_EXPORT_OPENSSH:
1342 case IDC_EXPORT_SSHCOM:
b723338c 1343 cmd = "JI(`',`puttygen.conversions')"; break;
0906628e 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;
6e522441 1353 case WM_CLOSE:
32874aea 1354 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1355 sfree(state);
0906628e 1356 if (requested_help) {
1357 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1358 requested_help = FALSE;
1359 }
32874aea 1360 EndDialog(hwnd, 1);
6e522441 1361 return 0;
1362 }
1363 return 0;
1364}
1365
93b581bd 1366void cleanup_exit(int code) { exit(code); }
1367
32874aea 1368int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1369{
8b8cf871 1370 InitCommonControls();
6e522441 1371 hinst = inst;
0906628e 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
6e522441 1393 random_init();
32874aea 1394 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1395 MainDlgProc) != IDOK;
6e522441 1396}