trustedintents / trustedintents / src / info / guardianproject / trustedintents / TrustedIntents.java @ cba1dc86
History | View | Annotate | Download (7.99 KB)
1 |
|
---|---|
2 |
package info.guardianproject.trustedintents; |
3 |
|
4 |
import android.app.Activity; |
5 |
import android.content.ActivityNotFoundException; |
6 |
import android.content.ComponentName; |
7 |
import android.content.Context; |
8 |
import android.content.Intent; |
9 |
import android.content.pm.ActivityInfo; |
10 |
import android.content.pm.PackageInfo; |
11 |
import android.content.pm.PackageManager; |
12 |
import android.content.pm.PackageManager.NameNotFoundException; |
13 |
import android.content.pm.ResolveInfo; |
14 |
import android.content.pm.Signature; |
15 |
import android.text.TextUtils; |
16 |
|
17 |
import java.lang.reflect.Constructor; |
18 |
import java.security.cert.CertificateException; |
19 |
import java.util.LinkedHashSet; |
20 |
|
21 |
public class TrustedIntents { |
22 |
|
23 |
private static TrustedIntents instance; |
24 |
|
25 |
private static PackageManager pm; |
26 |
|
27 |
private final LinkedHashSet<ApkSignaturePin> pinList; |
28 |
|
29 |
private TrustedIntents(Context context) { |
30 |
pm = context.getPackageManager(); |
31 |
this.pinList = new LinkedHashSet<ApkSignaturePin>(); |
32 |
} |
33 |
|
34 |
public static TrustedIntents get(Context context) { |
35 |
if (instance == null) |
36 |
instance = new TrustedIntents(context);
|
37 |
return instance;
|
38 |
} |
39 |
|
40 |
/**
|
41 |
* Check whether a resolved {@link Activity} is trusted.
|
42 |
*
|
43 |
* @param resolveInfo the one to check
|
44 |
* @return whether the {@code Intent}'s receiver is trusted
|
45 |
*/
|
46 |
public boolean isReceiverTrusted(ResolveInfo resolveInfo) { |
47 |
return isPackageNameTrusted(resolveInfo.activityInfo.packageName);
|
48 |
} |
49 |
|
50 |
/**
|
51 |
* Check whether a resolved {@link Activity} is trusted.
|
52 |
*
|
53 |
* @param activityInfo the one to check
|
54 |
* @return whether the {@code Intent}'s receiver is trusted
|
55 |
*/
|
56 |
public boolean isReceiverTrusted(ActivityInfo activityInfo) { |
57 |
return isPackageNameTrusted(activityInfo.packageName);
|
58 |
} |
59 |
|
60 |
/**
|
61 |
* Check an {@link Intent} is trusted based on the {@code packageName} set
|
62 |
* by {@link Intent#setPackage(String)}
|
63 |
*
|
64 |
* @param intent the one to check
|
65 |
* @return whether the {@code Intent}'s receiver is trusted
|
66 |
*/
|
67 |
public boolean isReceiverTrusted(Intent intent) { |
68 |
if (!isIntentSane(intent))
|
69 |
return false; |
70 |
String packageName = intent.getPackage();
|
71 |
if (TextUtils.isEmpty(packageName)) {
|
72 |
packageName = intent.getComponent().getPackageName(); |
73 |
} |
74 |
return isPackageNameTrusted(packageName);
|
75 |
} |
76 |
|
77 |
/**
|
78 |
* Check whether a {@code packageName} is trusted.
|
79 |
*
|
80 |
* @param packageName the one to check
|
81 |
* @return whether the {@code packageName} is trusted
|
82 |
*/
|
83 |
public boolean isPackageNameTrusted(String packageName) { |
84 |
try {
|
85 |
checkTrustedSigner(packageName); |
86 |
} catch (NameNotFoundException e) { |
87 |
e.printStackTrace(); |
88 |
return false; |
89 |
} catch (CertificateException e) { |
90 |
return false; |
91 |
} |
92 |
return true; |
93 |
} |
94 |
|
95 |
public Intent getIntentFromTrustedSender(Activity activity)
|
96 |
throws NameNotFoundException, CertificateException { |
97 |
Intent intent = activity.getIntent(); |
98 |
if (!isIntentSane(intent))
|
99 |
throw new NameNotFoundException( |
100 |
"Intent incomplete or was sent using startActivity() instead of startActivityWithResult()");
|
101 |
String packageName = intent.getPackage();
|
102 |
if (TextUtils.isEmpty(packageName)) {
|
103 |
packageName = intent.getComponent().getPackageName(); |
104 |
} |
105 |
if (TextUtils.isEmpty(packageName))
|
106 |
throw new NameNotFoundException(packageName); |
107 |
checkTrustedSigner(packageName); |
108 |
return intent;
|
109 |
} |
110 |
|
111 |
private boolean isIntentSane(Intent intent) { |
112 |
if (intent == null) |
113 |
return false; |
114 |
if (TextUtils.isEmpty(intent.getPackage())) {
|
115 |
ComponentName componentName = intent.getComponent(); |
116 |
if (componentName == null || TextUtils.isEmpty(componentName.getPackageName())) { |
117 |
return false; |
118 |
} |
119 |
} |
120 |
return true; |
121 |
} |
122 |
|
123 |
/**
|
124 |
* Add an APK signature that is always trusted for any packageName.
|
125 |
*
|
126 |
* @param cls {@link Class} of the {@link ApkSignaturePin} to trust
|
127 |
* @return boolean
|
128 |
* @throws {@link IllegalArgumentException} the class cannot be instantiated
|
129 |
*/
|
130 |
public boolean addTrustedSigner(Class<? extends ApkSignaturePin> cls) { |
131 |
try {
|
132 |
Constructor<? extends ApkSignaturePin> constructor = cls.getConstructor(); |
133 |
return pinList.add((ApkSignaturePin) constructor.newInstance((Object[]) null)); |
134 |
} catch (Exception e) { |
135 |
e.printStackTrace(); |
136 |
throw new IllegalArgumentException(e); |
137 |
} |
138 |
} |
139 |
|
140 |
/**
|
141 |
* Remove an APK signature from the trusted set.
|
142 |
*
|
143 |
* @param cls {@link Class} of the {@link ApkSignaturePin} to remove
|
144 |
*/
|
145 |
public boolean removeTrustedSigner(Class<? extends ApkSignaturePin> cls) { |
146 |
for (ApkSignaturePin pin : pinList) {
|
147 |
if (pin.getClass().equals(cls)) {
|
148 |
return pinList.remove(pin);
|
149 |
} |
150 |
} |
151 |
return false; |
152 |
} |
153 |
|
154 |
/**
|
155 |
* Remove all {@link ApkSignaturePin}s from the trusted set.
|
156 |
*/
|
157 |
public boolean removeAllTrustedSigners() { |
158 |
pinList.clear(); |
159 |
return pinList.isEmpty();
|
160 |
} |
161 |
|
162 |
/**
|
163 |
* Check if a {@link ApkSignaturePin} is trusted.
|
164 |
*
|
165 |
* @param cls {@link Class} of the {@link ApkSignaturePin} to check
|
166 |
*/
|
167 |
public boolean isTrustedSigner(Class<? extends ApkSignaturePin> cls) { |
168 |
for (ApkSignaturePin pin : pinList) {
|
169 |
if (pin.getClass().equals(cls)) {
|
170 |
return true; |
171 |
} |
172 |
} |
173 |
return false; |
174 |
} |
175 |
|
176 |
public void checkTrustedSigner(String packageName) |
177 |
throws NameNotFoundException, CertificateException { |
178 |
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); |
179 |
checkTrustedSigner(packageInfo.signatures); |
180 |
} |
181 |
|
182 |
public void checkTrustedSigner(PackageInfo packageInfo) |
183 |
throws NameNotFoundException, CertificateException { |
184 |
checkTrustedSigner(packageInfo.signatures); |
185 |
} |
186 |
|
187 |
public void checkTrustedSigner(Signature[] signatures) |
188 |
throws NameNotFoundException, CertificateException { |
189 |
if (signatures == null || signatures.length == 0) |
190 |
throw new CertificateException("signatures cannot be null or empty!"); |
191 |
for (int i = 0; i < signatures.length; i++) |
192 |
if (signatures[i] == null || signatures[i].toByteArray().length == 0) |
193 |
throw new CertificateException("Certificates cannot be null or empty!"); |
194 |
|
195 |
// check whether the APK signer is trusted for all apps
|
196 |
for (ApkSignaturePin pin : pinList)
|
197 |
if (areSignaturesEqual(signatures, pin.getSignatures()))
|
198 |
return; // found a matching trusted APK signer |
199 |
|
200 |
throw new CertificateException("APK signatures did not match!"); |
201 |
} |
202 |
|
203 |
public boolean areSignaturesEqual(Signature[] sigs0, Signature[] sigs1) { |
204 |
// TODO where is Android's implementation of this that I can just call?
|
205 |
if (sigs0 == null || sigs1 == null) |
206 |
return false; |
207 |
if (sigs0.length == 0 || sigs1.length == 0) |
208 |
return false; |
209 |
if (sigs0.length != sigs1.length)
|
210 |
return false; |
211 |
for (int i = 0; i < sigs0.length; i++) |
212 |
if (!sigs0[i].equals(sigs1[i]))
|
213 |
return false; |
214 |
return true; |
215 |
} |
216 |
|
217 |
public void startActivity(Context context, Intent intent) throws CertificateException { |
218 |
if (!isIntentSane(intent))
|
219 |
throw new ActivityNotFoundException("The intent was null or empty!"); |
220 |
String packageName = intent.getPackage();
|
221 |
if (TextUtils.isEmpty(packageName)) {
|
222 |
packageName = intent.getComponent().getPackageName(); |
223 |
intent.setPackage(packageName); |
224 |
} |
225 |
try {
|
226 |
checkTrustedSigner(packageName); |
227 |
} catch (NameNotFoundException e) { |
228 |
e.printStackTrace(); |
229 |
throw new ActivityNotFoundException(e.getLocalizedMessage()); |
230 |
} |
231 |
context.startActivity(intent); |
232 |
} |
233 |
} |