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, ×tamp));
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