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

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

Issue 2645813006: Download web payment manifests. (Closed)
Patch Set: Rebase 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b20667fd2bdc140c1e0aca261263622b773c3f5
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentManifestVerifier.java
@@ -0,0 +1,246 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+
+import org.chromium.base.Log;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.components.payments.PaymentManifestDownloader;
+import org.chromium.components.payments.PaymentManifestDownloader.ManifestDownloadCallback;
+import org.chromium.components.payments.PaymentManifestParser;
+import org.chromium.components.payments.PaymentManifestParser.ManifestParseCallback;
+import org.chromium.payments.mojom.PaymentManifestSection;
+
+import java.net.URI;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Formatter;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Verifies that the discovered native Android payment apps have the sufficient privileges
+ * to handle a single payment method. Downloads and parses the manifest to compare package
+ * names, versions, and signatures to the apps.
+ *
+ * Spec:
+ * https://docs.google.com/document/d/1izV4uC-tiRJG3JLooqY3YRLU22tYOsLTNq0P_InPJeE/edit#heading=h.cjp3jlnl47h5
+ */
+public class PaymentManifestVerifier implements ManifestDownloadCallback, ManifestParseCallback {
+ private static final String TAG = "cr_PaymentManifest";
+
+ /** Interface for the callback to invoke when finished verification. */
+ public interface ManifestVerifyCallback {
+ /**
+ * Enables invoking the given native Android payment app for the given payment method.
+ * Called when the app has been found to have the right privileges to handle this payment
+ * method.
+ *
+ * @param methodName The payment method name that the payment app offers to handle.
+ * @param resolveInfo Identifying information for the native Android payment app.
+ */
+ void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo);
+
+ /**
+ * Disables invoking the given native Android payment app for the given payment method.
+ * Called when the app has been found to not have the right privileges to handle this
+ * payment app.
+ *
+ * @param methodName The payment method name that the payment app offers to handle.
+ * @param resolveInfo Identifying information for the native Android payment app.
+ */
+ void onInvalidPaymentApp(URI methodName, ResolveInfo resolveInfo);
+
+ /**
+ * Disables invoking any native Android payment app for the given payment method. Called if
+ * unable to download or parse the payment method manifest.
+ *
+ * @param methodName The payment method name that has an invalid payment method manifest.
+ */
+ void onInvalidManifest(URI methodName);
+ }
+
+ /** Identifying information about an installed native Android payment app. */
+ private static class AppInfo {
+ /** Identifies a native Android payment app. */
+ public ResolveInfo resolveInfo;
+
+ /** The version code for the native Android payment app, e.g., 123. */
+ public long version;
+
+ /**
+ * The SHA256 certificate fingerprints for the native Android payment app, .e.g,
+ * ["308201dd30820146020101300d06092a864886f70d010105050030"].
+ */
+ public Set<String> sha256CertFingerprints;
+ }
+
+ private final PaymentManifestDownloader mDownloader;
+ private final URI mMethodName;
+ private final List<AppInfo> mMatchingApps;
+ private final PaymentManifestParser mParser;
+ private final PackageManagerDelegate mPackageManagerDelegate;
+ private final ManifestVerifyCallback mCallback;
+ private final MessageDigest mMessageDigest;
+
+ /**
+ * Builds the manifest verifier.
+ *
+ * @param methodName The name of the payment method name that apps offer to handle.
+ * Must be an absolute URI with HTTPS scheme.
+ * @param matchingApps The identifying information for the native Android payment apps
+ * that offer to handle this payment method.
+ * @param downloader The manifest downloader.
+ * @param parser The manifest parser.
+ * @param packageManagerDelegate The package information retriever.
+ * @param callback The callback to be notified of verification result.
+ */
+ public PaymentManifestVerifier(URI methodName, List<ResolveInfo> matchingApps,
+ PaymentManifestDownloader downloader, PaymentManifestParser parser,
+ PackageManagerDelegate packageManagerDelegate, ManifestVerifyCallback callback) {
+ assert methodName.isAbsolute();
+ assert UrlConstants.HTTPS_SCHEME.equals(methodName.getScheme());
+ assert !matchingApps.isEmpty();
+
+ mMethodName = methodName;
+ mMatchingApps = new ArrayList<>();
+ for (int i = 0; i < matchingApps.size(); i++) {
+ AppInfo appInfo = new AppInfo();
+ appInfo.resolveInfo = matchingApps.get(i);
+ mMatchingApps.add(appInfo);
+ }
+ mDownloader = downloader;
+ mParser = parser;
+ mPackageManagerDelegate = packageManagerDelegate;
+ mCallback = callback;
+
+ MessageDigest md = null;
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ // Intentionally ignore.
+ Log.d(TAG, "Unable to generate SHA-256 hashes. Only \"package\": \"*\" supported.");
+ }
+ mMessageDigest = md;
+ }
+
+ /**
+ * Verifies that the discovered native Android payment apps have the sufficient
+ * privileges to handle this payment method.
+ */
+ public void verify() {
+ mDownloader.download(mMethodName, this);
+ }
+
+ @Override
+ public void onManifestDownloadSuccess(String content) {
+ mParser.parse(content, this);
+ }
+
+ @Override
+ public void onManifestDownloadFailure() {
+ mCallback.onInvalidManifest(mMethodName);
+ }
+
+ @Override
+ public void onManifestParseSuccess(PaymentManifestSection[] manifest) {
+ assert manifest != null;
+ assert manifest.length > 0;
+
+ for (int i = 0; i < manifest.length; i++) {
+ PaymentManifestSection section = manifest[i];
+ // "package": "*" in the manifest file indicates an unrestricted payment method. Any app
+ // can use this payment method name.
+ if ("*".equals(section.packageName)) {
+ for (int j = 0; j < mMatchingApps.size(); j++) {
+ mCallback.onValidPaymentApp(mMethodName, mMatchingApps.get(j).resolveInfo);
+ }
+ return;
+ }
+ }
+
+ if (mMessageDigest == null) {
+ mCallback.onInvalidManifest(mMethodName);
+ return;
+ }
+
+ for (int i = 0; i < mMatchingApps.size(); i++) {
+ AppInfo appInfo = mMatchingApps.get(i);
+ PackageInfo packageInfo = mPackageManagerDelegate.getPackageInfoWithSignatures(
+ appInfo.resolveInfo.activityInfo.packageName);
+
+ // Leaving appInfo.sha256CertFingerprints uninitialized will call onInvalidPaymentApp()
+ // for this app below.
+ if (packageInfo == null) continue;
+
+ appInfo.version = packageInfo.versionCode;
+ appInfo.sha256CertFingerprints = new HashSet<>();
+ Signature[] signatures = packageInfo.signatures;
+ for (int j = 0; j < signatures.length; j++) {
+ mMessageDigest.update(signatures[j].toByteArray());
+
+ // The digest is reset after completing the hash computation.
+ appInfo.sha256CertFingerprints.add(byteArrayToString(mMessageDigest.digest()));
+ }
+ }
+
+ List<Set<String>> sectionsFingerprints = new ArrayList<>();
+ for (int i = 0; i < manifest.length; i++) {
+ PaymentManifestSection section = manifest[i];
+ Set<String> fingerprints = new HashSet<>();
+ if (section.sha256CertFingerprints != null) {
+ for (int j = 0; j < section.sha256CertFingerprints.length; j++) {
+ fingerprints.add(byteArrayToString(section.sha256CertFingerprints[j]));
+ }
+ }
+ sectionsFingerprints.add(fingerprints);
+ }
+
+ for (int i = 0; i < mMatchingApps.size(); i++) {
+ AppInfo appInfo = mMatchingApps.get(i);
+ boolean isAllowed = false;
+ for (int j = 0; j < manifest.length; j++) {
+ PaymentManifestSection section = manifest[j];
+ if (appInfo.resolveInfo.activityInfo.packageName.equals(section.packageName)
+ && appInfo.version >= section.version
+ && appInfo.sha256CertFingerprints != null
+ && appInfo.sha256CertFingerprints.equals(sectionsFingerprints.get(j))) {
+ mCallback.onValidPaymentApp(mMethodName, appInfo.resolveInfo);
+ isAllowed = true;
+ break;
+ }
+ }
+ if (!isAllowed) mCallback.onInvalidPaymentApp(mMethodName, appInfo.resolveInfo);
+ }
+ }
+
+ /**
+ * Formats bytes into a string for easier comparison as a member of a set.
+ *
+ * @param input Input bytes.
+ * @return A string representation of the input bytes, e.g., "0123456789abcdef".
+ */
+ private static String byteArrayToString(byte[] input) {
+ if (input == null) return null;
+
+ StringBuilder builder = new StringBuilder(input.length * 2);
+ Formatter formatter = new Formatter(builder);
+ for (byte b : input) {
+ formatter.format("%02x", b);
+ }
+
+ return builder.toString();
+ }
+
+ @Override
+ public void onManifestParseFailure() {
+ mCallback.onInvalidManifest(mMethodName);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698