Line data Source code
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 <http://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 6 : is_eddsa (gcry_sexp_t keyparms)
43 : {
44 6 : int result = 0;
45 : gcry_sexp_t list;
46 : const char *s;
47 : size_t n;
48 : int i;
49 :
50 6 : list = gcry_sexp_find_token (keyparms, "flags", 0);
51 6 : for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
52 : {
53 2 : s = gcry_sexp_nth_data (list, i, &n);
54 2 : if (!s)
55 0 : continue; /* Not a data element. */
56 :
57 2 : if (n == 5 && !memcmp (s, "eddsa", 5))
58 : {
59 2 : result = 1;
60 2 : break;
61 : }
62 : }
63 6 : gcry_sexp_release (list);
64 6 : 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 11 : get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
74 : {
75 : gpg_error_t err;
76 11 : gcry_sexp_t list = NULL;
77 11 : gcry_sexp_t l2 = NULL;
78 : const char *s;
79 11 : char *name = NULL;
80 : int idx;
81 : const char *elems;
82 11 : gcry_md_hd_t md = NULL;
83 11 : int blobmode = 0;
84 :
85 11 : *r_fpr = NULL;
86 11 : *r_len = 0;
87 :
88 : /* Check that the first element is valid. */
89 11 : list = gcry_sexp_find_token (key, "public-key", 0);
90 11 : if (!list)
91 11 : list = gcry_sexp_find_token (key, "private-key", 0);
92 11 : if (!list)
93 6 : list = gcry_sexp_find_token (key, "protected-private-key", 0);
94 11 : if (!list)
95 0 : list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
96 11 : if (!list)
97 : {
98 0 : err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP);
99 0 : goto leave;
100 : }
101 :
102 11 : l2 = gcry_sexp_cadr (list);
103 11 : gcry_sexp_release (list);
104 11 : list = l2;
105 11 : l2 = NULL;
106 :
107 11 : name = gcry_sexp_nth_string (list, 0);
108 11 : if (!name)
109 : {
110 0 : err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
111 0 : goto leave;
112 : }
113 :
114 11 : err = gcry_md_open (&md, GCRY_MD_MD5, 0);
115 11 : if (err)
116 0 : goto leave;
117 :
118 11 : switch (gcry_pk_map_name (name))
119 : {
120 : case GCRY_PK_RSA:
121 3 : elems = "en";
122 3 : gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
123 3 : break;
124 :
125 : case GCRY_PK_DSA:
126 2 : elems = "pqgy";
127 2 : gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
128 2 : break;
129 :
130 : case GCRY_PK_ECC:
131 6 : if (is_eddsa (list))
132 : {
133 2 : elems = "q";
134 2 : blobmode = 1;
135 : /* For now there is just one curve, thus no need to switch
136 : on it. */
137 2 : 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 4 : elems = "q";
144 4 : gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
145 4 : l2 = gcry_sexp_find_token (list, "curve", 0);
146 4 : if (!l2)
147 0 : elems = "";
148 : else
149 : {
150 4 : gcry_free (name);
151 4 : name = gcry_sexp_nth_string (l2, 1);
152 4 : gcry_sexp_release (l2);
153 4 : l2 = NULL;
154 4 : if (!name)
155 0 : elems = "";
156 4 : else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256"))
157 2 : gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
158 2 : else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384"))
159 1 : gcry_md_write (md, "384\0\0\0\x08nistp384", 15);
160 1 : else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521"))
161 1 : gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
162 : else
163 0 : elems = "";
164 : }
165 4 : if (!*elems)
166 0 : err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
167 : }
168 6 : break;
169 :
170 : default:
171 0 : elems = "";
172 0 : err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
173 0 : break;
174 : }
175 11 : if (err)
176 0 : goto leave;
177 :
178 :
179 31 : for (idx = 0, s = elems; *s; s++, idx++)
180 : {
181 20 : l2 = gcry_sexp_find_token (list, s, 1);
182 20 : if (!l2)
183 : {
184 0 : err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
185 0 : goto leave;
186 : }
187 20 : if (blobmode)
188 : {
189 : const char *blob;
190 : size_t bloblen;
191 : unsigned char lenbuf[4];
192 :
193 2 : blob = gcry_sexp_nth_data (l2, 1, &bloblen);
194 2 : if (!blob)
195 : {
196 0 : err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
197 0 : goto leave;
198 : }
199 2 : blob++;
200 2 : bloblen--;
201 2 : lenbuf[0] = bloblen >> 24;
202 2 : lenbuf[1] = bloblen >> 16;
203 2 : lenbuf[2] = bloblen >> 8;
204 2 : lenbuf[3] = bloblen;
205 2 : gcry_md_write (md, lenbuf, 4);
206 2 : 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 18 : a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
215 18 : gcry_sexp_release (l2);
216 18 : l2 = NULL;
217 18 : if (!a)
218 : {
219 0 : err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
220 0 : goto leave;
221 : }
222 :
223 18 : err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
224 18 : gcry_mpi_release (a);
225 18 : if (err)
226 0 : goto leave;
227 18 : gcry_md_write (md, buf, buflen);
228 18 : gcry_free (buf);
229 : }
230 : }
231 :
232 11 : *r_fpr = gcry_malloc (as_string? 61:20);
233 11 : if (!*r_fpr)
234 : {
235 0 : err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
236 0 : goto leave;
237 : }
238 :
239 11 : if (as_string)
240 : {
241 11 : bin2hexcolon (gcry_md_read (md, GCRY_MD_MD5), 16, *r_fpr);
242 11 : *r_len = 3*16+1;
243 11 : strlwr (*r_fpr);
244 : }
245 : else
246 : {
247 0 : memcpy (*r_fpr, gcry_md_read (md, GCRY_MD_MD5), 16);
248 0 : *r_len = 16;
249 : }
250 11 : err = 0;
251 :
252 : leave:
253 11 : gcry_free (name);
254 11 : gcry_sexp_release (l2);
255 11 : gcry_md_close (md);
256 11 : gcry_sexp_release (list);
257 11 : 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 0 : ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len)
266 : {
267 0 : 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 11 : 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 11 : err = get_fingerprint (key, &string, &dummy, 1);
282 11 : *r_fprstr = string;
283 11 : return err;
284 : }
|