aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Langley <agl@chromium.org>2018-08-07 10:32:19 -0700
committerEric Biggers <ebiggers@google.com>2018-09-05 21:32:22 -0700
commitbdebc45b4527d64109723ad5753fa514bac47c9f (patch)
treeb56d7aa6e1cf469e6bf887f2fddae1b807120a3f
parent0aa29563ad4b6668edac56414877d204397c36a4 (diff)
downloadfsverity-utils-bdebc45b4527d64109723ad5753fa514bac47c9f.tar.gz
Add support for BoringSSL.
BoringSSL (mostly) does not support PKCS#7. Thus lay down / parse the PKCS#7 directly when building with it. Signed-off-by: Adam Langley <agl@chromium.org> (EB: coding style tweaks) Signed-off-by: Eric Biggers <ebiggers@google.com>
-rw-r--r--sign.c320
1 files changed, 243 insertions, 77 deletions
diff --git a/sign.c b/sign.c
index d7bf078..20fecfa 100644
--- a/sign.c
+++ b/sign.c
@@ -36,22 +36,6 @@ error_msg_openssl(const char *format, ...)
ERR_print_errors_fp(stderr);
}
-static BIO *new_mem_buf(const void *buf, size_t size)
-{
- BIO *bio;
-
- ASSERT(size <= INT_MAX);
- /*
- * Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
- * despite still marking the resulting bio as read-only. So cast away
- * the const to avoid a compiler warning with older OpenSSL versions.
- */
- bio = BIO_new_mem_buf((void *)buf, size);
- if (!bio)
- error_msg_openssl("out of memory");
- return bio;
-}
-
/* Read a PEM PKCS#8 formatted private key */
static EVP_PKEY *read_private_key(const char *keyfile)
{
@@ -123,22 +107,166 @@ compare_fsverity_digest(const void *data, size_t size,
return NULL;
}
-/*
- * Sign the specified @data_to_sign of length @data_size bytes using the private
- * key in @keyfile, the certificate in @certfile, and the hash algorithm
- * @hash_alg. Returns the DER-formatted PKCS#7 signature, with the signed data
- * included (not detached), in @sig_ret and @sig_size_ret.
- */
-static bool sign_data(const void *data_to_sign, size_t data_size,
- const char *keyfile, const char *certfile,
- const struct fsverity_hash_alg *hash_alg,
- void **sig_ret, int *sig_size_ret)
+#ifdef OPENSSL_IS_BORINGSSL
+
+static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
+ EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
+ void **sig_ret, int *sig_size_ret)
+{
+ CBB out, outer_seq, wrapped_seq, seq, digest_algos_set, digest_algo,
+ null, content_info, issuer_and_serial, signed_data,
+ wrapped_signed_data, signer_infos, signer_info, sign_algo,
+ signature;
+ EVP_MD_CTX md_ctx;
+ u8 *name_der = NULL, *sig = NULL, *pkcs7_data = NULL;
+ size_t pkcs7_data_len, sig_len;
+ int name_der_len, sig_nid;
+ bool ok = false;
+
+ EVP_MD_CTX_init(&md_ctx);
+ BIGNUM *serial = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
+
+ if (!CBB_init(&out, 1024)) {
+ error_msg("out of memory");
+ goto out;
+ }
+
+ name_der_len = i2d_X509_NAME(X509_get_subject_name(cert), &name_der);
+ if (name_der_len < 0) {
+ error_msg_openssl("i2d_X509_NAME failed");
+ goto out;
+ }
+
+ if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey)) {
+ error_msg_openssl("EVP_DigestSignInit failed");
+ goto out;
+ }
+
+ sig_len = EVP_PKEY_size(pkey);
+ sig = xmalloc(sig_len);
+ if (!EVP_DigestSign(&md_ctx, sig, &sig_len, data_to_sign, data_size)) {
+ error_msg_openssl("EVP_DigestSign failed");
+ goto out;
+ }
+
+ sig_nid = EVP_PKEY_id(pkey);
+ /* To mirror OpenSSL behaviour, always use |NID_rsaEncryption| with RSA
+ * rather than the combined hash+pkey NID. */
+ if (sig_nid != NID_rsaEncryption) {
+ OBJ_find_sigid_by_algs(&sig_nid, EVP_MD_type(md),
+ EVP_PKEY_id(pkey));
+ }
+
+ // See https://tools.ietf.org/html/rfc2315#section-7
+ if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
+ !CBB_add_asn1(&outer_seq, &wrapped_seq, CBS_ASN1_CONTEXT_SPECIFIC |
+ CBS_ASN1_CONSTRUCTED | 0) ||
+ // See https://tools.ietf.org/html/rfc2315#section-9.1
+ !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
+ !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
+ !CBB_add_asn1(
+ &content_info, &signed_data,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+ !CBB_add_asn1(&signed_data, &wrapped_signed_data,
+ CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_bytes(&wrapped_signed_data, (const u8 *)data_to_sign,
+ data_size) ||
+ !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
+ !CBB_add_asn1(&signer_info, &issuer_and_serial,
+ CBS_ASN1_SEQUENCE) ||
+ !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
+ !BN_marshal_asn1(&issuer_and_serial, serial) ||
+ !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&digest_algo, EVP_MD_type(md)) ||
+ !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
+ !OBJ_nid2cbb(&sign_algo, sig_nid) ||
+ !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
+ !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_bytes(&signature, sig, sig_len) ||
+ !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
+ error_msg_openssl("failed to construct PKCS#7 data");
+ goto out;
+ }
+
+ *sig_ret = xmemdup(pkcs7_data, pkcs7_data_len);
+ *sig_size_ret = pkcs7_data_len;
+ ok = true;
+out:
+ BN_free(serial);
+ EVP_MD_CTX_cleanup(&md_ctx);
+ CBB_cleanup(&out);
+ free(sig);
+ OPENSSL_free(name_der);
+ OPENSSL_free(pkcs7_data);
+ return ok;
+}
+
+static const char *
+compare_fsverity_digest_pkcs7(const void *sig, size_t sig_len,
+ const u8 *expected_measurement,
+ const struct fsverity_hash_alg *hash_alg)
+{
+ CBS in, content_info, content_type, wrapped_signed_data, signed_data,
+ content, wrapped_data, data;
+ u64 version;
+
+ CBS_init(&in, sig, sig_len);
+ if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT) ||
+ (OBJ_cbs2nid(&content_type) != NID_pkcs7_signed) ||
+ !CBS_get_asn1(
+ &content_info, &wrapped_signed_data,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+ !CBS_get_asn1(&wrapped_signed_data, &signed_data,
+ CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1_uint64(&signed_data, &version) ||
+ (version < 1) ||
+ !CBS_get_asn1(&signed_data, NULL /* digests */, CBS_ASN1_SET) ||
+ !CBS_get_asn1(&signed_data, &content, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1(&content, &content_type, CBS_ASN1_OBJECT) ||
+ (OBJ_cbs2nid(&content_type) != NID_pkcs7_data) ||
+ !CBS_get_asn1(&content, &wrapped_data, CBS_ASN1_CONTEXT_SPECIFIC |
+ CBS_ASN1_CONSTRUCTED | 0) ||
+ !CBS_get_asn1(&wrapped_data, &data, CBS_ASN1_OCTETSTRING)) {
+ return "invalid PKCS#7 data";
+ }
+
+ return compare_fsverity_digest(CBS_data(&data), CBS_len(&data),
+ expected_measurement, hash_alg);
+}
+
+#else /* OPENSSL_IS_BORINGSSL */
+
+static BIO *new_mem_buf(const void *buf, size_t size)
+{
+ BIO *bio;
+
+ ASSERT(size <= INT_MAX);
+ /*
+ * Prior to OpenSSL 1.1.0, BIO_new_mem_buf() took a non-const pointer,
+ * despite still marking the resulting bio as read-only. So cast away
+ * the const to avoid a compiler warning with older OpenSSL versions.
+ */
+ bio = BIO_new_mem_buf((void *)buf, size);
+ if (!bio)
+ error_msg_openssl("out of memory");
+ return bio;
+}
+
+static bool sign_pkcs7(const void *data_to_sign, size_t data_size,
+ EVP_PKEY *pkey, X509 *cert, const EVP_MD *md,
+ void **sig_ret, int *sig_size_ret)
{
- EVP_PKEY *pkey = NULL;
- X509 *cert = NULL;
- BIO *bio = NULL;
- PKCS7 *p7 = NULL;
- const EVP_MD *md;
/*
* PKCS#7 signing flags:
*
@@ -159,27 +287,10 @@ static bool sign_data(const void *data_to_sign, size_t data_size,
PKCS7_PARTIAL;
void *sig;
int sig_size;
+ BIO *bio = NULL;
+ PKCS7 *p7 = NULL;
bool ok = false;
- pkey = read_private_key(keyfile);
- if (!pkey)
- goto out;
-
- cert = read_certificate(certfile);
- if (!cert)
- goto out;
-
- OpenSSL_add_all_digests();
- ASSERT(hash_alg->cryptographic);
- md = EVP_get_digestbyname(hash_alg->name);
- if (!md) {
- fprintf(stderr,
- "Warning: '%s' algorithm not found in OpenSSL library.\n"
- " Falling back to SHA-256 signature.\n",
- hash_alg->name);
- md = EVP_sha256();
- }
-
bio = new_mem_buf(data_to_sign, data_size);
if (!bio)
goto out;
@@ -217,13 +328,91 @@ static bool sign_data(const void *data_to_sign, size_t data_size,
*sig_size_ret = sig_size;
ok = true;
out:
- EVP_PKEY_free(pkey);
- X509_free(cert);
PKCS7_free(p7);
BIO_free(bio);
return ok;
}
+static const char *
+compare_fsverity_digest_pkcs7(const void *sig, size_t sig_len,
+ const u8 *expected_measurement,
+ const struct fsverity_hash_alg *hash_alg)
+{
+ BIO *bio = NULL;
+ PKCS7 *p7 = NULL;
+ const char *reason = NULL;
+
+ bio = new_mem_buf(sig, sig_len);
+ if (!bio)
+ return "out of memory";
+
+ p7 = d2i_PKCS7_bio(bio, NULL);
+ if (!p7) {
+ reason = "failed to decode PKCS#7 signature";
+ goto out;
+ }
+
+ if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed ||
+ OBJ_obj2nid(p7->d.sign->contents->type) != NID_pkcs7_data) {
+ reason = "unexpected PKCS#7 content type";
+ } else {
+ const ASN1_OCTET_STRING *o = p7->d.sign->contents->d.data;
+
+ reason = compare_fsverity_digest(o->data, o->length,
+ expected_measurement,
+ hash_alg);
+ }
+out:
+ BIO_free(bio);
+ PKCS7_free(p7);
+ return reason;
+}
+
+#endif /* !OPENSSL_IS_BORINGSSL */
+
+/*
+ * Sign the specified @data_to_sign of length @data_size bytes using the private
+ * key in @keyfile, the certificate in @certfile, and the hash algorithm
+ * @hash_alg. Returns the DER-formatted PKCS#7 signature, with the signed data
+ * included (not detached), in @sig_ret and @sig_size_ret.
+ */
+static bool sign_data(const void *data_to_sign, size_t data_size,
+ const char *keyfile, const char *certfile,
+ const struct fsverity_hash_alg *hash_alg,
+ void **sig_ret, int *sig_size_ret)
+{
+ EVP_PKEY *pkey = NULL;
+ X509 *cert = NULL;
+ const EVP_MD *md;
+ bool ok = false;
+
+ pkey = read_private_key(keyfile);
+ if (!pkey)
+ goto out;
+
+ cert = read_certificate(certfile);
+ if (!cert)
+ goto out;
+
+ OpenSSL_add_all_digests();
+ ASSERT(hash_alg->cryptographic);
+ md = EVP_get_digestbyname(hash_alg->name);
+ if (!md) {
+ fprintf(stderr,
+ "Warning: '%s' algorithm not found in OpenSSL library.\n"
+ " Falling back to SHA-256 signature.\n",
+ hash_alg->name);
+ md = EVP_sha256();
+ }
+
+ ok = sign_pkcs7(data_to_sign, data_size, pkey, cert, md,
+ sig_ret, sig_size_ret);
+out:
+ EVP_PKEY_free(pkey);
+ X509_free(cert);
+ return ok;
+}
+
/*
* Read a file measurement signature in PKCS#7 DER format from @signature_file,
* validate that the signed data matches the expected measurement, then return
@@ -237,8 +426,6 @@ static bool read_signature(const char *signature_file,
struct filedes file = { .fd = -1 };
u64 filesize;
void *sig = NULL;
- BIO *bio = NULL;
- PKCS7 *p7 = NULL;
bool ok = false;
const char *reason;
@@ -258,27 +445,8 @@ static bool read_signature(const char *signature_file,
if (!full_read(&file, sig, filesize))
goto out;
- bio = new_mem_buf(sig, filesize);
- if (!bio)
- goto out;
-
- p7 = d2i_PKCS7_bio(bio, NULL);
- if (!p7) {
- error_msg_openssl("failed to decode PKCS#7 signature from '%s'",
- signature_file);
- goto out;
- }
-
- if (OBJ_obj2nid(p7->type) != NID_pkcs7_signed ||
- OBJ_obj2nid(p7->d.sign->contents->type) != NID_pkcs7_data) {
- reason = "unexpected PKCS#7 content type";
- } else {
- const ASN1_OCTET_STRING *o = p7->d.sign->contents->d.data;
-
- reason = compare_fsverity_digest(o->data, o->length,
- expected_measurement,
- hash_alg);
- }
+ reason = compare_fsverity_digest_pkcs7(sig, filesize,
+ expected_measurement, hash_alg);
if (reason) {
error_msg("signed file measurement from '%s' is invalid (%s)",
signature_file, reason);
@@ -294,8 +462,6 @@ static bool read_signature(const char *signature_file,
out:
filedes_close(&file);
free(sig);
- BIO_free(bio);
- PKCS7_free(p7);
return ok;
}