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