1 #include <gnutls/gnutls.h>
2 #include <gnutls/openpgp.h>
3 #include <gnutls/x509.h>
14 Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
15 Date: Tue, 01 Apr 2008
16 License: GPL v3 or later
18 monkeysphere private key translator: execute this with an GPG
19 secret key on stdin (at the moment, only passphraseless RSA keys
22 It will spit out a PEM-encoded version of the key on stdout, which
23 can be fed into ssh-add like this:
25 gpg --export-secret-keys $KEYID | monkeysphere | ssh-add -c /dev/stdin
27 Requirements: I've only built this so far with GnuTLS v2.3.4 --
28 version 2.2.0 does not contain the appropriate pieces.
30 Notes: gpgkey2ssh doesn't seem to provide the same public
33 0 wt215@squeak:~/monkeysphere$ gpg --export-secret-keys 1DCDF89F | ~dkg/src/monkeysphere/monkeysphere | ssh-add -c /dev/stdin
35 OpenPGP RSA Key, with 1024 bits
36 Identity added: /dev/stdin (/dev/stdin)
37 The user has to confirm each use of the key
38 0 wt215@squeak:~/monkeysphere$ ssh-add -L
39 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC9gWQqfrnhQKDQnND/3eOexpddE64J+1zp9fcyCje7H5LKclb6DBV2HS6WgW32PJhIzvP+fYZM3dzXea3fpv14y1SicXiRBDgF9SnsNA1qWn2RyzkLcKy7PmM0PDYtU1oiLTcQj/xkWcqW2sLKHT/WW+vZP5XP7RMGN/yWNMfE2Q== /dev/stdin
40 0 wt215@squeak:~/monkeysphere$ gpgkey2ssh 1DCDF89F
41 ssh-rsa AAAAB3NzaC1yc2EAAACBAL2BZCp+ueFAoNCc0P/d457Gl10Trgn7XOn19zIKN7sfkspyVvoMFXYdLpaBbfY8mEjO8/59hkzd3Nd5rd+m/XjLVKJxeJEEOAX1Kew0DWpafZHLOQtwrLs+YzQ8Ni1TWiItNxCP/GRZypbawsodP9Zb69k/lc/tEwY3/JY0x8TZAAAAAwEAAQ== COMMENT
42 0 wt215@squeak:~/monkeysphere$
50 void err(const char* fmt, ...) {
53 vfprintf(stderr, fmt, ap);
57 void logfunc(int level, const char* string) {
58 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
61 void init_datum(gnutls_datum_t* d) {
65 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
66 dest->data = gnutls_realloc(dest->data, src->size);
67 dest->size = src->size;
68 memcpy(dest->data, src->data, src->size);
70 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
71 if (a->size > b->size) {
75 if (a->size < b->size) {
79 return memcmp(a->data, b->data, a->size);
81 void free_datum(gnutls_datum_t* d) {
87 /* read the passed-in string, store in a single datum */
88 int set_datum_string(gnutls_datum_t* d, const char* s) {
89 unsigned int x = strlen(s)+1;
90 unsigned char* c = NULL;
92 c = gnutls_realloc(d->data, x);
97 memcpy(d->data, s, x);
101 /* read the passed-in file descriptor until EOF, store in a single
103 int set_datum_fd(gnutls_datum_t* d, int fd) {
104 unsigned int bufsize = 1024;
105 unsigned int len = 0;
107 FILE* f = fdopen(fd, "r");
108 if (bufsize > d->size) {
110 d->data = gnutls_realloc(d->data, bufsize);
111 if (d->data == NULL) {
112 err("out of memory!\n");
121 err("could not fdopen FD %d\n", fd);
124 while (!feof(f) && !ferror(f)) {
125 if (len == bufsize) {
126 /* allocate more space by doubling: */
128 d->data = gnutls_realloc(d->data, bufsize);
129 if (d->data == NULL) {
130 err("out of memory!\n");
135 len += fread(d->data + len, 1, bufsize - len, f);
136 /* err("read %d bytes\n", len); */
139 err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
143 /* touch up buffer size to match reality: */
144 d->data = gnutls_realloc(d->data, len);
149 /* read the file indicated (by na1me) in the fname parameter. store
150 its entire contents in a single datum. */
151 int set_datum_file(gnutls_datum_t* d, const char* fname) {
153 unsigned char* c = NULL;
157 if (0 != stat(fname, &sbuf)) {
158 err("failed to stat '%s'\n", fname);
162 c = gnutls_realloc(d->data, sbuf.st_size);
164 err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
169 d->size = sbuf.st_size;
170 file = fopen(fname, "r");
172 err("failed to open '%s' for reading\n", fname);
176 x = fread(d->data, d->size, 1, file);
178 err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
187 int main(int argc, char* argv[]) {
188 const char* version = NULL;
189 const char* debug_string = NULL;
191 gnutls_x509_privkey_t x509_privkey;
192 gnutls_datum_t data, test, clean;
196 const char *certfile, *keyfile;
197 gnutls_certificate_credentials_t pgp_creds;
199 gnutls_datum_t m, e, d, p, q, u, g, y, x;
201 /* gnutls_x509_crt_t crt; */
203 gnutls_openpgp_privkey_t pgp_privkey;
204 gnutls_pk_algorithm_t pgp_algo;
205 unsigned int pgp_bits;
207 char output_data[10240];
208 size_t ods = sizeof(output_data);
223 if (ret = gnutls_global_init(), ret) {
224 err("Failed to do gnutls_global_init() (error: %d)\n", ret);
230 version = gnutls_check_version(NULL);
233 err("gnutls version: %s\n", version);
235 err("no version found!\n");
239 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
240 loglevel = atoi(debug_string);
241 gnutls_global_set_log_function(logfunc);
243 gnutls_global_set_log_level(loglevel);
244 err("set log level to %d\n", loglevel);
247 if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
248 err("Failed to initialize X.509 private key (error: %d)\n", ret);
252 if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
253 err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
257 /* slurp in the private key from stdin */
258 if (ret = set_datum_fd(&data, 0), ret) {
259 err("didn't read file descriptor 0\n");
263 /* Or, instead, read in key from a file name:
264 if (ret = set_datum_file(&data, argv[1]), ret) {
265 err("didn't read file '%s'\n", argv[1]);
270 /* treat the passed file as an X.509 private key, and extract its
273 /* if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */
274 /* err("Failed to import the X.509 key (error: %d)\n", ret); */
277 /* gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */
279 /* try to print the PEM-encoded private key: */
280 /* ret = gnutls_x509_privkey_export (x509_privkey, */
281 /* GNUTLS_X509_FMT_PEM, */
284 /* printf("ret: %u; ods: %u;\n", ret, ods); */
285 /* if (ret == 0) { */
286 /* write(0, output_data, ods); */
289 copy_datum(&clean, &data);
290 copy_datum(&test, &data);
292 if (0 != compare_data(&data, &clean))
293 err("data do not match after initial copy\n");
294 /* format could be either: GNUTLS_OPENPGP_FMT_RAW,
295 GNUTLS_OPENPGP_FMT_BASE64; we'll try them both, raw first */
299 /* if (ret = gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret) */
300 /* err("failed to import the OpenPGP private key in RAW format (error: %d)\n", ret); */
301 /* if (0 != compare_data(&data, &clean)) */
302 /* err("Datum changed after privkey import in raw format!\n"); */
305 if (ret = gnutls_openpgp_privkey_import (pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret)
306 err("failed to import the OpenPGP private key in BASE64 format (error: %d)\n", ret);
307 if (0 != compare_data(&data, &clean))
308 err("Datum changed after privkey import in base64 format!\n");
312 pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits);
314 err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
317 if (pgp_algo == GNUTLS_PK_RSA) {
318 err("OpenPGP RSA Key, with %d bits\n", pgp_bits);
319 ret = gnutls_openpgp_privkey_export_rsa_raw(pgp_privkey, &m, &e, &d, &p, &q, &u);
320 if (GNUTLS_E_SUCCESS != ret) {
321 err ("failed to export RSA key parameters (error: %d)\n", ret);
325 ret = gnutls_x509_privkey_import_rsa_raw (x509_privkey, &m, &e, &d, &p, &q, &u);
326 if (GNUTLS_E_SUCCESS != ret) {
327 err ("failed to import RSA key parameters (error: %d)\n", ret);
330 } else if (pgp_algo == GNUTLS_PK_DSA) {
331 err("OpenPGP DSA Key, with %d bits\n", pgp_bits);
332 ret = gnutls_openpgp_privkey_export_dsa_raw(pgp_privkey, &p, &q, &g, &y, &x);
333 if (GNUTLS_E_SUCCESS != ret) {
334 err ("failed to export DSA key parameters (error: %d)\n", ret);
338 ret = gnutls_x509_privkey_import_dsa_raw (x509_privkey, &p, &q, &g, &y, &x);
339 if (GNUTLS_E_SUCCESS != ret) {
340 err ("failed to import DSA key parameters (error: %d)\n", ret);
344 err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
348 /* 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); */
350 ret = gnutls_x509_privkey_fix(x509_privkey);
352 err("failed to fix up the private key in X.509 format (error: %d)\n", ret);
355 ret = gnutls_x509_privkey_export (x509_privkey,
359 printf("ret: %u; ods: %u;\n", ret, ods);
361 write(1, output_data, ods);
365 gnutls_x509_privkey_deinit(x509_privkey);
366 gnutls_openpgp_privkey_deinit(pgp_privkey);
367 gnutls_global_deinit();