puttygen.c 42 KB

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