Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.chrome.browser.payments; | |
| 6 | |
| 7 import android.content.pm.PackageInfo; | |
| 8 import android.content.pm.PackageManager; | |
| 9 import android.content.pm.PackageManager.NameNotFoundException; | |
| 10 import android.content.pm.ResolveInfo; | |
| 11 import android.content.pm.Signature; | |
| 12 | |
| 13 import org.chromium.base.ContextUtils; | |
| 14 import org.chromium.chrome.browser.payments.PaymentManifestDownloader.ManifestDo wnloadCallback; | |
| 15 import org.chromium.chrome.browser.payments.PaymentManifestParser.ManifestParseC allback; | |
| 16 import org.chromium.content_public.browser.WebContents; | |
| 17 import org.chromium.payments.mojom.PaymentManifestSection; | |
| 18 | |
| 19 import java.net.URI; | |
| 20 import java.security.MessageDigest; | |
| 21 import java.security.NoSuchAlgorithmException; | |
| 22 import java.util.ArrayList; | |
| 23 import java.util.Formatter; | |
| 24 import java.util.HashSet; | |
| 25 import java.util.List; | |
| 26 import java.util.Set; | |
| 27 | |
| 28 /** | |
| 29 * Verifies that the discovered native Android payment apps have the sufficient privileges | |
| 30 * to handle a single payment method. Downloads and parses the manifest to compa re package | |
| 31 * names, versions, and signatures to the apps. | |
| 32 */ | |
| 33 public class PaymentManifestVerifier implements ManifestDownloadCallback, Manife stParseCallback { | |
| 34 /** Interface for the callback to invoke when finished verification. */ | |
| 35 public interface ManifestVerifyCallback { | |
| 36 /** | |
| 37 * Enables invoking the given native Android payment app for the given p ayment method. | |
| 38 * Called when the app has been found to have the right privileges to ha ndle this payment | |
| 39 * method. | |
| 40 * | |
| 41 * @param methodName The payment method name that the payment app offer s to handle. | |
| 42 * @param resolveInfo Identifying information for the native Android pay ment app. | |
| 43 */ | |
| 44 void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo); | |
| 45 | |
| 46 /** | |
| 47 * Disables invoking the given native Android payment app for the given payment method. | |
| 48 * Called when the app has been found to not have the right privileges t o handle this | |
| 49 * payment app. | |
| 50 * | |
| 51 * @param methodName The payment method name that the payment app offer s to handle. | |
| 52 * @param resolveInfo Identifying information for the native Android pay ment app. | |
| 53 */ | |
| 54 void onInvalidPaymentApp(URI methodName, ResolveInfo resolveInfo); | |
| 55 | |
| 56 /** | |
| 57 * Disables invoking any native Android payment app for the given paymen t method. Called if | |
| 58 * unable to download or parse the payment method manifest. | |
| 59 * | |
| 60 * @param methodName The payment method name that has an invalid payment method manifest. | |
| 61 */ | |
| 62 void onInvalidManifest(URI methodName); | |
| 63 } | |
| 64 | |
| 65 /** Identifying information about an installed native Android payment app. * / | |
| 66 private static class AppInfo { | |
| 67 /** Identifies a native Android payment app. */ | |
| 68 public ResolveInfo resolveInfo; | |
| 69 | |
| 70 /** The version code for the native Android payment app, e.g., 123. */ | |
| 71 public long version; | |
| 72 | |
| 73 /** | |
| 74 * The SHA256 certificate fingerprints for the native Android payment ap p, .e.g, | |
| 75 * ["30:82:01:dd:30:82:01:46:02:01:01:30:0d:06:09:2a:86:48:86:f7:0d:01:0 1:05:05:00:30"]. | |
| 76 * Order does not matter for comparison. | |
| 77 */ | |
| 78 public Set<String> sha256CertFingerprints; | |
| 79 } | |
| 80 | |
| 81 private final PaymentManifestDownloader mDownloader; | |
| 82 private final URI mMethodName; | |
| 83 private final List<AppInfo> mMatchingApps; | |
| 84 private final PaymentManifestParser mParser; | |
| 85 private final ManifestVerifyCallback mCallback; | |
| 86 private final MessageDigest mMessageDigest; | |
| 87 | |
| 88 /** | |
| 89 * Builds the manifest verifier. | |
| 90 * | |
| 91 * @param webContents The web contents. | |
| 92 * @param methodName The name of the payment method name that apps offer t o handle. Must be | |
| 93 * a valid URI that starts with "https://". | |
| 94 * @param matchingApps The identifying information for the native Android pa yment apps that | |
| 95 * offer to handle this payment method. | |
| 96 * @param parser The manifest parser. | |
| 97 * @param callback The callback to be notified of verification result. | |
| 98 */ | |
| 99 public PaymentManifestVerifier(WebContents webContents, URI methodName, | |
| 100 List<ResolveInfo> matchingApps, PaymentManifestParser parser, | |
| 101 ManifestVerifyCallback callback) { | |
| 102 mDownloader = new PaymentManifestDownloader(webContents); | |
| 103 mMethodName = methodName; | |
| 104 mMatchingApps = new ArrayList<>(); | |
| 105 for (int i = 0; i < matchingApps.size(); i++) { | |
| 106 AppInfo appInfo = new AppInfo(); | |
| 107 appInfo.resolveInfo = matchingApps.get(i); | |
| 108 mMatchingApps.add(appInfo); | |
| 109 } | |
| 110 mParser = parser; | |
| 111 mCallback = callback; | |
| 112 | |
| 113 MessageDigest md = null; | |
| 114 try { | |
| 115 md = MessageDigest.getInstance("SHA-256"); | |
| 116 } catch (NoSuchAlgorithmException e) { | |
| 117 // Intentionally ignore. | |
|
Ted C
2017/03/03 17:44:45
should we log anything here or where it marks it a
please use gerrit instead
2017/03/09 18:05:33
Done.
| |
| 118 } | |
| 119 mMessageDigest = md; | |
| 120 } | |
| 121 | |
| 122 /** | |
| 123 * Verifies that the discovered native Android payment apps have the suffici ent | |
| 124 * privileges to handle this payment method. | |
| 125 */ | |
| 126 public void verify() { | |
| 127 mDownloader.download(mMethodName, this); | |
| 128 } | |
| 129 | |
| 130 @Override | |
| 131 public void onManifestDownloadSuccess(String content) { | |
| 132 mParser.parse(content, this); | |
| 133 } | |
| 134 | |
| 135 @Override | |
| 136 public void onManifestDownloadFailure() { | |
| 137 mCallback.onInvalidManifest(mMethodName); | |
| 138 } | |
| 139 | |
| 140 @Override | |
| 141 public void onManifestParseSuccess(PaymentManifestSection[] manifest) { | |
| 142 assert manifest != null; | |
| 143 assert manifest.length > 0; | |
| 144 | |
| 145 for (int i = 0; i < manifest.length; i++) { | |
| 146 PaymentManifestSection section = manifest[i]; | |
| 147 // "package": "*" in the manifest file indicates an unrestricted pay ment method. Any app | |
| 148 // can use this payment method name. | |
| 149 if ("*".equals(section.packageName)) { | |
| 150 for (int j = 0; j < mMatchingApps.size(); j++) { | |
| 151 mCallback.onValidPaymentApp(mMethodName, mMatchingApps.get(j ).resolveInfo); | |
| 152 } | |
| 153 return; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 if (mMessageDigest == null) { | |
| 158 mCallback.onInvalidManifest(mMethodName); | |
| 159 return; | |
| 160 } | |
| 161 | |
| 162 for (int i = 0; i < mMatchingApps.size(); i++) { | |
| 163 AppInfo appInfo = mMatchingApps.get(i); | |
| 164 try { | |
| 165 PackageInfo packageInfo = | |
| 166 ContextUtils.getApplicationContext().getPackageManager() .getPackageInfo( | |
| 167 appInfo.resolveInfo.activityInfo.packageName, | |
| 168 PackageManager.GET_SIGNATURES); | |
| 169 appInfo.version = packageInfo.versionCode; | |
| 170 appInfo.sha256CertFingerprints = new HashSet<>(); | |
| 171 Signature[] signatures = packageInfo.signatures; | |
| 172 for (int j = 0; j < signatures.length; j++) { | |
| 173 mMessageDigest.update(signatures[j].toByteArray()); | |
| 174 | |
| 175 // The digest is reset after completing the hash computation . | |
| 176 appInfo.sha256CertFingerprints.add(formatFingerprint(mMessag eDigest.digest())); | |
| 177 } | |
| 178 } catch (NameNotFoundException e) { | |
| 179 // Leaving appInfo.sha256CertFingerprints uninitialized will cal l | |
| 180 // onInvalidApp() for this app below. | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 List<Set<String>> sectionsFingerprints = new ArrayList<>(); | |
| 185 for (int i = 0; i < manifest.length; i++) { | |
| 186 PaymentManifestSection section = manifest[i]; | |
| 187 Set<String> fingerprints = new HashSet<>(); | |
| 188 if (section.sha256CertFingerprints != null) { | |
| 189 for (int j = 0; j < section.sha256CertFingerprints.length; j++) { | |
| 190 fingerprints.add(section.sha256CertFingerprints[j]); | |
| 191 } | |
| 192 } | |
| 193 sectionsFingerprints.add(fingerprints); | |
| 194 } | |
| 195 | |
| 196 for (int i = 0; i < mMatchingApps.size(); i++) { | |
| 197 AppInfo appInfo = mMatchingApps.get(i); | |
| 198 boolean isAllowed = false; | |
| 199 for (int j = 0; j < manifest.length; j++) { | |
| 200 PaymentManifestSection section = manifest[j]; | |
| 201 if (appInfo.resolveInfo.activityInfo.packageName.equals(section. packageName) | |
| 202 && appInfo.version >= section.version | |
| 203 && appInfo.sha256CertFingerprints != null | |
| 204 && appInfo.sha256CertFingerprints.equals(sectionsFingerp rints.get(j))) { | |
| 205 mCallback.onValidPaymentApp(mMethodName, appInfo.resolveInfo ); | |
| 206 isAllowed = true; | |
| 207 break; | |
| 208 } | |
| 209 } | |
| 210 if (!isAllowed) mCallback.onInvalidPaymentApp(mMethodName, appInfo.r esolveInfo); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 @Override | |
| 215 public void onManifestParseFailure() { | |
| 216 mCallback.onInvalidManifest(mMethodName); | |
| 217 } | |
| 218 | |
| 219 /** | |
| 220 * Formats bytes into a string. | |
| 221 * | |
| 222 * @param input Input bytes. Should not be null. | |
| 223 * @return A string representation of the input bytes, e.g., "01:23:45:67:89 :AB:CD:EF". | |
| 224 */ | |
| 225 private static String formatFingerprint(byte[] input) { | |
| 226 StringBuilder builder = new StringBuilder(input.length * 3); | |
| 227 Formatter formatter = new Formatter(builder); | |
| 228 | |
| 229 for (byte b : input) { | |
| 230 formatter.format(":%02X", b); | |
| 231 } | |
| 232 | |
| 233 // Cut off the first ":". | |
| 234 return builder.substring(1); | |
| 235 } | |
| 236 } | |
| OLD | NEW |