Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package android.keystore.cts;
     16 
     17 import com.android.org.bouncycastle.asn1.ASN1Encodable;
     18 import com.android.org.bouncycastle.asn1.ASN1Sequence;
     19 import com.android.org.bouncycastle.asn1.ASN1Set;
     20 
     21 import java.security.cert.CertificateParsingException;
     22 import java.security.MessageDigest;
     23 import java.security.NoSuchAlgorithmException;
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 import android.content.Context;
     27 import android.content.pm.PackageInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.PackageManager.NameNotFoundException;
     30 import android.content.pm.Signature;
     31 
     32 public class AttestationApplicationId implements java.lang.Comparable<AttestationApplicationId> {
     33     private static final int PACKAGE_INFOS_INDEX = 0;
     34     private static final int SIGNATURE_DIGESTS_INDEX = 1;
     35 
     36     private final List<AttestationPackageInfo> packageInfos;
     37     private final List<byte[]> signatureDigests;
     38 
     39     public AttestationApplicationId(Context context)
     40             throws NoSuchAlgorithmException, NameNotFoundException {
     41         PackageManager pm = context.getPackageManager();
     42         int uid = context.getApplicationInfo().uid;
     43         String[] packageNames = pm.getPackagesForUid(uid);
     44         if (packageNames == null || packageNames.length == 0) {
     45             throw new NameNotFoundException("No names found for uid");
     46         }
     47         packageInfos = new ArrayList<AttestationPackageInfo>();
     48         for (String packageName : packageNames) {
     49             // get the package info for the given package name including
     50             // the signatures
     51             PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
     52             packageInfos.add(new AttestationPackageInfo(packageName, packageInfo.versionCode));
     53         }
     54         // The infos must be sorted, the implementation of Comparable relies on it.
     55         packageInfos.sort(null);
     56 
     57         // compute the sha256 digests of the signature blobs
     58         signatureDigests = new ArrayList<byte[]>();
     59         PackageInfo packageInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
     60         for (Signature signature : packageInfo.signatures) {
     61             MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
     62             signatureDigests.add(sha256.digest(signature.toByteArray()));
     63         }
     64         // The digests must be sorted. the implementation of Comparable relies on it
     65         signatureDigests.sort(new ByteArrayComparator());
     66     }
     67 
     68     public AttestationApplicationId(ASN1Encodable asn1Encodable)
     69             throws CertificateParsingException {
     70         if (!(asn1Encodable instanceof ASN1Sequence)) {
     71             throw new CertificateParsingException(
     72                     "Expected sequence for AttestationApplicationId, found "
     73                             + asn1Encodable.getClass().getName());
     74         }
     75 
     76         ASN1Sequence sequence = (ASN1Sequence) asn1Encodable;
     77         packageInfos = parseAttestationPackageInfos(sequence.getObjectAt(PACKAGE_INFOS_INDEX));
     78         // The infos must be sorted, the implementation of Comparable relies on it.
     79         packageInfos.sort(null);
     80         signatureDigests = parseSignatures(sequence.getObjectAt(SIGNATURE_DIGESTS_INDEX));
     81         // The digests must be sorted. the implementation of Comparable relies on it
     82         signatureDigests.sort(new ByteArrayComparator());
     83     }
     84 
     85     public List<AttestationPackageInfo> getAttestationPackageInfos() {
     86         return packageInfos;
     87     }
     88 
     89     public List<byte[]> getSignatureDigests() {
     90         return signatureDigests;
     91     }
     92 
     93     @Override
     94     public String toString() {
     95         StringBuilder sb = new StringBuilder();
     96         sb.append("AttestationApplicationId:");
     97         int noOfInfos = packageInfos.size();
     98         int i = 1;
     99         for (AttestationPackageInfo info : packageInfos) {
    100             sb.append("\n### Package info " + i + "/" + noOfInfos + " ###\n");
    101             sb.append(info);
    102         }
    103         i = 1;
    104         int noOfSigs = signatureDigests.size();
    105         for (byte[] sig : signatureDigests) {
    106             sb.append("\nSignature digest " + i++ + "/" + noOfSigs + ":");
    107             for (byte b : sig) {
    108                 sb.append(String.format(" %02X", b));
    109             }
    110         }
    111         return sb.toString();
    112     }
    113 
    114     @Override
    115     public int compareTo(AttestationApplicationId other) {
    116         int res = Integer.compare(packageInfos.size(), other.packageInfos.size());
    117         if (res != 0) return res;
    118         for (int i = 0; i < packageInfos.size(); ++i) {
    119             res = packageInfos.get(i).compareTo(other.packageInfos.get(i));
    120             if (res != 0) return res;
    121         }
    122         res = Integer.compare(signatureDigests.size(), other.signatureDigests.size());
    123         if (res != 0) return res;
    124         ByteArrayComparator cmp = new ByteArrayComparator();
    125         for (int i = 0; i < signatureDigests.size(); ++i) {
    126             res = cmp.compare(signatureDigests.get(i), other.signatureDigests.get(i));
    127             if (res != 0) return res;
    128         }
    129         return res;
    130     }
    131 
    132     @Override
    133     public boolean equals(Object o) {
    134         return (o instanceof AttestationApplicationId)
    135                 && (0 == compareTo((AttestationApplicationId) o));
    136     }
    137 
    138     private List<AttestationPackageInfo> parseAttestationPackageInfos(ASN1Encodable asn1Encodable)
    139             throws CertificateParsingException {
    140         if (!(asn1Encodable instanceof ASN1Set)) {
    141             throw new CertificateParsingException(
    142                     "Expected set for AttestationApplicationsInfos, found "
    143                             + asn1Encodable.getClass().getName());
    144         }
    145 
    146         ASN1Set set = (ASN1Set) asn1Encodable;
    147         List<AttestationPackageInfo> result = new ArrayList<AttestationPackageInfo>();
    148         for (ASN1Encodable e : set) {
    149             result.add(new AttestationPackageInfo(e));
    150         }
    151         return result;
    152     }
    153 
    154     private List<byte[]> parseSignatures(ASN1Encodable asn1Encodable)
    155             throws CertificateParsingException {
    156         if (!(asn1Encodable instanceof ASN1Set)) {
    157             throw new CertificateParsingException("Expected set for Signature digests, found "
    158                     + asn1Encodable.getClass().getName());
    159         }
    160 
    161         ASN1Set set = (ASN1Set) asn1Encodable;
    162         List<byte[]> result = new ArrayList<byte[]>();
    163 
    164         for (ASN1Encodable e : set) {
    165             result.add(Asn1Utils.getByteArrayFromAsn1(e));
    166         }
    167         return result;
    168     }
    169 
    170     private class ByteArrayComparator implements java.util.Comparator<byte[]> {
    171         @Override
    172         public int compare(byte[] a, byte[] b) {
    173             int res = Integer.compare(a.length, b.length);
    174             if (res != 0) return res;
    175             for (int i = 0; i < a.length; ++i) {
    176                 res = Byte.compare(a[i], b[i]);
    177                 if (res != 0) return res;
    178             }
    179             return res;
    180         }
    181     }
    182 }
    183