initial pass at converting PEM-encoded private keys into OpenPGP public keys.
[monkeysphere.git] / ssh2gpg.c
1 #include "gnutls-helpers.h"
2
3 #include <gnutls/openpgp.h>
4 #include <gnutls/x509.h>
5
6 /* for waitpid() */
7 #include <sys/types.h>
8 #include <sys/wait.h>
9
10 /* for time() */
11 #include <time.h>
12
13 /* for htons() */
14 #include <arpa/inet.h>
15
16
17 /* 
18    Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
19    Date: Sun, 2008-04-20
20    License: GPL v3 or later
21
22    monkeysphere public key translator: execute this with an ssh
23    private key on stdin.  It currently only works with RSA keys.
24
25    it should eventually work with OpenSSH-style public keys instead of
26    the full private key, but it was easier to do this way.
27
28    It shoud spit out a version of the public key suitable for acting
29    as an OpenPGP public sub key packet.
30
31  */
32
33 int main(int argc, char* argv[]) {
34   gnutls_datum_t data;
35   int ret;
36   gnutls_x509_privkey_t x509_privkey;
37   gnutls_openpgp_crt_t openpgp_crt;
38   gnutls_openpgp_keyid_t keyid;
39   printable_keyid p_keyid;
40   unsigned int keyidx;
41   unsigned int usage, bits;
42   gnutls_pk_algorithm_t algo;
43
44   unsigned char packettag;
45   unsigned char openpgpversion;
46   time_t timestamp;
47   uint32_t clunkytime;
48   unsigned char openpgpalgo;
49   unsigned int packetlen;
50   uint16_t plen;
51
52   gnutls_datum_t m, e, d, p, q, u, g, y;
53   gnutls_datum_t algolabel;
54
55   char output_data[10240];
56   char userid[10240];
57   size_t uidsz = sizeof(userid);
58
59   const gnutls_datum_t* all[5];
60   int pipefd;
61   pid_t child_pid;
62   char* const args[] = {"/usr/bin/base64", "--wrap=0", NULL};
63   const char* algoname;
64   int mpicount;
65   int pipestatus;
66
67   init_gnutls();
68   
69   init_datum(&data);
70
71   init_datum(&m);
72   init_datum(&e);
73   init_datum(&d);
74   init_datum(&p);
75   init_datum(&q);
76   init_datum(&u);
77   init_datum(&g);
78   init_datum(&y);
79
80   init_datum(&algolabel);
81
82   init_keyid(keyid);
83
84   /* slurp in the private key from stdin */
85   if (ret = set_datum_fd(&data, 0), ret) {
86     err("didn't read file descriptor 0\n");
87     return 1;
88   }
89
90
91   if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) {
92     err("Failed to initialize private key structure (error: %d)\n", ret);
93     return 1;
94   }
95
96   err("assuming PEM formatted private key\n");
97   if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) {
98     err("failed to import the PEM-encoded private key (error: %d)\n", ret);
99     return ret;
100   }
101
102   algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey);
103   if (algo < 0) {
104     err("failed to get the algorithm of the PEM-encoded public key (error: %d)\n", algo);
105     return algo;
106   } else if (algo == GNUTLS_PK_RSA) {
107     err("RSA private key\n");
108     ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u);
109     if (GNUTLS_E_SUCCESS != ret) {
110       err ("failed to export RSA key parameters (error: %d)\n", ret);
111       return 1;
112     }
113     err("Modulus size %d, exponent size %d\n", m.size, e.size);
114   } else if (algo == GNUTLS_PK_DSA) {
115     err("DSA Key, not implemented!!\n", bits);
116     return 1;
117   } else {
118     err("Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo);
119     return 1;
120   }
121
122   /* now we have algo, and the various MPI data are set.  Can we
123      export them as a public subkey packet? */
124
125   /* this packet should be tagged 14, and should contain:
126
127      1 octet: version (4)
128      4 octets: time of generation (seconds since 1970)
129      1 octet: algo (http://tools.ietf.org/html/rfc4880#section-5.5.2 implies 1 for RSA)
130  
131      MPI: modulus
132      MPI: exponent
133   */
134
135   packetlen = 1 + 4 + 1;
136   /* FIXME: this is RSA only.  for DSA, there'll be more: */
137   packetlen += get_openpgp_mpi_size(&m) + get_openpgp_mpi_size(&e);
138
139   /* FIXME: we should generate this bound more cleanly -- i just
140      happen to know that 65535 is 2^16-1: */
141   if (packetlen > 65535) {
142     err("packet length is too long (%d)\n", packetlen);
143     return 1;
144   }
145
146   /* we're going to emit an old-style packet, with tag 14 (public
147      subkey), with a two-octet packet length */
148   packettag = 0x80 | (14 << 2) | 1;
149   
150   write(1, &packettag, sizeof(packettag));
151   plen = htons(packetlen);
152   write(1, &plen, sizeof(plen));
153
154   openpgpversion = 4;
155   write(1, &openpgpversion, 1);
156
157   timestamp = time(NULL);
158   clunkytime = htonl(timestamp);
159   write(1, &clunkytime, 4);
160
161   /* FIXME: handle things other than RSA */
162   openpgpalgo = 1;
163   write(1, &openpgpalgo, 1);
164   
165   write_openpgp_mpi_to_fd(1, &m);
166   write_openpgp_mpi_to_fd(1, &e);
167
168   gnutls_x509_privkey_deinit(x509_privkey);
169   gnutls_global_deinit();
170   return 0;
171 }