
package info.guardianproject.trustedintents;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.text.TextUtils;

import java.security.cert.CertificateException;
import java.util.LinkedHashSet;

public class TrustedIntents {

    private static TrustedIntents instance;

    private final Context context;
    private PackageManager pm;

    private final LinkedHashSet<ApkSignaturePin> pinList;

    private TrustedIntents(Context context) {
        this.context = context;
        pinList = new LinkedHashSet<ApkSignaturePin>();
    }

    public static TrustedIntents get(Context context) {
        if (instance == null)
            instance = new TrustedIntents(context.getApplicationContext());
        return instance;
    }

    public boolean isReceiverTrusted(Intent intent) {
        if (intent == null)
            return false;
        String packageName = intent.getPackage();
        if (TextUtils.isEmpty(packageName))
            return false;
        try {
            checkTrustedSigner(packageName);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (CertificateException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * Add an APK signature that is always trusted for any packageName.
     *
     * @param pin the APK signature to trust
     */
    public void addTrustedSigner(ApkSignaturePin pin) {
        pinList.add(pin);
    }

    public void checkTrustedSigner(String packageName)
            throws NameNotFoundException, CertificateException {
        if (pm == null)
            pm = context.getPackageManager();
        PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
        checkTrustedSigner(packageInfo.signatures);
    }

    public void checkTrustedSigner(PackageInfo packageInfo)
            throws NameNotFoundException, CertificateException {
        checkTrustedSigner(packageInfo.signatures);
    }

    public void checkTrustedSigner(Signature[] signatures)
            throws NameNotFoundException, CertificateException {
        if (signatures == null || signatures.length == 0)
            throw new CertificateException("signatures cannot be null or empty!");
        for (int i = 0; i < signatures.length; i++)
            if (signatures[i] == null || signatures[i].toByteArray().length == 0)
                throw new CertificateException("Certificates cannot be null or empty!");

        // check whether the APK signer is trusted for all apps
        for (ApkSignaturePin pin : pinList)
            if (areSignaturesEqual(signatures, pin.getSignatures()))
                return; // found a matching trusted APK signer

        throw new CertificateException("APK signatures did not match!");
    }

    public boolean areSignaturesEqual(Signature[] sigs0, Signature[] sigs1) {
        // TODO where is Android's implementation of this that I can just call?
        if (sigs0 == null || sigs1 == null)
            return false;
        if (sigs0.length == 0 || sigs1.length == 0)
            return false;
        if (sigs0.length != sigs1.length)
            return false;
        for (int i = 0; i < sigs0.length; i++)
            if (!sigs0[i].equals(sigs1[i]))
                return false;
        return true;
    }
}

@@ -36,24 +36,103 @@
 edintents;%0A%0A
+import android.app.Activity;%0Aimport android.content.ActivityNotFoundException;%0A
 import andro
@@ -151,16 +151,16 @@
 ontext;%0A
-
 import a
@@ -1084,22 +1084,29 @@
 if (
-i
+!isI
 ntent
- == null
+Sane(intent)
 )%0A  
@@ -1183,78 +1183,8 @@
 ();%0A
-        if (TextUtils.isEmpty(packageName))%0A            return false;%0A
     
@@ -1388,41 +1388,8 @@
 ) %7B%0A
-            e.printStackTrace();%0A
     
@@ -1444,24 +1444,277 @@
 rue;%0A    %7D%0A%0A
+    private boolean isIntentSane(Intent intent) %7B%0A        if (intent == null)%0A            return false;%0A        String packageName = intent.getPackage();%0A        if (TextUtils.isEmpty(packageName))%0A            return false;%0A%0A        return true;%0A    %7D%0A%0A
     /**%0A    
@@ -3757,32 +3757,32 @@
   return false;%0A
-
         return t
@@ -3791,11 +3791,539 @@
 e;%0A    %7D
+%0A%0A    public void startActivity(Activity activity, Intent intent) throws CertificateException %7B%0A        if (!isIntentSane(intent))%0A            throw new ActivityNotFoundException(%22The intent was null or empty!%22);%0A        String packageName = intent.getPackage();%0A        try %7B%0A            checkTrustedSigner(packageName);%0A        %7D catch (NameNotFoundException e) %7B%0A            e.printStackTrace();%0A            throw new ActivityNotFoundException(e.getLocalizedMessage());%0A        %7D%0A        activity.startActivity(intent);%0A    %7D
 %0A%7D%0A

add TrustedIntents.startActivity(Intent intent) convenience method