OSDN Git Service

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