diff options
author | Adam Vartanian <flooey@google.com> | 2018-08-10 14:55:24 +0100 |
---|---|---|
committer | Adam Vartanian <flooey@google.com> | 2018-08-10 15:32:39 +0100 |
commit | bfcf3a72c0bcb62cfde80e932db0668a7f96c0f8 (patch) | |
tree | 3ab110046e45c25f8f69df137f8a9d4a52bf60b5 /src/crypto/pkcs7 | |
parent | 6120b2b9dfbe5b19fe2d0f69a4b8418ad5f7fe08 (diff) | |
download | boringssl-bfcf3a72c0bcb62cfde80e932db0668a7f96c0f8.tar.gz |
external/boringssl: Sync to 8625ec4b436ccb4098ed4aac10891eff8372be41.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/c596415ec62b501523d80f9afa26b135406da6bf..8625ec4b436ccb4098ed4aac10891eff8372be41
Test: cts -m CtsLibcoreTestCases
Change-Id: I47a45e6b6f46b19fcbcb6c917895867d56dcd2ca
Diffstat (limited to 'src/crypto/pkcs7')
-rw-r--r-- | src/crypto/pkcs7/pkcs7.c | 32 | ||||
-rw-r--r-- | src/crypto/pkcs7/pkcs7_test.cc | 79 | ||||
-rw-r--r-- | src/crypto/pkcs7/pkcs7_x509.c | 194 |
3 files changed, 267 insertions, 38 deletions
diff --git a/src/crypto/pkcs7/pkcs7.c b/src/crypto/pkcs7/pkcs7.c index fc175a94..c04bffde 100644 --- a/src/crypto/pkcs7/pkcs7.c +++ b/src/crypto/pkcs7/pkcs7.c @@ -41,23 +41,14 @@ static const uint8_t kPKCS7SignedData[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, // It returns one on success or zero on error. On error, |*der_bytes| is // NULL. int pkcs7_parse_header(uint8_t **der_bytes, CBS *out, CBS *cbs) { - size_t der_len; CBS in, content_info, content_type, wrapped_signed_data, signed_data; uint64_t version; // The input may be in BER format. *der_bytes = NULL; - if (!CBS_asn1_ber_to_der(cbs, der_bytes, &der_len)) { - return 0; - } - if (*der_bytes != NULL) { - CBS_init(&in, *der_bytes, der_len); - } else { - CBS_init(&in, CBS_data(cbs), CBS_len(cbs)); - } - - // See https://tools.ietf.org/html/rfc2315#section-7 - if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE) || + if (!CBS_asn1_ber_to_der(cbs, &in, der_bytes) || + // See https://tools.ietf.org/html/rfc2315#section-7 + !CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE) || !CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) { goto err; } @@ -96,20 +87,21 @@ int PKCS7_get_raw_certificates(STACK_OF(CRYPTO_BUFFER) *out_certs, CBS *cbs, CRYPTO_BUFFER_POOL *pool) { CBS signed_data, certificates; uint8_t *der_bytes = NULL; - int ret = 0; + int ret = 0, has_certificates; const size_t initial_certs_len = sk_CRYPTO_BUFFER_num(out_certs); - if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs)) { - return 0; - } - // See https://tools.ietf.org/html/rfc2315#section-9.1 - if (!CBS_get_asn1(&signed_data, &certificates, - CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) { - OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CERTIFICATES_INCLUDED); + if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs) || + !CBS_get_optional_asn1( + &signed_data, &certificates, &has_certificates, + CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) { goto err; } + if (!has_certificates) { + CBS_init(&certificates, NULL, 0); + } + while (CBS_len(&certificates) > 0) { CBS cert; if (!CBS_get_asn1_element(&certificates, &cert, CBS_ASN1_SEQUENCE)) { diff --git a/src/crypto/pkcs7/pkcs7_test.cc b/src/crypto/pkcs7/pkcs7_test.cc index 54f7e8aa..1ac9af2b 100644 --- a/src/crypto/pkcs7/pkcs7_test.cc +++ b/src/crypto/pkcs7/pkcs7_test.cc @@ -480,6 +480,7 @@ static void TestCertRepase(const uint8_t *der_bytes, size_t der_len) { CBS pkcs7; CBS_init(&pkcs7, der_bytes, der_len); ASSERT_TRUE(PKCS7_get_certificates(certs.get(), &pkcs7)); + EXPECT_EQ(0u, CBS_len(&pkcs7)); bssl::ScopedCBB cbb; ASSERT_TRUE(CBB_init(cbb.get(), der_len)); @@ -489,9 +490,9 @@ static void TestCertRepase(const uint8_t *der_bytes, size_t der_len) { CBS_init(&pkcs7, result_data, result_len); ASSERT_TRUE(PKCS7_get_certificates(certs2.get(), &pkcs7)); + EXPECT_EQ(0u, CBS_len(&pkcs7)); ASSERT_EQ(sk_X509_num(certs.get()), sk_X509_num(certs2.get())); - for (size_t i = 0; i < sk_X509_num(certs.get()); i++) { X509 *a = sk_X509_value(certs.get(), i); X509 *b = sk_X509_value(certs2.get(), i); @@ -504,6 +505,50 @@ static void TestCertRepase(const uint8_t *der_bytes, size_t der_len) { bssl::UniquePtr<uint8_t> free_result2_data(result2_data); EXPECT_EQ(Bytes(result_data, result_len), Bytes(result2_data, result2_len)); + + // Parse with the legacy API instead. + const uint8_t *ptr = der_bytes; + bssl::UniquePtr<PKCS7> pkcs7_obj(d2i_PKCS7(nullptr, &ptr, der_len)); + ASSERT_TRUE(pkcs7_obj); + EXPECT_EQ(ptr, der_bytes + der_len); + + ASSERT_TRUE(PKCS7_type_is_signed(pkcs7_obj.get())); + const STACK_OF(X509) *certs3 = pkcs7_obj->d.sign->cert; + ASSERT_EQ(sk_X509_num(certs.get()), sk_X509_num(certs3)); + for (size_t i = 0; i < sk_X509_num(certs.get()); i++) { + X509 *a = sk_X509_value(certs.get(), i); + X509 *b = sk_X509_value(certs3, i); + ASSERT_EQ(0, X509_cmp(a, b)); + } + + // Serialize the original object. This should echo back the original saved + // bytes. + uint8_t *result3_data = nullptr; + int result3_len = i2d_PKCS7(pkcs7_obj.get(), &result3_data); + ASSERT_GT(result3_len, 0); + bssl::UniquePtr<uint8_t> free_result3_data(result3_data); + EXPECT_EQ(Bytes(der_bytes, der_len), Bytes(result3_data, result3_len)); + + // Make a new object with the legacy API. + pkcs7_obj.reset( + PKCS7_sign(nullptr, nullptr, certs.get(), nullptr, PKCS7_DETACHED)); + ASSERT_TRUE(pkcs7_obj); + + ASSERT_TRUE(PKCS7_type_is_signed(pkcs7_obj.get())); + const STACK_OF(X509) *certs4 = pkcs7_obj->d.sign->cert; + ASSERT_EQ(sk_X509_num(certs.get()), sk_X509_num(certs4)); + for (size_t i = 0; i < sk_X509_num(certs.get()); i++) { + X509 *a = sk_X509_value(certs.get(), i); + X509 *b = sk_X509_value(certs4, i); + ASSERT_EQ(0, X509_cmp(a, b)); + } + + // This new object should serialize canonically. + uint8_t *result4_data = nullptr; + int result4_len = i2d_PKCS7(pkcs7_obj.get(), &result4_data); + ASSERT_GT(result4_len, 0); + bssl::UniquePtr<uint8_t> free_result4_data(result4_data); + EXPECT_EQ(Bytes(result_data, result_len), Bytes(result4_data, result4_len)); } static void TestCRLReparse(const uint8_t *der_bytes, size_t der_len) { @@ -517,6 +562,7 @@ static void TestCRLReparse(const uint8_t *der_bytes, size_t der_len) { CBS pkcs7; CBS_init(&pkcs7, der_bytes, der_len); ASSERT_TRUE(PKCS7_get_CRLs(crls.get(), &pkcs7)); + EXPECT_EQ(0u, CBS_len(&pkcs7)); bssl::ScopedCBB cbb; ASSERT_TRUE(CBB_init(cbb.get(), der_len)); @@ -526,9 +572,9 @@ static void TestCRLReparse(const uint8_t *der_bytes, size_t der_len) { CBS_init(&pkcs7, result_data, result_len); ASSERT_TRUE(PKCS7_get_CRLs(crls2.get(), &pkcs7)); + EXPECT_EQ(0u, CBS_len(&pkcs7)); ASSERT_EQ(sk_X509_CRL_num(crls.get()), sk_X509_CRL_num(crls.get())); - for (size_t i = 0; i < sk_X509_CRL_num(crls.get()); i++) { X509_CRL *a = sk_X509_CRL_value(crls.get(), i); X509_CRL *b = sk_X509_CRL_value(crls2.get(), i); @@ -541,6 +587,35 @@ static void TestCRLReparse(const uint8_t *der_bytes, size_t der_len) { bssl::UniquePtr<uint8_t> free_result2_data(result2_data); EXPECT_EQ(Bytes(result_data, result_len), Bytes(result2_data, result2_len)); + + // Parse with the legacy API instead. + const uint8_t *ptr = der_bytes; + bssl::UniquePtr<PKCS7> pkcs7_obj(d2i_PKCS7(nullptr, &ptr, der_len)); + ASSERT_TRUE(pkcs7_obj); + EXPECT_EQ(ptr, der_bytes + der_len); + + ASSERT_TRUE(PKCS7_type_is_signed(pkcs7_obj.get())); + const STACK_OF(X509_CRL) *crls3 = pkcs7_obj->d.sign->crl; + ASSERT_EQ(sk_X509_CRL_num(crls.get()), sk_X509_CRL_num(crls3)); + for (size_t i = 0; i < sk_X509_CRL_num(crls.get()); i++) { + X509_CRL *a = sk_X509_CRL_value(crls.get(), i); + X509_CRL *b = sk_X509_CRL_value(crls3, i); + ASSERT_EQ(0, X509_CRL_cmp(a, b)); + } + + ptr = result_data; + pkcs7_obj.reset(d2i_PKCS7(nullptr, &ptr, result_len)); + ASSERT_TRUE(pkcs7_obj); + EXPECT_EQ(ptr, result_data + result_len); + + ASSERT_TRUE(PKCS7_type_is_signed(pkcs7_obj.get())); + const STACK_OF(X509_CRL) *crls4 = pkcs7_obj->d.sign->crl; + ASSERT_EQ(sk_X509_CRL_num(crls.get()), sk_X509_CRL_num(crls4)); + for (size_t i = 0; i < sk_X509_CRL_num(crls.get()); i++) { + X509_CRL *a = sk_X509_CRL_value(crls.get(), i); + X509_CRL *b = sk_X509_CRL_value(crls4, i); + ASSERT_EQ(0, X509_CRL_cmp(a, b)); + } } static void TestPEMCerts(const char *pem) { diff --git a/src/crypto/pkcs7/pkcs7_x509.c b/src/crypto/pkcs7/pkcs7_x509.c index 7bc39d27..a2a6b46a 100644 --- a/src/crypto/pkcs7/pkcs7_x509.c +++ b/src/crypto/pkcs7/pkcs7_x509.c @@ -26,6 +26,7 @@ #include <openssl/x509.h> #include "internal.h" +#include "../internal.h" int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) { @@ -64,28 +65,24 @@ err: int PKCS7_get_CRLs(STACK_OF(X509_CRL) *out_crls, CBS *cbs) { CBS signed_data, crls; uint8_t *der_bytes = NULL; - int ret = 0; + int ret = 0, has_crls; const size_t initial_crls_len = sk_X509_CRL_num(out_crls); - if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs)) { - return 0; - } - // See https://tools.ietf.org/html/rfc2315#section-9.1 - - // Even if only CRLs are included, there may be an empty certificates block. - // OpenSSL does this, for example. - if (CBS_peek_asn1_tag(&signed_data, - CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) && - !CBS_get_asn1(&signed_data, NULL /* certificates */, - CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) { + if (!pkcs7_parse_header(&der_bytes, &signed_data, cbs) || + // Even if only CRLs are included, there may be an empty certificates + // block. OpenSSL does this, for example. + !CBS_get_optional_asn1( + &signed_data, NULL, NULL, + CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) || + !CBS_get_optional_asn1( + &signed_data, &crls, &has_crls, + CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) { goto err; } - if (!CBS_get_asn1(&signed_data, &crls, - CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1)) { - OPENSSL_PUT_ERROR(PKCS7, PKCS7_R_NO_CRLS_INCLUDED); - goto err; + if (!has_crls) { + CBS_init(&crls, NULL, 0); } while (CBS_len(&crls) > 0) { @@ -231,3 +228,168 @@ static int pkcs7_bundle_crls_cb(CBB *out, const void *arg) { int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls) { return pkcs7_bundle(out, pkcs7_bundle_crls_cb, crls); } + +static PKCS7 *pkcs7_new(CBS *cbs) { + PKCS7 *ret = OPENSSL_malloc(sizeof(PKCS7)); + if (ret == NULL) { + return NULL; + } + OPENSSL_memset(ret, 0, sizeof(PKCS7)); + ret->type = (ASN1_OBJECT *)OBJ_nid2obj(NID_pkcs7_signed); + ret->d.sign = OPENSSL_malloc(sizeof(PKCS7_SIGNED)); + if (ret->d.sign == NULL) { + goto err; + } + ret->d.sign->cert = sk_X509_new_null(); + ret->d.sign->crl = sk_X509_CRL_new_null(); + CBS copy = *cbs, copy2 = *cbs; + if (ret->d.sign->cert == NULL || ret->d.sign->crl == NULL || + !PKCS7_get_certificates(ret->d.sign->cert, ©) || + !PKCS7_get_CRLs(ret->d.sign->crl, cbs)) { + goto err; + } + + if (sk_X509_num(ret->d.sign->cert) == 0) { + sk_X509_free(ret->d.sign->cert); + ret->d.sign->cert = NULL; + } + + if (sk_X509_CRL_num(ret->d.sign->crl) == 0) { + sk_X509_CRL_free(ret->d.sign->crl); + ret->d.sign->crl = NULL; + } + + ret->ber_len = CBS_len(©2) - CBS_len(cbs); + ret->ber_bytes = BUF_memdup(CBS_data(©2), ret->ber_len); + if (ret->ber_bytes == NULL) { + goto err; + } + + return ret; + +err: + PKCS7_free(ret); + return NULL; +} + +PKCS7 *d2i_PKCS7(PKCS7 **out, const uint8_t **inp, + size_t len) { + CBS cbs; + CBS_init(&cbs, *inp, len); + PKCS7 *ret = pkcs7_new(&cbs); + if (ret == NULL) { + return NULL; + } + *inp = CBS_data(&cbs); + if (out != NULL) { + PKCS7_free(*out); + *out = ret; + } + return ret; +} + +PKCS7 *d2i_PKCS7_bio(BIO *bio, PKCS7 **out) { + // Use a generous bound, to allow for PKCS#7 files containing large root sets. + static const size_t kMaxSize = 4 * 1024 * 1024; + uint8_t *data; + size_t len; + if (!BIO_read_asn1(bio, &data, &len, kMaxSize)) { + return NULL; + } + + CBS cbs; + CBS_init(&cbs, data, len); + PKCS7 *ret = pkcs7_new(&cbs); + OPENSSL_free(data); + if (out != NULL && ret != NULL) { + PKCS7_free(*out); + *out = ret; + } + return ret; +} + +int i2d_PKCS7(const PKCS7 *p7, uint8_t **out) { + if (p7->ber_len > INT_MAX) { + OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW); + return -1; + } + + if (out == NULL) { + return (int)p7->ber_len; + } + + if (*out == NULL) { + *out = OPENSSL_malloc(p7->ber_len); + if (*out == NULL) { + OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE); + return -1; + } + OPENSSL_memcpy(*out, p7->ber_bytes, p7->ber_len); + } else { + OPENSSL_memcpy(*out, p7->ber_bytes, p7->ber_len); + *out += p7->ber_len; + } + return (int)p7->ber_len; +} + +int i2d_PKCS7_bio(BIO *bio, const PKCS7 *p7) { + size_t written = 0; + while (written < p7->ber_len) { + size_t todo = p7->ber_len - written; + int len = todo > INT_MAX ? INT_MAX : (int)todo; + int ret = BIO_write(bio, p7->ber_bytes + written, len); + if (ret <= 0) { + return 0; + } + written += (size_t)ret; + } + return 1; +} + +void PKCS7_free(PKCS7 *p7) { + if (p7 == NULL) { + return; + } + + OPENSSL_free(p7->ber_bytes); + ASN1_OBJECT_free(p7->type); + // We only supported signed data. + if (p7->d.sign != NULL) { + sk_X509_pop_free(p7->d.sign->cert, X509_free); + sk_X509_CRL_pop_free(p7->d.sign->crl, X509_CRL_free); + OPENSSL_free(p7->d.sign); + } + OPENSSL_free(p7); +} + +// We only support signed data, so these getters are no-ops. +int PKCS7_type_is_data(const PKCS7 *p7) { return 0; } +int PKCS7_type_is_digest(const PKCS7 *p7) { return 0; } +int PKCS7_type_is_encrypted(const PKCS7 *p7) { return 0; } +int PKCS7_type_is_enveloped(const PKCS7 *p7) { return 0; } +int PKCS7_type_is_signed(const PKCS7 *p7) { return 1; } +int PKCS7_type_is_signedAndEnveloped(const PKCS7 *p7) { return 0; } + +PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey, STACK_OF(X509) *certs, + BIO *data, int flags) { + if (sign_cert != NULL || pkey != NULL || flags != PKCS7_DETACHED) { + OPENSSL_PUT_ERROR(PKCS7, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return NULL; + } + + uint8_t *der; + size_t len; + CBB cbb; + if (!CBB_init(&cbb, 2048) || + !PKCS7_bundle_certificates(&cbb, certs) || + !CBB_finish(&cbb, &der, &len)) { + CBB_cleanup(&cbb); + return NULL; + } + + CBS cbs; + CBS_init(&cbs, der, len); + PKCS7 *ret = pkcs7_new(&cbs); + OPENSSL_free(der); + return ret; +} |