xref: /freebsd/lib/libsecureboot/openpgp/opgp_key.c (revision a64729f5077d77e13b9497cb33ecb3c82e606ee8)
1 /*-
2  * Copyright (c) 2018, Juniper Networks, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 #include "../libsecureboot-priv.h"
28 
29 #include "decode.h"
30 #include "packet.h"
31 
32 /**
33  * @brief decode user-id packet
34  *
35  * This is trivial
36  *
37  * @sa rfc4880:5.11
38  */
39 ssize_t
40 decode_user(int tag, unsigned char **pptr, size_t len, OpenPGP_user *user)
41 {
42 	char *cp;
43 
44 	if (tag == 13) {
45 		user->id = malloc(len + 1);
46 		strncpy(user->id, (char *)*pptr, len);
47 		user->id[len] = '\0';
48 		user->name = user->id;
49 		cp = strchr(user->id, '<');
50 		if (cp > user->id) {
51 			user->id = strdup(user->id);
52 			cp[-1] = '\0';
53 		}
54 	}
55 	*pptr += len;
56 	return ((ssize_t)len);
57 }
58 
59 /**
60  * @brief decode a key packet
61  *
62  * We only really support v4 and RSA
63  *
64  * @sa rfc4880:5.5.1.1
65  */
66 ssize_t
67 decode_key(int tag, unsigned char **pptr, size_t len, OpenPGP_key *key)
68 {
69 	unsigned char *ptr;
70 	int version;
71 #ifdef USE_BEARSSL
72 	br_sha1_context mctx;
73 	unsigned char mdata[br_sha512_SIZE];
74 	size_t mlen;
75 #else
76 	RSA *rsa = NULL;
77 	const EVP_MD *md = NULL;
78 	EVP_MD_CTX mctx;
79 	unsigned char mdata[EVP_MAX_MD_SIZE];
80 	unsigned int mlen;
81 #endif
82 
83 	if (tag != 6)
84 		return (-1);
85 
86 	key->key = NULL;
87 	ptr = *pptr;
88 	version = *ptr;
89 	if (version == 4) {		/* all we support really */
90 		/* comput key fingerprint and id @sa rfc4880:12.2 */
91 		mdata[0] = 0x99;	/* rfc4880: 12.2.a.1 */
92 		mdata[1] = (len >> 8) & 0xff;
93 		mdata[2] = len & 0xff;
94 
95 #ifdef USE_BEARSSL
96 		br_sha1_init(&mctx);
97 		br_sha1_update(&mctx, mdata, 3);
98 		br_sha1_update(&mctx, ptr, len);
99 		br_sha1_out(&mctx, mdata);
100 		mlen = br_sha1_SIZE;
101 #else
102 		md = EVP_get_digestbyname("sha1");
103 		EVP_DigestInit(&mctx, md);
104 		EVP_DigestUpdate(&mctx, mdata, 3);
105 		EVP_DigestUpdate(&mctx, ptr, len);
106 		mlen = (unsigned int)sizeof(mdata);
107 		EVP_DigestFinal(&mctx, mdata, &mlen);
108 #endif
109 		key->id = octets2hex(&mdata[mlen - 8], 8);
110 	}
111 	ptr += 1;			/* done with version */
112 	ptr += 4;			/* skip ctime */
113 	if (version == 3)
114 		ptr += 2;		/* valid days */
115 	key->sig_alg = *ptr++;
116 	if (key->sig_alg == 1) {	/* RSA */
117 #ifdef USE_BEARSSL
118 		key->key = NEW(br_rsa_public_key);
119 		if (!key->key)
120 			goto oops;
121 		key->key->n = mpi2bn(&ptr, &key->key->nlen);
122 		key->key->e = mpi2bn(&ptr, &key->key->elen);
123 #else
124 		rsa = RSA_new();
125 		if (!rsa)
126 			goto oops;
127 		rsa->n = mpi2bn(&ptr);
128 		rsa->e = mpi2bn(&ptr);
129 		key->key = EVP_PKEY_new();
130 		if (!key->key || !rsa->n || !rsa->e) {
131 			goto oops;
132 		}
133 		if (!EVP_PKEY_set1_RSA(key->key, rsa))
134 			goto oops;
135 #endif
136 	}
137 	/* we are done */
138 	return ((ssize_t)len);
139 oops:
140 #ifdef USE_BEARSSL
141 	free(key->key);
142 	key->key = NULL;
143 #else
144 	if (rsa)
145 		RSA_free(rsa);
146 	if (key->key) {
147 		EVP_PKEY_free(key->key);
148 		key->key = NULL;
149 	}
150 #endif
151 	return (-1);
152 }
153 
154 static OpenPGP_key *
155 load_key_buf(unsigned char *buf, size_t nbytes)
156 {
157 	unsigned char *data = NULL;
158 	unsigned char *ptr;
159 	ssize_t rc;
160 	int tag;
161 	OpenPGP_key *key;
162 
163 	if (!buf)
164 		return (NULL);
165 
166 	initialize();
167 
168 	if (!(buf[0] & OPENPGP_TAG_ISTAG)) {
169 		/* Note: we do *not* free data */
170 		data = dearmor((char *)buf, nbytes, &nbytes);
171 		ptr = data;
172 	} else
173 		ptr = buf;
174 	key = NEW(OpenPGP_key);
175 	if (key) {
176 		rc = decode_packet(0, &ptr, nbytes, (decoder_t)decode_key,
177 		    key);
178 		if (rc < 0) {
179 			free(key);
180 			key = NULL;
181 		} else if (rc > 8) {
182 			int isnew, ltype;
183 
184 			tag = decode_tag(ptr, &isnew, &ltype);
185 			if (tag == 13) {
186 				key->user = NEW(OpenPGP_user);
187 				rc = decode_packet(0, &ptr, (size_t)rc,
188 				    (decoder_t)decode_user, key->user);
189 			}
190 		}
191 	}
192 	return (key);
193 }
194 
195 static LIST_HEAD(, OpenPGP_key_) trust_list;
196 
197 /**
198  * @brief add a key to our list
199  */
200 void
201 openpgp_trust_add(OpenPGP_key *key)
202 {
203 	static int once = 0;
204 
205 	if (!once) {
206 		once = 1;
207 
208 		LIST_INIT(&trust_list);
209 	}
210 	if (key && openpgp_trust_get(key->id) == NULL) {
211 		if (ve_anchor_verbose_get())
212 			printf("openpgp_trust_add(%s)\n", key->id);
213 		LIST_INSERT_HEAD(&trust_list, key, entries);
214 	}
215 }
216 
217 /**
218  * @brief add trust anchor from buf
219  */
220 int
221 openpgp_trust_add_buf(unsigned char *buf, size_t nbytes)
222 {
223 	OpenPGP_key *key;
224 
225 	if ((key = load_key_buf(buf, nbytes))) {
226 		openpgp_trust_add(key);
227 	}
228 	return (key != NULL);
229 }
230 
231 
232 /**
233  * @brief if keyID is in our list clobber it
234  *
235  * @return true if keyID removed
236  */
237 int
238 openpgp_trust_revoke(const char *keyID)
239 {
240 	OpenPGP_key *key, *tkey;
241 
242 	openpgp_trust_add(NULL);	/* initialize if needed */
243 
244 	LIST_FOREACH(key, &trust_list, entries) {
245 		if (strcmp(key->id, keyID) == 0) {
246 			tkey = key;
247 			LIST_REMOVE(tkey, entries);
248 			printf("openpgp_trust_revoke(%s)\n", key->id);
249 			memset(key, 0, sizeof(OpenPGP_key));
250 			free(key);
251 			return (1);
252 		}
253 	}
254 	return (0);
255 }
256 
257 /**
258  * @brief if keyID is in our list return the key
259  *
260  * @return key or NULL
261  */
262 OpenPGP_key *
263 openpgp_trust_get(const char *keyID)
264 {
265 	OpenPGP_key *key;
266 
267 	openpgp_trust_add(NULL);	/* initialize if needed */
268 
269 	LIST_FOREACH(key, &trust_list, entries) {
270 		if (strcmp(key->id, keyID) == 0)
271 			return (key);
272 	}
273 	return (NULL);
274 }
275 
276 /**
277  * @brief load a key from file
278  */
279 OpenPGP_key *
280 load_key_file(const char *kfile)
281 {
282 	unsigned char *data = NULL;
283 	size_t n;
284 	OpenPGP_key *key;
285 
286 	data = read_file(kfile, &n);
287 	key = load_key_buf(data, n);
288 	free(data);
289 	openpgp_trust_add(key);
290 	return (key);
291 }
292 
293 #ifdef HAVE_TA_ASC_H
294 #include <ta_asc.h>
295 #endif
296 
297 #ifndef _STANDALONE
298 /* we can lookup keyID in filesystem */
299 
300 static const char *trust_store[] = {
301 	"/var/db/trust",
302 	"/etc/db/trust",
303 	NULL,
304 };
305 
306 /**
307  * @brief lookup key id in trust store
308  *
309  */
310 static OpenPGP_key *
311 load_trusted_key_id(const char *keyID)
312 {
313 	char kfile[MAXPATHLEN];
314 	const char **tp;
315 	size_t n;
316 
317 	for (tp = trust_store; *tp; tp++) {
318 		n = (size_t)snprintf(kfile, sizeof(kfile), "%s/%s", *tp, keyID);
319 		if (n >= sizeof(kfile))
320 			return (NULL);
321 		if (access(kfile, R_OK) == 0) {
322 			return (load_key_file(kfile));
323 		}
324 	}
325 	return (NULL);
326 }
327 #endif
328 
329 /**
330  * @brief return key if trusted
331  */
332 OpenPGP_key *
333 load_key_id(const char *keyID)
334 {
335 	OpenPGP_key *key;
336 
337 	key = openpgp_trust_get(keyID);
338 #ifndef _STANDALONE
339 	if (!key)
340 		key = load_trusted_key_id(keyID);
341 #endif
342 	DEBUG_PRINTF(2, ("load_key_id(%s): %s\n", keyID, key ? "found" : "nope"));
343 	return (key);
344 }
345 
346 /**
347  * @brief initialize our internal trust store if any
348  */
349 int
350 openpgp_trust_init(void)
351 {
352 	static int once = -1;
353 #ifdef HAVE_TA_ASC
354 	OpenPGP_key *key;
355 	const char **tp;
356 	char *cp;
357 	size_t n;
358 #endif
359 
360 	if (once < 0) {
361 		once = 0;
362 #ifdef HAVE_TA_ASC
363 		for (tp = ta_ASC; *tp; tp++) {
364 			if ((cp = strdup(*tp))) {
365 				n = strlen(cp);
366 				key = load_key_buf((unsigned char *)cp, n);
367 				free(cp);
368 				if (key) {
369 					openpgp_trust_add(key);
370 					once++;
371 				}
372 			}
373 		}
374 #endif
375 	}
376 	return (once);
377 }
378 
379 /**
380  * @brief test that we can verify a signature
381  *
382  * Unlike X.509 certificates, we only support RSA keys
383  * so we stop after first successful signature verification
384  * (which should also be the first attempt ;-)
385  */
386 int
387 openpgp_self_tests(void)
388 {
389 	static int rc = -1;		/* remember result */
390 #ifdef HAVE_VC_ASC
391 	const char **vp, **tp;
392 	char *fdata, *sdata = NULL;
393 	size_t fbytes, sbytes;
394 
395 	if (openpgp_trust_init() > 0) {
396 		for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) {
397 			if ((fdata = strdup(*tp)) &&
398 			    (sdata = strdup(*vp))) {
399 				fbytes = strlen(fdata);
400 				sbytes = strlen(sdata);
401 				rc = openpgp_verify("ta_ASC",
402 				    (unsigned char *)fdata, fbytes,
403 				    (unsigned char *)sdata, sbytes, 0);
404 				printf("Testing verify OpenPGP signature:\t\t%s\n",
405 				    rc ? "Failed" : "Passed");
406 			}
407 			free(fdata);
408 			free(sdata);
409 		}
410 	}
411 #endif
412 	return (rc);
413 }
414