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