chromium/net/cert/x509_util_apple_unittest.cc

// 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 <string_view>

#include "base/containers/span.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::apple::ScopedCFTypeRef<CFDataRef> der_data(
      SecCertificateCopyData(sec_cert));
  if (!der_data) {
    ADD_FAILURE();
    return result;
  }
  result.assign(reinterpret_cast<const char*>(CFDataGetBytePtr(der_data.get())),
                CFDataGetLength(der_data.get()));
  return result;
}

std::string BytesForSecCert(const void* sec_cert) {
  return BytesForSecCert(
      reinterpret_cast<SecCertificateRef>(const_cast<void*>(sec_cert)));
}

}  // namespace

TEST(X509UtilTest, CreateSecCertificateArrayForX509Certificate) {
  scoped_refptr<X509Certificate> cert = CreateCertificateChainFromFile(
      GetTestCertsDirectory(), "multi-root-chain1.pem",
      X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
  ASSERT_TRUE(cert);
  EXPECT_EQ(3U, cert->intermediate_buffers().size());

  base::apple::ScopedCFTypeRef<CFMutableArrayRef> 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<X509Certificate> ok_cert(
      ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"));
  ASSERT_TRUE(ok_cert);

  bssl::UniquePtr<CRYPTO_BUFFER> bad_cert =
      x509_util::CreateCryptoBuffer(std::string_view("invalid"));
  ASSERT_TRUE(bad_cert);

  scoped_refptr<X509Certificate> ok_cert2(
      ImportCertFromFile(GetTestCertsDirectory(), "root_ca_cert.pem"));
  ASSERT_TRUE(ok_cert);

  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
  intermediates.push_back(bssl::UpRef(bad_cert));
  intermediates.push_back(bssl::UpRef(ok_cert2->cert_buffer()));
  scoped_refptr<X509Certificate> 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::apple::ScopedCFTypeRef<CFMutableArrayRef> 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::apple::ScopedCFTypeRef<SecCertificateRef> sec_cert0(
      CreateSecCertificateFromBytes(base::as_byte_span(bytes_cert0)));
  ASSERT_TRUE(sec_cert0);
  EXPECT_EQ(bytes_cert0, BytesForSecCert(sec_cert0.get()));

  base::apple::ScopedCFTypeRef<SecCertificateRef> sec_cert1(
      CreateSecCertificateFromBytes(base::as_byte_span(bytes_cert1)));
  ASSERT_TRUE(sec_cert1);
  EXPECT_EQ(bytes_cert1, BytesForSecCert(sec_cert1.get()));

  base::apple::ScopedCFTypeRef<SecCertificateRef> sec_cert2(
      CreateSecCertificateFromX509Certificate(certs[2].get()));
  ASSERT_TRUE(sec_cert2);
  EXPECT_EQ(bytes_cert2, BytesForSecCert(sec_cert2.get()));

  base::apple::ScopedCFTypeRef<SecCertificateRef> sec_cert3(
      CreateSecCertificateFromX509Certificate(certs[3].get()));
  ASSERT_TRUE(sec_cert3);
  EXPECT_EQ(bytes_cert3, BytesForSecCert(sec_cert3.get()));

  scoped_refptr<X509Certificate> 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<X509Certificate> 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<X509Certificate> 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