| Index: components/cast_certificate/cast_cert_validator_unittest.cc
|
| diff --git a/components/cast_certificate/cast_cert_validator_unittest.cc b/components/cast_certificate/cast_cert_validator_unittest.cc
|
| index 30b5c38846d908125cc13ce58a4d67af1340d03d..ee6b47f0bea77598f3e672a146cef7464d9a61d8 100644
|
| --- a/components/cast_certificate/cast_cert_validator_unittest.cc
|
| +++ b/components/cast_certificate/cast_cert_validator_unittest.cc
|
| @@ -5,6 +5,8 @@
|
| #include "components/cast_certificate/cast_cert_validator.h"
|
|
|
| #include "components/cast_certificate/cast_cert_validator_test_helpers.h"
|
| +#include "net/cert/internal/parsed_certificate.h"
|
| +#include "net/cert/internal/trust_store.h"
|
| #include "testing/gtest/include/gtest/gtest.h"
|
|
|
| namespace cast_certificate {
|
| @@ -23,6 +25,24 @@ enum TestResult {
|
| RESULT_FAIL,
|
| };
|
|
|
| +enum TrustStoreDependency {
|
| + // Uses the built-in trust store for Cast. This is how certificates are
|
| + // verified in production.
|
| + TRUST_STORE_BUILTIN,
|
| +
|
| + // Instead of using the built-in trust store, use root certificate in the
|
| + // provided test chain as the trust anchor.
|
| + //
|
| + // This trust anchor is initialized with anchor constraints, similar to how
|
| + // TrustAnchors in the built-in store are setup.
|
| + TRUST_STORE_FROM_TEST_FILE,
|
| +
|
| + // This is the same as TRUST_STORE_FROM_TEST_FILE except the TrustAnchor is
|
| + // setup to NOT enforce anchor constraints. This mode is useful for
|
| + // verifying control groups. It is not how code works in production.
|
| + TRUST_STORE_FROM_TEST_FILE_UNCONSTRAINED,
|
| +};
|
| +
|
| // Reads a test chain from |certs_file_name|, and asserts that verifying it as
|
| // a Cast device certificate yields |expected_result|.
|
| //
|
| @@ -32,6 +52,8 @@ enum TestResult {
|
| // * |expected_policy| - The policy that should have been identified for the
|
| // device certificate.
|
| // * |time| - The timestamp to use when verifying the certificate.
|
| +// * |trust_store_dependency| - Which trust store to use when verifying (see
|
| +// enum's definition).
|
| // * |optional_signed_data_file_name| - optional path to a PEM file containing
|
| // a valid signature generated by the device certificate.
|
| //
|
| @@ -40,14 +62,60 @@ void RunTest(TestResult expected_result,
|
| CastDeviceCertPolicy expected_policy,
|
| const std::string& certs_file_name,
|
| const base::Time& time,
|
| + TrustStoreDependency trust_store_dependency,
|
| const std::string& optional_signed_data_file_name) {
|
| auto certs =
|
| cast_certificate::testing::ReadCertificateChainFromFile(certs_file_name);
|
|
|
| + std::unique_ptr<net::TrustStore> trust_store;
|
| +
|
| + switch (trust_store_dependency) {
|
| + case TRUST_STORE_BUILTIN:
|
| + // Leave trust_store as nullptr.
|
| + break;
|
| +
|
| + case TRUST_STORE_FROM_TEST_FILE:
|
| + case TRUST_STORE_FROM_TEST_FILE_UNCONSTRAINED: {
|
| + ASSERT_FALSE(certs.empty());
|
| +
|
| + // Parse the root certificate of the chain.
|
| + scoped_refptr<net::ParsedCertificate> root =
|
| + net::ParsedCertificate::CreateFromCertificateCopy(certs.back(), {});
|
| + ASSERT_TRUE(root);
|
| +
|
| + // Remove it from the chain.
|
| + certs.pop_back();
|
| +
|
| + // Add it to the trust store as a trust anchor
|
| + trust_store.reset(new net::TrustStore);
|
| +
|
| + if (trust_store_dependency == TRUST_STORE_FROM_TEST_FILE_UNCONSTRAINED) {
|
| + // This is a test-only mode where anchor constraints are not enforced.
|
| + trust_store->AddTrustAnchor(
|
| + net::TrustAnchor::CreateFromCertificateNoConstraints(
|
| + std::move(root)));
|
| + } else {
|
| + // This is the regular mode used by the TrustAnchors for the built-in
|
| + // Cast store.
|
| + trust_store->AddTrustAnchor(
|
| + net::TrustAnchor::CreateFromCertificateWithConstraints(
|
| + std::move(root)));
|
| + }
|
| + }
|
| + }
|
| +
|
| std::unique_ptr<CertVerificationContext> context;
|
| CastDeviceCertPolicy policy;
|
| - bool result = VerifyDeviceCert(certs, time, &context, &policy, nullptr,
|
| - CRLPolicy::CRL_OPTIONAL);
|
| +
|
| + bool result;
|
| + if (trust_store.get()) {
|
| + result =
|
| + VerifyDeviceCertForTest(certs, time, &context, &policy, nullptr,
|
| + CRLPolicy::CRL_OPTIONAL, trust_store.get());
|
| + } else {
|
| + result = VerifyDeviceCert(certs, time, &context, &policy, nullptr,
|
| + CRLPolicy::CRL_OPTIONAL);
|
| + }
|
|
|
| if (expected_result == RESULT_FAIL) {
|
| ASSERT_FALSE(result);
|
| @@ -131,11 +199,11 @@ base::Time MarchFirst2037() {
|
| // 1: Eureka Gen1 ICA
|
| //
|
| // Chains to trust anchor:
|
| -// Eureka Root CA (not included)
|
| +// Eureka Root CA (built-in trust store)
|
| TEST(VerifyCastDeviceCertTest, ChromecastGen1) {
|
| RunTest(RESULT_SUCCESS, "2ZZBG9 FA8FCA3EF91A", CastDeviceCertPolicy::NONE,
|
| "certificates/chromecast_gen1.pem", AprilFirst2016(),
|
| - "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
|
| + TRUST_STORE_BUILTIN, "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 2:
|
| @@ -144,11 +212,11 @@ TEST(VerifyCastDeviceCertTest, ChromecastGen1) {
|
| // 1: Eureka Gen1 ICA
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| TEST(VerifyCastDeviceCertTest, ChromecastGen1Reissue) {
|
| RunTest(RESULT_SUCCESS, "2ZZBG9 FA8FCA3EF91A", CastDeviceCertPolicy::NONE,
|
| "certificates/chromecast_gen1_reissue.pem", AprilFirst2016(),
|
| - "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
|
| + TRUST_STORE_BUILTIN, "signeddata/2ZZBG9_FA8FCA3EF91A.pem");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 2:
|
| @@ -157,10 +225,11 @@ TEST(VerifyCastDeviceCertTest, ChromecastGen1Reissue) {
|
| // 1: Chromecast ICA 3
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| TEST(VerifyCastDeviceCertTest, ChromecastGen2) {
|
| RunTest(RESULT_SUCCESS, "3ZZAK6 FA8FCA3F0D35", CastDeviceCertPolicy::NONE,
|
| - "certificates/chromecast_gen2.pem", AprilFirst2016(), "");
|
| + "certificates/chromecast_gen2.pem", AprilFirst2016(),
|
| + TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 3:
|
| @@ -170,10 +239,10 @@ TEST(VerifyCastDeviceCertTest, ChromecastGen2) {
|
| // 2: Widevine Cast Subroot
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| TEST(VerifyCastDeviceCertTest, Fugu) {
|
| RunTest(RESULT_SUCCESS, "-6394818897508095075", CastDeviceCertPolicy::NONE,
|
| - "certificates/fugu.pem", AprilFirst2016(), "");
|
| + "certificates/fugu.pem", AprilFirst2016(), TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying an invalid certificate chain of length 1:
|
| @@ -181,12 +250,13 @@ TEST(VerifyCastDeviceCertTest, Fugu) {
|
| // 0: Cast Test Untrusted Device
|
| //
|
| // Chains to:
|
| -// Cast Test Untrusted ICA (not included)
|
| +// Cast Test Untrusted ICA (Not part of trust store)
|
| //
|
| // This is invalid because it does not chain to a trust anchor.
|
| TEST(VerifyCastDeviceCertTest, Unchained) {
|
| RunTest(RESULT_FAIL, "", CastDeviceCertPolicy::NONE,
|
| - "certificates/unchained.pem", AprilFirst2016(), "");
|
| + "certificates/unchained.pem", AprilFirst2016(), TRUST_STORE_BUILTIN,
|
| + "");
|
| }
|
|
|
| // Tests verifying one of the self-signed trust anchors (chain of length 1):
|
| @@ -194,14 +264,15 @@ TEST(VerifyCastDeviceCertTest, Unchained) {
|
| // 0: Cast Root CA
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA
|
| +// Cast Root CA (built-in trust store)
|
| //
|
| // Although this is a valid and trusted certificate (it is one of the
|
| // trust anchors after all) it fails the test as it is not a *device
|
| // certificate*.
|
| TEST(VerifyCastDeviceCertTest, CastRootCa) {
|
| RunTest(RESULT_FAIL, "", CastDeviceCertPolicy::NONE,
|
| - "certificates/cast_root_ca.pem", AprilFirst2016(), "");
|
| + "certificates/cast_root_ca.pem", AprilFirst2016(),
|
| + TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 2:
|
| @@ -210,14 +281,14 @@ TEST(VerifyCastDeviceCertTest, CastRootCa) {
|
| // 1: Chromecast ICA 4 (Audio)
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| //
|
| // This device certificate has a policy that means it is valid only for audio
|
| // devices.
|
| TEST(VerifyCastDeviceCertTest, ChromecastAudio) {
|
| RunTest(RESULT_SUCCESS, "4ZZDZJ FA8FCA7EFE3C",
|
| CastDeviceCertPolicy::AUDIO_ONLY, "certificates/chromecast_audio.pem",
|
| - AprilFirst2016(), "");
|
| + AprilFirst2016(), TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 3:
|
| @@ -227,14 +298,14 @@ TEST(VerifyCastDeviceCertTest, ChromecastAudio) {
|
| // 2: Cast Audio Dev Root CA
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| //
|
| // This device certificate has a policy that means it is valid only for audio
|
| // devices.
|
| TEST(VerifyCastDeviceCertTest, MtkAudioDev) {
|
| RunTest(RESULT_SUCCESS, "MediaTek Audio Dev Test",
|
| CastDeviceCertPolicy::AUDIO_ONLY, "certificates/mtk_audio_dev.pem",
|
| - JanuaryFirst2015(), "");
|
| + JanuaryFirst2015(), TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 2:
|
| @@ -243,10 +314,10 @@ TEST(VerifyCastDeviceCertTest, MtkAudioDev) {
|
| // 1: Cast TV ICA (Vizio)
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| TEST(VerifyCastDeviceCertTest, Vizio) {
|
| RunTest(RESULT_SUCCESS, "9V0000VB FA8FCA784D01", CastDeviceCertPolicy::NONE,
|
| - "certificates/vizio.pem", AprilFirst2016(), "");
|
| + "certificates/vizio.pem", AprilFirst2016(), TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 2 using expired
|
| @@ -257,15 +328,15 @@ TEST(VerifyCastDeviceCertTest, ChromecastGen2InvalidTime) {
|
| // Control test - certificate should be valid at some time otherwise
|
| // this test is pointless.
|
| RunTest(RESULT_SUCCESS, "3ZZAK6 FA8FCA3F0D35", CastDeviceCertPolicy::NONE,
|
| - kCertsFile, AprilFirst2016(), "");
|
| + kCertsFile, AprilFirst2016(), TRUST_STORE_BUILTIN, "");
|
|
|
| // Use a time before notBefore.
|
| RunTest(RESULT_FAIL, "", CastDeviceCertPolicy::NONE, kCertsFile,
|
| - JanuaryFirst2015(), "");
|
| + JanuaryFirst2015(), TRUST_STORE_BUILTIN, "");
|
|
|
| // Use a time after notAfter.
|
| RunTest(RESULT_FAIL, "", CastDeviceCertPolicy::NONE, kCertsFile,
|
| - MarchFirst2037(), "");
|
| + MarchFirst2037(), TRUST_STORE_BUILTIN, "");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 3:
|
| @@ -275,7 +346,7 @@ TEST(VerifyCastDeviceCertTest, ChromecastGen2InvalidTime) {
|
| // 2: Cast Audio Dev Root CA
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| //
|
| // This device certificate has a policy that means it is valid only for audio
|
| // devices.
|
| @@ -283,7 +354,7 @@ TEST(VerifyCastDeviceCertTest, AudioRefDevTestChain3) {
|
| RunTest(RESULT_SUCCESS, "Audio Reference Dev Test",
|
| CastDeviceCertPolicy::AUDIO_ONLY,
|
| "certificates/audio_ref_dev_test_chain_3.pem", AprilFirst2016(),
|
| - "signeddata/AudioReferenceDevTest.pem");
|
| + TRUST_STORE_BUILTIN, "signeddata/AudioReferenceDevTest.pem");
|
| }
|
|
|
| // Tests verifying a valid certificate chain of length 3. Note that the first
|
| @@ -296,7 +367,7 @@ TEST(VerifyCastDeviceCertTest, AudioRefDevTestChain3) {
|
| // 2: Cast Audio Sony CA
|
| //
|
| // Chains to trust anchor:
|
| -// Cast Root CA (not included)
|
| +// Cast Root CA (built-in trust store)
|
| //
|
| // This device certificate has a policy that means it is valid only for audio
|
| // devices.
|
| @@ -304,7 +375,51 @@ TEST(VerifyCastDeviceCertTest, IntermediateSerialNumberTooLong) {
|
| RunTest(RESULT_SUCCESS, "8C579B806FFC8A9DFFFF F8:8F:CA:6B:E6:DA",
|
| CastDeviceCertPolicy::AUDIO_ONLY,
|
| "certificates/intermediate_serialnumber_toolong.pem",
|
| - AprilFirst2016(), "");
|
| + AprilFirst2016(), TRUST_STORE_BUILTIN, "");
|
| +}
|
| +
|
| +// Tests verifying a valid certificate chain of length 2 when the trust anchor
|
| +// is "expired". This is expected to work since expiration is not an enforced
|
| +// anchor constraint, even though it may appear in the root certificate.
|
| +//
|
| +// 0: CastDevice
|
| +// 1: CastIntermediate
|
| +//
|
| +// Chains to trust anchor:
|
| +// Expired CastRoot (provided by test data)
|
| +TEST(VerifyCastDeviceCertTest, ExpiredTrustAnchor) {
|
| + // The root certificate is only valid in 2015, so validating with a time in
|
| + // 2016 means it is expired.
|
| + RunTest(RESULT_SUCCESS, "CastDevice", CastDeviceCertPolicy::NONE,
|
| + "certificates/expired_root.pem", AprilFirst2016(),
|
| + TRUST_STORE_FROM_TEST_FILE, "");
|
| +}
|
| +
|
| +// Tests verifying a certificate chain where the root certificate has a pathlen
|
| +// constraint which is violated by the chain. In this case Root has a pathlen=1
|
| +// constraint, however neither intermediate is constrained.
|
| +//
|
| +// The expectation is for pathlen constraints on trust anchors to be enforced,
|
| +// so this validation must fail.
|
| +//
|
| +// 0: Target
|
| +// 1: Intermediate2
|
| +// 2: Intermediate1
|
| +//
|
| +// Chains to trust anchor:
|
| +// Root (provided by test data; has pathlen=1 constraint)
|
| +TEST(VerifyCastDeviceCertTest, ViolatesPathlenTrustAnchorConstraint) {
|
| + // First do a control test -- when anchor constraints are NOT enforced this
|
| + // chain should validate just fine.
|
| + RunTest(RESULT_SUCCESS, "Target", CastDeviceCertPolicy::NONE,
|
| + "certificates/violates_root_pathlen_constraint.pem", AprilFirst2016(),
|
| + TRUST_STORE_FROM_TEST_FILE_UNCONSTRAINED, "");
|
| +
|
| + // Now do the real test and verify validation fails when using a TrustAncho
|
| + // with pathlen constraint.
|
| + RunTest(RESULT_FAIL, "Target", CastDeviceCertPolicy::NONE,
|
| + "certificates/violates_root_pathlen_constraint.pem", AprilFirst2016(),
|
| + TRUST_STORE_FROM_TEST_FILE, "");
|
| }
|
|
|
| // ------------------------------------------------------
|
|
|