gpgtar: Improve error messages.
[gnupg.git] / common / ssh-utils.c
1 /* ssh-utils.c - Secure Shell helper functions
2  * Copyright (C) 2011 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <https://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <assert.h>
35
36 #include "util.h"
37 #include "ssh-utils.h"
38
39
40 /* Return true if KEYPARMS holds an EdDSA key.  */
41 static int
42 is_eddsa (gcry_sexp_t keyparms)
43 {
44   int result = 0;
45   gcry_sexp_t list;
46   const char *s;
47   size_t n;
48   int i;
49
50   list = gcry_sexp_find_token (keyparms, "flags", 0);
51   for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
52     {
53       s = gcry_sexp_nth_data (list, i, &n);
54       if (!s)
55         continue; /* Not a data element. */
56
57       if (n == 5 && !memcmp (s, "eddsa", 5))
58         {
59           result = 1;
60           break;
61         }
62     }
63   gcry_sexp_release (list);
64   return result;
65 }
66
67 /* Dummy functions for es_mopen.  */
68 static void *dummy_realloc (void *mem, size_t size) { (void) size; return mem; }
69 static void dummy_free (void *mem) { (void) mem; }
70
71 /* Return the Secure Shell type fingerprint for KEY using digest ALGO.
72    The length of the fingerprint is returned at R_LEN and the
73    fingerprint itself at R_FPR.  In case of a error code is returned
74    and NULL stored at R_FPR.  */
75 static gpg_error_t
76 get_fingerprint (gcry_sexp_t key, int algo,
77                  void **r_fpr, size_t *r_len, int as_string)
78 {
79   gpg_error_t err;
80   gcry_sexp_t list = NULL;
81   gcry_sexp_t l2 = NULL;
82   const char *s;
83   char *name = NULL;
84   int idx;
85   const char *elems;
86   gcry_md_hd_t md = NULL;
87   int blobmode = 0;
88
89   *r_fpr = NULL;
90   *r_len = 0;
91
92   /* Check that the first element is valid. */
93   list = gcry_sexp_find_token (key, "public-key", 0);
94   if (!list)
95     list = gcry_sexp_find_token (key, "private-key", 0);
96   if (!list)
97     list = gcry_sexp_find_token (key, "protected-private-key", 0);
98   if (!list)
99     list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
100   if (!list)
101     {
102       err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP);
103       goto leave;
104     }
105
106   l2 = gcry_sexp_cadr (list);
107   gcry_sexp_release (list);
108   list = l2;
109   l2 = NULL;
110
111   name = gcry_sexp_nth_string (list, 0);
112   if (!name)
113     {
114       err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
115       goto leave;
116     }
117
118   err = gcry_md_open (&md, algo, 0);
119   if (err)
120     goto leave;
121
122   switch (gcry_pk_map_name (name))
123     {
124     case GCRY_PK_RSA:
125       elems = "en";
126       gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
127       break;
128
129     case GCRY_PK_DSA:
130       elems = "pqgy";
131       gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
132       break;
133
134     case GCRY_PK_ECC:
135       if (is_eddsa (list))
136         {
137           elems = "q";
138           blobmode = 1;
139           /* For now there is just one curve, thus no need to switch
140              on it.  */
141           gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15);
142         }
143       else
144         {
145           /* We only support the 3 standard curves for now.  It is
146              just a quick hack.  */
147           elems = "q";
148           gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
149           l2 = gcry_sexp_find_token (list, "curve", 0);
150           if (!l2)
151             elems = "";
152           else
153             {
154               gcry_free (name);
155               name = gcry_sexp_nth_string (l2, 1);
156               gcry_sexp_release (l2);
157               l2 = NULL;
158               if (!name)
159                 elems = "";
160               else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256"))
161                 gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
162               else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384"))
163                 gcry_md_write (md, "384\0\0\0\x08nistp384", 15);
164               else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521"))
165                 gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
166               else
167                 elems = "";
168             }
169           if (!*elems)
170             err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
171         }
172       break;
173
174     default:
175       elems = "";
176       err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
177       break;
178     }
179   if (err)
180     goto leave;
181
182
183   for (idx = 0, s = elems; *s; s++, idx++)
184     {
185       l2 = gcry_sexp_find_token (list, s, 1);
186       if (!l2)
187         {
188           err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
189           goto leave;
190         }
191       if (blobmode)
192         {
193           const char *blob;
194           size_t bloblen;
195           unsigned char lenbuf[4];
196
197           blob = gcry_sexp_nth_data (l2, 1, &bloblen);
198           if (!blob)
199             {
200               err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
201               goto leave;
202             }
203           blob++;
204           bloblen--;
205           lenbuf[0] = bloblen >> 24;
206           lenbuf[1] = bloblen >> 16;
207           lenbuf[2] = bloblen >>  8;
208           lenbuf[3] = bloblen;
209           gcry_md_write (md, lenbuf, 4);
210           gcry_md_write (md, blob, bloblen);
211         }
212       else
213         {
214           gcry_mpi_t a;
215           unsigned char *buf;
216           size_t buflen;
217
218           a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
219           gcry_sexp_release (l2);
220           l2 = NULL;
221           if (!a)
222             {
223               err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
224               goto leave;
225             }
226
227           err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
228           gcry_mpi_release (a);
229           if (err)
230             goto leave;
231           gcry_md_write (md, buf, buflen);
232           gcry_free (buf);
233         }
234     }
235
236   if (as_string)
237     {
238       const char *algo_name;
239       char *fpr;
240
241       /* Prefix string with the algorithm name and a colon.  */
242       algo_name = gcry_md_algo_name (algo);
243       *r_fpr = xtrymalloc (strlen (algo_name) + 1 + 3 * gcry_md_get_algo_dlen (algo) + 1);
244       if (*r_fpr == NULL)
245         {
246           err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
247           goto leave;
248         }
249
250       memcpy (*r_fpr, algo_name, strlen (algo_name));
251       fpr = (char *) *r_fpr + strlen (algo_name);
252       *fpr++ = ':';
253
254       if (algo == GCRY_MD_MD5)
255         {
256           bin2hexcolon (gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo), fpr);
257           strlwr (fpr);
258         }
259       else
260         {
261           struct b64state b64s;
262           estream_t stream;
263           char *p;
264           long int len;
265
266           /* Write the base64-encoded hash to fpr.  */
267           stream = es_mopen (fpr, 3 * gcry_md_get_algo_dlen (algo) + 1, 0,
268                              0, dummy_realloc, dummy_free, "w");
269           if (stream == NULL)
270             {
271               err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
272               goto leave;
273             }
274
275           err = b64enc_start_es (&b64s, stream, "");
276           if (err)
277             {
278               es_fclose (stream);
279               goto leave;
280             }
281
282           err = b64enc_write (&b64s,
283                               gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo));
284           if (err)
285             {
286               es_fclose (stream);
287               goto leave;
288             }
289
290           /* Finish, get the length, and close the stream.  */
291           err = b64enc_finish (&b64s);
292           len = es_ftell (stream);
293           es_fclose (stream);
294           if (err)
295             goto leave;
296
297           /* Terminate.  */
298           fpr[len] = 0;
299
300           /* Strip the trailing padding characters.  */
301           for (p = fpr + len - 1; p > fpr && *p == '='; p--)
302             *p = 0;
303         }
304
305       *r_len = strlen (*r_fpr) + 1;
306     }
307   else
308     {
309       *r_len = gcry_md_get_algo_dlen (algo);
310       *r_fpr = xtrymalloc (*r_len);
311       if (!*r_fpr)
312         {
313           err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
314           goto leave;
315         }
316       memcpy (*r_fpr, gcry_md_read (md, algo), *r_len);
317     }
318   err = 0;
319
320  leave:
321   gcry_free (name);
322   gcry_sexp_release (l2);
323   gcry_md_close (md);
324   gcry_sexp_release (list);
325   return err;
326 }
327
328 /* Return the Secure Shell type fingerprint for KEY using digest ALGO.
329    The length of the fingerprint is returned at R_LEN and the
330    fingerprint itself at R_FPR.  In case of an error an error code is
331    returned and NULL stored at R_FPR.  */
332 gpg_error_t
333 ssh_get_fingerprint (gcry_sexp_t key, int algo,
334                      void **r_fpr, size_t *r_len)
335 {
336   return get_fingerprint (key, algo, r_fpr, r_len, 0);
337 }
338
339
340 /* Return the Secure Shell type fingerprint for KEY using digest ALGO
341    as a string.  The fingerprint is mallcoed and stored at R_FPRSTR.
342    In case of an error an error code is returned and NULL stored at
343    R_FPRSTR.  */
344 gpg_error_t
345 ssh_get_fingerprint_string (gcry_sexp_t key, int algo, char **r_fprstr)
346 {
347   gpg_error_t err;
348   size_t dummy;
349   void *string;
350
351   err = get_fingerprint (key, algo, &string, &dummy, 1);
352   *r_fprstr = string;
353   return err;
354 }