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