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