1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <string>
6 
7 #include "base/time/time.h"
8 #include "net/tools/transport_security_state_generator/input_file_parsers.h"
9 #include "net/tools/transport_security_state_generator/pinsets.h"
10 #include "net/tools/transport_security_state_generator/transport_security_state_entry.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 
13 namespace net::transport_security_state {
14 
15 namespace {
16 
17 // Test that all values are correctly parsed from a valid JSON input.
TEST(InputFileParsersTest,ParseJSON)18 TEST(InputFileParsersTest, ParseJSON) {
19   std::string valid_hsts =
20       "{"
21       "  \"entries\": ["
22       "    {"
23       "      \"name\": \"hsts.example.com\","
24       "      \"policy\": \"test\","
25       "      \"mode\": \"force-https\", "
26       "      \"include_subdomains\": true"
27       "    }, {"
28       "      \"name\": \"hsts-no-subdomains.example.com\","
29       "      \"policy\": \"test\","
30       "      \"mode\": \"force-https\", "
31       "      \"include_subdomains\": false"
32       "    }, {"
33       "      \"name\": \"hpkp.example.com\","
34       "      \"policy\": \"test\""
35       "    }, {"
36       "      \"name\": \"hpkp-no-subdomains.example.com\","
37       "      \"policy\": \"test\""
38       "    }"
39       "  ]"
40       "}";
41 
42   std::string valid_pinning =
43       "{"
44       "  \"pinsets\": [{"
45       "      \"name\": \"test\","
46       "      \"static_spki_hashes\": [\"TestSPKI\"],"
47       "      \"bad_static_spki_hashes\": [\"BadTestSPKI\"],"
48       "      \"report_uri\": \"https://hpkp-log.example.com\""
49       "  }],"
50       "  \"entries\": ["
51       "    {"
52       "      \"name\": \"hpkp.example.com\","
53       "      \"pins\": \"thepinset\","
54       "      \"include_subdomains\": true"
55       "    }, {"
56       "      \"name\": \"hpkp-no-subdomains.example.com\","
57       "      \"pins\": \"thepinset2\", "
58       "      \"include_subdomains\": false"
59       "    }, {"
60       "      \"name\": \"hpkp-no-hsts.example.com\","
61       "      \"pins\": \"test\", "
62       "      \"include_subdomains\": true"
63       "    }"
64       "  ]"
65       "}";
66 
67   TransportSecurityStateEntries entries;
68   Pinsets pinsets;
69 
70   EXPECT_TRUE(ParseJSON(valid_hsts, valid_pinning, &entries, &pinsets));
71 
72   ASSERT_EQ(1U, pinsets.size());
73   auto pinset = pinsets.pinsets().find("test");
74   ASSERT_NE(pinset, pinsets.pinsets().cend());
75   EXPECT_EQ("test", pinset->second->name());
76   EXPECT_EQ("https://hpkp-log.example.com", pinset->second->report_uri());
77 
78   ASSERT_EQ(1U, pinset->second->static_spki_hashes().size());
79   EXPECT_EQ("TestSPKI", pinset->second->static_spki_hashes()[0]);
80 
81   ASSERT_EQ(1U, pinset->second->bad_static_spki_hashes().size());
82   EXPECT_EQ("BadTestSPKI", pinset->second->bad_static_spki_hashes()[0]);
83 
84   ASSERT_EQ(5U, entries.size());
85   TransportSecurityStateEntry* entry = entries[0].get();
86   EXPECT_EQ("hsts.example.com", entry->hostname);
87   EXPECT_TRUE(entry->force_https);
88   EXPECT_TRUE(entry->include_subdomains);
89   EXPECT_FALSE(entry->hpkp_include_subdomains);
90   EXPECT_EQ("", entry->pinset);
91 
92   entry = entries[1].get();
93   EXPECT_EQ("hsts-no-subdomains.example.com", entry->hostname);
94   EXPECT_TRUE(entry->force_https);
95   EXPECT_FALSE(entry->include_subdomains);
96   EXPECT_FALSE(entry->hpkp_include_subdomains);
97   EXPECT_EQ("", entry->pinset);
98 
99   entry = entries[2].get();
100   EXPECT_EQ("hpkp.example.com", entry->hostname);
101   EXPECT_FALSE(entry->force_https);
102   EXPECT_FALSE(entry->include_subdomains);
103   EXPECT_TRUE(entry->hpkp_include_subdomains);
104   EXPECT_EQ("thepinset", entry->pinset);
105 
106   entry = entries[3].get();
107   EXPECT_EQ("hpkp-no-subdomains.example.com", entry->hostname);
108   EXPECT_FALSE(entry->force_https);
109   EXPECT_FALSE(entry->include_subdomains);
110   EXPECT_FALSE(entry->hpkp_include_subdomains);
111   EXPECT_EQ("thepinset2", entry->pinset);
112 
113   entry = entries[4].get();
114   EXPECT_EQ("hpkp-no-hsts.example.com", entry->hostname);
115   EXPECT_FALSE(entry->force_https);
116   EXPECT_FALSE(entry->include_subdomains);
117   EXPECT_TRUE(entry->hpkp_include_subdomains);
118   EXPECT_EQ("test", entry->pinset);
119 }
120 
121 // Test that parsing valid JSON with missing keys fails.
TEST(InputFileParsersTest,ParseJSONInvalid)122 TEST(InputFileParsersTest, ParseJSONInvalid) {
123   TransportSecurityStateEntries entries;
124   Pinsets pinsets;
125 
126   std::string no_pinsets =
127       "{"
128       "  \"entries\": []"
129       "}";
130 
131   EXPECT_FALSE(ParseJSON(no_pinsets, "", &entries, &pinsets));
132 
133   std::string no_entries =
134       "{"
135       "  \"pinsets\": []"
136       "}";
137 
138   EXPECT_FALSE(ParseJSON("", no_entries, &entries, &pinsets));
139 
140   std::string missing_hostname =
141       "{"
142       "  \"entries\": ["
143       "    {"
144       "      \"policy\": \"test\","
145       "      \"mode\": \"force-https\""
146       "    }"
147       "  ]"
148       "}";
149 
150   EXPECT_FALSE(ParseJSON(missing_hostname, "", &entries, &pinsets));
151 
152   std::string missing_policy =
153       "{"
154       "  \"entries\": ["
155       "    {"
156       "      \"name\": \"example.test\","
157       "      \"mode\": \"force-https\""
158       "    }"
159       "  ]"
160       "}";
161 
162   EXPECT_FALSE(ParseJSON(missing_policy, "", &entries, &pinsets));
163 }
164 
165 // Test that parsing valid JSON with an invalid (HPKP) pinset fails.
TEST(InputFileParsersTest,ParseJSONInvalidPinset)166 TEST(InputFileParsersTest, ParseJSONInvalidPinset) {
167   TransportSecurityStateEntries entries;
168   Pinsets pinsets;
169 
170   std::string missing_pinset_name =
171       "{"
172       "  \"pinsets\": [{"
173       "      \"static_spki_hashes\": [\"TestSPKI\"],"
174       "      \"bad_static_spki_hashes\": [\"BadTestSPKI\"],"
175       "      \"report_uri\": \"https://hpkp-log.example.com\""
176       "  }],"
177       "  \"entries\": []"
178       "}";
179 
180   EXPECT_FALSE(ParseJSON("", missing_pinset_name, &entries, &pinsets));
181 }
182 
183 // Test that parsing valid JSON containing an entry with an invalid mode fails.
TEST(InputFileParsersTest,ParseJSONInvalidMode)184 TEST(InputFileParsersTest, ParseJSONInvalidMode) {
185   TransportSecurityStateEntries entries;
186   Pinsets pinsets;
187 
188   std::string invalid_mode =
189       "{"
190       "  \"entries\": ["
191       "    {"
192       "      \"name\": \"preloaded.test\","
193       "      \"policy\": \"test\","
194       "      \"mode\": \"something-invalid\""
195       "    }"
196       "  ]"
197       "}";
198 
199   EXPECT_FALSE(ParseJSON(invalid_mode, "", &entries, &pinsets));
200 }
201 
202 // Test that parsing valid JSON containing an entry with an unknown field fails.
TEST(InputFileParsersTest,ParseJSONUnkownField)203 TEST(InputFileParsersTest, ParseJSONUnkownField) {
204   TransportSecurityStateEntries entries;
205   Pinsets pinsets;
206 
207   std::string unknown_field =
208       "{"
209       "  \"entries\": ["
210       "    {"
211       "      \"name\": \"preloaded.test\","
212       "      \"policy\": \"test\","
213       "      \"unknown_key\": \"value\""
214       "    }"
215       "  ]"
216       "}";
217 
218   EXPECT_FALSE(ParseJSON(unknown_field, "", &entries, &pinsets));
219 }
220 
221 // Test that parsing valid JSON containing an entry with an unknown policy
222 // fails.
TEST(InputFileParsersTest,ParseJSONUnkownPolicy)223 TEST(InputFileParsersTest, ParseJSONUnkownPolicy) {
224   TransportSecurityStateEntries entries;
225   Pinsets pinsets;
226 
227   std::string unknown_policy =
228       "{"
229       "  \"entries\": ["
230       "    {"
231       "      \"name\": \"preloaded.test\","
232       "      \"policy\": \"invalid\""
233       "    }"
234       "  ]"
235       "}";
236 
237   EXPECT_FALSE(ParseJSON(unknown_policy, "", &entries, &pinsets));
238 }
239 
240 // Test parsing of all 3 SPKI formats.
TEST(InputFileParsersTest,ParseCertificatesFile)241 TEST(InputFileParsersTest, ParseCertificatesFile) {
242   std::string valid =
243       "# This line should ignored. The rest should result in 3 pins.\n"
244       "PinsListTimestamp\n"
245       "1649894400\n"
246       "TestPublicKey1\n"
247       "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"
248       "\n"
249       "TestPublicKey2\n"
250       "-----BEGIN PUBLIC KEY-----\n"
251       "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAujzwcb5bJuC/A/Y9izGl\n"
252       "LlA3fnKGbeyn53BdVznJN4fQwU82WKVYdqt8d/1ZDRdYyhGrTgXJeCURe9VSJyX1\n"
253       "X2a5EApSFsopP8Yjy0Rl6dNOLO84KCW9dPmfHC3uP0ac4hnHT5dUr05YvhJmHCkf\n"
254       "as6v/aEgpPLDhRF6UruSUh+gIpUg/F3+vlD99HLfbloukoDtQyxW+86s9sO7RQ00\n"
255       "pd79VOoa/v09FvoS7MFgnBBOtvBQLOXjEH7/qBsnrXFtHBeOtxSLar/FL3OhVXuh\n"
256       "dUTRyc1Mg0ECtz8zHZugW+LleIm5Bf5Yr0bN1O/HfDPCkDaCldcm6xohEHn9pBaW\n"
257       "+wIDAQAB\n"
258       "-----END PUBLIC KEY-----\n"
259       "\n"
260       "# The 'Chromium' prefix is required here.\n"
261       "ChromiumTestCertificate3\n"
262       "-----BEGIN CERTIFICATE-----\n"
263       "MIIDeTCCAmGgAwIBAgIJAMRHXuiAgufAMA0GCSqGSIb3DQEBCwUAMFMxETAPBgNV\n"
264       "BAMMCENocm9taXVtMR4wHAYDVQQKDBVUaGUgQ2hyb21pdW0gUHJvamVjdHMxETAP\n"
265       "BgNVBAsMCFNlY3VyaXR5MQswCQYDVQQGEwJVUzAeFw0xNzAyMDExOTAyMzFaFw0x\n"
266       "ODAyMDExOTAyMzFaMFMxETAPBgNVBAMMCENocm9taXVtMR4wHAYDVQQKDBVUaGUg\n"
267       "Q2hyb21pdW0gUHJvamVjdHMxETAPBgNVBAsMCFNlY3VyaXR5MQswCQYDVQQGEwJV\n"
268       "UzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALtggpf5vNVsmQrJKTQe\n"
269       "ynTeOzVOyROGDugGtR+Cri8WlNg1UAlIyYIS8txZ4oCknsT8gs3TFfu0wxmWNxx5\n"
270       "4oLGy2BQOHH00dgBAsKgqX//mY4mH5AZ85UFYni1hj9aszIJMIBWtgbNGVkppW65\n"
271       "8maF1KVdHmxXMvtKxn/9UsusH/A0ng5UJDYBPISQMv0XqIlv0wdVTIVWIcQhOjWz\n"
272       "MGwFDSjxS1WgEnPgd4Qi7MYaDbUTsXGtWba83vZJ8CQzjLumSJJCnz2aquGmraX0\n"
273       "J0joUjB4fuYL8xrbDqnFmADvozMMVkZ4843w8ikvJkM8nWoIXexVvirfXDoqtdUo\n"
274       "YOcCAwEAAaNQME4wHQYDVR0OBBYEFGJ6O/oLtzpb4OWvrEFxieYb1JbsMB8GA1Ud\n"
275       "IwQYMBaAFGJ6O/oLtzpb4OWvrEFxieYb1JbsMAwGA1UdEwQFMAMBAf8wDQYJKoZI\n"
276       "hvcNAQELBQADggEBAFpt9jlBT6OsfKFAJZnmExbW8JlsqXOJAaR+nD1XOnp6o+DM\n"
277       "NIguj9+wJOW34OM+2Om0n+KMYbDER0p4g3gxoaDblu7otgnC0OTOnx3DPUYab0jr\n"
278       "uT6O4C3/nfWW5sl3Ni3Y99dmdcqKcmYkHsr7uADLPWsjb+sfUrQQfHHnPwzyUz/A\n"
279       "w4rSJ0wxnLOmjk5F5YHMLkNpPrzFA1mFyGIau7THsRIr3B632MLNcOlNR21nOc7i\n"
280       "eB4u+OzpcZXuiQg3bqrNp6Xb70OIW1rfNEiCpps4UZyRnZ/nrzByxeHH5zPWWZk9\n"
281       "nZtxI+65PFOekOjBpbnRC8v1CfOmUSVKIqWaPys=\n"
282       "-----END CERTIFICATE-----";
283 
284   Pinsets pinsets;
285   base::Time timestamp;
286 
287   base::Time expected_timestamp;
288   ASSERT_TRUE(
289       base::Time::FromUTCString("2022-04-14T00:00:00Z", &expected_timestamp));
290 
291   EXPECT_TRUE(ParseCertificatesFile(valid, &pinsets, &timestamp));
292 
293   EXPECT_EQ(3U, pinsets.spki_size());
294 
295   EXPECT_EQ(timestamp, expected_timestamp);
296 
297   const SPKIHashMap& hashes = pinsets.spki_hashes();
298   EXPECT_NE(hashes.cend(), hashes.find("TestPublicKey1"));
299   EXPECT_NE(hashes.cend(), hashes.find("TestPublicKey2"));
300   EXPECT_NE(hashes.cend(), hashes.find("ChromiumTestCertificate3"));
301 }
302 
TEST(InputFileParsersTest,ParseCertificatesFileInvalid)303 TEST(InputFileParsersTest, ParseCertificatesFileInvalid) {
304   Pinsets pinsets;
305   base::Time unused;
306 
307   std::string invalid =
308       "PinsListTimestamp\n"
309       "1649894400\n"
310       "TestName\n"
311       "unexpected";
312   EXPECT_FALSE(ParseCertificatesFile(invalid, &pinsets, &unused));
313 }
314 
315 // Test that parsing invalid certificate names fails.
TEST(InputFileParsersTest,ParseCertificatesFileInvalidName)316 TEST(InputFileParsersTest, ParseCertificatesFileInvalidName) {
317   Pinsets pinsets;
318   base::Time unused;
319 
320   std::string invalid_name_small_character =
321       "PinsListTimestamp\n"
322       "1649894400\n"
323       "startsWithSmallLetter\n"
324       "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n";
325   EXPECT_FALSE(
326       ParseCertificatesFile(invalid_name_small_character, &pinsets, &unused));
327 
328   std::string invalid_name_invalid_characters =
329       "PinsListTimestamp\n"
330       "1649894400\n"
331       "Invalid-Characters-In-Name\n"
332       "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n";
333   EXPECT_FALSE(ParseCertificatesFile(invalid_name_invalid_characters, &pinsets,
334                                      &unused));
335 
336   std::string invalid_name_number =
337       "PinsListTimestamp\n"
338       "1649894400\n"
339       "1InvalidName\n"
340       "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n";
341   EXPECT_FALSE(ParseCertificatesFile(invalid_name_number, &pinsets, &unused));
342 
343   std::string invalid_name_space =
344       "PinsListTimestamp\n"
345       "1649894400\n"
346       "Invalid Name\n"
347       "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n";
348   EXPECT_FALSE(ParseCertificatesFile(invalid_name_space, &pinsets, &unused));
349 }
350 
351 // Test that parsing of a certificate with an incomplete or incorrect name
352 // fails.
TEST(InputFileParsersTest,ParseCertificatesFileInvalidCertificateName)353 TEST(InputFileParsersTest, ParseCertificatesFileInvalidCertificateName) {
354   Pinsets pinsets;
355   base::Time unused;
356   std::string timestamp_prefix =
357       "PinsListTimestamp\n"
358       "1649894400\n";
359   std::string certificate =
360       "-----BEGIN CERTIFICATE-----\n"
361       "MIIDIzCCAgugAwIBAgIJALs84KlxWh4GMA0GCSqGSIb3DQEBCwUAMCgxGTAXBgNV\n"
362       "BAoMEENocm9taXVtIENsYXNzIDMxCzAJBgNVBAsMAkcxMB4XDTE3MDIwMTE5NTUw\n"
363       "NVoXDTE4MDIwMTE5NTUwNVowKDEZMBcGA1UECgwQQ2hyb21pdW0gQ2xhc3MgMzEL\n"
364       "MAkGA1UECwwCRzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkolrR\n"
365       "7gCPm22Cc9psS2Jh1mksVneee5ntEezZ2gEU20y9Z9URBReo8SFvaZcgKkAkca1v\n"
366       "552YIG+FBO/u8njxzlHXvuVJ5x2geciqqR4TRhA4jO1ndrNW6nlJfOoYueWbdym3\n"
367       "8zwugoULoCtyLyzdiMI5g8iVBQHDh8+K3TZIHar3HS49TjX5u5nv4igO4RfDcFUa\n"
368       "h8g+6x5nWoFF8oa3FG0YTN+q6iI1i2JHmj/q03fVPv3WLPGJ3JADau9gO1Lw1/qf\n"
369       "R/N3l4MVtjDFFGYzclfqW2UmL6zRirEV0GF2gwSBAGVX3WWhpOcM8rFIWYkZCsI5\n"
370       "iUdtwFNBfcKS9sNpAgMBAAGjUDBOMB0GA1UdDgQWBBTm4VJfibducqwb9h4XELn3\n"
371       "p6zLVzAfBgNVHSMEGDAWgBTm4VJfibducqwb9h4XELn3p6zLVzAMBgNVHRMEBTAD\n"
372       "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQApTm40RfsZG20IIgWJ62pZ2end/lvaneTh\n"
373       "MZSgFnoTRjKkd/5dh22YyKPw9PnpIuiyi85L36COreqZUvbxqRQnpL1oSCRlLBJQ\n"
374       "2LcGlF0j0Opa+SY2VWup4XjnYF8CvwMl4obNpSuywTFmkXCRxzN23tn8whNHvWHM\n"
375       "BQ7abw8X1KY02uPbHucrpou6KXkKkhyhfML8OD8IRkSM56K6YyedqV97cmEdW0Ie\n"
376       "LlpFJQVX13bmojtSNI1zaiCiEenn5xLa/dAlyFT18Mq6y8plioBinVWFYd0qcRoA\n"
377       "E2j3m+jTVIv3CZ+ivGxggZQ8ZYN8FJ/iTW3pXGojogHh0NRJJ8dM\n"
378       "-----END CERTIFICATE-----";
379 
380   std::string missing_prefix =
381       timestamp_prefix + "Class3_G1_Test\n" + certificate;
382   EXPECT_FALSE(ParseCertificatesFile(missing_prefix, &pinsets, &unused));
383 
384   std::string missing_class =
385       timestamp_prefix + "Chromium_G1_Test\n" + certificate;
386   EXPECT_FALSE(ParseCertificatesFile(missing_class, &pinsets, &unused));
387 
388   std::string missing_number =
389       timestamp_prefix + "Chromium_Class3_Test\n" + certificate;
390   EXPECT_FALSE(ParseCertificatesFile(missing_number, &pinsets, &unused));
391 
392   std::string valid =
393       timestamp_prefix + "Chromium_Class3_G1_Test\n" + certificate;
394   EXPECT_TRUE(ParseCertificatesFile(valid, &pinsets, &unused));
395 }
396 
397 // Tests that parsing a certificate with a missing or incorrect timestamp fails.
TEST(InputFileParsersTest,ParseCertificatesFileInvalidTimestamp)398 TEST(InputFileParsersTest, ParseCertificatesFileInvalidTimestamp) {
399   Pinsets pinsets;
400   base::Time unused;
401   std::string timestamp_prefix =
402       "PinsListTimestamp\n"
403       "1649894400\n";
404   std::string bad_timestamp_prefix =
405       "PinsListTimestamp\n"
406       "NotReallyTimestamp\n";
407   std::string certificate =
408       "Chromium_Class3_G1_Test\n"
409       "-----BEGIN CERTIFICATE-----\n"
410       "MIIDIzCCAgugAwIBAgIJALs84KlxWh4GMA0GCSqGSIb3DQEBCwUAMCgxGTAXBgNV\n"
411       "BAoMEENocm9taXVtIENsYXNzIDMxCzAJBgNVBAsMAkcxMB4XDTE3MDIwMTE5NTUw\n"
412       "NVoXDTE4MDIwMTE5NTUwNVowKDEZMBcGA1UECgwQQ2hyb21pdW0gQ2xhc3MgMzEL\n"
413       "MAkGA1UECwwCRzEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkolrR\n"
414       "7gCPm22Cc9psS2Jh1mksVneee5ntEezZ2gEU20y9Z9URBReo8SFvaZcgKkAkca1v\n"
415       "552YIG+FBO/u8njxzlHXvuVJ5x2geciqqR4TRhA4jO1ndrNW6nlJfOoYueWbdym3\n"
416       "8zwugoULoCtyLyzdiMI5g8iVBQHDh8+K3TZIHar3HS49TjX5u5nv4igO4RfDcFUa\n"
417       "h8g+6x5nWoFF8oa3FG0YTN+q6iI1i2JHmj/q03fVPv3WLPGJ3JADau9gO1Lw1/qf\n"
418       "R/N3l4MVtjDFFGYzclfqW2UmL6zRirEV0GF2gwSBAGVX3WWhpOcM8rFIWYkZCsI5\n"
419       "iUdtwFNBfcKS9sNpAgMBAAGjUDBOMB0GA1UdDgQWBBTm4VJfibducqwb9h4XELn3\n"
420       "p6zLVzAfBgNVHSMEGDAWgBTm4VJfibducqwb9h4XELn3p6zLVzAMBgNVHRMEBTAD\n"
421       "AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQApTm40RfsZG20IIgWJ62pZ2end/lvaneTh\n"
422       "MZSgFnoTRjKkd/5dh22YyKPw9PnpIuiyi85L36COreqZUvbxqRQnpL1oSCRlLBJQ\n"
423       "2LcGlF0j0Opa+SY2VWup4XjnYF8CvwMl4obNpSuywTFmkXCRxzN23tn8whNHvWHM\n"
424       "BQ7abw8X1KY02uPbHucrpou6KXkKkhyhfML8OD8IRkSM56K6YyedqV97cmEdW0Ie\n"
425       "LlpFJQVX13bmojtSNI1zaiCiEenn5xLa/dAlyFT18Mq6y8plioBinVWFYd0qcRoA\n"
426       "E2j3m+jTVIv3CZ+ivGxggZQ8ZYN8FJ/iTW3pXGojogHh0NRJJ8dM\n"
427       "-----END CERTIFICATE-----";
428 
429   std::string missing_timestamp = certificate;
430   EXPECT_FALSE(ParseCertificatesFile(certificate, &pinsets, &unused));
431 
432   std::string incorrect_timestamp = bad_timestamp_prefix + certificate;
433   EXPECT_FALSE(ParseCertificatesFile(incorrect_timestamp, &pinsets, &unused));
434 
435   std::string multiple_timestamp =
436       timestamp_prefix + timestamp_prefix + certificate;
437   EXPECT_FALSE(ParseCertificatesFile(multiple_timestamp, &pinsets, &unused));
438 
439   std::string valid = timestamp_prefix + certificate;
440   EXPECT_TRUE(ParseCertificatesFile(valid, &pinsets, &unused));
441 }
442 
443 }  // namespace
444 
445 }  // namespace net::transport_security_state
446