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.ResolveInfo; |
| 9 import android.content.pm.Signature; |
| 10 |
| 11 import org.chromium.base.Log; |
| 12 import org.chromium.chrome.browser.UrlConstants; |
| 13 import org.chromium.components.payments.PaymentManifestDownloader; |
| 14 import org.chromium.components.payments.PaymentManifestDownloader.ManifestDownlo
adCallback; |
| 15 import org.chromium.components.payments.PaymentManifestParser; |
| 16 import org.chromium.components.payments.PaymentManifestParser.ManifestParseCallb
ack; |
| 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 * Spec: |
| 34 * https://docs.google.com/document/d/1izV4uC-tiRJG3JLooqY3YRLU22tYOsLTNq0P_InPJ
eE/edit#heading=h.cjp3jlnl47h5 |
| 35 */ |
| 36 public class PaymentManifestVerifier implements ManifestDownloadCallback, Manife
stParseCallback { |
| 37 private static final String TAG = "cr_PaymentManifest"; |
| 38 |
| 39 /** Interface for the callback to invoke when finished verification. */ |
| 40 public interface ManifestVerifyCallback { |
| 41 /** |
| 42 * Enables invoking the given native Android payment app for the given p
ayment method. |
| 43 * Called when the app has been found to have the right privileges to ha
ndle this payment |
| 44 * method. |
| 45 * |
| 46 * @param methodName The payment method name that the payment app offer
s to handle. |
| 47 * @param resolveInfo Identifying information for the native Android pay
ment app. |
| 48 */ |
| 49 void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo); |
| 50 |
| 51 /** |
| 52 * Disables invoking the given native Android payment app for the given
payment method. |
| 53 * Called when the app has been found to not have the right privileges t
o handle this |
| 54 * payment app. |
| 55 * |
| 56 * @param methodName The payment method name that the payment app offer
s to handle. |
| 57 * @param resolveInfo Identifying information for the native Android pay
ment app. |
| 58 */ |
| 59 void onInvalidPaymentApp(URI methodName, ResolveInfo resolveInfo); |
| 60 |
| 61 /** |
| 62 * Disables invoking any native Android payment app for the given paymen
t method. Called if |
| 63 * unable to download or parse the payment method manifest. |
| 64 * |
| 65 * @param methodName The payment method name that has an invalid payment
method manifest. |
| 66 */ |
| 67 void onInvalidManifest(URI methodName); |
| 68 } |
| 69 |
| 70 /** Identifying information about an installed native Android payment app. *
/ |
| 71 private static class AppInfo { |
| 72 /** Identifies a native Android payment app. */ |
| 73 public ResolveInfo resolveInfo; |
| 74 |
| 75 /** The version code for the native Android payment app, e.g., 123. */ |
| 76 public long version; |
| 77 |
| 78 /** |
| 79 * The SHA256 certificate fingerprints for the native Android payment ap
p, .e.g, |
| 80 * ["308201dd30820146020101300d06092a864886f70d010105050030"]. |
| 81 */ |
| 82 public Set<String> sha256CertFingerprints; |
| 83 } |
| 84 |
| 85 private final PaymentManifestDownloader mDownloader; |
| 86 private final URI mMethodName; |
| 87 private final List<AppInfo> mMatchingApps; |
| 88 private final PaymentManifestParser mParser; |
| 89 private final PackageManagerDelegate mPackageManagerDelegate; |
| 90 private final ManifestVerifyCallback mCallback; |
| 91 private final MessageDigest mMessageDigest; |
| 92 |
| 93 /** |
| 94 * Builds the manifest verifier. |
| 95 * |
| 96 * @param methodName The name of the payment method name that ap
ps offer to handle. |
| 97 * Must be an absolute URI with HTTPS scheme. |
| 98 * @param matchingApps The identifying information for the native
Android payment apps |
| 99 * that offer to handle this payment method. |
| 100 * @param downloader The manifest downloader. |
| 101 * @param parser The manifest parser. |
| 102 * @param packageManagerDelegate The package information retriever. |
| 103 * @param callback The callback to be notified of verification
result. |
| 104 */ |
| 105 public PaymentManifestVerifier(URI methodName, List<ResolveInfo> matchingApp
s, |
| 106 PaymentManifestDownloader downloader, PaymentManifestParser parser, |
| 107 PackageManagerDelegate packageManagerDelegate, ManifestVerifyCallbac
k callback) { |
| 108 assert methodName.isAbsolute(); |
| 109 assert UrlConstants.HTTPS_SCHEME.equals(methodName.getScheme()); |
| 110 assert !matchingApps.isEmpty(); |
| 111 |
| 112 mMethodName = methodName; |
| 113 mMatchingApps = new ArrayList<>(); |
| 114 for (int i = 0; i < matchingApps.size(); i++) { |
| 115 AppInfo appInfo = new AppInfo(); |
| 116 appInfo.resolveInfo = matchingApps.get(i); |
| 117 mMatchingApps.add(appInfo); |
| 118 } |
| 119 mDownloader = downloader; |
| 120 mParser = parser; |
| 121 mPackageManagerDelegate = packageManagerDelegate; |
| 122 mCallback = callback; |
| 123 |
| 124 MessageDigest md = null; |
| 125 try { |
| 126 md = MessageDigest.getInstance("SHA-256"); |
| 127 } catch (NoSuchAlgorithmException e) { |
| 128 // Intentionally ignore. |
| 129 Log.d(TAG, "Unable to generate SHA-256 hashes. Only \"package\": \"*
\" supported."); |
| 130 } |
| 131 mMessageDigest = md; |
| 132 } |
| 133 |
| 134 /** |
| 135 * Verifies that the discovered native Android payment apps have the suffici
ent |
| 136 * privileges to handle this payment method. |
| 137 */ |
| 138 public void verify() { |
| 139 mDownloader.download(mMethodName, this); |
| 140 } |
| 141 |
| 142 @Override |
| 143 public void onManifestDownloadSuccess(String content) { |
| 144 mParser.parse(content, this); |
| 145 } |
| 146 |
| 147 @Override |
| 148 public void onManifestDownloadFailure() { |
| 149 mCallback.onInvalidManifest(mMethodName); |
| 150 } |
| 151 |
| 152 @Override |
| 153 public void onManifestParseSuccess(PaymentManifestSection[] manifest) { |
| 154 assert manifest != null; |
| 155 assert manifest.length > 0; |
| 156 |
| 157 for (int i = 0; i < manifest.length; i++) { |
| 158 PaymentManifestSection section = manifest[i]; |
| 159 // "package": "*" in the manifest file indicates an unrestricted pay
ment method. Any app |
| 160 // can use this payment method name. |
| 161 if ("*".equals(section.packageName)) { |
| 162 for (int j = 0; j < mMatchingApps.size(); j++) { |
| 163 mCallback.onValidPaymentApp(mMethodName, mMatchingApps.get(j
).resolveInfo); |
| 164 } |
| 165 return; |
| 166 } |
| 167 } |
| 168 |
| 169 if (mMessageDigest == null) { |
| 170 mCallback.onInvalidManifest(mMethodName); |
| 171 return; |
| 172 } |
| 173 |
| 174 for (int i = 0; i < mMatchingApps.size(); i++) { |
| 175 AppInfo appInfo = mMatchingApps.get(i); |
| 176 PackageInfo packageInfo = mPackageManagerDelegate.getPackageInfoWith
Signatures( |
| 177 appInfo.resolveInfo.activityInfo.packageName); |
| 178 |
| 179 // Leaving appInfo.sha256CertFingerprints uninitialized will call on
InvalidPaymentApp() |
| 180 // for this app below. |
| 181 if (packageInfo == null) continue; |
| 182 |
| 183 appInfo.version = packageInfo.versionCode; |
| 184 appInfo.sha256CertFingerprints = new HashSet<>(); |
| 185 Signature[] signatures = packageInfo.signatures; |
| 186 for (int j = 0; j < signatures.length; j++) { |
| 187 mMessageDigest.update(signatures[j].toByteArray()); |
| 188 |
| 189 // The digest is reset after completing the hash computation. |
| 190 appInfo.sha256CertFingerprints.add(byteArrayToString(mMessageDig
est.digest())); |
| 191 } |
| 192 } |
| 193 |
| 194 List<Set<String>> sectionsFingerprints = new ArrayList<>(); |
| 195 for (int i = 0; i < manifest.length; i++) { |
| 196 PaymentManifestSection section = manifest[i]; |
| 197 Set<String> fingerprints = new HashSet<>(); |
| 198 if (section.sha256CertFingerprints != null) { |
| 199 for (int j = 0; j < section.sha256CertFingerprints.length; j++)
{ |
| 200 fingerprints.add(byteArrayToString(section.sha256CertFingerp
rints[j])); |
| 201 } |
| 202 } |
| 203 sectionsFingerprints.add(fingerprints); |
| 204 } |
| 205 |
| 206 for (int i = 0; i < mMatchingApps.size(); i++) { |
| 207 AppInfo appInfo = mMatchingApps.get(i); |
| 208 boolean isAllowed = false; |
| 209 for (int j = 0; j < manifest.length; j++) { |
| 210 PaymentManifestSection section = manifest[j]; |
| 211 if (appInfo.resolveInfo.activityInfo.packageName.equals(section.
packageName) |
| 212 && appInfo.version >= section.version |
| 213 && appInfo.sha256CertFingerprints != null |
| 214 && appInfo.sha256CertFingerprints.equals(sectionsFingerp
rints.get(j))) { |
| 215 mCallback.onValidPaymentApp(mMethodName, appInfo.resolveInfo
); |
| 216 isAllowed = true; |
| 217 break; |
| 218 } |
| 219 } |
| 220 if (!isAllowed) mCallback.onInvalidPaymentApp(mMethodName, appInfo.r
esolveInfo); |
| 221 } |
| 222 } |
| 223 |
| 224 /** |
| 225 * Formats bytes into a string for easier comparison as a member of a set. |
| 226 * |
| 227 * @param input Input bytes. |
| 228 * @return A string representation of the input bytes, e.g., "0123456789abcd
ef". |
| 229 */ |
| 230 private static String byteArrayToString(byte[] input) { |
| 231 if (input == null) return null; |
| 232 |
| 233 StringBuilder builder = new StringBuilder(input.length * 2); |
| 234 Formatter formatter = new Formatter(builder); |
| 235 for (byte b : input) { |
| 236 formatter.format("%02x", b); |
| 237 } |
| 238 |
| 239 return builder.toString(); |
| 240 } |
| 241 |
| 242 @Override |
| 243 public void onManifestParseFailure() { |
| 244 mCallback.onInvalidManifest(mMethodName); |
| 245 } |
| 246 } |
OLD | NEW |