platform-packages-apps-Settings / src / com / android / settings / external / SignatureVerifier.java
SignatureVerifier.java
Raw
package com.android.settings.external;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class SignatureVerifier {
    private static final byte[] RELEASE_DIGEST_GMSCORE = new byte[]{-16, -3, 108, 91, 65, 15, 37, -53, 37, -61, -75, 51, 70, -56, -105, 47, -82, 48, -8, -18, 116, 17, -33, -111, 4, -128, -83, 107, 45, 96, -37, -125};
    private static final byte[] RELEASE_DIGEST_TIPS = new byte[]{14, 68, 121, -2, 25, 61, 1, -51, 70, 33, 95, -52, -48, -39, 35, 61, -20, 119, -2, -94, 89, -5, -52, -97, 9, 33, 25, -11, 10, -125, 114, -27};

    private static byte[] getDigestBytes(String packageName) {
        byte[] digestBytes;
        if (packageName.hashCode() == 40935373 && packageName.equals("com.google.android.apps.tips")) {
            digestBytes = RELEASE_DIGEST_TIPS;
        } else {
            digestBytes = RELEASE_DIGEST_GMSCORE;
        }
        return digestBytes;
    }

    private static boolean isCertWhitelisted(String packageName, byte[] signature) {
        byte[] digestBytes;
        try {
            digestBytes = MessageDigest.getInstance("SHA-256").digest(signature);
        } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
            throw new SecurityException("Failed to obtain SHA-256 digest impl.", noSuchAlgorithmException);
        }
        return Arrays.equals(digestBytes, SignatureVerifier.getDigestBytes(packageName));
    }

    public static boolean isPackageWhitelisted(Context context, String packageName) {
        PackageInfo info;
        String fullName;
        try {
            info = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
            fullName = info.packageName;
        } catch (PackageManager.NameNotFoundException nameNotFoundException) {
            Log.e("SignatureVerifier", "Could not find package name.", nameNotFoundException);
            return false;
        }

        if (!SignatureVerifier.verifyWhitelistedPackage(fullName))
            return false;

        return isSignatureWhitelisted(info);
    }

    private static boolean isSignatureWhitelisted(PackageInfo packageInfo) {
        // Make sure package only has 1 signature
        if (packageInfo.signatures.length != 1)
            return false;

        byte[] signature = packageInfo.signatures[0].toByteArray();
        return SignatureVerifier.isCertWhitelisted(packageInfo.packageName, signature);
    }

    private static String isUidWhitelisted(Context context, int uid) {
        String[] packages = context.getPackageManager().getPackagesForUid(uid);

        if (ArrayUtils.isEmpty(packages))
            return null;

        for (String packageName : packages) {
            if (!SignatureVerifier.isPackageWhitelisted(context, packageName))
                continue;

            return packageName;
        }
        return null;
    }

    public static String verifyCallerIsWhitelisted(Context context, int uid) throws SecurityException {
        String whitelist = SignatureVerifier.isUidWhitelisted(context, uid);
        if (!TextUtils.isEmpty(whitelist))
            return whitelist;
        throw new SecurityException("UID is not Google Signed");
    }

    private static boolean verifyWhitelistedPackage(String packageName) {
        boolean whitelisted = "com.google.android.googlequicksearchbox".equals(packageName)
                            || "com.google.android.gms".equals(packageName)
                            || "com.google.android.apps.tips".equals(packageName);
        return whitelisted;
    }
}