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 org.bouncycastle.asn1.ASN1Encodable; 18 import org.bouncycastle.asn1.ASN1Sequence; 19 import 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 AttestationApplicationId(Context context)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 AttestationApplicationId(ASN1Encodable asn1Encodable)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 getAttestationPackageInfos()85 public List<AttestationPackageInfo> getAttestationPackageInfos() { 86 return packageInfos; 87 } 88 getSignatureDigests()89 public List<byte[]> getSignatureDigests() { 90 return signatureDigests; 91 } 92 93 @Override toString()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 compareTo(AttestationApplicationId other)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 equals(Object o)133 public boolean equals(Object o) { 134 return (o instanceof AttestationApplicationId) 135 && (0 == compareTo((AttestationApplicationId) o)); 136 } 137 parseAttestationPackageInfos(ASN1Encodable asn1Encodable)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 parseSignatures(ASN1Encodable asn1Encodable)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 compare(byte[] a, byte[] b)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