.
[gnupg.git] / tools / gpgkey2ssh.c
1 /* gpgkey2ssh.c - Converter  (Debug helper)
2  *      Copyright (C) 2005 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14  * License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* 
21    FIXME:  This tool needs some cleanup:
22
23    - Do not use assert() for error output.
24    - Add proper option parsing and standard options.
25    - retrieve_key_material needs to take the ordinal at field 1 in account.
26    0 Write a man page.
27 */
28
29 #include <config.h>
30
31 #include <gcrypt.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <assert.h>
35 #include <stdio.h>
36 #include <errno.h>
37
38 #include "util.h"
39 #include "sysutils.h"
40
41 \f
42
43 typedef struct pkdbuf
44 {
45   unsigned char *buffer;
46   size_t buffer_n;
47 } pkdbuf_t;
48
49 \f
50
51 /* Retrieve the public key material for the RSA key, whose fingerprint
52    is FPR, from gpg output, which can be read through the stream FP.
53    The RSA modulus will be stored at the address of M and MLEN, the
54    public exponent at E and ELEN.  Returns zero on success, an error
55    code on failure.  Caller must release the allocated buffers at M
56    and E if the function returns success.  */
57 static gpg_error_t
58 retrieve_key_material (FILE *fp, const char *hexkeyid, int *algorithm_id,
59                        pkdbuf_t **pkdbuf, size_t *pkdbuf_n)
60 {
61   pkdbuf_t *pkdbuf_new;
62   pkdbuf_t *pkdbuf_tmp;
63   size_t pkdbuf_new_n;
64   gcry_error_t err = 0;
65   char *line = NULL;    /* read_line() buffer. */
66   size_t line_size = 0; /* Helper for for read_line. */
67   int found_key = 0;    /* Helper to find a matching key. */
68   int id;
69   unsigned char *buffer;
70   size_t buffer_n;
71   int i;
72
73   pkdbuf_new = NULL;
74   pkdbuf_new_n = 0;
75   id = 0;
76
77   /* Loop over all records until we have found the subkey
78      corresponsing to the fingerprint. Inm general the first record
79      should be the pub record, but we don't rely on that.  Given that
80      we only need to look at one key, it is sufficient to compare the
81      keyid so that we don't need to look at "fpr" records. */
82   for (;;)
83     {
84       char *p;
85       char *fields[6];
86       int nfields;
87       size_t max_length;
88       gcry_mpi_t mpi;
89
90       max_length = 4096;
91       i = read_line (fp, &line, &line_size, &max_length);
92       if (!i)
93         break; /* EOF. */
94       if (i < 0)
95         {
96           err = gpg_error_from_syserror ();
97           goto leave; /* Error. */
98         }
99       if (!max_length)
100         {
101           err = gpg_error (GPG_ERR_TRUNCATED);
102           goto leave;  /* Line truncated - we better stop processing.  */
103         }
104
105       /* Parse the line into fields. */
106       for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
107         {
108           fields[nfields] = p;
109           p = strchr (p, ':');
110           if (p)
111             *(p++) = 0;
112         }
113       if (!nfields)
114         continue; /* No fields at all - skip line.  */
115
116       if (!found_key)
117         {
118           if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
119                && nfields > 4 &&
120                (((strlen (hexkeyid) == 8)
121                  && (strlen (fields[4]) == 16)
122                  && (! strcmp (fields[4] + 8, hexkeyid)))
123                 || ((strlen (hexkeyid) == 16)
124                     && (! strcmp (fields[4], hexkeyid)))))
125             {
126               found_key = 1;
127               /* Save algorithm ID.  */
128               id = atoi (fields[3]);
129             }
130           continue;
131         }
132       
133       if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
134         break; /* Next key - stop.  */
135
136       if ( strcmp (fields[0], "pkd") )
137         continue; /* Not a key data record.  */
138
139       /* FIXME, necessary?  */
140
141       i = atoi (fields[1]);
142       if ((nfields < 4) || (i < 0))
143         {
144           err = gpg_error (GPG_ERR_GENERAL);
145           goto leave;
146         }
147
148       err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
149       if (err)
150         mpi = NULL;
151
152       err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &buffer, &buffer_n, mpi);
153       gcry_mpi_release (mpi);
154       if (err)
155         goto leave;
156
157       pkdbuf_tmp = xrealloc (pkdbuf_new, sizeof (*pkdbuf_new) * (pkdbuf_new_n + 1));
158       if (pkdbuf_new != pkdbuf_tmp)
159         pkdbuf_new = pkdbuf_tmp;
160       pkdbuf_new[pkdbuf_new_n].buffer = buffer;
161       pkdbuf_new[pkdbuf_new_n].buffer_n = buffer_n;
162       pkdbuf_new_n++;
163     }
164
165   *algorithm_id = id;
166   *pkdbuf = pkdbuf_new;
167   *pkdbuf_n = pkdbuf_new_n;
168
169  leave:
170
171   if (err)
172     if (pkdbuf_new)
173       {
174         for (i = 0; i < pkdbuf_new_n; i++)
175           xfree (pkdbuf_new[i].buffer);
176         xfree (pkdbuf_new);
177       }
178   xfree (line);
179
180   return err;
181 }
182
183 \f
184
185 int
186 key_to_blob (unsigned char **blob, size_t *blob_n, const char *identifier, ...)
187 {
188   unsigned char *blob_new;
189   size_t blob_new_n;
190   unsigned char uint32_buffer[4];
191   u32 identifier_n;
192   FILE *stream;
193   va_list ap;
194   int ret;
195   pkdbuf_t *pkd;
196
197   stream = gnupg_tmpfile ();
198   assert (stream);
199
200   identifier_n = strlen (identifier);
201   uint32_buffer[0] = identifier_n >> 24;
202   uint32_buffer[1] = identifier_n >> 16;
203   uint32_buffer[2] = identifier_n >>  8;
204   uint32_buffer[3] = identifier_n >>  0;
205   ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
206   assert (ret == 1);
207   ret = fwrite (identifier, identifier_n, 1, stream);
208   assert (ret == 1);
209
210   va_start (ap, identifier);
211   while (1)
212     {
213       pkd = va_arg (ap, pkdbuf_t *);
214       if (! pkd)
215         break;
216
217       uint32_buffer[0] = pkd->buffer_n >> 24;
218       uint32_buffer[1] = pkd->buffer_n >> 16;
219       uint32_buffer[2] = pkd->buffer_n >>  8;
220       uint32_buffer[3] = pkd->buffer_n >>  0;
221       ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
222       assert (ret == 1);
223       ret = fwrite (pkd->buffer, pkd->buffer_n, 1, stream);
224       assert (ret == 1);
225     }
226
227   blob_new_n = ftell (stream);
228   rewind (stream);
229
230   blob_new = xmalloc (blob_new_n);
231   ret = fread (blob_new, blob_new_n, 1, stream);
232   assert (ret == 1);
233
234   *blob = blob_new;
235   *blob_n = blob_new_n;
236
237   fclose (stream);
238
239   return 0;
240 }
241
242 int
243 main (int argc, char **argv)
244 {
245   const char *keyid;
246   int algorithm_id;
247   pkdbuf_t *pkdbuf;
248   size_t pkdbuf_n;
249   char *command;
250   FILE *fp;
251   int ret;
252   gcry_error_t err;
253   unsigned char *blob;
254   size_t blob_n;
255   struct b64state b64_state;
256   const char *identifier;
257
258   pkdbuf = NULL;
259   pkdbuf_n = 0;
260
261   algorithm_id = 0;  /* (avoid cc warning) */
262   identifier = NULL; /* (avoid cc warning) */
263
264   assert (argc == 2);
265
266   keyid = argv[1];
267
268   ret = asprintf (&command,
269                   "gpg --list-keys --with-colons --with-key-data '%s'",
270                   keyid);
271   assert (ret > 0);
272
273   fp = popen (command, "r");
274   assert (fp);
275
276   err = retrieve_key_material (fp, keyid, &algorithm_id, &pkdbuf, &pkdbuf_n);
277   assert (! err);
278   assert ((algorithm_id == 1) || (algorithm_id == 17));
279
280   if (algorithm_id == 1)
281     {
282       identifier = "ssh-rsa";
283       ret = key_to_blob (&blob, &blob_n, identifier,
284                          &pkdbuf[1], &pkdbuf[0], NULL);
285     }
286   else if (algorithm_id == 17)
287     {
288       identifier = "ssh-dss";
289       ret = key_to_blob (&blob, &blob_n, identifier,
290                          &pkdbuf[0], &pkdbuf[1], &pkdbuf[2], &pkdbuf[3], NULL);
291     }
292   assert (! ret);
293
294   printf ("%s ", identifier);
295
296   err = b64enc_start (&b64_state, stdout, "");
297   assert (! err);
298   err = b64enc_write (&b64_state, blob, blob_n);
299   assert (! err);
300   err = b64enc_finish (&b64_state);
301   assert (! err);
302
303   printf (" COMMENT\n");
304
305   return 0;
306 }