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