cmdgen.c 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656
  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, print the usage message and return failure.
  684. */
  685. if (!infile && keytype == NOKEYGEN) {
  686. usage(true);
  687. RETURN(1);
  688. }
  689. /* ------------------------------------------------------------------
  690. * Figure out further details of exactly what we're going to do.
  691. */
  692. /*
  693. * Bomb out if we've been asked to both load and generate a
  694. * key.
  695. */
  696. if (keytype != NOKEYGEN && infile) {
  697. fprintf(stderr, "puttygen: cannot both load and generate a key\n");
  698. RETURN(1);
  699. }
  700. /*
  701. * We must save the private part when generating a new key.
  702. */
  703. if (keytype != NOKEYGEN &&
  704. (outtype != PRIVATE && outtype != OPENSSH_AUTO &&
  705. outtype != OPENSSH_NEW && outtype != SSHCOM && outtype != TEXT)) {
  706. fprintf(stderr, "puttygen: this would generate a new key but "
  707. "discard the private part\n");
  708. RETURN(1);
  709. }
  710. /*
  711. * Analyse the type of the input file, in case this affects our
  712. * course of action.
  713. */
  714. if (infile) {
  715. const char *load_error;
  716. infilename = filename_from_str(infile);
  717. if (!strcmp(infile, "-"))
  718. infile_lf = lf_load_keyfile_fp(stdin, &load_error);
  719. else
  720. infile_lf = lf_load_keyfile(infilename, &load_error);
  721. if (!infile_lf) {
  722. fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
  723. infile, load_error);
  724. RETURN(1);
  725. }
  726. infile_bs = BinarySource_UPCAST(infile_lf);
  727. intype = key_type_s(infile_bs);
  728. BinarySource_REWIND(infile_bs);
  729. switch (intype) {
  730. case SSH_KEYTYPE_UNOPENABLE:
  731. case SSH_KEYTYPE_UNKNOWN:
  732. fprintf(stderr, "puttygen: unable to load file `%s': %s\n",
  733. infile, key_type_to_str(intype));
  734. RETURN(1);
  735. case SSH_KEYTYPE_SSH1:
  736. case SSH_KEYTYPE_SSH1_PUBLIC:
  737. if (sshver == 2) {
  738. fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys"
  739. " not supported\n");
  740. RETURN(1);
  741. }
  742. sshver = 1;
  743. break;
  744. case SSH_KEYTYPE_SSH2:
  745. case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
  746. case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
  747. case SSH_KEYTYPE_OPENSSH_PEM:
  748. case SSH_KEYTYPE_OPENSSH_NEW:
  749. case SSH_KEYTYPE_SSHCOM:
  750. if (sshver == 1) {
  751. fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys"
  752. " not supported\n");
  753. RETURN(1);
  754. }
  755. sshver = 2;
  756. break;
  757. case SSH_KEYTYPE_OPENSSH_AUTO:
  758. default:
  759. unreachable("Should never see these types on an input file");
  760. }
  761. }
  762. /*
  763. * Determine the default output file, if none is provided.
  764. *
  765. * This will usually be equal to stdout, except that if the
  766. * input and output file formats are the same then the default
  767. * output is to overwrite the input.
  768. *
  769. * Also in this code, we bomb out if the input and output file
  770. * formats are the same and no other action is performed.
  771. */
  772. if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) ||
  773. (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) ||
  774. (intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) ||
  775. (intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) ||
  776. (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) {
  777. if (!outfile) {
  778. outfile = infile;
  779. outfiletmp = dupcat(outfile, ".tmp");
  780. }
  781. if (!change_passphrase && !comment && !reencrypt && !certfile &&
  782. !remove_cert) {
  783. fprintf(stderr, "puttygen: this command would perform no useful"
  784. " action\n");
  785. RETURN(1);
  786. }
  787. } else {
  788. if (!outfile) {
  789. /*
  790. * Bomb out rather than automatically choosing to write
  791. * a private key file to stdout.
  792. */
  793. if (outtype == PRIVATE || outtype == OPENSSH_AUTO ||
  794. outtype == OPENSSH_NEW || outtype == SSHCOM) {
  795. fprintf(stderr, "puttygen: need to specify an output file\n");
  796. RETURN(1);
  797. }
  798. }
  799. }
  800. /*
  801. * Figure out whether we need to load the encrypted part of the
  802. * key. This will be the case if (a) we need to write out
  803. * a private key format, (b) the entire input key file is
  804. * encrypted, or (c) we're outputting TEXT, in which case we
  805. * want all of the input file including private material if it
  806. * exists.
  807. */
  808. bool intype_entirely_encrypted =
  809. intype == SSH_KEYTYPE_OPENSSH_PEM ||
  810. intype == SSH_KEYTYPE_OPENSSH_NEW ||
  811. intype == SSH_KEYTYPE_SSHCOM;
  812. bool intype_has_private =
  813. !(intype == SSH_KEYTYPE_SSH1_PUBLIC ||
  814. intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 ||
  815. intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH);
  816. bool outtype_has_private =
  817. outtype == PRIVATE || outtype == OPENSSH_AUTO ||
  818. outtype == OPENSSH_NEW || outtype == SSHCOM;
  819. if (outtype_has_private || intype_entirely_encrypted ||
  820. (outtype == TEXT && intype_has_private))
  821. load_encrypted = true;
  822. else
  823. load_encrypted = false;
  824. if (load_encrypted && !intype_has_private) {
  825. fprintf(stderr, "puttygen: cannot perform this action on a "
  826. "public-key-only input file\n");
  827. RETURN(1);
  828. }
  829. /*
  830. * Check consistency properties relating to certificates.
  831. */
  832. if (certfile && !(sshver == 2 && intype_has_private &&
  833. outtype_has_private && infile)) {
  834. fprintf(stderr, "puttygen: certificates can only be added to "
  835. "existing SSH-2 private key files\n");
  836. RETURN(1);
  837. }
  838. if (remove_cert && !(sshver == 2 && infile)) {
  839. fprintf(stderr, "puttygen: certificates can only be removed from "
  840. "existing SSH-2 key files\n");
  841. RETURN(1);
  842. }
  843. if (certfile && remove_cert) {
  844. fprintf(stderr, "puttygen: cannot both add and remove a "
  845. "certificate\n");
  846. RETURN(1);
  847. }
  848. /* ------------------------------------------------------------------
  849. * Now we're ready to actually do some stuff.
  850. */
  851. /*
  852. * Either load or generate a key.
  853. */
  854. if (keytype != NOKEYGEN) {
  855. char *entropy;
  856. char default_comment[80];
  857. struct tm tm;
  858. tm = ltime();
  859. if (keytype == DSA)
  860. strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm);
  861. else if (keytype == ECDSA)
  862. strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm);
  863. else if (keytype == EDDSA && bits == 255)
  864. strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm);
  865. else if (keytype == EDDSA)
  866. strftime(default_comment, 30, "eddsa-key-%Y%m%d", &tm);
  867. else
  868. strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm);
  869. entropy = get_random_data(bits / 8, random_device);
  870. if (!entropy) {
  871. fprintf(stderr, "puttygen: failed to collect entropy, "
  872. "could not generate key\n");
  873. RETURN(1);
  874. }
  875. random_setup_special();
  876. random_reseed(make_ptrlen(entropy, bits / 8));
  877. smemclr(entropy, bits/8);
  878. sfree(entropy);
  879. PrimeGenerationContext *pgc = primegen_new_context(primegen);
  880. if (keytype == DSA) {
  881. struct dsa_key *dsakey = snew(struct dsa_key);
  882. dsa_generate(dsakey, bits, pgc, &cmdgen_progress);
  883. ssh2key = snew(ssh2_userkey);
  884. ssh2key->key = &dsakey->sshk;
  885. ssh1key = NULL;
  886. } else if (keytype == ECDSA) {
  887. struct ecdsa_key *ek = snew(struct ecdsa_key);
  888. ecdsa_generate(ek, bits);
  889. ssh2key = snew(ssh2_userkey);
  890. ssh2key->key = &ek->sshk;
  891. ssh1key = NULL;
  892. } else if (keytype == EDDSA) {
  893. struct eddsa_key *ek = snew(struct eddsa_key);
  894. eddsa_generate(ek, bits);
  895. ssh2key = snew(ssh2_userkey);
  896. ssh2key->key = &ek->sshk;
  897. ssh1key = NULL;
  898. } else {
  899. RSAKey *rsakey = snew(RSAKey);
  900. rsa_generate(rsakey, bits, strong_rsa, pgc, &cmdgen_progress);
  901. rsakey->comment = NULL;
  902. if (keytype == RSA1) {
  903. ssh1key = rsakey;
  904. } else {
  905. ssh2key = snew(ssh2_userkey);
  906. ssh2key->key = &rsakey->sshk;
  907. }
  908. }
  909. primegen_free_context(pgc);
  910. if (ssh2key)
  911. ssh2key->comment = dupstr(default_comment);
  912. if (ssh1key)
  913. ssh1key->comment = dupstr(default_comment);
  914. } else {
  915. const char *error = NULL;
  916. bool encrypted;
  917. assert(infile != NULL);
  918. sfree(origcomment);
  919. origcomment = NULL;
  920. /*
  921. * Find out whether the input key is encrypted.
  922. */
  923. if (intype == SSH_KEYTYPE_SSH1)
  924. encrypted = rsa1_encrypted_s(infile_bs, &origcomment);
  925. else if (intype == SSH_KEYTYPE_SSH2)
  926. encrypted = ppk_encrypted_s(infile_bs, &origcomment);
  927. else
  928. encrypted = import_encrypted_s(infilename, infile_bs,
  929. intype, &origcomment);
  930. BinarySource_REWIND(infile_bs);
  931. /*
  932. * If so, ask for a passphrase.
  933. */
  934. if (encrypted && load_encrypted) {
  935. if (!old_passphrase) {
  936. prompts_t *p = new_prompts();
  937. SeatPromptResult spr;
  938. p->to_server = false;
  939. p->from_server = false;
  940. p->name = dupstr("SSH key passphrase");
  941. add_prompt(p, dupstr("Enter passphrase to load key: "), false);
  942. spr = console_get_userpass_input(p);
  943. assert(spr.kind != SPRK_INCOMPLETE);
  944. if (spr_is_abort(spr)) {
  945. free_prompts(p);
  946. spr_error(spr);
  947. RETURN(1);
  948. } else {
  949. old_passphrase = prompt_get_result(p->prompts[0]);
  950. free_prompts(p);
  951. }
  952. }
  953. } else {
  954. old_passphrase = NULL;
  955. }
  956. switch (intype) {
  957. int ret;
  958. case SSH_KEYTYPE_SSH1:
  959. case SSH_KEYTYPE_SSH1_PUBLIC:
  960. ssh1key = snew(RSAKey);
  961. memset(ssh1key, 0, sizeof(RSAKey));
  962. if (!load_encrypted) {
  963. strbuf *blob;
  964. BinarySource src[1];
  965. sfree(origcomment);
  966. origcomment = NULL;
  967. blob = strbuf_new();
  968. ret = rsa1_loadpub_s(infile_bs, BinarySink_UPCAST(blob),
  969. &origcomment, &error);
  970. BinarySource_BARE_INIT(src, blob->u, blob->len);
  971. get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST);
  972. strbuf_free(blob);
  973. ssh1key->comment = dupstr(origcomment);
  974. ssh1key->private_exponent = NULL;
  975. ssh1key->p = NULL;
  976. ssh1key->q = NULL;
  977. ssh1key->iqmp = NULL;
  978. } else {
  979. ret = rsa1_load_s(infile_bs, ssh1key, old_passphrase, &error);
  980. }
  981. BinarySource_REWIND(infile_bs);
  982. if (ret > 0)
  983. error = NULL;
  984. else if (!error)
  985. error = "unknown error";
  986. break;
  987. case SSH_KEYTYPE_SSH2:
  988. case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716:
  989. case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH:
  990. if (!load_encrypted) {
  991. sfree(origcomment);
  992. origcomment = NULL;
  993. ssh2blob = strbuf_new();
  994. if (ppk_loadpub_s(infile_bs, &ssh2alg,
  995. BinarySink_UPCAST(ssh2blob),
  996. &origcomment, &error)) {
  997. const ssh_keyalg *alg = find_pubkey_alg(ssh2alg);
  998. if (alg)
  999. bits = ssh_key_public_bits(
  1000. alg, ptrlen_from_strbuf(ssh2blob));
  1001. else
  1002. bits = -1;
  1003. } else {
  1004. strbuf_free(ssh2blob);
  1005. ssh2blob = NULL;
  1006. }
  1007. sfree(ssh2alg);
  1008. } else {
  1009. ssh2key = ppk_load_s(infile_bs, old_passphrase, &error);
  1010. }
  1011. BinarySource_REWIND(infile_bs);
  1012. if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob)
  1013. error = NULL;
  1014. else if (!error) {
  1015. if (ssh2key == SSH2_WRONG_PASSPHRASE)
  1016. error = "wrong passphrase";
  1017. else
  1018. error = "unknown error";
  1019. }
  1020. break;
  1021. case SSH_KEYTYPE_OPENSSH_PEM:
  1022. case SSH_KEYTYPE_OPENSSH_NEW:
  1023. case SSH_KEYTYPE_SSHCOM:
  1024. ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error);
  1025. if (ssh2key) {
  1026. if (ssh2key != SSH2_WRONG_PASSPHRASE)
  1027. error = NULL;
  1028. else
  1029. error = "wrong passphrase";
  1030. } else if (!error)
  1031. error = "unknown error";
  1032. break;
  1033. default:
  1034. unreachable("bad input key type");
  1035. }
  1036. if (error) {
  1037. fprintf(stderr, "puttygen: error loading `%s': %s\n",
  1038. infile, error);
  1039. RETURN(1);
  1040. }
  1041. }
  1042. /*
  1043. * Change the comment if asked to.
  1044. */
  1045. if (comment) {
  1046. if (sshver == 1) {
  1047. assert(ssh1key);
  1048. sfree(ssh1key->comment);
  1049. ssh1key->comment = dupstr(comment);
  1050. } else {
  1051. assert(ssh2key);
  1052. sfree(ssh2key->comment);
  1053. ssh2key->comment = dupstr(comment);
  1054. }
  1055. }
  1056. /*
  1057. * Swap out the public key for a different one, if asked to.
  1058. */
  1059. if (certfile) {
  1060. Filename *certfilename = filename_from_str(certfile);
  1061. LoadedFile *certfile_lf;
  1062. const char *error = NULL;
  1063. if (!strcmp(certfile, "-"))
  1064. certfile_lf = lf_load_keyfile_fp(stdin, &error);
  1065. else
  1066. certfile_lf = lf_load_keyfile(certfilename, &error);
  1067. filename_free(certfilename);
  1068. if (!certfile_lf) {
  1069. fprintf(stderr, "puttygen: unable to load certificate file `%s': "
  1070. "%s\n", certfile, error);
  1071. RETURN(1);
  1072. }
  1073. char *algname = NULL;
  1074. char *comment = NULL;
  1075. strbuf *pub = strbuf_new();
  1076. if (!ppk_loadpub_s(BinarySource_UPCAST(certfile_lf), &algname,
  1077. BinarySink_UPCAST(pub), &comment, &error)) {
  1078. fprintf(stderr, "puttygen: unable to load certificate file `%s': "
  1079. "%s\n", certfile, error);
  1080. strbuf_free(pub);
  1081. sfree(algname);
  1082. sfree(comment);
  1083. lf_free(certfile_lf);
  1084. RETURN(1);
  1085. }
  1086. lf_free(certfile_lf);
  1087. sfree(comment);
  1088. const ssh_keyalg *alg = find_pubkey_alg(algname);
  1089. if (!alg) {
  1090. fprintf(stderr, "puttygen: certificate file `%s' has unsupported "
  1091. "algorithm name `%s'\n", certfile, algname);
  1092. strbuf_free(pub);
  1093. sfree(algname);
  1094. RETURN(1);
  1095. }
  1096. sfree(algname);
  1097. /* Check the two public keys match apart from certificates */
  1098. strbuf *old_basepub = strbuf_new();
  1099. ssh_key_public_blob(ssh_key_base_key(ssh2key->key),
  1100. BinarySink_UPCAST(old_basepub));
  1101. ssh_key *new_pubkey = ssh_key_new_pub(alg, ptrlen_from_strbuf(pub));
  1102. strbuf *new_basepub = strbuf_new();
  1103. ssh_key_public_blob(ssh_key_base_key(new_pubkey),
  1104. BinarySink_UPCAST(new_basepub));
  1105. ssh_key_free(new_pubkey);
  1106. bool match = ptrlen_eq_ptrlen(ptrlen_from_strbuf(old_basepub),
  1107. ptrlen_from_strbuf(new_basepub));
  1108. strbuf_free(old_basepub);
  1109. strbuf_free(new_basepub);
  1110. if (!match) {
  1111. fprintf(stderr, "puttygen: certificate in `%s' does not match "
  1112. "public key in `%s'\n", certfile, infile);
  1113. strbuf_free(pub);
  1114. RETURN(1);
  1115. }
  1116. strbuf *priv = strbuf_new_nm();
  1117. ssh_key_private_blob(ssh2key->key, BinarySink_UPCAST(priv));
  1118. ssh_key *newkey = ssh_key_new_priv(
  1119. alg, ptrlen_from_strbuf(pub), ptrlen_from_strbuf(priv));
  1120. strbuf_free(pub);
  1121. strbuf_free(priv);
  1122. if (!newkey) {
  1123. fprintf(stderr, "puttygen: unable to combine certificate in `%s' "
  1124. "with private key\n", certfile);
  1125. RETURN(1);
  1126. }
  1127. ssh_key_free(ssh2key->key);
  1128. ssh2key->key = newkey;
  1129. } else if (remove_cert) {
  1130. /*
  1131. * Removing a certificate can be meaningfully done to a pure
  1132. * public key blob, as well as a full key pair.
  1133. */
  1134. if (ssh2key) {
  1135. ssh_key *newkey = ssh_key_clone(ssh_key_base_key(ssh2key->key));
  1136. ssh_key_free(ssh2key->key);
  1137. ssh2key->key = newkey;
  1138. } else if (ssh2blob) {
  1139. ptrlen algname = pubkey_blob_to_alg_name(
  1140. ptrlen_from_strbuf(ssh2blob));
  1141. const ssh_keyalg *alg = find_pubkey_alg_len(algname);
  1142. if (!alg) {
  1143. fprintf(stderr, "puttygen: input file `%s' has unsupported "
  1144. "algorithm name `%.*s'\n", infile,
  1145. PTRLEN_PRINTF(algname));
  1146. RETURN(1);
  1147. }
  1148. ssh_key *tmpkey = ssh_key_new_pub(
  1149. alg, ptrlen_from_strbuf(ssh2blob));
  1150. strbuf_clear(ssh2blob);
  1151. ssh_key_public_blob(ssh_key_base_key(tmpkey),
  1152. BinarySink_UPCAST(ssh2blob));
  1153. ssh_key_free(tmpkey);
  1154. }
  1155. }
  1156. /*
  1157. * Unless we're changing the passphrase, the old one (if any) is a
  1158. * reasonable default.
  1159. */
  1160. if (!change_passphrase && old_passphrase && !new_passphrase)
  1161. new_passphrase = dupstr(old_passphrase);
  1162. /*
  1163. * Prompt for a new passphrase if we have been asked to, or if
  1164. * we have just generated a key.
  1165. *
  1166. * In the latter case, an exception is if we're producing text
  1167. * output, because that output format doesn't support encryption
  1168. * in any case.
  1169. */
  1170. if (!new_passphrase && (change_passphrase ||
  1171. (keytype != NOKEYGEN && outtype != TEXT))) {
  1172. prompts_t *p = new_prompts();
  1173. SeatPromptResult spr;
  1174. p->to_server = false;
  1175. p->from_server = false;
  1176. p->name = dupstr("New SSH key passphrase");
  1177. add_prompt(p, dupstr("Enter passphrase to save key: "), false);
  1178. add_prompt(p, dupstr("Re-enter passphrase to verify: "), false);
  1179. spr = console_get_userpass_input(p);
  1180. assert(spr.kind != SPRK_INCOMPLETE);
  1181. if (spr_is_abort(spr)) {
  1182. free_prompts(p);
  1183. spr_error(spr);
  1184. RETURN(1);
  1185. } else {
  1186. if (strcmp(prompt_get_result_ref(p->prompts[0]),
  1187. prompt_get_result_ref(p->prompts[1]))) {
  1188. free_prompts(p);
  1189. fprintf(stderr, "puttygen: passphrases do not match\n");
  1190. RETURN(1);
  1191. }
  1192. new_passphrase = prompt_get_result(p->prompts[0]);
  1193. free_prompts(p);
  1194. }
  1195. }
  1196. if (new_passphrase && !*new_passphrase) {
  1197. sfree(new_passphrase);
  1198. new_passphrase = NULL;
  1199. }
  1200. /*
  1201. * Write output.
  1202. *
  1203. * (In the case where outfile and outfiletmp are both NULL,
  1204. * there is no semantic reason to initialise outfilename at
  1205. * all; but we have to write _something_ to it or some compiler
  1206. * will probably complain that it might be used uninitialised.)
  1207. */
  1208. if (outfiletmp)
  1209. outfilename = filename_from_str(outfiletmp);
  1210. else
  1211. outfilename = filename_from_str(outfile ? outfile : "");
  1212. switch (outtype) {
  1213. bool ret;
  1214. int real_outtype;
  1215. case PRIVATE:
  1216. random_ref(); /* we'll need a few random bytes in the save file */
  1217. if (sshver == 1) {
  1218. assert(ssh1key);
  1219. ret = rsa1_save_f(outfilename, ssh1key, new_passphrase);
  1220. if (!ret) {
  1221. fprintf(stderr, "puttygen: unable to save SSH-1 private key\n");
  1222. RETURN(1);
  1223. }
  1224. } else {
  1225. assert(ssh2key);
  1226. ret = ppk_save_f(outfilename, ssh2key, new_passphrase, &params);
  1227. if (!ret) {
  1228. fprintf(stderr, "puttygen: unable to save SSH-2 private key\n");
  1229. RETURN(1);
  1230. }
  1231. }
  1232. if (outfiletmp) {
  1233. if (!move(outfiletmp, outfile))
  1234. RETURN(1); /* rename failed */
  1235. }
  1236. break;
  1237. case PUBLIC:
  1238. case PUBLICO: {
  1239. FILE *fp;
  1240. if (outfile) {
  1241. fp = f_open(outfilename, "w", false);
  1242. if (!fp) {
  1243. fprintf(stderr, "unable to open output file\n");
  1244. exit(1);
  1245. }
  1246. } else {
  1247. fp = stdout;
  1248. }
  1249. if (sshver == 1) {
  1250. ssh1_write_pubkey(fp, ssh1key);
  1251. } else {
  1252. if (!ssh2blob) {
  1253. assert(ssh2key);
  1254. ssh2blob = strbuf_new();
  1255. ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob));
  1256. }
  1257. ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment,
  1258. ssh2blob->s, ssh2blob->len,
  1259. (outtype == PUBLIC ?
  1260. SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 :
  1261. SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH));
  1262. }
  1263. if (outfile)
  1264. fclose(fp);
  1265. break;
  1266. }
  1267. case FP: {
  1268. FILE *fp;
  1269. char *fingerprint;
  1270. if (sshver == 1) {
  1271. assert(ssh1key);
  1272. fingerprint = rsa_ssh1_fingerprint(ssh1key);
  1273. } else {
  1274. if (ssh2key) {
  1275. fingerprint = ssh2_fingerprint(ssh2key->key, fptype);
  1276. } else {
  1277. assert(ssh2blob);
  1278. fingerprint = ssh2_fingerprint_blob(
  1279. ptrlen_from_strbuf(ssh2blob), fptype);
  1280. }
  1281. }
  1282. if (outfile) {
  1283. fp = f_open(outfilename, "w", false);
  1284. if (!fp) {
  1285. fprintf(stderr, "unable to open output file\n");
  1286. exit(1);
  1287. }
  1288. } else {
  1289. fp = stdout;
  1290. }
  1291. fprintf(fp, "%s\n", fingerprint);
  1292. if (outfile)
  1293. fclose(fp);
  1294. sfree(fingerprint);
  1295. break;
  1296. }
  1297. case OPENSSH_AUTO:
  1298. case OPENSSH_NEW:
  1299. case SSHCOM:
  1300. assert(sshver == 2);
  1301. assert(ssh2key);
  1302. random_ref(); /* both foreign key types require randomness,
  1303. * for IV or padding */
  1304. switch (outtype) {
  1305. case OPENSSH_AUTO:
  1306. real_outtype = SSH_KEYTYPE_OPENSSH_AUTO;
  1307. break;
  1308. case OPENSSH_NEW:
  1309. real_outtype = SSH_KEYTYPE_OPENSSH_NEW;
  1310. break;
  1311. case SSHCOM:
  1312. real_outtype = SSH_KEYTYPE_SSHCOM;
  1313. break;
  1314. default:
  1315. unreachable("control flow goof");
  1316. }
  1317. ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase);
  1318. if (!ret) {
  1319. fprintf(stderr, "puttygen: unable to export key\n");
  1320. RETURN(1);
  1321. }
  1322. if (outfiletmp) {
  1323. if (!move(outfiletmp, outfile))
  1324. RETURN(1); /* rename failed */
  1325. }
  1326. break;
  1327. case TEXT: {
  1328. key_components *kc;
  1329. if (sshver == 1) {
  1330. assert(ssh1key);
  1331. kc = rsa_components(ssh1key);
  1332. } else {
  1333. if (ssh2key) {
  1334. kc = ssh_key_components(ssh2key->key);
  1335. } else {
  1336. assert(ssh2blob);
  1337. ptrlen algname = pubkey_blob_to_alg_name(
  1338. ptrlen_from_strbuf(ssh2blob));
  1339. const ssh_keyalg *alg = find_pubkey_alg_len(algname);
  1340. if (!alg) {
  1341. fprintf(stderr, "puttygen: cannot extract key components "
  1342. "from public key of unknown type '%.*s'\n",
  1343. PTRLEN_PRINTF(algname));
  1344. RETURN(1);
  1345. }
  1346. ssh_key *sk = ssh_key_new_pub(
  1347. alg, ptrlen_from_strbuf(ssh2blob));
  1348. if (!sk) {
  1349. fprintf(stderr, "puttygen: unable to decode public key\n");
  1350. RETURN(1);
  1351. }
  1352. kc = ssh_key_components(sk);
  1353. ssh_key_free(sk);
  1354. }
  1355. }
  1356. FILE *fp;
  1357. if (outfile) {
  1358. fp = f_open(outfilename, "w", false);
  1359. if (!fp) {
  1360. fprintf(stderr, "unable to open output file\n");
  1361. exit(1);
  1362. }
  1363. } else {
  1364. fp = stdout;
  1365. }
  1366. for (size_t i = 0; i < kc->ncomponents; i++) {
  1367. key_component *comp = &kc->components[i];
  1368. fprintf(fp, "%s=", comp->name);
  1369. switch (comp->type) {
  1370. case KCT_MPINT: {
  1371. char *hex = mp_get_hex(comp->mp);
  1372. fprintf(fp, "0x%s\n", hex);
  1373. smemclr(hex, strlen(hex));
  1374. sfree(hex);
  1375. break;
  1376. }
  1377. case KCT_TEXT:
  1378. fputs("\"", fp);
  1379. write_c_string_literal(fp, ptrlen_from_strbuf(comp->str));
  1380. fputs("\"\n", fp);
  1381. break;
  1382. case KCT_BINARY: {
  1383. /*
  1384. * Display format for binary key components is to show
  1385. * them as base64, with a wrapper so that the actual
  1386. * printed string is along the lines of
  1387. * 'b64("aGVsbG8sIHdvcmxkCg==")'.
  1388. *
  1389. * That's a compromise between not being too verbose
  1390. * for a human reader, and still being reasonably
  1391. * friendly to people pasting the output of this
  1392. * 'puttygen --dump' option into Python code (which
  1393. * the format is designed to permit in general).
  1394. *
  1395. * Python users pasting a dump containing one of these
  1396. * will have to define a function 'b64' in advance
  1397. * which takes a string, which you can do most easily
  1398. * using this import statement, as seen in
  1399. * cryptsuite.py:
  1400. *
  1401. * from base64 import b64decode as b64
  1402. */
  1403. fputs("b64(\"", fp);
  1404. char b64[4];
  1405. for (size_t j = 0; j < comp->str->len; j += 3) {
  1406. size_t len = comp->str->len - j;
  1407. if (len > 3) len = 3;
  1408. base64_encode_atom(comp->str->u + j, len, b64);
  1409. fwrite(b64, 1, 4, fp);
  1410. }
  1411. fputs("\")\n", fp);
  1412. break;
  1413. }
  1414. default:
  1415. unreachable("bad key component type");
  1416. }
  1417. }
  1418. if (outfile)
  1419. fclose(fp);
  1420. key_components_free(kc);
  1421. break;
  1422. }
  1423. case CERTINFO: {
  1424. if (sshver == 1) {
  1425. fprintf(stderr, "puttygen: SSH-1 keys cannot contain "
  1426. "certificates\n");
  1427. RETURN(1);
  1428. }
  1429. const ssh_keyalg *alg;
  1430. ssh_key *sk;
  1431. bool sk_allocated = false;
  1432. if (ssh2key) {
  1433. sk = ssh2key->key;
  1434. alg = ssh_key_alg(sk);
  1435. } else {
  1436. assert(ssh2blob);
  1437. ptrlen algname = pubkey_blob_to_alg_name(
  1438. ptrlen_from_strbuf(ssh2blob));
  1439. alg = find_pubkey_alg_len(algname);
  1440. if (!alg) {
  1441. fprintf(stderr, "puttygen: cannot extract certificate info "
  1442. "from public key of unknown type '%.*s'\n",
  1443. PTRLEN_PRINTF(algname));
  1444. RETURN(1);
  1445. }
  1446. sk = ssh_key_new_pub(alg, ptrlen_from_strbuf(ssh2blob));
  1447. if (!sk) {
  1448. fprintf(stderr, "puttygen: unable to decode public key\n");
  1449. RETURN(1);
  1450. }
  1451. sk_allocated = true;
  1452. }
  1453. if (!alg->is_certificate) {
  1454. fprintf(stderr, "puttygen: key is not a certificate\n");
  1455. } else {
  1456. SeatDialogText *text = ssh_key_cert_info(sk);
  1457. FILE *fp;
  1458. if (outfile) {
  1459. fp = f_open(outfilename, "w", false);
  1460. if (!fp) {
  1461. fprintf(stderr, "unable to open output file\n");
  1462. exit(1);
  1463. }
  1464. } else {
  1465. fp = stdout;
  1466. }
  1467. for (SeatDialogTextItem *item = text->items,
  1468. *end = item+text->nitems; item < end; item++) {
  1469. switch (item->type) {
  1470. case SDT_MORE_INFO_KEY:
  1471. fprintf(fp, "%s", item->text);
  1472. break;
  1473. case SDT_MORE_INFO_VALUE_SHORT:
  1474. fprintf(fp, ": %s\n", item->text);
  1475. break;
  1476. case SDT_MORE_INFO_VALUE_BLOB:
  1477. fprintf(fp, ":\n%s\n", item->text);
  1478. break;
  1479. default:
  1480. break;
  1481. }
  1482. }
  1483. if (outfile)
  1484. fclose(fp);
  1485. seat_dialog_text_free(text);
  1486. }
  1487. if (sk_allocated)
  1488. ssh_key_free(sk);
  1489. break;
  1490. }
  1491. }
  1492. out:
  1493. #undef RETURN
  1494. if (old_passphrase) {
  1495. smemclr(old_passphrase, strlen(old_passphrase));
  1496. sfree(old_passphrase);
  1497. }
  1498. if (new_passphrase) {
  1499. smemclr(new_passphrase, strlen(new_passphrase));
  1500. sfree(new_passphrase);
  1501. }
  1502. if (ssh1key) {
  1503. freersakey(ssh1key);
  1504. sfree(ssh1key);
  1505. }
  1506. if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) {
  1507. sfree(ssh2key->comment);
  1508. if (ssh2key->key)
  1509. ssh_key_free(ssh2key->key);
  1510. sfree(ssh2key);
  1511. }
  1512. if (ssh2blob)
  1513. strbuf_free(ssh2blob);
  1514. sfree(origcomment);
  1515. if (infilename)
  1516. filename_free(infilename);
  1517. if (infile_lf)
  1518. lf_free(infile_lf);
  1519. if (outfilename)
  1520. filename_free(outfilename);
  1521. sfree(outfiletmp);
  1522. return exit_status;
  1523. }