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 com.google.attestationexample; 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 27 import android.content.Context; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.PackageManager.NameNotFoundException; 31 import android.content.pm.Signature; 32 33 public class AttestationApplicationId implements java.lang.Comparable<AttestationApplicationId> { 34 private static final int PACKAGE_INFOS_INDEX = 0; 35 private static final int SIGNATURE_DIGESTS_INDEX = 1; 36 37 private final List<AttestationPackageInfo> packageInfos; 38 private final List<byte[]> signatureDigests; 39 AttestationApplicationId(Context context)40 public AttestationApplicationId(Context context) 41 throws NoSuchAlgorithmException, NameNotFoundException { 42 PackageManager pm = context.getPackageManager(); 43 int uid = context.getApplicationInfo().uid; 44 String[] packageNames = pm.getPackagesForUid(uid); 45 if (packageNames == null || packageNames.length == 0) { 46 throw new NameNotFoundException("No names found for uid"); 47 } 48 packageInfos = new ArrayList<AttestationPackageInfo>(); 49 for (String packageName : packageNames) { 50 // get the package info for the given package name including 51 // the signatures 52 PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); 53 packageInfos.add(new AttestationPackageInfo(packageName, packageInfo.versionCode)); 54 } 55 // The infos must be sorted, the implementation of Comparable relies on it. 56 packageInfos.sort(null); 57 58 // compute the sha256 digests of the signature blobs 59 signatureDigests = new ArrayList<byte[]>(); 60 PackageInfo packageInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 61 for (Signature signature : packageInfo.signatures) { 62 MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); 63 signatureDigests.add(sha256.digest(signature.toByteArray())); 64 } 65 // The digests must be sorted. the implementation of Comparable relies on it 66 signatureDigests.sort(new ByteArrayComparator()); 67 } 68 AttestationApplicationId(ASN1Encodable asn1Encodable)69 public AttestationApplicationId(ASN1Encodable asn1Encodable) 70 throws CertificateParsingException { 71 if (!(asn1Encodable instanceof ASN1Sequence)) { 72 throw new CertificateParsingException( 73 "Expected sequence for AttestationApplicationId, found " 74 + asn1Encodable.getClass().getName()); 75 } 76 77 ASN1Sequence sequence = (ASN1Sequence) asn1Encodable; 78 packageInfos = parseAttestationPackageInfos(sequence.getObjectAt(PACKAGE_INFOS_INDEX)); 79 // The infos must be sorted, the implementation of Comparable relies on it. 80 packageInfos.sort(null); 81 signatureDigests = parseSignatures(sequence.getObjectAt(SIGNATURE_DIGESTS_INDEX)); 82 // The digests must be sorted. the implementation of Comparable relies on it 83 signatureDigests.sort(new ByteArrayComparator()); 84 } 85 getAttestationPackageInfos()86 public List<AttestationPackageInfo> getAttestationPackageInfos() { 87 return packageInfos; 88 } 89 getSignatureDigests()90 public List<byte[]> getSignatureDigests() { 91 return signatureDigests; 92 } 93 94 @Override toString()95 public String toString() { 96 StringBuilder sb = new StringBuilder(); 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 }