cmdgen.c 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657
  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. " cert-info print certificate information\n"
  122. " text output the key components as "
  123. "'name=0x####'\n"
  124. " -o specify output file\n"
  125. " -l equivalent to `-O fingerprint'\n"
  126. " -L equivalent to `-O public-openssh'\n"
  127. " -p equivalent to `-O public'\n"
  128. " --cert-info equivalent to `-O cert-info'\n"
  129. " --dump equivalent to `-O text'\n"
  130. " -E fptype specify fingerprint output type:\n"
  131. " sha256, md5, sha256-cert, md5-cert\n"
  132. " --certificate file incorporate a certificate into the key\n"
  133. " --remove-certificate remove any certificate from the key\n"
  134. " --reencrypt load a key and save it with fresh "
  135. "encryption\n"
  136. " --old-passphrase file\n"
  137. " specify file containing old key passphrase\n"
  138. " --new-passphrase file\n"
  139. " specify file containing new key passphrase\n"
  140. " --random-device device\n"
  141. " specify device to read entropy from (e.g. /dev/urandom)\n"
  142. " --primes <type> select prime-generation method:\n"
  143. " probable conventional probabilistic prime finding\n"
  144. " proven numbers that have been proven to be prime\n"
  145. " proven-even also try harder for an even distribution\n"
  146. " --strong-rsa use \"strong\" primes as RSA key factors\n"
  147. " --ppk-param <key>=<value>[,<key>=<value>,...]\n"
  148. " specify parameters when writing PuTTY private key file "
  149. "format:\n"
  150. " version PPK format version (min 2, max 3, "
  151. "default 3)\n"
  152. " kdf key derivation function (argon2id, "
  153. "argon2i, argon2d)\n"
  154. " memory Kbyte of memory to use in passphrase "
  155. "hash\n"
  156. " (default 8192)\n"
  157. " time approx milliseconds to hash for "
  158. "(default 100)\n"
  159. " passes number of hash passes to run "
  160. "(alternative to 'time')\n"
  161. " parallelism number of parallelisable threads in the "
  162. "hash function\n"
  163. " (default 1)\n"
  164. );
  165. }
  166. static bool move(char *from, char *to)
  167. {
  168. int ret;
  169. ret = rename(from, to);
  170. if (ret) {
  171. /*
  172. * This OS may require us to remove the original file first.
  173. */
  174. remove(to);
  175. ret = rename(from, to);
  176. }
  177. if (ret) {
  178. perror("puttygen: cannot move new file on to old one");
  179. return false;
  180. }
  181. return true;
  182. }
  183. static char *readpassphrase(const char *filename)
  184. {
  185. FILE *fp;
  186. char *line;
  187. fp = fopen(filename, "r");
  188. if (!fp) {
  189. fprintf(stderr, "puttygen: cannot open %s: %s\n",
  190. filename, strerror(errno));
  191. return NULL;
  192. }
  193. line = fgetline(fp);
  194. if (line)
  195. line[strcspn(line, "\r\n")] = '\0';
  196. else if (ferror(fp))
  197. fprintf(stderr, "puttygen: error reading from %s: %s\n",
  198. filename, strerror(errno));
  199. else /* empty file */
  200. line = dupstr("");
  201. fclose(fp);
  202. return line;
  203. }
  204. #define DEFAULT_RSADSA_BITS 2048
  205. static void spr_error(SeatPromptResult spr)
  206. {
  207. if (spr.kind == SPRK_SW_ABORT) {
  208. char *err = spr_get_error_message(spr);
  209. fprintf(stderr, "puttygen: unable to read passphrase: %s", err);
  210. sfree(err);
  211. }
  212. }
  213. /* For Unix in particular, but harmless if this main() is reused elsewhere */
  214. const bool buildinfo_gtk_relevant = false;
  215. int main(int argc, char **argv)
  216. {
  217. char *infile = NULL;
  218. Filename *infilename = NULL, *outfilename = NULL;
  219. LoadedFile *infile_lf = NULL;
  220. BinarySource *infile_bs = NULL;
  221. enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, EDDSA } keytype = NOKEYGEN;
  222. char *outfile = NULL, *outfiletmp = NULL;
  223. enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO,
  224. OPENSSH_NEW, SSHCOM, TEXT, CERTINFO } outtype = PRIVATE;
  225. int bits = -1;
  226. const char *comment = NULL;
  227. char *origcomment = NULL;
  228. bool change_passphrase = false, reencrypt = false;
  229. bool errs = false, nogo = false;
  230. int intype = SSH_KEYTYPE_UNOPENABLE;
  231. int sshver = 0;
  232. ssh2_userkey *ssh2key = NULL;
  233. RSAKey *ssh1key = NULL;
  234. strbuf *ssh2blob = NULL;
  235. char *ssh2alg = NULL;
  236. char *old_passphrase = NULL, *new_passphrase = NULL;
  237. bool load_encrypted;
  238. const char *random_device = NULL;
  239. char *certfile = NULL;
  240. bool remove_cert = false;
  241. int exit_status = 0;
  242. const PrimeGenerationPolicy *primegen = &primegen_probabilistic;
  243. bool strong_rsa = false;
  244. ppk_save_parameters params = ppk_save_default_parameters;
  245. FingerprintType fptype = SSH_FPTYPE_DEFAULT;
  246. if (is_interactive())
  247. progress_fp = stderr;
  248. #define RETURN(status) do { exit_status = (status); goto out; } while (0)
  249. /* ------------------------------------------------------------------
  250. * Parse the command line to figure out what we've been asked to do.
  251. */
  252. /*
  253. * If run with no arguments at all, print the usage message and
  254. * return success.
  255. */
  256. if (argc <= 1) {
  257. usage(true);
  258. RETURN(0);
  259. }
  260. /*
  261. * Parse command line arguments.
  262. */
  263. while (--argc) {
  264. char *p = *++argv;
  265. if (p[0] == '-' && p[1]) {
  266. /*
  267. * An option.
  268. */
  269. while (p && *++p) {
  270. char c = *p;
  271. switch (c) {
  272. case '-': {
  273. /*
  274. * Long option.
  275. */
  276. char *opt, *val;
  277. opt = p++; /* opt will have _one_ leading - */
  278. while (*p && *p != '=')
  279. p++; /* find end of option */
  280. if (*p == '=') {
  281. *p++ = '\0';
  282. val = p;
  283. } else
  284. val = NULL;
  285. if (!strcmp(opt, "-help")) {
  286. if (val) {
  287. errs = true;
  288. fprintf(stderr, "puttygen: option `-%s'"
  289. " expects no argument\n", opt);
  290. } else {
  291. help();
  292. nogo = true;
  293. }
  294. } else if (!strcmp(opt, "-version")) {
  295. if (val) {
  296. errs = true;
  297. fprintf(stderr, "puttygen: option `-%s'"
  298. " expects no argument\n", opt);
  299. } else {
  300. showversion();
  301. nogo = true;
  302. }
  303. } else if (!strcmp(opt, "-pgpfp")) {
  304. if (val) {
  305. errs = true;
  306. fprintf(stderr, "puttygen: option `-%s'"
  307. " expects no argument\n", opt);
  308. } else {
  309. /* support --pgpfp for consistency */
  310. pgp_fingerprints();
  311. nogo = true;
  312. }
  313. } else if (!strcmp(opt, "-old-passphrase")) {
  314. if (!val && argc > 1)
  315. --argc, val = *++argv;
  316. if (!val) {
  317. errs = true;
  318. fprintf(stderr, "puttygen: option `-%s'"
  319. " expects an argument\n", opt);
  320. } else {
  321. old_passphrase = readpassphrase(val);
  322. if (!old_passphrase)
  323. errs = true;
  324. }
  325. } else if (!strcmp(opt, "-new-passphrase")) {
  326. if (!val && argc > 1)
  327. --argc, val = *++argv;
  328. if (!val) {
  329. errs = true;
  330. fprintf(stderr, "puttygen: option `-%s'"
  331. " expects an argument\n", opt);
  332. } else {
  333. new_passphrase = readpassphrase(val);
  334. if (!new_passphrase)
  335. errs = true;
  336. }
  337. } else if (!strcmp(opt, "-random-device")) {
  338. if (!val && argc > 1)
  339. --argc, val = *++argv;
  340. if (!val) {
  341. errs = true;
  342. fprintf(stderr, "puttygen: option `-%s'"
  343. " expects an argument\n", opt);
  344. } else {
  345. random_device = val;
  346. }
  347. } else if (!strcmp(opt, "-dump")) {
  348. outtype = TEXT;
  349. } else if (!strcmp(opt, "-cert-info") ||
  350. !strcmp(opt, "-certinfo") ||
  351. !strcmp(opt, "-cert_info")) {
  352. outtype = CERTINFO;
  353. } else if (!strcmp(opt, "-primes")) {
  354. if (!val && argc > 1)
  355. --argc, val = *++argv;
  356. if (!val) {
  357. errs = true;
  358. fprintf(stderr, "puttygen: option `-%s'"
  359. " expects an argument\n", opt);
  360. } else if (!strcmp(val, "probable") ||
  361. !strcmp(val, "probabilistic")) {
  362. primegen = &primegen_probabilistic;
  363. } else if (!strcmp(val, "provable") ||
  364. !strcmp(val, "proven") ||
  365. !strcmp(val, "simple") ||
  366. !strcmp(val, "maurer-simple")) {
  367. primegen = &primegen_provable_maurer_simple;
  368. } else if (!strcmp(val, "provable-even") ||
  369. !strcmp(val, "proven-even") ||
  370. !strcmp(val, "even") ||
  371. !strcmp(val, "complex") ||
  372. !strcmp(val, "maurer-complex")) {
  373. primegen = &primegen_provable_maurer_complex;
  374. } else {
  375. errs = true;
  376. fprintf(stderr, "puttygen: unrecognised prime-"
  377. "generation mode `%s'\n", val);
  378. }
  379. } else if (!strcmp(opt, "-strong-rsa")) {
  380. strong_rsa = true;
  381. } else if (!strcmp(opt, "-certificate")) {
  382. if (!val && argc > 1)
  383. --argc, val = *++argv;
  384. if (!val) {
  385. errs = true;
  386. fprintf(stderr, "puttygen: option `-%s'"
  387. " expects an argument\n", opt);
  388. } else {
  389. certfile = val;
  390. }
  391. } else if (!strcmp(opt, "-remove-certificate")) {
  392. remove_cert = true;
  393. } else if (!strcmp(opt, "-reencrypt")) {
  394. reencrypt = true;
  395. } else if (!strcmp(opt, "-ppk-param") ||
  396. !strcmp(opt, "-ppk-params")) {
  397. if (!val && argc > 1)
  398. --argc, val = *++argv;
  399. if (!val) {
  400. errs = true;
  401. fprintf(stderr, "puttygen: option `-%s'"
  402. " expects an argument\n", opt);
  403. } else {
  404. char *nextval;
  405. for (; val; val = nextval) {
  406. nextval = strchr(val, ',');
  407. if (nextval)
  408. *nextval++ = '\0';
  409. char *optvalue = strchr(val, '=');
  410. if (!optvalue) {
  411. errs = true;
  412. fprintf(stderr, "puttygen: PPK parameter "
  413. "'%s' expected a value\n", val);
  414. continue;
  415. }
  416. *optvalue++ = '\0';
  417. /* Non-numeric options */
  418. if (!strcmp(val, "kdf")) {
  419. if (!strcmp(optvalue, "Argon2id") ||
  420. !strcmp(optvalue, "argon2id")) {
  421. params.argon2_flavour = Argon2id;
  422. } else if (!strcmp(optvalue, "Argon2i") ||
  423. !strcmp(optvalue, "argon2i")) {
  424. params.argon2_flavour = Argon2i;
  425. } else if (!strcmp(optvalue, "Argon2d") ||
  426. !strcmp(optvalue, "argon2d")) {
  427. params.argon2_flavour = Argon2d;
  428. } else {
  429. errs = true;
  430. fprintf(stderr, "puttygen: unrecognise"
  431. "d kdf '%s'\n", optvalue);
  432. }
  433. continue;
  434. }
  435. char *end;
  436. unsigned long n = strtoul(optvalue, &end, 0);
  437. if (!*optvalue || *end) {
  438. errs = true;
  439. fprintf(stderr, "puttygen: value '%s' for "
  440. "PPK parameter '%s': expected a "
  441. "number\n", optvalue, val);
  442. continue;
  443. }
  444. if (!strcmp(val, "version")) {
  445. params.fmt_version = n;
  446. } else if (!strcmp(val, "memory") ||
  447. !strcmp(val, "mem")) {
  448. params.argon2_mem = n;
  449. } else if (!strcmp(val, "time")) {
  450. params.argon2_passes_auto = true;
  451. params.argon2_milliseconds = n;
  452. } else if (!strcmp(val, "passes")) {
  453. params.argon2_passes_auto = false;
  454. params.argon2_passes = n;
  455. } else if (!strcmp(val, "parallelism") ||
  456. !strcmp(val, "parallel")) {
  457. params.argon2_parallelism = n;
  458. } else {
  459. errs = true;
  460. fprintf(stderr, "puttygen: unrecognised "
  461. "PPK parameter '%s'\n", val);
  462. continue;
  463. }
  464. }
  465. }
  466. } else {
  467. errs = true;
  468. fprintf(stderr,
  469. "puttygen: no such option `-%s'\n", opt);
  470. }
  471. p = NULL;
  472. break;
  473. }
  474. case 'h':
  475. case 'V':
  476. case 'P':
  477. case 'l':
  478. case 'L':
  479. case 'p':
  480. case 'q':
  481. /*
  482. * Option requiring no parameter.
  483. */
  484. switch (c) {
  485. case 'h':
  486. help();
  487. nogo = true;
  488. break;
  489. case 'V':
  490. showversion();
  491. nogo = true;
  492. break;
  493. case 'P':
  494. change_passphrase = true;
  495. break;
  496. case 'l':
  497. outtype = FP;
  498. break;
  499. case 'L':
  500. outtype = PUBLICO;
  501. break;
  502. case 'p':
  503. outtype = PUBLIC;
  504. break;
  505. case 'q':
  506. progress_fp = NULL;
  507. break;
  508. }
  509. break;
  510. case 't':
  511. case 'b':
  512. case 'C':
  513. case 'O':
  514. case 'o':
  515. case 'E':
  516. /*
  517. * Option requiring parameter.
  518. */
  519. p++;
  520. if (!*p && argc > 1)
  521. --argc, p = *++argv;
  522. else if (!*p) {
  523. fprintf(stderr, "puttygen: option `-%c' expects a"
  524. " parameter\n", c);
  525. errs = true;
  526. }
  527. /*
  528. * Now c is the option and p is the parameter.
  529. */
  530. switch (c) {
  531. case 't':
  532. if (!strcmp(p, "rsa") || !strcmp(p, "rsa2"))
  533. keytype = RSA2, sshver = 2;
  534. else if (!strcmp(p, "rsa1"))
  535. keytype = RSA1, sshver = 1;
  536. else if (!strcmp(p, "dsa") || !strcmp(p, "dss"))
  537. keytype = DSA, sshver = 2;
  538. else if (!strcmp(p, "ecdsa"))
  539. keytype = ECDSA, sshver = 2;
  540. else if (!strcmp(p, "eddsa"))
  541. keytype = EDDSA, sshver = 2;
  542. else if (!strcmp(p, "ed25519"))
  543. keytype = EDDSA, bits = 255, sshver = 2;
  544. else if (!strcmp(p, "ed448"))
  545. keytype = EDDSA, bits = 448, sshver = 2;
  546. else {
  547. fprintf(stderr,
  548. "puttygen: unknown key type `%s'\n", p);
  549. errs = true;
  550. }
  551. break;
  552. case 'b':
  553. bits = atoi(p);
  554. break;
  555. case 'C':
  556. comment = p;
  557. break;
  558. case 'O':
  559. if (!strcmp(p, "public"))
  560. outtype = PUBLIC;
  561. else if (!strcmp(p, "public-openssh"))
  562. outtype = PUBLICO;
  563. else if (!strcmp(p, "private"))
  564. outtype = PRIVATE;
  565. else if (!strcmp(p, "fingerprint"))
  566. outtype = FP;
  567. else if (!strcmp(p, "private-openssh"))
  568. outtype = OPENSSH_AUTO, sshver = 2;
  569. else if (!strcmp(p, "private-openssh-new"))
  570. outtype = OPENSSH_NEW, sshver = 2;
  571. else if (!strcmp(p, "private-sshcom"))
  572. outtype = SSHCOM, sshver = 2;
  573. else if (!strcmp(p, "text"))
  574. outtype = TEXT;
  575. else if (!strcmp(p, "cert-info"))
  576. outtype = CERTINFO;
  577. else {
  578. fprintf(stderr,
  579. "puttygen: unknown output type `%s'\n", p);
  580. errs = true;
  581. }
  582. break;
  583. case 'o':
  584. outfile = p;
  585. break;
  586. case 'E':
  587. if (!strcmp(p, "md5"))
  588. fptype = SSH_FPTYPE_MD5;
  589. else if (!strcmp(p, "sha256"))
  590. fptype = SSH_FPTYPE_SHA256;
  591. else if (!strcmp(p, "md5-cert"))
  592. fptype = SSH_FPTYPE_MD5_CERT;
  593. else if (!strcmp(p, "sha256-cert"))
  594. fptype = SSH_FPTYPE_SHA256_CERT;
  595. else {
  596. fprintf(stderr, "puttygen: unknown fingerprint "
  597. "type `%s'\n", p);
  598. errs = true;
  599. }
  600. break;
  601. }
  602. p = NULL; /* prevent continued processing */
  603. break;
  604. default:
  605. /*
  606. * Unrecognised option.
  607. */
  608. errs = true;
  609. fprintf(stderr, "puttygen: no such option `-%c'\n", c);
  610. break;
  611. }
  612. }
  613. } else {
  614. /*
  615. * A non-option argument.
  616. */
  617. if (!infile)
  618. infile = p;
  619. else {
  620. errs = true;
  621. fprintf(stderr, "puttygen: cannot handle more than one"
  622. " input file\n");
  623. }
  624. }
  625. }
  626. if (bits == -1) {
  627. /*
  628. * No explicit key size was specified. Default varies
  629. * depending on key type.
  630. */
  631. switch (keytype) {
  632. case ECDSA:
  633. bits = 384;
  634. break;
  635. case EDDSA:
  636. bits = 255;
  637. break;
  638. default:
  639. bits = DEFAULT_RSADSA_BITS;
  640. break;
  641. }
  642. }
  643. if (keytype == ECDSA || keytype == EDDSA) {
  644. const char *name = (keytype == ECDSA ? "ECDSA" : "EdDSA");
  645. const int *valid_lengths = (keytype == ECDSA ? ec_nist_curve_lengths :
  646. ec_ed_curve_lengths);
  647. size_t n_lengths = (keytype == ECDSA ? n_ec_nist_curve_lengths :
  648. n_ec_ed_curve_lengths);
  649. bool (*alg_and_curve_by_bits)(int, const struct ec_curve **,
  650. const ssh_keyalg **) =
  651. (keytype == ECDSA ? ec_nist_alg_and_curve_by_bits :
  652. ec_ed_alg_and_curve_by_bits);
  653. const struct ec_curve *curve;
  654. const ssh_keyalg *alg;
  655. if (!alg_and_curve_by_bits(bits, &curve, &alg)) {
  656. fprintf(stderr, "puttygen: invalid bits for %s, choose", name);
  657. for (size_t i = 0; i < n_lengths; i++)
  658. fprintf(stderr, "%s%d", (i == 0 ? " " :
  659. i == n_lengths-1 ? " or " : ", "),
  660. valid_lengths[i]);
  661. fputc('\n', stderr);
  662. errs = true;
  663. }
  664. }
  665. if (keytype == RSA2 || keytype == RSA1 || keytype == DSA) {
  666. if (bits < 256) {
  667. fprintf(stderr, "puttygen: cannot generate %s keys shorter than"
  668. " 256 bits\n", (keytype == DSA ? "DSA" : "RSA"));
  669. errs = true;
  670. } else if (bits < DEFAULT_RSADSA_BITS) {
  671. fprintf(stderr, "puttygen: warning: %s keys shorter than"
  672. " %d bits are probably not secure\n",
  673. (keytype == DSA ? "DSA" : "RSA"), DEFAULT_RSADSA_BITS);
  674. /* but this is just a warning, so proceed anyway */
  675. }
  676. }
  677. if (errs)
  678. RETURN(1);
  679. if (nogo)
  680. RETURN(0);
  681. /*
  682. * If run with at least one argument _but_ not the required
  683. * ones, fail with an error.
  684. */
  685. if (!infile && keytype == NOKEYGEN) {
  686. fprintf(stderr, "puttygen: expected an input key file name, "
  687. "or -t for a type of key to generate\n");
  688. RETURN(1);
  689. }
  690. /* ------------------------------------------------------------------
  691. * Figure out further details of exactly what we're going to do.
  692. */
  693. /*
  694. * Bomb out if we've been asked to both load and generate a
  695. * key.
  696. */
  697. if (keytype != NOKEYGEN && infile) {
  698. fprintf(stderr, "puttygen: cannot both load and generate a key\n");
  699. RETURN(1);
  700. }
  701. /*
  702. * We must save the private part when generating a new key.
  703. */
  704. if (keytype != NOKEYGEN &&
  705. (outtype != PRIVATE && outtype != OPENSSH_AUTO &&
  706. outtype != OPENSSH_NEW && outtype != SSHCOM && outtype != TEXT)) {
  707. fprintf(stderr, "puttygen: this would generate a new key but "
  708. "discard the private part\n");
  709. RETURN(1);
  710. }
  711. /*
  712. * Analyse the type of the input file, in case this affects our
  713. * course of action.
  714. */
  715. if (infile) {
  716. const char *load_error;
  717. infilename = filename_from_str(infile);
  718. if (!strcmp(infile, "-"))
  719. infile_lf = lf_load_keyfile_fp(stdin, &load_error);
  720. else
  721. infile_lf = lf_load_keyfile(infilename, &load_error);
  722. if (!infile_lf) {
  723. fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
  724. infile, load_error);
  725. RETURN(1);
  726. }
  727. infile_bs = BinarySource_UPCAST(infile_lf);
  728. intype = key_type_s(infile_bs);
  729. BinarySource_REWIND(infile_bs);
  730. switch (intype) {
  731. case SSH_KEYTYPE_UNOPENABLE:
  732. case SSH_KEYTYPE_UNKNOWN:
  733. fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
  734. infile, key_type_to_str(intype));
  735. RETURN(1);
  736. case SSH_KEYTYPE_SSH1:
  737. case SSH_KEYTYPE_SSH1_PUBLIC:
  738. if (sshver == 2) {
  739. fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys"
  740. " not supported\n");
  741. RETURN(1);
  742. }
  743. sshver = 1;
  744. break;
  745. case SSH_KEYTYPE_SSH2:
  746. case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
  747. case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
  748. case SSH_KEYTYPE_OPENSSH_PEM:
  749. case SSH_KEYTYPE_OPENSSH_NEW:
  750. case SSH_KEYTYPE_SSHCOM:
  751. if (sshver == 1) {
  752. fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys"
  753. " not supported\n");
  754. RETURN(1);
  755. }
  756. sshver = 2;
  757. break;
  758. case SSH_KEYTYPE_OPENSSH_AUTO:
  759. default:
  760. unreachable("Should never see these types on an input file");
  761. }
  762. }
  763. /*
  764. * Determine the default output file, if none is provided.
  765. *
  766. * This will usually be equal to stdout, except that if the
  767. * input and output file formats are the same then the default
  768. * output is to overwrite the input.
  769. *
  770. * Also in this code, we bomb out if the input and output file
  771. * formats are the same and no other action is performed.
  772. */
  773. if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||
  774. (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||
  775. (intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) ||
  776. (intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) ||
  777. (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
  778. if (!outfile) {
  779. outfile = infile;
  780. outfiletmp = dupcat(outfile, ".tmp");
  781. }
  782. if (!change_passphrase && !comment && !reencrypt && !certfile &&
  783. !remove_cert) {
  784. fprintf(stderr, "puttygen: this command would perform no useful"
  785. " action\n");
  786. RETURN(1);
  787. }
  788. } else {
  789. if (!outfile) {
  790. /*
  791. * Bomb out rather than automatically choosing to write
  792. * a private key file to stdout.
  793. */
  794. if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
  795. outtype == OPENSSH_NEW || outtype == SSHCOM) {
  796. fprintf(stderr, "puttygen: need to specify an output file\n");
  797. RETURN(1);
  798. }
  799. }
  800. }
  801. /*
  802. * Figure out whether we need to load the encrypted part of the
  803. * key. This will be the case if (a) we need to write out
  804. * a private key format, (b) the entire input key file is
  805. * encrypted, or (c) we're outputting TEXT, in which case we
  806. * want all of the input file including private material if it
  807. * exists.
  808. */
  809. bool intype_entirely_encrypted =
  810. intype == SSH_KEYTYPE_OPENSSH_PEM ||
  811. intype == SSH_KEYTYPE_OPENSSH_NEW ||
  812. intype == SSH_KEYTYPE_SSHCOM;
  813. bool intype_has_private =
  814. !(intype == SSH_KEYTYPE_SSH1_PUBLIC ||
  815. intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
  816. intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  817. bool outtype_has_private =
  818. outtype == PRIVATE || outtype == OPENSSH_AUTO ||
  819. outtype == OPENSSH_NEW || outtype == SSHCOM;
  820. if (outtype_has_private || intype_entirely_encrypted ||
  821. (outtype == TEXT && intype_has_private))
  822. load_encrypted = true;
  823. else
  824. load_encrypted = false;
  825. if (load_encrypted && !intype_has_private) {
  826. fprintf(stderr, "puttygen: cannot perform this action on a "
  827. "public-key-only input file\n");
  828. RETURN(1);
  829. }
  830. /*
  831. * Check consistency properties relating to certificates.
  832. */
  833. if (certfile && !(sshver == 2 && intype_has_private &&
  834. outtype_has_private && infile)) {
  835. fprintf(stderr, "puttygen: certificates can only be added to "
  836. "existing SSH-2 private key files\n");
  837. RETURN(1);
  838. }
  839. if (remove_cert && !(sshver == 2 && infile)) {
  840. fprintf(stderr, "puttygen: certificates can only be removed from "
  841. "existing SSH-2 key files\n");
  842. RETURN(1);
  843. }
  844. if (certfile && remove_cert) {
  845. fprintf(stderr, "puttygen: cannot both add and remove a "
  846. "certificate\n");
  847. RETURN(1);
  848. }
  849. /* ------------------------------------------------------------------
  850. * Now we're ready to actually do some stuff.
  851. */
  852. /*
  853. * Either load or generate a key.
  854. */
  855. if (keytype != NOKEYGEN) {
  856. char *entropy;
  857. char default_comment[80];
  858. struct tm tm;
  859. tm = ltime();
  860. if (keytype == DSA)
  861. strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
  862. else if (keytype == ECDSA)
  863. strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
  864. else if (keytype == EDDSA && bits == 255)
  865. strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm);
  866. else if (keytype == EDDSA)
  867. strftime(default_comment, 30, "eddsa-key-%Y%m%d", &tm);
  868. else
  869. strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
  870. entropy = get_random_data(bits / 8, random_device);
  871. if (!entropy) {
  872. fprintf(stderr, "puttygen: failed to collect entropy, "
  873. "could not generate key\n");
  874. RETURN(1);
  875. }
  876. random_setup_special();
  877. random_reseed(make_ptrlen(entropy, bits / 8));
  878. smemclr(entropy, bits/8);
  879. sfree(entropy);
  880. PrimeGenerationContext *pgc = primegen_new_context(primegen);
  881. if (keytype == DSA) {
  882. struct dsa_key *dsakey = snew(struct dsa_key);
  883. dsa_generate(dsakey, bits, pgc, &cmdgen_progress);
  884. ssh2key = snew(ssh2_userkey);
  885. ssh2key->key = &dsakey->sshk;
  886. ssh1key = NULL;
  887. } else if (keytype == ECDSA) {
  888. struct ecdsa_key *ek = snew(struct ecdsa_key);
  889. ecdsa_generate(ek, bits);
  890. ssh2key = snew(ssh2_userkey);
  891. ssh2key->key = &ek->sshk;
  892. ssh1key = NULL;
  893. } else if (keytype == EDDSA) {
  894. struct eddsa_key *ek = snew(struct eddsa_key);
  895. eddsa_generate(ek, bits);
  896. ssh2key = snew(ssh2_userkey);
  897. ssh2key->key = &ek->sshk;
  898. ssh1key = NULL;
  899. } else {
  900. RSAKey *rsakey = snew(RSAKey);
  901. rsa_generate(rsakey, bits, strong_rsa, pgc, &cmdgen_progress);
  902. rsakey->comment = NULL;
  903. if (keytype == RSA1) {
  904. ssh1key = rsakey;
  905. } else {
  906. ssh2key = snew(ssh2_userkey);
  907. ssh2key->key = &rsakey->sshk;
  908. }
  909. }
  910. primegen_free_context(pgc);
  911. if (ssh2key)
  912. ssh2key->comment = dupstr(default_comment);
  913. if (ssh1key)
  914. ssh1key->comment = dupstr(default_comment);
  915. } else {
  916. const char *error = NULL;
  917. bool encrypted;
  918. assert(infile != NULL);
  919. sfree(origcomment);
  920. origcomment = NULL;
  921. /*
  922. * Find out whether the input key is encrypted.
  923. */
  924. if (intype == SSH_KEYTYPE_SSH1)
  925. encrypted = rsa1_encrypted_s(infile_bs, &origcomment);
  926. else if (intype == SSH_KEYTYPE_SSH2)
  927. encrypted = ppk_encrypted_s(infile_bs, &origcomment);
  928. else
  929. encrypted = import_encrypted_s(infilename, infile_bs,
  930. intype, &origcomment);
  931. BinarySource_REWIND(infile_bs);
  932. /*
  933. * If so, ask for a passphrase.
  934. */
  935. if (encrypted && load_encrypted) {
  936. if (!old_passphrase) {
  937. prompts_t *p = new_prompts();
  938. SeatPromptResult spr;
  939. p->to_server = false;
  940. p->from_server = false;
  941. p->name = dupstr("SSH key passphrase");
  942. add_prompt(p, dupstr("Enter passphrase to load key: "), false);
  943. spr = console_get_userpass_input(p);
  944. assert(spr.kind != SPRK_INCOMPLETE);
  945. if (spr_is_abort(spr)) {
  946. free_prompts(p);
  947. spr_error(spr);
  948. RETURN(1);
  949. } else {
  950. old_passphrase = prompt_get_result(p->prompts[0]);
  951. free_prompts(p);
  952. }
  953. }
  954. } else {
  955. old_passphrase = NULL;
  956. }
  957. switch (intype) {
  958. int ret;
  959. case SSH_KEYTYPE_SSH1:
  960. case SSH_KEYTYPE_SSH1_PUBLIC:
  961. ssh1key = snew(RSAKey);
  962. memset(ssh1key, 0, sizeof(RSAKey));
  963. if (!load_encrypted) {
  964. strbuf *blob;
  965. BinarySource src[1];
  966. sfree(origcomment);
  967. origcomment = NULL;
  968. blob = strbuf_new();
  969. ret = rsa1_loadpub_s(infile_bs, BinarySink_UPCAST(blob),
  970. &origcomment, &error);
  971. BinarySource_BARE_INIT(src, blob->u, blob->len);
  972. get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST);
  973. strbuf_free(blob);
  974. ssh1key->comment = dupstr(origcomment);
  975. ssh1key->private_exponent = NULL;
  976. ssh1key->p = NULL;
  977. ssh1key->q = NULL;
  978. ssh1key->iqmp = NULL;
  979. } else {
  980. ret = rsa1_load_s(infile_bs, ssh1key, old_passphrase, &error);
  981. }
  982. BinarySource_REWIND(infile_bs);
  983. if (ret > 0)
  984. error = NULL;
  985. else if (!error)
  986. error = "unknown error";
  987. break;
  988. case SSH_KEYTYPE_SSH2:
  989. case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
  990. case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
  991. if (!load_encrypted) {
  992. sfree(origcomment);
  993. origcomment = NULL;
  994. ssh2blob = strbuf_new();
  995. if (ppk_loadpub_s(infile_bs, &ssh2alg,
  996. BinarySink_UPCAST(ssh2blob),
  997. &origcomment, &error)) {
  998. const ssh_keyalg *alg = find_pubkey_alg(ssh2alg);
  999. if (alg)
  1000. bits = ssh_key_public_bits(
  1001. alg, ptrlen_from_strbuf(ssh2blob));
  1002. else
  1003. bits = -1;
  1004. } else {
  1005. strbuf_free(ssh2blob);
  1006. ssh2blob = NULL;
  1007. }
  1008. sfree(ssh2alg);
  1009. } else {
  1010. ssh2key = ppk_load_s(infile_bs, old_passphrase, &error);
  1011. }
  1012. BinarySource_REWIND(infile_bs);
  1013. if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
  1014. error = NULL;
  1015. else if (!error) {
  1016. if (ssh2key == SSH2_WRONG_PASSPHRASE)
  1017. error = "wrong passphrase";
  1018. else
  1019. error = "unknown error";
  1020. }
  1021. break;
  1022. case SSH_KEYTYPE_OPENSSH_PEM:
  1023. case SSH_KEYTYPE_OPENSSH_NEW:
  1024. case SSH_KEYTYPE_SSHCOM:
  1025. ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error);
  1026. if (ssh2key) {
  1027. if (ssh2key != SSH2_WRONG_PASSPHRASE)
  1028. error = NULL;
  1029. else
  1030. error = "wrong passphrase";
  1031. } else if (!error)
  1032. error = "unknown error";
  1033. break;
  1034. default:
  1035. unreachable("bad input key type");
  1036. }
  1037. if (error) {
  1038. fprintf(stderr, "puttygen: error loading `%s': %s\n",
  1039. infile, error);
  1040. RETURN(1);
  1041. }
  1042. }
  1043. /*
  1044. * Change the comment if asked to.
  1045. */
  1046. if (comment) {
  1047. if (sshver == 1) {
  1048. assert(ssh1key);
  1049. sfree(ssh1key->comment);
  1050. ssh1key->comment = dupstr(comment);
  1051. } else {
  1052. assert(ssh2key);
  1053. sfree(ssh2key->comment);
  1054. ssh2key->comment = dupstr(comment);
  1055. }
  1056. }
  1057. /*
  1058. * Swap out the public key for a different one, if asked to.
  1059. */
  1060. if (certfile) {
  1061. Filename *certfilename = filename_from_str(certfile);
  1062. LoadedFile *certfile_lf;
  1063. const char *error = NULL;
  1064. if (!strcmp(certfile, "-"))
  1065. certfile_lf = lf_load_keyfile_fp(stdin, &error);
  1066. else
  1067. certfile_lf = lf_load_keyfile(certfilename, &error);
  1068. filename_free(certfilename);
  1069. if (!certfile_lf) {
  1070. fprintf(stderr, "puttygen: unable to load certificate file `%s': "
  1071. "%s\n", certfile, error);
  1072. RETURN(1);
  1073. }
  1074. char *algname = NULL;
  1075. char *comment = NULL;
  1076. strbuf *pub = strbuf_new();
  1077. if (!ppk_loadpub_s(BinarySource_UPCAST(certfile_lf), &algname,
  1078. BinarySink_UPCAST(pub), &comment, &error)) {
  1079. fprintf(stderr, "puttygen: unable to load certificate file `%s': "
  1080. "%s\n", certfile, error);
  1081. strbuf_free(pub);
  1082. sfree(algname);
  1083. sfree(comment);
  1084. lf_free(certfile_lf);
  1085. RETURN(1);
  1086. }
  1087. lf_free(certfile_lf);
  1088. sfree(comment);
  1089. const ssh_keyalg *alg = find_pubkey_alg(algname);
  1090. if (!alg) {
  1091. fprintf(stderr, "puttygen: certificate file `%s' has unsupported "
  1092. "algorithm name `%s'\n", certfile, algname);
  1093. strbuf_free(pub);
  1094. sfree(algname);
  1095. RETURN(1);
  1096. }
  1097. sfree(algname);
  1098. /* Check the two public keys match apart from certificates */
  1099. strbuf *old_basepub = strbuf_new();
  1100. ssh_key_public_blob(ssh_key_base_key(ssh2key->key),
  1101. BinarySink_UPCAST(old_basepub));
  1102. ssh_key *new_pubkey = ssh_key_new_pub(alg, ptrlen_from_strbuf(pub));
  1103. strbuf *new_basepub = strbuf_new();
  1104. ssh_key_public_blob(ssh_key_base_key(new_pubkey),
  1105. BinarySink_UPCAST(new_basepub));
  1106. ssh_key_free(new_pubkey);
  1107. bool match = ptrlen_eq_ptrlen(ptrlen_from_strbuf(old_basepub),
  1108. ptrlen_from_strbuf(new_basepub));
  1109. strbuf_free(old_basepub);
  1110. strbuf_free(new_basepub);
  1111. if (!match) {
  1112. fprintf(stderr, "puttygen: certificate in `%s' does not match "
  1113. "public key in `%s'\n", certfile, infile);
  1114. strbuf_free(pub);
  1115. RETURN(1);
  1116. }
  1117. strbuf *priv = strbuf_new_nm();
  1118. ssh_key_private_blob(ssh2key->key, BinarySink_UPCAST(priv));
  1119. ssh_key *newkey = ssh_key_new_priv(
  1120. alg, ptrlen_from_strbuf(pub), ptrlen_from_strbuf(priv));
  1121. strbuf_free(pub);
  1122. strbuf_free(priv);
  1123. if (!newkey) {
  1124. fprintf(stderr, "puttygen: unable to combine certificate in `%s' "
  1125. "with private key\n", certfile);
  1126. RETURN(1);
  1127. }
  1128. ssh_key_free(ssh2key->key);
  1129. ssh2key->key = newkey;
  1130. } else if (remove_cert) {
  1131. /*
  1132. * Removing a certificate can be meaningfully done to a pure
  1133. * public key blob, as well as a full key pair.
  1134. */
  1135. if (ssh2key) {
  1136. ssh_key *newkey = ssh_key_clone(ssh_key_base_key(ssh2key->key));
  1137. ssh_key_free(ssh2key->key);
  1138. ssh2key->key = newkey;
  1139. } else if (ssh2blob) {
  1140. ptrlen algname = pubkey_blob_to_alg_name(
  1141. ptrlen_from_strbuf(ssh2blob));
  1142. const ssh_keyalg *alg = find_pubkey_alg_len(algname);
  1143. if (!alg) {
  1144. fprintf(stderr, "puttygen: input file `%s' has unsupported "
  1145. "algorithm name `%.*s'\n", infile,
  1146. PTRLEN_PRINTF(algname));
  1147. RETURN(1);
  1148. }
  1149. ssh_key *tmpkey = ssh_key_new_pub(
  1150. alg, ptrlen_from_strbuf(ssh2blob));
  1151. strbuf_clear(ssh2blob);
  1152. ssh_key_public_blob(ssh_key_base_key(tmpkey),
  1153. BinarySink_UPCAST(ssh2blob));
  1154. ssh_key_free(tmpkey);
  1155. }
  1156. }
  1157. /*
  1158. * Unless we're changing the passphrase, the old one (if any) is a
  1159. * reasonable default.
  1160. */
  1161. if (!change_passphrase && old_passphrase && !new_passphrase)
  1162. new_passphrase = dupstr(old_passphrase);
  1163. /*
  1164. * Prompt for a new passphrase if we have been asked to, or if
  1165. * we have just generated a key.
  1166. *
  1167. * In the latter case, an exception is if we're producing text
  1168. * output, because that output format doesn't support encryption
  1169. * in any case.
  1170. */
  1171. if (!new_passphrase && (change_passphrase ||
  1172. (keytype != NOKEYGEN && outtype != TEXT))) {
  1173. prompts_t *p = new_prompts();
  1174. SeatPromptResult spr;
  1175. p->to_server = false;
  1176. p->from_server = false;
  1177. p->name = dupstr("New SSH key passphrase");
  1178. add_prompt(p, dupstr("Enter passphrase to save key: "), false);
  1179. add_prompt(p, dupstr("Re-enter passphrase to verify: "), false);
  1180. spr = console_get_userpass_input(p);
  1181. assert(spr.kind != SPRK_INCOMPLETE);
  1182. if (spr_is_abort(spr)) {
  1183. free_prompts(p);
  1184. spr_error(spr);
  1185. RETURN(1);
  1186. } else {
  1187. if (strcmp(prompt_get_result_ref(p->prompts[0]),
  1188. prompt_get_result_ref(p->prompts[1]))) {
  1189. free_prompts(p);
  1190. fprintf(stderr, "puttygen: passphrases do not match\n");
  1191. RETURN(1);
  1192. }
  1193. new_passphrase = prompt_get_result(p->prompts[0]);
  1194. free_prompts(p);
  1195. }
  1196. }
  1197. if (new_passphrase && !*new_passphrase) {
  1198. sfree(new_passphrase);
  1199. new_passphrase = NULL;
  1200. }
  1201. /*
  1202. * Write output.
  1203. *
  1204. * (In the case where outfile and outfiletmp are both NULL,
  1205. * there is no semantic reason to initialise outfilename at
  1206. * all; but we have to write _something_ to it or some compiler
  1207. * will probably complain that it might be used uninitialised.)
  1208. */
  1209. if (outfiletmp)
  1210. outfilename = filename_from_str(outfiletmp);
  1211. else
  1212. outfilename = filename_from_str(outfile ? outfile : "");
  1213. switch (outtype) {
  1214. bool ret;
  1215. int real_outtype;
  1216. case PRIVATE:
  1217. random_ref(); /* we'll need a few random bytes in the save file */
  1218. if (sshver == 1) {
  1219. assert(ssh1key);
  1220. ret = rsa1_save_f(outfilename, ssh1key, new_passphrase);
  1221. if (!ret) {
  1222. fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
  1223. RETURN(1);
  1224. }
  1225. } else {
  1226. assert(ssh2key);
  1227. ret = ppk_save_f(outfilename, ssh2key, new_passphrase, &params);
  1228. if (!ret) {
  1229. fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
  1230. RETURN(1);
  1231. }
  1232. }
  1233. if (outfiletmp) {
  1234. if (!move(outfiletmp, outfile))
  1235. RETURN(1); /* rename failed */
  1236. }
  1237. break;
  1238. case PUBLIC:
  1239. case PUBLICO: {
  1240. FILE *fp;
  1241. if (outfile) {
  1242. fp = f_open(outfilename, "w", false);
  1243. if (!fp) {
  1244. fprintf(stderr, "unable to open output file\n");
  1245. exit(1);
  1246. }
  1247. } else {
  1248. fp = stdout;
  1249. }
  1250. if (sshver == 1) {
  1251. ssh1_write_pubkey(fp, ssh1key);
  1252. } else {
  1253. if (!ssh2blob) {
  1254. assert(ssh2key);
  1255. ssh2blob = strbuf_new();
  1256. ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob));
  1257. }
  1258. ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
  1259. ssh2blob->s, ssh2blob->len,
  1260. (outtype == PUBLIC ?
  1261. SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 :
  1262. SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH));
  1263. }
  1264. if (outfile)
  1265. fclose(fp);
  1266. break;
  1267. }
  1268. case FP: {
  1269. FILE *fp;
  1270. char *fingerprint;
  1271. if (sshver == 1) {
  1272. assert(ssh1key);
  1273. fingerprint = rsa_ssh1_fingerprint(ssh1key);
  1274. } else {
  1275. if (ssh2key) {
  1276. fingerprint = ssh2_fingerprint(ssh2key->key, fptype);
  1277. } else {
  1278. assert(ssh2blob);
  1279. fingerprint = ssh2_fingerprint_blob(
  1280. ptrlen_from_strbuf(ssh2blob), fptype);
  1281. }
  1282. }
  1283. if (outfile) {
  1284. fp = f_open(outfilename, "w", false);
  1285. if (!fp) {
  1286. fprintf(stderr, "unable to open output file\n");
  1287. exit(1);
  1288. }
  1289. } else {
  1290. fp = stdout;
  1291. }
  1292. fprintf(fp, "%s\n", fingerprint);
  1293. if (outfile)
  1294. fclose(fp);
  1295. sfree(fingerprint);
  1296. break;
  1297. }
  1298. case OPENSSH_AUTO:
  1299. case OPENSSH_NEW:
  1300. case SSHCOM:
  1301. assert(sshver == 2);
  1302. assert(ssh2key);
  1303. random_ref(); /* both foreign key types require randomness,
  1304. * for IV or padding */
  1305. switch (outtype) {
  1306. case OPENSSH_AUTO:
  1307. real_outtype = SSH_KEYTYPE_OPENSSH_AUTO;
  1308. break;
  1309. case OPENSSH_NEW:
  1310. real_outtype = SSH_KEYTYPE_OPENSSH_NEW;
  1311. break;
  1312. case SSHCOM:
  1313. real_outtype = SSH_KEYTYPE_SSHCOM;
  1314. break;
  1315. default:
  1316. unreachable("control flow goof");
  1317. }
  1318. ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
  1319. if (!ret) {
  1320. fprintf(stderr, "puttygen: unable to export key\n");
  1321. RETURN(1);
  1322. }
  1323. if (outfiletmp) {
  1324. if (!move(outfiletmp, outfile))
  1325. RETURN(1); /* rename failed */
  1326. }
  1327. break;
  1328. case TEXT: {
  1329. key_components *kc;
  1330. if (sshver == 1) {
  1331. assert(ssh1key);
  1332. kc = rsa_components(ssh1key);
  1333. } else {
  1334. if (ssh2key) {
  1335. kc = ssh_key_components(ssh2key->key);
  1336. } else {
  1337. assert(ssh2blob);
  1338. ptrlen algname = pubkey_blob_to_alg_name(
  1339. ptrlen_from_strbuf(ssh2blob));
  1340. const ssh_keyalg *alg = find_pubkey_alg_len(algname);
  1341. if (!alg) {
  1342. fprintf(stderr, "puttygen: cannot extract key components "
  1343. "from public key of unknown type '%.*s'\n",
  1344. PTRLEN_PRINTF(algname));
  1345. RETURN(1);
  1346. }
  1347. ssh_key *sk = ssh_key_new_pub(
  1348. alg, ptrlen_from_strbuf(ssh2blob));
  1349. if (!sk) {
  1350. fprintf(stderr, "puttygen: unable to decode public key\n");
  1351. RETURN(1);
  1352. }
  1353. kc = ssh_key_components(sk);
  1354. ssh_key_free(sk);
  1355. }
  1356. }
  1357. FILE *fp;
  1358. if (outfile) {
  1359. fp = f_open(outfilename, "w", false);
  1360. if (!fp) {
  1361. fprintf(stderr, "unable to open output file\n");
  1362. exit(1);
  1363. }
  1364. } else {
  1365. fp = stdout;
  1366. }
  1367. for (size_t i = 0; i < kc->ncomponents; i++) {
  1368. key_component *comp = &kc->components[i];
  1369. fprintf(fp, "%s=", comp->name);
  1370. switch (comp->type) {
  1371. case KCT_MPINT: {
  1372. char *hex = mp_get_hex(comp->mp);
  1373. fprintf(fp, "0x%s\n", hex);
  1374. smemclr(hex, strlen(hex));
  1375. sfree(hex);
  1376. break;
  1377. }
  1378. case KCT_TEXT:
  1379. fputs("\"", fp);
  1380. write_c_string_literal(fp, ptrlen_from_strbuf(comp->str));
  1381. fputs("\"\n", fp);
  1382. break;
  1383. case KCT_BINARY: {
  1384. /*
  1385. * Display format for binary key components is to show
  1386. * them as base64, with a wrapper so that the actual
  1387. * printed string is along the lines of
  1388. * 'b64("aGVsbG8sIHdvcmxkCg==")'.
  1389. *
  1390. * That's a compromise between not being too verbose
  1391. * for a human reader, and still being reasonably
  1392. * friendly to people pasting the output of this
  1393. * 'puttygen --dump' option into Python code (which
  1394. * the format is designed to permit in general).
  1395. *
  1396. * Python users pasting a dump containing one of these
  1397. * will have to define a function 'b64' in advance
  1398. * which takes a string, which you can do most easily
  1399. * using this import statement, as seen in
  1400. * cryptsuite.py:
  1401. *
  1402. * from base64 import b64decode as b64
  1403. */
  1404. fputs("b64(\"", fp);
  1405. char b64[4];
  1406. for (size_t j = 0; j < comp->str->len; j += 3) {
  1407. size_t len = comp->str->len - j;
  1408. if (len > 3) len = 3;
  1409. base64_encode_atom(comp->str->u + j, len, b64);
  1410. fwrite(b64, 1, 4, fp);
  1411. }
  1412. fputs("\")\n", fp);
  1413. break;
  1414. }
  1415. default:
  1416. unreachable("bad key component type");
  1417. }
  1418. }
  1419. if (outfile)
  1420. fclose(fp);
  1421. key_components_free(kc);
  1422. break;
  1423. }
  1424. case CERTINFO: {
  1425. if (sshver == 1) {
  1426. fprintf(stderr, "puttygen: SSH-1 keys cannot contain "
  1427. "certificates\n");
  1428. RETURN(1);
  1429. }
  1430. const ssh_keyalg *alg;
  1431. ssh_key *sk;
  1432. bool sk_allocated = false;
  1433. if (ssh2key) {
  1434. sk = ssh2key->key;
  1435. alg = ssh_key_alg(sk);
  1436. } else {
  1437. assert(ssh2blob);
  1438. ptrlen algname = pubkey_blob_to_alg_name(
  1439. ptrlen_from_strbuf(ssh2blob));
  1440. alg = find_pubkey_alg_len(algname);
  1441. if (!alg) {
  1442. fprintf(stderr, "puttygen: cannot extract certificate info "
  1443. "from public key of unknown type '%.*s'\n",
  1444. PTRLEN_PRINTF(algname));
  1445. RETURN(1);
  1446. }
  1447. sk = ssh_key_new_pub(alg, ptrlen_from_strbuf(ssh2blob));
  1448. if (!sk) {
  1449. fprintf(stderr, "puttygen: unable to decode public key\n");
  1450. RETURN(1);
  1451. }
  1452. sk_allocated = true;
  1453. }
  1454. if (!alg->is_certificate) {
  1455. fprintf(stderr, "puttygen: key is not a certificate\n");
  1456. } else {
  1457. SeatDialogText *text = ssh_key_cert_info(sk);
  1458. FILE *fp;
  1459. if (outfile) {
  1460. fp = f_open(outfilename, "w", false);
  1461. if (!fp) {
  1462. fprintf(stderr, "unable to open output file\n");
  1463. exit(1);
  1464. }
  1465. } else {
  1466. fp = stdout;
  1467. }
  1468. for (SeatDialogTextItem *item = text->items,
  1469. *end = item+text->nitems; item < end; item++) {
  1470. switch (item->type) {
  1471. case SDT_MORE_INFO_KEY:
  1472. fprintf(fp, "%s", item->text);
  1473. break;
  1474. case SDT_MORE_INFO_VALUE_SHORT:
  1475. fprintf(fp, ": %s\n", item->text);
  1476. break;
  1477. case SDT_MORE_INFO_VALUE_BLOB:
  1478. fprintf(fp, ":\n%s\n", item->text);
  1479. break;
  1480. default:
  1481. break;
  1482. }
  1483. }
  1484. if (outfile)
  1485. fclose(fp);
  1486. seat_dialog_text_free(text);
  1487. }
  1488. if (sk_allocated)
  1489. ssh_key_free(sk);
  1490. break;
  1491. }
  1492. }
  1493. out:
  1494. #undef RETURN
  1495. if (old_passphrase) {
  1496. smemclr(old_passphrase, strlen(old_passphrase));
  1497. sfree(old_passphrase);
  1498. }
  1499. if (new_passphrase) {
  1500. smemclr(new_passphrase, strlen(new_passphrase));
  1501. sfree(new_passphrase);
  1502. }
  1503. if (ssh1key) {
  1504. freersakey(ssh1key);
  1505. sfree(ssh1key);
  1506. }
  1507. if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) {
  1508. sfree(ssh2key->comment);
  1509. if (ssh2key->key)
  1510. ssh_key_free(ssh2key->key);
  1511. sfree(ssh2key);
  1512. }
  1513. if (ssh2blob)
  1514. strbuf_free(ssh2blob);
  1515. sfree(origcomment);
  1516. if (infilename)
  1517. filename_free(infilename);
  1518. if (infile_lf)
  1519. lf_free(infile_lf);
  1520. if (outfilename)
  1521. filename_free(outfilename);
  1522. sfree(outfiletmp);
  1523. return exit_status;
  1524. }