cgtest.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789
  1. /*
  2. * cgtest.c: stub file to compile cmdgen.c in self-test mode
  3. */
  4. /*
  5. * Before we #include cmdgen.c, we override some function names for
  6. * test purposes. We do this via #define, so that when we link against
  7. * modules containing the original versions, we don't get a link-time
  8. * symbol clash:
  9. *
  10. * - Calls to get_random_data() are replaced with the diagnostic
  11. * function below, in order to avoid depleting the test system's
  12. * /dev/random unnecessarily.
  13. *
  14. * - Calls to console_get_userpass_input() are replaced with the
  15. * diagnostic function below, so that I can run tests in an
  16. * automated manner and provide their interactive passphrase
  17. * inputs.
  18. *
  19. * - The main() defined by cmdgen.c is renamed to cmdgen_main(); in
  20. * this file I define another main() which calls the former
  21. * repeatedly to run tests.
  22. */
  23. #define get_random_data get_random_data_diagnostic
  24. #define console_get_userpass_input console_get_userpass_input_diagnostic
  25. #define main cmdgen_main
  26. #define ppk_save_default_parameters ppk_save_cgtest_parameters
  27. #include "cmdgen.c"
  28. #undef get_random_data
  29. #undef console_get_userpass_input
  30. #undef main
  31. static bool cgtest_verbose = false;
  32. const struct ppk_save_parameters ppk_save_cgtest_parameters = {
  33. /* Replacement set of key derivation parameters that make this
  34. * test suite run a bit faster and also add determinism: we don't
  35. * try to auto-scale the number of passes (in case it gets
  36. * different answers twice in the test suite when we were
  37. * expecting two key files to compare equal), and we specify a
  38. * passphrase salt. */
  39. .fmt_version = 3,
  40. .argon2_flavour = Argon2id,
  41. .argon2_mem = 16,
  42. .argon2_passes_auto = false,
  43. .argon2_passes = 2,
  44. .argon2_parallelism = 1,
  45. .salt = (const uint8_t *)"SameSaltEachTime",
  46. .saltlen = 16,
  47. };
  48. /*
  49. * Define the special versions of get_random_data and
  50. * console_get_userpass_input that we need for this test rig.
  51. */
  52. char *get_random_data_diagnostic(int len, const char *device)
  53. {
  54. char *buf = snewn(len, char);
  55. memset(buf, 'x', len);
  56. return buf;
  57. }
  58. static int nprompts, promptsgot;
  59. static const char *prompts[3];
  60. SeatPromptResult console_get_userpass_input_diagnostic(prompts_t *p)
  61. {
  62. size_t i;
  63. SeatPromptResult ret = SPR_OK;
  64. for (i = 0; i < p->n_prompts; i++) {
  65. if (promptsgot < nprompts) {
  66. prompt_set_result(p->prompts[i], prompts[promptsgot++]);
  67. if (cgtest_verbose)
  68. printf(" prompt \"%s\": response \"%s\"\n",
  69. p->prompts[i]->prompt, p->prompts[i]->result->s);
  70. } else {
  71. promptsgot++; /* track number of requests anyway */
  72. ret = SPR_SW_ABORT("preloaded prompt unavailable in cgtest");
  73. if (cgtest_verbose)
  74. printf(" prompt \"%s\": no response preloaded\n",
  75. p->prompts[i]->prompt);
  76. }
  77. }
  78. return ret;
  79. }
  80. #include <stdarg.h>
  81. static int passes, fails;
  82. void setup_passphrases(char *first, ...)
  83. {
  84. va_list ap;
  85. char *next;
  86. nprompts = 0;
  87. if (first) {
  88. prompts[nprompts++] = first;
  89. va_start(ap, first);
  90. while ((next = va_arg(ap, char *)) != NULL) {
  91. assert(nprompts < lenof(prompts));
  92. prompts[nprompts++] = next;
  93. }
  94. va_end(ap);
  95. }
  96. }
  97. void test(int retval, ...)
  98. {
  99. va_list ap;
  100. int i, argc, ret;
  101. char **argv;
  102. argc = 0;
  103. va_start(ap, retval);
  104. while (va_arg(ap, char *) != NULL)
  105. argc++;
  106. va_end(ap);
  107. argv = snewn(argc+1, char *);
  108. va_start(ap, retval);
  109. for (i = 0; i <= argc; i++)
  110. argv[i] = va_arg(ap, char *);
  111. va_end(ap);
  112. promptsgot = 0;
  113. if (cgtest_verbose) {
  114. printf("run:");
  115. for (int i = 0; i < argc; i++) {
  116. static const char okchars[] =
  117. "0123456789abcdefghijklmnopqrstuvwxyz"
  118. "ABCDEFGHIJKLMNOPQRSTUVWXYZ%+,-./:=[]^_";
  119. const char *arg = argv[i];
  120. printf(" ");
  121. if (arg[strspn(arg, okchars)]) {
  122. printf("'");
  123. for (const char *c = argv[i]; *c; c++) {
  124. if (*c == '\'') {
  125. printf("'\\''");
  126. } else {
  127. putchar(*c);
  128. }
  129. }
  130. printf("'");
  131. } else {
  132. fputs(arg, stdout);
  133. }
  134. }
  135. printf("\n");
  136. }
  137. ret = cmdgen_main(argc, argv);
  138. random_clear();
  139. if (ret != retval) {
  140. printf("FAILED retval (exp %d got %d):", retval, ret);
  141. for (i = 0; i < argc; i++)
  142. printf(" %s", argv[i]);
  143. printf("\n");
  144. fails++;
  145. } else if (promptsgot != nprompts) {
  146. printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot);
  147. for (i = 0; i < argc; i++)
  148. printf(" %s", argv[i]);
  149. printf("\n");
  150. fails++;
  151. } else {
  152. passes++;
  153. }
  154. sfree(argv);
  155. }
  156. PRINTF_LIKE(3, 4) void filecmp(char *file1, char *file2, char *fmt, ...)
  157. {
  158. /*
  159. * Ideally I should do file comparison myself, to maximise the
  160. * portability of this test suite once this application begins
  161. * running on non-Unix platforms. For the moment, though,
  162. * calling Unix diff is perfectly adequate.
  163. */
  164. char *buf;
  165. int ret;
  166. buf = dupprintf("diff -q '%s' '%s'", file1, file2);
  167. ret = system(buf);
  168. sfree(buf);
  169. if (ret) {
  170. va_list ap;
  171. printf("FAILED diff (ret=%d): ", ret);
  172. va_start(ap, fmt);
  173. vprintf(fmt, ap);
  174. va_end(ap);
  175. printf("\n");
  176. fails++;
  177. } else
  178. passes++;
  179. }
  180. /*
  181. * General-purpose flags word
  182. */
  183. #define CGT_FLAGS(X) \
  184. X(CGT_TYPE_KNOWN_EARLY) \
  185. X(CGT_OPENSSH) \
  186. X(CGT_SSHCOM) \
  187. X(CGT_SSH_KEYGEN) \
  188. X(CGT_ED25519) \
  189. /* end of list */
  190. #define FLAG_SHIFTS(name) name ## _shift,
  191. enum { CGT_FLAGS(FLAG_SHIFTS) CGT_dummy_shift };
  192. #define FLAG_VALUES(name) name = 1 << name ## _shift,
  193. enum { CGT_FLAGS(FLAG_VALUES) CGT_dummy_flag };
  194. char *cleanup_fp(char *s, unsigned flags)
  195. {
  196. ptrlen pl = ptrlen_from_asciz(s);
  197. static const char separators[] = " \n\t";
  198. /* Skip initial key type word if we find one */
  199. if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL) ||
  200. ptrlen_startswith(pl, PTRLEN_LITERAL("ecdsa-"), NULL))
  201. ptrlen_get_word(&pl, separators);
  202. /* Expect two words giving the key length and the hash */
  203. ptrlen bits = ptrlen_get_word(&pl, separators);
  204. ptrlen hash = ptrlen_get_word(&pl, separators);
  205. if (flags & CGT_SSH_KEYGEN) {
  206. /* Strip "MD5:" prefix if it's present, and do nothing if it isn't */
  207. ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash);
  208. if (flags & CGT_ED25519) {
  209. /* OpenSSH ssh-keygen lists ed25519 keys as 256 bits, not 255 */
  210. if (ptrlen_eq_string(bits, "256"))
  211. bits = PTRLEN_LITERAL("255");
  212. }
  213. }
  214. return dupprintf("%.*s %.*s", PTRLEN_PRINTF(bits), PTRLEN_PRINTF(hash));
  215. }
  216. char *get_line(char *filename)
  217. {
  218. FILE *fp;
  219. char *line;
  220. fp = fopen(filename, "r");
  221. if (!fp)
  222. return NULL;
  223. line = fgetline(fp);
  224. fclose(fp);
  225. return line;
  226. }
  227. char *get_fp(char *filename, unsigned flags)
  228. {
  229. char *orig = get_line(filename);
  230. if (!orig)
  231. return NULL;
  232. char *toret = cleanup_fp(orig, flags);
  233. sfree(orig);
  234. return toret;
  235. }
  236. PRINTF_LIKE(3, 4) void check_fp(char *filename, char *fp, char *fmt, ...)
  237. {
  238. char *newfp;
  239. if (!fp)
  240. return;
  241. newfp = get_fp(filename, 0);
  242. if (!strcmp(fp, newfp)) {
  243. passes++;
  244. } else {
  245. va_list ap;
  246. printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp);
  247. va_start(ap, fmt);
  248. vprintf(fmt, ap);
  249. va_end(ap);
  250. printf("\n");
  251. fails++;
  252. }
  253. sfree(newfp);
  254. }
  255. static const struct cgtest_keytype {
  256. const char *name;
  257. unsigned flags;
  258. } cgtest_keytypes[] = {
  259. { "rsa1", CGT_TYPE_KNOWN_EARLY },
  260. { "dsa", CGT_OPENSSH | CGT_SSHCOM },
  261. { "rsa", CGT_OPENSSH | CGT_SSHCOM },
  262. { "ecdsa", CGT_OPENSSH },
  263. { "ed25519", CGT_OPENSSH | CGT_ED25519 },
  264. };
  265. int main(int argc, char **argv)
  266. {
  267. int i;
  268. int active[lenof(cgtest_keytypes)], active_value;
  269. bool remove_files = true;
  270. active_value = 0;
  271. for (i = 0; i < lenof(cgtest_keytypes); i++)
  272. active[i] = active_value;
  273. while (--argc > 0) {
  274. ptrlen arg = ptrlen_from_asciz(*++argv);
  275. if (ptrlen_eq_string(arg, "-v") ||
  276. ptrlen_eq_string(arg, "--verbose")) {
  277. cgtest_verbose = true;
  278. } else if (ptrlen_eq_string(arg, "--keep")) {
  279. remove_files = false;
  280. } else if (ptrlen_eq_string(arg, "--help")) {
  281. printf("usage: cgtest [options] [key types]\n");
  282. printf("options: -v, --verbose "
  283. "print more output during tests\n");
  284. printf(" --keep "
  285. "do not delete the temporary output files\n");
  286. printf(" --help "
  287. "display this help text\n");
  288. printf("key types: ");
  289. for (i = 0; i < lenof(cgtest_keytypes); i++)
  290. printf("%s%s", i ? ", " : "", cgtest_keytypes[i].name);
  291. printf("\n");
  292. return 0;
  293. } else if (!ptrlen_startswith(arg, PTRLEN_LITERAL("-"), NULL)) {
  294. for (i = 0; i < lenof(cgtest_keytypes); i++)
  295. if (ptrlen_eq_string(arg, cgtest_keytypes[i].name))
  296. break;
  297. if (i == lenof(cgtest_keytypes)) {
  298. fprintf(stderr, "cgtest: unrecognised key type '%.*s'\n",
  299. PTRLEN_PRINTF(arg));
  300. return 1;
  301. }
  302. active_value = 1; /* disables all keys not explicitly enabled */
  303. active[i] = active_value;
  304. } else {
  305. fprintf(stderr, "cgtest: unrecognised option '%.*s'\n",
  306. PTRLEN_PRINTF(arg));
  307. return 1;
  308. }
  309. }
  310. passes = fails = 0;
  311. for (i = 0; i < lenof(cgtest_keytypes); i++) {
  312. if (active[i] != active_value)
  313. continue;
  314. const struct cgtest_keytype *keytype = &cgtest_keytypes[i];
  315. bool supports_openssh = keytype->flags & CGT_OPENSSH;
  316. bool supports_sshcom = keytype->flags & CGT_SSHCOM;
  317. bool type_known_early = keytype->flags & CGT_TYPE_KNOWN_EARLY;
  318. char filename[128], osfilename[128], scfilename[128];
  319. char pubfilename[128], tmpfilename1[128], tmpfilename2[128];
  320. char *fps[SSH_N_FPTYPES];
  321. sprintf(filename, "test-%s.ppk", keytype->name);
  322. sprintf(pubfilename, "test-%s.pub", keytype->name);
  323. sprintf(osfilename, "test-%s.os", keytype->name);
  324. sprintf(scfilename, "test-%s.sc", keytype->name);
  325. sprintf(tmpfilename1, "test-%s.tmp1", keytype->name);
  326. sprintf(tmpfilename2, "test-%s.tmp2", keytype->name);
  327. /*
  328. * Create an encrypted key.
  329. */
  330. setup_passphrases("sponge", "sponge", NULL);
  331. test(0, "puttygen", "-t", keytype->name, "-o", filename, NULL);
  332. /*
  333. * List the public key in OpenSSH format.
  334. */
  335. setup_passphrases(NULL);
  336. test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL);
  337. for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) {
  338. const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256");
  339. char *cmdbuf;
  340. char *fp = NULL;
  341. cmdbuf = dupprintf("ssh-keygen -E %s -l -f '%s' > '%s'",
  342. fpname, pubfilename, tmpfilename1);
  343. if (cgtest_verbose)
  344. printf("OpenSSH %s fp check: %s\n", fpname, cmdbuf);
  345. if (system(cmdbuf) ||
  346. (fp = get_fp(tmpfilename1,
  347. CGT_SSH_KEYGEN | keytype->flags)) == NULL) {
  348. printf("UNABLE to test fingerprint matching against "
  349. "OpenSSH\n");
  350. }
  351. sfree(cmdbuf);
  352. if (fp && cgtest_verbose) {
  353. char *line = get_line(tmpfilename1);
  354. printf("OpenSSH %s fp: %s\n", fpname, line);
  355. printf("Cleaned up: %s\n", fp);
  356. sfree(line);
  357. }
  358. fps[fptype] = fp;
  359. }
  360. /*
  361. * List the public key in IETF/ssh.com format.
  362. */
  363. setup_passphrases(NULL);
  364. test(0, "puttygen", "-p", filename, NULL);
  365. /*
  366. * List the fingerprint of the key.
  367. */
  368. setup_passphrases(NULL);
  369. for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) {
  370. const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256");
  371. test(0, "puttygen", "-E", fpname, "-l", filename,
  372. "-o", tmpfilename1, NULL);
  373. if (!fps[fptype]) {
  374. /*
  375. * If we can't test fingerprints against OpenSSH, we
  376. * can at the very least test equality of all the
  377. * fingerprints we generate of this key throughout
  378. * testing.
  379. */
  380. fps[fptype] = get_fp(tmpfilename1, 0);
  381. } else {
  382. check_fp(tmpfilename1, fps[fptype], "%s initial %s fp",
  383. keytype->name, fpname);
  384. }
  385. }
  386. /*
  387. * Change the comment of the key; this _does_ require a
  388. * passphrase owing to the tamperproofing.
  389. *
  390. * NOTE: In SSH-1, this only requires a passphrase because
  391. * of inadequacies of the loading and saving mechanisms. In
  392. * _principle_, it should be perfectly possible to modify
  393. * the comment on an SSH-1 key without requiring a
  394. * passphrase; the only reason I can't do it is because my
  395. * loading and saving mechanisms don't include a method of
  396. * loading all the key data without also trying to decrypt
  397. * the private section.
  398. *
  399. * I don't consider this to be a problem worth solving,
  400. * because (a) to fix it would probably end up bloating
  401. * PuTTY proper, and (b) SSH-1 is on the way out anyway so
  402. * it shouldn't be highly significant. If it seriously
  403. * bothers anyone then perhaps I _might_ be persuadable.
  404. */
  405. setup_passphrases("sponge", NULL);
  406. test(0, "puttygen", "-C", "new-comment", filename, NULL);
  407. /*
  408. * Change the passphrase to nothing.
  409. */
  410. setup_passphrases("sponge", "", "", NULL);
  411. test(0, "puttygen", "-P", filename, NULL);
  412. /*
  413. * Change the comment of the key again; this time we expect no
  414. * passphrase to be required.
  415. */
  416. setup_passphrases(NULL);
  417. test(0, "puttygen", "-C", "new-comment-2", filename, NULL);
  418. /*
  419. * Export the private key into OpenSSH format; no passphrase
  420. * should be required since the key is currently unencrypted.
  421. */
  422. setup_passphrases(NULL);
  423. test(supports_openssh ? 0 : 1,
  424. "puttygen", "-O", "private-openssh", "-o", osfilename,
  425. filename, NULL);
  426. if (supports_openssh) {
  427. /*
  428. * List the fingerprint of the OpenSSH-formatted key.
  429. */
  430. setup_passphrases(NULL);
  431. test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
  432. check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
  433. "%s openssh clear fp", keytype->name);
  434. /*
  435. * List the public half of the OpenSSH-formatted key in
  436. * OpenSSH format.
  437. */
  438. setup_passphrases(NULL);
  439. test(0, "puttygen", "-L", osfilename, NULL);
  440. /*
  441. * List the public half of the OpenSSH-formatted key in
  442. * IETF/ssh.com format.
  443. */
  444. setup_passphrases(NULL);
  445. test(0, "puttygen", "-p", osfilename, NULL);
  446. }
  447. /*
  448. * Export the private key into ssh.com format; no passphrase
  449. * should be required since the key is currently unencrypted.
  450. */
  451. setup_passphrases(NULL);
  452. test(supports_sshcom ? 0 : 1,
  453. "puttygen", "-O", "private-sshcom",
  454. "-o", scfilename, filename, NULL);
  455. if (supports_sshcom) {
  456. /*
  457. * List the fingerprint of the ssh.com-formatted key.
  458. */
  459. setup_passphrases(NULL);
  460. test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
  461. check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
  462. "%s ssh.com clear fp", keytype->name);
  463. /*
  464. * List the public half of the ssh.com-formatted key in
  465. * OpenSSH format.
  466. */
  467. setup_passphrases(NULL);
  468. test(0, "puttygen", "-L", scfilename, NULL);
  469. /*
  470. * List the public half of the ssh.com-formatted key in
  471. * IETF/ssh.com format.
  472. */
  473. setup_passphrases(NULL);
  474. test(0, "puttygen", "-p", scfilename, NULL);
  475. }
  476. if (supports_openssh && supports_sshcom) {
  477. /*
  478. * Convert from OpenSSH into ssh.com.
  479. */
  480. setup_passphrases(NULL);
  481. test(0, "puttygen", osfilename, "-o", tmpfilename1,
  482. "-O", "private-sshcom", NULL);
  483. /*
  484. * Convert from ssh.com back into a PuTTY key,
  485. * supplying the same comment as we had before we
  486. * started to ensure the comparison works.
  487. */
  488. setup_passphrases(NULL);
  489. test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
  490. "-o", tmpfilename2, NULL);
  491. /*
  492. * See if the PuTTY key thus generated is the same as
  493. * the original.
  494. */
  495. filecmp(filename, tmpfilename2,
  496. "p->o->s->p clear %s", keytype->name);
  497. /*
  498. * Convert from ssh.com to OpenSSH.
  499. */
  500. setup_passphrases(NULL);
  501. test(0, "puttygen", scfilename, "-o", tmpfilename1,
  502. "-O", "private-openssh", NULL);
  503. /*
  504. * Convert from OpenSSH back into a PuTTY key,
  505. * supplying the same comment as we had before we
  506. * started to ensure the comparison works.
  507. */
  508. setup_passphrases(NULL);
  509. test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
  510. "-o", tmpfilename2, NULL);
  511. /*
  512. * See if the PuTTY key thus generated is the same as
  513. * the original.
  514. */
  515. filecmp(filename, tmpfilename2,
  516. "p->s->o->p clear %s", keytype->name);
  517. /*
  518. * Finally, do a round-trip conversion between PuTTY
  519. * and ssh.com without involving OpenSSH, to test that
  520. * the key comment is preserved in that case.
  521. */
  522. setup_passphrases(NULL);
  523. test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
  524. filename, NULL);
  525. setup_passphrases(NULL);
  526. test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
  527. filecmp(filename, tmpfilename2,
  528. "p->s->p clear %s", keytype->name);
  529. }
  530. /*
  531. * Check that mismatched passphrases cause an error.
  532. */
  533. setup_passphrases("sponge2", "sponge3", NULL);
  534. test(1, "puttygen", "-P", filename, NULL);
  535. /*
  536. * Put a passphrase back on.
  537. */
  538. setup_passphrases("sponge2", "sponge2", NULL);
  539. test(0, "puttygen", "-P", filename, NULL);
  540. /*
  541. * Export the private key into OpenSSH format, this time
  542. * while encrypted.
  543. */
  544. if (!supports_openssh && type_known_early) {
  545. /* We'll know far enough in advance that this combination
  546. * is going to fail that we never ask for the passphrase */
  547. setup_passphrases(NULL);
  548. } else {
  549. setup_passphrases("sponge2", NULL);
  550. }
  551. test(supports_openssh ? 0 : 1,
  552. "puttygen", "-O", "private-openssh", "-o", osfilename,
  553. filename, NULL);
  554. if (supports_openssh) {
  555. /*
  556. * List the fingerprint of the OpenSSH-formatted key.
  557. */
  558. setup_passphrases("sponge2", NULL);
  559. test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
  560. check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
  561. "%s openssh encrypted fp", keytype->name);
  562. /*
  563. * List the public half of the OpenSSH-formatted key in
  564. * OpenSSH format.
  565. */
  566. setup_passphrases("sponge2", NULL);
  567. test(0, "puttygen", "-L", osfilename, NULL);
  568. /*
  569. * List the public half of the OpenSSH-formatted key in
  570. * IETF/ssh.com format.
  571. */
  572. setup_passphrases("sponge2", NULL);
  573. test(0, "puttygen", "-p", osfilename, NULL);
  574. }
  575. /*
  576. * Export the private key into ssh.com format, this time
  577. * while encrypted. For RSA1 keys, this should give an
  578. * error.
  579. */
  580. if (!supports_sshcom && type_known_early) {
  581. /* We'll know far enough in advance that this combination
  582. * is going to fail that we never ask for the passphrase */
  583. setup_passphrases(NULL);
  584. } else {
  585. setup_passphrases("sponge2", NULL);
  586. }
  587. test(supports_sshcom ? 0 : 1,
  588. "puttygen", "-O", "private-sshcom", "-o", scfilename,
  589. filename, NULL);
  590. if (supports_sshcom) {
  591. /*
  592. * List the fingerprint of the ssh.com-formatted key.
  593. */
  594. setup_passphrases("sponge2", NULL);
  595. test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
  596. check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT],
  597. "%s ssh.com encrypted fp", keytype->name);
  598. /*
  599. * List the public half of the ssh.com-formatted key in
  600. * OpenSSH format.
  601. */
  602. setup_passphrases("sponge2", NULL);
  603. test(0, "puttygen", "-L", scfilename, NULL);
  604. /*
  605. * List the public half of the ssh.com-formatted key in
  606. * IETF/ssh.com format.
  607. */
  608. setup_passphrases("sponge2", NULL);
  609. test(0, "puttygen", "-p", scfilename, NULL);
  610. }
  611. if (supports_openssh && supports_sshcom) {
  612. /*
  613. * Convert from OpenSSH into ssh.com.
  614. */
  615. setup_passphrases("sponge2", NULL);
  616. test(0, "puttygen", osfilename, "-o", tmpfilename1,
  617. "-O", "private-sshcom", NULL);
  618. /*
  619. * Convert from ssh.com back into a PuTTY key,
  620. * supplying the same comment as we had before we
  621. * started to ensure the comparison works.
  622. */
  623. setup_passphrases("sponge2", NULL);
  624. test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
  625. "-o", tmpfilename2, NULL);
  626. /*
  627. * See if the PuTTY key thus generated is the same as
  628. * the original.
  629. */
  630. filecmp(filename, tmpfilename2,
  631. "p->o->s->p encrypted %s", keytype->name);
  632. /*
  633. * Convert from ssh.com to OpenSSH.
  634. */
  635. setup_passphrases("sponge2", NULL);
  636. test(0, "puttygen", scfilename, "-o", tmpfilename1,
  637. "-O", "private-openssh", NULL);
  638. /*
  639. * Convert from OpenSSH back into a PuTTY key,
  640. * supplying the same comment as we had before we
  641. * started to ensure the comparison works.
  642. */
  643. setup_passphrases("sponge2", NULL);
  644. test(0, "puttygen", tmpfilename1, "-C", "new-comment-2",
  645. "-o", tmpfilename2, NULL);
  646. /*
  647. * See if the PuTTY key thus generated is the same as
  648. * the original.
  649. */
  650. filecmp(filename, tmpfilename2,
  651. "p->s->o->p encrypted %s", keytype->name);
  652. /*
  653. * Finally, do a round-trip conversion between PuTTY
  654. * and ssh.com without involving OpenSSH, to test that
  655. * the key comment is preserved in that case.
  656. */
  657. setup_passphrases("sponge2", NULL);
  658. test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1,
  659. filename, NULL);
  660. setup_passphrases("sponge2", NULL);
  661. test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
  662. filecmp(filename, tmpfilename2,
  663. "p->s->p encrypted %s", keytype->name);
  664. }
  665. /*
  666. * Load with the wrong passphrase.
  667. */
  668. setup_passphrases("sponge8", NULL);
  669. test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL);
  670. /*
  671. * Load a totally bogus file.
  672. */
  673. setup_passphrases(NULL);
  674. test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL);
  675. for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++)
  676. sfree(fps[fptype]);
  677. if (remove_files) {
  678. remove(filename);
  679. remove(pubfilename);
  680. remove(osfilename);
  681. remove(scfilename);
  682. remove(tmpfilename1);
  683. remove(tmpfilename2);
  684. }
  685. }
  686. printf("%d passes, %d fails\n", passes, fails);
  687. return fails == 0 ? 0 : 1;
  688. }