Chromium Code Reviews| OLD | NEW |
|---|---|
| (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.Collection; | |
| 18 | |
| 19 /** | |
| 20 * Public-Key-Pinning tests of Cronet Java API. | |
|
xunjieli
2015/11/06 15:47:54
Should we say **dynamic** public key pinning here?
kapishnikov
2015/11/06 16:18:13
I would like to clarify the terminology here. I wa
xunjieli
2015/11/06 16:43:50
I got the impression that we do not want to do sta
davidben
2015/11/06 16:53:13
"static" vs "dynamic" is whatever we want to call
| |
| 21 */ | |
| 22 public class HpkpTest extends CronetTestBase { | |
| 23 private static final String CERT_USED = "quic_test.example.com.crt"; | |
| 24 private static final String[] CERTS_USED = {CERT_USED}; | |
| 25 | |
| 26 private CronetTestFramework mTestFramework; | |
| 27 private CronetEngine.Builder mBuilder; | |
| 28 private TestUrlRequestCallback mListener; | |
| 29 private String mServerUrl; // https://test.example.com:6121 | |
| 30 private String mServerHost; // test.example.com | |
| 31 private String mDomain; // example.com | |
| 32 | |
| 33 @Override | |
| 34 protected void setUp() throws Exception { | |
| 35 super.setUp(); | |
| 36 // Start QUIC Test Server | |
| 37 System.loadLibrary("cronet_tests"); | |
| 38 QuicTestServer.startQuicTestServer(getContext()); | |
| 39 mServerUrl = QuicTestServer.getServerURL(); | |
| 40 mServerHost = QuicTestServer.getServerHost(); | |
| 41 mDomain = mServerHost.substring(mServerHost.indexOf('.') + 1, mServerHos t.length()); | |
| 42 | |
| 43 // Set common CronetEngine parameters | |
| 44 mBuilder = new CronetEngine.Builder(getContext()); | |
| 45 mBuilder.enableQUIC(true); | |
| 46 mBuilder.addQuicHint(QuicTestServer.getServerHost(), QuicTestServer.getS erverPort(), | |
| 47 QuicTestServer.getServerPort()); | |
| 48 mBuilder.setStoragePath(CronetTestFramework.getTestStorage(getContext()) ); | |
| 49 mBuilder.enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 1 000 * 1024); | |
| 50 mBuilder.setMockCertVerifierForTesting(MockCertVerifier.createMockCertVe rifier(CERTS_USED)); | |
| 51 } | |
| 52 | |
| 53 @Override | |
| 54 protected void tearDown() throws Exception { | |
| 55 QuicTestServer.shutdownQuicTestServer(); | |
| 56 super.tearDown(); | |
| 57 } | |
| 58 | |
| 59 /** | |
| 60 * Tests the case when the pin hash does not match. The client is expected t o | |
| 61 * receive the error response. | |
| 62 * | |
| 63 * @throws Exception | |
| 64 */ | |
| 65 @SmallTest | |
| 66 @Feature({"Cronet"}) | |
| 67 public void testErrorCodeIfPinDoesNotMatch() throws Exception { | |
| 68 byte[] nonMatchingHash = generateSomeSha256(); | |
| 69 addPkpSha256(mServerHost, nonMatchingHash, false); | |
| 70 startCronetFramework(); | |
| 71 registerHostResolver(); | |
| 72 sendRequestAndWaitForResult(); | |
| 73 | |
| 74 assertErrorResponse(); | |
| 75 } | |
| 76 | |
| 77 /** | |
| 78 * Tests the case when the pin hash matches. The client is expected to | |
| 79 * receive the successful response with the response code 200. | |
| 80 * | |
| 81 * @throws Exception | |
| 82 */ | |
| 83 @SmallTest | |
| 84 @Feature({"Cronet"}) | |
| 85 public void testSuccessIfPinMatches() throws Exception { | |
| 86 // Get PKP hash of the real certificate | |
| 87 X509Certificate cert = readCertFromFileInPemFormat(CERT_USED); | |
| 88 byte[] matchingHash = CertTestUtil.getPublicKeySha256(cert); | |
| 89 | |
| 90 addPkpSha256(mServerHost, matchingHash, false); | |
| 91 startCronetFramework(); | |
| 92 registerHostResolver(); | |
| 93 sendRequestAndWaitForResult(); | |
| 94 | |
| 95 assertSuccessfulResponse(); | |
| 96 } | |
| 97 | |
| 98 /** | |
| 99 * Tests the case when the pin hash does not match and the client accesses t he subdomain of | |
| 100 * the configured HPKP host with includeSubdomains flag set to true. The cli ent is | |
| 101 * expected to receive the error response. | |
| 102 * | |
| 103 * @throws Exception | |
| 104 */ | |
| 105 @SmallTest | |
| 106 @Feature({"Cronet"}) | |
| 107 public void testIncludeSubdomainsFlagEqualTrue() throws Exception { | |
| 108 addPkpSha256(mDomain, generateSomeSha256(), true); | |
| 109 startCronetFramework(); | |
| 110 registerHostResolver(); | |
| 111 sendRequestAndWaitForResult(); | |
| 112 | |
| 113 assertErrorResponse(); | |
| 114 } | |
| 115 | |
| 116 /** | |
| 117 * Tests the case when the pin hash does not match and the client accesses t he subdomain of | |
| 118 * the configured HPKP host with includeSubdomains flag set to false. The cl ient is expected to | |
| 119 * receive the successful response with the response code 200. | |
| 120 * | |
| 121 * @throws Exception | |
| 122 */ | |
| 123 @SmallTest | |
| 124 @Feature({"Cronet"}) | |
| 125 public void testIncludeSubdomainsFlagEqualFalse() throws Exception { | |
| 126 addPkpSha256(mDomain, generateSomeSha256(), false); | |
| 127 startCronetFramework(); | |
| 128 registerHostResolver(); | |
| 129 sendRequestAndWaitForResult(); | |
| 130 | |
| 131 assertSuccessfulResponse(); | |
| 132 } | |
| 133 | |
| 134 /** | |
| 135 * Tests the case when the mismatching pin is set for some host that is diff erent from the one | |
| 136 * the client wants to access. In that case the other host pinning policy sh ould not be applied | |
| 137 * and the client is expected to receive the successful response with the re sponse code 200. | |
| 138 * | |
| 139 * @throws Exception | |
| 140 */ | |
| 141 @SmallTest | |
| 142 @Feature({"Cronet"}) | |
| 143 public void testSuccessIfNoPinSpecified() throws Exception { | |
| 144 addPkpSha256("somerandomhost.com", generateSomeSha256(), true); | |
| 145 startCronetFramework(); | |
| 146 registerHostResolver(); | |
| 147 sendRequestAndWaitForResult(); | |
| 148 | |
| 149 assertSuccessfulResponse(); | |
| 150 } | |
| 151 | |
| 152 /** | |
| 153 * Asserts that the response from the server contains an HPKP error. | |
| 154 * TODO(kapishnikov) currently QUIC returns ERR_QUIC_PROTOCOL_ERROR instead of expected | |
|
xunjieli
2015/11/06 15:47:54
nit: Probably only one TODO is needed. There shoul
kapishnikov
2015/11/06 16:18:13
Done.
| |
| 155 * TODO(kapishnikov) ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN error code when th e pin doesn't match. | |
| 156 * TODO(kapishnikov) This method should be changed when the bug is resolved. | |
| 157 * TODO(kapishnikov) See http://crbug.com/548378 | |
| 158 */ | |
| 159 private void assertErrorResponse() { | |
| 160 assertNotNull("Expected an error", mListener.mError); | |
| 161 int errorCode = mListener.mError.netError(); | |
| 162 boolean correctErrorCode = errorCode == NetError.ERR_SSL_PINNED_KEY_NOT_ IN_CERT_CHAIN | |
| 163 || errorCode == NetError.ERR_QUIC_PROTOCOL_ERROR; | |
| 164 assertTrue(String.format("Incorrect error code. Expected %s or %s but re ceived %s", | |
| 165 NetError.ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN, | |
| 166 NetError.ERR_QUIC_PROTOCOL_ERROR, errorCode), | |
| 167 correctErrorCode); | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * Asserts a successful response with response code 200. | |
| 172 */ | |
| 173 private void assertSuccessfulResponse() { | |
| 174 if (mListener.mError != null) { | |
| 175 fail("Did not expect an error but got error code " + mListener.mErro r.mNetError); | |
| 176 } | |
| 177 assertNotNull("Expected non-null response from the server", mListener.mR esponseInfo); | |
| 178 assertEquals(200, mListener.mResponseInfo.getHttpStatusCode()); | |
| 179 } | |
| 180 | |
| 181 private void startCronetFramework() { | |
| 182 mTestFramework = startCronetTestFrameworkWithUrlAndCronetEngineBuilder(n ull, mBuilder); | |
| 183 } | |
| 184 | |
| 185 private void registerHostResolver() { | |
| 186 long urlRequestContextAdapter = ((CronetUrlRequestContext) mTestFramewor k.mCronetEngine) | |
| 187 .getUrlRequestContextAdapter(); | |
| 188 NativeTestServer.registerHostResolverProc(urlRequestContextAdapter, fals e); | |
| 189 } | |
| 190 | |
| 191 private byte[] generateSomeSha256() { | |
| 192 byte[] sha256 = new byte[32]; | |
| 193 Arrays.fill(sha256, (byte) 58); | |
| 194 return sha256; | |
| 195 } | |
| 196 | |
| 197 private void addPkpSha256(String host, byte[] pinHashValue, boolean includeS ubdomain) { | |
| 198 Collection<byte[]> hashes = new ArrayList<>(); | |
| 199 hashes.add(pinHashValue); | |
| 200 mBuilder.addPublicKeyPins(host, hashes, includeSubdomain); | |
| 201 } | |
| 202 | |
| 203 private void sendRequestAndWaitForResult() { | |
| 204 mListener = new TestUrlRequestCallback(); | |
| 205 | |
| 206 String quicURL = mServerUrl + "/simple.txt"; | |
| 207 UrlRequest.Builder requestBuilder = new UrlRequest.Builder( | |
| 208 quicURL, mListener, mListener.getExecutor(), mTestFramework.mCro netEngine); | |
| 209 requestBuilder.build().start(); | |
| 210 mListener.blockForDone(); | |
| 211 } | |
| 212 | |
| 213 private X509Certificate readCertFromFileInPemFormat(String certFileName) thr ows Exception { | |
| 214 byte[] certDer = CertTestUtil.pemToDer(CertTestUtil.CERTS_DIRECTORY + ce rtFileName); | |
| 215 CertificateFactory certFactory = CertificateFactory.getInstance("X.509") ; | |
| 216 return (X509Certificate) certFactory.generateCertificate(new ByteArrayIn putStream(certDer)); | |
| 217 } | |
| 218 } | |
| OLD | NEW |