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

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java

Issue 2767333006: Add Digital Asset Links verification for postMessage API (Closed)
Patch Set: moved digital_asset_links to a component 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/customtabs/OriginVerifier.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..cca9f2d204baf24efea07ec7bed270c221b31b82
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/OriginVerifier.java
@@ -0,0 +1,162 @@
+// 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.customtabs;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.chrome.browser.IntentHandler;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Used to verify postMessage origin for a designated package name.
+ *
+ * Uses Digital Asset Links to confirm that the given origin is associated with the package name as
+ * a postMessage origin.
+ */
+@JNINamespace("customtabs")
+class OriginVerifier {
+ private static final String TAG = "OriginVerifier";
+ private static Map<String, Uri> sCachedOriginMap;
+ private long mNativeOriginVerifier = 0;
+ private final OriginVerificationListener mListener;
+ private final String mPackageName;
+ private final String mSignatureFingerprint;
+ private Uri mOrigin;
+
+ private static Uri getPostMessageOriginFromVerifiedOrigin(
+ String packageName, Uri verifiedOrigin) {
+ return Uri.parse(IntentHandler.ANDROID_APP_REFERRER_SCHEME + "://"
+ + verifiedOrigin.getHost() + packageName);
+ }
+
+ private static void cacheVerifiedOriginIfNeeded(String packageName, Uri origin) {
+ if (sCachedOriginMap == null) sCachedOriginMap = new HashMap<>();
+ if (!sCachedOriginMap.containsKey(packageName)) {
+ sCachedOriginMap.put(packageName, origin);
+ }
+ }
+
+ private Uri getCachedOriginIfExists() {
+ if (sCachedOriginMap == null) return null;
+ if (!sCachedOriginMap.containsKey(mPackageName)) return null;
+ return sCachedOriginMap.get(mPackageName);
+ }
+
+ public interface OriginVerificationListener {
+ void onOriginVerified(String packageName, Uri origin, boolean result);
+ }
+
+ /**
+ * Main constructor.
+ * Use {@link OriginVerifier#start(Uri)}
+ * @param listener The listener who will get the verification result.
+ * @param packageName The package for the Android application for verification.
+ */
+ public OriginVerifier(OriginVerificationListener listener, String packageName) {
+ mListener = listener;
+ mPackageName = packageName;
+ mSignatureFingerprint = getCertificateSHA256FingerprintForPackage(mPackageName);
+ }
+
+ /**
+ * Verify the claimed origin for the cached package name asynchronously.
+ * @param origin The postMessage origin the application is claiming to have. Can't be null
+ */
+ public void start(@NonNull Uri origin) {
+ mOrigin = origin;
+
+ // If this origin is cached as verified already, use that.
+ Uri cachedOrigin = getCachedOriginIfExists();
+ if (cachedOrigin != null && cachedOrigin.equals(origin)) {
+ originVerified(true);
+ return;
+ }
+
+ if (mNativeOriginVerifier != 0) cleanUp();
+ mNativeOriginVerifier = nativeInit();
+ assert mNativeOriginVerifier != 0;
+ nativeVerifyOrigin(
+ mNativeOriginVerifier, mPackageName, mSignatureFingerprint, mOrigin.toString());
+ }
+
+ private void cleanUp() {
+ nativeDestroy(mNativeOriginVerifier);
+ mNativeOriginVerifier = 0;
+ }
+
+ private String getCertificateSHA256FingerprintForPackage(String packageName) {
+ PackageManager pm = ContextUtils.getApplicationContext().getPackageManager();
+ PackageInfo packageInfo = null;
+ try {
+ packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Given package name was not found in installed packages");
+ return "";
+ }
+ InputStream input = new ByteArrayInputStream(packageInfo.signatures[0].toByteArray());
+ X509Certificate certificate = null;
+ try {
+ certificate =
+ (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(
+ input);
+ } catch (CertificateException e) {
+ Log.w(TAG, "Certificate type X509 is not available");
+ return "";
+ }
+ String hexString = null;
+ try {
+ hexString = byteArrayToHexString(
+ MessageDigest.getInstance("SHA256").digest(certificate.getEncoded()));
+ } catch (NoSuchAlgorithmException e1) {
+ Log.w(TAG, "SHA256 is not available");
+ } catch (CertificateEncodingException e) {
+ Log.w(TAG, "Certificate type X509 encoding failed");
+ }
+ return hexString;
+ }
+
+ private static String byteArrayToHexString(byte[] byteArray) {
+ StringBuilder hexString = new StringBuilder(byteArray.length * 2);
+ for (int i = 0; i < byteArray.length; i++) {
+ String hex = Integer.toHexString(byteArray[i]);
+ int length = hex.length();
+ hex = (length == 1) ? "0" + hex
+ : (length > 2) ? hex.substring(length - 2, length) : hex;
+ hexString.append(hex.toUpperCase(Locale.getDefault()));
+ if (i < (byteArray.length - 1)) hexString.append(':');
+ }
+ return hexString.toString();
+ }
+
+ @CalledByNative
+ private void originVerified(boolean originVerified) {
+ mListener.onOriginVerified(mPackageName, mOrigin, originVerified);
+ if (originVerified) cacheVerifiedOriginIfNeeded(mPackageName, mOrigin);
+ cleanUp();
+ }
+
+ private native long nativeInit();
+ private native void nativeVerifyOrigin(long nativeOriginVerifier, String packageName,
+ String signatureFingerprint, String origin);
+ private native void nativeDestroy(long nativeOriginVerifier);
+}

Powered by Google App Engine
This is Rietveld 408576698