Update SafetyNet extension implementation
This commit is contained in:
parent
b0a5dbb4c2
commit
90d218ebc8
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,6 +4,9 @@ out
|
||||
*.apk
|
||||
config.prop
|
||||
|
||||
# Manually dumped jars
|
||||
snet/libs
|
||||
|
||||
# Built binaries
|
||||
native/out
|
||||
|
||||
|
@ -12,8 +12,8 @@
|
||||
1. Building is tested on macOS, Ubuntu, and Windows 10 using the latest stable NDK and NDK r10e. Officially released binaries were built with NDK r10e.
|
||||
2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
|
||||
3. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
|
||||
4. By default, `build.py` will build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (through the `--release` flag), you will need to place a Java Keystore file at `release-key.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
||||
|
||||
4. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `--release` flag), you need a Java Keystore file `release-key.jks` to sign Magisk Manager's APK. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
||||
5. The SafetyNet extension pack requires the full Magisk Manager as a `compileOnly` dependency. Build the **release** APK, convert it back to Java `.class` files (I use [dex2jar](https://github.com/pxb1988/dex2jar)), and place the converted JAR under `snet/libs` before compiling.
|
||||
|
||||
## License
|
||||
|
||||
|
2
app
2
app
@ -1 +1 @@
|
||||
Subproject commit a0466085fe3b5eb8929c893d02459e6d94ecb115
|
||||
Subproject commit 499a157946f0063aea0795b204a1f2858591d8d3
|
7
build.py
7
build.py
@ -83,7 +83,6 @@ def build_all(args):
|
||||
build_apk(args)
|
||||
zip_main(args)
|
||||
zip_uninstaller(args)
|
||||
build_snet(args)
|
||||
|
||||
def collect_binary():
|
||||
for arch in ['armeabi-v7a', 'x86']:
|
||||
@ -235,7 +234,11 @@ def build_snet(args):
|
||||
error('Build snet extention failed!')
|
||||
source = os.path.join('snet', 'build', 'outputs', 'apk', 'release', 'snet-release-unsigned.apk')
|
||||
target = os.path.join(config['outdir'], 'snet.apk')
|
||||
mv(source, target)
|
||||
# Re-compress the whole APK for smaller size
|
||||
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
|
||||
with zipfile.ZipFile(source) as zin:
|
||||
for item in zin.infolist():
|
||||
zout.writestr(item.filename, zin.read(item))
|
||||
header('Output: ' + target)
|
||||
|
||||
def gen_update_binary():
|
||||
|
@ -6,7 +6,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.topjohnwu.snet"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 14
|
||||
targetSdkVersion rootProject.ext.compileSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
@ -15,12 +15,13 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compileOnly fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'com.google.android.gms:play-services-safetynet:7.0.0' /* The oldest version */
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
package com.topjohnwu.snet;
|
||||
|
||||
public interface SafetyNetCallback {
|
||||
void onResponse(int responseCode);
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package com.topjohnwu.snet;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -11,18 +9,19 @@ import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import com.google.android.gms.common.api.Status;
|
||||
import com.google.android.gms.safetynet.SafetyNet;
|
||||
import com.google.android.gms.safetynet.SafetyNetApi;
|
||||
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class SafetyNetHelper
|
||||
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
|
||||
public class SafetyNetHelper implements ISafetyNetHelper, GoogleApiClient.ConnectionCallbacks,
|
||||
GoogleApiClient.OnConnectionFailedListener, ResultCallback<SafetyNetApi.AttestationResult> {
|
||||
|
||||
public static final int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
||||
public static final int CAUSE_NETWORK_LOST = 0x02;
|
||||
@ -32,36 +31,24 @@ public class SafetyNetHelper
|
||||
public static final int BASIC_PASS = 0x10;
|
||||
public static final int CTS_PASS = 0x20;
|
||||
|
||||
public static final int SNET_EXT_VER = 7;
|
||||
public static final int SNET_EXT_VER = 8;
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private Activity mActivity;
|
||||
private int responseCode;
|
||||
private SafetyNetCallback cb;
|
||||
private String dexPath;
|
||||
private boolean isDarkTheme;
|
||||
private Callback callback;
|
||||
|
||||
public static int getVersion() {
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return SNET_EXT_VER;
|
||||
}
|
||||
|
||||
public SafetyNetHelper(Activity activity, String dexPath, SafetyNetCallback cb) {
|
||||
public SafetyNetHelper(Activity activity, Callback cb) {
|
||||
mActivity = activity;
|
||||
this.cb = cb;
|
||||
this.dexPath = dexPath;
|
||||
responseCode = 0;
|
||||
|
||||
// Get theme
|
||||
try {
|
||||
Context context = activity.getApplicationContext();
|
||||
Field theme = context.getClass().getField("isDarkTheme");
|
||||
isDarkTheme = (boolean) theme.get(context);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callback = cb;
|
||||
}
|
||||
|
||||
// Entry point to start test
|
||||
@Override
|
||||
public void attest() {
|
||||
// Connect Google Service
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(mActivity)
|
||||
@ -74,26 +61,15 @@ public class SafetyNetHelper
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int i) {
|
||||
cb.onResponse(i);
|
||||
callback.onResponse(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(@NonNull ConnectionResult result) {
|
||||
Class<? extends Activity> clazz = mActivity.getClass();
|
||||
try {
|
||||
// Use external resources
|
||||
clazz.getMethod("swapResources", String.class, int.class).invoke(mActivity, dexPath,
|
||||
isDarkTheme ? android.R.style.Theme_Material : android.R.style.Theme_Material_Light);
|
||||
try {
|
||||
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
clazz.getMethod("restoreResources").invoke(mActivity);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
cb.onResponse(CONNECTION_FAIL);
|
||||
mActivity.swapResources(CheckSafetyNet.dexPath.getPath());
|
||||
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), mActivity, 0).show();
|
||||
mActivity.restoreResources();
|
||||
callback.onResponse(CONNECTION_FAIL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,28 +79,28 @@ public class SafetyNetHelper
|
||||
new SecureRandom().nextBytes(nonce);
|
||||
|
||||
// Call SafetyNet
|
||||
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce)
|
||||
.setResultCallback(new ResultCallback<SafetyNetApi.AttestationResult>() {
|
||||
@Override
|
||||
public void onResult(@NonNull SafetyNetApi.AttestationResult result) {
|
||||
Status status = result.getStatus();
|
||||
try {
|
||||
if (!status.isSuccess()) throw new JSONException("");
|
||||
String json = new String(Base64.decode(
|
||||
result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
|
||||
JSONObject decoded = new JSONObject(json);
|
||||
responseCode |= decoded.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
|
||||
responseCode |= decoded.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
|
||||
} catch (JSONException e) {
|
||||
responseCode = RESPONSE_ERR;
|
||||
}
|
||||
SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce).setResultCallback(this);
|
||||
}
|
||||
|
||||
// Disconnect
|
||||
mGoogleApiClient.disconnect();
|
||||
@Override
|
||||
public void onResult(SafetyNetApi.AttestationResult result) {
|
||||
int code = 0;
|
||||
try {
|
||||
if (!result.getStatus().isSuccess())
|
||||
throw new JSONException("");
|
||||
String jsonStr = new String(Base64.decode(
|
||||
result.getJwsResult().split("\\.")[1], Base64.DEFAULT));
|
||||
JSONObject json = new JSONObject(jsonStr);
|
||||
code |= json.getBoolean("ctsProfileMatch") ? CTS_PASS : 0;
|
||||
code |= json.getBoolean("basicIntegrity") ? BASIC_PASS : 0;
|
||||
} catch (JSONException e) {
|
||||
code = RESPONSE_ERR;
|
||||
}
|
||||
|
||||
// Return results
|
||||
cb.onResponse(responseCode);
|
||||
}
|
||||
});
|
||||
// Disconnect
|
||||
mGoogleApiClient.disconnect();
|
||||
|
||||
// Return results
|
||||
callback.onResponse(code);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user