Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java

Issue 2645813006: Download web payment manifests. (Closed)
Patch Set: Self-review Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698