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