summaryrefslogtreecommitdiff
path: root/src/keytrans/openpgp2ssh.c
blob: 4593b3345ccd5000665200014358aa2c35042188 (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, gnutls_openpgp_keyid_t* keyid) {
  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. /* FIXME: actually respect keyid argument. At the moment, we just
  32. emit the primary key. */
  33. init_datum(&m);
  34. init_datum(&e);
  35. init_datum(&d);
  36. init_datum(&p);
  37. init_datum(&q);
  38. init_datum(&u);
  39. init_datum(&g);
  40. init_datum(&y);
  41. init_datum(&x);
  42. pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(*pgp_privkey, &pgp_bits);
  43. if (pgp_algo < 0) {
  44. err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
  45. return 1;
  46. }
  47. if (pgp_algo == GNUTLS_PK_RSA) {
  48. err("OpenPGP RSA Key, with %d bits\n", pgp_bits);
  49. ret = gnutls_openpgp_privkey_export_rsa_raw(*pgp_privkey, &m, &e, &d, &p, &q, &u);
  50. if (GNUTLS_E_SUCCESS != ret) {
  51. err ("failed to export RSA key parameters (error: %d)\n", ret);
  52. return 1;
  53. }
  54. ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u);
  55. if (GNUTLS_E_SUCCESS != ret) {
  56. err ("failed to import RSA key parameters (error: %d)\n", ret);
  57. return 1;
  58. }
  59. } else if (pgp_algo == GNUTLS_PK_DSA) {
  60. err("OpenPGP DSA Key, with %d bits\n", pgp_bits);
  61. ret = gnutls_openpgp_privkey_export_dsa_raw(*pgp_privkey, &p, &q, &g, &y, &x);
  62. if (GNUTLS_E_SUCCESS != ret) {
  63. err ("failed to export DSA key parameters (error: %d)\n", ret);
  64. return 1;
  65. }
  66. ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x);
  67. if (GNUTLS_E_SUCCESS != ret) {
  68. err ("failed to import DSA key parameters (error: %d)\n", ret);
  69. return 1;
  70. }
  71. } else {
  72. err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
  73. return 1;
  74. }
  75. ret = gnutls_x509_privkey_fix(*output);
  76. if (ret != 0) {
  77. err("failed to fix up the private key in X.509 format (error: %d)\n", ret);
  78. return 1;
  79. }
  80. return 0;
  81. }
  82. /* FIXME: keyid should be const also */
  83. int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
  84. gnutls_openpgp_keyid_t curkeyid;
  85. int ret;
  86. int subkeyidx;
  87. int subkeycount;
  88. int found = 0;
  89. gnutls_datum_t m, e, p, q, g, y, algolabel;
  90. unsigned int bits;
  91. gnutls_pk_algorithm_t algo;
  92. const gnutls_datum_t* all[5];
  93. const char* algoname;
  94. int mpicount;
  95. /* output_data must be at least 2 chars longer than the maximum possible
  96. algorithm name: */
  97. char output_data[20];
  98. /* variables for the output conversion: */
  99. int pipestatus;
  100. int pipefd, child_pid;
  101. char* const b64args[] = {"/usr/bin/base64", "--wrap=0", NULL};
  102. init_datum(&m);
  103. init_datum(&e);
  104. init_datum(&p);
  105. init_datum(&q);
  106. init_datum(&g);
  107. init_datum(&algolabel);
  108. /* figure out if we've got the right thing: */
  109. subkeycount = gnutls_openpgp_crt_get_subkey_count(*pgp_crt);
  110. if (subkeycount < 0) {
  111. err("Could not determine subkey count (got value %d)\n", subkeycount);
  112. return 1;
  113. }
  114. if ((keyid == NULL) &&
  115. (subkeycount > 0)) {
  116. err("No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
  117. return 1;
  118. }
  119. if (keyid != NULL) {
  120. ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
  121. if (ret) {
  122. err("Could not get keyid (error: %d)\n", ret);
  123. return 1;
  124. }
  125. }
  126. if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
  127. /* we want to export the primary key: */
  128. err("exporting primary key\n");
  129. /* FIXME: this is almost identical to the block below for subkeys.
  130. This clumsiness seems inherent in the gnutls OpenPGP API,
  131. though. ugh. */
  132. algo = gnutls_openpgp_crt_get_pk_algorithm(*pgp_crt, &bits);
  133. if (algo < 0) {
  134. err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
  135. return algo;
  136. } else if (algo == GNUTLS_PK_RSA) {
  137. err("OpenPGP RSA certificate, with %d bits\n", bits);
  138. ret = gnutls_openpgp_crt_get_pk_rsa_raw(*pgp_crt, &m, &e);
  139. if (GNUTLS_E_SUCCESS != ret) {
  140. err ("failed to export RSA key parameters (error: %d)\n", ret);
  141. return 1;
  142. }
  143. } else if (algo == GNUTLS_PK_DSA) {
  144. err("OpenPGP DSA Key, with %d bits\n", bits);
  145. ret = gnutls_openpgp_crt_get_pk_dsa_raw(*pgp_crt, &p, &q, &g, &y);
  146. if (GNUTLS_E_SUCCESS != ret) {
  147. err ("failed to export DSA key parameters (error: %d)\n", ret);
  148. return 1;
  149. }
  150. }
  151. found = 1;
  152. } else {
  153. /* lets trawl through the subkeys until we find the one we want: */
  154. for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
  155. ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
  156. if (ret) {
  157. err("Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
  158. return 1;
  159. }
  160. if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
  161. err("exporting subkey index %d\n", subkeyidx);
  162. /* FIXME: this is almost identical to the block above for the
  163. primary key. */
  164. algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(*pgp_crt, subkeyidx, &bits);
  165. if (algo < 0) {
  166. err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo);
  167. return algo;
  168. } else if (algo == GNUTLS_PK_RSA) {
  169. err("OpenPGP RSA certificate, with %d bits\n", bits);
  170. ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(*pgp_crt, subkeyidx, &m, &e);
  171. if (GNUTLS_E_SUCCESS != ret) {
  172. err ("failed to export RSA key parameters (error: %d)\n", ret);
  173. return 1;
  174. }
  175. } else if (algo == GNUTLS_PK_DSA) {
  176. err("OpenPGP DSA Key, with %d bits\n", bits);
  177. ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(*pgp_crt, subkeyidx, &p, &q, &g, &y);
  178. if (GNUTLS_E_SUCCESS != ret) {
  179. err ("failed to export DSA key parameters (error: %d)\n", ret);
  180. return 1;
  181. }
  182. }
  183. found = 1;
  184. }
  185. }
  186. }
  187. if (!found) {
  188. err("Could not find key in input\n");
  189. return 1;
  190. }
  191. /* if we made it this far, we've got MPIs, and we've got the
  192. algorithm, so we just need to emit the info */
  193. if (algo == GNUTLS_PK_RSA) {
  194. algoname = "ssh-rsa";
  195. mpicount = 3;
  196. all[0] = &algolabel;
  197. all[1] = &e;
  198. all[2] = &m;
  199. } else if (algo == GNUTLS_PK_DSA) {
  200. algoname = "ssh-dss";
  201. mpicount = 5;
  202. all[0] = &algolabel;
  203. all[1] = &p;
  204. all[2] = &q;
  205. all[3] = &g;
  206. all[4] = &y;
  207. } else {
  208. err("Key algorithm was neither DSA nor RSA (it was %d). Can't deal. Sorry!\n", algo);
  209. return 1;
  210. }
  211. if (ret = datum_from_string(&algolabel, algoname), ret) {
  212. err("couldn't label string (error: %d)\n", ret);
  213. return ret;
  214. }
  215. snprintf(output_data, sizeof(output_data), "%s ", algoname);
  216. pipefd = create_writing_pipe(&child_pid, b64args[0], b64args);
  217. if (pipefd < 0) {
  218. err("failed to create a writing pipe (returned %d)\n", pipefd);
  219. return pipefd;
  220. }
  221. write(1, output_data, strlen(output_data));
  222. if (0 != write_data_fd_with_length(pipefd, all, mpicount)) {
  223. err("was not able to write out RSA key data\n");
  224. return 1;
  225. }
  226. close(pipefd);
  227. if (child_pid != waitpid(child_pid, &pipestatus, 0)) {
  228. err("could not wait for child process to return for some reason.\n");
  229. return 1;
  230. }
  231. if (pipestatus != 0) {
  232. err("base64 pipe died with return code %d\n", pipestatus);
  233. return pipestatus;
  234. }
  235. write(1, "\n", 1);
  236. return 0;
  237. }
  238. int main(int argc, char* argv[]) {
  239. gnutls_datum_t data;
  240. int ret;
  241. gnutls_x509_privkey_t x509_privkey;
  242. gnutls_openpgp_privkey_t pgp_privkey;
  243. gnutls_openpgp_crt_t pgp_crt;
  244. char output_data[10240];
  245. size_t ods = sizeof(output_data);
  246. gnutls_openpgp_keyid_t keyid;
  247. gnutls_openpgp_keyid_t* use_keyid;
  248. init_gnutls();
  249. /* figure out what keyid we should be looking for: */
  250. use_keyid = NULL;
  251. if (argv[1] != NULL) {
  252. ret = convert_string_to_keyid(keyid, argv[1]);
  253. if (ret != 0)
  254. return ret;
  255. use_keyid = &keyid;
  256. }
  257. init_datum(&data);
  258. /* slurp in the key from stdin */
  259. if (ret = set_datum_fd(&data, 0), ret) {
  260. err("didn't read file descriptor 0\n");
  261. return 1;
  262. }
  263. if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
  264. err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
  265. return 1;
  266. }
  267. /* check whether it's a private key or a public key, by trying them: */
  268. if ((gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0) == 0) ||
  269. (gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0) == 0)) {
  270. /* we're dealing with a private key */
  271. err("Translating private key\n");
  272. if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
  273. err("Failed to initialize X.509 private key for output (error: %d)\n", ret);
  274. return 1;
  275. }
  276. ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
  277. gnutls_openpgp_privkey_deinit(pgp_privkey);
  278. if (ret)
  279. return ret;
  280. ret = gnutls_x509_privkey_export (x509_privkey,
  281. GNUTLS_X509_FMT_PEM,
  282. output_data,
  283. &ods);
  284. if (ret == 0) {
  285. write(1, output_data, ods);
  286. }
  287. gnutls_x509_privkey_deinit(x509_privkey);
  288. } else {
  289. if (ret = gnutls_openpgp_crt_init(&pgp_crt), ret) {
  290. err("Failed to initialized OpenPGP certificate (error: %d)\n", ret);
  291. return 1;
  292. }
  293. if ((gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW) == 0) ||
  294. (gnutls_openpgp_crt_import(pgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64) == 0)) {
  295. /* we're dealing with a public key */
  296. err("Translating public key\n");
  297. ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
  298. } else {
  299. /* we have no idea what kind of key this is at all anyway! */
  300. err("Input does contain any form of OpenPGP key I recognize.");
  301. return 1;
  302. }
  303. }
  304. gnutls_global_deinit();
  305. return 0;
  306. }