1 /* ssh-utils.c - Secure Shell helper functions
2 * Copyright (C) 2011 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * This file is free software; you can redistribute it and/or modify
7 * it under the terms of either
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.
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.
19 * or both in parallel, as here.
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.
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/>.
37 #include "ssh-utils.h"
40 /* Return true if KEYPARMS holds an EdDSA key. */
42 is_eddsa (gcry_sexp_t keyparms)
50 list = gcry_sexp_find_token (keyparms, "flags", 0);
51 for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
53 s = gcry_sexp_nth_data (list, i, &n);
55 continue; /* Not a data element. */
57 if (n == 5 && !memcmp (s, "eddsa", 5))
63 gcry_sexp_release (list);
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; }
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. */
76 get_fingerprint (gcry_sexp_t key, int algo,
77 void **r_fpr, size_t *r_len, int as_string)
80 gcry_sexp_t list = NULL;
81 gcry_sexp_t l2 = NULL;
86 gcry_md_hd_t md = NULL;
92 /* Check that the first element is valid. */
93 list = gcry_sexp_find_token (key, "public-key", 0);
95 list = gcry_sexp_find_token (key, "private-key", 0);
97 list = gcry_sexp_find_token (key, "protected-private-key", 0);
99 list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
102 err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP);
106 l2 = gcry_sexp_cadr (list);
107 gcry_sexp_release (list);
111 name = gcry_sexp_nth_string (list, 0);
114 err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
118 err = gcry_md_open (&md, algo, 0);
122 switch (gcry_pk_map_name (name))
126 gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
131 gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
139 /* For now there is just one curve, thus no need to switch
141 gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15);
145 /* We only support the 3 standard curves for now. It is
146 just a quick hack. */
148 gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
149 l2 = gcry_sexp_find_token (list, "curve", 0);
155 name = gcry_sexp_nth_string (l2, 1);
156 gcry_sexp_release (l2);
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);
170 err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
176 err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
183 for (idx = 0, s = elems; *s; s++, idx++)
185 l2 = gcry_sexp_find_token (list, s, 1);
188 err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
195 unsigned char lenbuf[4];
197 blob = gcry_sexp_nth_data (l2, 1, &bloblen);
200 err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
205 lenbuf[0] = bloblen >> 24;
206 lenbuf[1] = bloblen >> 16;
207 lenbuf[2] = bloblen >> 8;
209 gcry_md_write (md, lenbuf, 4);
210 gcry_md_write (md, blob, bloblen);
218 a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
219 gcry_sexp_release (l2);
223 err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
227 err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
228 gcry_mpi_release (a);
231 gcry_md_write (md, buf, buflen);
238 const char *algo_name;
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);
246 err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
250 strncpy (*r_fpr, algo_name, strlen (algo_name));
251 fpr = (char *) *r_fpr + strlen (algo_name);
254 if (algo == GCRY_MD_MD5)
256 bin2hexcolon (gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo), fpr);
261 struct b64state b64s;
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");
271 err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
275 err = b64enc_start_es (&b64s, stream, "");
282 err = b64enc_write (&b64s,
283 gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo));
290 /* Finish, get the length, and close the stream. */
291 err = b64enc_finish (&b64s);
292 len = es_ftell (stream);
300 /* Strip the trailing padding characters. */
301 for (p = fpr + len - 1; p > fpr && *p == '='; p--)
305 *r_len = strlen (*r_fpr) + 1;
309 *r_len = gcry_md_get_algo_dlen (algo);
310 *r_fpr = xtrymalloc (*r_len);
313 err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
316 memcpy (*r_fpr, gcry_md_read (md, algo), *r_len);
322 gcry_sexp_release (l2);
324 gcry_sexp_release (list);
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. */
333 ssh_get_fingerprint (gcry_sexp_t key, int algo,
334 void **r_fpr, size_t *r_len)
336 return get_fingerprint (key, algo, r_fpr, r_len, 0);
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
345 ssh_get_fingerprint_string (gcry_sexp_t key, int algo, char **r_fprstr)
351 err = get_fingerprint (key, algo, &string, &dummy, 1);