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 #else /* OPENSSL_IS_BORINGSSL */
225
new_mem_buf(const void * buf,size_t size)226 static BIO *new_mem_buf(const void *buf, size_t size)
227 {
228 BIO *bio;
229
230 if (WARN_ON(size > INT_MAX))
231 return NULL;
232
233 /*
234 * Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
235 * despite still marking the resulting bio as read-only. So cast away
236 * the const to avoid a compiler warning with older OpenSSL versions.
237 */
238 bio = BIO_new_mem_buf((void *)buf, size);
239 if (!bio)
240 error_msg_openssl("out of memory");
241 return bio;
242 }
243
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)244 static int sign_pkcs7(const void *data_to_sign, size_t data_size,
245 EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
246 u8 **sig_ret, size_t *sig_size_ret)
247 {
248 /*
249 * PKCS#7 signing flags:
250 *
251 * - PKCS7_BINARY signing binary data, so skip MIME translation
252 *
253 * - PKCS7_DETACHED omit the signed data (include signature only)
254 *
255 * - PKCS7_NOATTR omit extra authenticated attributes, such as
256 * SMIMECapabilities
257 *
258 * - PKCS7_NOCERTS omit the signer's certificate
259 *
260 * - PKCS7_PARTIAL PKCS7_sign() creates a handle only, then
261 * PKCS7_sign_add_signer() can add a signer later.
262 * This is necessary to change the message digest
263 * algorithm from the default of SHA-1. Requires
264 * OpenSSL 1.0.0 or later.
265 */
266 int pkcs7_flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_NOATTR |
267 PKCS7_NOCERTS | PKCS7_PARTIAL;
268 u8 *sig;
269 u32 sig_size;
270 BIO *bio = NULL;
271 PKCS7 *p7 = NULL;
272 int err;
273
274 bio = new_mem_buf(data_to_sign, data_size);
275 if (!bio) {
276 err = -ENOMEM;
277 goto out;
278 }
279
280 p7 = PKCS7_sign(NULL, NULL, NULL, bio, pkcs7_flags);
281 if (!p7) {
282 error_msg_openssl("failed to initialize PKCS#7 signature object");
283 err = -EINVAL;
284 goto out;
285 }
286
287 if (!PKCS7_sign_add_signer(p7, cert, pkey, md, pkcs7_flags)) {
288 error_msg_openssl("failed to add signer to PKCS#7 signature object");
289 err = -EINVAL;
290 goto out;
291 }
292
293 if (PKCS7_final(p7, bio, pkcs7_flags) != 1) {
294 error_msg_openssl("failed to finalize PKCS#7 signature");
295 err = -EINVAL;
296 goto out;
297 }
298
299 BIO_free(bio);
300 bio = BIO_new(BIO_s_mem());
301 if (!bio) {
302 error_msg_openssl("out of memory");
303 err = -ENOMEM;
304 goto out;
305 }
306
307 if (i2d_PKCS7_bio(bio, p7) != 1) {
308 error_msg_openssl("failed to DER-encode PKCS#7 signature object");
309 err = -EINVAL;
310 goto out;
311 }
312
313 sig_size = BIO_get_mem_data(bio, &sig);
314 *sig_ret = libfsverity_memdup(sig, sig_size);
315 if (!*sig_ret) {
316 err = -ENOMEM;
317 goto out;
318 }
319 *sig_size_ret = sig_size;
320 err = 0;
321 out:
322 PKCS7_free(p7);
323 BIO_free(bio);
324 return err;
325 }
326
327 #endif /* !OPENSSL_IS_BORINGSSL */
328
329 #ifdef OPENSSL_NO_ENGINE
330 static int
load_pkcs11_private_key(const struct libfsverity_signature_params * sig_params,EVP_PKEY ** pkey_ret)331 load_pkcs11_private_key(const struct libfsverity_signature_params *sig_params
332 __attribute__((unused)),
333 EVP_PKEY **pkey_ret __attribute__((unused)))
334 {
335 libfsverity_error_msg("libfsverity was linked to a version of OpenSSL that doesn't support PKCS#11 tokens");
336 return -EINVAL;
337 }
338 #else
339 static int
load_pkcs11_private_key(const struct libfsverity_signature_params * sig_params,EVP_PKEY ** pkey_ret)340 load_pkcs11_private_key(const struct libfsverity_signature_params *sig_params,
341 EVP_PKEY **pkey_ret)
342 {
343 ENGINE *engine;
344
345 if (!sig_params->pkcs11_engine) {
346 libfsverity_error_msg("no PKCS#11 engine specified");
347 return -EINVAL;
348 }
349 if (!sig_params->pkcs11_module) {
350 libfsverity_error_msg("no PKCS#11 module specified");
351 return -EINVAL;
352 }
353 ENGINE_load_dynamic();
354 engine = ENGINE_by_id("dynamic");
355 if (!engine) {
356 error_msg_openssl("failed to initialize OpenSSL PKCS#11 engine");
357 return -EINVAL;
358 }
359 if (!ENGINE_ctrl_cmd_string(engine, "SO_PATH",
360 sig_params->pkcs11_engine, 0) ||
361 !ENGINE_ctrl_cmd_string(engine, "ID", "pkcs11", 0) ||
362 !ENGINE_ctrl_cmd_string(engine, "LIST_ADD", "1", 0) ||
363 !ENGINE_ctrl_cmd_string(engine, "LOAD", NULL, 0) ||
364 !ENGINE_ctrl_cmd_string(engine, "MODULE_PATH",
365 sig_params->pkcs11_module, 0) ||
366 !ENGINE_init(engine)) {
367 error_msg_openssl("failed to initialize OpenSSL PKCS#11 engine");
368 ENGINE_free(engine);
369 return -EINVAL;
370 }
371 *pkey_ret = ENGINE_load_private_key(engine, sig_params->pkcs11_keyid,
372 NULL, NULL);
373 ENGINE_finish(engine);
374 ENGINE_free(engine);
375 if (!*pkey_ret) {
376 error_msg_openssl("failed to load private key from PKCS#11 token");
377 return -EINVAL;
378 }
379 return 0;
380 }
381 #endif
382
383 /* Get a private key, either from disk or from a PKCS#11 token. */
384 static int
get_private_key(const struct libfsverity_signature_params * sig_params,EVP_PKEY ** pkey_ret)385 get_private_key(const struct libfsverity_signature_params *sig_params,
386 EVP_PKEY **pkey_ret)
387 {
388 if (sig_params->pkcs11_engine || sig_params->pkcs11_module ||
389 sig_params->pkcs11_keyid) {
390 if (sig_params->keyfile) {
391 libfsverity_error_msg("private key must be specified either by file or by PKCS#11 token, not both");
392 return -EINVAL;
393 }
394 return load_pkcs11_private_key(sig_params, pkey_ret);
395 }
396 if (!sig_params->keyfile) {
397 libfsverity_error_msg("no private key specified");
398 return -EINVAL;
399 }
400 return read_private_key(sig_params->keyfile, pkey_ret);
401 }
402
403 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)404 libfsverity_sign_digest(const struct libfsverity_digest *digest,
405 const struct libfsverity_signature_params *sig_params,
406 u8 **sig_ret, size_t *sig_size_ret)
407 {
408 const struct fsverity_hash_alg *hash_alg;
409 X509 *cert = NULL;
410 EVP_PKEY *pkey = NULL;
411 const EVP_MD *md;
412 struct fsverity_formatted_digest *d = NULL;
413 int err;
414
415 if (!digest || !sig_params || !sig_ret || !sig_size_ret) {
416 libfsverity_error_msg("missing required parameters for sign_digest");
417 return -EINVAL;
418 }
419
420 if (!libfsverity_mem_is_zeroed(sig_params->reserved1,
421 sizeof(sig_params->reserved1)) ||
422 !libfsverity_mem_is_zeroed(sig_params->reserved2,
423 sizeof(sig_params->reserved2))) {
424 libfsverity_error_msg("reserved bits set in signature_params");
425 return -EINVAL;
426 }
427
428 hash_alg = libfsverity_find_hash_alg_by_num(digest->digest_algorithm);
429 if (!hash_alg || digest->digest_size != hash_alg->digest_size) {
430 libfsverity_error_msg("malformed fsverity digest");
431 return -EINVAL;
432 }
433
434 err = read_certificate(sig_params->certfile, &cert);
435 if (err)
436 goto out;
437
438 err = get_private_key(sig_params, &pkey);
439 if (err)
440 goto out;
441
442 OpenSSL_add_all_digests();
443 md = EVP_get_digestbyname(hash_alg->name);
444 if (!md) {
445 libfsverity_error_msg("'%s' algorithm not found in OpenSSL library",
446 hash_alg->name);
447 err = -ENOPKG;
448 goto out;
449 }
450
451 d = libfsverity_zalloc(sizeof(*d) + digest->digest_size);
452 if (!d) {
453 err = -ENOMEM;
454 goto out;
455 }
456 memcpy(d->magic, "FSVerity", 8);
457 d->digest_algorithm = cpu_to_le16(digest->digest_algorithm);
458 d->digest_size = cpu_to_le16(digest->digest_size);
459 memcpy(d->digest, digest->digest, digest->digest_size);
460
461 err = sign_pkcs7(d, sizeof(*d) + digest->digest_size,
462 pkey, cert, md, sig_ret, sig_size_ret);
463 out:
464 X509_free(cert);
465 EVP_PKEY_free(pkey);
466 free(d);
467 return err;
468 }
469