• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: MIT
2 /*
3  * Implementation of libfsverity_sign_digest().
4  *
5  * Copyright 2018 Google LLC
6  * Copyright (C) 2020 Facebook
7  *
8  * Use of this source code is governed by an MIT-style
9  * license that can be found in the LICENSE file or at
10  * https://opensource.org/licenses/MIT.
11  */
12 
13 #include "lib_private.h"
14 
15 #include <limits.h>
16 #include <openssl/bio.h>
17 #include <openssl/err.h>
18 #include <openssl/pem.h>
19 #include <openssl/pkcs7.h>
20 #include <string.h>
21 
22 #ifndef OPENSSL_IS_BORINGSSL
23 #include <openssl/engine.h>
24 #endif
25 
print_openssl_err_cb(const char * str,size_t len,void * u)26 static int print_openssl_err_cb(const char *str,
27 				size_t len __attribute__((unused)),
28 				void *u __attribute__((unused)))
29 {
30 	libfsverity_error_msg("%s", str);
31 	return 1;
32 }
33 
34 static void __printf(1, 2) __cold
error_msg_openssl(const char * format,...)35 error_msg_openssl(const char *format, ...)
36 {
37 	int saved_errno = errno;
38 	va_list va;
39 
40 	va_start(va, format);
41 	libfsverity_do_error_msg(format, va);
42 	va_end(va);
43 
44 	if (ERR_peek_error() == 0)
45 		return;
46 
47 	libfsverity_error_msg("OpenSSL library errors:");
48 	ERR_print_errors_cb(print_openssl_err_cb, NULL);
49 	errno = saved_errno;
50 }
51 
52 /* Read a PEM PKCS#8 formatted private key */
read_private_key(const char * keyfile,EVP_PKEY ** pkey_ret)53 static int read_private_key(const char *keyfile, EVP_PKEY **pkey_ret)
54 {
55 	BIO *bio;
56 	EVP_PKEY *pkey;
57 	int err;
58 
59 	errno = 0;
60 	bio = BIO_new_file(keyfile, "r");
61 	if (!bio) {
62 		error_msg_openssl("can't open '%s' for reading", keyfile);
63 		return errno ? -errno : -EIO;
64 	}
65 
66 	pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
67 	if (!pkey) {
68 		error_msg_openssl("Failed to parse private key file '%s'.\n"
69 				  "       Note: it must be in PEM PKCS#8 format.",
70 				  keyfile);
71 		err = -EBADMSG;
72 		goto out;
73 	}
74 	*pkey_ret = pkey;
75 	err = 0;
76 out:
77 	BIO_free(bio);
78 	return err;
79 }
80 
81 /* Read a PEM X.509 formatted certificate */
read_certificate(const char * certfile,X509 ** cert_ret)82 static int read_certificate(const char *certfile, X509 **cert_ret)
83 {
84 	BIO *bio;
85 	X509 *cert;
86 	int err;
87 
88 	if (!certfile) {
89 		libfsverity_error_msg("no certificate specified");
90 		return -EINVAL;
91 	}
92 
93 	errno = 0;
94 	bio = BIO_new_file(certfile, "r");
95 	if (!bio) {
96 		error_msg_openssl("can't open '%s' for reading", certfile);
97 		return errno ? -errno : -EIO;
98 	}
99 	cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
100 	if (!cert) {
101 		error_msg_openssl("Failed to parse X.509 certificate file '%s'.\n"
102 				  "       Note: it must be in PEM format.",
103 				  certfile);
104 		err = -EBADMSG;
105 		goto out;
106 	}
107 	*cert_ret = cert;
108 	err = 0;
109 out:
110 	BIO_free(bio);
111 	return err;
112 }
113 
114 #ifdef OPENSSL_IS_BORINGSSL
115 
sign_pkcs7(const void * data_to_sign,size_t data_size,EVP_PKEY * pkey,X509 * cert,const EVP_MD * md,u8 ** sig_ret,size_t * sig_size_ret)116 static int sign_pkcs7(const void *data_to_sign, size_t data_size,
117 		      EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
118 		      u8 **sig_ret, size_t *sig_size_ret)
119 {
120 	BIGNUM *serial;
121 	CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo,
122 		null, content_info, issuer_and_serial, signer_infos,
123 		signer_info, sign_algo, signature;
124 	EVP_MD_CTX md_ctx;
125 	u8 *name_der = NULL, *sig = NULL, *pkcs7_data = NULL;
126 	size_t pkcs7_data_len, sig_len;
127 	int name_der_len, sig_nid;
128 	int err;
129 
130 	EVP_MD_CTX_init(&md_ctx);
131 	serial = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
132 
133 	if (!CBB_init(&out, 1024)) {
134 		error_msg_openssl("out of memory");
135 		err = -ENOMEM;
136 		goto out;
137 	}
138 
139 	name_der_len = i2d_X509_NAME(X509_get_subject_name(cert), &name_der);
140 	if (name_der_len < 0) {
141 		error_msg_openssl("i2d_X509_NAME failed");
142 		err = -EINVAL;
143 		goto out;
144 	}
145 
146 	if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey)) {
147 		error_msg_openssl("EVP_DigestSignInit failed");
148 		err = -EINVAL;
149 		goto out;
150 	}
151 
152 	sig_len = EVP_PKEY_size(pkey);
153 	sig = libfsverity_zalloc(sig_len);
154 	if (!sig) {
155 		err = -ENOMEM;
156 		goto out;
157 	}
158 	if (!EVP_DigestSign(&md_ctx, sig, &sig_len, data_to_sign, data_size)) {
159 		error_msg_openssl("EVP_DigestSign failed");
160 		err = -EINVAL;
161 		goto out;
162 	}
163 
164 	sig_nid = EVP_PKEY_id(pkey);
165 	/* To mirror OpenSSL behaviour, always use |NID_rsaEncryption| with RSA
166 	 * rather than the combined hash+pkey NID. */
167 	if (sig_nid != NID_rsaEncryption) {
168 		OBJ_find_sigid_by_algs(&sig_nid, EVP_MD_type(md),
169 				       EVP_PKEY_id(pkey));
170 	}
171 
172 	// See https://tools.ietf.org/html/rfc2315#section-7
173 	if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
174 	    !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
175 	    !CBB_add_asn1(&outer_seq, &wrapped_seq, CBS_ASN1_CONTEXT_SPECIFIC |
176 			  CBS_ASN1_CONSTRUCTED | 0) ||
177 	    // See https://tools.ietf.org/html/rfc2315#section-9.1
178 	    !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
179 	    !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
180 	    !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
181 	    !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
182 	    !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
183 	    !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
184 	    !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
185 	    !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
186 	    !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
187 	    !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
188 	    !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
189 	    !CBB_add_asn1(&signer_info, &issuer_and_serial,
190 			  CBS_ASN1_SEQUENCE) ||
191 	    !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
192 	    !BN_marshal_asn1(&issuer_and_serial, serial) ||
193 	    !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
194 	    !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
195 	    !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
196 	    !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
197 	    !OBJ_nid2cbb(&sign_algo, sig_nid) ||
198 	    !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
199 	    !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
200 	    !CBB_add_bytes(&signature, sig, sig_len) ||
201 	    !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
202 		error_msg_openssl("failed to construct PKCS#7 data");
203 		err = -EINVAL;
204 		goto out;
205 	}
206 
207 	*sig_ret = libfsverity_memdup(pkcs7_data, pkcs7_data_len);
208 	if (!*sig_ret) {
209 		err = -ENOMEM;
210 		goto out;
211 	}
212 	*sig_size_ret = pkcs7_data_len;
213 	err = 0;
214 out:
215 	BN_free(serial);
216 	EVP_MD_CTX_cleanup(&md_ctx);
217 	CBB_cleanup(&out);
218 	free(sig);
219 	OPENSSL_free(name_der);
220 	OPENSSL_free(pkcs7_data);
221 	return err;
222 }
223 
224 static int
load_pkcs11_private_key(const struct libfsverity_signature_params * sig_params,EVP_PKEY ** pkey_ret)225 load_pkcs11_private_key(const struct libfsverity_signature_params *sig_params
226 			__attribute__((unused)),
227 			EVP_PKEY **pkey_ret __attribute__((unused)))
228 {
229 	libfsverity_error_msg("BoringSSL doesn't support PKCS#11 tokens");
230 	return -EINVAL;
231 }
232 
233 #else /* OPENSSL_IS_BORINGSSL */
234 
new_mem_buf(const void * buf,size_t size)235 static BIO *new_mem_buf(const void *buf, size_t size)
236 {
237 	BIO *bio;
238 
239 	if (WARN_ON(size > INT_MAX))
240 		return NULL;
241 
242 	/*
243 	 * Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
244 	 * despite still marking the resulting bio as read-only.  So cast away
245 	 * the const to avoid a compiler warning with older OpenSSL versions.
246 	 */
247 	bio = BIO_new_mem_buf((void *)buf, size);
248 	if (!bio)
249 		error_msg_openssl("out of memory");
250 	return bio;
251 }
252 
sign_pkcs7(const void * data_to_sign,size_t data_size,EVP_PKEY * pkey,X509 * cert,const EVP_MD * md,u8 ** sig_ret,size_t * sig_size_ret)253 static int sign_pkcs7(const void *data_to_sign, size_t data_size,
254 		      EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
255 		      u8 **sig_ret, size_t *sig_size_ret)
256 {
257 	/*
258 	 * PKCS#7 signing flags:
259 	 *
260 	 * - PKCS7_BINARY	signing binary data, so skip MIME translation
261 	 *
262 	 * - PKCS7_DETACHED	omit the signed data (include signature only)
263 	 *
264 	 * - PKCS7_NOATTR	omit extra authenticated attributes, such as
265 	 *			SMIMECapabilities
266 	 *
267 	 * - PKCS7_NOCERTS	omit the signer's certificate
268 	 *
269 	 * - PKCS7_PARTIAL	PKCS7_sign() creates a handle only, then
270 	 *			PKCS7_sign_add_signer() can add a signer later.
271 	 *			This is necessary to change the message digest
272 	 *			algorithm from the default of SHA-1.  Requires
273 	 *			OpenSSL 1.0.0 or later.
274 	 */
275 	int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR |
276 			  PKCS7_NOCERTS | PKCS7_PARTIAL;
277 	u8 *sig;
278 	u32 sig_size;
279 	BIO *bio = NULL;
280 	PKCS7 *p7 = NULL;
281 	int err;
282 
283 	bio = new_mem_buf(data_to_sign, data_size);
284 	if (!bio) {
285 		err = -ENOMEM;
286 		goto out;
287 	}
288 
289 	p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
290 	if (!p7) {
291 		error_msg_openssl("failed to initialize PKCS#7 signature object");
292 		err = -EINVAL;
293 		goto out;
294 	}
295 
296 	if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
297 		error_msg_openssl("failed to add signer to PKCS#7 signature object");
298 		err = -EINVAL;
299 		goto out;
300 	}
301 
302 	if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
303 		error_msg_openssl("failed to finalize PKCS#7 signature");
304 		err = -EINVAL;
305 		goto out;
306 	}
307 
308 	BIO_free(bio);
309 	bio = BIO_new(BIO_s_mem());
310 	if (!bio) {
311 		error_msg_openssl("out of memory");
312 		err = -ENOMEM;
313 		goto out;
314 	}
315 
316 	if (i2d_PKCS7_bio(bio, p7) != 1) {
317 		error_msg_openssl("failed to DER-encode PKCS#7 signature object");
318 		err = -EINVAL;
319 		goto out;
320 	}
321 
322 	sig_size = BIO_get_mem_data(bio, &sig);
323 	*sig_ret = libfsverity_memdup(sig, sig_size);
324 	if (!*sig_ret) {
325 		err = -ENOMEM;
326 		goto out;
327 	}
328 	*sig_size_ret = sig_size;
329 	err = 0;
330 out:
331 	PKCS7_free(p7);
332 	BIO_free(bio);
333 	return err;
334 }
335 
336 static int
load_pkcs11_private_key(const struct libfsverity_signature_params * sig_params,EVP_PKEY ** pkey_ret)337 load_pkcs11_private_key(const struct libfsverity_signature_params *sig_params,
338 			EVP_PKEY **pkey_ret)
339 {
340 	ENGINE *engine;
341 
342 	if (!sig_params->pkcs11_engine) {
343 		libfsverity_error_msg("no PKCS#11 engine specified");
344 		return -EINVAL;
345 	}
346 	if (!sig_params->pkcs11_module) {
347 		libfsverity_error_msg("no PKCS#11 module specified");
348 		return -EINVAL;
349 	}
350 	ENGINE_load_dynamic();
351 	engine = ENGINE_by_id("dynamic");
352 	if (!engine) {
353 		error_msg_openssl("failed to initialize OpenSSL PKCS#11 engine");
354 		return -EINVAL;
355 	}
356 	if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH",
357 				    sig_params->pkcs11_engine, 0) ||
358 	    !ENGINE_ctrl_cmd_string(engine, "ID", "pkcs11", 0) ||
359 	    !ENGINE_ctrl_cmd_string(engine, "LIST_ADD", "1", 0) ||
360 	    !ENGINE_ctrl_cmd_string(engine, "LOAD", NULL, 0) ||
361 	    !ENGINE_ctrl_cmd_string(engine, "MODULE_PATH",
362 				    sig_params->pkcs11_module, 0) ||
363 	    !ENGINE_init(engine)) {
364 		error_msg_openssl("failed to initialize OpenSSL PKCS#11 engine");
365 		ENGINE_free(engine);
366 		return -EINVAL;
367 	}
368 	*pkey_ret = ENGINE_load_private_key(engine, sig_params->pkcs11_keyid,
369 					    NULL, NULL);
370 	ENGINE_finish(engine);
371 	ENGINE_free(engine);
372 	if (!*pkey_ret) {
373 		error_msg_openssl("failed to load private key from PKCS#11 token");
374 		return -EINVAL;
375 	}
376 	return 0;
377 }
378 
379 #endif /* !OPENSSL_IS_BORINGSSL */
380 
381 /* Get a private key, either from disk or from a PKCS#11 token. */
382 static int
get_private_key(const struct libfsverity_signature_params * sig_params,EVP_PKEY ** pkey_ret)383 get_private_key(const struct libfsverity_signature_params *sig_params,
384 		EVP_PKEY **pkey_ret)
385 {
386 	if (sig_params->pkcs11_engine || sig_params->pkcs11_module ||
387 	    sig_params->pkcs11_keyid) {
388 		if (sig_params->keyfile) {
389 			libfsverity_error_msg("private key must be specified either by file or by PKCS#11 token, not both");
390 			return -EINVAL;
391 		}
392 		return load_pkcs11_private_key(sig_params, pkey_ret);
393 	}
394 	if (!sig_params->keyfile) {
395 		libfsverity_error_msg("no private key specified");
396 		return -EINVAL;
397 	}
398 	return read_private_key(sig_params->keyfile, pkey_ret);
399 }
400 
401 LIBEXPORT int
libfsverity_sign_digest(const struct libfsverity_digest * digest,const struct libfsverity_signature_params * sig_params,u8 ** sig_ret,size_t * sig_size_ret)402 libfsverity_sign_digest(const struct libfsverity_digest *digest,
403 			const struct libfsverity_signature_params *sig_params,
404 			u8 **sig_ret, size_t *sig_size_ret)
405 {
406 	const struct fsverity_hash_alg *hash_alg;
407 	X509 *cert = NULL;
408 	EVP_PKEY *pkey = NULL;
409 	const EVP_MD *md;
410 	struct fsverity_formatted_digest *d = NULL;
411 	int err;
412 
413 	if (!digest || !sig_params || !sig_ret || !sig_size_ret)  {
414 		libfsverity_error_msg("missing required parameters for sign_digest");
415 		return -EINVAL;
416 	}
417 
418 	if (!libfsverity_mem_is_zeroed(sig_params->reserved1,
419 				       sizeof(sig_params->reserved1)) ||
420 	    !libfsverity_mem_is_zeroed(sig_params->reserved2,
421 				       sizeof(sig_params->reserved2))) {
422 		libfsverity_error_msg("reserved bits set in signature_params");
423 		return -EINVAL;
424 	}
425 
426 	hash_alg = libfsverity_find_hash_alg_by_num(digest->digest_algorithm);
427 	if (!hash_alg || digest->digest_size != hash_alg->digest_size) {
428 		libfsverity_error_msg("malformed fsverity digest");
429 		return -EINVAL;
430 	}
431 
432 	err = read_certificate(sig_params->certfile, &cert);
433 	if (err)
434 		goto out;
435 
436 	err = get_private_key(sig_params, &pkey);
437 	if (err)
438 		goto out;
439 
440 	OpenSSL_add_all_digests();
441 	md = EVP_get_digestbyname(hash_alg->name);
442 	if (!md) {
443 		libfsverity_error_msg("'%s' algorithm not found in OpenSSL library",
444 				      hash_alg->name);
445 		err = -ENOPKG;
446 		goto out;
447 	}
448 
449 	d = libfsverity_zalloc(sizeof(*d) + digest->digest_size);
450 	if (!d) {
451 		err = -ENOMEM;
452 		goto out;
453 	}
454 	memcpy(d->magic, "FSVerity", 8);
455 	d->digest_algorithm = cpu_to_le16(digest->digest_algorithm);
456 	d->digest_size = cpu_to_le16(digest->digest_size);
457 	memcpy(d->digest, digest->digest, digest->digest_size);
458 
459 	err = sign_pkcs7(d, sizeof(*d) + digest->digest_size,
460 			 pkey, cert, md, sig_ret, sig_size_ret);
461  out:
462 	X509_free(cert);
463 	EVP_PKEY_free(pkey);
464 	free(d);
465 	return err;
466 }
467