Add a check to ensure GMS is not from Google
This commit is contained in:
@@ -32,6 +32,7 @@ import com.aurora.store.data.model.Algorithm
|
|||||||
import com.aurora.store.util.PackageUtil.getPackageInfo
|
import com.aurora.store.util.PackageUtil.getPackageInfo
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
object CertUtil {
|
object CertUtil {
|
||||||
|
|
||||||
@@ -40,7 +41,17 @@ object CertUtil {
|
|||||||
const val GOOGLE_ACCOUNT_TYPE = "com.google"
|
const val GOOGLE_ACCOUNT_TYPE = "com.google"
|
||||||
const val GOOGLE_PLAY_AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/googleplay"
|
const val GOOGLE_PLAY_AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/googleplay"
|
||||||
const val GOOGLE_PLAY_PACKAGE_NAME = "com.android.vending"
|
const val GOOGLE_PLAY_PACKAGE_NAME = "com.android.vending"
|
||||||
const val GOOGLE_PLAY_CERT = "MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK"
|
const val GOOGLE_PLAY_CERT =
|
||||||
|
"MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK"
|
||||||
|
|
||||||
|
// Keep this list updated as & when new signatures are added.
|
||||||
|
private val knownGMSSignatures = listOf(
|
||||||
|
"bd32424203e0fb25f36b57e5aa356f9bdd1da998",
|
||||||
|
"38918a453d07199354f8b19af05ec6562ced5788,",
|
||||||
|
"2169eddb5fbb1fdf241c262681024692c4fc1ecb",
|
||||||
|
"58e1c4133f7441ec3d2c270270a14802da47ba0e",
|
||||||
|
"4f87463a1ae6f7d71b2c0b0658845790236dba42"
|
||||||
|
)
|
||||||
|
|
||||||
fun isFDroidApp(context: Context, packageName: String): Boolean {
|
fun isFDroidApp(context: Context, packageName: String): Boolean {
|
||||||
return isInstalledByFDroid(context, packageName) || isSignedByFDroid(context, packageName)
|
return isInstalledByFDroid(context, packageName) || isSignedByFDroid(context, packageName)
|
||||||
@@ -89,6 +100,31 @@ object CertUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isGoogleGMS(context: Context, packageName: String): Boolean {
|
||||||
|
return try {
|
||||||
|
getX509Certificates(context, packageName).any { certificate ->
|
||||||
|
val signatureHash = extractSHA1Fingerprint(certificate)
|
||||||
|
|
||||||
|
if (knownGMSSignatures.contains(signatureHash)) return true
|
||||||
|
|
||||||
|
// Follow heuristics to determine if the app is signed by Google, just to ensure we don't miss any.
|
||||||
|
listOf(
|
||||||
|
certificate.issuerX500Principal,
|
||||||
|
certificate.subjectX500Principal
|
||||||
|
).any {
|
||||||
|
val map = parseX500Principal(it)
|
||||||
|
map["O"] == "Google LLC" || map["O"] == "Google Inc."
|
||||||
|
&& map["L"] == "Mountain View"
|
||||||
|
&& map["ST"] == "California"
|
||||||
|
&& map["C"] == "US"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(TAG, "Failed to check signing cert for $packageName")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun isInstalledByFDroid(context: Context, packageName: String): Boolean {
|
private fun isInstalledByFDroid(context: Context, packageName: String): Boolean {
|
||||||
val fdroidPackages = listOf(
|
val fdroidPackages = listOf(
|
||||||
"org.fdroid.basic", "org.fdroid.fdroid", "org.fdroid.fdroid.privileged"
|
"org.fdroid.basic", "org.fdroid.fdroid", "org.fdroid.fdroid.privileged"
|
||||||
@@ -125,4 +161,19 @@ object CertUtil {
|
|||||||
getPackageInfo(context, packageName, PackageManager.GET_SIGNATURES)
|
getPackageInfo(context, packageName, PackageManager.GET_SIGNATURES)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun extractSHA1Fingerprint(certificate: X509Certificate): String {
|
||||||
|
val messageDigest = MessageDigest.getInstance(Algorithm.SHA1.value)
|
||||||
|
messageDigest.update(certificate.encoded)
|
||||||
|
return messageDigest.digest()
|
||||||
|
.joinToString("") { byte -> String.format("%02x", byte) }
|
||||||
|
.lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseX500Principal(principal: X500Principal): Map<String, String> {
|
||||||
|
return principal.name.split(",").associate {
|
||||||
|
val (left, right) = it.split("=")
|
||||||
|
left.trim() to right.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ object PackageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun hasSupportedMicroG(context: Context): Boolean {
|
fun hasSupportedMicroG(context: Context): Boolean {
|
||||||
|
val isGoogle = CertUtil.isGoogleGMS(context, PACKAGE_NAME_MICRO_G)
|
||||||
|
|
||||||
|
// Do not check for MicroG if Google Play Services is installed
|
||||||
|
if (isGoogle) return false
|
||||||
|
|
||||||
return if (isHuawei) {
|
return if (isHuawei) {
|
||||||
isInstalled(context, PACKAGE_NAME_MICRO_G, VERSION_CODE_MICRO_G_HUAWEI)
|
isInstalled(context, PACKAGE_NAME_MICRO_G, VERSION_CODE_MICRO_G_HUAWEI)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user