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