summaryrefslogtreecommitdiff
path: root/src/keytrans/openpgp2ssh.c
blob: 5cc6cfa087e967e8e8afe49af2839073570fade6 (plain)
  1. #include "gnutls-helpers.h"
  2. #include <gnutls/openpgp.h>
  3. #include <gnutls/x509.h>
  4. /* for waitpid() */
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. /*
  8. Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
  9. Date: 2008-06-12 13:47:41-0400
  10. License: GPL v3 or later
  11. monkeysphere key translator: execute this with an OpenPGP key on
  12. stdin, (please indicate the specific keyid that you want as the
  13. first argument if there are subkeys). At the moment, only public
  14. keys and passphraseless secret keys work.
  15. For secret keys, it will spit out a PEM-encoded version of the key
  16. on stdout, which can be fed into ssh-add like this:
  17. gpg --export-secret-keys $KEYID | openpgp2ssh $KEYID | ssh-add -c /dev/stdin
  18. For public keys, it will spit out a single line of text that can
  19. (with some massaging) be used in an openssh known_hosts or
  20. authorized_keys file. For example:
  21. echo server.example.org $(gpg --export $KEYID | openpgp2ssh $KEYID) >> ~/.ssh/known_hosts
  22. Requirements: I've only built this so far with GnuTLS v2.3.x.
  23. GnuTLS 2.2.x does not contain the appropriate functionality.
  24. */
  25. /* FIXME: keyid should be const as well */
  26. int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, const unsigned char* keyfpr, unsigned int fprlen) {
  27. gnutls_datum_t m, e, d, p, q, u, g, y, x;
  28. gnutls_pk_algorithm_t pgp_algo;
  29. unsigned int pgp_bits;
  30. int ret;
  31. int subkeyidx;
  32. int subkeycount;
  33. int found = 0;
  34. unsigned char fingerprint[20];
  35. size_t fingerprint_length = sizeof(fingerprint);
  36. init_datum(&m);
  37. init_datum(&e);
  38. init_datum(&d);
  39. init_datum(&p);
  40. init_datum(&q);
  41. init_datum(&u);
  42. init_datum(&g);
  43. init_datum(&y);
  44. init_datum(&x);
  45. subkeycount = gnutls_openpgp_privkey_get_subkey_count(*pgp_privkey);
  46. if (subkeycount < 0) {
  47. err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
  48. return 1;
  49. }
  50. if ((keyfpr == NULL) &&
  51. (subkeycount > 0)) {
  52. err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
  53. return 1;
  54. }
  55. if (keyfpr != NULL) {
  56. ret = gnutls_openpgp_privkey_get_fingerprint(*pgp_privkey, fingerprint, &fingerprint_length);
  57. if (ret) {
  58. err(0,"Could not get fingerprint (error: %d)\n", ret);
  59. return 1;
  60. }
  61. if (fprlen > fingerprint_length) {
  62. err(0, "Requested key identifier is longer than computed fingerprint\n");
  63. return 1;
  64. }
  65. if (fingerprint_length > fprlen) {
  66. err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
  67. }
  68. }
  69. if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
  70. /* we want to export the primary key: */
  71. err(0,"exporting primary key\n");
  72. /* FIXME: this is almost identical to the block below for subkeys.
  73. This clumsiness seems inherent in the gnutls OpenPGP API,
  74. though. ugh. */
  75. pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
  76. if (pgp_algo < 0) {
  77. err(0, "failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
  78. return 1;
  79. }
  80. if (pgp_algo == GNUTLS_PK_RSA) {
  81. err(0,"OpenPGP RSA Key, with %d bits\n", pgp_bits);
  82. ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
  83. if (GNUTLS_E_SUCCESS != ret) {
  84. err(0, "failed to export RSA key parameters (error: %d)\n", ret);
  85. return 1;
  86. }
  87. } else if (pgp_algo == GNUTLS_PK_DSA) {
  88. err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
  89. ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
  90. if (GNUTLS_E_SUCCESS != ret) {
  91. err(0,"failed to export DSA key parameters (error: %d)\n", ret);
  92. return 1;
  93. }
  94. }
  95. found = 1;
  96. } else {
  97. /* lets trawl through the subkeys until we find the one we want: */
  98. for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
  99. ret = gnutls_openpgp_privkey_get_subkey_fingerprint(*pgp_privkey, subkeyidx, fingerprint, &fingerprint_length);
  100. if (ret) {
  101. err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
  102. return 1;
  103. }
  104. if (fprlen > fingerprint_length) {
  105. err(0, "Requested key identifier is longer than computed fingerprint\n");
  106. return 1;
  107. }
  108. if (fingerprint_length > fprlen) {
  109. err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
  110. }
  111. if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
  112. err(0,"exporting subkey index %d\n", subkeyidx);
  113. /* FIXME: this is almost identical to the block above for the
  114. primary key. */
  115. pgp_algo = gnutls_openpgp_privkey_get_subkey_pk_algorithm(*pgp_privkey, subkeyidx, &pgp_bits);
  116. if (pgp_algo < 0) {
  117. err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", pgp_algo);
  118. return pgp_algo;
  119. } else if (pgp_algo == GNUTLS_PK_RSA) {
  120. err(0,"OpenPGP RSA key, with %d bits\n", pgp_bits);
  121. ret = gnutls_openpgp_privkey_export_subkey_rsa_raw(*pgp_privkey, subkeyidx, &m, &e, &d, &p, &q, &u);
  122. if (GNUTLS_E_SUCCESS != ret) {
  123. err(0,"failed to export RSA key parameters (error: %d)\n", ret);
  124. return 1;
  125. }
  126. } else if (pgp_algo == GNUTLS_PK_DSA) {
  127. err(0,"OpenPGP DSA Key, with %d bits\n", pgp_bits);
  128. ret = gnutls_openpgp_privkey_export_subkey_dsa_raw(*pgp_privkey, subkeyidx, &p, &q, &g, &y, &x);
  129. if (GNUTLS_E_SUCCESS != ret) {
  130. err(0,"failed to export DSA key parameters (error: %d)\n", ret);
  131. return 1;
  132. }
  133. }
  134. found = 1;
  135. }
  136. }
  137. }
  138. if (!found) {
  139. err(0,"Could not find key in input\n");
  140. return 1;
  141. }
  142. if (pgp_algo == GNUTLS_PK_RSA) {
  143. ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u);
  144. if (GNUTLS_E_SUCCESS != ret) {
  145. err(0, "failed to import RSA key parameters (error: %d)\n", ret);
  146. return 1;
  147. }
  148. } else if (pgp_algo == GNUTLS_PK_DSA) {
  149. ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x);
  150. if (GNUTLS_E_SUCCESS != ret) {
  151. err(0,"failed to import DSA key parameters (error: %d)\n", ret);
  152. return 1;
  153. }
  154. } else {
  155. err(0,"OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
  156. return 1;
  157. }
  158. ret = gnutls_x509_privkey_fix(*output);
  159. if (ret != 0) {
  160. err(0,"failed to fix up the private key in X.509 format (error: %d)\n", ret);
  161. return 1;
  162. }
  163. return 0;
  164. }
  165. /* FIXME: keyid should be const also */
  166. int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, const unsigned char* keyfpr, size_t fprlen) {
  167. int ret;
  168. int subkeyidx;
  169. int subkeycount;
  170. int found = 0;
  171. gnutls_datum_t m, e, p, q, g, y, algolabel;
  172. unsigned int bits;
  173. gnutls_pk_algorithm_t algo;
  174. const gnutls_datum_t* all[5];
  175. const char* algoname;
  176. int mpicount;
  177. /* output_data must be at least 2 chars longer than the maximum possible
  178. algorithm name: */
  179. char output_data[20];
  180. unsigned char fingerprint[20];
  181. size_t fingerprint_length = sizeof(fingerprint);
  182. /* variables for the output conversion: */
  183. int pipestatus;
  184. int pipefd, child_pid;
  185. char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL};
  186. init_datum(&m);
  187. init_datum(&e);
  188. init_datum(&p);
  189. init_datum(&q);
  190. init_datum(&g);
  191. init_datum(&algolabel);
  192. /* figure out if we've got the right thing: */
  193. subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
  194. if (subkeycount < 0) {
  195. err(0,"Could not determine subkey count (got value %d)\n", subkeycount);
  196. return 1;
  197. }
  198. if ((keyfpr == NULL) &&
  199. (subkeycount > 0)) {
  200. err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
  201. return 1;
  202. }
  203. if (keyfpr != NULL) {
  204. ret = gnutls_openpgp_crt_get_fingerprint(*pgp_crt, fingerprint, &fingerprint_length);
  205. if (ret) {
  206. err(0,"Could not get key fingerprint (error: %d)\n", ret);
  207. return 1;
  208. }
  209. if (fprlen > fingerprint_length) {
  210. err(0, "Requested key identifier is longer than computed fingerprint\n");
  211. return 1;
  212. }
  213. if (fingerprint_length > fprlen) {
  214. err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
  215. }
  216. }
  217. if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
  218. /* we want to export the primary key: */
  219. err(0,"exporting primary key\n");
  220. /* FIXME: this is almost identical to the block below for subkeys.
  221. This clumsiness seems inherent in the gnutls OpenPGP API,
  222. though. ugh. */
  223. algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
  224. if (algo < 0) {
  225. err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
  226. return algo;
  227. } else if (algo == GNUTLS_PK_RSA) {
  228. err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
  229. ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
  230. if (GNUTLS_E_SUCCESS != ret) {
  231. err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
  232. return 1;
  233. }
  234. } else if (algo == GNUTLS_PK_DSA) {
  235. err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
  236. ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
  237. if (GNUTLS_E_SUCCESS != ret) {
  238. err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
  239. return 1;
  240. }
  241. }
  242. found = 1;
  243. } else {
  244. /* lets trawl through the subkeys until we find the one we want: */
  245. for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
  246. ret = gnutls_openpgp_crt_get_subkey_fingerprint(*pgp_crt, subkeyidx, fingerprint, &fingerprint_length);
  247. if (ret) {
  248. err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
  249. return 1;
  250. }
  251. if (fprlen > fingerprint_length) {
  252. err(0, "Requested key identifier is longer than computed fingerprint\n");
  253. return 1;
  254. }
  255. if (fingerprint_length > fprlen) {
  256. err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
  257. }
  258. if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
  259. err(0,"exporting subkey index %d\n", subkeyidx);
  260. /* FIXME: this is almost identical to the block above for the
  261. primary key. */
  262. algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
  263. if (algo < 0) {
  264. err(0,"failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
  265. return algo;
  266. } else if (algo == GNUTLS_PK_RSA) {
  267. err(0,"OpenPGP RSA certificate, with %d bits\n", bits);
  268. ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
  269. if (GNUTLS_E_SUCCESS != ret) {
  270. err(0,"failed to export RSA certificate parameters (error: %d)\n", ret);
  271. return 1;
  272. }
  273. } else if (algo == GNUTLS_PK_DSA) {
  274. err(0,"OpenPGP DSA certificate, with %d bits\n", bits);
  275. ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
  276. if (GNUTLS_E_SUCCESS != ret) {
  277. err(0,"failed to export DSA certificate parameters (error: %d)\n", ret);
  278. return 1;
  279. }
  280. }
  281. found = 1;
  282. }
  283. }
  284. }
  285. if (!found) {
  286. err(0,"Could not find key in input\n");
  287. return 1;
  288. }
  289. /* if we made it this far, we've got MPIs, and we've got the
  290. algorithm, so we just need to emit the info */
  291. if (algo == GNUTLS_PK_RSA) {
  292. algoname = "ssh-rsa";
  293. mpicount = 3;
  294. all[0] = &algolabel;
  295. all[1] = &e;
  296. all[2] = &m;
  297. } else if (algo == GNUTLS_PK_DSA) {
  298. algoname = "ssh-dss";
  299. mpicount = 5;
  300. all[0] = &algolabel;
  301. all[1] = &p;
  302. all[2] = &q;
  303. all[3] = &g;
  304. all[4] = &y;
  305. } else {
  306. err(0,"Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo);
  307. return 1;
  308. }
  309. if (ret = datum_from_string(&algolabel, algoname), ret) {
  310. err(0,"couldn't label string (error: %d)\n", ret);
  311. return ret;
  312. }
  313. snprintf(output_data, sizeof(output_data), "%s ", algoname);
  314. pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
  315. if (pipefd < 0) {
  316. err(0,"failed to create a writing pipe (returned %d)\n", pipefd);
  317. return pipefd;
  318. }
  319. write(1, output_data, strlen(output_data));
  320. if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
  321. err(0,"was not able to write out RSA key data\n");
  322. return 1;
  323. }
  324. close(pipefd);
  325. if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
  326. err(0,"could not wait for child process to return for some reason.\n");
  327. return 1;
  328. }
  329. if (pipestatus != 0) {
  330. err(0,"base64 pipe died with return code %d\n", pipestatus);
  331. return pipestatus;
  332. }
  333. write(1, "\n", 1);
  334. return 0;
  335. }
  336. int main(int argc, char* argv[]) {
  337. gnutls_datum_t data;
  338. int ret = 0;
  339. gnutls_x509_privkey_t x509_privkey;
  340. gnutls_openpgp_privkey_t pgp_privkey;
  341. gnutls_openpgp_crt_t pgp_crt;
  342. char output_data[10240];
  343. size_t ods = sizeof(output_data);
  344. unsigned char * fingerprint = NULL;
  345. size_t fpr_size;
  346. char * prettyfpr = NULL;
  347. init_gnutls();
  348. /* figure out what key we should be looking for: */
  349. if (argv[1] != NULL) {
  350. if (strlen(argv[1]) > 81) {
  351. /* safety check to avoid some sort of wacky overflow situation:
  352. there's no reason that the key id should be longer than twice
  353. a sane fingerprint (one byte between chars, and then another
  354. two at the beginning and end) */
  355. err(0, "Key identifier is way too long. Please use at most 40 hex digits.\n");
  356. return 1;
  357. }
  358. fpr_size = hexstring2bin(NULL, argv[1]);
  359. if (fpr_size > 40*4) {
  360. err(0, "Key identifier is longer than 40 hex digits\n");
  361. return 1;
  362. }
  363. /* since fpr_size is initially in bits: */
  364. if (fpr_size % 8 != 0) {
  365. err(0, "Please provide an even number of hex digits for the key identifier\n");
  366. return 1;
  367. }
  368. fpr_size /= 8;
  369. fingerprint = malloc(sizeof(unsigned char) * fpr_size);
  370. bzero(fingerprint, sizeof(unsigned char) * fpr_size);
  371. hexstring2bin(fingerprint, argv[1]);
  372. prettyfpr = malloc(sizeof(unsigned char)*fpr_size*2 + 1);
  373. if (prettyfpr != NULL) {
  374. hex_print_data(prettyfpr, fingerprint, fpr_size);
  375. prettyfpr[sizeof(unsigned char)*fpr_size*2] = '\0';
  376. err(1, "searching for key with fingerprint '%s'\n", prettyfpr);
  377. free(prettyfpr);
  378. }
  379. if (fpr_size < 4) {
  380. err(0, "You MUST provide at least 8 hex digits in any key identifier\n");
  381. return 1;
  382. }
  383. if (fpr_size < 8)
  384. err(0, "You should provide at least 16 hex digits in any key identifier (proceeding with %d digits anyway)\n", fpr_size*2);
  385. }
  386. init_datum(&data);
  387. /* slurp in the key from stdin */
  388. if (ret = set_datum_fd(&data, 0), ret) {
  389. err(0,"didn't read file descriptor 0\n");
  390. return 1;
  391. }
  392. if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
  393. err(0,"Failed to initialized OpenPGP private key (error: %d)\n", ret);
  394. return 1;
  395. }
  396. /* check whether it's a private key or a public key, by trying them: */
  397. if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) ||
  398. (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
  399. /* we're dealing with a private key */
  400. err(0,"Translating private key\n");
  401. if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
  402. err(0,"Failed to initialize X.509 private key for output (error: %d)\n", ret);
  403. return 1;
  404. }
  405. ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, fingerprint, fpr_size);
  406. gnutls_openpgp_privkey_deinit(pgp_privkey);
  407. if (ret)
  408. return ret;
  409. ret = gnutls_x509_privkey_export (x509_privkey,
  410. GNUTLS_X509_FMT_PEM,
  411. output_data,
  412. &ods);
  413. if (ret == 0) {
  414. write(1, output_data, ods);
  415. }
  416. gnutls_x509_privkey_deinit(x509_privkey);
  417. } else {
  418. if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
  419. err(0,"Failed to initialized OpenPGP certificate (error: %d)\n", ret);
  420. return 1;
  421. }
  422. if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) ||
  423. (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
  424. /* we're dealing with a public key */
  425. err(0,"Translating public key\n");
  426. ret = emit_public_openssh_from_pgp(&pgp_crt, fingerprint, fpr_size);
  427. } else {
  428. /* we have no idea what kind of key this is at all anyway! */
  429. err(0,"Input does not contain any form of OpenPGP key I recognize.\n");
  430. return 1;
  431. }
  432. }
  433. gnutls_global_deinit();
  434. free(fingerprint);
  435. return 0;
  436. }