Index: components/installedapp/android/java/src/org/chromium/device/installedapp/AssociationVerifier.java |
diff --git a/components/installedapp/android/java/src/org/chromium/device/installedapp/AssociationVerifier.java b/components/installedapp/android/java/src/org/chromium/device/installedapp/AssociationVerifier.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..71c76d6a7ea8aa6f99a7d79b8f89076422bd8daf |
--- /dev/null |
+++ b/components/installedapp/android/java/src/org/chromium/device/installedapp/AssociationVerifier.java |
@@ -0,0 +1,138 @@ |
+package org.chromium.components.installedapp; |
+ |
+import android.content.pm.PackageManager; |
+import android.content.pm.PackageManager.NameNotFoundException; |
+import android.content.pm.PackageInfo; |
+import android.content.pm.Signature; |
+ |
+import android.content.res.Resources.NotFoundException; |
+import android.content.res.Resources; |
+ |
+import java.security.MessageDigest; |
+import java.security.NoSuchAlgorithmException; |
+import java.util.HashSet; |
+import java.util.List; |
+import java.util.Arrays; |
+import java.util.Collections; |
+ |
+import org.json.JSONArray; |
+import org.json.JSONException; |
+import org.json.JSONObject; |
+ |
+public class AssociationVerifier { |
+ private static final String ASSOCIATED_ASSETS_KEY = "associated_assets"; |
+ private static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target"; |
+ private static final String ASSET_DESCRIPTOR_WEB = "web"; |
+ private static final String ASSET_DESCRIPTOR_FIELD_SITE = "site"; |
+ private static final String ASSET_DESCRIPTOR_FIELD_NAMESPACE = "namespace"; |
+ |
+ public static boolean verifyCertFingerprints( |
+ String packageName, List<String> sha, PackageManager pm) { |
+ Signature[] signatures; |
+ try { |
+ signatures = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures; |
+ } catch (NameNotFoundException e) { |
+ return false; |
+ } |
+ |
+ // TODO: Convert this to an array. |
+ HashSet<String> signatureStrings = new HashSet<String>(signatures.length); |
+ for (Signature sig : signatures) { |
+ signatureStrings.add(computeNormalizedSha256Fingerprint(sig.toByteArray())); |
+ } |
+ |
+ return signatureStrings.containsAll(sha) && sha.containsAll(signatureStrings); |
+ } |
+ |
+ private static List<String> getAssociations(String packageName, PackageManager pm) { |
+ Resources resources; |
+ |
+ try { |
+ resources = pm.getResourcesForApplication(packageName); |
+ } catch (NameNotFoundException e) { |
+ return Collections.<String>emptyList(); |
+ } |
+ |
+ if (resources == null) { |
+ return Collections.<String>emptyList(); |
+ } |
+ |
+ int identifier = resources.getIdentifier(ASSOCIATED_ASSETS_KEY, "array", packageName); |
+ System.out.println("Identifier was " + identifier); |
+ if (identifier == 0) { |
+ return Collections.<String>emptyList(); |
+ } |
+ return Arrays.asList(resources.getStringArray(identifier)); |
+ } |
+ |
+ public static boolean isOriginAssociatedWithPackage( |
+ String packageName, String origin, PackageManager pm) { |
+ List<String> associations = getAssociations(packageName, pm); |
+ // HashSet<String> associationSet = (new HashSet<>(associations)); |
+ // System.out.println(associationSet); |
+ |
+ for (String statementString : associations) { |
+ try { |
+ JSONObject statement = new JSONObject(statementString); |
+ String jsonOrigin = statement.getString(ASSET_DESCRIPTOR_FIELD_TARGET); |
+ JSONObject webAsset = new JSONObject(jsonOrigin); |
+ System.out.println(jsonOrigin); |
+ |
+ // // TODO: Handle this. |
+ // JSONArray relations = statement.getJSONArray(ASSET_DESCRIPTOR_FIELD_RELATION); |
+ // for (int i = 0; i < relations.length(); i++) { |
+ // // statements.add(Statement |
+ // // .create(source, target, Relation.create())); |
+ // } |
+ |
+ if (!webAsset.getString(ASSET_DESCRIPTOR_FIELD_NAMESPACE) |
+ .equals(ASSET_DESCRIPTOR_WEB)) { |
+ System.out.println("ASSET WAS NOT WEB"); |
+ continue; |
+ } |
+ |
+ if (webAsset.getString(ASSET_DESCRIPTOR_FIELD_SITE).equals(origin)) { |
+ return true; |
+ } |
+ System.out.println(webAsset.getString(ASSET_DESCRIPTOR_FIELD_SITE) |
+ + " failed to match against " + origin); |
+ } catch (JSONException e) { |
+ } |
+ } |
+ |
+ return false; |
+ } |
+ |
+ /** |
+ * Computes the hash of the byte array using the specified algorithm, returning a hex string |
+ * with a colon between each byte. |
+ */ |
+ public static String computeNormalizedSha256Fingerprint(byte[] cert) { |
+ MessageDigest messageDigest; |
+ try { |
+ messageDigest = MessageDigest.getInstance("SHA-256"); |
+ } catch (NoSuchAlgorithmException e) { |
+ throw new AssertionError("No SHA-256 implementation found."); |
+ } |
+ messageDigest.update(cert); |
+ return byteArrayToHexString(messageDigest.digest()); |
+ } |
+ |
+ /** |
+ * Converts the byte array to an lowercase hexadecimal digits String with a colon character (:) |
+ * between each byte. |
+ */ |
+ private static final char[] HEX_DIGITS = { |
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
+ private static String byteArrayToHexString(byte[] array) { |
+ char[] hexString = new char[array.length * 3]; |
+ int index = 0; |
+ for (byte b : array) { |
+ hexString[index++] = HEX_DIGITS[(b >>> 4) & 0x0F]; |
+ hexString[index++] = HEX_DIGITS[b & 0x0F]; |
+ hexString[index++] = ':'; |
+ } |
+ |
+ return new String(Arrays.copyOf(hexString, hexString.length - 1)); |
+ } |
+} |