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 |