• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.telephony;
17 
18 import android.annotation.Nullable;
19 import android.annotation.SystemApi;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.Signature;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.text.TextUtils;
25 
26 import com.android.internal.telephony.uicc.IccUtils;
27 
28 import java.io.ByteArrayInputStream;
29 import java.io.ByteArrayOutputStream;
30 import java.io.DataInputStream;
31 import java.io.DataOutputStream;
32 import java.io.IOException;
33 import java.security.MessageDigest;
34 import java.security.NoSuchAlgorithmException;
35 import java.util.Arrays;
36 import java.util.Objects;
37 
38 /**
39  * Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control
40  * specification.
41  *
42  * @hide
43  */
44 @SystemApi
45 public final class UiccAccessRule implements Parcelable {
46     private static final String TAG = "UiccAccessRule";
47 
48     private static final int ENCODING_VERSION = 1;
49 
50     public static final @android.annotation.NonNull Creator<UiccAccessRule> CREATOR = new Creator<UiccAccessRule>() {
51         @Override
52         public UiccAccessRule createFromParcel(Parcel in) {
53             return new UiccAccessRule(in);
54         }
55 
56         @Override
57         public UiccAccessRule[] newArray(int size) {
58             return new UiccAccessRule[size];
59         }
60     };
61 
62     /**
63      * Encode these access rules as a byte array which can be parsed with {@link #decodeRules}.
64      * @hide
65      */
66     @Nullable
encodeRules(@ullable UiccAccessRule[] accessRules)67     public static byte[] encodeRules(@Nullable UiccAccessRule[] accessRules) {
68         if (accessRules == null) {
69             return null;
70         }
71         try {
72             ByteArrayOutputStream baos = new ByteArrayOutputStream();
73             DataOutputStream output = new DataOutputStream(baos);
74             output.writeInt(ENCODING_VERSION);
75             output.writeInt(accessRules.length);
76             for (UiccAccessRule accessRule : accessRules) {
77                 output.writeInt(accessRule.mCertificateHash.length);
78                 output.write(accessRule.mCertificateHash);
79                 if (accessRule.mPackageName != null) {
80                     output.writeBoolean(true);
81                     output.writeUTF(accessRule.mPackageName);
82                 } else {
83                     output.writeBoolean(false);
84                 }
85                 output.writeLong(accessRule.mAccessType);
86             }
87             output.close();
88             return baos.toByteArray();
89         } catch (IOException e) {
90             throw new IllegalStateException(
91                     "ByteArrayOutputStream should never lead to an IOException", e);
92         }
93     }
94 
95     /**
96      * Decodes a byte array generated with {@link #encodeRules}.
97      * @hide
98      */
99     @Nullable
decodeRules(@ullable byte[] encodedRules)100     public static UiccAccessRule[] decodeRules(@Nullable byte[] encodedRules) {
101         if (encodedRules == null) {
102             return null;
103         }
104         ByteArrayInputStream bais = new ByteArrayInputStream(encodedRules);
105         try (DataInputStream input = new DataInputStream(bais)) {
106             input.readInt(); // version; currently ignored
107             int count = input.readInt();
108             UiccAccessRule[] accessRules = new UiccAccessRule[count];
109             for (int i = 0; i < count; i++) {
110                 int certificateHashLength = input.readInt();
111                 byte[] certificateHash = new byte[certificateHashLength];
112                 input.readFully(certificateHash);
113                 String packageName = input.readBoolean() ? input.readUTF() : null;
114                 long accessType = input.readLong();
115                 accessRules[i] = new UiccAccessRule(certificateHash, packageName, accessType);
116             }
117             input.close();
118             return accessRules;
119         } catch (IOException e) {
120             throw new IllegalStateException(
121                     "ByteArrayInputStream should never lead to an IOException", e);
122         }
123     }
124 
125     private final byte[] mCertificateHash;
126     private final @Nullable String mPackageName;
127     // This bit is not currently used, but reserved for future use.
128     private final long mAccessType;
129 
UiccAccessRule(byte[] certificateHash, @Nullable String packageName, long accessType)130     public UiccAccessRule(byte[] certificateHash, @Nullable String packageName, long accessType) {
131         this.mCertificateHash = certificateHash;
132         this.mPackageName = packageName;
133         this.mAccessType = accessType;
134     }
135 
UiccAccessRule(Parcel in)136     UiccAccessRule(Parcel in) {
137         mCertificateHash = in.createByteArray();
138         mPackageName = in.readString();
139         mAccessType = in.readLong();
140     }
141 
142     @Override
writeToParcel(Parcel dest, int flags)143     public void writeToParcel(Parcel dest, int flags) {
144         dest.writeByteArray(mCertificateHash);
145         dest.writeString(mPackageName);
146         dest.writeLong(mAccessType);
147     }
148 
149     /**
150      * Return the package name this rule applies to.
151      *
152      * @return the package name, or null if this rule applies to any package signed with the given
153      *     certificate.
154      */
getPackageName()155     public @Nullable String getPackageName() {
156         return mPackageName;
157     }
158 
159     /**
160      * Returns the hex string of the certificate hash.
161      */
getCertificateHexString()162     public String getCertificateHexString() {
163         return IccUtils.bytesToHexString(mCertificateHash);
164     }
165 
166     /**
167      * Returns the carrier privilege status associated with the given package.
168      *
169      * @param packageInfo package info fetched from
170      *     {@link android.content.pm.PackageManager#getPackageInfo}.
171      *     {@link android.content.pm.PackageManager#GET_SIGNATURES} must have been passed in.
172      * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
173      *     {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
174      */
getCarrierPrivilegeStatus(PackageInfo packageInfo)175     public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
176         if (packageInfo.signatures == null || packageInfo.signatures.length == 0) {
177             throw new IllegalArgumentException(
178                     "Must use GET_SIGNATURES when looking up package info");
179         }
180 
181         for (Signature sig : packageInfo.signatures) {
182             int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName);
183             if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
184                 return accessStatus;
185             }
186         }
187 
188         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
189     }
190 
191     /**
192      * Returns the carrier privilege status for the given certificate and package name.
193      *
194      * @param signature The signature of the certificate.
195      * @param packageName name of the package.
196      * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
197      *     {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
198      */
getCarrierPrivilegeStatus(Signature signature, String packageName)199     public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
200         // SHA-1 is for backward compatible support only, strongly discouraged for new use.
201         byte[] certHash = getCertHash(signature, "SHA-1");
202         byte[] certHash256 = getCertHash(signature, "SHA-256");
203         if (matches(certHash, packageName) || matches(certHash256, packageName)) {
204             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
205         }
206 
207         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
208     }
209 
matches(byte[] certHash, String packageName)210     private boolean matches(byte[] certHash, String packageName) {
211         return certHash != null && Arrays.equals(this.mCertificateHash, certHash) &&
212                 (TextUtils.isEmpty(this.mPackageName) || this.mPackageName.equals(packageName));
213     }
214 
215     @Override
equals(Object obj)216     public boolean equals(Object obj) {
217         if (this == obj) {
218             return true;
219         }
220         if (obj == null || getClass() != obj.getClass()) {
221             return false;
222         }
223 
224         UiccAccessRule that = (UiccAccessRule) obj;
225         return Arrays.equals(mCertificateHash, that.mCertificateHash)
226                 && Objects.equals(mPackageName, that.mPackageName)
227                 && mAccessType == that.mAccessType;
228     }
229 
230     @Override
hashCode()231     public int hashCode() {
232         int result = 1;
233         result = 31 * result + Arrays.hashCode(mCertificateHash);
234         result = 31 * result + Objects.hashCode(mPackageName);
235         result = 31 * result + Objects.hashCode(mAccessType);
236         return result;
237     }
238 
239     @Override
toString()240     public String toString() {
241         return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " +
242                 mPackageName + " access: " + mAccessType;
243     }
244 
245     @Override
describeContents()246     public int describeContents() {
247         return 0;
248     }
249 
250     /**
251      * Converts a Signature into a Certificate hash usable for comparison.
252      */
getCertHash(Signature signature, String algo)253     private static byte[] getCertHash(Signature signature, String algo) {
254         try {
255             MessageDigest md = MessageDigest.getInstance(algo);
256             return md.digest(signature.toByteArray());
257         } catch (NoSuchAlgorithmException ex) {
258             Rlog.e(TAG, "NoSuchAlgorithmException: " + ex);
259         }
260         return null;
261     }
262 }
263