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