ieof-for-nonexistent-channel problem: avoid comparing an unsigned with -1
[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;
9d07b2db 335 HMENU filemenu, keymenu, exportmenu;
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);
542 EnableMenuItem(state->exportmenu, IDC_EXPORT_OPENSSH,
543 MF_GRAYED|MF_BYCOMMAND);
544 EnableMenuItem(state->exportmenu, IDC_EXPORT_SSHCOM,
545 MF_GRAYED|MF_BYCOMMAND);
546 break;
547 case 1: /* generating key */
548 hidemany(hwnd, nokey_ids, TRUE);
549 hidemany(hwnd, generating_ids, FALSE);
550 hidemany(hwnd, gotkey_ids, TRUE);
551 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0);
552 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0);
553 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0);
554 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0);
555 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0);
556 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0);
557 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0);
558 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0);
559 EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND);
560 EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND);
561 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND);
562 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND);
563 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND);
564 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND);
565 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND);
566 EnableMenuItem(state->exportmenu, IDC_EXPORT_OPENSSH,
567 MF_GRAYED|MF_BYCOMMAND);
568 EnableMenuItem(state->exportmenu, IDC_EXPORT_SSHCOM,
569 MF_GRAYED|MF_BYCOMMAND);
570 break;
571 case 2:
572 hidemany(hwnd, nokey_ids, TRUE);
573 hidemany(hwnd, generating_ids, TRUE);
574 hidemany(hwnd, gotkey_ids, FALSE);
575 EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1);
576 EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1);
577 EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1);
578 EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1);
579 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1);
580 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1);
581 EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1);
582 EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1);
583 EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND);
584 EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND);
585 EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND);
586 EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND);
587 EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND);
588 EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND);
589 EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND);
590 /*
591 * Enable export menu items if and only if the key type
592 * supports this kind of export.
593 */
594 type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1;
595#define do_export_menuitem(x,y) \
596 EnableMenuItem(state->exportmenu, x, MF_BYCOMMAND | \
597 (import_target_type(y)==type?MF_ENABLED:MF_GRAYED))
598 do_export_menuitem(IDC_EXPORT_OPENSSH, SSH_KEYTYPE_OPENSSH);
599 do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM);
600#undef do_export_menuitem
601 break;
602 }
603}
604
6e522441 605/*
606 * Dialog-box function for the main PuTTYgen dialog box.
607 */
32874aea 608static int CALLBACK MainDlgProc(HWND hwnd, UINT msg,
609 WPARAM wParam, LPARAM lParam)
610{
6e522441 611 static const char generating_msg[] =
32874aea 612 "Please wait while a key is generated...";
6e522441 613 static const char entropy_msg[] =
32874aea 614 "Please generate some randomness by moving the mouse over the blank area.";
6e522441 615 struct MainDlgState *state;
616
617 switch (msg) {
618 case WM_INITDIALOG:
0906628e 619 if (help_path)
620 SetWindowLong(hwnd, GWL_EXSTYLE,
621 GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP);
622 else {
623 /*
624 * If we add a Help button, this is where we destroy it
625 * if the help file isn't present.
626 */
627 }
628 requested_help = FALSE;
629
9d07b2db 630 state = smalloc(sizeof(*state));
631 state->generation_thread_exists = FALSE;
632 state->collecting_entropy = FALSE;
633 state->entropy = NULL;
634 state->key_exists = FALSE;
635 SetWindowLong(hwnd, GWL_USERDATA, (LONG) state);
9dda6459 636 {
637 HMENU menu, menu1;
638
639 menu = CreateMenu();
640
641 menu1 = CreateMenu();
9dda6459 642 AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key");
643 AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key");
644 AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key");
9d07b2db 645 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
646 AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit");
9dda6459 647 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&File");
9d07b2db 648 state->filemenu = menu1;
649
650 menu1 = CreateMenu();
651 AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair");
652 AppendMenu(menu1, MF_SEPARATOR, 0, 0);
653 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH&1 key (RSA)");
654 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH2 &RSA key");
655 AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH2 &DSA key");
656 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Key");
657 state->keymenu = menu1;
9dda6459 658
9dda6459 659 menu1 = CreateMenu();
660 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH,
661 "Export &OpenSSH key");
662 AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM,
663 "Export &ssh.com key");
9dda6459 664 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1,
665 "&Export");
9d07b2db 666 state->exportmenu = menu1;
9dda6459 667
668 menu1 = CreateMenu();
669 AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About");
670 if (help_path)
671 AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help");
9dda6459 672 AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT) menu1, "&Help");
673
674 SetMenu(hwnd, menu);
675 }
676
65a22376 677 /*
678 * Centre the window.
679 */
680 { /* centre the window */
681 RECT rs, rd;
682 HWND hw;
683
684 hw = GetDesktopWindow();
32874aea 685 if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd))
686 MoveWindow(hwnd,
687 (rs.right + rs.left + rd.left - rd.right) / 2,
688 (rs.bottom + rs.top + rd.top - rd.bottom) / 2,
689 rd.right - rd.left, rd.bottom - rd.top, TRUE);
65a22376 690 }
691
32874aea 692 {
693 struct ctlpos cp, cp2;
6e522441 694
5c72ca61 695 /* Accelerators used: acglops1rbd */
a5470c60 696
e52455b1 697 ctlposinit(&cp, hwnd, 4, 4, 4);
32874aea 698 beginbox(&cp, "Key", IDC_BOX_KEY);
699 cp2 = cp;
ca68ebca 700 statictext(&cp2, "No key.", 1, IDC_NOKEY);
32874aea 701 cp2 = cp;
ca68ebca 702 statictext(&cp2, "", 1, IDC_GENERATING);
32874aea 703 progressbar(&cp2, IDC_PROGRESS);
704 bigeditctrl(&cp,
705 "&Public key for pasting into authorized_keys file:",
e52455b1 706 IDC_PKSTATIC, IDC_KEYDISPLAY, 5);
32874aea 707 SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0);
708 staticedit(&cp, "Key fingerprint:", IDC_FPSTATIC,
709 IDC_FINGERPRINT, 75);
710 SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1,
711 0);
712 staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC,
713 IDC_COMMENTEDIT, 75);
714 staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC,
715 IDC_PASSPHRASE1EDIT, 75);
716 staticpassedit(&cp, "C&onfirm passphrase:",
717 IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75);
718 endbox(&cp);
719 beginbox(&cp, "Actions", IDC_BOX_ACTIONS);
720 staticbtn(&cp, "Generate a public/private key pair",
721 IDC_GENSTATIC, "&Generate", IDC_GENERATE);
722 staticbtn(&cp, "Load an existing private key file",
723 IDC_LOADSTATIC, "&Load", IDC_LOAD);
af282e3b 724 static2btn(&cp, "Save the generated key", IDC_SAVESTATIC,
725 "Save p&ublic key", IDC_SAVEPUB,
726 "&Save private key", IDC_SAVE);
32874aea 727 endbox(&cp);
728 beginbox(&cp, "Parameters", IDC_BOX_PARAMS);
5c72ca61 729 radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 3,
32874aea 730 "SSH&1 (RSA)", IDC_KEYSSH1,
5c72ca61 731 "SSH2 &RSA", IDC_KEYSSH2RSA,
732 "SSH2 &DSA", IDC_KEYSSH2DSA, NULL);
32874aea 733 staticedit(&cp, "Number of &bits in a generated key:",
0c2986d0 734 IDC_BITSSTATIC, IDC_BITS, 20);
32874aea 735 endbox(&cp);
736 }
00060c72 737 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA, IDC_KEYSSH1);
9d07b2db 738 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
739 IDC_KEYSSH1, MF_BYCOMMAND);
0c2986d0 740 SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEYSIZE, FALSE);
741
32874aea 742 /*
743 * Initially, hide the progress bar and the key display,
744 * and show the no-key display. Also disable the Save
af282e3b 745 * buttons, because with no key we obviously can't save
32874aea 746 * anything.
747 */
9d07b2db 748 ui_set_state(hwnd, state, 0);
6e522441 749
750 return 1;
751 case WM_MOUSEMOVE:
32874aea 752 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
753 if (state->collecting_entropy &&
754 state->entropy && state->entropy_got < state->entropy_required) {
755 state->entropy[state->entropy_got++] = lParam;
756 state->entropy[state->entropy_got++] = GetMessageTime();
757 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS,
758 state->entropy_got, 0);
759 if (state->entropy_got >= state->entropy_required) {
760 struct rsa_key_thread_params *params;
761 DWORD threadid;
762
763 /*
764 * Seed the entropy pool
765 */
766 random_add_heavynoise(state->entropy, state->entropy_size);
767 memset(state->entropy, 0, state->entropy_size);
768 sfree(state->entropy);
769 state->collecting_entropy = FALSE;
770
771 SetDlgItemText(hwnd, IDC_GENERATING, generating_msg);
772 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
773 MAKELPARAM(0, PROGRESSRANGE));
774 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
775
776 params = smalloc(sizeof(*params));
777 params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS);
778 params->dialog = hwnd;
0c2986d0 779 params->keysize = state->keysize;
5c72ca61 780 params->is_dsa = state->is_dsa;
32874aea 781 params->key = &state->key;
5c72ca61 782 params->dsskey = &state->dsskey;
32874aea 783
784 if (!CreateThread(NULL, 0, generate_rsa_key_thread,
785 params, 0, &threadid)) {
786 MessageBox(hwnd, "Out of thread resources",
787 "Key generation error",
788 MB_OK | MB_ICONERROR);
789 sfree(params);
790 } else {
791 state->generation_thread_exists = TRUE;
792 }
793 }
794 }
795 break;
6e522441 796 case WM_COMMAND:
797 switch (LOWORD(wParam)) {
9d07b2db 798 case IDC_KEYSSH1:
799 case IDC_KEYSSH2RSA:
800 case IDC_KEYSSH2DSA:
801 {
802 state = (struct MainDlgState *)
803 GetWindowLong(hwnd, GWL_USERDATA);
804 if (!IsDlgButtonChecked(hwnd, LOWORD(wParam)))
805 CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2DSA,
806 LOWORD(wParam));
807 CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2DSA,
808 LOWORD(wParam), MF_BYCOMMAND);
809 }
810 break;
811 case IDC_QUIT:
812 PostMessage(hwnd, WM_CLOSE, 0, 0);
813 break;
6e522441 814 case IDC_COMMENTEDIT:
815 if (HIWORD(wParam) == EN_CHANGE) {
32874aea 816 state = (struct MainDlgState *)
817 GetWindowLong(hwnd, GWL_USERDATA);
818 if (state->key_exists) {
819 HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT);
820 int len = GetWindowTextLength(editctl);
821 if (*state->commentptr)
822 sfree(*state->commentptr);
823 *state->commentptr = smalloc(len + 1);
824 GetWindowText(editctl, *state->commentptr, len + 1);
2e8324fc 825 if (state->ssh2) {
af282e3b 826 setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
32874aea 827 &state->ssh2key);
2e8324fc 828 } else {
af282e3b 829 setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC,
830 &state->key);
2e8324fc 831 }
832 }
833 }
6e522441 834 break;
835 case IDC_ABOUT:
836 EnableWindow(hwnd, 0);
32874aea 837 DialogBox(hinst, MAKEINTRESOURCE(213), NULL, AboutProc);
6e522441 838 EnableWindow(hwnd, 1);
32874aea 839 SetActiveWindow(hwnd);
6e522441 840 return 0;
9dda6459 841 case IDC_GIVEHELP:
842 if (HIWORD(wParam) == BN_CLICKED ||
843 HIWORD(wParam) == BN_DOUBLECLICKED) {
844 if (help_path) {
845 WinHelp(hwnd, help_path, HELP_COMMAND,
846 (DWORD)"JI(`',`puttygen.general')");
847 requested_help = TRUE;
848 }
849 }
850 return 0;
32874aea 851 case IDC_GENERATE:
852 state =
853 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
854 if (!state->generation_thread_exists) {
855 BOOL ok;
856 state->keysize = GetDlgItemInt(hwnd, IDC_BITS, &ok, FALSE);
857 if (!ok)
858 state->keysize = DEFAULT_KEYSIZE;
65a22376 859 /* If we ever introduce a new key type, check it here! */
860 state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1);
5c72ca61 861 state->is_dsa = IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA);
32874aea 862 if (state->keysize < 256) {
863 int ret = MessageBox(hwnd,
864 "PuTTYgen will not generate a key"
865 " smaller than 256 bits.\n"
866 "Key length reset to 256. Continue?",
867 "PuTTYgen Warning",
868 MB_ICONWARNING | MB_OKCANCEL);
869 if (ret != IDOK)
870 break;
871 state->keysize = 256;
872 SetDlgItemInt(hwnd, IDC_BITS, 256, FALSE);
873 }
9d07b2db 874 ui_set_state(hwnd, state, 1);
32874aea 875 SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg);
9d07b2db 876 state->key_exists = FALSE;
32874aea 877 state->collecting_entropy = TRUE;
878
879 /*
880 * My brief statistical tests on mouse movements
881 * suggest that there are about 2.5 bits of
882 * randomness in the x position, 2.5 in the y
883 * position, and 1.7 in the message time, making
884 * 5.7 bits of unpredictability per mouse movement.
885 * However, other people have told me it's far less
886 * than that, so I'm going to be stupidly cautious
887 * and knock that down to a nice round 2. With this
888 * method, we require two words per mouse movement,
889 * so with 2 bits per mouse movement we expect 2
890 * bits every 2 words.
891 */
892 state->entropy_required = (state->keysize / 2) * 2;
893 state->entropy_got = 0;
894 state->entropy_size = (state->entropy_required *
895 sizeof(*state->entropy));
896 state->entropy = smalloc(state->entropy_size);
897
898 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
899 MAKELPARAM(0, state->entropy_required));
900 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
901 }
902 break;
903 case IDC_SAVE:
4801c01c 904 case IDC_EXPORT_OPENSSH:
905 case IDC_EXPORT_SSHCOM:
32874aea 906 state =
907 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
908 if (state->key_exists) {
909 char filename[FILENAME_MAX];
910 char passphrase[PASSPHRASE_MAXLEN];
911 char passphrase2[PASSPHRASE_MAXLEN];
4801c01c 912 int type, realtype;
913
914 if (state->ssh2)
915 realtype = SSH_KEYTYPE_SSH2;
916 else
917 realtype = SSH_KEYTYPE_SSH1;
918
919 if (LOWORD(wParam) == IDC_EXPORT_OPENSSH)
920 type = SSH_KEYTYPE_OPENSSH;
921 else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM)
922 type = SSH_KEYTYPE_SSHCOM;
923 else
924 type = realtype;
925
926 if (type != realtype &&
927 import_target_type(type) != realtype) {
928 char msg[256];
929 sprintf(msg, "Cannot export an SSH%d key in an SSH%d"
930 " format", (state->ssh2 ? 2 : 1),
931 (state->ssh2 ? 1 : 2));
932 MessageBox(hwnd, msg,
933 "PuTTYgen Error", MB_OK | MB_ICONERROR);
934 break;
935 }
936
32874aea 937 GetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
938 passphrase, sizeof(passphrase));
939 GetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
940 passphrase2, sizeof(passphrase2));
a5470c60 941 if (strcmp(passphrase, passphrase2)) {
32874aea 942 MessageBox(hwnd,
a5470c60 943 "The two passphrases given do not match.",
32874aea 944 "PuTTYgen Error", MB_OK | MB_ICONERROR);
a5470c60 945 break;
946 }
32874aea 947 if (!*passphrase) {
948 int ret;
949 ret = MessageBox(hwnd,
950 "Are you sure you want to save this key\n"
951 "without a passphrase to protect it?",
952 "PuTTYgen Warning",
953 MB_YESNO | MB_ICONWARNING);
954 if (ret != IDYES)
955 break;
956 }
957 if (prompt_keyfile(hwnd, "Save private key as:",
958 filename, 1)) {
bdea0743 959 int ret;
18790478 960 FILE *fp = fopen(filename, "r");
961 if (fp) {
32874aea 962 char buffer[FILENAME_MAX + 80];
18790478 963 fclose(fp);
964 sprintf(buffer, "Overwrite existing file\n%.*s?",
965 FILENAME_MAX, filename);
966 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
967 MB_YESNO | MB_ICONWARNING);
968 if (ret != IDYES)
969 break;
970 }
4801c01c 971
65a22376 972 if (state->ssh2) {
4801c01c 973 if (type != realtype)
974 ret = export_ssh2(filename, type, &state->ssh2key,
975 *passphrase ? passphrase : NULL);
976 else
977 ret = ssh2_save_userkey(filename, &state->ssh2key,
978 *passphrase ? passphrase :
979 NULL);
65a22376 980 } else {
4801c01c 981 if (type != realtype)
982 ret = export_ssh1(filename, type, &state->key,
983 *passphrase ? passphrase : NULL);
984 else
985 ret = saversakey(filename, &state->key,
986 *passphrase ? passphrase : NULL);
65a22376 987 }
bdea0743 988 if (ret <= 0) {
989 MessageBox(hwnd, "Unable to save key file",
32874aea 990 "PuTTYgen Error", MB_OK | MB_ICONERROR);
bdea0743 991 }
32874aea 992 }
993 }
994 break;
af282e3b 995 case IDC_SAVEPUB:
996 state =
997 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
998 if (state->key_exists) {
999 char filename[FILENAME_MAX];
1000 if (prompt_keyfile(hwnd, "Save public key as:",
1001 filename, 1)) {
1002 int ret;
1003 FILE *fp = fopen(filename, "r");
1004 if (fp) {
1005 char buffer[FILENAME_MAX + 80];
1006 fclose(fp);
1007 sprintf(buffer, "Overwrite existing file\n%.*s?",
1008 FILENAME_MAX, filename);
1009 ret = MessageBox(hwnd, buffer, "PuTTYgen Warning",
1010 MB_YESNO | MB_ICONWARNING);
1011 if (ret != IDYES)
1012 break;
1013 }
1014 if (state->ssh2) {
1015 ret = save_ssh2_pubkey(filename, &state->ssh2key);
1016 } else {
1017 ret = save_ssh1_pubkey(filename, &state->key);
1018 }
1019 if (ret <= 0) {
1020 MessageBox(hwnd, "Unable to save key file",
1021 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1022 }
1023 }
1024 }
1025 break;
32874aea 1026 case IDC_LOAD:
1027 state =
1028 (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1029 if (!state->generation_thread_exists) {
1030 char filename[FILENAME_MAX];
1031 if (prompt_keyfile(hwnd, "Load private key:", filename, 0)) {
1032 char passphrase[PASSPHRASE_MAXLEN];
1033 int needs_pass;
9dda6459 1034 int type, realtype;
32874aea 1035 int ret;
1036 char *comment;
1037 struct PassphraseProcStruct pps;
1038 struct RSAKey newkey1;
2d466ffd 1039 struct ssh2_userkey *newkey2 = NULL;
65a22376 1040
9dda6459 1041 type = realtype = key_type(filename);
1042 if (type != SSH_KEYTYPE_SSH1 &&
1043 type != SSH_KEYTYPE_SSH2 &&
1044 !import_possible(type)) {
231ee168 1045 char msg[256];
1046 sprintf(msg, "Couldn't load private key (%s)",
1047 key_type_to_str(type));
1048 MessageBox(NULL, msg,
32874aea 1049 "PuTTYgen Error", MB_OK | MB_ICONERROR);
65a22376 1050 break;
1051 }
6e522441 1052
9dda6459 1053 if (type != SSH_KEYTYPE_SSH1 &&
1054 type != SSH_KEYTYPE_SSH2) {
1055 realtype = type;
1056 type = import_target_type(type);
1057 }
1058
65a22376 1059 comment = NULL;
9dda6459 1060 if (realtype == SSH_KEYTYPE_SSH1)
65a22376 1061 needs_pass = rsakey_encrypted(filename, &comment);
9dda6459 1062 else if (realtype == SSH_KEYTYPE_SSH2)
32874aea 1063 needs_pass =
1064 ssh2_userkey_encrypted(filename, &comment);
9dda6459 1065 else
1066 needs_pass = import_encrypted(filename, realtype,
1067 &comment);
32874aea 1068 pps.passphrase = passphrase;
1069 pps.comment = comment;
1070 do {
1071 if (needs_pass) {
1072 int dlgret;
1073 dlgret = DialogBoxParam(hinst,
1074 MAKEINTRESOURCE(210),
1075 NULL, PassphraseProc,
1076 (LPARAM) & pps);
1077 if (!dlgret) {
1078 ret = -2;
1079 break;
1080 }
1081 } else
1082 *passphrase = '\0';
9dda6459 1083 if (type == SSH_KEYTYPE_SSH1) {
1084 if (realtype == type)
1085 ret = loadrsakey(filename, &newkey1,
1086 passphrase);
1087 else
1088 ret = import_ssh1(filename, realtype,
1089 &newkey1, passphrase);
1090 } else {
1091 if (realtype == type)
1092 newkey2 = ssh2_load_userkey(filename,
1093 passphrase);
1094 else
1095 newkey2 = import_ssh2(filename, realtype,
1096 passphrase);
65a22376 1097 if (newkey2 == SSH2_WRONG_PASSPHRASE)
1098 ret = -1;
1099 else if (!newkey2)
1100 ret = 0;
1101 else
1102 ret = 1;
1103 }
32874aea 1104 } while (ret == -1);
1105 if (comment)
1106 sfree(comment);
1107 if (ret == 0) {
1108 MessageBox(NULL, "Couldn't load private key.",
1109 "PuTTYgen Error", MB_OK | MB_ICONERROR);
1110 } else if (ret == 1) {
32874aea 1111 /*
1112 * Now update the key controls with all the
1113 * key data.
1114 */
1115 {
1116 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT,
1117 passphrase);
1118 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT,
1119 passphrase);
231ee168 1120 if (type == SSH_KEYTYPE_SSH1) {
65a22376 1121 char buf[128];
1122 char *savecomment;
1123
1124 state->ssh2 = FALSE;
1125 state->commentptr = &state->key.comment;
1126 state->key = newkey1;
1127
1128 /*
1129 * Set the key fingerprint.
1130 */
1131 savecomment = state->key.comment;
32874aea 1132 state->key.comment = NULL;
1133 rsa_fingerprint(buf, sizeof(buf),
1134 &state->key);
1135 state->key.comment = savecomment;
65a22376 1136
1137 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
1138 /*
1139 * Construct a decimal representation
1140 * of the key, for pasting into
1141 * .ssh/authorized_keys on a Unix box.
1142 */
32874aea 1143 setupbigedit1(hwnd, IDC_KEYDISPLAY,
af282e3b 1144 IDC_PKSTATIC, &state->key);
65a22376 1145 } else {
1146 char *fp;
1147 char *savecomment;
1148
1149 state->ssh2 = TRUE;
32874aea 1150 state->commentptr =
1151 &state->ssh2key.comment;
1152 state->ssh2key = *newkey2; /* structure copy */
65a22376 1153 sfree(newkey2);
1154
1155 savecomment = state->ssh2key.comment;
32874aea 1156 state->ssh2key.comment = NULL;
1157 fp =
1158 state->ssh2key.alg->
1159 fingerprint(state->ssh2key.data);
1160 state->ssh2key.comment = savecomment;
65a22376 1161
1162 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1163 sfree(fp);
1164
32874aea 1165 setupbigedit2(hwnd, IDC_KEYDISPLAY,
af282e3b 1166 IDC_PKSTATIC, &state->ssh2key);
65a22376 1167 }
32874aea 1168 SetDlgItemText(hwnd, IDC_COMMENTEDIT,
1169 *state->commentptr);
1170 }
1171 /*
1172 * Finally, hide the progress bar and show
1173 * the key data.
1174 */
9d07b2db 1175 ui_set_state(hwnd, state, 2);
32874aea 1176 state->key_exists = TRUE;
1177 }
1178 }
1179 }
1180 break;
6e522441 1181 }
1182 return 0;
1183 case WM_DONEKEY:
32874aea 1184 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1185 state->generation_thread_exists = FALSE;
1186 state->key_exists = TRUE;
5c72ca61 1187 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0,
1188 MAKELPARAM(0, PROGRESSRANGE));
1189 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0);
3b4c5899 1190 if (state->ssh2) {
5c72ca61 1191 if (state->is_dsa) {
1192 state->ssh2key.data = &state->dsskey;
1193 state->ssh2key.alg = &ssh_dss;
1194 } else {
1195 state->ssh2key.data = &state->key;
1196 state->ssh2key.alg = &ssh_rsa;
1197 }
65a22376 1198 state->commentptr = &state->ssh2key.comment;
3b4c5899 1199 } else {
65a22376 1200 state->commentptr = &state->key.comment;
3b4c5899 1201 }
32874aea 1202 /*
1203 * Invent a comment for the key. We'll do this by including
1204 * the date in it. This will be so horrifyingly ugly that
1205 * the user will immediately want to change it, which is
1206 * what we want :-)
1207 */
1208 *state->commentptr = smalloc(30);
1209 {
1210 time_t t;
1211 struct tm *tm;
1212 time(&t);
1213 tm = localtime(&t);
5c72ca61 1214 if (state->is_dsa)
1215 strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", tm);
1216 else
1217 strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", tm);
32874aea 1218 }
1219
1220 /*
1221 * Now update the key controls with all the key data.
1222 */
1223 {
65a22376 1224 char *savecomment;
32874aea 1225 /*
1226 * Blank passphrase, initially. This isn't dangerous,
1227 * because we will warn (Are You Sure?) before allowing
1228 * the user to save an unprotected private key.
1229 */
1230 SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, "");
1231 SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, "");
1232 /*
1233 * Set the comment.
1234 */
1235 SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr);
1236 /*
1237 * Set the key fingerprint.
1238 */
65a22376 1239 savecomment = *state->commentptr;
1240 *state->commentptr = NULL;
32874aea 1241 if (state->ssh2) {
65a22376 1242 char *fp;
32874aea 1243 fp = state->ssh2key.alg->fingerprint(state->ssh2key.data);
65a22376 1244 SetDlgItemText(hwnd, IDC_FINGERPRINT, fp);
1245 sfree(fp);
1246 } else {
1247 char buf[128];
32874aea 1248 rsa_fingerprint(buf, sizeof(buf), &state->key);
65a22376 1249 SetDlgItemText(hwnd, IDC_FINGERPRINT, buf);
32874aea 1250 }
65a22376 1251 *state->commentptr = savecomment;
32874aea 1252 /*
1253 * Construct a decimal representation of the key, for
af282e3b 1254 * pasting into .ssh/authorized_keys or
1255 * .ssh/authorized_keys2 on a Unix box.
32874aea 1256 */
65a22376 1257 if (state->ssh2) {
af282e3b 1258 setupbigedit2(hwnd, IDC_KEYDISPLAY,
1259 IDC_PKSTATIC, &state->ssh2key);
65a22376 1260 } else {
af282e3b 1261 setupbigedit1(hwnd, IDC_KEYDISPLAY,
1262 IDC_PKSTATIC, &state->key);
65a22376 1263 }
32874aea 1264 }
1265 /*
1266 * Finally, hide the progress bar and show the key data.
1267 */
9d07b2db 1268 ui_set_state(hwnd, state, 2);
32874aea 1269 break;
0906628e 1270 case WM_HELP:
1271 if (help_path) {
1272 int id = ((LPHELPINFO)lParam)->iCtrlId;
1273 char *cmd = NULL;
1274 switch (id) {
1275 case IDC_GENERATING:
1276 case IDC_PROGRESS:
1277 case IDC_GENSTATIC:
1278 case IDC_GENERATE:
1279 cmd = "JI(`',`puttygen.generate')"; break;
1280 case IDC_PKSTATIC:
1281 case IDC_KEYDISPLAY:
1282 cmd = "JI(`',`puttygen.pastekey')"; break;
1283 case IDC_FPSTATIC:
1284 case IDC_FINGERPRINT:
1285 cmd = "JI(`',`puttygen.fingerprint')"; break;
1286 case IDC_COMMENTSTATIC:
1287 case IDC_COMMENTEDIT:
1288 cmd = "JI(`',`puttygen.comment')"; break;
1289 case IDC_PASSPHRASE1STATIC:
1290 case IDC_PASSPHRASE1EDIT:
1291 case IDC_PASSPHRASE2STATIC:
1292 case IDC_PASSPHRASE2EDIT:
1293 cmd = "JI(`',`puttygen.passphrase')"; break;
1294 case IDC_LOADSTATIC:
1295 case IDC_LOAD:
1296 cmd = "JI(`',`puttygen.load')"; break;
1297 case IDC_SAVESTATIC:
1298 case IDC_SAVE:
1299 cmd = "JI(`',`puttygen.savepriv')"; break;
1300 case IDC_SAVEPUB:
1301 cmd = "JI(`',`puttygen.savepub')"; break;
1302 case IDC_TYPESTATIC:
1303 case IDC_KEYSSH1:
1304 case IDC_KEYSSH2RSA:
1305 case IDC_KEYSSH2DSA:
1306 cmd = "JI(`',`puttygen.keytype')"; break;
1307 case IDC_BITSSTATIC:
1308 case IDC_BITS:
1309 cmd = "JI(`',`puttygen.bits')"; break;
9d07b2db 1310 case IDC_EXPORT_OPENSSH:
1311 case IDC_EXPORT_SSHCOM:
1312 cmd = "JI(`',`puttygen.export')"; break;
0906628e 1313 }
1314 if (cmd) {
1315 WinHelp(hwnd, help_path, HELP_COMMAND, (DWORD)cmd);
1316 requested_help = TRUE;
1317 } else {
1318 MessageBeep(0);
1319 }
1320 }
1321 break;
6e522441 1322 case WM_CLOSE:
32874aea 1323 state = (struct MainDlgState *) GetWindowLong(hwnd, GWL_USERDATA);
1324 sfree(state);
0906628e 1325 if (requested_help) {
1326 WinHelp(hwnd, help_path, HELP_QUIT, 0);
1327 requested_help = FALSE;
1328 }
32874aea 1329 EndDialog(hwnd, 1);
6e522441 1330 return 0;
1331 }
1332 return 0;
1333}
1334
93b581bd 1335void cleanup_exit(int code) { exit(code); }
1336
32874aea 1337int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
1338{
8b8cf871 1339 InitCommonControls();
6e522441 1340 hinst = inst;
0906628e 1341
1342 /*
1343 * See if we can find our Help file.
1344 */
1345 {
1346 char b[2048], *p, *q, *r;
1347 FILE *fp;
1348 GetModuleFileName(NULL, b, sizeof(b) - 1);
1349 r = b;
1350 p = strrchr(b, '\\');
1351 if (p && p >= r) r = p+1;
1352 q = strrchr(b, ':');
1353 if (q && q >= r) r = q+1;
1354 strcpy(r, "putty.hlp");
1355 if ( (fp = fopen(b, "r")) != NULL) {
1356 help_path = dupstr(b);
1357 fclose(fp);
1358 } else
1359 help_path = NULL;
1360 }
1361
6e522441 1362 random_init();
32874aea 1363 return DialogBox(hinst, MAKEINTRESOURCE(201), NULL,
1364 MainDlgProc) != IDOK;
6e522441 1365}