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