summaryrefslogtreecommitdiff
path: root/main.c
blob: 8225f0a01df331269a2bd5dbbd90247c7297372d (plain)
  1. #include <gnutls/gnutls.h>
  2. #include <gnutls/openpgp.h>
  3. #include <gnutls/x509.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <errno.h>
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <unistd.h>
  11. #include <stdarg.h>
  12. /*
  13. Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
  14. Date: Tue, 01 Apr 2008
  15. License: GPL v3 or later
  16. monkeysphere private key translator: execute this with an GPG
  17. secret key on stdin (at the moment, only passphraseless RSA keys
  18. work).
  19. It will spit out a PEM-encoded version of the key on stdout, which
  20. can be fed into ssh-add like this:
  21. gpg --export-secret-keys $KEYID | monkeysphere | ssh-add -c /dev/stdin
  22. Requirements: I've only built this so far with GnuTLS v2.3.4 --
  23. version 2.2.0 does not contain the appropriate pieces.
  24. Notes: gpgkey2ssh doesn't seem to provide the same public
  25. keys. Mighty weird!
  26. 0 wt215@squeak:~/monkeysphere$ gpg --export-secret-keys 1DCDF89F | ~dkg/src/monkeysphere/monkeysphere | ssh-add -c /dev/stdin
  27. gnutls version: 2.3.4
  28. OpenPGP RSA Key, with 1024 bits
  29. Identity added: /dev/stdin (/dev/stdin)
  30. The user has to confirm each use of the key
  31. 0 wt215@squeak:~/monkeysphere$ ssh-add -L
  32. ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9gWQqfrnhQKDQnND/3eOexpddE64J+1zp9fcyCje7H5LKclb6DBV2HS6WgW32PJhIzvP+fYZM3dzXea3fpv14y1SicXiRBDgF9SnsNA1qWn2RyzkLcKy7PmM0PDYtU1oiLTcQj/xkWcqW2sLKHT/WW+vZP5XP7RMGN/yWNMfE2Q== /dev/stdin
  33. 0 wt215@squeak:~/monkeysphere$ gpgkey2ssh 1DCDF89F
  34. ssh-rsa AAAAB3NzaC1yc2EAAACBAL2BZCp+ueFAoNCc0P/d457Gl10Trgn7XOn19zIKN7sfkspyVvoMFXYdLpaBbfY8mEjO8/59hkzd3Nd5rd+m/XjLVKJxeJEEOAX1Kew0DWpafZHLOQtwrLs+YzQ8Ni1TWiItNxCP/GRZypbawsodP9Zb69k/lc/tEwY3/JY0x8TZAAAAAwEAAQ== COMMENT
  35. 0 wt215@squeak:~/monkeysphere$
  36. */
  37. int loglevel = 0;
  38. void err(const char* fmt, ...) {
  39. va_list ap;
  40. va_start(ap, fmt);
  41. vfprintf(stderr, fmt, ap);
  42. va_end(ap);
  43. }
  44. void logfunc(int level, const char* string) {
  45. fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
  46. }
  47. void init_datum(gnutls_datum_t* d) {
  48. d->data = NULL;
  49. d->size = 0;
  50. }
  51. void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
  52. dest->data = gnutls_realloc(dest->data, src->size);
  53. dest->size = src->size;
  54. memcpy(dest->data, src->data, src->size);
  55. }
  56. int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
  57. if (a->size > b->size) {
  58. err("a is larger\n");
  59. return 1;
  60. }
  61. if (a->size < b->size) {
  62. err("b is larger\n");
  63. return -1;
  64. }
  65. return memcmp(a->data, b->data, a->size);
  66. }
  67. void free_datum(gnutls_datum_t* d) {
  68. gnutls_free(d->data);
  69. d->data = NULL;
  70. d->size = 0;
  71. }
  72. /* read the passed-in string, store in a single datum */
  73. int set_datum_string(gnutls_datum_t* d, const char* s) {
  74. unsigned int x = strlen(s)+1;
  75. unsigned char* c = NULL;
  76. c = gnutls_realloc(d->data, x);
  77. if (NULL == c)
  78. return -1;
  79. d->data = c;
  80. d->size = x;
  81. memcpy(d->data, s, x);
  82. return 0;
  83. }
  84. /* read the passed-in file descriptor until EOF, store in a single
  85. datum */
  86. int set_datum_fd(gnutls_datum_t* d, int fd) {
  87. unsigned int bufsize = 1024;
  88. unsigned int len = 0;
  89. FILE* f = fdopen(fd, "r");
  90. if (bufsize > d->size) {
  91. bufsize = 1024;
  92. d->data = gnutls_realloc(d->data, bufsize);
  93. if (d->data == NULL) {
  94. err("out of memory!\n");
  95. return -1;
  96. }
  97. d->size = bufsize;
  98. } else {
  99. bufsize = d->size;
  100. }
  101. f = fdopen(fd, "r");
  102. if (NULL == f) {
  103. err("could not fdopen FD %d\n", fd);
  104. }
  105. clearerr(f);
  106. while (!feof(f) && !ferror(f)) {
  107. if (len == bufsize) {
  108. /* allocate more space by doubling: */
  109. bufsize *= 2;
  110. d->data = gnutls_realloc(d->data, bufsize);
  111. if (d->data == NULL) {
  112. err("out of memory!\n");
  113. return -1;
  114. };
  115. d->size = bufsize;
  116. }
  117. len += fread(d->data + len, 1, bufsize - len, f);
  118. /* err("read %d bytes\n", len); */
  119. }
  120. if (ferror(f)) {
  121. err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
  122. return -1;
  123. }
  124. /* touch up buffer size to match reality: */
  125. d->data = gnutls_realloc(d->data, len);
  126. d->size = len;
  127. return 0;
  128. }
  129. /* read the file indicated (by na1me) in the fname parameter. store
  130. its entire contents in a single datum. */
  131. int set_datum_file(gnutls_datum_t* d, const char* fname) {
  132. struct stat sbuf;
  133. unsigned char* c = NULL;
  134. FILE* file = NULL;
  135. size_t x = 0;
  136. if (0 != stat(fname, &sbuf)) {
  137. err("failed to stat '%s'\n", fname);
  138. return -1;
  139. }
  140. c = gnutls_realloc(d->data, sbuf.st_size);
  141. if (NULL == c) {
  142. err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
  143. return -1;
  144. }
  145. d->data = c;
  146. d->size = sbuf.st_size;
  147. file = fopen(fname, "r");
  148. if (NULL == file) {
  149. err("failed to open '%s' for reading\n", fname);
  150. return -1;
  151. }
  152. x = fread(d->data, d->size, 1, file);
  153. if (x != 1) {
  154. err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
  155. fclose(file);
  156. return -1;
  157. }
  158. fclose(file);
  159. return 0;
  160. }
  161. int main(int argc, char* argv[]) {
  162. const char* version = NULL;
  163. const char* debug_string = NULL;
  164. gnutls_x509_privkey_t x509_privkey;
  165. gnutls_datum_t data, test, clean;
  166. int ret;
  167. /*
  168. const char *certfile, *keyfile;
  169. gnutls_certificate_credentials_t pgp_creds;
  170. */
  171. gnutls_datum_t m, e, d, p, q, u, g, y, x;
  172. /* gnutls_x509_crt_t crt; */
  173. gnutls_openpgp_privkey_t pgp_privkey;
  174. gnutls_pk_algorithm_t pgp_algo;
  175. unsigned int pgp_bits;
  176. char output_data[10240];
  177. size_t ods = sizeof(output_data);
  178. init_datum(&data);
  179. init_datum(&test);
  180. init_datum(&clean);
  181. init_datum(&m);
  182. init_datum(&e);
  183. init_datum(&d);
  184. init_datum(&p);
  185. init_datum(&q);
  186. init_datum(&u);
  187. init_datum(&g);
  188. init_datum(&y);
  189. init_datum(&x);
  190. if (ret = gnutls_global_init(), ret) {
  191. err("Failed to do gnutls_global_init() (error: %d)\n", ret);
  192. return 1;
  193. }
  194. version = gnutls_check_version(NULL);
  195. if (version)
  196. err("gnutls version: %s\n", version);
  197. else {
  198. err("no version found!\n");
  199. return 1;
  200. }
  201. if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
  202. loglevel = atoi(debug_string);
  203. gnutls_global_set_log_function(logfunc);
  204. gnutls_global_set_log_level(loglevel);
  205. err("set log level to %d\n", loglevel);
  206. }
  207. if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
  208. err("Failed to initialize X.509 private key (error: %d)\n", ret);
  209. return 1;
  210. }
  211. if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
  212. err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
  213. return 1;
  214. }
  215. /* slurp in the private key from stdin */
  216. if (ret = set_datum_fd(&data, 0), ret) {
  217. err("didn't read file descriptor 0\n");
  218. return 1;
  219. }
  220. /* Or, instead, read in key from a file name:
  221. if (ret = set_datum_file(&data, argv[1]), ret) {
  222. err("didn't read file '%s'\n", argv[1]);
  223. return 1;
  224. }
  225. */
  226. /* treat the passed file as an X.509 private key, and extract its
  227. component values: */
  228. /* if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */
  229. /* err("Failed to import the X.509 key (error: %d)\n", ret); */
  230. /* return 1; */
  231. /* } */
  232. /* gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */
  233. /* try to print the PEM-encoded private key: */
  234. /* ret = gnutls_x509_privkey_export (x509_privkey, */
  235. /* GNUTLS_X509_FMT_PEM, */
  236. /* output_data, */
  237. /* &ods); */
  238. /* printf("ret: %u; ods: %u;\n", ret, ods); */
  239. /* if (ret == 0) { */
  240. /* write(0, output_data, ods); */
  241. /* } */
  242. /* format could be either: GNUTLS_OPENPGP_FMT_RAW,
  243. GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW,
  244. otherwise, use BASE64: */
  245. if (getenv("MONKEYSPHERE_RAW")) {
  246. err("assuming RAW formatted private keys\n");
  247. if (ret = gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret)
  248. err("failed to import the OpenPGP private key in RAW format (error: %d)\n", ret);
  249. } else {
  250. err("assuming BASE64 formatted private keys\n");
  251. if (ret = gnutls_openpgp_privkey_import (pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret)
  252. err("failed to import the OpenPGP private key in BASE64 format (error: %d)\n", ret);
  253. }
  254. pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits);
  255. if (pgp_algo < 0) {
  256. err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
  257. return 1;
  258. }
  259. if (pgp_algo == GNUTLS_PK_RSA) {
  260. err("OpenPGP RSA Key, with %d bits\n", pgp_bits);
  261. ret = gnutls_openpgp_privkey_export_rsa_raw(pgp_privkey, &m, &e, &d, &p, &q, &u);
  262. if (GNUTLS_E_SUCCESS != ret) {
  263. err ("failed to export RSA key parameters (error: %d)\n", ret);
  264. return 1;
  265. }
  266. ret = gnutls_x509_privkey_import_rsa_raw (x509_privkey, &m, &e, &d, &p, &q, &u);
  267. if (GNUTLS_E_SUCCESS != ret) {
  268. err ("failed to import RSA key parameters (error: %d)\n", ret);
  269. return 1;
  270. }
  271. } else if (pgp_algo == GNUTLS_PK_DSA) {
  272. err("OpenPGP DSA Key, with %d bits\n", pgp_bits);
  273. ret = gnutls_openpgp_privkey_export_dsa_raw(pgp_privkey, &p, &q, &g, &y, &x);
  274. if (GNUTLS_E_SUCCESS != ret) {
  275. err ("failed to export DSA key parameters (error: %d)\n", ret);
  276. return 1;
  277. }
  278. ret = gnutls_x509_privkey_import_dsa_raw (x509_privkey, &p, &q, &g, &y, &x);
  279. if (GNUTLS_E_SUCCESS != ret) {
  280. err ("failed to import DSA key parameters (error: %d)\n", ret);
  281. return 1;
  282. }
  283. } else {
  284. err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
  285. return 1;
  286. }
  287. /* const gnutls_datum_t * m, const gnutls_datum_t * e, const gnutls_datum_t * d, const gnutls_datum_t * p, const gnutls_datum_t * q, const gnutls_datum_t * u); */
  288. ret = gnutls_x509_privkey_fix(x509_privkey);
  289. if (ret != 0) {
  290. err("failed to fix up the private key in X.509 format (error: %d)\n", ret);
  291. return 1;
  292. }
  293. ret = gnutls_x509_privkey_export (x509_privkey,
  294. GNUTLS_X509_FMT_PEM,
  295. output_data,
  296. &ods);
  297. printf("ret: %u; ods: %u;\n", ret, ods);
  298. if (ret == 0) {
  299. write(1, output_data, ods);
  300. }
  301. gnutls_x509_privkey_deinit(x509_privkey);
  302. gnutls_openpgp_privkey_deinit(pgp_privkey);
  303. gnutls_global_deinit();
  304. return 0;
  305. }