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

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

Issue 2802043002: Use web-app manifest format for Android payment apps. (Closed)
Patch Set: Address java comments. Created 3 years, 8 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
« no previous file with comments | « no previous file | chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.payments; 5 package org.chromium.chrome.browser.payments;
6 6
7 import android.content.pm.PackageInfo; 7 import android.content.pm.PackageInfo;
8 import android.content.pm.ResolveInfo; 8 import android.content.pm.ResolveInfo;
9 import android.content.pm.Signature; 9 import android.content.pm.Signature;
10 10
11 import org.chromium.base.Log; 11 import org.chromium.base.Log;
12 import org.chromium.chrome.browser.UrlConstants; 12 import org.chromium.chrome.browser.UrlConstants;
13 import org.chromium.components.payments.PaymentManifestDownloader; 13 import org.chromium.components.payments.PaymentManifestDownloader;
14 import org.chromium.components.payments.PaymentManifestDownloader.ManifestDownlo adCallback; 14 import org.chromium.components.payments.PaymentManifestDownloader.ManifestDownlo adCallback;
15 import org.chromium.components.payments.PaymentManifestParser; 15 import org.chromium.components.payments.PaymentManifestParser;
16 import org.chromium.components.payments.PaymentManifestParser.ManifestParseCallb ack; 16 import org.chromium.components.payments.PaymentManifestParser.ManifestParseCallb ack;
17 import org.chromium.payments.mojom.PaymentManifestSection; 17 import org.chromium.payments.mojom.WebAppManifestSection;
18 18
19 import java.net.URI; 19 import java.net.URI;
20 import java.security.MessageDigest; 20 import java.security.MessageDigest;
21 import java.security.NoSuchAlgorithmException; 21 import java.security.NoSuchAlgorithmException;
22 import java.util.ArrayList; 22 import java.util.ArrayList;
23 import java.util.Formatter; 23 import java.util.Formatter;
24 import java.util.HashMap;
24 import java.util.HashSet; 25 import java.util.HashSet;
25 import java.util.List; 26 import java.util.List;
27 import java.util.Map;
26 import java.util.Set; 28 import java.util.Set;
27 29
28 /** 30 /**
29 * Verifies that the discovered native Android payment apps have the sufficient privileges 31 * 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 32 * to handle a single payment method. Downloads and parses the manifest to compa re package
31 * names, versions, and signatures to the apps. 33 * names, versions, and signatures to the apps.
32 * 34 *
33 * Spec: 35 * Spec:
34 * https://docs.google.com/document/d/1izV4uC-tiRJG3JLooqY3YRLU22tYOsLTNq0P_InPJ eE/edit#heading=h.cjp3jlnl47h5 36 * https://docs.google.com/document/d/1izV4uC-tiRJG3JLooqY3YRLU22tYOsLTNq0P_InPJ eE/edit#heading=h.cjp3jlnl47h5
35 */ 37 */
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 79
78 /** 80 /**
79 * The SHA256 certificate fingerprints for the native Android payment ap p, .e.g, 81 * The SHA256 certificate fingerprints for the native Android payment ap p, .e.g,
80 * ["308201dd30820146020101300d06092a864886f70d010105050030"]. 82 * ["308201dd30820146020101300d06092a864886f70d010105050030"].
81 */ 83 */
82 public Set<String> sha256CertFingerprints; 84 public Set<String> sha256CertFingerprints;
83 } 85 }
84 86
85 private final PaymentManifestDownloader mDownloader; 87 private final PaymentManifestDownloader mDownloader;
86 private final URI mMethodName; 88 private final URI mMethodName;
87 private final List<AppInfo> mMatchingApps; 89
90 /** A mapping from the package name to the application that matches the meth od name. */
91 private final Map<String, AppInfo> mMatchingApps;
92
88 private final PaymentManifestParser mParser; 93 private final PaymentManifestParser mParser;
89 private final PackageManagerDelegate mPackageManagerDelegate; 94 private final PackageManagerDelegate mPackageManagerDelegate;
90 private final ManifestVerifyCallback mCallback; 95 private final ManifestVerifyCallback mCallback;
91 private final MessageDigest mMessageDigest; 96 private final MessageDigest mMessageDigest;
97 private int mPendingWebAppManifestsCount;
98 private boolean mAtLeastOneManifestFailedToDownloadOrParse;
92 99
93 /** 100 /**
94 * Builds the manifest verifier. 101 * Builds the manifest verifier.
95 * 102 *
96 * @param methodName The name of the payment method name that ap ps offer to handle. 103 * @param methodName The name of the payment method name that ap ps offer to handle.
97 * Must be an absolute URI with HTTPS scheme. 104 * Must be an absolute URI with HTTPS scheme.
98 * @param matchingApps The identifying information for the native Android payment apps 105 * @param matchingApps The identifying information for the native Android payment apps
99 * that offer to handle this payment method. 106 * that offer to handle this payment method.
100 * @param downloader The manifest downloader. 107 * @param downloader The manifest downloader.
101 * @param parser The manifest parser. 108 * @param parser The manifest parser.
102 * @param packageManagerDelegate The package information retriever. 109 * @param packageManagerDelegate The package information retriever.
103 * @param callback The callback to be notified of verification result. 110 * @param callback The callback to be notified of verification result.
104 */ 111 */
105 public PaymentManifestVerifier(URI methodName, List<ResolveInfo> matchingApp s, 112 public PaymentManifestVerifier(URI methodName, List<ResolveInfo> matchingApp s,
106 PaymentManifestDownloader downloader, PaymentManifestParser parser, 113 PaymentManifestDownloader downloader, PaymentManifestParser parser,
107 PackageManagerDelegate packageManagerDelegate, ManifestVerifyCallbac k callback) { 114 PackageManagerDelegate packageManagerDelegate, ManifestVerifyCallbac k callback) {
108 assert methodName.isAbsolute(); 115 assert methodName.isAbsolute();
109 assert UrlConstants.HTTPS_SCHEME.equals(methodName.getScheme()); 116 assert UrlConstants.HTTPS_SCHEME.equals(methodName.getScheme());
110 assert !matchingApps.isEmpty(); 117 assert !matchingApps.isEmpty();
111 118
112 mMethodName = methodName; 119 mMethodName = methodName;
113 mMatchingApps = new ArrayList<>(); 120 mMatchingApps = new HashMap<>();
114 for (int i = 0; i < matchingApps.size(); i++) { 121 for (int i = 0; i < matchingApps.size(); i++) {
115 AppInfo appInfo = new AppInfo(); 122 AppInfo appInfo = new AppInfo();
116 appInfo.resolveInfo = matchingApps.get(i); 123 appInfo.resolveInfo = matchingApps.get(i);
117 mMatchingApps.add(appInfo); 124 mMatchingApps.put(appInfo.resolveInfo.activityInfo.packageName, appI nfo);
118 } 125 }
126
119 mDownloader = downloader; 127 mDownloader = downloader;
120 mParser = parser; 128 mParser = parser;
121 mPackageManagerDelegate = packageManagerDelegate; 129 mPackageManagerDelegate = packageManagerDelegate;
122 mCallback = callback; 130 mCallback = callback;
123 131
124 MessageDigest md = null; 132 MessageDigest md = null;
125 try { 133 try {
126 md = MessageDigest.getInstance("SHA-256"); 134 md = MessageDigest.getInstance("SHA-256");
127 } catch (NoSuchAlgorithmException e) { 135 } catch (NoSuchAlgorithmException e) {
128 // Intentionally ignore. 136 // Intentionally ignore.
129 Log.d(TAG, "Unable to generate SHA-256 hashes. Only \"package\": \"* \" supported."); 137 Log.d(TAG, "Unable to generate SHA-256 hashes.");
130 } 138 }
131 mMessageDigest = md; 139 mMessageDigest = md;
132 } 140 }
133 141
134 /** 142 /**
135 * Verifies that the discovered native Android payment apps have the suffici ent 143 * Verifies that the discovered native Android payment apps have the suffici ent
136 * privileges to handle this payment method. 144 * privileges to handle this payment method.
137 */ 145 */
138 public void verify() { 146 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) { 147 if (mMessageDigest == null) {
170 mCallback.onInvalidManifest(mMethodName); 148 mCallback.onInvalidManifest(mMethodName);
171 return; 149 return;
172 } 150 }
173 151
174 for (int i = 0; i < mMatchingApps.size(); i++) { 152 List<String> invalidAppsToRemove = new ArrayList<>();
175 AppInfo appInfo = mMatchingApps.get(i); 153 for (Map.Entry<String, AppInfo> entry : mMatchingApps.entrySet()) {
176 PackageInfo packageInfo = mPackageManagerDelegate.getPackageInfoWith Signatures( 154 String packageName = entry.getKey();
177 appInfo.resolveInfo.activityInfo.packageName); 155 AppInfo appInfo = entry.getValue();
178 156
179 // Leaving appInfo.sha256CertFingerprints uninitialized will call on InvalidPaymentApp() 157 PackageInfo packageInfo =
180 // for this app below. 158 mPackageManagerDelegate.getPackageInfoWithSignatures(package Name);
181 if (packageInfo == null) continue; 159 if (packageInfo == null) {
160 mCallback.onInvalidPaymentApp(mMethodName, appInfo.resolveInfo);
161 invalidAppsToRemove.add(packageName);
162 continue;
163 }
182 164
183 appInfo.version = packageInfo.versionCode; 165 appInfo.version = packageInfo.versionCode;
184 appInfo.sha256CertFingerprints = new HashSet<>(); 166 appInfo.sha256CertFingerprints = new HashSet<>();
185 Signature[] signatures = packageInfo.signatures; 167 Signature[] signatures = packageInfo.signatures;
186 for (int j = 0; j < signatures.length; j++) { 168 for (int i = 0; i < signatures.length; i++) {
187 mMessageDigest.update(signatures[j].toByteArray()); 169 mMessageDigest.update(signatures[i].toByteArray());
188 170
189 // The digest is reset after completing the hash computation. 171 // The digest is reset after completing the hash computation.
190 appInfo.sha256CertFingerprints.add(byteArrayToString(mMessageDig est.digest())); 172 appInfo.sha256CertFingerprints.add(byteArrayToString(mMessageDig est.digest()));
191 } 173 }
192 } 174 }
193 175
194 List<Set<String>> sectionsFingerprints = new ArrayList<>(); 176 for (int i = 0; i < invalidAppsToRemove.size(); i++) {
195 for (int i = 0; i < manifest.length; i++) { 177 mMatchingApps.remove(invalidAppsToRemove.get(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 } 178 }
205 179
206 for (int i = 0; i < mMatchingApps.size(); i++) { 180 if (!mMatchingApps.isEmpty()) mDownloader.downloadPaymentMethodManifest( mMethodName, this);
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 } 181 }
223 182
224 /** 183 /**
225 * Formats bytes into a string for easier comparison as a member of a set. 184 * Formats bytes into a string for easier comparison as a member of a set.
226 * 185 *
227 * @param input Input bytes. 186 * @param input Input bytes.
228 * @return A string representation of the input bytes, e.g., "0123456789abcd ef". 187 * @return A string representation of the input bytes, e.g., "0123456789abcd ef".
229 */ 188 */
230 private static String byteArrayToString(byte[] input) { 189 private static String byteArrayToString(byte[] input) {
231 if (input == null) return null; 190 if (input == null) return null;
232 191
233 StringBuilder builder = new StringBuilder(input.length * 2); 192 StringBuilder builder = new StringBuilder(input.length * 2);
234 Formatter formatter = new Formatter(builder); 193 Formatter formatter = new Formatter(builder);
235 for (byte b : input) { 194 for (byte b : input) {
236 formatter.format("%02x", b); 195 formatter.format("%02x", b);
237 } 196 }
238 197
239 return builder.toString(); 198 return builder.toString();
240 } 199 }
241 200
242 @Override 201 @Override
202 public void onPaymentMethodManifestDownloadSuccess(String content) {
203 mParser.parsePaymentMethodManifest(content, this);
204 }
205
206 @Override
207 public void onPaymentMethodManifestParseSuccess(URI[] webAppManifestUris) {
208 assert webAppManifestUris != null;
209 assert webAppManifestUris.length > 0;
210 assert !mAtLeastOneManifestFailedToDownloadOrParse;
211 assert mPendingWebAppManifestsCount == 0;
212
213 mPendingWebAppManifestsCount = webAppManifestUris.length;
214 for (int i = 0; i < webAppManifestUris.length; i++) {
215 mDownloader.downloadWebAppManifest(webAppManifestUris[i], this);
216 }
217 }
218
219 @Override
220 public void onWebAppManifestDownloadSuccess(String content) {
221 if (mAtLeastOneManifestFailedToDownloadOrParse) return;
222 mParser.parseWebAppManifest(content, this);
223 }
224
225 @Override
226 public void onWebAppManifestParseSuccess(WebAppManifestSection[] manifest) {
227 assert manifest != null;
228 assert manifest.length > 0;
229
230 if (mAtLeastOneManifestFailedToDownloadOrParse) return;
231
232 List<Set<String>> sectionsFingerprints = new ArrayList<>();
233 for (int i = 0; i < manifest.length; i++) {
234 WebAppManifestSection section = manifest[i];
235 Set<String> fingerprints = new HashSet<>();
236 for (int j = 0; j < section.fingerprints.length; j++) {
237 fingerprints.add(byteArrayToString(section.fingerprints[j]));
238 }
239 sectionsFingerprints.add(fingerprints);
240 }
241
242 for (int i = 0; i < manifest.length; i++) {
243 WebAppManifestSection section = manifest[i];
244 AppInfo appInfo = mMatchingApps.get(section.id);
245 if (appInfo != null && appInfo.version >= section.minVersion
246 && appInfo.sha256CertFingerprints != null
247 && appInfo.sha256CertFingerprints.equals(sectionsFingerprint s.get(i))) {
248 mCallback.onValidPaymentApp(mMethodName, appInfo.resolveInfo);
249 mMatchingApps.remove(section.id);
250 break;
251 }
252 }
253
254 mPendingWebAppManifestsCount--;
255 if (mPendingWebAppManifestsCount == 0) {
256 for (Map.Entry<String, AppInfo> entry : mMatchingApps.entrySet()) {
257 mCallback.onInvalidPaymentApp(mMethodName, entry.getValue().reso lveInfo);
258 }
259 }
260 }
261
262 @Override
263 public void onManifestDownloadFailure() {
264 if (mAtLeastOneManifestFailedToDownloadOrParse) return;
265 mAtLeastOneManifestFailedToDownloadOrParse = true;
266
267 mCallback.onInvalidManifest(mMethodName);
268 }
269
270 @Override
243 public void onManifestParseFailure() { 271 public void onManifestParseFailure() {
272 if (mAtLeastOneManifestFailedToDownloadOrParse) return;
273 mAtLeastOneManifestFailedToDownloadOrParse = true;
274
244 mCallback.onInvalidManifest(mMethodName); 275 mCallback.onInvalidManifest(mMethodName);
245 } 276 }
246 } 277 }
OLDNEW
« no previous file with comments | « no previous file | chrome/android/junit/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698