• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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