cmdgen.c 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363
  1. /*
  2. * cmdgen.c - command-line form of PuTTYgen
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <ctype.h>
  7. #include <limits.h>
  8. #include <assert.h>
  9. #include <time.h>
  10. #include <errno.h>
  11. #include <string.h>
  12. #include "putty.h"
  13. #include "ssh.h"
  14. #include "sshkeygen.h"
  15. #include "mpint.h"
  16. static FILE *progress_fp = NULL;
  17. static bool linear_progress_phase;
  18. static unsigned last_progress_col;
  19. static ProgressPhase cmdgen_progress_add_linear(
  20. ProgressReceiver *prog, double c)
  21. {
  22. ProgressPhase ph = { .n = 0 };
  23. return ph;
  24. }
  25. static ProgressPhase cmdgen_progress_add_probabilistic(
  26. ProgressReceiver *prog, double c, double p)
  27. {
  28. ProgressPhase ph = { .n = 1 };
  29. return ph;
  30. }
  31. static void cmdgen_progress_start_phase(ProgressReceiver *prog,
  32. ProgressPhase p)
  33. {
  34. linear_progress_phase = (p.n == 0);
  35. last_progress_col = 0;
  36. }
  37. static void cmdgen_progress_report(ProgressReceiver *prog, double p)
  38. {
  39. unsigned new_col = p * 64 + 0.5;
  40. for (; last_progress_col < new_col; last_progress_col++)
  41. fputc('+', progress_fp);
  42. }
  43. static void cmdgen_progress_report_attempt(ProgressReceiver *prog)
  44. {
  45. if (progress_fp) {
  46. fputc('+', progress_fp);
  47. fflush(progress_fp);
  48. }
  49. }
  50. static void cmdgen_progress_report_phase_complete(ProgressReceiver *prog)
  51. {
  52. if (linear_progress_phase)
  53. cmdgen_progress_report(prog, 1.0);
  54. if (progress_fp) {
  55. fputc('\n', progress_fp);
  56. fflush(progress_fp);
  57. }
  58. }
  59. static const ProgressReceiverVtable cmdgen_progress_vt = {
  60. .add_linear = cmdgen_progress_add_linear,
  61. .add_probabilistic = cmdgen_progress_add_probabilistic,
  62. .ready = null_progress_ready,
  63. .start_phase = cmdgen_progress_start_phase,
  64. .report = cmdgen_progress_report,
  65. .report_attempt = cmdgen_progress_report_attempt,
  66. .report_phase_complete = cmdgen_progress_report_phase_complete,
  67. };
  68. static ProgressReceiver cmdgen_progress = { .vt = &cmdgen_progress_vt };
  69. /*
  70. * Stubs to let everything else link sensibly.
  71. */
  72. char *x_get_default(const char *key)
  73. {
  74. return NULL;
  75. }
  76. void sk_cleanup(void)
  77. {
  78. }
  79. void showversion(void)
  80. {
  81. char *buildinfo_text = buildinfo("\n");
  82. printf("puttygen: %s\n%s\n", ver, buildinfo_text);
  83. sfree(buildinfo_text);
  84. }
  85. void usage(bool standalone)
  86. {
  87. fprintf(standalone ? stderr : stdout,
  88. "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n"
  89. " [ -C comment ] [ -P ] [ -q ]\n"
  90. " [ -o output-keyfile ] [ -O type | -l | -L"
  91. " | -p ]\n");
  92. if (standalone)
  93. fprintf(stderr,
  94. "Use \"puttygen --help\" for more detail.\n");
  95. }
  96. void help(void)
  97. {
  98. /*
  99. * Help message is an extended version of the usage message. So
  100. * start with that, plus a version heading.
  101. */
  102. printf("PuTTYgen: key generator and converter for the PuTTY tools\n"
  103. "%s\n", ver);
  104. usage(false);
  105. printf(" -t specify key type when generating:\n"
  106. " eddsa, ecdsa, rsa, dsa, rsa1 use with -b\n"
  107. " ed25519, ed448 special cases of eddsa\n"
  108. " -b specify number of bits when generating key\n"
  109. " -C change or specify key comment\n"
  110. " -P change key passphrase\n"
  111. " -q quiet: do not display progress bar\n"
  112. " -O specify output type:\n"
  113. " private output PuTTY private key format\n"
  114. " private-openssh export OpenSSH private key\n"
  115. " private-openssh-new export OpenSSH private key "
  116. "(force new format)\n"
  117. " private-sshcom export ssh.com private key\n"
  118. " public RFC 4716 / ssh.com public key\n"
  119. " public-openssh OpenSSH public key\n"
  120. " fingerprint output the key fingerprint\n"
  121. " text output the key components as "
  122. "'name=0x####'\n"
  123. " -o specify output file\n"
  124. " -l equivalent to `-O fingerprint'\n"
  125. " -L equivalent to `-O public-openssh'\n"
  126. " -p equivalent to `-O public'\n"
  127. " --dump equivalent to `-O text'\n"
  128. " --reencrypt load a key and save it with fresh "
  129. "encryption\n"
  130. " --old-passphrase file\n"
  131. " specify file containing old key passphrase\n"
  132. " --new-passphrase file\n"
  133. " specify file containing new key passphrase\n"
  134. " --random-device device\n"
  135. " specify device to read entropy from (e.g. /dev/urandom)\n"
  136. " --primes <type> select prime-generation method:\n"
  137. " probable conventional probabilistic prime finding\n"
  138. " proven numbers that have been proven to be prime\n"
  139. " proven-even also try harder for an even distribution\n"
  140. " --strong-rsa use \"strong\" primes as RSA key factors\n"
  141. " --ppk-param <key>=<value>[,<key>=<value>,...]\n"
  142. " specify parameters when writing PuTTY private key file "
  143. "format:\n"
  144. " version PPK format version (min 2, max 3, "
  145. "default 3)\n"
  146. " kdf key derivation function (argon2id, "
  147. "argon2i, argon2d)\n"
  148. " memory Kbyte of memory to use in passphrase "
  149. "hash\n"
  150. " (default 8192)\n"
  151. " time approx milliseconds to hash for "
  152. "(default 100)\n"
  153. " passes number of hash passes to run "
  154. "(alternative to 'time')\n"
  155. " parallelism number of parallelisable threads in the "
  156. "hash function\n"
  157. " (default 1)\n"
  158. );
  159. }
  160. static bool move(char *from, char *to)
  161. {
  162. int ret;
  163. ret = rename(from, to);
  164. if (ret) {
  165. /*
  166. * This OS may require us to remove the original file first.
  167. */
  168. remove(to);
  169. ret = rename(from, to);
  170. }
  171. if (ret) {
  172. perror("puttygen: cannot move new file on to old one");
  173. return false;
  174. }
  175. return true;
  176. }
  177. static char *readpassphrase(const char *filename)
  178. {
  179. FILE *fp;
  180. char *line;
  181. fp = fopen(filename, "r");
  182. if (!fp) {
  183. fprintf(stderr, "puttygen: cannot open %s: %s\n",
  184. filename, strerror(errno));
  185. return NULL;
  186. }
  187. line = fgetline(fp);
  188. if (line)
  189. line[strcspn(line, "\r\n")] = '\0';
  190. else if (ferror(fp))
  191. fprintf(stderr, "puttygen: error reading from %s: %s\n",
  192. filename, strerror(errno));
  193. else /* empty file */
  194. line = dupstr("");
  195. fclose(fp);
  196. return line;
  197. }
  198. #define DEFAULT_RSADSA_BITS 2048
  199. /* For Unix in particular, but harmless if this main() is reused elsewhere */
  200. const bool buildinfo_gtk_relevant = false;
  201. int main(int argc, char **argv)
  202. {
  203. char *infile = NULL;
  204. Filename *infilename = NULL, *outfilename = NULL;
  205. LoadedFile *infile_lf = NULL;
  206. BinarySource *infile_bs = NULL;
  207. enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, EDDSA } keytype = NOKEYGEN;
  208. char *outfile = NULL, *outfiletmp = NULL;
  209. enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO,
  210. OPENSSH_NEW, SSHCOM, TEXT } outtype = PRIVATE;
  211. int bits = -1;
  212. const char *comment = NULL;
  213. char *origcomment = NULL;
  214. bool change_passphrase = false, reencrypt = false;
  215. bool errs = false, nogo = false;
  216. int intype = SSH_KEYTYPE_UNOPENABLE;
  217. int sshver = 0;
  218. ssh2_userkey *ssh2key = NULL;
  219. RSAKey *ssh1key = NULL;
  220. strbuf *ssh2blob = NULL;
  221. char *ssh2alg = NULL;
  222. char *old_passphrase = NULL, *new_passphrase = NULL;
  223. bool load_encrypted;
  224. const char *random_device = NULL;
  225. int exit_status = 0;
  226. const PrimeGenerationPolicy *primegen = &primegen_probabilistic;
  227. bool strong_rsa = false;
  228. ppk_save_parameters params = ppk_save_default_parameters;
  229. FingerprintType fptype = SSH_FPTYPE_DEFAULT;
  230. if (is_interactive())
  231. progress_fp = stderr;
  232. #define RETURN(status) do { exit_status = (status); goto out; } while (0)
  233. /* ------------------------------------------------------------------
  234. * Parse the command line to figure out what we've been asked to do.
  235. */
  236. /*
  237. * If run with no arguments at all, print the usage message and
  238. * return success.
  239. */
  240. if (argc <= 1) {
  241. usage(true);
  242. RETURN(0);
  243. }
  244. /*
  245. * Parse command line arguments.
  246. */
  247. while (--argc) {
  248. char *p = *++argv;
  249. if (p[0] == '-' && p[1]) {
  250. /*
  251. * An option.
  252. */
  253. while (p && *++p) {
  254. char c = *p;
  255. switch (c) {
  256. case '-': {
  257. /*
  258. * Long option.
  259. */
  260. char *opt, *val;
  261. opt = p++; /* opt will have _one_ leading - */
  262. while (*p && *p != '=')
  263. p++; /* find end of option */
  264. if (*p == '=') {
  265. *p++ = '\0';
  266. val = p;
  267. } else
  268. val = NULL;
  269. if (!strcmp(opt, "-help")) {
  270. if (val) {
  271. errs = true;
  272. fprintf(stderr, "puttygen: option `-%s'"
  273. " expects no argument\n", opt);
  274. } else {
  275. help();
  276. nogo = true;
  277. }
  278. } else if (!strcmp(opt, "-version")) {
  279. if (val) {
  280. errs = true;
  281. fprintf(stderr, "puttygen: option `-%s'"
  282. " expects no argument\n", opt);
  283. } else {
  284. showversion();
  285. nogo = true;
  286. }
  287. } else if (!strcmp(opt, "-pgpfp")) {
  288. if (val) {
  289. errs = true;
  290. fprintf(stderr, "puttygen: option `-%s'"
  291. " expects no argument\n", opt);
  292. } else {
  293. /* support --pgpfp for consistency */
  294. pgp_fingerprints();
  295. nogo = true;
  296. }
  297. } else if (!strcmp(opt, "-old-passphrase")) {
  298. if (!val && argc > 1)
  299. --argc, val = *++argv;
  300. if (!val) {
  301. errs = true;
  302. fprintf(stderr, "puttygen: option `-%s'"
  303. " expects an argument\n", opt);
  304. } else {
  305. old_passphrase = readpassphrase(val);
  306. if (!old_passphrase)
  307. errs = true;
  308. }
  309. } else if (!strcmp(opt, "-new-passphrase")) {
  310. if (!val && argc > 1)
  311. --argc, val = *++argv;
  312. if (!val) {
  313. errs = true;
  314. fprintf(stderr, "puttygen: option `-%s'"
  315. " expects an argument\n", opt);
  316. } else {
  317. new_passphrase = readpassphrase(val);
  318. if (!new_passphrase)
  319. errs = true;
  320. }
  321. } else if (!strcmp(opt, "-random-device")) {
  322. if (!val && argc > 1)
  323. --argc, val = *++argv;
  324. if (!val) {
  325. errs = true;
  326. fprintf(stderr, "puttygen: option `-%s'"
  327. " expects an argument\n", opt);
  328. } else {
  329. random_device = val;
  330. }
  331. } else if (!strcmp(opt, "-dump")) {
  332. outtype = TEXT;
  333. } else if (!strcmp(opt, "-primes")) {
  334. if (!val && argc > 1)
  335. --argc, val = *++argv;
  336. if (!val) {
  337. errs = true;
  338. fprintf(stderr, "puttygen: option `-%s'"
  339. " expects an argument\n", opt);
  340. } else if (!strcmp(val, "probable") ||
  341. !strcmp(val, "probabilistic")) {
  342. primegen = &primegen_probabilistic;
  343. } else if (!strcmp(val, "provable") ||
  344. !strcmp(val, "proven") ||
  345. !strcmp(val, "simple") ||
  346. !strcmp(val, "maurer-simple")) {
  347. primegen = &primegen_provable_maurer_simple;
  348. } else if (!strcmp(val, "provable-even") ||
  349. !strcmp(val, "proven-even") ||
  350. !strcmp(val, "even") ||
  351. !strcmp(val, "complex") ||
  352. !strcmp(val, "maurer-complex")) {
  353. primegen = &primegen_provable_maurer_complex;
  354. } else {
  355. errs = true;
  356. fprintf(stderr, "puttygen: unrecognised prime-"
  357. "generation mode `%s'\n", val);
  358. }
  359. } else if (!strcmp(opt, "-strong-rsa")) {
  360. strong_rsa = true;
  361. } else if (!strcmp(opt, "-reencrypt")) {
  362. reencrypt = true;
  363. } else if (!strcmp(opt, "-ppk-param") ||
  364. !strcmp(opt, "-ppk-params")) {
  365. if (!val && argc > 1)
  366. --argc, val = *++argv;
  367. if (!val) {
  368. errs = true;
  369. fprintf(stderr, "puttygen: option `-%s'"
  370. " expects an argument\n", opt);
  371. } else {
  372. char *nextval;
  373. for (; val; val = nextval) {
  374. nextval = strchr(val, ',');
  375. if (nextval)
  376. *nextval++ = '\0';
  377. char *optvalue = strchr(val, '=');
  378. if (!optvalue) {
  379. errs = true;
  380. fprintf(stderr, "puttygen: PPK parameter "
  381. "'%s' expected a value\n", val);
  382. continue;
  383. }
  384. *optvalue++ = '\0';
  385. /* Non-numeric options */
  386. if (!strcmp(val, "kdf")) {
  387. if (!strcmp(optvalue, "Argon2id") ||
  388. !strcmp(optvalue, "argon2id")) {
  389. params.argon2_flavour = Argon2id;
  390. } else if (!strcmp(optvalue, "Argon2i") ||
  391. !strcmp(optvalue, "argon2i")) {
  392. params.argon2_flavour = Argon2i;
  393. } else if (!strcmp(optvalue, "Argon2d") ||
  394. !strcmp(optvalue, "argon2d")) {
  395. params.argon2_flavour = Argon2d;
  396. } else {
  397. errs = true;
  398. fprintf(stderr, "puttygen: unrecognise"
  399. "d kdf '%s'\n", optvalue);
  400. }
  401. continue;
  402. }
  403. char *end;
  404. unsigned long n = strtoul(optvalue, &end, 0);
  405. if (!*optvalue || *end) {
  406. errs = true;
  407. fprintf(stderr, "puttygen: value '%s' for "
  408. "PPK parameter '%s': expected a "
  409. "number\n", optvalue, val);
  410. continue;
  411. }
  412. if (!strcmp(val, "version")) {
  413. params.fmt_version = n;
  414. } else if (!strcmp(val, "memory") ||
  415. !strcmp(val, "mem")) {
  416. params.argon2_mem = n;
  417. } else if (!strcmp(val, "time")) {
  418. params.argon2_passes_auto = true;
  419. params.argon2_milliseconds = n;
  420. } else if (!strcmp(val, "passes")) {
  421. params.argon2_passes_auto = false;
  422. params.argon2_passes = n;
  423. } else if (!strcmp(val, "parallelism") ||
  424. !strcmp(val, "parallel")) {
  425. params.argon2_parallelism = n;
  426. } else {
  427. errs = true;
  428. fprintf(stderr, "puttygen: unrecognised "
  429. "PPK parameter '%s'\n", val);
  430. continue;
  431. }
  432. }
  433. }
  434. } else {
  435. errs = true;
  436. fprintf(stderr,
  437. "puttygen: no such option `-%s'\n", opt);
  438. }
  439. p = NULL;
  440. break;
  441. }
  442. case 'h':
  443. case 'V':
  444. case 'P':
  445. case 'l':
  446. case 'L':
  447. case 'p':
  448. case 'q':
  449. /*
  450. * Option requiring no parameter.
  451. */
  452. switch (c) {
  453. case 'h':
  454. help();
  455. nogo = true;
  456. break;
  457. case 'V':
  458. showversion();
  459. nogo = true;
  460. break;
  461. case 'P':
  462. change_passphrase = true;
  463. break;
  464. case 'l':
  465. outtype = FP;
  466. break;
  467. case 'L':
  468. outtype = PUBLICO;
  469. break;
  470. case 'p':
  471. outtype = PUBLIC;
  472. break;
  473. case 'q':
  474. progress_fp = NULL;
  475. break;
  476. }
  477. break;
  478. case 't':
  479. case 'b':
  480. case 'C':
  481. case 'O':
  482. case 'o':
  483. case 'E':
  484. /*
  485. * Option requiring parameter.
  486. */
  487. p++;
  488. if (!*p && argc > 1)
  489. --argc, p = *++argv;
  490. else if (!*p) {
  491. fprintf(stderr, "puttygen: option `-%c' expects a"
  492. " parameter\n", c);
  493. errs = true;
  494. }
  495. /*
  496. * Now c is the option and p is the parameter.
  497. */
  498. switch (c) {
  499. case 't':
  500. if (!strcmp(p, "rsa") || !strcmp(p, "rsa2"))
  501. keytype = RSA2, sshver = 2;
  502. else if (!strcmp(p, "rsa1"))
  503. keytype = RSA1, sshver = 1;
  504. else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
  505. keytype = DSA, sshver = 2;
  506. else if (!strcmp(p, "ecdsa"))
  507. keytype = ECDSA, sshver = 2;
  508. else if (!strcmp(p, "eddsa"))
  509. keytype = EDDSA, sshver = 2;
  510. else if (!strcmp(p, "ed25519"))
  511. keytype = EDDSA, bits = 255, sshver = 2;
  512. else if (!strcmp(p, "ed448"))
  513. keytype = EDDSA, bits = 448, sshver = 2;
  514. else {
  515. fprintf(stderr,
  516. "puttygen: unknown key type `%s'\n", p);
  517. errs = true;
  518. }
  519. break;
  520. case 'b':
  521. bits = atoi(p);
  522. break;
  523. case 'C':
  524. comment = p;
  525. break;
  526. case 'O':
  527. if (!strcmp(p, "public"))
  528. outtype = PUBLIC;
  529. else if (!strcmp(p, "public-openssh"))
  530. outtype = PUBLICO;
  531. else if (!strcmp(p, "private"))
  532. outtype = PRIVATE;
  533. else if (!strcmp(p, "fingerprint"))
  534. outtype = FP;
  535. else if (!strcmp(p, "private-openssh"))
  536. outtype = OPENSSH_AUTO, sshver = 2;
  537. else if (!strcmp(p, "private-openssh-new"))
  538. outtype = OPENSSH_NEW, sshver = 2;
  539. else if (!strcmp(p, "private-sshcom"))
  540. outtype = SSHCOM, sshver = 2;
  541. else if (!strcmp(p, "text"))
  542. outtype = TEXT;
  543. else {
  544. fprintf(stderr,
  545. "puttygen: unknown output type `%s'\n", p);
  546. errs = true;
  547. }
  548. break;
  549. case 'o':
  550. outfile = p;
  551. break;
  552. case 'E':
  553. if (!strcmp(p, "md5"))
  554. fptype = SSH_FPTYPE_MD5;
  555. else if (!strcmp(p, "sha256"))
  556. fptype = SSH_FPTYPE_SHA256;
  557. else {
  558. fprintf(stderr, "puttygen: unknown fingerprint "
  559. "type `%s'\n", p);
  560. errs = true;
  561. }
  562. break;
  563. }
  564. p = NULL; /* prevent continued processing */
  565. break;
  566. default:
  567. /*
  568. * Unrecognised option.
  569. */
  570. errs = true;
  571. fprintf(stderr, "puttygen: no such option `-%c'\n", c);
  572. break;
  573. }
  574. }
  575. } else {
  576. /*
  577. * A non-option argument.
  578. */
  579. if (!infile)
  580. infile = p;
  581. else {
  582. errs = true;
  583. fprintf(stderr, "puttygen: cannot handle more than one"
  584. " input file\n");
  585. }
  586. }
  587. }
  588. if (bits == -1) {
  589. /*
  590. * No explicit key size was specified. Default varies
  591. * depending on key type.
  592. */
  593. switch (keytype) {
  594. case ECDSA:
  595. bits = 384;
  596. break;
  597. case EDDSA:
  598. bits = 255;
  599. break;
  600. default:
  601. bits = DEFAULT_RSADSA_BITS;
  602. break;
  603. }
  604. }
  605. if (keytype == ECDSA || keytype == EDDSA) {
  606. const char *name = (keytype == ECDSA ? "ECDSA" : "EdDSA");
  607. const int *valid_lengths = (keytype == ECDSA ? ec_nist_curve_lengths :
  608. ec_ed_curve_lengths);
  609. size_t n_lengths = (keytype == ECDSA ? n_ec_nist_curve_lengths :
  610. n_ec_ed_curve_lengths);
  611. bool (*alg_and_curve_by_bits)(int, const struct ec_curve **,
  612. const ssh_keyalg **) =
  613. (keytype == ECDSA ? ec_nist_alg_and_curve_by_bits :
  614. ec_ed_alg_and_curve_by_bits);
  615. const struct ec_curve *curve;
  616. const ssh_keyalg *alg;
  617. if (!alg_and_curve_by_bits(bits, &curve, &alg)) {
  618. fprintf(stderr, "puttygen: invalid bits for %s, choose", name);
  619. for (size_t i = 0; i < n_lengths; i++)
  620. fprintf(stderr, "%s%d", (i == 0 ? " " :
  621. i == n_lengths-1 ? " or " : ", "),
  622. valid_lengths[i]);
  623. fputc('\n', stderr);
  624. errs = true;
  625. }
  626. }
  627. if (keytype == RSA2 || keytype == RSA1 || keytype == DSA) {
  628. if (bits < 256) {
  629. fprintf(stderr, "puttygen: cannot generate %s keys shorter than"
  630. " 256 bits\n", (keytype == DSA ? "DSA" : "RSA"));
  631. errs = true;
  632. } else if (bits < DEFAULT_RSADSA_BITS) {
  633. fprintf(stderr, "puttygen: warning: %s keys shorter than"
  634. " %d bits are probably not secure\n",
  635. (keytype == DSA ? "DSA" : "RSA"), DEFAULT_RSADSA_BITS);
  636. /* but this is just a warning, so proceed anyway */
  637. }
  638. }
  639. if (errs)
  640. RETURN(1);
  641. if (nogo)
  642. RETURN(0);
  643. /*
  644. * If run with at least one argument _but_ not the required
  645. * ones, print the usage message and return failure.
  646. */
  647. if (!infile && keytype == NOKEYGEN) {
  648. usage(true);
  649. RETURN(1);
  650. }
  651. /* ------------------------------------------------------------------
  652. * Figure out further details of exactly what we're going to do.
  653. */
  654. /*
  655. * Bomb out if we've been asked to both load and generate a
  656. * key.
  657. */
  658. if (keytype != NOKEYGEN && infile) {
  659. fprintf(stderr, "puttygen: cannot both load and generate a key\n");
  660. RETURN(1);
  661. }
  662. /*
  663. * We must save the private part when generating a new key.
  664. */
  665. if (keytype != NOKEYGEN &&
  666. (outtype != PRIVATE && outtype != OPENSSH_AUTO &&
  667. outtype != OPENSSH_NEW && outtype != SSHCOM && outtype != TEXT)) {
  668. fprintf(stderr, "puttygen: this would generate a new key but "
  669. "discard the private part\n");
  670. RETURN(1);
  671. }
  672. /*
  673. * Analyse the type of the input file, in case this affects our
  674. * course of action.
  675. */
  676. if (infile) {
  677. const char *load_error;
  678. infilename = filename_from_str(infile);
  679. if (!strcmp(infile, "-"))
  680. infile_lf = lf_load_keyfile_fp(stdin, &load_error);
  681. else
  682. infile_lf = lf_load_keyfile(infilename, &load_error);
  683. if (!infile_lf) {
  684. fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
  685. infile, load_error);
  686. RETURN(1);
  687. }
  688. infile_bs = BinarySource_UPCAST(infile_lf);
  689. intype = key_type_s(infile_bs);
  690. BinarySource_REWIND(infile_bs);
  691. switch (intype) {
  692. case SSH_KEYTYPE_UNOPENABLE:
  693. case SSH_KEYTYPE_UNKNOWN:
  694. fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
  695. infile, key_type_to_str(intype));
  696. RETURN(1);
  697. case SSH_KEYTYPE_SSH1:
  698. case SSH_KEYTYPE_SSH1_PUBLIC:
  699. if (sshver == 2) {
  700. fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys"
  701. " not supported\n");
  702. RETURN(1);
  703. }
  704. sshver = 1;
  705. break;
  706. case SSH_KEYTYPE_SSH2:
  707. case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
  708. case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
  709. case SSH_KEYTYPE_OPENSSH_PEM:
  710. case SSH_KEYTYPE_OPENSSH_NEW:
  711. case SSH_KEYTYPE_SSHCOM:
  712. if (sshver == 1) {
  713. fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys"
  714. " not supported\n");
  715. RETURN(1);
  716. }
  717. sshver = 2;
  718. break;
  719. case SSH_KEYTYPE_OPENSSH_AUTO:
  720. default:
  721. unreachable("Should never see these types on an input file");
  722. }
  723. }
  724. /*
  725. * Determine the default output file, if none is provided.
  726. *
  727. * This will usually be equal to stdout, except that if the
  728. * input and output file formats are the same then the default
  729. * output is to overwrite the input.
  730. *
  731. * Also in this code, we bomb out if the input and output file
  732. * formats are the same and no other action is performed.
  733. */
  734. if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||
  735. (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||
  736. (intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) ||
  737. (intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) ||
  738. (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
  739. if (!outfile) {
  740. outfile = infile;
  741. outfiletmp = dupcat(outfile, ".tmp");
  742. }
  743. if (!change_passphrase && !comment && !reencrypt) {
  744. fprintf(stderr, "puttygen: this command would perform no useful"
  745. " action\n");
  746. RETURN(1);
  747. }
  748. } else {
  749. if (!outfile) {
  750. /*
  751. * Bomb out rather than automatically choosing to write
  752. * a private key file to stdout.
  753. */
  754. if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
  755. outtype == OPENSSH_NEW || outtype == SSHCOM) {
  756. fprintf(stderr, "puttygen: need to specify an output file\n");
  757. RETURN(1);
  758. }
  759. }
  760. }
  761. /*
  762. * Figure out whether we need to load the encrypted part of the
  763. * key. This will be the case if (a) we need to write out
  764. * a private key format, (b) the entire input key file is
  765. * encrypted, or (c) we're outputting TEXT, in which case we
  766. * want all of the input file including private material if it
  767. * exists.
  768. */
  769. bool intype_entirely_encrypted =
  770. intype == SSH_KEYTYPE_OPENSSH_PEM ||
  771. intype == SSH_KEYTYPE_OPENSSH_NEW ||
  772. intype == SSH_KEYTYPE_SSHCOM;
  773. bool intype_has_private =
  774. !(intype == SSH_KEYTYPE_SSH1_PUBLIC ||
  775. intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
  776. intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  777. bool outtype_has_private =
  778. outtype == PRIVATE || outtype == OPENSSH_AUTO ||
  779. outtype == OPENSSH_NEW || outtype == SSHCOM;
  780. if (outtype_has_private || intype_entirely_encrypted ||
  781. (outtype == TEXT && intype_has_private))
  782. load_encrypted = true;
  783. else
  784. load_encrypted = false;
  785. if (load_encrypted && !intype_has_private) {
  786. fprintf(stderr, "puttygen: cannot perform this action on a "
  787. "public-key-only input file\n");
  788. RETURN(1);
  789. }
  790. /* ------------------------------------------------------------------
  791. * Now we're ready to actually do some stuff.
  792. */
  793. /*
  794. * Either load or generate a key.
  795. */
  796. if (keytype != NOKEYGEN) {
  797. char *entropy;
  798. char default_comment[80];
  799. struct tm tm;
  800. tm = ltime();
  801. if (keytype == DSA)
  802. strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
  803. else if (keytype == ECDSA)
  804. strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
  805. else if (keytype == EDDSA && bits == 255)
  806. strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm);
  807. else if (keytype == EDDSA)
  808. strftime(default_comment, 30, "eddsa-key-%Y%m%d", &tm);
  809. else
  810. strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
  811. entropy = get_random_data(bits / 8, random_device);
  812. if (!entropy) {
  813. fprintf(stderr, "puttygen: failed to collect entropy, "
  814. "could not generate key\n");
  815. RETURN(1);
  816. }
  817. random_setup_special();
  818. random_reseed(make_ptrlen(entropy, bits / 8));
  819. smemclr(entropy, bits/8);
  820. sfree(entropy);
  821. PrimeGenerationContext *pgc = primegen_new_context(primegen);
  822. if (keytype == DSA) {
  823. struct dss_key *dsskey = snew(struct dss_key);
  824. dsa_generate(dsskey, bits, pgc, &cmdgen_progress);
  825. ssh2key = snew(ssh2_userkey);
  826. ssh2key->key = &dsskey->sshk;
  827. ssh1key = NULL;
  828. } else if (keytype == ECDSA) {
  829. struct ecdsa_key *ek = snew(struct ecdsa_key);
  830. ecdsa_generate(ek, bits);
  831. ssh2key = snew(ssh2_userkey);
  832. ssh2key->key = &ek->sshk;
  833. ssh1key = NULL;
  834. } else if (keytype == EDDSA) {
  835. struct eddsa_key *ek = snew(struct eddsa_key);
  836. eddsa_generate(ek, bits);
  837. ssh2key = snew(ssh2_userkey);
  838. ssh2key->key = &ek->sshk;
  839. ssh1key = NULL;
  840. } else {
  841. RSAKey *rsakey = snew(RSAKey);
  842. rsa_generate(rsakey, bits, strong_rsa, pgc, &cmdgen_progress);
  843. rsakey->comment = NULL;
  844. if (keytype == RSA1) {
  845. ssh1key = rsakey;
  846. } else {
  847. ssh2key = snew(ssh2_userkey);
  848. ssh2key->key = &rsakey->sshk;
  849. }
  850. }
  851. primegen_free_context(pgc);
  852. if (ssh2key)
  853. ssh2key->comment = dupstr(default_comment);
  854. if (ssh1key)
  855. ssh1key->comment = dupstr(default_comment);
  856. } else {
  857. const char *error = NULL;
  858. bool encrypted;
  859. assert(infile != NULL);
  860. sfree(origcomment);
  861. origcomment = NULL;
  862. /*
  863. * Find out whether the input key is encrypted.
  864. */
  865. if (intype == SSH_KEYTYPE_SSH1)
  866. encrypted = rsa1_encrypted_s(infile_bs, &origcomment);
  867. else if (intype == SSH_KEYTYPE_SSH2)
  868. encrypted = ppk_encrypted_s(infile_bs, &origcomment);
  869. else
  870. encrypted = import_encrypted_s(infilename, infile_bs,
  871. intype, &origcomment);
  872. BinarySource_REWIND(infile_bs);
  873. /*
  874. * If so, ask for a passphrase.
  875. */
  876. if (encrypted && load_encrypted) {
  877. if (!old_passphrase) {
  878. prompts_t *p = new_prompts();
  879. int ret;
  880. p->to_server = false;
  881. p->from_server = false;
  882. p->name = dupstr("SSH key passphrase");
  883. add_prompt(p, dupstr("Enter passphrase to load key: "), false);
  884. ret = console_get_userpass_input(p);
  885. assert(ret >= 0);
  886. if (!ret) {
  887. free_prompts(p);
  888. perror("puttygen: unable to read passphrase");
  889. RETURN(1);
  890. } else {
  891. old_passphrase = prompt_get_result(p->prompts[0]);
  892. free_prompts(p);
  893. }
  894. }
  895. } else {
  896. old_passphrase = NULL;
  897. }
  898. switch (intype) {
  899. int ret;
  900. case SSH_KEYTYPE_SSH1:
  901. case SSH_KEYTYPE_SSH1_PUBLIC:
  902. ssh1key = snew(RSAKey);
  903. memset(ssh1key, 0, sizeof(RSAKey));
  904. if (!load_encrypted) {
  905. strbuf *blob;
  906. BinarySource src[1];
  907. sfree(origcomment);
  908. origcomment = NULL;
  909. blob = strbuf_new();
  910. ret = rsa1_loadpub_s(infile_bs, BinarySink_UPCAST(blob),
  911. &origcomment, &error);
  912. BinarySource_BARE_INIT(src, blob->u, blob->len);
  913. get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST);
  914. strbuf_free(blob);
  915. ssh1key->comment = dupstr(origcomment);
  916. ssh1key->private_exponent = NULL;
  917. ssh1key->p = NULL;
  918. ssh1key->q = NULL;
  919. ssh1key->iqmp = NULL;
  920. } else {
  921. ret = rsa1_load_s(infile_bs, ssh1key, old_passphrase, &error);
  922. }
  923. BinarySource_REWIND(infile_bs);
  924. if (ret > 0)
  925. error = NULL;
  926. else if (!error)
  927. error = "unknown error";
  928. break;
  929. case SSH_KEYTYPE_SSH2:
  930. case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
  931. case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
  932. if (!load_encrypted) {
  933. sfree(origcomment);
  934. origcomment = NULL;
  935. ssh2blob = strbuf_new();
  936. if (ppk_loadpub_s(infile_bs, &ssh2alg,
  937. BinarySink_UPCAST(ssh2blob),
  938. &origcomment, &error)) {
  939. const ssh_keyalg *alg = find_pubkey_alg(ssh2alg);
  940. if (alg)
  941. bits = ssh_key_public_bits(
  942. alg, ptrlen_from_strbuf(ssh2blob));
  943. else
  944. bits = -1;
  945. } else {
  946. strbuf_free(ssh2blob);
  947. ssh2blob = NULL;
  948. }
  949. sfree(ssh2alg);
  950. } else {
  951. ssh2key = ppk_load_s(infile_bs, old_passphrase, &error);
  952. }
  953. BinarySource_REWIND(infile_bs);
  954. if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
  955. error = NULL;
  956. else if (!error) {
  957. if (ssh2key == SSH2_WRONG_PASSPHRASE)
  958. error = "wrong passphrase";
  959. else
  960. error = "unknown error";
  961. }
  962. break;
  963. case SSH_KEYTYPE_OPENSSH_PEM:
  964. case SSH_KEYTYPE_OPENSSH_NEW:
  965. case SSH_KEYTYPE_SSHCOM:
  966. ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error);
  967. if (ssh2key) {
  968. if (ssh2key != SSH2_WRONG_PASSPHRASE)
  969. error = NULL;
  970. else
  971. error = "wrong passphrase";
  972. } else if (!error)
  973. error = "unknown error";
  974. break;
  975. default:
  976. unreachable("bad input key type");
  977. }
  978. if (error) {
  979. fprintf(stderr, "puttygen: error loading `%s': %s\n",
  980. infile, error);
  981. RETURN(1);
  982. }
  983. }
  984. /*
  985. * Change the comment if asked to.
  986. */
  987. if (comment) {
  988. if (sshver == 1) {
  989. assert(ssh1key);
  990. sfree(ssh1key->comment);
  991. ssh1key->comment = dupstr(comment);
  992. } else {
  993. assert(ssh2key);
  994. sfree(ssh2key->comment);
  995. ssh2key->comment = dupstr(comment);
  996. }
  997. }
  998. /*
  999. * Unless we're changing the passphrase, the old one (if any) is a
  1000. * reasonable default.
  1001. */
  1002. if (!change_passphrase && old_passphrase && !new_passphrase)
  1003. new_passphrase = dupstr(old_passphrase);
  1004. /*
  1005. * Prompt for a new passphrase if we have been asked to, or if
  1006. * we have just generated a key.
  1007. *
  1008. * In the latter case, an exception is if we're producing text
  1009. * output, because that output format doesn't support encryption
  1010. * in any case.
  1011. */
  1012. if (!new_passphrase && (change_passphrase ||
  1013. (keytype != NOKEYGEN && outtype != TEXT))) {
  1014. prompts_t *p = new_prompts();
  1015. int ret;
  1016. p->to_server = false;
  1017. p->from_server = false;
  1018. p->name = dupstr("New SSH key passphrase");
  1019. add_prompt(p, dupstr("Enter passphrase to save key: "), false);
  1020. add_prompt(p, dupstr("Re-enter passphrase to verify: "), false);
  1021. ret = console_get_userpass_input(p);
  1022. assert(ret >= 0);
  1023. if (!ret) {
  1024. free_prompts(p);
  1025. perror("puttygen: unable to read new passphrase");
  1026. RETURN(1);
  1027. } else {
  1028. if (strcmp(prompt_get_result_ref(p->prompts[0]),
  1029. prompt_get_result_ref(p->prompts[1]))) {
  1030. free_prompts(p);
  1031. fprintf(stderr, "puttygen: passphrases do not match\n");
  1032. RETURN(1);
  1033. }
  1034. new_passphrase = prompt_get_result(p->prompts[0]);
  1035. free_prompts(p);
  1036. }
  1037. }
  1038. if (new_passphrase && !*new_passphrase) {
  1039. sfree(new_passphrase);
  1040. new_passphrase = NULL;
  1041. }
  1042. /*
  1043. * Write output.
  1044. *
  1045. * (In the case where outfile and outfiletmp are both NULL,
  1046. * there is no semantic reason to initialise outfilename at
  1047. * all; but we have to write _something_ to it or some compiler
  1048. * will probably complain that it might be used uninitialised.)
  1049. */
  1050. if (outfiletmp)
  1051. outfilename = filename_from_str(outfiletmp);
  1052. else
  1053. outfilename = filename_from_str(outfile ? outfile : "");
  1054. switch (outtype) {
  1055. bool ret;
  1056. int real_outtype;
  1057. case PRIVATE:
  1058. random_ref(); /* we'll need a few random bytes in the save file */
  1059. if (sshver == 1) {
  1060. assert(ssh1key);
  1061. ret = rsa1_save_f(outfilename, ssh1key, new_passphrase);
  1062. if (!ret) {
  1063. fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
  1064. RETURN(1);
  1065. }
  1066. } else {
  1067. assert(ssh2key);
  1068. ret = ppk_save_f(outfilename, ssh2key, new_passphrase, &params);
  1069. if (!ret) {
  1070. fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
  1071. RETURN(1);
  1072. }
  1073. }
  1074. if (outfiletmp) {
  1075. if (!move(outfiletmp, outfile))
  1076. RETURN(1); /* rename failed */
  1077. }
  1078. break;
  1079. case PUBLIC:
  1080. case PUBLICO: {
  1081. FILE *fp;
  1082. if (outfile) {
  1083. fp = f_open(outfilename, "w", false);
  1084. if (!fp) {
  1085. fprintf(stderr, "unable to open output file\n");
  1086. exit(1);
  1087. }
  1088. } else {
  1089. fp = stdout;
  1090. }
  1091. if (sshver == 1) {
  1092. ssh1_write_pubkey(fp, ssh1key);
  1093. } else {
  1094. if (!ssh2blob) {
  1095. assert(ssh2key);
  1096. ssh2blob = strbuf_new();
  1097. ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob));
  1098. }
  1099. ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
  1100. ssh2blob->s, ssh2blob->len,
  1101. (outtype == PUBLIC ?
  1102. SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 :
  1103. SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH));
  1104. }
  1105. if (outfile)
  1106. fclose(fp);
  1107. break;
  1108. }
  1109. case FP: {
  1110. FILE *fp;
  1111. char *fingerprint;
  1112. if (sshver == 1) {
  1113. assert(ssh1key);
  1114. fingerprint = rsa_ssh1_fingerprint(ssh1key);
  1115. } else {
  1116. if (ssh2key) {
  1117. fingerprint = ssh2_fingerprint(ssh2key->key, fptype);
  1118. } else {
  1119. assert(ssh2blob);
  1120. fingerprint = ssh2_fingerprint_blob(
  1121. ptrlen_from_strbuf(ssh2blob), fptype);
  1122. }
  1123. }
  1124. if (outfile) {
  1125. fp = f_open(outfilename, "w", false);
  1126. if (!fp) {
  1127. fprintf(stderr, "unable to open output file\n");
  1128. exit(1);
  1129. }
  1130. } else {
  1131. fp = stdout;
  1132. }
  1133. fprintf(fp, "%s\n", fingerprint);
  1134. if (outfile)
  1135. fclose(fp);
  1136. sfree(fingerprint);
  1137. break;
  1138. }
  1139. case OPENSSH_AUTO:
  1140. case OPENSSH_NEW:
  1141. case SSHCOM:
  1142. assert(sshver == 2);
  1143. assert(ssh2key);
  1144. random_ref(); /* both foreign key types require randomness,
  1145. * for IV or padding */
  1146. switch (outtype) {
  1147. case OPENSSH_AUTO:
  1148. real_outtype = SSH_KEYTYPE_OPENSSH_AUTO;
  1149. break;
  1150. case OPENSSH_NEW:
  1151. real_outtype = SSH_KEYTYPE_OPENSSH_NEW;
  1152. break;
  1153. case SSHCOM:
  1154. real_outtype = SSH_KEYTYPE_SSHCOM;
  1155. break;
  1156. default:
  1157. unreachable("control flow goof");
  1158. }
  1159. ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
  1160. if (!ret) {
  1161. fprintf(stderr, "puttygen: unable to export key\n");
  1162. RETURN(1);
  1163. }
  1164. if (outfiletmp) {
  1165. if (!move(outfiletmp, outfile))
  1166. RETURN(1); /* rename failed */
  1167. }
  1168. break;
  1169. case TEXT: {
  1170. key_components *kc;
  1171. if (sshver == 1) {
  1172. assert(ssh1key);
  1173. kc = rsa_components(ssh1key);
  1174. } else {
  1175. if (ssh2key) {
  1176. kc = ssh_key_components(ssh2key->key);
  1177. } else {
  1178. assert(ssh2blob);
  1179. BinarySource src[1];
  1180. BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(ssh2blob));
  1181. ptrlen algname = get_string(src);
  1182. const ssh_keyalg *alg = find_pubkey_alg_len(algname);
  1183. if (!alg) {
  1184. fprintf(stderr, "puttygen: cannot extract key components "
  1185. "from public key of unknown type '%.*s'\n",
  1186. PTRLEN_PRINTF(algname));
  1187. RETURN(1);
  1188. }
  1189. ssh_key *sk = ssh_key_new_pub(
  1190. alg, ptrlen_from_strbuf(ssh2blob));
  1191. if (!sk) {
  1192. fprintf(stderr, "puttygen: unable to decode public key\n");
  1193. RETURN(1);
  1194. }
  1195. kc = ssh_key_components(sk);
  1196. ssh_key_free(sk);
  1197. }
  1198. }
  1199. FILE *fp;
  1200. if (outfile) {
  1201. fp = f_open(outfilename, "w", false);
  1202. if (!fp) {
  1203. fprintf(stderr, "unable to open output file\n");
  1204. exit(1);
  1205. }
  1206. } else {
  1207. fp = stdout;
  1208. }
  1209. for (size_t i = 0; i < kc->ncomponents; i++) {
  1210. if (kc->components[i].is_mp_int) {
  1211. char *hex = mp_get_hex(kc->components[i].mp);
  1212. fprintf(fp, "%s=0x%s\n", kc->components[i].name, hex);
  1213. smemclr(hex, strlen(hex));
  1214. sfree(hex);
  1215. } else {
  1216. fprintf(fp, "%s=\"", kc->components[i].name);
  1217. write_c_string_literal(fp, ptrlen_from_asciz(
  1218. kc->components[i].text));
  1219. fputs("\"\n", fp);
  1220. }
  1221. }
  1222. if (outfile)
  1223. fclose(fp);
  1224. key_components_free(kc);
  1225. break;
  1226. }
  1227. }
  1228. out:
  1229. #undef RETURN
  1230. if (old_passphrase) {
  1231. smemclr(old_passphrase, strlen(old_passphrase));
  1232. sfree(old_passphrase);
  1233. }
  1234. if (new_passphrase) {
  1235. smemclr(new_passphrase, strlen(new_passphrase));
  1236. sfree(new_passphrase);
  1237. }
  1238. if (ssh1key) {
  1239. freersakey(ssh1key);
  1240. sfree(ssh1key);
  1241. }
  1242. if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) {
  1243. sfree(ssh2key->comment);
  1244. if (ssh2key->key)
  1245. ssh_key_free(ssh2key->key);
  1246. sfree(ssh2key);
  1247. }
  1248. if (ssh2blob)
  1249. strbuf_free(ssh2blob);
  1250. sfree(origcomment);
  1251. if (infilename)
  1252. filename_free(infilename);
  1253. if (infile_lf)
  1254. lf_free(infile_lf);
  1255. if (outfilename)
  1256. filename_free(outfilename);
  1257. sfree(outfiletmp);
  1258. return exit_status;
  1259. }