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