cmdgen.c 58 KB

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