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