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

Side by Side Diff: components/cronet/android/test/javatests/src/org/chromium/net/HpkpTest.java

Issue 1407263010: [Cronet] Public key pinning for Java API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed review comments Created 5 years, 1 month 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 2015 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 package org.chromium.net;
6
7 import android.test.suitebuilder.annotation.SmallTest;
8
9 import org.chromium.base.test.util.Feature;
10 import org.chromium.net.test.util.CertTestUtil;
11
12 import java.io.ByteArrayInputStream;
13 import java.security.cert.CertificateFactory;
14 import java.security.cert.X509Certificate;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Calendar;
18 import java.util.Collection;
19 import java.util.Date;
20
21 /**
22 * Public-Key-Pinning tests of Cronet Java API.
23 */
24 public class HpkpTest extends CronetTestBase {
pauljensen 2015/11/17 19:01:23 can we add a test that the pins are not persisted?
kapishnikov 2015/11/18 00:10:31 I am adding the test. Somehow TransportSecuritySta
25 private static final String CERT_USED = "quic_test.example.com.crt";
26 private static final String[] CERTS_USED = {CERT_USED};
27 private static final int DISTANT_FUTURE = Integer.MAX_VALUE;
28 private static final boolean INCLUDE_SUBDOMAINS = true;
29 private static final boolean EXCLUDE_SUBDOMAINS = false;
30
31 private CronetTestFramework mTestFramework;
32 private CronetEngine.Builder mBuilder;
33 private TestUrlRequestCallback mListener;
34 private String mServerUrl; // https://test.example.com:6121
35 private String mServerHost; // test.example.com
36 private String mDomain; // example.com
37
38 @Override
39 protected void setUp() throws Exception {
40 super.setUp();
41 // Start QUIC Test Server
42 System.loadLibrary("cronet_tests");
43 QuicTestServer.startQuicTestServer(getContext());
44 mServerUrl = QuicTestServer.getServerURL();
45 mServerHost = QuicTestServer.getServerHost();
46 mDomain = mServerHost.substring(mServerHost.indexOf('.') + 1, mServerHos t.length());
47
48 // Set common CronetEngine parameters
49 mBuilder = new CronetEngine.Builder(getContext());
50 mBuilder.enableQUIC(true);
51 mBuilder.addQuicHint(QuicTestServer.getServerHost(), QuicTestServer.getS erverPort(),
52 QuicTestServer.getServerPort());
53 mBuilder.setStoragePath(CronetTestFramework.getTestStorage(getContext()) );
54 mBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 1 000 * 1024);
55 mBuilder.setMockCertVerifierForTesting(MockCertVerifier.createMockCertVe rifier(CERTS_USED));
56 }
57
58 @Override
59 protected void tearDown() throws Exception {
60 QuicTestServer.shutdownQuicTestServer();
61 super.tearDown();
62 }
63
64 /**
65 * Tests the case when the pin hash does not match. The client is expected t o
66 * receive the error response.
67 *
68 * @throws Exception
69 */
70 @SmallTest
71 @Feature({"Cronet"})
72 public void testErrorCodeIfPinDoesNotMatch() throws Exception {
73 byte[] nonMatchingHash = generateSomeSha256();
74 addPkpSha256(mServerHost, nonMatchingHash, EXCLUDE_SUBDOMAINS, DISTANT_F UTURE);
75 startCronetFramework();
76 registerHostResolver();
77 sendRequestAndWaitForResult();
78
79 assertErrorResponse();
80 }
81
82 /**
83 * Tests the case when the pin hash matches. The client is expected to
84 * receive the successful response with the response code 200.
85 *
86 * @throws Exception
87 */
88 @SmallTest
89 @Feature({"Cronet"})
90 public void testSuccessIfPinMatches() throws Exception {
91 // Get PKP hash of the real certificate
92 X509Certificate cert = readCertFromFileInPemFormat(CERT_USED);
93 byte[] matchingHash = CertTestUtil.getPublicKeySha256(cert);
94
95 addPkpSha256(mServerHost, matchingHash, EXCLUDE_SUBDOMAINS, DISTANT_FUTU RE);
96 startCronetFramework();
97 registerHostResolver();
98 sendRequestAndWaitForResult();
99
100 assertSuccessfulResponse();
101 }
102
103 /**
104 * Tests the case when the pin hash does not match and the client accesses t he subdomain of
105 * the configured HPKP host with includeSubdomains flag set to true. The cli ent is
106 * expected to receive the error response.
107 *
108 * @throws Exception
109 */
110 @SmallTest
111 @Feature({"Cronet"})
112 public void testIncludeSubdomainsFlagEqualTrue() throws Exception {
113 addPkpSha256(mDomain, generateSomeSha256(), INCLUDE_SUBDOMAINS, DISTANT_ FUTURE);
114 startCronetFramework();
115 registerHostResolver();
116 sendRequestAndWaitForResult();
117
118 assertErrorResponse();
119 }
120
121 /**
122 * Tests the case when the pin hash does not match and the client accesses t he subdomain of
123 * the configured HPKP host with includeSubdomains flag set to false. The cl ient is expected to
124 * receive the successful response with the response code 200.
125 *
126 * @throws Exception
127 */
128 @SmallTest
129 @Feature({"Cronet"})
130 public void testIncludeSubdomainsFlagEqualFalse() throws Exception {
131 addPkpSha256(mDomain, generateSomeSha256(), EXCLUDE_SUBDOMAINS, DISTANT_ FUTURE);
132 startCronetFramework();
133 registerHostResolver();
134 sendRequestAndWaitForResult();
135
136 assertSuccessfulResponse();
137 }
138
139 /**
140 * Tests the case when the mismatching pin is set for some host that is diff erent from the one
141 * the client wants to access. In that case the other host pinning policy sh ould not be applied
142 * and the client is expected to receive the successful response with the re sponse code 200.
143 *
144 * @throws Exception
145 */
146 @SmallTest
147 @Feature({"Cronet"})
148 public void testSuccessIfNoPinSpecified() throws Exception {
149 addPkpSha256("otherhost.com", generateSomeSha256(), INCLUDE_SUBDOMAINS, DISTANT_FUTURE);
150 startCronetFramework();
151 registerHostResolver();
152 sendRequestAndWaitForResult();
153
154 assertSuccessfulResponse();
155 }
156
157 /**
158 * Tests mismatching pins that will expire in 10 seconds. The pins should be still valid and
159 * enforced during the request; thus returning PIN mismatch error.
160 *
161 * @throws Exception
162 */
163 @SmallTest
164 @Feature({"Cronet"})
165 public void testSoonExpiringPin() throws Exception {
166 final int tenSecondsAhead = 10;
167 byte[] nonMatchingHash = generateSomeSha256();
168 addPkpSha256(mServerHost, nonMatchingHash, EXCLUDE_SUBDOMAINS, tenSecond sAhead);
169 startCronetFramework();
170 registerHostResolver();
171 sendRequestAndWaitForResult();
172
173 assertErrorResponse();
174 }
175
176 /**
177 * Tests mismatching pins that expired 1 second ago. Since the pins have exp ired, they
178 * should not be enforced during the request; thus a successful response is expected.
179 *
180 * @throws Exception
181 */
182 @SmallTest
183 @Feature({"Cronet"})
184 public void testRecentlyExpiredPin() throws Exception {
185 final int oneSecondAgo = -1;
186 byte[] nonMatchingHash = generateSomeSha256();
187 addPkpSha256(mServerHost, nonMatchingHash, EXCLUDE_SUBDOMAINS, oneSecond Ago);
188 startCronetFramework();
189 registerHostResolver();
190 sendRequestAndWaitForResult();
191
192 assertSuccessfulResponse();
193 }
194
195 /**
196 * Tests that the client receives {@code InvalidArgumentException} when the pinned host name
197 * is invalid.
198 *
199 * @throws Exception
200 */
201 @SmallTest
202 @Feature({"Cronet"})
203 public void testHostNameArgumentValidation() throws Exception {
204 assertExceptionWhenHostNameIsInvalid("http://domain.com");
205 assertExceptionWhenHostNameIsInvalid("domain.com:4000");
206 assertExceptionWhenHostNameIsInvalid("domain.com/mydoc.txt");
207 assertExceptionWhenHostNameIsInvalid("128.55.0.1");
208 }
209
210 /**
211 * Asserts that the response from the server contains an HPKP error.
212 * TODO(kapishnikov): currently QUIC returns ERR_QUIC_PROTOCOL_ERROR instead of expected
213 * ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN error code when the pin doesn't matc h.
214 * This method should be changed when the bug is resolved. See http://crbug. com/548378
215 */
216 private void assertErrorResponse() {
217 assertNotNull("Expected an error", mListener.mError);
218 int errorCode = mListener.mError.netError();
219 boolean correctErrorCode = errorCode == NetError.ERR_SSL_PINNED_KEY_NOT_ IN_CERT_CHAIN
220 || errorCode == NetError.ERR_QUIC_PROTOCOL_ERROR;
221 assertTrue(String.format("Incorrect error code. Expected %s or %s but re ceived %s",
222 NetError.ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN,
223 NetError.ERR_QUIC_PROTOCOL_ERROR, errorCode),
224 correctErrorCode);
225 }
226
227 /**
228 * Asserts a successful response with response code 200.
229 */
230 private void assertSuccessfulResponse() {
231 if (mListener.mError != null) {
232 fail("Did not expect an error but got error code " + mListener.mErro r.mNetError);
233 }
234 assertNotNull("Expected non-null response from the server", mListener.mR esponseInfo);
235 assertEquals(200, mListener.mResponseInfo.getHttpStatusCode());
236 }
237
238 private void startCronetFramework() {
239 mTestFramework = startCronetTestFrameworkWithUrlAndCronetEngineBuilder(n ull, mBuilder);
240 }
241
242 private void registerHostResolver() {
243 long urlRequestContextAdapter = ((CronetUrlRequestContext) mTestFramewor k.mCronetEngine)
244 .getUrlRequestContextAdapter();
245 NativeTestServer.registerHostResolverProc(urlRequestContextAdapter, fals e);
246 }
247
248 private byte[] generateSomeSha256() {
249 byte[] sha256 = new byte[32];
250 Arrays.fill(sha256, (byte) 58);
251 return sha256;
252 }
253
254 private void addPkpSha256(
255 String host, byte[] pinHashValue, boolean includeSubdomain, int maxA geInSec) {
256 Collection<byte[]> hashes = new ArrayList<>();
257 hashes.add(pinHashValue);
258 mBuilder.addPublicKeyPins(host, hashes, includeSubdomain, dateInFuture(m axAgeInSec));
259 }
260
261 private void sendRequestAndWaitForResult() {
262 mListener = new TestUrlRequestCallback();
263
264 String quicURL = mServerUrl + "/simple.txt";
265 UrlRequest.Builder requestBuilder = new UrlRequest.Builder(
266 quicURL, mListener, mListener.getExecutor(), mTestFramework.mCro netEngine);
267 requestBuilder.build().start();
268 mListener.blockForDone();
269 }
270
271 private X509Certificate readCertFromFileInPemFormat(String certFileName) thr ows Exception {
272 byte[] certDer = CertTestUtil.pemToDer(CertTestUtil.CERTS_DIRECTORY + ce rtFileName);
273 CertificateFactory certFactory = CertificateFactory.getInstance("X.509") ;
274 return (X509Certificate) certFactory.generateCertificate(new ByteArrayIn putStream(certDer));
275 }
276
277 private Date dateInFuture(int secondsIntoFuture) {
278 Calendar cal = Calendar.getInstance();
279 cal.add(Calendar.SECOND, secondsIntoFuture);
280 return cal.getTime();
281 }
282
283 private void assertExceptionWhenHostNameIsInvalid(String hostName) {
284 try {
285 addPkpSha256(hostName, generateSomeSha256(), INCLUDE_SUBDOMAINS, DIS TANT_FUTURE);
286 } catch (IllegalArgumentException ex) {
287 // Expected exception.
288 return;
289 }
290 fail("Expected IllegalArgumentException when passing " + hostName + " ho st name");
291 }
292 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698