Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1301)

Side by Side Diff: components/cronet/ios/test/cronet_pkp_test.mm

Issue 2928653002: [Cronet-iOS] Public-Key-Pinning Tests (Closed)
Patch Set: Addressed Misha's comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 #import <Cronet/Cronet.h>
6
7 #include "components/cronet/ios/test/start_cronet.h"
8 #include "components/grpc_support/test/quic_test_server.h"
9 #include "cronet_test_base.h"
10 #include "net/base/mac/url_conversions.h"
11 #include "net/cert/mock_cert_verifier.h"
12 #include "net/test/cert_test_util.h"
13 #include "net/test/test_data_directory.h"
14
15 #include "testing/gtest_mac.h"
16
17 namespace {
18 const bool kIncludeSubdomains = true;
19 const bool kExcludeSubdomains = false;
20 const bool kSuccess = true;
21 const bool kError = false;
22 const std::string kServerCert = "quic_test.example.com.crt";
23 NSDate* g_distant_future = [NSDate distantFuture];
mef 2017/06/19 15:02:33 Can this be kDistantFuture or use [NSDate distantF
kapishnikov 2017/06/19 18:32:48 Done.
24 } // namespace
25
26 namespace cronet {
27 // Tests public-key-pinning functionality.
28 class PkpTest : public CronetTestBase {
29 protected:
30 void SetUp() override {
31 CronetTestBase::SetUp();
32 server_host_ = [NSString stringWithCString:grpc_support::kTestServerHost
33 encoding:NSUTF8StringEncoding];
34 server_domain_ = [NSString stringWithCString:grpc_support::kTestServerDomain
35 encoding:NSUTF8StringEncoding];
36
37 NSString* request_url_str =
38 [NSString stringWithCString:grpc_support::kTestServerSimpleUrl
39 encoding:NSUTF8StringEncoding];
40 request_url_ = [NSURL URLWithString:request_url_str];
41
42 // Create a Cronet enabled NSURLSession.
43 NSURLSessionConfiguration* sessionConfig =
44 [NSURLSessionConfiguration defaultSessionConfiguration];
45 [Cronet installIntoSessionConfiguration:sessionConfig];
46 url_session_ = [NSURLSession sessionWithConfiguration:sessionConfig
47 delegate:delegate_
48 delegateQueue:nil];
49
50 // Set mock cert verifier.
51 [Cronet setMockCertVerifierForTesting:CreateMockCertVerifier({kServerCert},
52 YES)];
53 }
54
55 void TearDown() override {
56 // It is safe to call the shutdownForTesting method even if a test
57 // didn't call StartCronet().
58 [Cronet shutdownForTesting];
59 CronetTestBase::TearDown();
60 }
61
62 // Sends a request to a given URL, waits for the response and asserts that
63 // the response is either successful or containing an error depending on
64 // the value of the passed |expected_success| parameter.
65 void sendRequestAndAssertResult(NSURL* url, bool expected_success) {
66 NSURLSessionDataTask* dataTask =
67 [url_session_ dataTaskWithURL:request_url_];
68 StartDataTaskAndWaitForCompletion(dataTask);
69 if (expected_success) {
70 ASSERT_TRUE(IsResponseSuccessful());
71 } else {
72 ASSERT_FALSE(IsResponseSuccessful());
73 }
74 }
75
76 // Adds a given public-key-pin and starts a Cronet engine for testing.
77 void AddPkpAndStartCronet(NSString* host,
78 NSData* hash,
79 BOOL include_subdomains,
80 const NSDate* expiration_date) {
81 NSSet* hashes = [NSSet setWithObject:hash];
82 NSError* error;
83 BOOL success = [Cronet addPublicKeyPinsForHost:host
84 pinHashes:hashes
85 includeSubdomains:include_subdomains
86 expirationDate:(NSDate*)expiration_date
87 error:&error];
88 CHECK(success);
89 CHECK(!error);
90 StartCronet(grpc_support::GetQuicTestServerPort());
91 }
92
93 // Returns an arbitrary public key hash that doesn't match with any test
94 // certificate.
95 static NSData* NonMatchingHash() {
96 const int length = 32;
97 const char* hash = std::string(length, '\077').c_str();
98 return [NSData dataWithBytes:hash length:length];
99 }
100
101 // Returns hash value that matches the hash of the public key certificate used
102 // for testing.
103 static NSData* MatchingHash() {
104 scoped_refptr<net::X509Certificate> cert =
105 net::ImportCertFromFile(net::GetTestCertsDirectory(), kServerCert);
106 net::HashValue hash_value;
107 CalculatePublicKeySha256(*cert, &hash_value);
108 CHECK_EQ(32ul, hash_value.size());
109 return [NSData dataWithBytes:hash_value.data() length:hash_value.size()];
110 }
111
112 NSURLSession* url_session_;
113 NSURL* request_url_; // "https://test.example.com/simple.txt"
114 NSString* server_host_; // test.example.com
115 NSString* server_domain_; // example.com
116 }; // class PkpTest
117
118 // Tests the case when a mismatching pin is set for some host that is
119 // different from the one the client wants to access. In that case the other
120 // host pinning policy should not be applied and the client is expected to
121 // receive the successful response with the response code 200.
122 TEST_F(PkpTest, TestSuccessIfPinSetForDifferentHost) {
123 AddPkpAndStartCronet(@"some-other-host.com", NonMatchingHash(),
124 kExcludeSubdomains, g_distant_future);
125 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSuccess));
126 }
127
128 // Tests the case when the pin hash does not match. The client is expected to
129 // receive the error response.
130 TEST_F(PkpTest, TestErrorIfPinDoesNotMatch) {
131 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kExcludeSubdomains,
132 g_distant_future);
133 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kError));
134 }
135
136 // Tests the case when the pin hash matches. The client is expected to
137 // receive the successful response with the response code 200.
138 TEST_F(PkpTest, TestSuccessIfPinMatches) {
139 AddPkpAndStartCronet(server_host_, MatchingHash(), kExcludeSubdomains,
140 g_distant_future);
141 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSuccess));
142 }
143
144 // Tests the case when the pin hash does not match and the client accesses the
145 // subdomain of the configured PKP host with includeSubdomains flag set to true.
146 // The client is expected to receive the error response.
147 TEST_F(PkpTest, TestIncludeSubdomainsFlagEqualTrue) {
148 AddPkpAndStartCronet(server_domain_, NonMatchingHash(), kIncludeSubdomains,
149 g_distant_future);
150 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kError));
151 }
152
153 // Tests the case when the pin hash does not match and the client accesses the
154 // subdomain of the configured PKP host with includeSubdomains flag set to
155 // false. The client is expected to receive the successful response with the
156 // response code 200.
157 TEST_F(PkpTest, TestIncludeSubdomainsFlagEqualFalse) {
158 AddPkpAndStartCronet(server_domain_, NonMatchingHash(), kExcludeSubdomains,
159 g_distant_future);
160 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSuccess));
161 }
162
163 // Tests a mismatching pin that will expire in 10 seconds. The pins should be
164 // still valid and enforced during the request; thus returning the pin match
165 // error.
166 TEST_F(PkpTest, TestSoonExpiringPin) {
167 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kExcludeSubdomains,
168 [NSDate dateWithTimeIntervalSinceNow:10]);
169 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kError));
170 }
171
172 // Tests mismatching pin that expired 1 second ago. Since the pin has
173 // expired, it should not be enforced during the request; thus a successful
174 // response is expected.
175 TEST_F(PkpTest, TestRecentlyExpiredPin) {
176 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kExcludeSubdomains,
177 [NSDate dateWithTimeIntervalSinceNow:-1]);
178 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSuccess));
179 }
180
181 // Tests that host pinning is not persisted between multiple CronetEngine
182 // instances.
183 TEST_F(PkpTest, TestPinsAreNotPersisted) {
184 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kExcludeSubdomains,
185 g_distant_future);
186 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kError));
187 [Cronet shutdownForTesting];
188
189 // Restart Cronet engine and try the same request again. Since the pins are
190 // not persisted, a successful response is expected.
191 StartCronet(grpc_support::GetQuicTestServerPort());
192 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSuccess));
193 }
194
195 // Tests that an error is returned when PKP hash size is not equal to 256 bits.
196 TEST_F(PkpTest, TestHashLengthError) {
197 char hash[31];
198 NSData* shortHash = [NSData dataWithBytes:hash length:sizeof(hash)];
199 NSSet* hashes = [NSSet setWithObject:shortHash];
200 NSError* error;
201 BOOL success = [Cronet addPublicKeyPinsForHost:server_host_
202 pinHashes:hashes
203 includeSubdomains:kExcludeSubdomains
204 expirationDate:g_distant_future
205 error:&error];
206 EXPECT_FALSE(success);
207 ASSERT_TRUE(error != nil);
208 EXPECT_EQ(CRNErrorInvalidArgument, error.code);
209 EXPECT_TRUE([error.description rangeOfString:@"Invalid argument"].location !=
210 NSNotFound);
211 EXPECT_TRUE([error.description rangeOfString:@"pinHashes"].location !=
212 NSNotFound);
213 EXPECT_STREQ("pinHashes", [error.userInfo[CRNInvalidArgumentKey]
214 cStringUsingEncoding:NSUTF8StringEncoding]);
215 }
216
217 // Tests that setting pins for the same host second time overrides the previous
218 // pins.
219 TEST_F(PkpTest, TestHashOverrideNonMatchingToMatching) {
220 // Add non-matching pin.
221 BOOL success =
222 [Cronet addPublicKeyPinsForHost:server_host_
223 pinHashes:[NSSet setWithObject:NonMatchingHash()]
224 includeSubdomains:kExcludeSubdomains
225 expirationDate:g_distant_future
226 error:nil];
227 ASSERT_TRUE(success);
228 // Add matching pin.
229 AddPkpAndStartCronet(server_host_, MatchingHash(), kExcludeSubdomains,
230 g_distant_future);
231 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSuccess));
232 }
233
234 // Tests that setting pins for the same host second time overrides the previous
235 // pins.
236 TEST_F(PkpTest, TestHashOverridematchingToNonMatching) {
237 // Add matching pin.
238 BOOL success =
239 [Cronet addPublicKeyPinsForHost:server_host_
240 pinHashes:[NSSet setWithObject:MatchingHash()]
241 includeSubdomains:kExcludeSubdomains
242 expirationDate:g_distant_future
243 error:nil];
244 ASSERT_TRUE(success);
245 // Add non-matching pin.
246 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kExcludeSubdomains,
247 g_distant_future);
248 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kError));
249 }
250
251 } // namespace cronet
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698