// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "third_party/blink/public/common/origin_trials/trial_token.h" #include <memory> #include "base/strings/string_util.h" #include "base/test/simple_test_clock.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" namespace blink { namespace { const uint8_t kVersion2 = …; const uint8_t kVersion3 = …; // This is a sample public key for testing the API. The corresponding private // key (use this to generate new samples for this test file) is: // // 0x83, 0x67, 0xf4, 0xcd, 0x2a, 0x1f, 0x0e, 0x04, 0x0d, 0x43, 0x13, // 0x4c, 0x67, 0xc4, 0xf4, 0x28, 0xc9, 0x90, 0x15, 0x02, 0xe2, 0xba, // 0xfd, 0xbb, 0xfa, 0xbc, 0x92, 0x76, 0x8a, 0x2c, 0x4b, 0xc7, 0x75, // 0x10, 0xac, 0xf9, 0x3a, 0x1c, 0xb8, 0xa9, 0x28, 0x70, 0xd2, 0x9a, // 0xd0, 0x0b, 0x59, 0xe1, 0xac, 0x2b, 0xb7, 0xd5, 0xca, 0x1f, 0x64, // 0x90, 0x08, 0x8e, 0xa8, 0xe0, 0x56, 0x3a, 0x04, 0xd0 // // This private key can also be found in tools/origin_trials/eftest.key in // binary form. Please update that if changing the key. // // To use this with a real browser, use --origin-trial-public-key with the // public key, base-64-encoded: // --origin-trial-public-key=dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA= const OriginTrialPublicKey kTestPublicKey = …; // This is a valid, but incorrect, public key for testing signatures against. // The corresponding private key is: // // 0x21, 0xee, 0xfa, 0x81, 0x6a, 0xff, 0xdf, 0xb8, 0xc1, 0xdd, 0x75, // 0x05, 0x04, 0x29, 0x68, 0x67, 0x60, 0x85, 0x91, 0xd0, 0x50, 0x16, // 0x0a, 0xcf, 0xa2, 0x37, 0xa3, 0x2e, 0x11, 0x7a, 0x17, 0x96, 0x50, // 0x07, 0x4d, 0x76, 0x55, 0x56, 0x42, 0x17, 0x2d, 0x8a, 0x9c, 0x47, // 0x96, 0x25, 0xda, 0x70, 0xaa, 0xb9, 0xfd, 0x53, 0x5d, 0x51, 0x3e, // 0x16, 0xab, 0xb4, 0x86, 0xea, 0xf3, 0x35, 0xc6, 0xca const OriginTrialPublicKey kTestPublicKey2 = …; // This is a good trial token, signed with the above test private key. // Generate this token with the command (in tools/origin_trials): // generate_token.py valid.example.com 2 Frobulate --expire-timestamp=1458766277 const char kSampleTokenV2[] = …; const uint8_t kSampleTokenV2Signature[] = …; // This is a good trial token, signed with the above test private key. // Generate this token with the command (in tools/origin_trials): // generate_token.py valid.example.com 3 Frobulate --expire-timestamp=1458766277 const char kSampleTokenV3[] = …; const uint8_t kSampleTokenV3Signature[] = …; // This is a good subdomain trial token, signed with the above test private key. // Generate this token with the command (in tools/origin_trials): // generate_token.py 2 example.com Frobulate --is-subdomain // --expire-timestamp=1458766277 const char kSampleSubdomainToken[] = …; const uint8_t kSampleSubdomainTokenSignature[] = …; // This is a good trial token, explicitly not a subdomain, signed with the above // test private key. Generate this token with the command: // generate_token.py 2 valid.example.com Frobulate --no-subdomain // --expire-timestamp=1458766277 const char kSampleNonSubdomainToken[] = …; const uint8_t kSampleNonSubdomainTokenSignature[] = …; // This is a good third party trial token, signed with the above test private // key. Generate this token with the command (in tools/origin_trials): // generate_token.py 3 example.com Frobulate --is-third-party // --expire-timestamp=1458766277 const char kSampleThirdPartyToken[] = …; const uint8_t kSampleThirdPartyTokenSignature[] = …; // This is a good trial token, explicitly not a third party, signed with the // above test private key. Generate this token with the command: // generate_token.py 3 valid.example.com Frobulate --no-third-party // --expire-timestamp=1458766277 const char kSampleNonThirdPartyToken[] = …; const uint8_t kSampleNonThirdPartyTokenSignature[] = …; // This is a good third party trial token with usage restriction set to subset, // signed with the above test private key. Generate this token with the // command: // generate_token.py valid.example.com Frobulate --version 3 --is-third-party // --expire-timestamp=1458766277 --usage-restriction subset const char kSampleThirdPartyUsageSubsetToken[] = …; const uint8_t kSampleThirdPartyUsageSubsetTokenSignature[] = …; // This is a good third party trial token with usage restriction set to none, // signed with the above test private key. Generate this token with the // command: // generate_token.py valid.example.com Frobulate --version 3 --is-third-party // --expire-timestamp=1458766277 --usage-restriction "" const char kSampleThirdPartyUsageEmptyToken[] = …; const uint8_t kSampleThirdPartyUsageEmptyTokenSignature[] = …; const char kExpectedFeatureName[] = …; // This is an excessively long feature name (100 characters). This is valid, as // there is no explicit limit on feature name length. Excessive refers to the // fact that is very unlikely that a developer would choose such a long name. const char kExpectedLongFeatureName[] = …; const char kExpectedOrigin[] = …; const char kExpectedSubdomainOrigin[] = …; const char kExpectedMultipleSubdomainOrigin[] = …; const uint64_t kExpectedExpiry = …; // The token should not be valid for this origin, or for this feature. const char kInvalidOrigin[] = …; const char kInsecureOrigin[] = …; const char kIncorrectPortOrigin[] = …; const char kIncorrectDomainOrigin[] = …; const char kInvalidTLDOrigin[] = …; const char kInvalidFeatureName[] = …; // The token should be valid if the current time is kValidTimestamp or earlier. double kValidTimestamp = …; // The token should be invalid if the current time is kInvalidTimestamp or // later. double kInvalidTimestamp = …; // Well-formed trial token with an invalid signature. const char kInvalidSignatureToken[] = …; // Trial token truncated in the middle of the length field; too short to // possibly be valid. const char kTruncatedToken[] = …; // Trial token with an incorrectly-declared length, but with a valid signature. const char kIncorrectLengthToken[] = …; // Trial token with a misidentified version (42). const char kIncorrectVersionToken[] = …; const char kSampleTokenJSON[] = …; const char kSampleNonSubdomainTokenJSON[] = …; const char kSampleSubdomainTokenJSON[] = …; const char kUsageEmptyTokenJSON[] = …; const char kUsageSubsetTokenJSON[] = …; const char kSampleNonThirdPartyTokenJSON[] = …; const char kSampleThirdPartyTokenJSON[] = …; const char kSampleThirdPartyTokenUsageSubsetJSON[] = …; const char kSampleThirdPartyTokenUsageEmptyJSON[] = …; // Various ill-formed trial tokens. These should all fail to parse. const char* kInvalidTokens[] = …; const char* kInvalidTokensVersion3[] = …; // Valid token JSON. The feature name matches matches kExpectedLongFeatureName // (100 characters), and the origin is 2048 chars. const char kLargeTokenJSON[] = …; // clang-format on const char kExpectedLongTokenOrigin[] = …; // Valid token JSON, over 4KB in size. The feature name matches // kExpectedLongFeatureName (100 characters), and the origin is 3929 chars. const char kTooLargeTokenJSON[] = …; // clang-format on // Large valid token, size = 3052 chars. The feature name matches // kExpectedLongFeatureName (100 characters), and the origin is 2048 chars. // Generate this token with the command: // generate_token.py --is-subdomain --expire-timestamp=1458766277 \ // 2 https://www.<2027 random chars>.com:9999 \ // ThisTrialNameIs100CharactersLongIncludingPaddingAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA const char kLargeValidToken[] = …; const uint8_t kLargeValidTokenSignature[] = …; // Valid token that is too large, size = 4100 chars. The feature name matches // kExpectedLongFeatureName (100 characters), and the origin is 2833 chars. // Generate this token with the command: // generate_token.py --is-subdomain --expire-timestamp=1458766277 \ // 2 https://www.<4348 random chars>.com:9999 \ // ThisTrialNameIs100CharactersLongIncludingPaddingAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA const char kTooLargeValidToken[] = …; } // namespace class TrialTokenTest : public testing::Test { … }; // Test the extraction of the signed payload from token strings. This includes // checking the included version identifier, payload length, and cryptographic // signature. TEST_F(TrialTokenTest, ExtractValidSignatureVersion2) { … } TEST_F(TrialTokenTest, ExtractValidSignatureVersion3) { … } TEST_F(TrialTokenTest, ExtractSubdomainValidSignature) { … } TEST_F(TrialTokenTest, ExtractNonSubdomainValidSignature) { … } TEST_F(TrialTokenTest, ExtractThirdPartyValidSignature) { … } TEST_F(TrialTokenTest, ExtractNonThirdPartyValidSignature) { … } TEST_F(TrialTokenTest, ExtractThirdPartyUsageEmptyValidSignature) { … } TEST_F(TrialTokenTest, ExtractThirdPartyUsageSubsetValidSignature) { … } TEST_F(TrialTokenTest, ExtractInvalidSignature) { … } TEST_F(TrialTokenTest, ExtractSignatureWithIncorrectKey) { … } TEST_F(TrialTokenTest, ExtractEmptyToken) { … } TEST_F(TrialTokenTest, ExtractShortToken) { … } TEST_F(TrialTokenTest, ExtractUnsupportedVersion) { … } TEST_F(TrialTokenTest, ExtractSignatureWithIncorrectLength) { … } TEST_F(TrialTokenTest, ExtractLargeToken) { … } TEST_F(TrialTokenTest, ExtractTooLargeToken) { … } // Test parsing of fields from JSON token. class TrialTokenParseInvalidTest : public TrialTokenTest, public testing::WithParamInterface<std::tuple<const char*, uint8_t>> { … }; TEST_P(TrialTokenParseInvalidTest, ParseInvalidString) { … } INSTANTIATE_TEST_SUITE_P(…); class TrialTokenParseInvalidVersion3Test : public TrialTokenTest, public testing::WithParamInterface<const char*> { … }; TEST_P(TrialTokenParseInvalidVersion3Test, ParseInvalidString) { … } INSTANTIATE_TEST_SUITE_P(…); // Test parsing of fields from JSON token. class TrialTokenParseTest : public TrialTokenTest, public testing::WithParamInterface<uint8_t> { … }; TEST_P(TrialTokenParseTest, ParseValidToken) { … } TEST_P(TrialTokenParseTest, ParseValidNonSubdomainToken) { … } TEST_P(TrialTokenParseTest, ParseValidSubdomainToken) { … } TEST_P(TrialTokenParseTest, ParseValidLargeToken) { … } TEST_P(TrialTokenParseTest, ParseTooLargeToken) { … } TEST_P(TrialTokenParseTest, ValidateValidToken) { … } TEST_P(TrialTokenParseTest, ValidateValidSubdomainToken) { … } TEST_P(TrialTokenParseTest, TokenIsValid) { … } TEST_P(TrialTokenParseTest, SubdomainTokenIsValid) { … } INSTANTIATE_TEST_SUITE_P(…); TEST_F(TrialTokenTest, ParseValidNonThirdPartyToken) { … } TEST_F(TrialTokenTest, ParseValidThirdPartyToken) { … } TEST_F(TrialTokenTest, ParseValidThirdPartyTokenInvalidVersion) { … } TEST_F(TrialTokenTest, ParseValidUsageEmptyToken) { … } TEST_F(TrialTokenTest, ParseValidUsageSubsetToken) { … } TEST_F(TrialTokenTest, ParseValidThirdPartyUsageSubsetToken) { … } TEST_F(TrialTokenTest, ParseValidThirdPartyUsageEmptyToken) { … } // Test overall extraction and parsing, to ensure output status matches returned // token, and signature is provided. // Test Version 2. TEST_F(TrialTokenTest, FromValidToken) { … } TEST_F(TrialTokenTest, FromInvalidSignature) { … } // Test Version 3. TEST_F(TrialTokenTest, FromValidTokenVersion3) { … } TEST_F(TrialTokenTest, FromInvalidSignatureVersion3) { … } TEST_F(TrialTokenTest, FromMalformedToken) { … } } // namespace blink