Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e87ef7b7 authored by Danny Lin's avatar Danny Lin Committed by Mohammed Althaf T
Browse files

keystore: Block key attestation for SafetyNet

SafetyNet (part of Google Play Services) opportunistically uses
hardware-backed key attestation via KeyStore as a strong integrity
check. This causes SafetyNet to fail on custom ROMs because the verified
boot key and bootloader unlock state can be detected from attestation
certificates.

As a workaround, we can take advantage of the fact that SafetyNet's
usage of key attestation is opportunistic (i.e. falls back to basic
integrity checks if it fails) and prevent it from getting the
attestation certificate chain from KeyStore. This is done by checking
the stack for DroidGuard, which is the codename for SafetyNet, and
pretending that the device doesn't support key attestation.

Key attestation has only been blocked for SafetyNet specifically, as
Google Play Services and other apps have many valid reasons to use it.
For example, it appears to be involved in Google's mobile security key
ferature.

Change-Id: I5146439d47f42dc6231cb45c4dab9f61540056f6
parent 1bda2e18
Loading
Loading
Loading
Loading
+15 −0
Original line number Original line Diff line number Diff line
@@ -31,6 +31,8 @@ public final class SafetyNetHooks {
    private static final String SPOOFED_DEVICE_PRODUCT = "walleye";
    private static final String SPOOFED_DEVICE_PRODUCT = "walleye";
    private static final String SPOOFED_FINGERPRINT = "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys";
    private static final String SPOOFED_FINGERPRINT = "google/walleye/walleye:8.1.0/OPM1.171019.011/4448085:user/release-keys";


    private static volatile boolean sIsGms = false;

    private static void setBuildField(String key, String value) {
    private static void setBuildField(String key, String value) {
        try {
        try {
            Field field = Build.class.getDeclaredField(key);
            Field field = Build.class.getDeclaredField(key);
@@ -44,10 +46,23 @@ public final class SafetyNetHooks {


    public static void init(Application app) {
    public static void init(Application app) {
        if (GMS_PACKAGE_NAME.equals(app.getPackageName())) {
        if (GMS_PACKAGE_NAME.equals(app.getPackageName())) {
            sIsGms = true;
            setBuildField("MODEL", SPOOFED_MODEL);
            setBuildField("MODEL", SPOOFED_MODEL);
            setBuildField("DEVICE", SPOOFED_DEVICE_PRODUCT);
            setBuildField("DEVICE", SPOOFED_DEVICE_PRODUCT);
            setBuildField("PRODUCT", SPOOFED_DEVICE_PRODUCT);
            setBuildField("PRODUCT", SPOOFED_DEVICE_PRODUCT);
            setBuildField("FINGERPRINT", SPOOFED_FINGERPRINT);
            setBuildField("FINGERPRINT", SPOOFED_FINGERPRINT);
        }
        }
    }
    }

    private static boolean isCallerSafetyNet() {
        return Arrays.stream(Thread.currentThread().getStackTrace())
                .anyMatch(elem -> elem.getClassName().contains("DroidGuard"));
    }

    public static void onEngineGetCertificateChain() {
        // Check stack for SafetyNet
        if (sIsGms && isCallerSafetyNet()) {
            throw new UnsupportedOperationException();
        }
    }
}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -48,6 +48,7 @@ import android.system.keystore2.ResponseCode;
import android.util.Log;
import android.util.Log;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.safetynet.SafetyNetHooks;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.IOException;
@@ -178,6 +179,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {


    @Override
    @Override
    public Certificate[] engineGetCertificateChain(String alias) {
    public Certificate[] engineGetCertificateChain(String alias) {
        SafetyNetHooks.onEngineGetCertificateChain();
        KeyEntryResponse response = getKeyMetadata(alias);
        KeyEntryResponse response = getKeyMetadata(alias);


        if (response == null || response.metadata.certificate == null) {
        if (response == null || response.metadata.certificate == null) {