OLD | NEW |
(Empty) | |
| 1 package org.chromium.components.installedapp; |
| 2 |
| 3 import android.content.pm.PackageManager; |
| 4 import android.content.pm.PackageManager.NameNotFoundException; |
| 5 import android.content.pm.PackageInfo; |
| 6 import android.content.pm.Signature; |
| 7 |
| 8 import android.content.res.Resources.NotFoundException; |
| 9 import android.content.res.Resources; |
| 10 |
| 11 import java.security.MessageDigest; |
| 12 import java.security.NoSuchAlgorithmException; |
| 13 import java.util.HashSet; |
| 14 import java.util.List; |
| 15 import java.util.Arrays; |
| 16 import java.util.Collections; |
| 17 |
| 18 import org.json.JSONArray; |
| 19 import org.json.JSONException; |
| 20 import org.json.JSONObject; |
| 21 |
| 22 public class AssociationVerifier { |
| 23 private static final String ASSOCIATED_ASSETS_KEY = "associated_assets"; |
| 24 private static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target"; |
| 25 private static final String ASSET_DESCRIPTOR_WEB = "web"; |
| 26 private static final String ASSET_DESCRIPTOR_FIELD_SITE = "site"; |
| 27 private static final String ASSET_DESCRIPTOR_FIELD_NAMESPACE = "namespace"; |
| 28 |
| 29 public static boolean verifyCertFingerprints( |
| 30 String packageName, List<String> sha, PackageManager pm) { |
| 31 Signature[] signatures; |
| 32 try { |
| 33 signatures = pm.getPackageInfo(packageName, PackageManager.GET_SIGNA
TURES).signatures; |
| 34 } catch (NameNotFoundException e) { |
| 35 return false; |
| 36 } |
| 37 |
| 38 // TODO: Convert this to an array. |
| 39 HashSet<String> signatureStrings = new HashSet<String>(signatures.length
); |
| 40 for (Signature sig : signatures) { |
| 41 signatureStrings.add(computeNormalizedSha256Fingerprint(sig.toByteAr
ray())); |
| 42 } |
| 43 |
| 44 return signatureStrings.containsAll(sha) && sha.containsAll(signatureStr
ings); |
| 45 } |
| 46 |
| 47 private static List<String> getAssociations(String packageName, PackageManag
er pm) { |
| 48 Resources resources; |
| 49 |
| 50 try { |
| 51 resources = pm.getResourcesForApplication(packageName); |
| 52 } catch (NameNotFoundException e) { |
| 53 return Collections.<String>emptyList(); |
| 54 } |
| 55 |
| 56 if (resources == null) { |
| 57 return Collections.<String>emptyList(); |
| 58 } |
| 59 |
| 60 int identifier = resources.getIdentifier(ASSOCIATED_ASSETS_KEY, "array",
packageName); |
| 61 System.out.println("Identifier was " + identifier); |
| 62 if (identifier == 0) { |
| 63 return Collections.<String>emptyList(); |
| 64 } |
| 65 return Arrays.asList(resources.getStringArray(identifier)); |
| 66 } |
| 67 |
| 68 public static boolean isOriginAssociatedWithPackage( |
| 69 String packageName, String origin, PackageManager pm) { |
| 70 List<String> associations = getAssociations(packageName, pm); |
| 71 // HashSet<String> associationSet = (new HashSet<>(associations)); |
| 72 // System.out.println(associationSet); |
| 73 |
| 74 for (String statementString : associations) { |
| 75 try { |
| 76 JSONObject statement = new JSONObject(statementString); |
| 77 String jsonOrigin = statement.getString(ASSET_DESCRIPTOR_FIELD_T
ARGET); |
| 78 JSONObject webAsset = new JSONObject(jsonOrigin); |
| 79 System.out.println(jsonOrigin); |
| 80 |
| 81 // // TODO: Handle this. |
| 82 // JSONArray relations = statement.getJSONArray(ASSET_DESCRIPTOR
_FIELD_RELATION); |
| 83 // for (int i = 0; i < relations.length(); i++) { |
| 84 // // statements.add(Statement |
| 85 // // .create(source, target, Relation.create())); |
| 86 // } |
| 87 |
| 88 if (!webAsset.getString(ASSET_DESCRIPTOR_FIELD_NAMESPACE) |
| 89 .equals(ASSET_DESCRIPTOR_WEB)) { |
| 90 System.out.println("ASSET WAS NOT WEB"); |
| 91 continue; |
| 92 } |
| 93 |
| 94 if (webAsset.getString(ASSET_DESCRIPTOR_FIELD_SITE).equals(origi
n)) { |
| 95 return true; |
| 96 } |
| 97 System.out.println(webAsset.getString(ASSET_DESCRIPTOR_FIELD_SIT
E) |
| 98 + " failed to match against " + origin); |
| 99 } catch (JSONException e) { |
| 100 } |
| 101 } |
| 102 |
| 103 return false; |
| 104 } |
| 105 |
| 106 /** |
| 107 * Computes the hash of the byte array using the specified algorithm, return
ing a hex string |
| 108 * with a colon between each byte. |
| 109 */ |
| 110 public static String computeNormalizedSha256Fingerprint(byte[] cert) { |
| 111 MessageDigest messageDigest; |
| 112 try { |
| 113 messageDigest = MessageDigest.getInstance("SHA-256"); |
| 114 } catch (NoSuchAlgorithmException e) { |
| 115 throw new AssertionError("No SHA-256 implementation found."); |
| 116 } |
| 117 messageDigest.update(cert); |
| 118 return byteArrayToHexString(messageDigest.digest()); |
| 119 } |
| 120 |
| 121 /** |
| 122 * Converts the byte array to an lowercase hexadecimal digits String with a colo
n character (:) |
| 123 * between each byte. |
| 124 */ |
| 125 private static final char[] HEX_DIGITS = { |
| 126 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D'
, 'E', 'F'}; |
| 127 private static String byteArrayToHexString(byte[] array) { |
| 128 char[] hexString = new char[array.length * 3]; |
| 129 int index = 0; |
| 130 for (byte b : array) { |
| 131 hexString[index++] = HEX_DIGITS[(b >>> 4) & 0x0F]; |
| 132 hexString[index++] = HEX_DIGITS[b & 0x0F]; |
| 133 hexString[index++] = ':'; |
| 134 } |
| 135 |
| 136 return new String(Arrays.copyOf(hexString, hexString.length - 1)); |
| 137 } |
| 138 } |
OLD | NEW |