// Copyright 2017 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/x509_util_apple.h" #include "build/build_config.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" #include "net/test/cert_test_util.h" #include "net/test/test_data_directory.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace x509_util { namespace { std::string BytesForSecCert(SecCertificateRef sec_cert) { std::string result; base::ScopedCFTypeRef der_data(SecCertificateCopyData(sec_cert)); if (!der_data) { ADD_FAILURE(); return result; } result.assign(reinterpret_cast(CFDataGetBytePtr(der_data)), CFDataGetLength(der_data)); return result; } std::string BytesForSecCert(const void* sec_cert) { return BytesForSecCert( reinterpret_cast(const_cast(sec_cert))); } } // namespace TEST(X509UtilTest, CreateSecCertificateArrayForX509Certificate) { scoped_refptr cert = CreateCertificateChainFromFile( GetTestCertsDirectory(), "multi-root-chain1.pem", X509Certificate::FORMAT_PEM_CERT_SEQUENCE); ASSERT_TRUE(cert); EXPECT_EQ(3U, cert->intermediate_buffers().size()); base::ScopedCFTypeRef sec_certs( CreateSecCertificateArrayForX509Certificate(cert.get())); ASSERT_TRUE(sec_certs); ASSERT_EQ(4, CFArrayGetCount(sec_certs.get())); for (int i = 0; i < 4; ++i) ASSERT_TRUE(CFArrayGetValueAtIndex(sec_certs.get(), i)); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(cert->cert_buffer()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0))); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( cert->intermediate_buffers()[0].get()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1))); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( cert->intermediate_buffers()[1].get()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 2))); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece( cert->intermediate_buffers()[2].get()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 3))); } TEST(X509UtilTest, CreateSecCertificateArrayForX509CertificateErrors) { scoped_refptr ok_cert( ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem")); ASSERT_TRUE(ok_cert); bssl::UniquePtr bad_cert = x509_util::CreateCryptoBuffer(base::StringPiece("invalid")); ASSERT_TRUE(bad_cert); scoped_refptr ok_cert2( ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem")); ASSERT_TRUE(ok_cert); std::vector> intermediates; intermediates.push_back(bssl::UpRef(bad_cert)); intermediates.push_back(bssl::UpRef(ok_cert2->cert_buffer())); scoped_refptr cert_with_intermediates( X509Certificate::CreateFromBuffer(bssl::UpRef(ok_cert->cert_buffer()), std::move(intermediates))); ASSERT_TRUE(cert_with_intermediates); EXPECT_EQ(2U, cert_with_intermediates->intermediate_buffers().size()); // With InvalidIntermediateBehavior::kIgnore, invalid intermediate certs // should be silently dropped. base::ScopedCFTypeRef sec_certs( CreateSecCertificateArrayForX509Certificate( cert_with_intermediates.get(), InvalidIntermediateBehavior::kIgnore)); ASSERT_TRUE(sec_certs); for (int i = 0; i < CFArrayGetCount(sec_certs.get()); ++i) ASSERT_TRUE(CFArrayGetValueAtIndex(sec_certs.get(), i)); if (CFArrayGetCount(sec_certs.get()) == 2) { EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert->cert_buffer()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0))); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert2->cert_buffer()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1))); // Normal CreateSecCertificateArrayForX509Certificate should fail with // invalid certs in chain. EXPECT_FALSE(CreateSecCertificateArrayForX509Certificate( cert_with_intermediates.get())); } else if (CFArrayGetCount(sec_certs.get()) == 3) { // On older macOS versions that do lazy parsing of SecCertificates, the // invalid certificate may be accepted, which is okay. The test is just // verifying that *if* creating a SecCertificate from one of the // intermediates fails, that cert is ignored and the other certs are still // returned. EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert->cert_buffer()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 0))); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(bad_cert.get()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 1))); EXPECT_EQ(x509_util::CryptoBufferAsStringPiece(ok_cert2->cert_buffer()), BytesForSecCert(CFArrayGetValueAtIndex(sec_certs.get(), 2))); // Normal CreateSecCertificateArrayForX509Certificate should also // succeed in this case. EXPECT_TRUE(CreateSecCertificateArrayForX509Certificate( cert_with_intermediates.get())); } else { ADD_FAILURE() << "CFArrayGetCount(sec_certs.get()) = " << CFArrayGetCount(sec_certs.get()); } } TEST(X509UtilTest, CreateSecCertificateFromBytesAndCreateX509CertificateFromSecCertificate) { CertificateList certs = CreateCertificateListFromFile( GetTestCertsDirectory(), "multi-root-chain1.pem", X509Certificate::FORMAT_PEM_CERT_SEQUENCE); ASSERT_EQ(4u, certs.size()); std::string bytes_cert0( x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer())); std::string bytes_cert1( x509_util::CryptoBufferAsStringPiece(certs[1]->cert_buffer())); std::string bytes_cert2( x509_util::CryptoBufferAsStringPiece(certs[2]->cert_buffer())); std::string bytes_cert3( x509_util::CryptoBufferAsStringPiece(certs[3]->cert_buffer())); base::ScopedCFTypeRef sec_cert0( CreateSecCertificateFromBytes( reinterpret_cast(bytes_cert0.data()), bytes_cert0.length())); ASSERT_TRUE(sec_cert0); EXPECT_EQ(bytes_cert0, BytesForSecCert(sec_cert0)); base::ScopedCFTypeRef sec_cert1( CreateSecCertificateFromBytes( reinterpret_cast(bytes_cert1.data()), bytes_cert1.length())); ASSERT_TRUE(sec_cert1); EXPECT_EQ(bytes_cert1, BytesForSecCert(sec_cert1)); base::ScopedCFTypeRef sec_cert2( CreateSecCertificateFromX509Certificate(certs[2].get())); ASSERT_TRUE(sec_cert2); EXPECT_EQ(bytes_cert2, BytesForSecCert(sec_cert2)); base::ScopedCFTypeRef sec_cert3( CreateSecCertificateFromX509Certificate(certs[3].get())); ASSERT_TRUE(sec_cert3); EXPECT_EQ(bytes_cert3, BytesForSecCert(sec_cert3)); scoped_refptr x509_cert_no_intermediates = CreateX509CertificateFromSecCertificate(sec_cert0, {}); ASSERT_TRUE(x509_cert_no_intermediates); EXPECT_EQ(0U, x509_cert_no_intermediates->intermediate_buffers().size()); EXPECT_EQ(bytes_cert0, x509_util::CryptoBufferAsStringPiece( x509_cert_no_intermediates->cert_buffer())); scoped_refptr x509_cert_one_intermediate = CreateX509CertificateFromSecCertificate(sec_cert0, {sec_cert1}); ASSERT_TRUE(x509_cert_one_intermediate); EXPECT_EQ(bytes_cert0, x509_util::CryptoBufferAsStringPiece( x509_cert_one_intermediate->cert_buffer())); ASSERT_EQ(1U, x509_cert_one_intermediate->intermediate_buffers().size()); EXPECT_EQ(bytes_cert1, x509_util::CryptoBufferAsStringPiece( x509_cert_one_intermediate->intermediate_buffers()[0].get())); scoped_refptr x509_cert_two_intermediates = CreateX509CertificateFromSecCertificate(sec_cert0, {sec_cert1, sec_cert2}); ASSERT_TRUE(x509_cert_two_intermediates); EXPECT_EQ(bytes_cert0, x509_util::CryptoBufferAsStringPiece( x509_cert_two_intermediates->cert_buffer())); ASSERT_EQ(2U, x509_cert_two_intermediates->intermediate_buffers().size()); EXPECT_EQ(bytes_cert1, x509_util::CryptoBufferAsStringPiece( x509_cert_two_intermediates->intermediate_buffers()[0].get())); EXPECT_EQ(bytes_cert2, x509_util::CryptoBufferAsStringPiece( x509_cert_two_intermediates->intermediate_buffers()[1].get())); } } // namespace x509_util } // namespace net