key translator: added more debugging infrastructure.
[monkeysphere.git] / main.c
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 /* 
14    Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
15    Date: Tue, 01 Apr 2008
16    License: GPL v3 or later
17
18    monkeysphere private key translator: execute this with an GPG
19    secret key on stdin (at the moment, only passphraseless RSA keys
20    work).
21
22    It will spit out a PEM-encoded version of the key on stdout, which
23    can be fed into ssh-add like this:
24
25     gpg --export-secret-keys $KEYID | monkeysphere | ssh-add -c /dev/stdin
26
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.
29
30    Notes: gpgkey2ssh doesn't seem to provide the same public
31    keys. Mighty weird!
32
33 0 wt215@squeak:~/monkeysphere$ gpg --export-secret-keys 1DCDF89F | ~dkg/src/monkeysphere/monkeysphere  | ssh-add -c /dev/stdin
34 gnutls version: 2.3.4
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$ 
43
44  */
45
46
47 int loglevel = 0;
48
49
50 void err(const char* fmt, ...) {
51   va_list ap;
52   va_start(ap, fmt);
53   vfprintf(stderr, fmt, ap);
54   va_end(ap);
55 }
56
57 void logfunc(int level, const char* string) {
58   fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
59 }
60
61 void init_datum(gnutls_datum_t* d) {
62   d->data = NULL;
63   d->size = 0;
64 }
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);
69 }
70 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
71   if (a->size > b->size) {
72     err("a is larger\n");
73     return 1;
74   }
75   if (a->size < b->size) {
76     err("b is larger\n");
77     return -1;
78   }
79   return memcmp(a->data, b->data, a->size);
80 }
81 void free_datum(gnutls_datum_t* d) {
82   gnutls_free(d->data);
83   d->data = NULL;
84   d->size = 0;
85 }
86
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;
91
92   c = gnutls_realloc(d->data, x);
93   if (NULL == c)
94     return -1;
95   d->data = c;
96   d->size = x;
97   memcpy(d->data, s, x);
98   return 0;
99 }
100
101 /* read the passed-in file descriptor until EOF, store in a single
102    datum */
103 int set_datum_fd(gnutls_datum_t* d, int fd) {
104   unsigned int bufsize = 1024;
105   unsigned int len = 0;
106
107   FILE* f = fdopen(fd, "r");
108   if (bufsize > d->size) {
109     bufsize = 1024;
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   } else {
117     bufsize = d->size;
118   }
119   f = fdopen(fd, "r");
120   if (NULL == f) {
121     err("could not fdopen FD %d\n", fd);
122   }
123   clearerr(f);
124   while (!feof(f) && !ferror(f)) { 
125     if (len == bufsize) {
126       /* allocate more space by doubling: */
127       bufsize *= 2;
128       d->data = gnutls_realloc(d->data, bufsize);
129       if (d->data == NULL) {
130         err("out of memory!\n"); 
131         return -1;
132       };
133       d->size = bufsize;
134     }
135     len += fread(d->data + len, 1, bufsize - len, f);
136     /*     err("read %d bytes\n", len); */
137   }
138   if (ferror(f)) {
139     err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
140     return -1;
141   }
142     
143   /* touch up buffer size to match reality: */
144   d->data = gnutls_realloc(d->data, len);
145   d->size = len;
146   return 0;
147 }
148
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) {
152   struct stat sbuf;
153   unsigned char* c = NULL;
154   FILE* file = NULL;
155   size_t x = 0;
156
157   if (0 != stat(fname, &sbuf)) {
158     err("failed to stat '%s'\n", fname);
159     return -1;
160   }
161   
162   c = gnutls_realloc(d->data, sbuf.st_size);
163   if (NULL == c) {
164     err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
165     return -1;
166   }
167
168   d->data = c;
169   d->size = sbuf.st_size;
170   file = fopen(fname, "r");
171   if (NULL == file) {
172     err("failed to open '%s' for reading\n",  fname);
173     return -1;
174   }
175
176   x = fread(d->data, d->size, 1, file);
177   if (x != 1) {
178     err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
179     fclose(file);
180     return -1;
181   }
182   fclose(file);
183   return 0;
184 }
185
186
187 int main(int argc, char* argv[]) {
188   const char* version = NULL;
189   const char* debug_string = NULL;
190
191   gnutls_x509_privkey_t x509_privkey;
192   gnutls_datum_t data, test, clean;
193   int ret;
194
195   /*  
196       const char *certfile, *keyfile;
197       gnutls_certificate_credentials_t pgp_creds;
198   */
199   gnutls_datum_t m, e, d, p, q, u, g, y, x;
200
201   /*  gnutls_x509_crt_t crt; */
202
203   gnutls_openpgp_privkey_t pgp_privkey;
204   gnutls_pk_algorithm_t pgp_algo;
205   unsigned int pgp_bits;
206
207   char output_data[10240];
208   size_t ods = sizeof(output_data);
209   
210   init_datum(&data);
211   init_datum(&test);
212   init_datum(&clean);
213   init_datum(&m);
214   init_datum(&e);
215   init_datum(&d);
216   init_datum(&p);
217   init_datum(&q);
218   init_datum(&u);
219   init_datum(&g);
220   init_datum(&y);
221   init_datum(&x);
222
223   if (ret = gnutls_global_init(), ret) {
224     err("Failed to do gnutls_global_init() (error: %d)\n", ret);
225     return 1;
226   }
227
228
229
230   version = gnutls_check_version(NULL);
231
232   if (version) 
233     err("gnutls version: %s\n", version);
234   else {
235     err("no version found!\n");
236     return 1;
237   }
238
239   if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
240     loglevel = atoi(debug_string);
241     gnutls_global_set_log_function(logfunc);
242     
243     gnutls_global_set_log_level(loglevel);
244     err("set log level to %d\n", loglevel);
245   }
246
247   if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
248     err("Failed to initialize X.509 private key (error: %d)\n", ret);
249     return 1;
250   }
251
252   if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
253     err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
254     return 1;
255   }
256
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");
260     return 1;
261   }
262
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]);
266     return 1;
267   }
268 */
269
270   /* treat the passed file as an X.509 private key, and extract its
271      component values: */
272
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); */
275 /*     return 1; */
276 /*   } */
277 /*   gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */
278
279   /* try to print the PEM-encoded private key: */
280 /*   ret = gnutls_x509_privkey_export (x509_privkey, */
281 /*                                  GNUTLS_X509_FMT_PEM, */
282 /*                                  output_data, */
283 /*                                  &ods); */
284 /*   printf("ret: %u; ods: %u;\n", ret, ods); */
285 /*   if (ret == 0) { */
286 /*     write(0, output_data, ods); */
287 /*   } */
288
289   copy_datum(&clean, &data);
290   copy_datum(&test, &data);
291   
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 */
296
297
298
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"); */
303
304
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");
309
310
311
312   pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits);
313   if (pgp_algo < 0) {
314     err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
315     return 1;
316   }
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);
322       return 1;
323     }
324
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);
328       return 1;
329     }
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);
335       return 1;
336     }
337
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);
341       return 1;
342     }
343   } else {
344     err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", pgp_algo);
345     return 1;
346   }
347   
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); */
349   
350   ret = gnutls_x509_privkey_fix(x509_privkey);
351   if (ret != 0) {
352     err("failed to fix up the private key in X.509 format (error: %d)\n", ret);
353     return 1; 
354   }
355   ret = gnutls_x509_privkey_export (x509_privkey,
356                                     GNUTLS_X509_FMT_PEM,
357                                     output_data,
358                                     &ods);
359   printf("ret: %u; ods: %u;\n", ret, ods);
360   if (ret == 0) {
361     write(1, output_data, ods);
362   }
363
364
365   gnutls_x509_privkey_deinit(x509_privkey);
366   gnutls_openpgp_privkey_deinit(pgp_privkey);
367   gnutls_global_deinit();
368   return 0;
369 }