Statistics
| Branch: | Tag: | Revision:

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
}