| Index: net/cert/internal/verify_certificate_chain_typed_unittest.h
|
| diff --git a/net/cert/internal/verify_certificate_chain_typed_unittest.h b/net/cert/internal/verify_certificate_chain_typed_unittest.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1ddd46466da00ddc636bcf51745af24337db85ef
|
| --- /dev/null
|
| +++ b/net/cert/internal/verify_certificate_chain_typed_unittest.h
|
| @@ -0,0 +1,305 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#ifndef NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
|
| +#define NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
|
| +
|
| +#include "base/base_paths.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/path_service.h"
|
| +#include "net/cert/internal/parsed_certificate.h"
|
| +#include "net/cert/internal/test_helpers.h"
|
| +#include "net/cert/pem_tokenizer.h"
|
| +#include "net/der/input.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +
|
| +template <typename TestDelegate>
|
| +class VerifyCertificateChainTest : public ::testing::Test {
|
| + public:
|
| + void RunTest(const char* file_name) {
|
| + ParsedCertificateList chain;
|
| + ParsedCertificateList roots;
|
| + der::GeneralizedTime time;
|
| + bool expected_result;
|
| +
|
| + ReadTestFromFile(file_name, &chain, &roots, &time, &expected_result);
|
| +
|
| + TestDelegate::Verify(chain, roots, time, expected_result);
|
| + }
|
| +
|
| + private:
|
| + // Reads a data file from the unit-test data.
|
| + std::string ReadTestFileToString(const std::string& file_name) {
|
| + // Compute the full path, relative to the src/ directory.
|
| + base::FilePath src_root;
|
| + PathService::Get(base::DIR_SOURCE_ROOT, &src_root);
|
| + base::FilePath filepath = src_root.AppendASCII(
|
| + std::string("net/data/verify_certificate_chain_unittest/") + file_name);
|
| +
|
| + // Read the full contents of the file.
|
| + std::string file_data;
|
| + if (!base::ReadFileToString(filepath, &file_data)) {
|
| + ADD_FAILURE() << "Couldn't read file: " << filepath.value();
|
| + return std::string();
|
| + }
|
| +
|
| + return file_data;
|
| + }
|
| +
|
| + // Reads a test case from |file_name|. Test cases are comprised of a
|
| + // certificate chain, trust store, a timestamp to validate at, and the
|
| + // expected result of verification.
|
| + void ReadTestFromFile(const std::string& file_name,
|
| + ParsedCertificateList* chain,
|
| + ParsedCertificateList* roots,
|
| + der::GeneralizedTime* time,
|
| + bool* verify_result) {
|
| + chain->clear();
|
| + roots->clear();
|
| +
|
| + std::string file_data = ReadTestFileToString(file_name);
|
| +
|
| + std::vector<std::string> pem_headers;
|
| +
|
| + const char kCertificateHeader[] = "CERTIFICATE";
|
| + const char kTrustedCertificateHeader[] = "TRUSTED_CERTIFICATE";
|
| + const char kTimeHeader[] = "TIME";
|
| + const char kResultHeader[] = "VERIFY_RESULT";
|
| +
|
| + pem_headers.push_back(kCertificateHeader);
|
| + pem_headers.push_back(kTrustedCertificateHeader);
|
| + pem_headers.push_back(kTimeHeader);
|
| + pem_headers.push_back(kResultHeader);
|
| +
|
| + bool has_time = false;
|
| + bool has_result = false;
|
| +
|
| + PEMTokenizer pem_tokenizer(file_data, pem_headers);
|
| + while (pem_tokenizer.GetNext()) {
|
| + const std::string& block_type = pem_tokenizer.block_type();
|
| + const std::string& block_data = pem_tokenizer.data();
|
| +
|
| + if (block_type == kCertificateHeader) {
|
| + ASSERT_TRUE(net::ParsedCertificate::CreateAndAddToVector(
|
| + reinterpret_cast<const uint8_t*>(block_data.data()),
|
| + block_data.size(),
|
| + net::ParsedCertificate::DataSource::INTERNAL_COPY, {}, chain));
|
| + } else if (block_type == kTrustedCertificateHeader) {
|
| + ASSERT_TRUE(net::ParsedCertificate::CreateAndAddToVector(
|
| + reinterpret_cast<const uint8_t*>(block_data.data()),
|
| + block_data.size(),
|
| + net::ParsedCertificate::DataSource::INTERNAL_COPY, {}, roots));
|
| + } else if (block_type == kTimeHeader) {
|
| + ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader;
|
| + has_time = true;
|
| + ASSERT_TRUE(der::ParseUTCTime(der::Input(&block_data), time));
|
| + } else if (block_type == kResultHeader) {
|
| + ASSERT_FALSE(has_result) << "Duplicate " << kResultHeader;
|
| + ASSERT_TRUE(block_data == "SUCCESS" || block_data == "FAIL")
|
| + << "Unrecognized result: " << block_data;
|
| + has_result = true;
|
| + *verify_result = block_data == "SUCCESS";
|
| + }
|
| + }
|
| +
|
| + ASSERT_TRUE(has_time);
|
| + ASSERT_TRUE(has_result);
|
| + }
|
| +};
|
| +
|
| +// Tests that have only one root. These can be tested without requiring any
|
| +// path-building ability.
|
| +template <typename TestDelegate>
|
| +class VerifyCertificateChainSingleRootTest
|
| + : public VerifyCertificateChainTest<TestDelegate> {};
|
| +
|
| +TYPED_TEST_CASE_P(VerifyCertificateChainSingleRootTest);
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetAndIntermediary) {
|
| + this->RunTest("target-and-intermediary.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IntermediaryLacksBasicConstraints) {
|
| + this->RunTest("intermediary-lacks-basic-constraints.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IntermediaryBasicConstraintsCaFalse) {
|
| + this->RunTest("intermediary-basic-constraints-ca-false.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IntermediaryBasicConstraintsNotCritical) {
|
| + this->RunTest("intermediary-basic-constraints-not-critical.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IntermediaryLacksSigningKeyUsage) {
|
| + this->RunTest("intermediary-lacks-signing-key-usage.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IntermediaryUnknownCriticalExtension) {
|
| + this->RunTest("intermediary-unknown-critical-extension.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IntermediaryUnknownNonCriticalExtension) {
|
| + this->RunTest("intermediary-unknown-non-critical-extension.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + ViolatesBasicConstraintsPathlen0) {
|
| + this->RunTest("violates-basic-constraints-pathlen-0.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + BasicConstraintsPathlen0SelfIssued) {
|
| + this->RunTest("basic-constraints-pathlen-0-self-issued.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedWithMd5) {
|
| + this->RunTest("target-signed-with-md5.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, IntermediarySignedWithMd5) {
|
| + this->RunTest("intermediary-signed-with-md5.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetWrongSignature) {
|
| + this->RunTest("target-wrong-signature.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedBy512bitRsa) {
|
| + this->RunTest("target-signed-by-512bit-rsa.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetSignedUsingEcdsa) {
|
| + this->RunTest("target-signed-using-ecdsa.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredIntermediary) {
|
| + this->RunTest("expired-intermediary.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredTarget) {
|
| + this->RunTest("expired-target.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredTargetNotBefore) {
|
| + this->RunTest("expired-target-notBefore.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ExpiredRoot) {
|
| + this->RunTest("expired-root.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetNotEndEntity) {
|
| + this->RunTest("target-not-end-entity.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + TargetHasKeyCertSignButNotCa) {
|
| + this->RunTest("target-has-keycertsign-but-not-ca.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, TargetHasPathlenButNotCa) {
|
| + this->RunTest("target-has-pathlen-but-not-ca.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + TargetUnknownCriticalExtension) {
|
| + this->RunTest("target-unknown-critical-extension.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IssuerAndSubjectNotByteForByteEqual) {
|
| + this->RunTest("issuer-and-subject-not-byte-for-byte-equal.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + IssuerAndSubjectNotByteForByteEqualAnchor) {
|
| + this->RunTest("issuer-and-subject-not-byte-for-byte-equal-anchor.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, ViolatesPathlen1Root) {
|
| + this->RunTest("violates-pathlen-1-root.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, NonSelfSignedRoot) {
|
| + this->RunTest("non-self-signed-root.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverOldChain) {
|
| + this->RunTest("key-rollover-oldchain.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverRolloverChain) {
|
| + this->RunTest("key-rollover-rolloverchain.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest,
|
| + KeyRolloverLongRolloverChain) {
|
| + this->RunTest("key-rollover-longrolloverchain.pem");
|
| +}
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainSingleRootTest, KeyRolloverNewChain) {
|
| + this->RunTest("key-rollover-newchain.pem");
|
| +}
|
| +
|
| +// TODO(eroman): Add test that invalidate validity dates where the day or month
|
| +// ordinal not in range, like "March 39, 2016" are rejected.
|
| +
|
| +REGISTER_TYPED_TEST_CASE_P(VerifyCertificateChainSingleRootTest,
|
| + TargetAndIntermediary,
|
| + IntermediaryLacksBasicConstraints,
|
| + IntermediaryBasicConstraintsCaFalse,
|
| + IntermediaryBasicConstraintsNotCritical,
|
| + IntermediaryLacksSigningKeyUsage,
|
| + IntermediaryUnknownCriticalExtension,
|
| + IntermediaryUnknownNonCriticalExtension,
|
| + ViolatesBasicConstraintsPathlen0,
|
| + BasicConstraintsPathlen0SelfIssued,
|
| + TargetSignedWithMd5,
|
| + IntermediarySignedWithMd5,
|
| + TargetWrongSignature,
|
| + TargetSignedBy512bitRsa,
|
| + TargetSignedUsingEcdsa,
|
| + ExpiredIntermediary,
|
| + ExpiredTarget,
|
| + ExpiredTargetNotBefore,
|
| + ExpiredRoot,
|
| + TargetNotEndEntity,
|
| + TargetHasKeyCertSignButNotCa,
|
| + TargetHasPathlenButNotCa,
|
| + TargetUnknownCriticalExtension,
|
| + IssuerAndSubjectNotByteForByteEqual,
|
| + IssuerAndSubjectNotByteForByteEqualAnchor,
|
| + ViolatesPathlen1Root,
|
| + NonSelfSignedRoot,
|
| + KeyRolloverOldChain,
|
| + KeyRolloverRolloverChain,
|
| + KeyRolloverLongRolloverChain,
|
| + KeyRolloverNewChain);
|
| +
|
| +// Tests that have zero roots or more than one root.
|
| +template <typename TestDelegate>
|
| +class VerifyCertificateChainNonSingleRootTest
|
| + : public VerifyCertificateChainTest<TestDelegate> {};
|
| +
|
| +TYPED_TEST_CASE_P(VerifyCertificateChainNonSingleRootTest);
|
| +
|
| +TYPED_TEST_P(VerifyCertificateChainNonSingleRootTest, UnknownRoot) {
|
| + this->RunTest("unknown-root.pem");
|
| +}
|
| +
|
| +REGISTER_TYPED_TEST_CASE_P(VerifyCertificateChainNonSingleRootTest,
|
| + UnknownRoot);
|
| +
|
| +} // namespace net
|
| +
|
| +#endif // NET_CERT_INTERNAL_VERIFY_CERTIFICATE_CHAIN_TYPED_UNITTEST_H_
|
|
|