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