Chromium Code Reviews| OLD | NEW |
|---|---|
| 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.customtabs; | 5 package org.chromium.chrome.browser.customtabs; |
| 6 | 6 |
| 7 import android.content.pm.PackageInfo; | 7 import android.content.pm.PackageInfo; |
| 8 import android.content.pm.PackageManager; | 8 import android.content.pm.PackageManager; |
| 9 import android.net.Uri; | 9 import android.net.Uri; |
| 10 import android.support.annotation.NonNull; | 10 import android.support.annotation.NonNull; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 23 | 23 |
| 24 import java.io.ByteArrayInputStream; | 24 import java.io.ByteArrayInputStream; |
| 25 import java.io.InputStream; | 25 import java.io.InputStream; |
| 26 import java.security.MessageDigest; | 26 import java.security.MessageDigest; |
| 27 import java.security.NoSuchAlgorithmException; | 27 import java.security.NoSuchAlgorithmException; |
| 28 import java.security.cert.CertificateEncodingException; | 28 import java.security.cert.CertificateEncodingException; |
| 29 import java.security.cert.CertificateException; | 29 import java.security.cert.CertificateException; |
| 30 import java.security.cert.CertificateFactory; | 30 import java.security.cert.CertificateFactory; |
| 31 import java.security.cert.X509Certificate; | 31 import java.security.cert.X509Certificate; |
| 32 import java.util.HashMap; | 32 import java.util.HashMap; |
| 33 import java.util.HashSet; | |
| 33 import java.util.Locale; | 34 import java.util.Locale; |
| 34 import java.util.Map; | 35 import java.util.Map; |
| 36 import java.util.Set; | |
| 35 | 37 |
| 36 /** | 38 /** |
| 37 * Used to verify postMessage origin for a designated package name. | 39 * Used to verify postMessage origin for a designated package name. |
| 38 * | 40 * |
| 39 * Uses Digital Asset Links to confirm that the given origin is associated with the package name as | 41 * Uses Digital Asset Links to confirm that the given origin is associated with the package name as |
| 40 * a postMessage origin. It caches any origin that has been verified during the current application | 42 * a postMessage origin. It caches any origin that has been verified during the current application |
| 41 * lifecycle and reuses that without making any new network requests. | 43 * lifecycle and reuses that without making any new network requests. |
| 42 * | 44 * |
| 43 * The lifecycle of this object is governed by the owner. The owner has to call | 45 * The lifecycle of this object is governed by the owner. The owner has to call |
| 44 * {@link OriginVerifier#cleanUp()} for proper cleanup of dependencies. | 46 * {@link OriginVerifier#cleanUp()} for proper cleanup of dependencies. |
| 45 */ | 47 */ |
| 46 @JNINamespace("customtabs") | 48 @JNINamespace("customtabs") |
| 47 class OriginVerifier { | 49 class OriginVerifier { |
| 48 private static final String TAG = "OriginVerifier"; | 50 private static final String TAG = "OriginVerifier"; |
| 49 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray (); | 51 private static final char[] HEX_CHAR_LOOKUP = "0123456789ABCDEF".toCharArray (); |
| 50 private static Map<String, Uri> sCachedOriginMap; | 52 private static Map<String, Set<Uri>> sPackageToCachedOrigins; |
| 51 private final OriginVerificationListener mListener; | 53 private final OriginVerificationListener mListener; |
| 52 private final String mPackageName; | 54 private final String mPackageName; |
| 53 private final String mSignatureFingerprint; | 55 private final String mSignatureFingerprint; |
| 54 private long mNativeOriginVerifier = 0; | 56 private long mNativeOriginVerifier = 0; |
| 55 private Uri mOrigin; | 57 private Uri mOrigin; |
| 56 | 58 |
| 57 /** | 59 /** Small helper class to post a result of origin verification. */ |
| 58 * To be used for prepopulating verified origin for testing functionality. | 60 private class VerifiedCallback implements Runnable { |
|
Yusuf
2017/07/06 06:34:42
Thanks!
| |
| 59 * @param packageName The package name to prepopulate for. | 61 private final boolean mResult; |
| 60 * @param origin The origin to add as verified. | 62 |
| 61 */ | 63 public VerifiedCallback(boolean result) { |
| 62 @VisibleForTesting | 64 this.mResult = result; |
| 63 static void prePopulateVerifiedOriginForTesting(String packageName, Uri orig in) { | 65 } |
| 64 cacheVerifiedOriginIfNeeded(packageName, origin); | 66 |
| 67 @Override | |
| 68 public void run() { | |
| 69 originVerified(mResult); | |
| 70 } | |
| 65 } | 71 } |
| 66 | 72 |
| 67 private static Uri getPostMessageOriginFromVerifiedOrigin( | 73 private static Uri getPostMessageOriginFromVerifiedOrigin( |
| 68 String packageName, Uri verifiedOrigin) { | 74 String packageName, Uri verifiedOrigin) { |
| 69 return Uri.parse(IntentHandler.ANDROID_APP_REFERRER_SCHEME + "://" | 75 return Uri.parse(IntentHandler.ANDROID_APP_REFERRER_SCHEME + "://" |
| 70 + verifiedOrigin.getHost() + "/" + packageName); | 76 + verifiedOrigin.getHost() + "/" + packageName); |
| 71 } | 77 } |
| 72 | 78 |
| 73 private static void cacheVerifiedOriginIfNeeded(String packageName, Uri orig in) { | 79 /** Clears all known relations. */ |
| 74 if (sCachedOriginMap == null) sCachedOriginMap = new HashMap<>(); | 80 @VisibleForTesting |
| 75 if (!sCachedOriginMap.containsKey(packageName)) { | 81 static void reset() { |
| 76 sCachedOriginMap.put(packageName, origin); | 82 ThreadUtils.assertOnUiThread(); |
| 77 } | 83 sPackageToCachedOrigins.clear(); |
| 78 } | 84 } |
| 79 | 85 |
| 80 /** | 86 /** |
| 87 * Mark an origin as verified for a package. | |
| 88 * @param packageName The package name to prepopulate for. | |
| 89 * @param origin The origin to add as verified. | |
| 90 */ | |
| 91 static void addVerifiedOriginForPackage(String packageName, Uri origin) { | |
| 92 ThreadUtils.assertOnUiThread(); | |
| 93 if (sPackageToCachedOrigins == null) sPackageToCachedOrigins = new HashM ap<>(); | |
| 94 Set<Uri> cachedOrigins = sPackageToCachedOrigins.get(packageName); | |
| 95 if (cachedOrigins == null) { | |
| 96 cachedOrigins = new HashSet<Uri>(); | |
| 97 sPackageToCachedOrigins.put(packageName, cachedOrigins); | |
| 98 } | |
| 99 cachedOrigins.add(origin); | |
| 100 } | |
| 101 | |
| 102 static boolean isValidOrigin(String packageName, Uri origin) { | |
|
Yusuf
2017/07/06 06:34:42
javadoc
Maybe something around no async verificat
Benoit L
2017/07/07 11:30:56
Done.
| |
| 103 ThreadUtils.assertOnUiThread(); | |
| 104 if (sPackageToCachedOrigins == null) return false; | |
| 105 Set<Uri> cachedOrigins = sPackageToCachedOrigins.get(packageName); | |
| 106 if (cachedOrigins == null) return false; | |
| 107 return cachedOrigins.contains(origin); | |
| 108 } | |
| 109 | |
| 110 /** | |
| 81 * Callback interface for getting verification results. | 111 * Callback interface for getting verification results. |
| 82 */ | 112 */ |
| 83 public interface OriginVerificationListener { | 113 public interface OriginVerificationListener { |
| 84 /** | 114 /** |
| 85 * To be posted on the handler thread after the verification finishes. | 115 * To be posted on the handler thread after the verification finishes. |
| 86 * @param packageName The package name for the origin verification query for this result. | 116 * @param packageName The package name for the origin verification query for this result. |
| 87 * @param origin The origin that was declared on the query for this resu lt. | 117 * @param origin The origin that was declared on the query for this resu lt. |
| 88 * @param verified Whether the given origin was verified to correspond t o the given package. | 118 * @param verified Whether the given origin was verified to correspond t o the given package. |
| 89 */ | 119 */ |
| 90 void onOriginVerified(String packageName, Uri origin, boolean verified); | 120 void onOriginVerified(String packageName, Uri origin, boolean verified); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 105 /** | 135 /** |
| 106 * Verify the claimed origin for the cached package name asynchronously. Thi s will end up | 136 * Verify the claimed origin for the cached package name asynchronously. Thi s will end up |
| 107 * making a network request for non-cached origins with a URLFetcher using t he last used | 137 * making a network request for non-cached origins with a URLFetcher using t he last used |
| 108 * profile as context. | 138 * profile as context. |
| 109 * @param origin The postMessage origin the application is claiming to have. Can't be null. | 139 * @param origin The postMessage origin the application is claiming to have. Can't be null. |
| 110 */ | 140 */ |
| 111 public void start(@NonNull Uri origin) { | 141 public void start(@NonNull Uri origin) { |
| 112 ThreadUtils.assertOnUiThread(); | 142 ThreadUtils.assertOnUiThread(); |
| 113 mOrigin = origin; | 143 mOrigin = origin; |
| 114 if (!UrlConstants.HTTPS_SCHEME.equals(mOrigin.getScheme().toLowerCase(Lo cale.US))) { | 144 if (!UrlConstants.HTTPS_SCHEME.equals(mOrigin.getScheme().toLowerCase(Lo cale.US))) { |
| 115 ThreadUtils.postOnUiThread(new Runnable() { | 145 ThreadUtils.postOnUiThread(new VerifiedCallback(false)); |
| 116 @Override | |
| 117 public void run() { | |
| 118 originVerified(false); | |
| 119 } | |
| 120 }); | |
| 121 return; | 146 return; |
| 122 } | 147 } |
| 123 | 148 |
| 124 // If this origin is cached as verified already, use that. | 149 // If this origin is cached as verified already, use that. |
| 125 Uri cachedOrigin = getCachedOriginIfExists(); | 150 if (isValidOrigin(mPackageName, origin)) { |
| 126 if (cachedOrigin != null && cachedOrigin.equals(origin)) { | 151 ThreadUtils.postOnUiThread(new VerifiedCallback(true)); |
| 127 ThreadUtils.postOnUiThread(new Runnable() { | |
| 128 @Override | |
| 129 public void run() { | |
| 130 originVerified(true); | |
| 131 } | |
| 132 }); | |
| 133 return; | 152 return; |
| 134 } | 153 } |
| 135 if (mNativeOriginVerifier != 0) cleanUp(); | 154 if (mNativeOriginVerifier != 0) cleanUp(); |
| 136 if (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER) | 155 if (!BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER) |
| 137 .isStartupSuccessfullyCompleted()) { | 156 .isStartupSuccessfullyCompleted()) { |
| 138 // Early return for testing without native. | 157 // Early return for testing without native. |
| 139 return; | 158 return; |
| 140 } | 159 } |
| 141 mNativeOriginVerifier = nativeInit(Profile.getLastUsedProfile().getOrigi nalProfile()); | 160 mNativeOriginVerifier = nativeInit(Profile.getLastUsedProfile().getOrigi nalProfile()); |
| 142 assert mNativeOriginVerifier != 0; | 161 assert mNativeOriginVerifier != 0; |
| 143 boolean success = nativeVerifyOrigin( | 162 boolean success = nativeVerifyOrigin( |
| 144 mNativeOriginVerifier, mPackageName, mSignatureFingerprint, mOri gin.toString()); | 163 mNativeOriginVerifier, mPackageName, mSignatureFingerprint, mOri gin.toString()); |
| 145 if (!success) { | 164 if (!success) ThreadUtils.postOnUiThread(new VerifiedCallback(false)); |
| 146 ThreadUtils.postOnUiThread(new Runnable() { | |
| 147 @Override | |
| 148 public void run() { | |
| 149 originVerified(false); | |
| 150 } | |
| 151 }); | |
| 152 } | |
| 153 } | 165 } |
| 154 | 166 |
| 155 /** | 167 /** |
| 156 * Cleanup native dependencies on this object. | 168 * Cleanup native dependencies on this object. |
| 157 */ | 169 */ |
| 158 void cleanUp() { | 170 void cleanUp() { |
| 159 if (mNativeOriginVerifier == 0) return; | 171 if (mNativeOriginVerifier == 0) return; |
| 160 nativeDestroy(mNativeOriginVerifier); | 172 nativeDestroy(mNativeOriginVerifier); |
| 161 mNativeOriginVerifier = 0; | 173 mNativeOriginVerifier = 0; |
| 162 } | 174 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 212 hexString.append(HEX_CHAR_LOOKUP[(byteArray[i] & 0xf0) >>> 4]); | 224 hexString.append(HEX_CHAR_LOOKUP[(byteArray[i] & 0xf0) >>> 4]); |
| 213 hexString.append(HEX_CHAR_LOOKUP[byteArray[i] & 0xf]); | 225 hexString.append(HEX_CHAR_LOOKUP[byteArray[i] & 0xf]); |
| 214 if (i < (byteArray.length - 1)) hexString.append(':'); | 226 if (i < (byteArray.length - 1)) hexString.append(':'); |
| 215 } | 227 } |
| 216 return hexString.toString(); | 228 return hexString.toString(); |
| 217 } | 229 } |
| 218 | 230 |
| 219 @CalledByNative | 231 @CalledByNative |
| 220 private void originVerified(boolean originVerified) { | 232 private void originVerified(boolean originVerified) { |
| 221 if (originVerified) { | 233 if (originVerified) { |
| 222 cacheVerifiedOriginIfNeeded(mPackageName, mOrigin); | 234 addVerifiedOriginForPackage(mPackageName, mOrigin); |
| 223 mOrigin = getPostMessageOriginFromVerifiedOrigin(mPackageName, mOrig in); | 235 mOrigin = getPostMessageOriginFromVerifiedOrigin(mPackageName, mOrig in); |
| 224 } | 236 } |
| 225 mListener.onOriginVerified(mPackageName, mOrigin, originVerified); | 237 mListener.onOriginVerified(mPackageName, mOrigin, originVerified); |
| 226 cleanUp(); | 238 cleanUp(); |
| 227 } | 239 } |
| 228 | 240 |
| 229 private Uri getCachedOriginIfExists() { | |
| 230 if (sCachedOriginMap == null) return null; | |
| 231 return sCachedOriginMap.get(mPackageName); | |
| 232 } | |
| 233 | |
| 234 private native long nativeInit(Profile profile); | 241 private native long nativeInit(Profile profile); |
| 235 private native boolean nativeVerifyOrigin(long nativeOriginVerifier, String packageName, | 242 private native boolean nativeVerifyOrigin(long nativeOriginVerifier, String packageName, |
| 236 String signatureFingerprint, String origin); | 243 String signatureFingerprint, String origin); |
| 237 private native void nativeDestroy(long nativeOriginVerifier); | 244 private native void nativeDestroy(long nativeOriginVerifier); |
| 238 } | 245 } |
| OLD | NEW |