8c3129db65dc90a969ae65d4bf655cab565716b9
[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
19    ASCII-armored private RSA key on stdin (at the moment, only
20    passphraseless keys 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 void err(const char* fmt, ...) {
48   va_list ap;
49   va_start(ap, fmt);
50   vfprintf(stderr, fmt, ap);
51   va_end(ap);
52 }
53
54
55 void init_datum(gnutls_datum_t* d) {
56   d->data = NULL;
57   d->size = 0;
58 }
59 void free_datum(gnutls_datum_t* d) {
60   gnutls_free(d->data);
61   d->data = NULL;
62   d->size = 0;
63 }
64
65 /* read the passed-in string, store in a single datum */
66 int set_datum_string(gnutls_datum_t* d, const char* s) {
67   unsigned int x = strlen(s)+1;
68   unsigned char* c = NULL;
69
70   c = gnutls_realloc(d->data, x);
71   if (NULL == c)
72     return -1;
73   d->data = c;
74   d->size = x;
75   memcpy(d->data, s, x);
76   return 0;
77 }
78
79 /* read the passed-in file descriptor until EOF, store in a single
80    datum */
81 int set_datum_fd(gnutls_datum_t* d, int fd) {
82   unsigned int bufsize = 1024;
83   unsigned int len = 0;
84
85   FILE* f = fdopen(fd, "r");
86   if (bufsize > d->size) {
87     bufsize = 1024;
88     d->data = gnutls_realloc(d->data, bufsize);
89     if (d->data == NULL) {
90       err("out of memory!\n");
91       return -1;
92     }
93     d->size = bufsize;
94   } else {
95     bufsize = d->size;
96   }
97   f = fdopen(fd, "r");
98   if (NULL == f) {
99     err("could not fdopen FD %d\n", fd);
100   }
101   clearerr(f);
102   while (!feof(f) && !ferror(f)) { 
103     if (len == bufsize) {
104       /* allocate more space by doubling: */
105       bufsize *= 2;
106       d->data = gnutls_realloc(d->data, bufsize);
107       if (d->data == NULL) {
108         err("out of memory!\n"); 
109         return -1;
110       };
111       d->size = bufsize;
112     }
113     len += fread(d->data + len, 1, bufsize - len, f);
114     /*     err("read %d bytes\n", len); */
115   }
116   if (ferror(f)) {
117     err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
118     return -1;
119   }
120     
121   /* touch up buffer size to match reality: */
122   d->data = gnutls_realloc(d->data, len);
123   d->size = len;
124   return 0;
125 }
126
127 /* read the file indicated (by na1me) in the fname parameter.  store
128    its entire contents in a single datum. */
129 int set_datum_file(gnutls_datum_t* d, const char* fname) {
130   struct stat sbuf;
131   unsigned char* c = NULL;
132   FILE* file = NULL;
133   size_t x = 0;
134
135   if (0 != stat(fname, &sbuf)) {
136     err("failed to stat '%s'\n", fname);
137     return -1;
138   }
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
146   d->data = c;
147   d->size = sbuf.st_size;
148   file = fopen(fname, "r");
149   if (NULL == file) {
150     err("failed to open '%s' for reading\n",  fname);
151     return -1;
152   }
153
154   x = fread(d->data, d->size, 1, file);
155   if (x != 1) {
156     err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
157     fclose(file);
158     return -1;
159   }
160   fclose(file);
161   return 0;
162 }
163
164
165 int main(int argc, char* argv[]) {
166   const char* version = NULL;
167
168   gnutls_x509_privkey_t x509_privkey;
169   gnutls_datum_t data;
170   int ret;
171
172   /*  
173       const char *certfile, *keyfile;
174       gnutls_certificate_credentials_t pgp_creds;
175   */
176   gnutls_datum_t m, e, d, p, q, u;
177   /*  gnutls_x509_crt_t crt; */
178
179   gnutls_openpgp_privkey_t pgp_privkey;
180   gnutls_openpgp_crt_fmt_t pgp_format;
181   gnutls_pk_algorithm_t pgp_algo;
182   unsigned int pgp_bits;
183
184   char output_data[10240];
185   size_t ods = sizeof(output_data);
186
187   init_datum(&data);
188   init_datum(&m);
189   init_datum(&e);
190   init_datum(&d);
191   init_datum(&p);
192   init_datum(&q);
193   init_datum(&u);
194
195   if (ret = gnutls_global_init(), ret) {
196     err("Failed to do gnutls_global_init() (error: %d)\n", ret);
197     return 1;
198   }
199
200
201
202   version = gnutls_check_version(NULL);
203
204   if (version) 
205     err("gnutls version: %s\n", version);
206   else {
207     err("no version found!\n");
208     return 1;
209   }
210
211   if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
212     err("Failed to initialize X.509 private key (error: %d)\n", ret);
213     return 1;
214   }
215
216   if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) {
217     err("Failed to initialized OpenPGP private key (error: %d)\n", ret);
218     return 1;
219   }
220
221   /* slurp in the private key from stdin */
222   if (ret = set_datum_fd(&data, 0), ret) {
223     err("didn't read file descriptor 0\n");
224     return 1;
225   }
226
227   /* Or, instead, read in key from a file name: 
228   if (ret = set_datum_file(&data, argv[1]), ret) {
229     err("didn't read file '%s'\n", argv[1]);
230     return 1;
231   }
232 */
233
234   /* treat the passed file as an X.509 private key, and extract its
235      component values: */
236
237 /*   if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */
238 /*     err("Failed to import the X.509 key (error: %d)\n", ret); */
239 /*     return 1; */
240 /*   } */
241 /*   gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */
242
243   /* try to print the PEM-encoded private key: */
244 /*   ret = gnutls_x509_privkey_export (x509_privkey, */
245 /*                                  GNUTLS_X509_FMT_PEM, */
246 /*                                  output_data, */
247 /*                                  &ods); */
248 /*   printf("ret: %u; ods: %u;\n", ret, ods); */
249 /*   if (ret == 0) { */
250 /*     write(0, output_data, ods); */
251 /*   } */
252
253   
254   /* format could be either: GNUTLS_OPENPGP_FMT_RAW,
255      GNUTLS_OPENPGP_FMT_BASE64 */
256   pgp_format = GNUTLS_OPENPGP_FMT_RAW;
257   if (ret = gnutls_openpgp_privkey_import (pgp_privkey, &data, pgp_format, NULL, 0), ret) {
258     err("failed to import the OpenPGP private key (error: %d)\n", ret);
259     return 1;
260   }
261   pgp_algo = gnutls_openpgp_privkey_get_pk_algorithm(pgp_privkey, &pgp_bits);
262   if (pgp_algo < 0) {
263     err("failed to get OpenPGP key algorithm (error: %d)\n", pgp_algo);
264     return 1;
265   }
266   if (pgp_algo != GNUTLS_PK_RSA) {
267     err("OpenPGP Key was not RSA (actual algorithm was: %d)\n", pgp_algo);
268     return 1;
269   }
270   
271   err("OpenPGP RSA Key, with %d bits\n", pgp_bits);
272
273
274   ret = gnutls_openpgp_privkey_export_rsa_raw(pgp_privkey, &m, &e, &d, &p, &q, &u);
275   if (GNUTLS_E_SUCCESS != ret) {
276     err ("failed to export RSA key parameters (error: %0x)\n", ret);
277     return 1;
278   }
279
280   ret = gnutls_x509_privkey_import_rsa_raw (x509_privkey, &m, &e, &d, &p, &q, &u); 
281   if (GNUTLS_E_SUCCESS != ret) {
282     err ("failed to import RSA key parameters (error: %d)\n", ret);
283     return 1;
284   }
285   /* 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); */
286   
287   ret = gnutls_x509_privkey_fix(x509_privkey);
288   if (ret != 0) {
289     err("failed to fix up the private key in X.509 format (error: %d)\n", ret);
290     return 1; 
291   }
292   ret = gnutls_x509_privkey_export (x509_privkey,
293                                     GNUTLS_X509_FMT_PEM,
294                                     output_data,
295                                     &ods);
296   printf("ret: %u; ods: %u;\n", ret, ods);
297   if (ret == 0) {
298     write(1, output_data, ods);
299   }
300
301
302   gnutls_x509_privkey_deinit(x509_privkey);
303   gnutls_openpgp_privkey_deinit(pgp_privkey);
304   gnutls_global_deinit();
305   return 0;
306 }