From 442e840a53555cd5d3093f4983a12a07a8ec4624 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 23 Dec 2016 23:05:41 +0800 Subject: [PATCH] Add SafetyNet check --- app/build.gradle | 9 +- app/src/main/AndroidManifest.xml | 4 + .../com/topjohnwu/magisk/SplashActivity.java | 20 +++++ .../com/topjohnwu/magisk/utils/Async.java | 4 +- .../com/topjohnwu/magisk/utils/Logger.java | 14 +++- .../magisk/utils/SafetyNetHelper.java | 84 +++++++++++++++++++ 6 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java diff --git a/app/build.gradle b/app/build.gradle index dfeba7aeb..a80b5d9f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.1" + buildToolsVersion "25.0.2" defaultConfig { applicationId "com.topjohnwu.magisk" @@ -49,9 +49,9 @@ repositories { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:recyclerview-v7:25.0.1' - compile 'com.android.support:cardview-v7:25.0.1' - compile 'com.android.support:design:25.0.1' + compile 'com.android.support:recyclerview-v7:25.1.0' + compile 'com.android.support:cardview-v7:25.1.0' + compile 'com.android.support:design:25.1.0' compile 'com.jakewharton:butterknife:8.4.0' compile 'com.google.code.gson:gson:2.8.0' compile 'com.github.clans:fab:1.6.4' @@ -59,5 +59,6 @@ dependencies { compile 'com.madgag.spongycastle:prov:1.54.0.0' compile 'com.madgag.spongycastle:pkix:1.54.0.0' compile 'com.madgag.spongycastle:pg:1.54.0.0' + compile 'com.google.android.gms:play-services-safetynet:10.0.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 62787a159..5eade796a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,6 +52,10 @@ android:resource="@xml/file_paths" /> + + diff --git a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java b/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java index 3367b1da5..bba73ff64 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java @@ -5,9 +5,11 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; +import android.widget.Toast; import com.topjohnwu.magisk.utils.Async; import com.topjohnwu.magisk.utils.Logger; +import com.topjohnwu.magisk.utils.SafetyNetHelper; import com.topjohnwu.magisk.utils.Utils; public class SplashActivity extends AppCompatActivity { @@ -36,6 +38,24 @@ public class SplashActivity extends AppCompatActivity { .putBoolean("hosts", Utils.itemExist(false, "/magisk/.core/hosts")) .apply(); + // Simple POC for checking SN status + new SafetyNetHelper(getApplicationContext()) { + @Override + public void handleResults(int i) { + switch (i) { + case -1: + Toast.makeText(mContext, "SN: Error", Toast.LENGTH_LONG).show(); + break; + case 0: + Toast.makeText(mContext, "SN: Fail", Toast.LENGTH_LONG).show(); + break; + case 1: + Toast.makeText(mContext, "SN: Success", Toast.LENGTH_LONG).show(); + break; + } + } + }.requestTest(); + new Async.CheckUpdates(prefs).exec(); new Async.LoadModules(prefs) { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Async.java b/app/src/main/java/com/topjohnwu/magisk/utils/Async.java index 19c65f4f5..4a1093c27 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Async.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Async.java @@ -178,10 +178,10 @@ public class Async { Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath()); in.close(); } catch (FileNotFoundException e) { - Log.e(Logger.LOG_TAG, "FlashZip: Invalid Uri"); + Log.e(Logger.TAG, "FlashZip: Invalid Uri"); throw e; } catch (IOException e) { - Log.e(Logger.LOG_TAG, "FlashZip: Error in creating file"); + Log.e(Logger.TAG, "FlashZip: Error in creating file"); throw e; } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java b/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java index 06cb97d99..14f51ac6f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java @@ -4,23 +4,29 @@ import android.util.Log; public class Logger { - public static final String LOG_TAG = "Magisk: DEV"; + public static final String TAG = "Magisk"; + public static final String DEV_TAG = "Magisk: DEV"; + public static final String DEBUG_TAG = "Magisk: DEBUG"; public static boolean logShell, devLog; + public static void debug(String msg) { + Log.d(DEBUG_TAG, msg); + } + public static void dev(String msg, Object... args) { if (devLog) { if (args.length == 1 && args[0] instanceof Throwable) { - Log.d(LOG_TAG, msg, (Throwable) args[0]); + Log.d(DEV_TAG, msg, (Throwable) args[0]); } else { - Log.d(LOG_TAG, String.format(msg, args)); + Log.d(DEV_TAG, String.format(msg, args)); } } } public static void dev(String msg) { if (devLog) { - Log.d(LOG_TAG, msg); + Log.d(DEV_TAG, msg); } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java b/app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java new file mode 100644 index 000000000..abd547a57 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.java @@ -0,0 +1,84 @@ +package com.topjohnwu.magisk.utils; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Base64; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Status; +import com.google.android.gms.safetynet.SafetyNet; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.security.SecureRandom; +import java.util.Arrays; + +public abstract class SafetyNetHelper + implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { + + private GoogleApiClient mGoogleApiClient; + protected Context mContext; + + public SafetyNetHelper(Context context) { + mContext = context; + mGoogleApiClient = new GoogleApiClient.Builder(mContext) + .addApi(SafetyNet.API) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .build(); + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult result) { + Logger.dev("SN: Google API fail"); + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + Logger.dev("SN: Google API Connected"); + safetyNetCheck(); + } + + @Override + public void onConnectionSuspended(int i) { + Logger.dev("SN: Google API Suspended"); + } + + public void requestTest() { + // Connect Google Service + mGoogleApiClient.connect(); + } + + private void safetyNetCheck() { + // Create nonce + byte[] nonce = new byte[24]; + new SecureRandom().nextBytes(nonce); + + Logger.dev("SN: Check with nonce: " + Base64.encodeToString(nonce, Base64.DEFAULT)); + + // Call SafetyNet + SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) + .setResultCallback(result -> { + Status status = result.getStatus(); + if (status.isSuccess()) { + String json = new String(Base64.decode(result.getJwsResult().split("\\.")[1], Base64.DEFAULT)); + Logger.dev("SN: Response: " + json); + try { + JSONObject decoded = new JSONObject(json); + handleResults(decoded.getBoolean("ctsProfileMatch") ? 1 : 0); + } catch (JSONException ignored) {} + } else { + Logger.dev("SN: No response"); + handleResults(-1); + } + // Disconnect + mGoogleApiClient.disconnect(); + }); + } + + public abstract void handleResults(int i); +}