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