1 /* 2 * Copyright (C) 2012 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 com.android.server.updates; 18 19 import com.android.internal.util.HexDump; 20 21 import android.content.Context; 22 import android.content.Intent; 23 import android.test.AndroidTestCase; 24 import android.provider.Settings; 25 import android.util.Base64; 26 import android.util.Log; 27 28 import java.io.ByteArrayInputStream; 29 import java.io.File; 30 import java.io.FileInputStream; 31 import java.io.FileOutputStream; 32 import java.io.FileWriter; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.security.cert.CertificateFactory; 36 import java.security.cert.Certificate; 37 import java.security.cert.X509Certificate; 38 import java.security.MessageDigest; 39 import java.security.NoSuchAlgorithmException; 40 import java.security.PrivateKey; 41 import java.security.Signature; 42 import java.security.spec.PKCS8EncodedKeySpec; 43 import java.security.KeyFactory; 44 import java.util.HashSet; 45 import java.io.*; 46 import libcore.io.IoUtils; 47 48 /** 49 * Tests for {@link com.android.server.CertPinInstallReceiver} 50 */ 51 public class CertPinInstallReceiverTest extends AndroidTestCase { 52 53 private static final String TAG = "CertPinInstallReceiverTest"; 54 55 private static final String PINLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/"; 56 57 public static final String PINLIST_CONTENT_PATH = PINLIST_ROOT + "pins"; 58 public static final String PINLIST_METADATA_PATH = PINLIST_CONTENT_PATH + "metadata"; 59 60 public static final String PINLIST_CONTENT_URL_KEY = "pinlist_content_url"; 61 public static final String PINLIST_METADATA_URL_KEY = "pinlist_metadata_url"; 62 public static final String PINLIST_CERTIFICATE_KEY = "config_update_certificate"; 63 public static final String PINLIST_VERSION_KEY = "pinlist_version"; 64 65 private static final String EXTRA_CONTENT_PATH = "CONTENT_PATH"; 66 private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH"; 67 private static final String EXTRA_SIGNATURE = "SIGNATURE"; 68 private static final String EXTRA_VERSION_NUMBER = "VERSION"; 69 70 public static final String TEST_CERT = "" + 71 "MIIDsjCCAxugAwIBAgIJAPLf2gS0zYGUMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYDVQQGEwJVUzET" + 72 "MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEPMA0GA1UEChMGR29v" + 73 "Z2xlMRAwDgYDVQQLEwd0ZXN0aW5nMRYwFAYDVQQDEw1HZXJlbXkgQ29uZHJhMSEwHwYJKoZIhvcN" + 74 "AQkBFhJnY29uZHJhQGdvb2dsZS5jb20wHhcNMTIwNzE0MTc1MjIxWhcNMTIwODEzMTc1MjIxWjCB" + 75 "mDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZp" + 76 "ZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHdGVzdGluZzEWMBQGA1UEAxMNR2VyZW15IENv" + 77 "bmRyYTEhMB8GCSqGSIb3DQEJARYSZ2NvbmRyYUBnb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA" + 78 "A4GNADCBiQKBgQCjGGHATBYlmas+0sEECkno8LZ1KPglb/mfe6VpCT3GhSr+7br7NG/ZwGZnEhLq" + 79 "E7YIH4fxltHmQC3Tz+jM1YN+kMaQgRRjo/LBCJdOKaMwUbkVynAH6OYsKevjrOPk8lfM5SFQzJMG" + 80 "sA9+Tfopr5xg0BwZ1vA/+E3mE7Tr3M2UvwIDAQABo4IBADCB/TAdBgNVHQ4EFgQUhzkS9E6G+x8W" + 81 "L4EsmRjDxu28tHUwgc0GA1UdIwSBxTCBwoAUhzkS9E6G+x8WL4EsmRjDxu28tHWhgZ6kgZswgZgx" + 82 "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3" + 83 "MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB3Rlc3RpbmcxFjAUBgNVBAMTDUdlcmVteSBDb25k" + 84 "cmExITAfBgkqhkiG9w0BCQEWEmdjb25kcmFAZ29vZ2xlLmNvbYIJAPLf2gS0zYGUMAwGA1UdEwQF" + 85 "MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYiugFDmbDOQ2U/+mqNt7o8ftlEo9SJrns6O8uTtK6AvR" + 86 "orDrR1AXTXkuxwLSbmVfedMGOZy7Awh7iZa8hw5x9XmUudfNxvmrKVEwGQY2DZ9PXbrnta/dwbhK" + 87 "mWfoepESVbo7CKIhJp8gRW0h1Z55ETXD57aGJRvQS4pxkP8ANhM="; 88 89 90 public static final String TEST_KEY = "" + 91 "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKMYYcBMFiWZqz7SwQQKSejwtnUo" + 92 "+CVv+Z97pWkJPcaFKv7tuvs0b9nAZmcSEuoTtggfh/GW0eZALdPP6MzVg36QxpCBFGOj8sEIl04p" + 93 "ozBRuRXKcAfo5iwp6+Os4+TyV8zlIVDMkwawD35N+imvnGDQHBnW8D/4TeYTtOvczZS/AgMBAAEC" + 94 "gYBxwFalNSwZK3WJipq+g6KLCiBn1JxGGDQlLKrweFaSuFyFky9fd3IvkIabirqQchD612sMb+GT" + 95 "0t1jptW6z4w2w6++IW0A3apDOCwoD+uvDBXrbFqI0VbyAWUNqHVdaFFIRk2IHGEE6463mGRdmILX" + 96 "IlCd/85RTHReg4rl/GFqWQJBANgLAIR4pWbl5Gm+DtY18wp6Q3pJAAMkmP/lISCBIidu1zcqYIKt" + 97 "PoDW4Knq9xnhxPbXrXKv4YzZWHBK8GkKhQ0CQQDBQnXufQcMew+PwiS0oJvS+eQ6YJwynuqG2ejg" + 98 "WE+T7489jKtscRATpUXpZUYmDLGg9bLt7L62hFvFSj2LO2X7AkBcdrD9AWnBFWlh/G77LVHczSEu" + 99 "KCoyLiqxcs5vy/TjLaQ8vw1ZQG580/qJnr+tOxyCjSJ18GK3VppsTRaBznfNAkB3nuCKNp9HTWCL" + 100 "dfrsRsFMrFpk++mSt6SoxXaMbn0LL2u1CD4PCEiQMGt+lK3/3TmRTKNs+23sYS7Ahjxj0udDAkEA" + 101 "p57Nj65WNaWeYiOfTwKXkLj8l29H5NbaGWxPT0XkWr4PvBOFZVH/wj0/qc3CMVGnv11+DyO+QUCN" + 102 "SqBB5aRe8g=="; 103 overrideSettings(String key, String value)104 private void overrideSettings(String key, String value) throws Exception { 105 assertTrue(Settings.Secure.putString(mContext.getContentResolver(), key, value)); 106 Thread.sleep(1000); 107 } 108 overrideCert(String value)109 private void overrideCert(String value) throws Exception { 110 overrideSettings(PINLIST_CERTIFICATE_KEY, value); 111 } 112 readPins()113 private String readPins() throws Exception { 114 return IoUtils.readFileAsString(PINLIST_CONTENT_PATH); 115 } 116 readCurrentVersion()117 private String readCurrentVersion() throws Exception { 118 return IoUtils.readFileAsString("/data/misc/keychain/metadata/version"); 119 } 120 getNextVersion()121 private String getNextVersion() throws Exception { 122 int currentVersion = Integer.parseInt(readCurrentVersion()); 123 return Integer.toString(currentVersion + 1); 124 } 125 getCurrentHash(String content)126 private static String getCurrentHash(String content) throws Exception { 127 if (content == null) { 128 return "0"; 129 } 130 MessageDigest dgst = MessageDigest.getInstance("SHA512"); 131 byte[] encoded = content.getBytes(); 132 byte[] fingerprint = dgst.digest(encoded); 133 return HexDump.toHexString(fingerprint, false); 134 } 135 getHashOfCurrentContent()136 private static String getHashOfCurrentContent() throws Exception { 137 String content = IoUtils.readFileAsString("/data/misc/keychain/pins"); 138 return getCurrentHash(content); 139 } 140 createKey()141 private PrivateKey createKey() throws Exception { 142 byte[] derKey = Base64.decode(TEST_KEY.getBytes(), Base64.DEFAULT); 143 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(derKey); 144 KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 145 return (PrivateKey) keyFactory.generatePrivate(keySpec); 146 } 147 createCertificate()148 private X509Certificate createCertificate() throws Exception { 149 byte[] derCert = Base64.decode(TEST_CERT.getBytes(), Base64.DEFAULT); 150 InputStream istream = new ByteArrayInputStream(derCert); 151 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 152 return (X509Certificate) cf.generateCertificate(istream); 153 } 154 makeTemporaryContentFile(String content)155 private String makeTemporaryContentFile(String content) throws Exception { 156 FileOutputStream fw = mContext.openFileOutput("content.txt", mContext.MODE_WORLD_READABLE); 157 fw.write(content.getBytes(), 0, content.length()); 158 fw.close(); 159 return mContext.getFilesDir() + "/content.txt"; 160 } 161 createSignature(String content, String version, String requiredHash)162 private String createSignature(String content, String version, String requiredHash) 163 throws Exception { 164 Signature signer = Signature.getInstance("SHA512withRSA"); 165 signer.initSign(createKey()); 166 signer.update(content.trim().getBytes()); 167 signer.update(version.trim().getBytes()); 168 signer.update(requiredHash.getBytes()); 169 String sig = new String(Base64.encode(signer.sign(), Base64.DEFAULT)); 170 assertEquals(true, 171 verifySignature(content, version, requiredHash, sig, createCertificate())); 172 return sig; 173 } 174 verifySignature(String content, String version, String requiredPrevious, String signature, X509Certificate cert)175 public boolean verifySignature(String content, String version, String requiredPrevious, 176 String signature, X509Certificate cert) throws Exception { 177 Signature signer = Signature.getInstance("SHA512withRSA"); 178 signer.initVerify(cert); 179 signer.update(content.trim().getBytes()); 180 signer.update(version.trim().getBytes()); 181 signer.update(requiredPrevious.trim().getBytes()); 182 return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT)); 183 } 184 sendIntent(String contentPath, String version, String required, String sig)185 private void sendIntent(String contentPath, String version, String required, String sig) { 186 Intent i = new Intent(); 187 i.setAction("android.intent.action.UPDATE_PINS"); 188 i.putExtra(EXTRA_CONTENT_PATH, contentPath); 189 i.putExtra(EXTRA_VERSION_NUMBER, version); 190 i.putExtra(EXTRA_REQUIRED_HASH, required); 191 i.putExtra(EXTRA_SIGNATURE, sig); 192 mContext.sendBroadcast(i); 193 } 194 runTest(String cert, String content, String version, String required, String sig)195 private String runTest(String cert, String content, String version, String required, String sig) 196 throws Exception { 197 Log.e(TAG, "started test"); 198 overrideCert(cert); 199 String contentPath = makeTemporaryContentFile(content); 200 sendIntent(contentPath, version, required, sig); 201 Thread.sleep(1000); 202 return readPins(); 203 } 204 runTestWithoutSig(String cert, String content, String version, String required)205 private String runTestWithoutSig(String cert, String content, String version, String required) 206 throws Exception { 207 String sig = createSignature(content, version, required); 208 return runTest(cert, content, version, required, sig); 209 } 210 testOverwritePinlist()211 public void testOverwritePinlist() throws Exception { 212 Log.e(TAG, "started testOverwritePinList"); 213 assertEquals("abcde", runTestWithoutSig(TEST_CERT, "abcde", getNextVersion(), getHashOfCurrentContent())); 214 Log.e(TAG, "started testOverwritePinList"); 215 } 216 testBadSignatureFails()217 public void testBadSignatureFails() throws Exception { 218 Log.e(TAG, "started testOverwritePinList"); 219 String text = "blahblah"; 220 runTestWithoutSig(TEST_CERT, text, getNextVersion(), getHashOfCurrentContent()); 221 assertEquals(text, runTest(TEST_CERT, "bcdef", getNextVersion(), getCurrentHash(text), "")); 222 Log.e(TAG, "started testOverwritePinList"); 223 } 224 testBadRequiredHashFails()225 public void testBadRequiredHashFails() throws Exception { 226 runTestWithoutSig(TEST_CERT, "blahblahblah", getNextVersion(), getHashOfCurrentContent()); 227 assertEquals("blahblahblah", runTestWithoutSig(TEST_CERT, "cdefg", getNextVersion(), "0")); 228 Log.e(TAG, "started testOverwritePinList"); 229 } 230 testBadVersionFails()231 public void testBadVersionFails() throws Exception { 232 String text = "blahblahblahblah"; 233 String version = getNextVersion(); 234 runTestWithoutSig(TEST_CERT, text, version, getHashOfCurrentContent()); 235 assertEquals(text, runTestWithoutSig(TEST_CERT, "defgh", version, getCurrentHash(text))); 236 Log.e(TAG, "started testOverwritePinList"); 237 } 238 testOverrideRequiredHash()239 public void testOverrideRequiredHash() throws Exception { 240 runTestWithoutSig(TEST_CERT, "blahblahblah", getNextVersion(), getHashOfCurrentContent()); 241 assertEquals("blahblahblah", runTestWithoutSig(TEST_CERT, "cdefg", "NONE", "0")); 242 Log.e(TAG, "started testOverwritePinList"); 243 } 244 245 } 246