1 // Copyright 2014 The Chromium Authors. All rights reserved.
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 "third_party/blink/renderer/platform/loader/subresource_integrity.h"
6 
7 #include "base/memory/scoped_refptr.h"
8 #include "base/stl_util.h"
9 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/renderer/platform/crypto.h"
12 #include "third_party/blink/renderer/platform/loader/fetch/integrity_metadata.h"
13 #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
14 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
15 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
16 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
17 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
18 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
19 #include "third_party/blink/renderer/platform/loader/testing/mock_fetch_context.h"
20 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
21 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
22 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
23 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
24 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
25 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
26 #include "third_party/blink/renderer/platform/wtf/vector.h"
27 
28 #include <algorithm>
29 
30 namespace blink {
31 
32 static const char kBasicScript[] = "alert('test');";
33 static const char kSha256Integrity[] =
34     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
35 static const char kSha256IntegrityLenientSyntax[] =
36     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=";
37 static const char kSha256IntegrityWithEmptyOption[] =
38     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?";
39 static const char kSha256IntegrityWithOption[] =
40     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar";
41 static const char kSha256IntegrityWithOptions[] =
42     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?foo=bar?baz=foz";
43 static const char kSha256IntegrityWithMimeOption[] =
44     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4=?ct=application/"
45     "javascript";
46 static const char kSha384Integrity[] =
47     "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
48 static const char kSha512Integrity[] =
49     "sha512-TXkJw18PqlVlEUXXjeXbGetop1TKB3wYQIp1_"
50     "ihxCOFGUfG9TYOaA1MlkpTAqSV6yaevLO8Tj5pgH1JmZ--ItA==";
51 static const char kSha384IntegrityLabeledAs256[] =
52     "sha256-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
53 static const char kSha256AndSha384Integrities[] =
54     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= "
55     "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
56 static const char kBadSha256AndGoodSha384Integrities[] =
57     "sha256-deadbeef "
58     "sha384-nep3XpvhUxpCMOVXIFPecThAqdY_uVeiD4kXSqXpx0YJUWU4fTTaFgciTuZk7fmE";
59 static const char kGoodSha256AndBadSha384Integrities[] =
60     "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4= sha384-deadbeef";
61 static const char kBadSha256AndBadSha384Integrities[] =
62     "sha256-deadbeef sha384-deadbeef";
63 static const char kUnsupportedHashFunctionIntegrity[] =
64     "sha1-JfLW308qMPKfb4DaHpUBEESwuPc=";
65 
66 class SubresourceIntegrityTest : public testing::Test {
67  public:
SubresourceIntegrityTest()68   SubresourceIntegrityTest()
69       : sec_url("https://example.test:443"),
70         insec_url("http://example.test:80"),
71         context(MakeGarbageCollected<MockFetchContext>()) {}
72 
73  protected:
Features() const74   SubresourceIntegrity::IntegrityFeatures Features() const {
75     return RuntimeEnabledFeatures::SignatureBasedIntegrityEnabledByRuntimeFlag()
76                ? SubresourceIntegrity::IntegrityFeatures::kSignatures
77                : SubresourceIntegrity::IntegrityFeatures::kDefault;
78   }
79 
ExpectAlgorithm(const String & text,IntegrityAlgorithm expected_algorithm)80   void ExpectAlgorithm(const String& text,
81                        IntegrityAlgorithm expected_algorithm) {
82     Vector<UChar> characters;
83     text.AppendTo(characters);
84     const UChar* position = characters.data();
85     const UChar* end = characters.end();
86     IntegrityAlgorithm algorithm;
87 
88     EXPECT_EQ(SubresourceIntegrity::kAlgorithmValid,
89               SubresourceIntegrity::ParseAttributeAlgorithm(
90                   position, end, Features(), algorithm));
91     EXPECT_EQ(expected_algorithm, algorithm);
92     EXPECT_EQ(end, position);
93   }
94 
ExpectAlgorithmFailure(const String & text,SubresourceIntegrity::AlgorithmParseResult expected_result)95   void ExpectAlgorithmFailure(
96       const String& text,
97       SubresourceIntegrity::AlgorithmParseResult expected_result) {
98     Vector<UChar> characters;
99     text.AppendTo(characters);
100     const UChar* position = characters.data();
101     const UChar* begin = characters.data();
102     const UChar* end = characters.end();
103     IntegrityAlgorithm algorithm;
104 
105     EXPECT_EQ(expected_result, SubresourceIntegrity::ParseAttributeAlgorithm(
106                                    position, end, Features(), algorithm));
107     EXPECT_EQ(begin, position);
108   }
109 
ExpectDigest(const String & text,const char * expected_digest)110   void ExpectDigest(const String& text, const char* expected_digest) {
111     Vector<UChar> characters;
112     text.AppendTo(characters);
113     const UChar* position = characters.data();
114     const UChar* end = characters.end();
115     String digest;
116 
117     EXPECT_TRUE(SubresourceIntegrity::ParseDigest(position, end, digest));
118     EXPECT_EQ(expected_digest, digest);
119   }
120 
ExpectDigestFailure(const String & text)121   void ExpectDigestFailure(const String& text) {
122     Vector<UChar> characters;
123     text.AppendTo(characters);
124     const UChar* position = characters.data();
125     const UChar* end = characters.end();
126     String digest;
127 
128     EXPECT_FALSE(SubresourceIntegrity::ParseDigest(position, end, digest));
129     EXPECT_TRUE(digest.IsEmpty());
130   }
131 
ExpectParse(const char * integrity_attribute,const char * expected_digest,IntegrityAlgorithm expected_algorithm)132   void ExpectParse(const char* integrity_attribute,
133                    const char* expected_digest,
134                    IntegrityAlgorithm expected_algorithm) {
135     IntegrityMetadataSet metadata_set;
136 
137     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
138               SubresourceIntegrity::ParseIntegrityAttribute(
139                   integrity_attribute, Features(), metadata_set));
140     EXPECT_EQ(1u, metadata_set.size());
141     if (metadata_set.size() > 0) {
142       IntegrityMetadata metadata = *metadata_set.begin();
143       EXPECT_EQ(expected_digest, metadata.Digest());
144       EXPECT_EQ(expected_algorithm, metadata.Algorithm());
145     }
146   }
147 
ExpectParseMultipleHashes(const char * integrity_attribute,const IntegrityMetadata expected_metadata_array[],size_t expected_metadata_array_size)148   void ExpectParseMultipleHashes(
149       const char* integrity_attribute,
150       const IntegrityMetadata expected_metadata_array[],
151       size_t expected_metadata_array_size) {
152     IntegrityMetadataSet expected_metadata_set;
153     for (size_t i = 0; i < expected_metadata_array_size; i++) {
154       expected_metadata_set.insert(expected_metadata_array[i].ToPair());
155     }
156     IntegrityMetadataSet metadata_set;
157     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
158               SubresourceIntegrity::ParseIntegrityAttribute(
159                   integrity_attribute, Features(), metadata_set));
160     EXPECT_TRUE(
161         IntegrityMetadata::SetsEqual(expected_metadata_set, metadata_set));
162   }
163 
ExpectParseFailure(const char * integrity_attribute)164   void ExpectParseFailure(const char* integrity_attribute) {
165     IntegrityMetadataSet metadata_set;
166 
167     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseNoValidResult,
168               SubresourceIntegrity::ParseIntegrityAttribute(
169                   integrity_attribute, Features(), metadata_set));
170   }
171 
ExpectEmptyParseResult(const char * integrity_attribute)172   void ExpectEmptyParseResult(const char* integrity_attribute) {
173     IntegrityMetadataSet metadata_set;
174 
175     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
176               SubresourceIntegrity::ParseIntegrityAttribute(
177                   integrity_attribute, Features(), metadata_set));
178     EXPECT_EQ(0u, metadata_set.size());
179   }
180 
181   enum ServiceWorkerMode {
182     kNoServiceWorker,
183     kSWOpaqueResponse,
184     kSWClearResponse
185   };
186 
187   enum Expectation { kIntegritySuccess, kIntegrityFailure };
188 
189   struct TestCase {
190     const KURL url;
191     network::mojom::RequestMode request_mode;
192     network::mojom::FetchResponseType response_type;
193     const Expectation expectation;
194   };
195 
CheckExpectedIntegrity(const char * integrity,const TestCase & test)196   void CheckExpectedIntegrity(const char* integrity, const TestCase& test) {
197     CheckExpectedIntegrity(integrity, test, test.expectation);
198   }
199 
200   // Allows to overwrite the test expectation for cases that are always expected
201   // to fail:
CheckExpectedIntegrity(const char * integrity,const TestCase & test,Expectation expectation)202   void CheckExpectedIntegrity(const char* integrity,
203                               const TestCase& test,
204                               Expectation expectation) {
205     IntegrityMetadataSet metadata_set;
206     EXPECT_EQ(SubresourceIntegrity::kIntegrityParseValidResult,
207               SubresourceIntegrity::ParseIntegrityAttribute(
208                   String(integrity), Features(), metadata_set));
209 
210     SubresourceIntegrity::ReportInfo report_info;
211     EXPECT_EQ(expectation == kIntegritySuccess,
212               SubresourceIntegrity::CheckSubresourceIntegrity(
213                   metadata_set, kBasicScript, strlen(kBasicScript), test.url,
214                   *CreateTestResource(test.url, test.request_mode,
215                                       test.response_type),
216                   report_info));
217   }
218 
CreateTestResource(const KURL & url,network::mojom::RequestMode request_mode,network::mojom::FetchResponseType response_type)219   Resource* CreateTestResource(
220       const KURL& url,
221       network::mojom::RequestMode request_mode,
222       network::mojom::FetchResponseType response_type) {
223     ResourceRequest request;
224     request.SetUrl(url);
225     request.SetMode(request_mode);
226     request.SetRequestorOrigin(SecurityOrigin::CreateUniqueOpaque());
227     Resource* resource =
228         RawResource::CreateForTest(request, ResourceType::kRaw);
229 
230     ResourceResponse response(url);
231     response.SetHttpStatusCode(200);
232     response.SetType(response_type);
233 
234     resource->SetResponse(response);
235     return resource;
236   }
237 
238   KURL sec_url;
239   KURL insec_url;
240 
241   Persistent<MockFetchContext> context;
242 };
243 
244 // Test the prioritization (i.e. selecting the "strongest" algorithm.
245 // This effectively tests the definition of IntegrityAlgorithm in
246 // IntegrityMetadata. The test is here, because SubresourceIntegrity is the
247 // class that relies on this working as expected.)
TEST_F(SubresourceIntegrityTest,Prioritization)248 TEST_F(SubresourceIntegrityTest, Prioritization) {
249   // Check that each algorithm is it's own "strongest".
250   EXPECT_EQ(
251       IntegrityAlgorithm::kSha256,
252       std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha256}));
253   EXPECT_EQ(
254       IntegrityAlgorithm::kSha384,
255       std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha384}));
256 
257   EXPECT_EQ(
258       IntegrityAlgorithm::kSha512,
259       std::max({IntegrityAlgorithm::kSha512, IntegrityAlgorithm::kSha512}));
260   EXPECT_EQ(
261       IntegrityAlgorithm::kEd25519,
262       std::max({IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kEd25519}));
263 
264   // Check a mix of algorithms.
265   EXPECT_EQ(IntegrityAlgorithm::kSha384,
266             std::max({IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha384,
267                       IntegrityAlgorithm::kSha256}));
268   EXPECT_EQ(IntegrityAlgorithm::kSha512,
269             std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
270                       IntegrityAlgorithm::kSha256}));
271   EXPECT_EQ(
272       IntegrityAlgorithm::kEd25519,
273       std::max({IntegrityAlgorithm::kSha384, IntegrityAlgorithm::kSha512,
274                 IntegrityAlgorithm::kEd25519, IntegrityAlgorithm::kSha512,
275                 IntegrityAlgorithm::kSha256, IntegrityAlgorithm::kSha512}));
276 }
277 
TEST_F(SubresourceIntegrityTest,ParseAlgorithm)278 TEST_F(SubresourceIntegrityTest, ParseAlgorithm) {
279   ExpectAlgorithm("sha256-", IntegrityAlgorithm::kSha256);
280   ExpectAlgorithm("sha384-", IntegrityAlgorithm::kSha384);
281   ExpectAlgorithm("sha512-", IntegrityAlgorithm::kSha512);
282   ExpectAlgorithm("sha-256-", IntegrityAlgorithm::kSha256);
283   ExpectAlgorithm("sha-384-", IntegrityAlgorithm::kSha384);
284   ExpectAlgorithm("sha-512-", IntegrityAlgorithm::kSha512);
285 
286   {
287     ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
288     ExpectAlgorithm("ed25519-", IntegrityAlgorithm::kEd25519);
289   }
290   ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
291   ExpectAlgorithmFailure("ed25519-", SubresourceIntegrity::kAlgorithmUnknown);
292 
293   ExpectAlgorithmFailure("sha1-", SubresourceIntegrity::kAlgorithmUnknown);
294   ExpectAlgorithmFailure("sha-1-", SubresourceIntegrity::kAlgorithmUnknown);
295   ExpectAlgorithmFailure("foobarsha256-",
296                          SubresourceIntegrity::kAlgorithmUnknown);
297   ExpectAlgorithmFailure("foobar-", SubresourceIntegrity::kAlgorithmUnknown);
298   ExpectAlgorithmFailure("-", SubresourceIntegrity::kAlgorithmUnknown);
299   ExpectAlgorithmFailure("ed-25519-", SubresourceIntegrity::kAlgorithmUnknown);
300   ExpectAlgorithmFailure("ed25518-", SubresourceIntegrity::kAlgorithmUnknown);
301 
302   ExpectAlgorithmFailure("sha256", SubresourceIntegrity::kAlgorithmUnparsable);
303   ExpectAlgorithmFailure("", SubresourceIntegrity::kAlgorithmUnparsable);
304 }
305 
TEST_F(SubresourceIntegrityTest,ParseDigest)306 TEST_F(SubresourceIntegrityTest, ParseDigest) {
307   ExpectDigest("abcdefg", "abcdefg");
308   ExpectDigest("abcdefg?", "abcdefg");
309   ExpectDigest("ab+de/g", "ab+de/g");
310   ExpectDigest("ab-de_g", "ab+de/g");
311 
312   ExpectDigestFailure("?");
313   ExpectDigestFailure("&&&foobar&&&");
314   ExpectDigestFailure("\x01\x02\x03\x04");
315 }
316 
317 //
318 // End-to-end parsing tests.
319 //
320 
TEST_F(SubresourceIntegrityTest,Parsing)321 TEST_F(SubresourceIntegrityTest, Parsing) {
322   ExpectParseFailure("not_really_a_valid_anything");
323   ExpectParseFailure("sha256-&&&foobar&&&");
324   ExpectParseFailure("sha256-\x01\x02\x03\x04");
325   ExpectParseFailure("sha256-!!! sha256-!!!");
326 
327   ExpectEmptyParseResult("foobar:///sha256-abcdefg");
328   ExpectEmptyParseResult("ni://sha256-abcdefg");
329   ExpectEmptyParseResult("ni:///sha256-abcdefg");
330   ExpectEmptyParseResult("notsha256atall-abcdefg");
331 
332   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
333               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
334               IntegrityAlgorithm::kSha256);
335 
336   ExpectParse("sha-256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
337               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
338               IntegrityAlgorithm::kSha256);
339 
340   ExpectParse("     sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=     ",
341               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
342               IntegrityAlgorithm::kSha256);
343 
344   ExpectParse(
345       "sha384-XVVXBGoYw6AJOh9J-Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_tA1v5GPr",
346       "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
347       IntegrityAlgorithm::kSha384);
348 
349   ExpectParse(
350       "sha-384-XVVXBGoYw6AJOh9J_Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup_"
351       "tA1v5GPr",
352       "XVVXBGoYw6AJOh9J/Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
353       IntegrityAlgorithm::kSha384);
354 
355   ExpectParse(
356       "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
357       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
358       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
359       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
360       IntegrityAlgorithm::kSha512);
361 
362   ExpectParse(
363       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
364       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
365       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
366       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
367       IntegrityAlgorithm::kSha512);
368 
369   ExpectParse(
370       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
371       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/javascript",
372       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
373       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
374       IntegrityAlgorithm::kSha512);
375 
376   ExpectParse(
377       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
378       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml",
379       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
380       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
381       IntegrityAlgorithm::kSha512);
382 
383   ExpectParse(
384       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
385       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?foo=bar?ct=application/xhtml+xml",
386       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
387       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
388       IntegrityAlgorithm::kSha512);
389 
390   ExpectParse(
391       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
392       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?ct=application/xhtml+xml?foo=bar",
393       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
394       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
395       IntegrityAlgorithm::kSha512);
396 
397   ExpectParse(
398       "sha-512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ-"
399       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==?baz=foz?ct=application/"
400       "xhtml+xml?foo=bar",
401       "tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
402       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
403       IntegrityAlgorithm::kSha512);
404 
405   ExpectParseMultipleHashes("", nullptr, 0);
406   ExpectParseMultipleHashes("    ", nullptr, 0);
407 
408   const IntegrityMetadata valid_sha384_and_sha512[] = {
409       IntegrityMetadata(
410           "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
411           IntegrityAlgorithm::kSha384),
412       IntegrityMetadata("tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
413                         "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
414                         IntegrityAlgorithm::kSha512),
415   };
416   ExpectParseMultipleHashes(
417       "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr "
418       "sha512-tbUPioKbVBplr0b1ucnWB57SJWt4x9dOE0Vy2mzCXvH3FepqDZ+"
419       "07yMK81ytlg0MPaIrPAjcHqba5csorDWtKg==",
420       valid_sha384_and_sha512, base::size(valid_sha384_and_sha512));
421 
422   const IntegrityMetadata valid_sha256_and_sha256[] = {
423       IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
424                         IntegrityAlgorithm::kSha256),
425       IntegrityMetadata("deadbeef", IntegrityAlgorithm::kSha256),
426   };
427   ExpectParseMultipleHashes(
428       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-deadbeef",
429       valid_sha256_and_sha256, base::size(valid_sha256_and_sha256));
430 
431   const IntegrityMetadata valid_sha256_and_invalid_sha256[] = {
432       IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
433                         IntegrityAlgorithm::kSha256),
434   };
435   ExpectParseMultipleHashes(
436       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE= sha256-!!!!",
437       valid_sha256_and_invalid_sha256,
438       base::size(valid_sha256_and_invalid_sha256));
439 
440   const IntegrityMetadata invalid_sha256_and_valid_sha256[] = {
441       IntegrityMetadata("BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
442                         IntegrityAlgorithm::kSha256),
443   };
444   ExpectParseMultipleHashes(
445       "sha256-!!! sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
446       invalid_sha256_and_valid_sha256,
447       base::size(invalid_sha256_and_valid_sha256));
448 
449   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
450               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
451               IntegrityAlgorithm::kSha256);
452 
453   ExpectParse(
454       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
455       "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
456       IntegrityAlgorithm::kSha256);
457 
458   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?",
459               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
460               IntegrityAlgorithm::kSha256);
461   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar",
462               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
463               IntegrityAlgorithm::kSha256);
464   ExpectParse(
465       "sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?baz=foz",
466       "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
467       IntegrityAlgorithm::kSha256);
468   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo",
469               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
470               IntegrityAlgorithm::kSha256);
471   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo=bar?",
472               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
473               IntegrityAlgorithm::kSha256);
474   ExpectParse("sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=?foo:bar",
475               "BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=",
476               IntegrityAlgorithm::kSha256);
477 
478   {
479     ScopedSignatureBasedIntegrityForTest signature_based_integrity(false);
480     ExpectEmptyParseResult("ed25519-xxxx");
481     ExpectEmptyParseResult(
482         "ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
483   }
484 
485   ScopedSignatureBasedIntegrityForTest signature_based_integrity(true);
486   ExpectParse("ed25519-xxxx", "xxxx", IntegrityAlgorithm::kEd25519);
487   ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
488               "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
489               IntegrityAlgorithm::kEd25519);
490   ExpectParse("ed25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=?foo=bar",
491               "qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=",
492               IntegrityAlgorithm::kEd25519);
493   ExpectEmptyParseResult("ed-25519-xxx");
494   ExpectEmptyParseResult(
495       "ed-25519-qGFmwTxlocg707D1cX4w60iTwtfwbMLf8ITDyfko7s0=");
496 }
497 
TEST_F(SubresourceIntegrityTest,ParsingBase64)498 TEST_F(SubresourceIntegrityTest, ParsingBase64) {
499   ExpectParse(
500       "sha384-XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
501       "XVVXBGoYw6AJOh9J+Z8pBDMVVPfkBpngexkA7JqZu8d5GENND6TEIup/tA1v5GPr",
502       IntegrityAlgorithm::kSha384);
503 }
504 
505 // Tests that SubresourceIntegrity::CheckSubresourceIntegrity behaves correctly
506 // when faced with secure or insecure origins, same origin and cross origin
507 // requests, successful and failing CORS checks as well as when the response was
508 // handled by a service worker.
TEST_F(SubresourceIntegrityTest,OriginIntegrity)509 TEST_F(SubresourceIntegrityTest, OriginIntegrity) {
510   using network::mojom::FetchResponseType;
511   using network::mojom::RequestMode;
512   constexpr auto kOk = kIntegritySuccess;
513   constexpr auto kFail = kIntegrityFailure;
514   const KURL& url = sec_url;
515 
516   const TestCase cases[] = {
517       // FetchResponseType::kError never arrives because it is a loading error.
518       {url, RequestMode::kNoCors, FetchResponseType::kBasic, kOk},
519       {url, RequestMode::kNoCors, FetchResponseType::kCors, kOk},
520       {url, RequestMode::kNoCors, FetchResponseType::kDefault, kOk},
521       {url, RequestMode::kNoCors, FetchResponseType::kOpaque, kFail},
522       {url, RequestMode::kNoCors, FetchResponseType::kOpaqueRedirect, kFail},
523 
524       // FetchResponseType::kError never arrives because it is a loading error.
525       // FetchResponseType::kOpaque and FetchResponseType::kOpaqueResponse
526       // never arrives: even when service worker is involved, it's handled as
527       // an error.
528       {url, RequestMode::kCors, FetchResponseType::kBasic, kOk},
529       {url, RequestMode::kCors, FetchResponseType::kCors, kOk},
530       {url, RequestMode::kCors, FetchResponseType::kDefault, kOk},
531   };
532 
533   for (const auto& test : cases) {
534     SCOPED_TRACE(testing::Message()
535                  << ", target: " << test.url.BaseAsString()
536                  << ", request mode: " << test.request_mode
537                  << ", response type: " << test.response_type
538                  << ", expected result: "
539                  << (test.expectation == kIntegritySuccess ? "integrity"
540                                                            : "failure"));
541 
542     // Verify basic sha256, sha384, and sha512 integrity checks.
543     CheckExpectedIntegrity(kSha256Integrity, test);
544     CheckExpectedIntegrity(kSha256IntegrityLenientSyntax, test);
545     CheckExpectedIntegrity(kSha384Integrity, test);
546     CheckExpectedIntegrity(kSha512Integrity, test);
547 
548     // Verify multiple hashes in an attribute.
549     CheckExpectedIntegrity(kSha256AndSha384Integrities, test);
550     CheckExpectedIntegrity(kBadSha256AndGoodSha384Integrities, test);
551 
552     // Unsupported hash functions should succeed.
553     CheckExpectedIntegrity(kUnsupportedHashFunctionIntegrity, test);
554 
555     // Options should be ignored
556     CheckExpectedIntegrity(kSha256IntegrityWithEmptyOption, test);
557     CheckExpectedIntegrity(kSha256IntegrityWithOption, test);
558     CheckExpectedIntegrity(kSha256IntegrityWithOptions, test);
559     CheckExpectedIntegrity(kSha256IntegrityWithMimeOption, test);
560 
561     // The following tests are expected to fail in every scenario:
562 
563     // The hash label must match the hash value.
564     CheckExpectedIntegrity(kSha384IntegrityLabeledAs256, test,
565                            Expectation::kIntegrityFailure);
566 
567     // With multiple values, at least one must match, and it must be the
568     // strongest hash algorithm.
569     CheckExpectedIntegrity(kGoodSha256AndBadSha384Integrities, test,
570                            Expectation::kIntegrityFailure);
571     CheckExpectedIntegrity(kBadSha256AndBadSha384Integrities, test,
572                            Expectation::kIntegrityFailure);
573   }
574 }
575 
TEST_F(SubresourceIntegrityTest,FindBestAlgorithm)576 TEST_F(SubresourceIntegrityTest, FindBestAlgorithm) {
577   // Each algorithm is its own best.
578   EXPECT_EQ(IntegrityAlgorithm::kSha256,
579             SubresourceIntegrity::FindBestAlgorithm(
580                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256}})));
581   EXPECT_EQ(IntegrityAlgorithm::kSha384,
582             SubresourceIntegrity::FindBestAlgorithm(
583                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha384}})));
584   EXPECT_EQ(IntegrityAlgorithm::kSha512,
585             SubresourceIntegrity::FindBestAlgorithm(
586                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha512}})));
587   EXPECT_EQ(IntegrityAlgorithm::kEd25519,
588             SubresourceIntegrity::FindBestAlgorithm(
589                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kEd25519}})));
590 
591   // Test combinations of multiple algorithms.
592   EXPECT_EQ(IntegrityAlgorithm::kSha384,
593             SubresourceIntegrity::FindBestAlgorithm(
594                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
595                                       {"", IntegrityAlgorithm::kSha384}})));
596   EXPECT_EQ(IntegrityAlgorithm::kSha512,
597             SubresourceIntegrity::FindBestAlgorithm(
598                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
599                                       {"", IntegrityAlgorithm::kSha512},
600                                       {"", IntegrityAlgorithm::kSha384}})));
601   EXPECT_EQ(IntegrityAlgorithm::kEd25519,
602             SubresourceIntegrity::FindBestAlgorithm(
603                 IntegrityMetadataSet({{"", IntegrityAlgorithm::kSha256},
604                                       {"", IntegrityAlgorithm::kSha512},
605                                       {"", IntegrityAlgorithm::kEd25519}})));
606 }
607 
TEST_F(SubresourceIntegrityTest,GetCheckFunctionForAlgorithm)608 TEST_F(SubresourceIntegrityTest, GetCheckFunctionForAlgorithm) {
609   EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
610               SubresourceIntegrity::GetCheckFunctionForAlgorithm(
611                   IntegrityAlgorithm::kSha256));
612   EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
613               SubresourceIntegrity::GetCheckFunctionForAlgorithm(
614                   IntegrityAlgorithm::kSha384));
615   EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegrityDigest ==
616               SubresourceIntegrity::GetCheckFunctionForAlgorithm(
617                   IntegrityAlgorithm::kSha512));
618   EXPECT_TRUE(SubresourceIntegrity::CheckSubresourceIntegritySignature ==
619               SubresourceIntegrity::GetCheckFunctionForAlgorithm(
620                   IntegrityAlgorithm::kEd25519));
621 }
622 
623 }  // namespace blink
624