Oops. Dirsep in Makefile.cyg should be / not \.
[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
1a418691 23static char *cmdline_keyfile = NULL;
24
6e522441 25/* ----------------------------------------------------------------------
26 * Progress report code. This is really horrible :-)
27 */
5c72ca61 28#define PROGRESSRANGE 65535
29#define MAXPHASE 5
6e522441 30struct progress {
5c72ca61 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;
6e522441 39 HWND progbar;
40};
41
5c72ca61 42static void progress_update(void *param, int action, int phase, int iprogress)
32874aea 43{
44 struct progress *p = (struct progress *) param;
6e522441 45 unsigned progress = iprogress;
46 int position;
47
5c72ca61 48 if (action < PROGFN_READY && p->nphases < phase)
49 p->nphases = phase;
50 switch (action) {
7c7f6893 51 case PROGFN_INITIALISE:
52 p->nphases = 0;
53 break;
5c72ca61 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;
32874aea 63 break;
5c72ca61 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));
32874aea 79 }
32874aea 80 break;
5c72ca61 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);
32874aea 95 break;
6e522441 96 }
6e522441 97}
98
99extern char ver[];
100
101#define PASSPHRASE_MAXLEN 512
102
103struct PassphraseProcStruct {
104 char *passphrase;
105 char *comment;
106};
107
108/*
109 * Dialog-box function for the passphrase box.
110 */
111static int CALLBACK PassphraseProc(HWND hwnd, UINT msg,
32874aea 112 WPARAM wParam, LPARAM lParam)
113{
8e1feb27 114 static char *passphrase = NULL;
6e522441 115 struct PassphraseProcStruct *p;
116
117 switch (msg) {
118 case WM_INITDIALOG:
32874aea 119 SetForegroundWindow(hwnd);
120 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
121 SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
65a22376 122
123 /*
124 * Centre the window.
125 */
126 { /* centre the window */
127 RECT rs, rd;
128 HWND hw;
129
130 hw = GetDesktopWindow();
32874aea 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);
65a22376 136 }
137
32874aea 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;
6e522441 145 case WM_COMMAND:
146 switch (LOWORD(wParam)) {
147 case IDOK:
148 if (*passphrase)
32874aea 149 EndDialog(hwnd, 1);
6e522441 150 else
32874aea 151 MessageBeep(0);
6e522441 152 return 0;
153 case IDCANCEL:
32874aea 154 EndDialog(hwnd, 0);
6e522441 155 return 0;
32874aea 156 case 102: /* edit box */
8e1feb27 157 if ((HIWORD(wParam) == EN_CHANGE) && passphrase) {
32874aea 158 GetDlgItemText(hwnd, 102, passphrase,
159 PASSPHRASE_MAXLEN - 1);
160 passphrase[PASSPHRASE_MAXLEN - 1] = '\0';
161 }
162 return 0;
6e522441 163 }
164 return 0;
165 case WM_CLOSE:
32874aea 166 EndDialog(hwnd, 0);
6e522441 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 */
176static int prompt_keyfile(HWND hwnd, char *dlgtitle,
06980e9d 177 char *filename, int save, int ppk)
32874aea 178{
6e522441 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;
06980e9d 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 }
6e522441 193 of.lpstrCustomFilter = NULL;
194 of.nFilterIndex = 1;
32874aea 195 of.lpstrFile = filename;
196 *filename = '\0';
6e522441 197 of.nMaxFile = FILENAME_MAX;
198 of.lpstrFileTitle = NULL;
199 of.lpstrInitialDir = NULL;
200 of.lpstrTitle = dlgtitle;
201 of.Flags = 0;
202 if (save)
32874aea 203 return GetSaveFileName(&of);
6e522441 204 else
32874aea 205 return GetOpenFileName(&of);
6e522441 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 */
32874aea 212void logevent(char *msg)
213{
6e522441 214}
215
216/*
217 * Dialog-box function for the Licence box.
218 */
32874aea 219static int CALLBACK LicenceProc(HWND hwnd, UINT msg,
220 WPARAM wParam, LPARAM lParam)
221{
6e522441 222 switch (msg) {
223 case WM_INITDIALOG:
65a22376 224 /*
225 * Centre the window.
226 */
227 { /* centre the window */
228 RECT rs, rd;
229 HWND hw;
230
231 hw = GetDesktopWindow();
32874aea 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);
65a22376 237 }
238
6e522441 239 return 1;
240 case WM_COMMAND:
241 switch (LOWORD(wParam)) {
242 case IDOK:
32874aea 243 EndDialog(hwnd, 1);
6e522441 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 */
32874aea 257static int CALLBACK AboutProc(HWND hwnd, UINT msg,
258 WPARAM wParam, LPARAM lParam)
259{
6e522441 260 switch (msg) {
261 case WM_INITDIALOG:
65a22376 262 /*
263 * Centre the window.
264 */
265 { /* centre the window */
266 RECT rs, rd;
267 HWND hw;
268
269 hw = GetDesktopWindow();
32874aea 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);
65a22376 275 }
276
32874aea 277 SetDlgItemText(hwnd, 100, ver);
6e522441 278 return 1;
279 case WM_COMMAND:
280 switch (LOWORD(wParam)) {
281 case IDOK:
32874aea 282 EndDialog(hwnd, 1);
6e522441 283 return 0;
284 case 101:
285 EnableWindow(hwnd, 0);
32874aea 286 DialogBox(hinst, MAKEINTRESOURCE(214), NULL, LicenceProc);
6e522441 287 EnableWindow(hwnd, 1);
32874aea 288 SetActiveWindow(hwnd);
6e522441 289 return 0;
290 }
291 return 0;
292 case WM_CLOSE:
32874aea 293 EndDialog(hwnd, 1);
6e522441 294 return 0;
295 }
296 return 0;
297}
298
299/*
300 * Thread to generate a key.
301 */
302struct rsa_key_thread_params {
32874aea 303 HWND progressbar; /* notify this with progress */
304 HWND dialog; /* notify this on completion */
0c2986d0 305 int keysize; /* bits in key */
5c72ca61 306 int is_dsa;
6e522441 307 struct RSAKey *key;
5c72ca61 308 struct dss_key *dsskey;
6e522441 309};
32874aea 310static DWORD WINAPI generate_rsa_key_thread(void *param)
311{
6e522441 312 struct rsa_key_thread_params *params =
32874aea 313 (struct rsa_key_thread_params *) param;
6e522441 314 struct progress prog;
315 prog.progbar = params->progressbar;
316
7c7f6893 317 progress_update(&prog, PROGFN_INITIALISE, 0, 0);
318
5c72ca61 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);
6e522441 323
324 PostMessage(params->dialog, WM_DONEKEY, 0, 0);
325
dcbde236 326 sfree(params);
6e522441 327 return 0;
328}
329
330struct MainDlgState {
331 int collecting_entropy;
332 int generation_thread_exists;
333 int key_exists;
334 int entropy_got, entropy_required, entropy_size;
0c2986d0 335 int keysize;
5c72ca61 336 int ssh2, is_dsa;
65a22376 337 char **commentptr; /* points to key.comment or ssh2key.comment */
338 struct ssh2_userkey ssh2key;
6e522441 339 unsigned *entropy;
340 struct RSAKey key;
5c72ca61 341 struct dss_key dsskey;
b723338c 342 HMENU filemenu, keymenu, cvtmenu;
6e522441 343};
344
32874aea 345static void hidemany(HWND hwnd, const int *ids, int hideit)
346{
6e522441 347 while (*ids) {
32874aea 348 ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW));
6e522441 349 }
350}
351
af282e3b 352static void setupbigedit1(HWND hwnd, int id, int idstatic, struct RSAKey *key)
32874aea 353{
6e522441 354 char *buffer;
355 char *dec1, *dec2;
356
357 dec1 = bignum_decimal(key->exponent);
358 dec2 = bignum_decimal(key->modulus);
32874aea 359 buffer = smalloc(strlen(dec1) + strlen(dec2) +
360 strlen(key->comment) + 30);
6e522441 361 sprintf(buffer, "%d %s %s %s",
32874aea 362 bignum_bitcount(key->modulus), dec1, dec2, key->comment);
6e522441 363 SetDlgItemText(hwnd, id, buffer);
af282e3b 364 SetDlgItemText(hwnd, idstatic,
365 "&Public key for pasting into authorized_keys file:");
dcbde236 366 sfree(dec1);
367 sfree(dec2);
368 sfree(buffer);
6e522441 369}
370
af282e3b 371static void setupbigedit2(HWND hwnd, int id, int idstatic,
372 struct ssh2_userkey *key)
32874aea 373{
65a22376 374 unsigned char *pub_blob;
375 char *buffer, *p;
44fb739e 376 int pub_len;
65a22376 377 int i;
378
379 pub_blob = key->alg->public_blob(key->data, &pub_len);
32874aea 380 buffer = smalloc(strlen(key->alg->name) + 4 * ((pub_len + 2) / 3) +
65a22376 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) {
32874aea 387 int n = (pub_len - i < 3 ? pub_len - i : 3);
388 base64_encode_atom(pub_blob + i, n, p);
65a22376 389 i += n;
390 p += 4;
391 }
392 *p++ = ' ';
393 strcpy(p, key->comment);
394 SetDlgItemText(hwnd, id, buffer);
af282e3b 395 SetDlgItemText(hwnd, idstatic, "&Public key for pasting into "
396 "OpenSSH authorized_keys2 file:");
65a22376 397 sfree(pub_blob);
32874aea 398 sfree(buffer);
65a22376 399}
400
af282e3b 401static 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
7bedb13c 419/*
420 * Warn about the obsolescent key file format.
421 */
422void 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
af282e3b 439static 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
9d07b2db 486enum {
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
511static const int nokey_ids[] = { IDC_NOKEY, 0 };
512static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 };
513static 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 */
525void 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);
b723338c 549 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
550 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
9d07b2db 551 MF_GRAYED|MF_BYCOMMAND);
b723338c 552 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
9d07b2db 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);
b723338c 574 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND);
575 EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH,
9d07b2db 576 MF_GRAYED|MF_BYCOMMAND);
b723338c 577 EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM,
9d07b2db 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);
b723338c 599 EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND);
9d07b2db 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) \
b723338c 606 EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \
9d07b2db 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
1a418691 615void 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
6e522441 784/*
785 * Dialog-box function for the main PuTTYgen dialog box.
786 */
32874aea 787static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
788 WPARAM wParam, LPARAM lParam)
789{
6e522441 790 static const char generating_msg[] =
32874aea 791 "Please wait while a key is generated...";
6e522441 792 static const char entropy_msg[] =
32874aea 793 "Please generate some randomness by moving the mouse over the blank area.";
6e522441 794 struct MainDlgState *state;
795
796 switch (msg) {
797 case WM_INITDIALOG:
0906628e 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
9d07b2db 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);
9dda6459 815 {
816 HMENU menu, menu1;
817
818 menu = CreateMenu();
819
820 menu1 = CreateMenu();
9dda6459 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");
9d07b2db 824 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
825 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
9dda6459 826 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
9d07b2db 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;
9dda6459 837
9dda6459 838 menu1 = CreateMenu();
b723338c 839 AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key");
840 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
9dda6459 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");
9dda6459 845 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
b723338c 846 "&Conversions");
847 state->cvtmenu = menu1;
9dda6459 848
849 menu1 = CreateMenu();
850 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
851 if (help_path)
852 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
9dda6459 853 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
854
855 SetMenu(hwnd, menu);
856 }
857
65a22376 858 /*
859 * Centre the window.
860 */
861 { /* centre the window */
862 RECT rs, rd;
863 HWND hw;
864
865 hw = GetDesktopWindow();
32874aea 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);
65a22376 871 }
872
32874aea 873 {
874 struct ctlpos cp, cp2;
6e522441 875
5c72ca61 876 /* Accelerators used: acglops1rbd */
a5470c60 877
e52455b1 878 ctlposinit(&cp, hwnd, 4, 4, 4);
32874aea 879 beginbox(&cp, "Key", IDC_BOX_KEY);
880 cp2 = cp;
ca68ebca 881 statictext(&cp2, "No key.", 1, IDC_NOKEY);
32874aea 882 cp2 = cp;
ca68ebca 883 statictext(&cp2, "", 1, IDC_GENERATING);
32874aea 884 progressbar(&cp2, IDC_PROGRESS);
885 bigeditctrl(&cp,
886 "&Public key for pasting into authorized_keys file:",
e52455b1 887 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
32874aea 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);
af282e3b 905 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
906 "Save p&ublic key", IDC_SAVEPUB,
907 "&Save private key", IDC_SAVE);
32874aea 908 endbox(&cp);
909 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
5c72ca61 910 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
32874aea 911 "SSH&1 (RSA)", IDC_KEYSSH1,
5c72ca61 912 "SSH2 &RSA", IDC_KEYSSH2RSA,
913 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
32874aea 914 staticedit(&cp, "Number of &bits in a generated key:",
0c2986d0 915 IDC_BITSSTATIC, IDC_BITS, 20);
32874aea 916 endbox(&cp);
917 }
00060c72 918 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
9d07b2db 919 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
920 IDC_KEYSSH1, MF_BYCOMMAND);
0c2986d0 921 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
922
32874aea 923 /*
924 * Initially, hide the progress bar and the key display,
925 * and show the no-key display. Also disable the Save
af282e3b 926 * buttons, because with no key we obviously can't save
32874aea 927 * anything.
928 */
9d07b2db 929 ui_set_state(hwnd, state, 0);
6e522441 930
1a418691 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
6e522441 937 return 1;
938 case WM_MOUSEMOVE:
32874aea 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;
0c2986d0 966 params->keysize = state->keysize;
5c72ca61 967 params->is_dsa = state->is_dsa;
32874aea 968 params->key = &state->key;
5c72ca61 969 params->dsskey = &state->dsskey;
32874aea 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;
6e522441 983 case WM_COMMAND:
984 switch (LOWORD(wParam)) {
9d07b2db 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;
6e522441 1001 case IDC_COMMENTEDIT:
1002 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 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);
2e8324fc 1012 if (state->ssh2) {
af282e3b 1013 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 1014 &state->ssh2key);
2e8324fc 1015 } else {
af282e3b 1016 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
1017 &state->key);
2e8324fc 1018 }
1019 }
1020 }
6e522441 1021 break;
1022 case IDC_ABOUT:
1023 EnableWindow(hwnd, 0);
32874aea 1024 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
6e522441 1025 EnableWindow(hwnd, 1);
32874aea 1026 SetActiveWindow(hwnd);
6e522441 1027 return 0;
9dda6459 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;
32874aea 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;
65a22376 1046 /* If we ever introduce a new key type, check it here! */
1047 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 1048 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 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 }
9d07b2db 1061 ui_set_state(hwnd, state, 1);
32874aea 1062 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
9d07b2db 1063 state->key_exists = FALSE;
32874aea 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:
4801c01c 1091 case IDC_EXPORT_OPENSSH:
1092 case IDC_EXPORT_SSHCOM:
32874aea 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];
4801c01c 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
32874aea 1124 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1125 passphrase, sizeof(passphrase));
1126 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1127 passphrase2, sizeof(passphrase2));
a5470c60 1128 if (strcmp(passphrase, passphrase2)) {
32874aea 1129 MessageBox(hwnd,
a5470c60 1130 "The two passphrases given do not match.",
32874aea 1131 "PuTTYgen Error", MB_OK | MB_ICONERROR);
a5470c60 1132 break;
1133 }
32874aea 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:",
06980e9d 1145 filename, 1, (type == realtype))) {
bdea0743 1146 int ret;
18790478 1147 FILE *fp = fopen(filename, "r");
1148 if (fp) {
32874aea 1149 char buffer[FILENAME_MAX + 80];
18790478 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 }
4801c01c 1158
65a22376 1159 if (state->ssh2) {
4801c01c 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);
65a22376 1167 } else {
4801c01c 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);
65a22376 1174 }
bdea0743 1175 if (ret <= 0) {
1176 MessageBox(hwnd, "Unable to save key file",
32874aea 1177 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 1178 }
32874aea 1179 }
1180 }
1181 break;
af282e3b 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:",
06980e9d 1188 filename, 1, 0)) {
af282e3b 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;
32874aea 1213 case IDC_LOAD:
b723338c 1214 case IDC_IMPORT:
32874aea 1215 state =
1216 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1217 if (!state->generation_thread_exists) {
1218 char filename[FILENAME_MAX];
06980e9d 1219 if (prompt_keyfile(hwnd, "Load private key:",
1a418691 1220 filename, 0, LOWORD(wParam)==IDC_LOAD))
1221 load_key_file(hwnd, state, filename,
1222 LOWORD(wParam) != IDC_LOAD);
32874aea 1223 }
1224 break;
6e522441 1225 }
1226 return 0;
1227 case WM_DONEKEY:
32874aea 1228 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1229 state->generation_thread_exists = FALSE;
1230 state->key_exists = TRUE;
5c72ca61 1231 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1232 MAKELPARAM(0, PROGRESSRANGE));
1233 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
3b4c5899 1234 if (state->ssh2) {
5c72ca61 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 }
65a22376 1242 state->commentptr = &state->ssh2key.comment;
3b4c5899 1243 } else {
65a22376 1244 state->commentptr = &state->key.comment;
3b4c5899 1245 }
32874aea 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);
5c72ca61 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);
32874aea 1262 }
1263
1264 /*
1265 * Now update the key controls with all the key data.
1266 */
1267 {
65a22376 1268 char *savecomment;
32874aea 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 */
65a22376 1283 savecomment = *state->commentptr;
1284 *state->commentptr = NULL;
32874aea 1285 if (state->ssh2) {
65a22376 1286 char *fp;
32874aea 1287 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1288 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1289 sfree(fp);
1290 } else {
1291 char buf[128];
32874aea 1292 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1293 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1294 }
65a22376 1295 *state->commentptr = savecomment;
32874aea 1296 /*
1297 * Construct a decimal representation of the key, for
af282e3b 1298 * pasting into .ssh/authorized_keys or
1299 * .ssh/authorized_keys2 on a Unix box.
32874aea 1300 */
65a22376 1301 if (state->ssh2) {
af282e3b 1302 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1303 IDC_PKSTATIC, &state->ssh2key);
65a22376 1304 } else {
af282e3b 1305 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1306 IDC_PKSTATIC, &state->key);
65a22376 1307 }
32874aea 1308 }
1309 /*
1310 * Finally, hide the progress bar and show the key data.
1311 */
9d07b2db 1312 ui_set_state(hwnd, state, 2);
32874aea 1313 break;
0906628e 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;
b723338c 1354 case IDC_IMPORT:
9d07b2db 1355 case IDC_EXPORT_OPENSSH:
1356 case IDC_EXPORT_SSHCOM:
b723338c 1357 cmd = "JI(`',`puttygen.conversions')"; break;
0906628e 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;
6e522441 1367 case WM_CLOSE:
32874aea 1368 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1369 sfree(state);
0906628e 1370 if (requested_help) {
1371 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1372 requested_help = FALSE;
1373 }
32874aea 1374 EndDialog(hwnd, 1);
6e522441 1375 return 0;
1376 }
1377 return 0;
1378}
1379
93b581bd 1380void cleanup_exit(int code) { exit(code); }
1381
32874aea 1382int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1383{
1a418691 1384 int argc;
1385 char **argv;
1386
d3a1a808 1387 split_into_argv(cmdline, &argc, &argv, NULL);
1a418691 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
8b8cf871 1397 InitCommonControls();
6e522441 1398 hinst = inst;
0906628e 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
6e522441 1420 random_init();
32874aea 1421 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1422 MainDlgProc) != IDOK;
6e522441 1423}