1 /* fingerprint.c - Get the fingerprint
2 * Copyright (C) 2001 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 <https://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <time.h>
27
28
29 #include "gpgsm.h"
30 #include <gcrypt.h>
31 #include <ksba.h>
32
33 #include "../common/host2net.h"
34
35
36 /* Return the fingerprint of the certificate (we can't put this into
37 libksba because we need libgcrypt support). The caller must
38 provide an array of sufficient length or NULL so that the function
39 allocates the array. If r_len is not NULL, the length of the
40 digest is returned; well, this can also be done by using
41 gcry_md_get_algo_dlen(). If algo is 0, a SHA-1 will be used.
42
43 If there is a problem , the function does never return NULL but a
44 digest of all 0xff.
45 */
46 unsigned char *
gpgsm_get_fingerprint(ksba_cert_t cert,int algo,unsigned char * array,int * r_len)47 gpgsm_get_fingerprint (ksba_cert_t cert, int algo,
48 unsigned char *array, int *r_len)
49 {
50 gcry_md_hd_t md;
51 int rc, len;
52
53 if (!algo)
54 algo = GCRY_MD_SHA1;
55
56 len = gcry_md_get_algo_dlen (algo);
57 log_assert (len);
58 if (!array)
59 array = xmalloc (len);
60
61 if (r_len)
62 *r_len = len;
63
64 /* Fist check whether we have cached the fingerprint. */
65 if (algo == GCRY_MD_SHA1)
66 {
67 size_t buflen;
68
69 log_assert (len >= 20);
70 if (!ksba_cert_get_user_data (cert, "sha1-fingerprint",
71 array, len, &buflen)
72 && buflen == 20)
73 return array;
74 }
75
76 /* No, need to compute it. */
77 rc = gcry_md_open (&md, algo, 0);
78 if (rc)
79 {
80 log_error ("md_open failed: %s\n", gpg_strerror (rc));
81 memset (array, 0xff, len); /* better return an invalid fpr than NULL */
82 return array;
83 }
84
85 rc = ksba_cert_hash (cert, 0, HASH_FNC, md);
86 if (rc)
87 {
88 log_error ("ksba_cert_hash failed: %s\n", gpg_strerror (rc));
89 gcry_md_close (md);
90 memset (array, 0xff, len); /* better return an invalid fpr than NULL */
91 return array;
92 }
93 gcry_md_final (md);
94 memcpy (array, gcry_md_read(md, algo), len );
95 gcry_md_close (md);
96
97 /* Cache an SHA-1 fingerprint. */
98 if ( algo == GCRY_MD_SHA1 )
99 ksba_cert_set_user_data (cert, "sha1-fingerprint", array, 20);
100
101 return array;
102 }
103
104
105 /* Return an allocated buffer with the formatted fingerprint */
106 char *
gpgsm_get_fingerprint_string(ksba_cert_t cert,int algo)107 gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo)
108 {
109 unsigned char digest[MAX_DIGEST_LEN];
110 char *buf;
111 int len;
112
113 if (!algo)
114 algo = GCRY_MD_SHA1;
115
116 len = gcry_md_get_algo_dlen (algo);
117 log_assert (len <= MAX_DIGEST_LEN );
118 gpgsm_get_fingerprint (cert, algo, digest, NULL);
119 buf = xmalloc (len*3+1);
120 bin2hexcolon (digest, len, buf);
121 return buf;
122 }
123
124 /* Return an allocated buffer with the formatted fingerprint as one
125 large hexnumber */
126 char *
gpgsm_get_fingerprint_hexstring(ksba_cert_t cert,int algo)127 gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo)
128 {
129 unsigned char digest[MAX_DIGEST_LEN];
130 char *buf;
131 int len;
132
133 if (!algo)
134 algo = GCRY_MD_SHA1;
135
136 len = gcry_md_get_algo_dlen (algo);
137 log_assert (len <= MAX_DIGEST_LEN );
138 gpgsm_get_fingerprint (cert, algo, digest, NULL);
139 buf = xmalloc (len*2+1);
140 bin2hex (digest, len, buf);
141 return buf;
142 }
143
144 /* Return a certificate ID. These are the last 4 bytes of the SHA-1
145 fingerprint. If R_HIGH is not NULL the next 4 bytes are stored
146 there. */
147 unsigned long
gpgsm_get_short_fingerprint(ksba_cert_t cert,unsigned long * r_high)148 gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high)
149 {
150 unsigned char digest[20];
151
152 gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL);
153 if (r_high)
154 *r_high = buf32_to_ulong (digest+12);
155 return buf32_to_ulong (digest + 16);
156 }
157
158
159 /* Return the so called KEYGRIP which is the SHA-1 hash of the public
160 key parameters expressed as an canonical encoded S-Exp. ARRAY must
161 be 20 bytes long. Returns ARRAY or a newly allocated buffer if ARRAY was
162 given as NULL. May return NULL on error. */
163 unsigned char *
gpgsm_get_keygrip(ksba_cert_t cert,unsigned char * array)164 gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array)
165 {
166 gcry_sexp_t s_pkey;
167 int rc;
168 ksba_sexp_t p;
169 size_t n;
170
171 p = ksba_cert_get_public_key (cert);
172 if (!p)
173 return NULL; /* oops */
174
175 if (DBG_X509)
176 log_debug ("get_keygrip for public key\n");
177 n = gcry_sexp_canon_len (p, 0, NULL, NULL);
178 if (!n)
179 {
180 log_error ("libksba did not return a proper S-Exp\n");
181 return NULL;
182 }
183 rc = gcry_sexp_sscan ( &s_pkey, NULL, (char*)p, n);
184 xfree (p);
185 if (rc)
186 {
187 log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc));
188 return NULL;
189 }
190 array = gcry_pk_get_keygrip (s_pkey, array);
191 gcry_sexp_release (s_pkey);
192 if (!array)
193 {
194 log_error ("can't calculate keygrip\n");
195 return NULL;
196 }
197 if (DBG_X509)
198 log_printhex (array, 20, "keygrip:");
199
200 return array;
201 }
202
203 /* Return an allocated buffer with the keygrip of CERT encoded as a
204 hexstring. NULL is returned in case of error. */
205 char *
gpgsm_get_keygrip_hexstring(ksba_cert_t cert)206 gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
207 {
208 unsigned char grip[20];
209 char *buf;
210
211 if (!gpgsm_get_keygrip (cert, grip))
212 return NULL;
213 buf = xtrymalloc (20*2+1);
214 if (buf)
215 bin2hex (grip, 20, buf);
216 return buf;
217 }
218
219
220 /* Return the PK algorithm used by CERT as well as the length in bits
221 * of the public key at NBITS. If R_CURVE is not NULL and an ECC
222 * algorithm is used the name or OID of the curve is stored there; the
223 * caller needs to free this value. */
224 int
gpgsm_get_key_algo_info2(ksba_cert_t cert,unsigned int * nbits,char ** r_curve)225 gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve)
226 {
227 gcry_sexp_t s_pkey;
228 int rc;
229 ksba_sexp_t p;
230 size_t n;
231 gcry_sexp_t l1, l2;
232 const char *curve;
233 const char *name;
234 char namebuf[128];
235
236 if (nbits)
237 *nbits = 0;
238 if (r_curve)
239 *r_curve = NULL;
240
241 p = ksba_cert_get_public_key (cert);
242 if (!p)
243 return 0;
244 n = gcry_sexp_canon_len (p, 0, NULL, NULL);
245 if (!n)
246 {
247 xfree (p);
248 return 0;
249 }
250 rc = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n);
251 xfree (p);
252 if (rc)
253 return 0;
254
255 if (nbits)
256 *nbits = gcry_pk_get_nbits (s_pkey);
257
258 /* Breaking the algorithm out of the S-exp is a bit of a challenge ... */
259 l1 = gcry_sexp_find_token (s_pkey, "public-key", 0);
260 if (!l1)
261 {
262 gcry_sexp_release (s_pkey);
263 return 0;
264 }
265
266 if (r_curve)
267 {
268 curve = gcry_pk_get_curve (l1, 0, NULL);
269 if (curve)
270 {
271 name = openpgp_oid_to_curve (openpgp_curve_to_oid (curve,
272 NULL, NULL), 0);
273 *r_curve = xtrystrdup (name? name : curve);
274 if (!*r_curve)
275 {
276 gcry_sexp_release (l1);
277 gcry_sexp_release (s_pkey);
278 return 0; /* Out of core. */
279 }
280 }
281 }
282
283 l2 = gcry_sexp_cadr (l1);
284 gcry_sexp_release (l1);
285 l1 = l2;
286 name = gcry_sexp_nth_data (l1, 0, &n);
287 if (name)
288 {
289 if (n > sizeof namebuf -1)
290 n = sizeof namebuf -1;
291 memcpy (namebuf, name, n);
292 namebuf[n] = 0;
293 }
294 else
295 *namebuf = 0;
296 gcry_sexp_release (l1);
297 gcry_sexp_release (s_pkey);
298 return gcry_pk_map_name (namebuf);
299 }
300
301
302 int
gpgsm_get_key_algo_info(ksba_cert_t cert,unsigned int * nbits)303 gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
304 {
305 return gpgsm_get_key_algo_info2 (cert, nbits, NULL);
306 }
307
308
309 /* This is a wrapper around pubkey_algo_string which takes a KSBA
310 * certificate instead of a Gcrypt public key. Note that this
311 * function may return NULL on error. */
312 char *
gpgsm_pubkey_algo_string(ksba_cert_t cert,int * r_algoid)313 gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid)
314 {
315 gpg_error_t err;
316 gcry_sexp_t s_pkey;
317 ksba_sexp_t p;
318 size_t n;
319 enum gcry_pk_algos algoid;
320 char *algostr;
321
322 p = ksba_cert_get_public_key (cert);
323 if (!p)
324 return NULL;
325 n = gcry_sexp_canon_len (p, 0, NULL, NULL);
326 if (!n)
327 {
328 xfree (p);
329 return NULL;
330 }
331 err = gcry_sexp_sscan (&s_pkey, NULL, (char *)p, n);
332 xfree (p);
333 if (err)
334 return NULL;
335
336 algostr = pubkey_algo_string (s_pkey, r_algoid? &algoid : NULL);
337 if (algostr && r_algoid)
338 *r_algoid = algoid;
339
340 gcry_sexp_release (s_pkey);
341 return algostr;
342 }
343
344
345 /* If KEY is an RSA key, return its modulus. For non-RSA keys or on
346 * error return NULL. */
347 gcry_mpi_t
gpgsm_get_rsa_modulus(ksba_cert_t cert)348 gpgsm_get_rsa_modulus (ksba_cert_t cert)
349 {
350 gpg_error_t err;
351 gcry_sexp_t key;
352 gcry_sexp_t list = NULL;
353 gcry_sexp_t l2 = NULL;
354 char *name = NULL;
355 gcry_mpi_t modulus = NULL;
356
357 {
358 ksba_sexp_t ckey;
359 size_t n;
360
361 ckey = ksba_cert_get_public_key (cert);
362 if (!ckey)
363 return NULL;
364 n = gcry_sexp_canon_len (ckey, 0, NULL, NULL);
365 if (!n)
366 {
367 xfree (ckey);
368 return NULL;
369 }
370 err = gcry_sexp_sscan (&key, NULL, (char *)ckey, n);
371 xfree (ckey);
372 if (err)
373 return NULL;
374 }
375
376 list = gcry_sexp_find_token (key, "public-key", 0);
377 if (!list)
378 list = gcry_sexp_find_token (key, "private-key", 0);
379 if (!list)
380 list = gcry_sexp_find_token (key, "protected-private-key", 0);
381 if (!list)
382 list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
383
384 gcry_sexp_release (key);
385 if (!list)
386 return NULL; /* No suitable key. */
387
388 l2 = gcry_sexp_cadr (list);
389 gcry_sexp_release (list);
390 list = l2;
391 l2 = NULL;
392
393 name = gcry_sexp_nth_string (list, 0);
394 if (!name)
395 ;
396 else if (gcry_pk_map_name (name) == GCRY_PK_RSA)
397 {
398 l2 = gcry_sexp_find_token (list, "n", 1);
399 if (l2)
400 modulus = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
401 }
402
403 gcry_free (name);
404 gcry_sexp_release (l2);
405 gcry_sexp_release (list);
406 return modulus;
407 }
408
409
410
411 /* For certain purposes we need a certificate id which has an upper
412 limit of the size. We use the hash of the issuer name and the
413 serial number for this. In most cases the serial number is not
414 that large and the resulting string can be passed on an assuan
415 command line. Everything is hexencoded with the serialnumber
416 delimited from the hash by a dot.
417
418 The caller must free the string.
419 */
420 char *
gpgsm_get_certid(ksba_cert_t cert)421 gpgsm_get_certid (ksba_cert_t cert)
422 {
423 ksba_sexp_t serial;
424 char *p;
425 char *endp;
426 unsigned char hash[20];
427 unsigned long n;
428 char *certid;
429 int i;
430
431 p = ksba_cert_get_issuer (cert, 0);
432 if (!p)
433 return NULL; /* Ooops: No issuer */
434 gcry_md_hash_buffer (GCRY_MD_SHA1, hash, p, strlen (p));
435 xfree (p);
436
437 serial = ksba_cert_get_serial (cert);
438 if (!serial)
439 return NULL; /* oops: no serial number */
440 p = (char *)serial;
441 if (*p != '(')
442 {
443 log_error ("Ooops: invalid serial number\n");
444 xfree (serial);
445 return NULL;
446 }
447 p++;
448 n = strtoul (p, &endp, 10);
449 p = endp;
450 if (*p != ':')
451 {
452 log_error ("Ooops: invalid serial number (no colon)\n");
453 xfree (serial);
454 return NULL;
455 }
456 p++;
457
458 certid = xtrymalloc ( 40 + 1 + n*2 + 1);
459 if (!certid)
460 {
461 xfree (serial);
462 return NULL; /* out of core */
463 }
464
465 for (i=0, endp = certid; i < 20; i++, endp += 2 )
466 sprintf (endp, "%02X", hash[i]);
467 *endp++ = '.';
468 for (i=0; i < n; i++, endp += 2)
469 sprintf (endp, "%02X", ((unsigned char*)p)[i]);
470 *endp = 0;
471
472 xfree (serial);
473 return certid;
474 }
475