• 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 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 }