1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.security.net.config.cts; 18 19 import android.security.net.config.cts.CtsNetSecConfigDownloadManagerTestCases.R; 20 21 import android.app.DownloadManager; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.database.Cursor; 27 import android.net.Uri; 28 import android.os.SystemClock; 29 import android.text.format.DateUtils; 30 import android.util.Log; 31 32 import java.io.ByteArrayOutputStream; 33 import java.io.InputStream; 34 import java.net.Socket; 35 import java.net.ServerSocket; 36 import java.security.KeyFactory; 37 import java.security.KeyStore; 38 import java.security.PrivateKey; 39 import java.security.Provider; 40 import java.security.Security; 41 import java.security.Signature; 42 import java.security.cert.Certificate; 43 import java.security.cert.CertificateFactory; 44 import java.security.cert.X509Certificate; 45 import java.security.spec.PKCS8EncodedKeySpec; 46 import java.util.Collection; 47 import java.util.HashSet; 48 import java.util.concurrent.Callable; 49 import java.util.concurrent.ExecutionException; 50 import java.util.concurrent.FutureTask; 51 import java.util.concurrent.TimeUnit; 52 import java.util.concurrent.TimeoutException; 53 54 import javax.net.ssl.KeyManagerFactory; 55 import javax.net.ssl.SSLContext; 56 import javax.net.ssl.SSLServerSocket; 57 import javax.net.ssl.TrustManager; 58 import javax.net.ssl.TrustManagerFactory; 59 import javax.net.ssl.X509TrustManager; 60 61 public class DownloadManagerTest extends BaseTestCase { 62 63 private static final String HTTP_RESPONSE = 64 "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-length: 5\r\n\r\nhello"; 65 private static final long TIMEOUT = 3 * DateUtils.SECOND_IN_MILLIS; 66 testConfigTrustedCaAccepted()67 public void testConfigTrustedCaAccepted() throws Exception { 68 SSLServerSocket serverSocket = bindTLSServer(R.raw.valid_chain, R.raw.test_key); 69 runDownloadManagerTest(serverSocket, true); 70 } 71 testUntrustedCaRejected()72 public void testUntrustedCaRejected() throws Exception { 73 try { 74 SSLServerSocket serverSocket = bindTLSServer(R.raw.invalid_chain, R.raw.test_key); 75 runDownloadManagerTest(serverSocket, true); 76 fail("Invalid CA should be rejected"); 77 } catch (Exception expected) { 78 } 79 } 80 testPerDomainCleartextAccepted()81 public void testPerDomainCleartextAccepted() throws Exception { 82 ServerSocket serverSocket = new ServerSocket(); 83 serverSocket.bind(null); 84 runDownloadManagerTest(serverSocket, false); 85 } 86 runDownloadManagerTest(ServerSocket serverSocket, boolean https)87 private void runDownloadManagerTest(ServerSocket serverSocket, boolean https) throws Exception { 88 DownloadManager dm = 89 (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); 90 DownloadCompleteReceiver receiver = new DownloadCompleteReceiver(); 91 FutureTask<Void> serverFuture = new FutureTask<Void>(new Callable() { 92 @Override 93 public Void call() throws Exception { 94 runServer(serverSocket); 95 return null; 96 } 97 }); 98 try { 99 IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); 100 getContext().registerReceiver(receiver, filter); 101 new Thread(serverFuture).start(); 102 String host = (https ? "https" : "http") + "://localhost"; 103 Uri destination = Uri.parse(host + ":" + serverSocket.getLocalPort()); 104 long id = dm.enqueue(new DownloadManager.Request(destination)); 105 try { 106 serverFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); 107 // Check that the download was successful. 108 receiver.waitForDownloadComplete(TIMEOUT, id); 109 assertSuccessfulDownload(id); 110 } catch (InterruptedException e) { 111 // Wrap InterruptedException since otherwise it gets eaten by AndroidTest 112 throw new RuntimeException(e); 113 } finally { 114 dm.remove(id); 115 } 116 } finally { 117 getContext().unregisterReceiver(receiver); 118 serverFuture.cancel(true); 119 try { 120 serverSocket.close(); 121 } catch (Exception ignored) {} 122 } 123 } 124 runServer(ServerSocket server)125 private void runServer(ServerSocket server) throws Exception { 126 Socket s = server.accept(); 127 s.getOutputStream().write(HTTP_RESPONSE.getBytes()); 128 s.getOutputStream().flush(); 129 s.close(); 130 } 131 bindTLSServer(int chainResId, int keyResId)132 private SSLServerSocket bindTLSServer(int chainResId, int keyResId) throws Exception { 133 // Load certificate chain. 134 CertificateFactory fact = CertificateFactory.getInstance("X.509"); 135 Collection<? extends Certificate> certs; 136 try (InputStream is = getContext().getResources().openRawResource(chainResId)) { 137 certs = fact.generateCertificates(is); 138 } 139 X509Certificate[] chain = new X509Certificate[certs.size()]; 140 int i = 0; 141 for (Certificate cert : certs) { 142 chain[i++] = (X509Certificate) cert; 143 } 144 145 // Load private key for the leaf. 146 PrivateKey key; 147 try (InputStream is = getContext().getResources().openRawResource(keyResId)) { 148 ByteArrayOutputStream keyout = new ByteArrayOutputStream(); 149 byte[] buffer = new byte[4096]; 150 int chunk_size; 151 while ((chunk_size = is.read(buffer)) != -1) { 152 keyout.write(buffer, 0, chunk_size); 153 } 154 is.close(); 155 byte[] keyBytes = keyout.toByteArray(); 156 key = KeyFactory.getInstance("RSA") 157 .generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); 158 } 159 160 // Create KeyStore based on the private key/chain. 161 KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); 162 ks.load(null); 163 ks.setKeyEntry("name", key, null, chain); 164 165 // Create SSLContext. 166 TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); 167 tmf.init(ks); 168 KeyManagerFactory kmf = 169 KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 170 kmf.init(ks, null); 171 SSLContext context = SSLContext.getInstance("TLS"); 172 context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 173 174 SSLServerSocket s = (SSLServerSocket) context.getServerSocketFactory().createServerSocket(); 175 s.bind(null); 176 return s; 177 } 178 assertSuccessfulDownload(long id)179 private void assertSuccessfulDownload(long id) throws Exception { 180 Cursor cursor = null; 181 DownloadManager dm = 182 (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE); 183 try { 184 cursor = dm.query(new DownloadManager.Query().setFilterById(id)); 185 assertTrue(cursor.moveToNext()); 186 assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt( 187 cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))); 188 } finally { 189 if (cursor != null) { 190 cursor.close(); 191 } 192 } 193 } 194 195 private static final class DownloadCompleteReceiver extends BroadcastReceiver { 196 private HashSet<Long> mCompletedDownloads = new HashSet<>(); 197 DownloadCompleteReceiver()198 public DownloadCompleteReceiver() { 199 } 200 201 @Override onReceive(Context context, Intent intent)202 public void onReceive(Context context, Intent intent) { 203 synchronized(mCompletedDownloads) { 204 mCompletedDownloads.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)); 205 mCompletedDownloads.notifyAll(); 206 } 207 } 208 waitForDownloadComplete(long timeout, long id)209 public void waitForDownloadComplete(long timeout, long id) 210 throws TimeoutException, InterruptedException { 211 long deadline = SystemClock.elapsedRealtime() + timeout; 212 do { 213 synchronized (mCompletedDownloads) { 214 long millisTillTimeout = deadline - SystemClock.elapsedRealtime(); 215 if (millisTillTimeout > 0) { 216 mCompletedDownloads.wait(millisTillTimeout); 217 } 218 if (mCompletedDownloads.contains(id)) { 219 return; 220 } 221 } 222 } while (SystemClock.elapsedRealtime() < deadline); 223 224 throw new TimeoutException("Timed out waiting for download complete"); 225 } 226 } 227 228 229 } 230