Change all http://www.gnu.org in license notices to https://
[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
68 /* Return the Secure Shell type fingerprint for KEY.  The length of
69    the fingerprint is returned at R_LEN and the fingerprint itself at
70    R_FPR.  In case of a error code is returned and NULL stored at
71    R_FPR.  */
72 static gpg_error_t
73 get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
74 {
75   gpg_error_t err;
76   gcry_sexp_t list = NULL;
77   gcry_sexp_t l2 = NULL;
78   const char *s;
79   char *name = NULL;
80   int idx;
81   const char *elems;
82   gcry_md_hd_t md = NULL;
83   int blobmode = 0;
84
85   *r_fpr = NULL;
86   *r_len = 0;
87
88   /* Check that the first element is valid. */
89   list = gcry_sexp_find_token (key, "public-key", 0);
90   if (!list)
91     list = gcry_sexp_find_token (key, "private-key", 0);
92   if (!list)
93     list = gcry_sexp_find_token (key, "protected-private-key", 0);
94   if (!list)
95     list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
96   if (!list)
97     {
98       err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP);
99       goto leave;
100     }
101
102   l2 = gcry_sexp_cadr (list);
103   gcry_sexp_release (list);
104   list = l2;
105   l2 = NULL;
106
107   name = gcry_sexp_nth_string (list, 0);
108   if (!name)
109     {
110       err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
111       goto leave;
112     }
113
114   err = gcry_md_open (&md, GCRY_MD_MD5, 0);
115   if (err)
116     goto leave;
117
118   switch (gcry_pk_map_name (name))
119     {
120     case GCRY_PK_RSA:
121       elems = "en";
122       gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
123       break;
124
125     case GCRY_PK_DSA:
126       elems = "pqgy";
127       gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
128       break;
129
130     case GCRY_PK_ECC:
131       if (is_eddsa (list))
132         {
133           elems = "q";
134           blobmode = 1;
135           /* For now there is just one curve, thus no need to switch
136              on it.  */
137           gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15);
138         }
139       else
140         {
141           /* We only support the 3 standard curves for now.  It is
142              just a quick hack.  */
143           elems = "q";
144           gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
145           l2 = gcry_sexp_find_token (list, "curve", 0);
146           if (!l2)
147             elems = "";
148           else
149             {
150               gcry_free (name);
151               name = gcry_sexp_nth_string (l2, 1);
152               gcry_sexp_release (l2);
153               l2 = NULL;
154               if (!name)
155                 elems = "";
156               else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256"))
157                 gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
158               else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384"))
159                 gcry_md_write (md, "384\0\0\0\x08nistp384", 15);
160               else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521"))
161                 gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
162               else
163                 elems = "";
164             }
165           if (!*elems)
166             err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
167         }
168       break;
169
170     default:
171       elems = "";
172       err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
173       break;
174     }
175   if (err)
176     goto leave;
177
178
179   for (idx = 0, s = elems; *s; s++, idx++)
180     {
181       l2 = gcry_sexp_find_token (list, s, 1);
182       if (!l2)
183         {
184           err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
185           goto leave;
186         }
187       if (blobmode)
188         {
189           const char *blob;
190           size_t bloblen;
191           unsigned char lenbuf[4];
192
193           blob = gcry_sexp_nth_data (l2, 1, &bloblen);
194           if (!blob)
195             {
196               err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
197               goto leave;
198             }
199           blob++;
200           bloblen--;
201           lenbuf[0] = bloblen >> 24;
202           lenbuf[1] = bloblen >> 16;
203           lenbuf[2] = bloblen >>  8;
204           lenbuf[3] = bloblen;
205           gcry_md_write (md, lenbuf, 4);
206           gcry_md_write (md, blob, bloblen);
207         }
208       else
209         {
210           gcry_mpi_t a;
211           unsigned char *buf;
212           size_t buflen;
213
214           a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
215           gcry_sexp_release (l2);
216           l2 = NULL;
217           if (!a)
218             {
219               err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
220               goto leave;
221             }
222
223           err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
224           gcry_mpi_release (a);
225           if (err)
226             goto leave;
227           gcry_md_write (md, buf, buflen);
228           gcry_free (buf);
229         }
230     }
231
232   *r_fpr = gcry_malloc (as_string? 61:20);
233   if (!*r_fpr)
234     {
235       err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
236       goto leave;
237     }
238
239   if (as_string)
240     {
241       bin2hexcolon (gcry_md_read (md, GCRY_MD_MD5), 16, *r_fpr);
242       *r_len = 3*16+1;
243       strlwr (*r_fpr);
244     }
245   else
246     {
247       memcpy (*r_fpr, gcry_md_read (md, GCRY_MD_MD5), 16);
248       *r_len = 16;
249     }
250   err = 0;
251
252  leave:
253   gcry_free (name);
254   gcry_sexp_release (l2);
255   gcry_md_close (md);
256   gcry_sexp_release (list);
257   return err;
258 }
259
260 /* Return the Secure Shell type fingerprint for KEY.  The length of
261    the fingerprint is returned at R_LEN and the fingerprint itself at
262    R_FPR.  In case of an error an error code is returned and NULL
263    stored at R_FPR.  */
264 gpg_error_t
265 ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len)
266 {
267   return get_fingerprint (key, r_fpr, r_len, 0);
268 }
269
270
271 /* Return the Secure Shell type fingerprint for KEY as a string.  The
272    fingerprint is mallcoed and stored at R_FPRSTR.  In case of an
273    error an error code is returned and NULL stored at R_FPRSTR.  */
274 gpg_error_t
275 ssh_get_fingerprint_string (gcry_sexp_t key, char **r_fprstr)
276 {
277   gpg_error_t err;
278   size_t dummy;
279   void *string;
280
281   err = get_fingerprint (key, &string, &dummy, 1);
282   *r_fprstr = string;
283   return err;
284 }