New functions to compute an ssh style fingerprint.
[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  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <assert.h>
25
26 #include "util.h"
27 #include "ssh-utils.h"
28
29
30
31 /* Return the Secure Shell type fingerprint for KEY.  The length of
32    the fingerprint is returned at R_LEN and the fingerprint itself at
33    R_FPR.  In case of a error code is returned and NULL stored at
34    R_FPR.  This function is usually called via the ssh_get_fingerprint
35    macro which makes sure to use the correct value for ERRSOURCE. */
36 static gpg_error_t
37 get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
38                  gpg_err_source_t errsource, int as_string)
39 {
40   gpg_error_t err;
41   gcry_sexp_t list = NULL;
42   gcry_sexp_t l2 = NULL;
43   const char *s;
44   char *name = NULL;
45   int idx;
46   const char *elems;
47   gcry_md_hd_t md = NULL;
48
49   *r_fpr = NULL;
50   *r_len = 0;
51
52   /* Check that the first element is valid. */
53   list = gcry_sexp_find_token (key, "public-key", 0);
54   if (!list)
55     list = gcry_sexp_find_token (key, "private-key", 0);
56   if (!list)
57     list = gcry_sexp_find_token (key, "protected-private-key", 0);
58   if (!list)
59     list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
60   if (!list)
61     {
62       err = gpg_err_make (errsource, GPG_ERR_UNKNOWN_SEXP);
63       goto leave;
64     }
65
66   l2 = gcry_sexp_cadr (list);
67   gcry_sexp_release (list);
68   list = l2;
69   l2 = NULL;
70
71   name = gcry_sexp_nth_string (list, 0);
72   if (!name)
73     {
74       err = gpg_err_make (errsource, GPG_ERR_INV_SEXP);
75       goto leave;
76     }
77
78   err = gcry_md_open (&md, GCRY_MD_MD5, 0);
79   if (err)
80     goto leave;
81
82   switch (gcry_pk_map_name (name))
83     {
84     case GCRY_PK_RSA:
85       elems = "en";
86       gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
87       break;
88     case GCRY_PK_DSA:
89       elems = "pqgy";
90       gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
91       break;
92     default:
93       elems = "";
94       err = gpg_err_make (errsource, GPG_ERR_PUBKEY_ALGO);
95       break;
96     }
97   if (err)
98     goto leave;
99
100   for (idx = 0, s = elems; *s; s++, idx++)
101     {
102       gcry_mpi_t a;
103       unsigned char *buf;
104       size_t buflen;
105
106       l2 = gcry_sexp_find_token (list, s, 1);
107       if (!l2)
108         {
109           err = gpg_err_make (errsource, GPG_ERR_INV_SEXP);
110           goto leave;
111         }
112       a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
113       gcry_sexp_release (l2);
114       l2 = NULL;
115       if (!a)
116         {
117           err = gpg_err_make (errsource, GPG_ERR_INV_SEXP);
118           goto leave;
119         }
120
121       err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
122       gcry_mpi_release (a);
123       if (err)
124         goto leave;
125       gcry_md_write (md, buf, buflen);
126       gcry_free (buf);
127     }
128
129   *r_fpr = gcry_malloc (as_string? 61:20);
130   if (!*r_fpr)
131     {
132       err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
133       goto leave;
134     }
135
136   if (as_string)
137     {
138       bin2hexcolon (gcry_md_read (md, GCRY_MD_MD5), 16, *r_fpr);
139       *r_len = 3*16+1;
140       strlwr (*r_fpr);
141     }
142   else
143     {
144       memcpy (*r_fpr, gcry_md_read (md, GCRY_MD_MD5), 16);
145       *r_len = 16;
146     }
147   err = 0;
148
149  leave:
150   gcry_free (name);
151   gcry_sexp_release (l2);
152   gcry_md_close (md);
153   gcry_sexp_release (list);
154   return err;
155 }
156
157 /* Return the Secure Shell type fingerprint for KEY.  The length of
158    the fingerprint is returned at R_LEN and the fingerprint itself at
159    R_FPR.  In case of an error an error code is returned and NULL
160    stored at R_FPR.  This function is usually called via the
161    ssh_get_fingerprint macro which makes sure to use the correct value
162    for ERRSOURCE. */
163 gpg_error_t
164 _ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
165                       gpg_err_source_t errsource)
166 {
167   return get_fingerprint (key, r_fpr, r_len, errsource, 0);
168 }
169
170
171 /* Return the Secure Shell type fingerprint for KEY as a string.  The
172    fingerprint is mallcoed and stored at R_FPRSTR.  In case of an
173    error an error code is returned and NULL stored at R_FPRSTR.  This
174    function is usually called via the ssh_get_fingerprint_string macro
175    which makes sure to use the correct value for ERRSOURCE. */
176 gpg_error_t
177 _ssh_get_fingerprint_string (gcry_sexp_t key, char **r_fprstr,
178                              gpg_err_source_t errsource)
179 {
180   gpg_error_t err;
181   size_t dummy;
182   void *string;
183
184   err = get_fingerprint (key, &string, &dummy, errsource, 1);
185   *r_fprstr = string;
186   return err;
187 }