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