OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.chrome.browser.payments; | |
6 | |
7 import android.content.pm.PackageInfo; | |
8 import android.content.pm.PackageManager; | |
9 import android.content.pm.PackageManager.NameNotFoundException; | |
10 import android.content.pm.ResolveInfo; | |
11 import android.content.pm.Signature; | |
12 | |
13 import org.chromium.base.ContextUtils; | |
14 import org.chromium.chrome.browser.payments.PaymentManifestDownloader.ManifestDo wnloadCallback; | |
15 import org.chromium.chrome.browser.payments.PaymentManifestParser.ManifestParseC allback; | |
16 import org.chromium.content_public.browser.WebContents; | |
17 import org.chromium.payments.mojom.PaymentManifestSection; | |
18 | |
19 import java.net.URI; | |
20 import java.security.MessageDigest; | |
21 import java.security.NoSuchAlgorithmException; | |
22 import java.util.ArrayList; | |
23 import java.util.Formatter; | |
24 import java.util.HashSet; | |
25 import java.util.List; | |
26 import java.util.Set; | |
27 | |
28 /** | |
29 * Verifies that the discovered native Android payment apps have the sufficient privileges | |
Mathieu
2017/03/03 19:42:09
Is there a public doc we can link to for this mani
please use gerrit instead
2017/03/09 18:05:33
Linking.
| |
30 * to handle a single payment method. Downloads and parses the manifest to compa re package | |
31 * names, versions, and signatures to the apps. | |
32 */ | |
33 public class PaymentManifestVerifier implements ManifestDownloadCallback, Manife stParseCallback { | |
34 /** Interface for the callback to invoke when finished verification. */ | |
35 public interface ManifestVerifyCallback { | |
36 /** | |
37 * Enables invoking the given native Android payment app for the given p ayment method. | |
38 * Called when the app has been found to have the right privileges to ha ndle this payment | |
39 * method. | |
40 * | |
41 * @param methodName The payment method name that the payment app offer s to handle. | |
42 * @param resolveInfo Identifying information for the native Android pay ment app. | |
43 */ | |
44 void onValidPaymentApp(URI methodName, ResolveInfo resolveInfo); | |
45 | |
46 /** | |
47 * Disables invoking the given native Android payment app for the given payment method. | |
48 * Called when the app has been found to not have the right privileges t o handle this | |
49 * payment app. | |
50 * | |
51 * @param methodName The payment method name that the payment app offer s to handle. | |
52 * @param resolveInfo Identifying information for the native Android pay ment app. | |
53 */ | |
54 void onInvalidPaymentApp(URI methodName, ResolveInfo resolveInfo); | |
55 | |
56 /** | |
57 * Disables invoking any native Android payment app for the given paymen t method. Called if | |
58 * unable to download or parse the payment method manifest. | |
59 * | |
60 * @param methodName The payment method name that has an invalid payment method manifest. | |
61 */ | |
62 void onInvalidManifest(URI methodName); | |
63 } | |
64 | |
65 /** Identifying information about an installed native Android payment app. * / | |
66 private static class AppInfo { | |
67 /** Identifies a native Android payment app. */ | |
68 public ResolveInfo resolveInfo; | |
69 | |
70 /** The version code for the native Android payment app, e.g., 123. */ | |
71 public long version; | |
72 | |
73 /** | |
74 * The SHA256 certificate fingerprints for the native Android payment ap p, .e.g, | |
75 * ["30:82:01:dd:30:82:01:46:02:01:01:30:0d:06:09:2a:86:48:86:f7:0d:01:0 1:05:05:00:30"]. | |
76 * Order does not matter for comparison. | |
77 */ | |
78 public Set<String> sha256CertFingerprints; | |
79 } | |
80 | |
81 private final PaymentManifestDownloader mDownloader; | |
82 private final URI mMethodName; | |
83 private final List<AppInfo> mMatchingApps; | |
84 private final PaymentManifestParser mParser; | |
85 private final ManifestVerifyCallback mCallback; | |
86 private final MessageDigest mMessageDigest; | |
87 | |
88 /** | |
89 * Builds the manifest verifier. | |
90 * | |
91 * @param webContents The web contents. | |
92 * @param methodName The name of the payment method name that apps offer t o handle. Must be | |
93 * a valid URI that starts with "https://". | |
94 * @param matchingApps The identifying information for the native Android pa yment apps that | |
95 * offer to handle this payment method. | |
96 * @param parser The manifest parser. | |
97 * @param callback The callback to be notified of verification result. | |
98 */ | |
99 public PaymentManifestVerifier(WebContents webContents, URI methodName, | |
100 List<ResolveInfo> matchingApps, PaymentManifestParser parser, | |
101 ManifestVerifyCallback callback) { | |
102 mDownloader = new PaymentManifestDownloader(webContents); | |
103 mMethodName = methodName; | |
104 mMatchingApps = new ArrayList<>(); | |
105 for (int i = 0; i < matchingApps.size(); i++) { | |
106 AppInfo appInfo = new AppInfo(); | |
107 appInfo.resolveInfo = matchingApps.get(i); | |
108 mMatchingApps.add(appInfo); | |
109 } | |
110 mParser = parser; | |
111 mCallback = callback; | |
112 | |
113 MessageDigest md = null; | |
114 try { | |
115 md = MessageDigest.getInstance("SHA-256"); | |
116 } catch (NoSuchAlgorithmException e) { | |
117 // Intentionally ignore. | |
118 } | |
119 mMessageDigest = md; | |
120 } | |
121 | |
122 /** | |
123 * Verifies that the discovered native Android payment apps have the suffici ent | |
124 * privileges to handle this payment method. | |
125 */ | |
126 public void verify() { | |
127 mDownloader.download(mMethodName, this); | |
128 } | |
129 | |
130 @Override | |
131 public void onManifestDownloadSuccess(String content) { | |
132 mParser.parse(content, this); | |
133 } | |
134 | |
135 @Override | |
136 public void onManifestDownloadFailure() { | |
137 mCallback.onInvalidManifest(mMethodName); | |
138 } | |
139 | |
140 @Override | |
141 public void onManifestParseSuccess(PaymentManifestSection[] manifest) { | |
142 assert manifest != null; | |
143 assert manifest.length > 0; | |
144 | |
145 for (int i = 0; i < manifest.length; i++) { | |
146 PaymentManifestSection section = manifest[i]; | |
147 // "package": "*" in the manifest file indicates an unrestricted pay ment method. Any app | |
148 // can use this payment method name. | |
149 if ("*".equals(section.packageName)) { | |
150 for (int j = 0; j < mMatchingApps.size(); j++) { | |
151 mCallback.onValidPaymentApp(mMethodName, mMatchingApps.get(j ).resolveInfo); | |
152 } | |
153 return; | |
154 } | |
155 } | |
156 | |
157 if (mMessageDigest == null) { | |
158 mCallback.onInvalidManifest(mMethodName); | |
159 return; | |
160 } | |
161 | |
162 for (int i = 0; i < mMatchingApps.size(); i++) { | |
163 AppInfo appInfo = mMatchingApps.get(i); | |
164 try { | |
165 PackageInfo packageInfo = | |
166 ContextUtils.getApplicationContext().getPackageManager() .getPackageInfo( | |
167 appInfo.resolveInfo.activityInfo.packageName, | |
168 PackageManager.GET_SIGNATURES); | |
169 appInfo.version = packageInfo.versionCode; | |
170 appInfo.sha256CertFingerprints = new HashSet<>(); | |
171 Signature[] signatures = packageInfo.signatures; | |
172 for (int j = 0; j < signatures.length; j++) { | |
173 mMessageDigest.update(signatures[j].toByteArray()); | |
174 | |
175 // The digest is reset after completing the hash computation . | |
176 appInfo.sha256CertFingerprints.add(formatFingerprint(mMessag eDigest.digest())); | |
177 } | |
178 } catch (NameNotFoundException e) { | |
179 // Leaving appInfo.sha256CertFingerprints uninitialized will cal l | |
180 // onInvalidApp() for this app below. | |
181 } | |
182 } | |
183 | |
184 List<Set<String>> sectionsFingerprints = new ArrayList<>(); | |
185 for (int i = 0; i < manifest.length; i++) { | |
186 PaymentManifestSection section = manifest[i]; | |
187 Set<String> fingerprints = new HashSet<>(); | |
188 if (section.sha256CertFingerprints != null) { | |
189 for (int j = 0; j < section.sha256CertFingerprints.length; j++) { | |
190 fingerprints.add(section.sha256CertFingerprints[j]); | |
191 } | |
192 } | |
193 sectionsFingerprints.add(fingerprints); | |
194 } | |
195 | |
196 for (int i = 0; i < mMatchingApps.size(); i++) { | |
197 AppInfo appInfo = mMatchingApps.get(i); | |
198 boolean isAllowed = false; | |
199 for (int j = 0; j < manifest.length; j++) { | |
200 PaymentManifestSection section = manifest[j]; | |
201 if (appInfo.resolveInfo.activityInfo.packageName.equals(section. packageName) | |
202 && appInfo.version >= section.version | |
203 && appInfo.sha256CertFingerprints != null | |
204 && appInfo.sha256CertFingerprints.equals(sectionsFingerp rints.get(j))) { | |
205 mCallback.onValidPaymentApp(mMethodName, appInfo.resolveInfo ); | |
206 isAllowed = true; | |
207 break; | |
208 } | |
209 } | |
210 if (!isAllowed) mCallback.onInvalidPaymentApp(mMethodName, appInfo.r esolveInfo); | |
211 } | |
212 } | |
213 | |
214 @Override | |
215 public void onManifestParseFailure() { | |
216 mCallback.onInvalidManifest(mMethodName); | |
217 } | |
218 | |
219 /** | |
220 * Formats bytes into a string. | |
221 * | |
222 * @param input Input bytes. Should not be null. | |
223 * @return A string representation of the input bytes, e.g., "01:23:45:67:89 :AB:CD:EF". | |
224 */ | |
225 private static String formatFingerprint(byte[] input) { | |
226 StringBuilder builder = new StringBuilder(input.length * 3); | |
227 Formatter formatter = new Formatter(builder); | |
228 | |
229 for (byte b : input) { | |
230 formatter.format(":%02X", b); | |
231 } | |
232 | |
233 // Cut off the first ":". | |
234 return builder.substring(1); | |
235 } | |
236 } | |
OLD | NEW |