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

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

Issue 2928653002: [Cronet-iOS] Public-Key-Pinning Tests (Closed)
Patch Set: Addressed Lily'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 kINCLUDE_SUBDOMAINS = true;
mef 2017/06/12 22:25:23 I think constant names are kCamelCase. Also, if th
kapishnikov 2017/06/16 20:11:05 Done.
19 const bool kEXCLUDE_SUBDOMAINS = false;
20 const bool kSUCCESS = true;
21 const bool kERROR = false;
22 const std::string kSERVER_CERT = "quic_test.example.com.crt";
23 NSDate* distant_future = [NSDate distantFuture];
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
33 NSString* full_url_str = [NSString
34 stringWithFormat:@"%@:%i",
35 [NSString
36 stringWithCString:grpc_support::kTestServerUrl
37 encoding:NSUTF8StringEncoding],
38 grpc_support::GetQuicTestServerPort()];
39 request_url_ = [NSURL URLWithString:full_url_str];
40 server_host_ = request_url_.host;
41 NSArray<NSString*>* components = [server_host_
42 componentsSeparatedByCharactersInSet:
43 [NSCharacterSet characterSetWithCharactersInString:@"."]];
44 domain_ =
mef 2017/06/12 22:25:23 Maybe add a comment why we need to extract 'exampl
kapishnikov 2017/06/16 20:11:05 Added new grpc_support::kTestServerDomain constant
45 [NSString stringWithFormat:@"%@.%@", components[components.count - 2],
46 components[components.count - 1]];
47
48 // Create a Cronet enabled NSURLSession
mef 2017/06/12 22:25:23 nit: Comments should be full sentences ending with
kapishnikov 2017/06/16 20:11:04 Done.
49 NSURLSessionConfiguration* sessionConfig =
50 [NSURLSessionConfiguration defaultSessionConfiguration];
51 [Cronet installIntoSessionConfiguration:sessionConfig];
52 url_session_ = [NSURLSession sessionWithConfiguration:sessionConfig
53 delegate:delegate_
54 delegateQueue:nil];
55
56 // Set mock cert verifier
57 [Cronet setMockCertVerifier:CreateMockCertVerifier({kSERVER_CERT}, YES)];
58 }
59
60 void TearDown() override {
61 [Cronet shutdownForTesting];
mef 2017/06/12 22:25:23 It is strange that we have an explicit shutdown he
kapishnikov 2017/06/16 20:11:04 Added comment. It should be safe to call shutdown
62 CronetTestBase::TearDown();
63 }
64
65 // Sends a request to a given URL, waits for the response and asserts that
66 // the response is either successful or containing an error depending on
67 // the value of the passed |expected_success| parameter.
68 void sendRequestAndAssertResult(NSURL* url, bool expected_success) {
69 NSURLSessionDataTask* dataTask =
70 [url_session_ dataTaskWithURL:request_url_];
71 StartDataTaskAndWaitForCompletion(dataTask);
72 if (expected_success) {
73 ASSERT_TRUE(IsResponseSuccessful());
74 } else {
75 ASSERT_FALSE(IsResponseSuccessful());
76 }
77 }
78
79 // Adds a give public-key-pin and starts a Cronet engine for testing.
mef 2017/06/12 22:25:23 nit: give -> given
kapishnikov 2017/06/16 20:11:04 Done.
80 static void AddPkpAndStartCronet(NSString* host,
81 NSData* hash,
82 BOOL include_subdomains,
83 const NSDate* expiration_date) {
84 NSSet* hashes = [NSSet setWithObject:hash];
85 [Cronet addPublicKeyPinsForHost:host
86 pinHashes:hashes
87 includeSubdomains:include_subdomains
88 expirationDate:(NSDate*)expiration_date];
89 StartCronet(grpc_support::GetQuicTestServerPort());
90 }
91
92 // Returns an arbitrary public key hash that doesn't match with any test
93 // certificate.
94 static NSData* NonMatchingHash() {
95 const int length = 32;
96 u_char hash[length];
mef 2017/06/12 22:25:23 I don't think u_char is normally used in Chromium.
kapishnikov 2017/06/16 20:11:05 Done.
97 memset(hash, 77, length);
98 return [NSData dataWithBytes:hash length:length];
99 }
100
101 NSURLSession* url_session_;
102 NSURL* request_url_; // "https://test.example.com/hello.txt:62922"
103 NSString* server_host_; // test.example.com
104 NSString* domain_; // example.com
105
106 }; // class PkpTest
107
108 // Tests the case when a mismatching pin is set for some host that is
109 // different from the one the client wants to access. In that case the other
110 // host pinning policy should not be applied and the client is expected to
111 // receive the successful response with the response code 200.
112 TEST_F(PkpTest, TestSuccessIfPinSetForDifferentHost) {
113 AddPkpAndStartCronet(@"some-other-host.com", NonMatchingHash(),
114 kEXCLUDE_SUBDOMAINS, distant_future);
115 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSUCCESS));
116 }
117
118 // Tests the case when the pin hash does not match. The client is expected to
119 // receive the error response.
120 TEST_F(PkpTest, TestErrorIfPinDoesNotMatch) {
121 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kEXCLUDE_SUBDOMAINS,
122 distant_future);
123 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kERROR));
124 }
125
126 // Tests the case when the pin hash matches. The client is expected to
127 // receive the successful response with the response code 200.
128 TEST_F(PkpTest, TestSuccessIfPinMatches) {
129 // Calculate hash of the server certificate
130 scoped_refptr<net::X509Certificate> cert =
131 net::ImportCertFromFile(net::GetTestCertsDirectory(), kSERVER_CERT);
132 net::HashValue hash_value;
133 CalculatePublicKeySha256(*cert, &hash_value);
134 ASSERT_EQ(32ul, hash_value.size());
135 NSData* matching_hash =
136 [NSData dataWithBytes:hash_value.data() length:hash_value.size()];
137
138 AddPkpAndStartCronet(server_host_, matching_hash, kEXCLUDE_SUBDOMAINS,
139 distant_future);
140 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSUCCESS));
141 }
142
143 // Tests the case when the pin hash does not match and the client accesses the
144 // subdomain of the configured PKP host with includeSubdomains flag set to true.
145 // The client is expected to receive the error response.
146 TEST_F(PkpTest, TestIncludeSubdomainsFlagEqualTrue) {
147 AddPkpAndStartCronet(domain_, NonMatchingHash(), kINCLUDE_SUBDOMAINS,
148 distant_future);
149 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kERROR));
150 }
151
152 // Tests the case when the pin hash does not match and the client accesses the
153 // subdomain of the configured PKP host with includeSubdomains flag set to
154 // false. The client is expected to receive the successful response with the
155 // response code 200.
156 TEST_F(PkpTest, TestIncludeSubdomainsFlagEqualFalse) {
157 AddPkpAndStartCronet(domain_, NonMatchingHash(), kEXCLUDE_SUBDOMAINS,
158 distant_future);
159 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSUCCESS));
160 }
161
162 // Tests a mismatching pin that will expire in 10 seconds. The pins should be
163 // still valid and enforced during the request; thus returning the pin match
164 // error.
165 TEST_F(PkpTest, TestSoonExpiringPin) {
166 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kEXCLUDE_SUBDOMAINS,
167 [NSDate dateWithTimeIntervalSinceNow:10]);
168 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kERROR));
169 }
170
171 // Tests mismatching pin that expired 1 second ago. Since the pin has
172 // expired, it should not be enforced during the request; thus a successful
173 // response is expected.
174 TEST_F(PkpTest, TestRecentlyExpiredPin) {
175 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kEXCLUDE_SUBDOMAINS,
176 [NSDate dateWithTimeIntervalSinceNow:-1]);
177 ASSERT_NO_FATAL_FAILURE(sendRequestAndAssertResult(request_url_, kSUCCESS));
178 }
179
180 // Tests that host pinning is not persisted between multiple CronetEngine
181 // instances.
182 TEST_F(PkpTest, TestPinsAreNotPersisted) {
183 //
mef 2017/06/12 22:25:23 nit: spurious //
kapishnikov 2017/06/16 20:11:05 Done.
184 AddPkpAndStartCronet(server_host_, NonMatchingHash(), kEXCLUDE_SUBDOMAINS,
185 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 } // namespace cronet
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698