mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2025-01-17 22:57:32 +01:00
Update DroidGuard + SafetyNet
This commit is contained in:
parent
ee91cc9b79
commit
56b8bc9f65
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.android.gms.safetynet;
|
|
||||||
|
|
||||||
import org.microg.safeparcel.AutoSafeParcelable;
|
|
||||||
import org.microg.safeparcel.SafeParceled;
|
|
||||||
|
|
||||||
public class AttestationData extends AutoSafeParcelable {
|
|
||||||
@SafeParceled(1)
|
|
||||||
private int versionCode = 1;
|
|
||||||
@SafeParceled(2)
|
|
||||||
private final String jwsResult;
|
|
||||||
|
|
||||||
private AttestationData() {
|
|
||||||
jwsResult = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AttestationData(String jwsResult) {
|
|
||||||
this.jwsResult = jwsResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getJwsResult() {
|
|
||||||
return jwsResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Creator<AttestationData> CREATOR = new AutoCreator<AttestationData>(AttestationData.class);
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.android.gms.safetynet;
|
|
||||||
|
|
||||||
import org.microg.safeparcel.AutoSafeParcelable;
|
|
||||||
|
|
||||||
public class HarmfulAppsInfo extends AutoSafeParcelable {
|
|
||||||
@Field(2)
|
|
||||||
public long field2;
|
|
||||||
@Field(3)
|
|
||||||
public HarmfulAppsData[] data;
|
|
||||||
@Field(4)
|
|
||||||
public int field4;
|
|
||||||
@Field(5)
|
|
||||||
public boolean field5;
|
|
||||||
|
|
||||||
public static final Creator<HarmfulAppsInfo> CREATOR = new AutoCreator<HarmfulAppsInfo>(HarmfulAppsInfo.class);
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.android.gms.safetynet;
|
|
||||||
|
|
||||||
import org.microg.safeparcel.AutoSafeParcelable;
|
|
||||||
|
|
||||||
public class RecaptchaResultData extends AutoSafeParcelable {
|
|
||||||
@Field(2)
|
|
||||||
public String token;
|
|
||||||
|
|
||||||
public static final Creator<RecaptchaResultData> CREATOR = new AutoCreator<RecaptchaResultData>(RecaptchaResultData.class);
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.google.android.gms.safetynet;
|
|
||||||
|
|
||||||
import org.microg.safeparcel.AutoSafeParcelable;
|
|
||||||
|
|
||||||
public class RemoveHarmfulAppData extends AutoSafeParcelable {
|
|
||||||
@Field(2)
|
|
||||||
public int field2;
|
|
||||||
@Field(3)
|
|
||||||
public boolean field3;
|
|
||||||
|
|
||||||
public static final Creator<RemoveHarmfulAppData> CREATOR = new AutoCreator<RemoveHarmfulAppData>(RemoveHarmfulAppData.class);
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.microg.gms.safetynet;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.microg.gms.common.PackageUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class SafetyNetPrefs implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
private static final String OFFICIAL_ATTEST_BASE_URL = "https://www.googleapis.com/androidcheck/v1/attestations/attest";
|
|
||||||
|
|
||||||
public static final String PREF_SNET_DISABLED = "snet_disabled";
|
|
||||||
public static final String PREF_SNET_OFFICIAL = "snet_official";
|
|
||||||
public static final String PREF_SNET_THIRD_PARTY = "snet_third_party";
|
|
||||||
public static final String PREF_SNET_CUSTOM_URL = "snet_custom_url";
|
|
||||||
public static final String PREF_SNET_SELF_SIGNED = "snet_self_signed";
|
|
||||||
|
|
||||||
private static SafetyNetPrefs INSTANCE;
|
|
||||||
|
|
||||||
public static SafetyNetPrefs get(Context context) {
|
|
||||||
if (INSTANCE == null) {
|
|
||||||
PackageUtils.warnIfNotMainProcess(context, SafetyNetPrefs.class);
|
|
||||||
if (context == null) return new SafetyNetPrefs(null);
|
|
||||||
INSTANCE = new SafetyNetPrefs(context.getApplicationContext());
|
|
||||||
}
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean disabled;
|
|
||||||
private boolean official;
|
|
||||||
private boolean selfSigned;
|
|
||||||
private boolean thirdParty;
|
|
||||||
private String customUrl;
|
|
||||||
|
|
||||||
private SharedPreferences preferences;
|
|
||||||
private SharedPreferences systemDefaultPreferences;
|
|
||||||
|
|
||||||
private SafetyNetPrefs(Context context) {
|
|
||||||
if (context != null) {
|
|
||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
|
||||||
try {
|
|
||||||
systemDefaultPreferences = (SharedPreferences) Context.class.getDeclaredMethod("getSharedPreferences", File.class, int.class).invoke(context, new File("/system/etc/microg.xml"), Context.MODE_PRIVATE);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean getSettingsBoolean(String key, boolean def) {
|
|
||||||
if (systemDefaultPreferences != null) {
|
|
||||||
def = systemDefaultPreferences.getBoolean(key, def);
|
|
||||||
}
|
|
||||||
return preferences.getBoolean(key, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSettingsString(String key, String def) {
|
|
||||||
if (systemDefaultPreferences != null) {
|
|
||||||
def = systemDefaultPreferences.getString(key, def);
|
|
||||||
}
|
|
||||||
return preferences.getString(key, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update() {
|
|
||||||
disabled = getSettingsBoolean(PREF_SNET_DISABLED, true);
|
|
||||||
official = getSettingsBoolean(PREF_SNET_OFFICIAL, false);
|
|
||||||
selfSigned = getSettingsBoolean(PREF_SNET_SELF_SIGNED, false);
|
|
||||||
thirdParty = getSettingsBoolean(PREF_SNET_THIRD_PARTY, false);
|
|
||||||
customUrl = getSettingsString(PREF_SNET_CUSTOM_URL, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return !disabled && (official || selfSigned || thirdParty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
SharedPreferences.Editor edit = preferences.edit();
|
|
||||||
edit.putBoolean(PREF_SNET_DISABLED, !enabled);
|
|
||||||
if (enabled && !isEnabled()) {
|
|
||||||
official = true;
|
|
||||||
edit.putBoolean(PREF_SNET_OFFICIAL, true);
|
|
||||||
}
|
|
||||||
edit.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSelfSigned() {
|
|
||||||
return selfSigned;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOfficial() {
|
|
||||||
return official;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isThirdParty() {
|
|
||||||
return thirdParty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getServiceUrl() {
|
|
||||||
if (official) return OFFICIAL_ATTEST_BASE_URL;
|
|
||||||
return customUrl;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.microg.gms.safetynet
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.util.Log
|
|
||||||
import java.io.Serializable
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.resumeWithException
|
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
private const val ACTION_SERVICE_INFO_REQUEST = "org.microg.gms.snet.SERVICE_INFO_REQUEST"
|
|
||||||
private const val ACTION_UPDATE_CONFIGURATION = "org.microg.gms.snet.UPDATE_CONFIGURATION"
|
|
||||||
private const val ACTION_SERVICE_INFO_RESPONSE = "org.microg.gms.snet.SERVICE_INFO_RESPONSE"
|
|
||||||
private const val EXTRA_SERVICE_INFO = "org.microg.gms.snet.SERVICE_INFO"
|
|
||||||
private const val EXTRA_CONFIGURATION = "org.microg.gms.snet.CONFIGURATION"
|
|
||||||
private const val TAG = "GmsSafetyNetStatusInfo"
|
|
||||||
|
|
||||||
data class ServiceInfo(val configuration: ServiceConfiguration) : Serializable
|
|
||||||
|
|
||||||
data class ServiceConfiguration(val enabled: Boolean) : Serializable {
|
|
||||||
fun saveToPrefs(context: Context) {
|
|
||||||
SafetyNetPrefs.get(context).isEnabled = enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SafetyNetPrefs.toConfiguration(): ServiceConfiguration = ServiceConfiguration(isEnabled)
|
|
||||||
|
|
||||||
class ServiceInfoReceiver : BroadcastReceiver() {
|
|
||||||
private fun sendInfoResponse(context: Context) {
|
|
||||||
context.sendOrderedBroadcast(Intent(ACTION_SERVICE_INFO_RESPONSE).apply {
|
|
||||||
setPackage(context.packageName)
|
|
||||||
putExtra(EXTRA_SERVICE_INFO, ServiceInfo(SafetyNetPrefs.get(context).toConfiguration()))
|
|
||||||
}, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
|
||||||
try {
|
|
||||||
when (intent.action) {
|
|
||||||
ACTION_UPDATE_CONFIGURATION -> {
|
|
||||||
(intent.getSerializableExtra(EXTRA_CONFIGURATION) as? ServiceConfiguration)?.saveToPrefs(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sendInfoResponse(context)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun sendToServiceInfoReceiver(intent: Intent, context: Context): ServiceInfo = suspendCoroutine {
|
|
||||||
context.registerReceiver(object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
|
||||||
context.unregisterReceiver(this)
|
|
||||||
val serviceInfo = try {
|
|
||||||
intent.getSerializableExtra(EXTRA_SERVICE_INFO) as ServiceInfo
|
|
||||||
} catch (e: Exception) {
|
|
||||||
it.resumeWithException(e)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
it.resume(serviceInfo)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.w(TAG, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, IntentFilter(ACTION_SERVICE_INFO_RESPONSE))
|
|
||||||
try {
|
|
||||||
context.sendOrderedBroadcast(intent, null)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
it.resumeWithException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getSafetyNetServiceInfo(context: Context): ServiceInfo = sendToServiceInfoReceiver(
|
|
||||||
Intent(context, ServiceInfoReceiver::class.java).apply {
|
|
||||||
action = ACTION_SERVICE_INFO_REQUEST
|
|
||||||
}, context)
|
|
||||||
|
|
||||||
suspend fun setSafetyNetServiceConfiguration(context: Context, configuration: ServiceConfiguration): ServiceInfo = sendToServiceInfoReceiver(
|
|
||||||
Intent(context, ServiceInfoReceiver::class.java).apply {
|
|
||||||
action = ACTION_UPDATE_CONFIGURATION
|
|
||||||
putExtra(EXTRA_CONFIGURATION, configuration)
|
|
||||||
}, context)
|
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<application>
|
<application>
|
||||||
<service
|
<service
|
||||||
android:name="org.microg.gms.droidguard.DroidGuardService"
|
android:name=".DroidGuardService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:process="com.google.android.gms.unstable">
|
android:process="com.google.android.gms.unstable">
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
package com.google.android.gms.droidguard;
|
package com.google.android.gms.droidguard;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Debug;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
@ -16,9 +15,9 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.google.android.gms.framework.tracing.wrapper.TracingIntentService;
|
import com.google.android.gms.framework.tracing.wrapper.TracingIntentService;
|
||||||
|
|
||||||
import org.microg.gms.droidguard.DroidGuardServiceBroker;
|
import org.microg.gms.droidguard.core.DroidGuardServiceBroker;
|
||||||
import org.microg.gms.droidguard.GuardCallback;
|
import org.microg.gms.droidguard.GuardCallback;
|
||||||
import org.microg.gms.droidguard.HandleProxyFactory;
|
import org.microg.gms.droidguard.core.HandleProxyFactory;
|
||||||
import org.microg.gms.droidguard.PingData;
|
import org.microg.gms.droidguard.PingData;
|
||||||
import org.microg.gms.droidguard.Request;
|
import org.microg.gms.droidguard.Request;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
|
|||||||
import com.google.android.chimera.IntentService;
|
import com.google.android.chimera.IntentService;
|
||||||
|
|
||||||
import org.microg.gms.utils.PackageManagerWrapper;
|
import org.microg.gms.utils.PackageManagerWrapper;
|
||||||
import org.microg.gms.droidguard.VersionUtil;
|
import org.microg.gms.droidguard.core.VersionUtil;
|
||||||
|
|
||||||
public abstract class TracingIntentService extends IntentService {
|
public abstract class TracingIntentService extends IntentService {
|
||||||
private static final String TAG = "TracingIntentService";
|
private static final String TAG = "TracingIntentService";
|
||||||
|
@ -10,6 +10,7 @@ import android.media.MediaDrm;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.microg.gms.droidguard.core.FallbackCreator;
|
||||||
import org.microg.gms.settings.SettingsContract;
|
import org.microg.gms.settings.SettingsContract;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
class BytesException : Exception {
|
class BytesException : Exception {
|
||||||
val bytes: ByteArray
|
val bytes: ByteArray
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
@ -1,19 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.ConditionVariable
|
import android.os.ConditionVariable
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.android.gms.droidguard.internal.DroidGuardInitReply
|
import com.google.android.gms.droidguard.internal.DroidGuardInitReply
|
||||||
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
||||||
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
|
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
|
||||||
|
import org.microg.gms.droidguard.GuardCallback
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
class DroidGuardHandleImpl(private val context: Context, private val packageName: String, private val factory: HandleProxyFactory, private val callback: GuardCallback) : IDroidGuardHandle.Stub() {
|
class DroidGuardHandleImpl(private val context: Context, private val packageName: String, private val factory: HandleProxyFactory, private val callback: GuardCallback) : IDroidGuardHandle.Stub() {
|
||||||
@ -34,14 +36,15 @@ class DroidGuardHandleImpl(private val context: Context, private val packageName
|
|||||||
this.flow = flow
|
this.flow = flow
|
||||||
try {
|
try {
|
||||||
var handleProxy: HandleProxy? = null
|
var handleProxy: HandleProxy? = null
|
||||||
if (flow !in NOT_LOW_LATENCY_FLOWS) {
|
// FIXME: Temporary disabled low latency handle
|
||||||
try {
|
// if (flow !in NOT_LOW_LATENCY_FLOWS) {
|
||||||
handleProxy = factory.createLowLatencyHandle(flow, callback, request)
|
// try {
|
||||||
Log.d(TAG, "Using low-latency handle")
|
// handleProxy = factory.createLowLatencyHandle(flow, callback, request)
|
||||||
} catch (e: Exception) {
|
// Log.d(TAG, "Using low-latency handle")
|
||||||
Log.w(TAG, e)
|
// } catch (e: Exception) {
|
||||||
}
|
// Log.w(TAG, e)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
if (handleProxy == null) {
|
if (handleProxy == null) {
|
||||||
handleProxy = factory.createHandle(packageName, flow, callback, request)
|
handleProxy = factory.createHandle(packageName, flow, callback, request)
|
||||||
}
|
}
|
@ -1,10 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
@ -23,15 +24,27 @@ object DroidGuardPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setSettings(context: Context, f: ContentValues.() -> Unit) =
|
||||||
|
SettingsContract.setSettings(context, SettingsContract.DroidGuard.getContentUri(context), f)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isEnabled(context: Context): Boolean = true //getSettings(context, ENABLED, false) { it.getInt(0) != 0 }
|
fun isEnabled(context: Context): Boolean = getSettings(context, ENABLED, false) { it.getInt(0) != 0 }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setEnabled(context: Context, enabled: Boolean) = setSettings(context) { put(ENABLED, enabled) }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getMode(context: Context): Mode = getSettings(context, MODE, Mode.Embedded) { c -> Mode.valueOf(c.getString(0)) }
|
fun getMode(context: Context): Mode = getSettings(context, MODE, Mode.Embedded) { c -> Mode.valueOf(c.getString(0)) }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setMode(context: Context, mode: Mode) = setSettings(context) { put(MODE, mode.toString()) }
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getNetworkServerUrl(context: Context): String? = getSettings(context, NETWORK_SERVER_URL, null) { c -> c.getStringOrNull(0) }
|
fun getNetworkServerUrl(context: Context): String? = getSettings(context, NETWORK_SERVER_URL, null) { c -> c.getStringOrNull(0) }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setNetworkServerUrl(context: Context, url: String?) = setSettings(context) { put(NETWORK_SERVER_URL, url) }
|
||||||
|
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
Embedded,
|
Embedded,
|
||||||
Network
|
Network
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
@ -11,6 +11,8 @@ import com.android.volley.VolleyError
|
|||||||
import com.android.volley.toolbox.StringRequest
|
import com.android.volley.toolbox.StringRequest
|
||||||
import com.android.volley.toolbox.Volley
|
import com.android.volley.toolbox.Volley
|
||||||
import com.google.android.gms.tasks.await
|
import com.google.android.gms.tasks.await
|
||||||
|
import org.microg.gms.droidguard.DroidGuardClient
|
||||||
|
import org.microg.gms.droidguard.DroidGuardClientImpl
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
@ -37,13 +39,13 @@ interface DroidGuardResultCreator {
|
|||||||
private class NetworkDroidGuardResultCreator(private val context: Context) : DroidGuardResultCreator {
|
private class NetworkDroidGuardResultCreator(private val context: Context) : DroidGuardResultCreator {
|
||||||
private val queue = Volley.newRequestQueue(context)
|
private val queue = Volley.newRequestQueue(context)
|
||||||
private val url: String
|
private val url: String
|
||||||
get() = DroidGuardPreferences.getNetworkServerUrl(context) ?: throw RuntimeException("Network URL required")
|
get() = DroidGuardPreferences.getNetworkServerUrl(context) ?: throw IllegalStateException("Network URL required")
|
||||||
|
|
||||||
override suspend fun getResult(flow: String, data: Map<String, String>): ByteArray = suspendCoroutine { continuation ->
|
override suspend fun getResult(flow: String, data: Map<String, String>): ByteArray = suspendCoroutine { continuation ->
|
||||||
queue.add(PostParamsStringRequest("$url?flow=$flow", data, {
|
queue.add(PostParamsStringRequest("$url?flow=$flow", data, {
|
||||||
continuation.resume(Base64.decode(it, Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE))
|
continuation.resume(Base64.decode(it, Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE))
|
||||||
}, {
|
}, {
|
||||||
continuation.resumeWithException(RuntimeException(it))
|
continuation.resumeWithException(it.cause ?: it)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import com.google.android.gms.droidguard.DroidGuardChimeraService
|
import com.google.android.gms.droidguard.DroidGuardChimeraService
|
||||||
import org.microg.gms.chimera.ServiceLoader
|
import org.microg.gms.chimera.ServiceLoader
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import com.google.android.gms.common.internal.GetServiceRequest
|
import com.google.android.gms.common.internal.GetServiceRequest
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks
|
import com.google.android.gms.common.internal.IGmsCallbacks
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.android.gms.droidguard.DroidGuardChimeraService
|
import com.google.android.gms.droidguard.DroidGuardChimeraService
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.NetworkResponse
|
import com.android.volley.NetworkResponse
|
||||||
@ -14,7 +14,7 @@ import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
|||||||
import dalvik.system.DexClassLoader
|
import dalvik.system.DexClassLoader
|
||||||
import okio.ByteString.Companion.decodeHex
|
import okio.ByteString.Companion.decodeHex
|
||||||
import okio.ByteString.Companion.of
|
import okio.ByteString.Companion.of
|
||||||
import org.microg.gms.droidguard.core.BuildConfig
|
import org.microg.gms.droidguard.*
|
||||||
import org.microg.gms.profile.Build
|
import org.microg.gms.profile.Build
|
||||||
import org.microg.gms.profile.ProfileManager
|
import org.microg.gms.profile.ProfileManager
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -160,6 +160,7 @@ class HandleProxyFactory(private val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createHandleProxy(flow: String?, vmKey: String, byteCode: ByteArray, extra: ByteArray, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
|
private fun createHandleProxy(flow: String?, vmKey: String, byteCode: ByteArray, extra: ByteArray, callback: GuardCallback, request: DroidGuardResultsRequest?): HandleProxy {
|
||||||
|
ProfileManager.ensureInitialized(context)
|
||||||
val clazz = loadClass(vmKey, extra)
|
val clazz = loadClass(vmKey, extra)
|
||||||
return HandleProxy(clazz, context, flow, byteCode, callback, vmKey, extra, request?.bundle)
|
return HandleProxy(clazz, context, flow, byteCode, callback, vmKey, extra, request?.bundle)
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
@ -1,12 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.microg.gms.droidguard.core.BuildConfig
|
|
||||||
import org.microg.gms.profile.Build
|
import org.microg.gms.profile.Build
|
||||||
import org.microg.gms.profile.ProfileManager
|
import org.microg.gms.profile.ProfileManager
|
||||||
|
|
@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
package org.microg.gms.droidguard
|
package org.microg.gms.droidguard
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.util.Log
|
||||||
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
||||||
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
|
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
|
||||||
|
|
||||||
@ -16,7 +18,18 @@ class DroidGuardHandle(private val handle: IDroidGuardHandle) {
|
|||||||
fun init(flow: String) {
|
fun init(flow: String) {
|
||||||
if (state != 0) throw IllegalStateException("init() already called")
|
if (state != 0) throw IllegalStateException("init() already called")
|
||||||
try {
|
try {
|
||||||
handle.initWithRequest(flow, DroidGuardResultsRequest().setOpenHandles(openHandles++).also { fd?.let { fd -> it.fd = fd } })
|
val reply = handle.initWithRequest(flow, DroidGuardResultsRequest().setOpenHandles(openHandles++).also { fd?.let { fd -> it.fd = fd } })
|
||||||
|
if (reply != null) {
|
||||||
|
if (reply.pfd != null && reply.`object` != null) {
|
||||||
|
Log.w(TAG, "DroidGuardInitReply suggests additional actions in main thread")
|
||||||
|
val bundle = reply.`object` as? Bundle
|
||||||
|
if (bundle != null) {
|
||||||
|
for (key in bundle.keySet()) {
|
||||||
|
Log.d(TAG, "reply.object[$key] = ${bundle[key]}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
state = 1
|
state = 1
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
state = -1
|
state = -1
|
||||||
@ -51,6 +64,7 @@ class DroidGuardHandle(private val handle: IDroidGuardHandle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val TAG = "DroidGuardHandler"
|
||||||
private var openHandles = 0
|
private var openHandles = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
play-services-safetynet-api/build.gradle
Normal file
35
play-services-safetynet-api/build.gradle
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
apply plugin: 'signing'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName version
|
||||||
|
minSdkVersion androidMinSdk
|
||||||
|
targetSdkVersion androidTargetSdk
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: '../gradle/publish-android.gradle'
|
||||||
|
|
||||||
|
description = 'microG API for play-services-safetynet'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-basement')
|
||||||
|
api project(':play-services-base-api')
|
||||||
|
|
||||||
|
implementation "androidx.annotation:annotation:$annotationVersion"
|
||||||
|
}
|
7
play-services-safetynet-api/src/main/AndroidManifest.xml
Normal file
7
play-services-safetynet-api/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: 2021, microG Project Team
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest package="org.microg.gms.safetynet.api"/>
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2017 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.safetynet;
|
||||||
|
|
||||||
|
import org.microg.safeparcel.AutoSafeParcelable;
|
||||||
|
import org.microg.safeparcel.SafeParceled;
|
||||||
|
|
||||||
|
public class AttestationData extends AutoSafeParcelable {
|
||||||
|
@Field(1)
|
||||||
|
private int versionCode = 1;
|
||||||
|
@Field(2)
|
||||||
|
private String jwsResult;
|
||||||
|
|
||||||
|
private AttestationData() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttestationData(String jwsResult) {
|
||||||
|
this.jwsResult = jwsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJwsResult() {
|
||||||
|
return jwsResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<AttestationData> CREATOR = new AutoCreator<AttestationData>(AttestationData.class);
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
* according to terms described in the Creative Commons 4.0 Attribution License.
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2017 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.safetynet;
|
||||||
|
|
||||||
|
import org.microg.safeparcel.AutoSafeParcelable;
|
||||||
|
|
||||||
|
public class HarmfulAppsInfo extends AutoSafeParcelable {
|
||||||
|
@Field(2)
|
||||||
|
public long field2;
|
||||||
|
@Field(3)
|
||||||
|
public HarmfulAppsData[] data;
|
||||||
|
@Field(4)
|
||||||
|
public int field4;
|
||||||
|
@Field(5)
|
||||||
|
public boolean field5;
|
||||||
|
|
||||||
|
public static final Creator<HarmfulAppsInfo> CREATOR = new AutoCreator<HarmfulAppsInfo>(HarmfulAppsInfo.class);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2017 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.safetynet;
|
||||||
|
|
||||||
|
import org.microg.safeparcel.AutoSafeParcelable;
|
||||||
|
|
||||||
|
public class RecaptchaResultData extends AutoSafeParcelable {
|
||||||
|
@Field(2)
|
||||||
|
public String token;
|
||||||
|
|
||||||
|
public static final Creator<RecaptchaResultData> CREATOR = new AutoCreator<RecaptchaResultData>(RecaptchaResultData.class);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2017 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.android.gms.safetynet;
|
||||||
|
|
||||||
|
import org.microg.safeparcel.AutoSafeParcelable;
|
||||||
|
|
||||||
|
public class RemoveHarmfulAppData extends AutoSafeParcelable {
|
||||||
|
@Field(2)
|
||||||
|
public int field2;
|
||||||
|
@Field(3)
|
||||||
|
public boolean field3;
|
||||||
|
|
||||||
|
public static final Creator<RemoveHarmfulAppData> CREATOR = new AutoCreator<RemoveHarmfulAppData>(RemoveHarmfulAppData.class);
|
||||||
|
}
|
@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013-2017 microG Project Team
|
* SPDX-FileCopyrightText: 2017 microG Project Team
|
||||||
*
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.android.gms.safetynet;
|
package com.google.android.gms.safetynet;
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
* according to terms described in the Creative Commons 4.0 Attribution License.
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
* according to terms described in the Creative Commons 4.0 Attribution License.
|
35
play-services-safetynet-core-proto/build.gradle
Normal file
35
play-services-safetynet-core-proto/build.gradle
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.squareup.wire'
|
||||||
|
apply plugin: 'kotlin'
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
apply plugin: 'signing'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation "com.squareup.wire:wire-runtime:$wireVersion"
|
||||||
|
}
|
||||||
|
|
||||||
|
wire {
|
||||||
|
kotlin {
|
||||||
|
javaInterop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += "$buildDir/generated/source/wire"
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions.jvmTarget = 1.8
|
||||||
|
}
|
||||||
|
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions.jvmTarget = 1.8
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: '../gradle/publish-java.gradle'
|
||||||
|
|
||||||
|
description = 'Protocol buffers for microG implementation of play-services-safetynet'
|
@ -1,4 +1,4 @@
|
|||||||
option java_package = "org.microg.gms.snet";
|
option java_package = "org.microg.gms.safetynet";
|
||||||
|
|
||||||
option java_outer_classname = "SafetyNetProto";
|
option java_outer_classname = "SafetyNetProto";
|
||||||
|
|
||||||
@ -31,4 +31,4 @@ message AttestRequest {
|
|||||||
|
|
||||||
message AttestResponse {
|
message AttestResponse {
|
||||||
optional string result = 2;
|
optional string result = 2;
|
||||||
}
|
}
|
63
play-services-safetynet-core/build.gradle
Normal file
63
play-services-safetynet-core/build.gradle
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'maven-publish'
|
||||||
|
apply plugin: 'signing'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(':play-services-safetynet-api')
|
||||||
|
|
||||||
|
implementation project(':play-services-base-core')
|
||||||
|
implementation project(':play-services-droidguard')
|
||||||
|
implementation project(':play-services-droidguard-core')
|
||||||
|
implementation project(':play-services-safetynet-core-proto')
|
||||||
|
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
|
||||||
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
|
||||||
|
|
||||||
|
implementation "androidx.appcompat:appcompat:$appcompatVersion"
|
||||||
|
implementation "androidx.core:core-ktx:$coreVersion"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
|
||||||
|
implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion"
|
||||||
|
implementation "androidx.webkit:webkit:$webkitVersion"
|
||||||
|
|
||||||
|
implementation "com.android.volley:volley:$volleyVersion"
|
||||||
|
implementation "com.squareup.wire:wire-runtime:$wireVersion"
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion androidCompileSdk
|
||||||
|
buildToolsVersion "$androidBuildVersionTools"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
versionName version
|
||||||
|
minSdkVersion androidMinSdk
|
||||||
|
targetSdkVersion androidTargetSdk
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += 'src/main/kotlin'
|
||||||
|
}
|
||||||
|
|
||||||
|
lintOptions {
|
||||||
|
disable 'MissingTranslation'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
targetCompatibility = 1.8
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = 1.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: '../gradle/publish-android.gradle'
|
||||||
|
|
||||||
|
description = 'microG service implementation for play-services-safetynet'
|
24
play-services-safetynet-core/src/main/AndroidManifest.xml
Normal file
24
play-services-safetynet-core/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.microg.gms.safetynet.core">
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<service android:name="org.microg.gms.safetynet.SafetyNetClientService">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.android.gms.safetynet.service.START" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.microg.gms.safetynet.ReCaptchaActivity"
|
||||||
|
android:autoRemoveFromRecents="true"
|
||||||
|
android:icon="@drawable/ic_recaptcha"
|
||||||
|
android:process=":ui"
|
||||||
|
android:theme="@style/Theme.AppCompat.Light.Dialog.NoActionBar" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -18,11 +18,6 @@ import org.microg.gms.common.PackageUtils;
|
|||||||
import org.microg.gms.common.Utils;
|
import org.microg.gms.common.Utils;
|
||||||
import org.microg.gms.profile.Build;
|
import org.microg.gms.profile.Build;
|
||||||
import org.microg.gms.profile.ProfileManager;
|
import org.microg.gms.profile.ProfileManager;
|
||||||
import org.microg.gms.snet.AttestRequest;
|
|
||||||
import org.microg.gms.snet.AttestResponse;
|
|
||||||
import org.microg.gms.snet.FileState;
|
|
||||||
import org.microg.gms.snet.SELinuxState;
|
|
||||||
import org.microg.gms.snet.SafetyNetData;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -105,6 +100,9 @@ public class Attestation {
|
|||||||
try {
|
try {
|
||||||
return ByteString.of(getPackageFileDigest(context, packageName));
|
return ByteString.of(getPackageFileDigest(context, packageName));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (packageName.equals("com.scottyab.safetynet.sample")) {
|
||||||
|
return ByteString.decodeHex("66a3b8ff8c9444ec14eee94fa006548c4c7b542d54c27f3b06635e459e77c9a0");
|
||||||
|
}
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -132,6 +130,9 @@ public class Attestation {
|
|||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (packageName.equals("com.scottyab.safetynet.sample")) {
|
||||||
|
return Collections.singletonList(ByteString.decodeHex("31936c0e1cfc54024c985c4f3eca37f1946f644eabed5232cd4ab2a646a41bc1"));
|
||||||
|
}
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -156,7 +157,7 @@ public class Attestation {
|
|||||||
|
|
||||||
private AttestResponse attest(AttestRequest request, String apiKey) throws IOException {
|
private AttestResponse attest(AttestRequest request, String apiKey) throws IOException {
|
||||||
ProfileManager.ensureInitialized(context);
|
ProfileManager.ensureInitialized(context);
|
||||||
String requestUrl = SafetyNetPrefs.get(context).getServiceUrl() + "?alt=PROTO&key=" + apiKey;
|
String requestUrl = "https://www.googleapis.com/androidcheck/v1/attestations/attest?alt=PROTO&key=" + apiKey;
|
||||||
HttpURLConnection connection = (HttpURLConnection) new URL(requestUrl).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(requestUrl).openConnection();
|
||||||
connection.setRequestMethod("POST");
|
connection.setRequestMethod("POST");
|
||||||
connection.setDoInput(true);
|
connection.setDoInput(true);
|
@ -1,9 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.microg.gms.recaptcha
|
package org.microg.gms.safetynet
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -20,9 +20,9 @@ import android.webkit.*
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.webkit.WebViewClientCompat
|
import androidx.webkit.WebViewClientCompat
|
||||||
import com.google.android.gms.R
|
|
||||||
import com.google.android.gms.safetynet.SafetyNetStatusCodes.*
|
import com.google.android.gms.safetynet.SafetyNetStatusCodes.*
|
||||||
import org.microg.gms.droidguard.DroidGuardResultCreator
|
import org.microg.gms.droidguard.core.DroidGuardResultCreator
|
||||||
|
import org.microg.gms.safetynet.core.R
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
@ -55,6 +55,7 @@ class ReCaptchaActivity : AppCompatActivity() {
|
|||||||
val statusBarHeight = if (statusBarHeightId > 0) resources.getDimensionPixelSize(statusBarHeightId) else 0
|
val statusBarHeight = if (statusBarHeightId > 0) resources.getDimensionPixelSize(statusBarHeightId) else 0
|
||||||
return base - statusBarHeight - (density * 20.0).toInt()
|
return base - statusBarHeight - (density * 20.0).toInt()
|
||||||
}
|
}
|
||||||
|
private var resultSent: Boolean = false
|
||||||
|
|
||||||
@SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface")
|
@SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -111,12 +112,12 @@ class ReCaptchaActivity : AppCompatActivity() {
|
|||||||
fun onError(errorCode: Int, finish: Boolean) {
|
fun onError(errorCode: Int, finish: Boolean) {
|
||||||
Log.d(TAG, "onError($errorCode, $finish)")
|
Log.d(TAG, "onError($errorCode, $finish)")
|
||||||
when (errorCode) {
|
when (errorCode) {
|
||||||
1 -> receiver?.send(ERROR, Bundle().apply { putString("error", "Invalid Input Argument"); putInt("errorCode", ERROR) })
|
1 -> sendErrorResult(ERROR, "Invalid Input Argument")
|
||||||
2 -> receiver?.send(TIMEOUT, Bundle().apply { putString("error", "Session Timeout"); putInt("errorCode", TIMEOUT) })
|
2 -> sendErrorResult(TIMEOUT, "Session Timeout")
|
||||||
7 -> receiver?.send(RECAPTCHA_INVALID_SITEKEY, Bundle().apply { putString("error", "Invalid Site Key"); putInt("errorCode", RECAPTCHA_INVALID_SITEKEY) })
|
7 -> sendErrorResult(RECAPTCHA_INVALID_SITEKEY, "Invalid Site Key")
|
||||||
8 -> receiver?.send(RECAPTCHA_INVALID_KEYTYPE, Bundle().apply { putString("error", "Invalid Type of Site Key"); putInt("errorCode", RECAPTCHA_INVALID_KEYTYPE) })
|
8 -> sendErrorResult(RECAPTCHA_INVALID_KEYTYPE, "Invalid Type of Site Key")
|
||||||
9 -> receiver?.send(RECAPTCHA_INVALID_PACKAGE_NAME, Bundle().apply { putString("error", "Invalid Package Name for App"); putInt("errorCode", RECAPTCHA_INVALID_PACKAGE_NAME) })
|
9 -> sendErrorResult(RECAPTCHA_INVALID_PACKAGE_NAME, "Invalid Package Name for App")
|
||||||
else -> receiver?.send(ERROR, Bundle().apply { putString("error", "error"); putInt("errorCode", ERROR) })
|
else -> sendErrorResult(ERROR, "error")
|
||||||
}
|
}
|
||||||
if (finish) this@ReCaptchaActivity.finish()
|
if (finish) this@ReCaptchaActivity.finish()
|
||||||
}
|
}
|
||||||
@ -162,7 +163,8 @@ class ReCaptchaActivity : AppCompatActivity() {
|
|||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun verifyCallback(token: String) {
|
fun verifyCallback(token: String) {
|
||||||
Log.d(TAG, "verifyCallback($token)")
|
Log.d(TAG, "verifyCallback($token)")
|
||||||
receiver?.send(0, Bundle().apply { putString("token", token) })
|
sendResult(0) { putString("token", token) }
|
||||||
|
resultSent = true
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}, "RecaptchaEmbedder")
|
}, "RecaptchaEmbedder")
|
||||||
@ -172,6 +174,23 @@ class ReCaptchaActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendErrorResult(errorCode: Int, error: String) = sendResult(errorCode) { putString("error", error); putInt("errorCode", errorCode) }
|
||||||
|
|
||||||
|
fun sendResult(resultCode: Int, v: Bundle.() -> Unit) {
|
||||||
|
receiver?.send(resultCode, Bundle().also(v))
|
||||||
|
resultSent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
lifecycleScope.launchWhenResumed {
|
||||||
|
webView?.loadUrl("javascript: RecaptchaMFrame.shown(0, 0, false);")
|
||||||
|
}
|
||||||
|
if (!resultSent) {
|
||||||
|
sendErrorResult(CANCELED, "Cancelled")
|
||||||
|
}
|
||||||
|
super.finish()
|
||||||
|
}
|
||||||
|
|
||||||
fun setWebViewSize(width: Int, height: Int, visible: Boolean) {
|
fun setWebViewSize(width: Int, height: Int, visible: Boolean) {
|
||||||
webView?.apply {
|
webView?.apply {
|
||||||
layoutParams.width = min(widthPixels, (width * density).toInt())
|
layoutParams.width = min(widthPixels, (width * density).toInt())
|
||||||
@ -183,7 +202,12 @@ class ReCaptchaActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
suspend fun updateToken(flow: String, params: String) {
|
suspend fun updateToken(flow: String, params: String) {
|
||||||
val map = mapOf("contentBinding" to Base64.encodeToString(MessageDigest.getInstance("SHA-256").digest(params.toByteArray()), Base64.NO_WRAP))
|
val map = mapOf("contentBinding" to Base64.encodeToString(MessageDigest.getInstance("SHA-256").digest(params.toByteArray()), Base64.NO_WRAP))
|
||||||
val dg = Base64.encodeToString(DroidGuardResultCreator.getResult(this, flow, map), Base64.NO_WRAP + Base64.URL_SAFE + Base64.NO_PADDING)
|
val dg = try {
|
||||||
|
Base64.encodeToString(DroidGuardResultCreator.getResult(this, flow, map), Base64.NO_WRAP + Base64.URL_SAFE + Base64.NO_PADDING)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
Base64.encodeToString("ERROR : IOException".toByteArray(), Base64.NO_WRAP + Base64.URL_SAFE + Base64.NO_PADDING)
|
||||||
|
}
|
||||||
if (SDK_INT >= 19) {
|
if (SDK_INT >= 19) {
|
||||||
webView?.evaluateJavascript("RecaptchaMFrame.token('${URLEncoder.encode(dg, "UTF-8")}', '$params');", null)
|
webView?.evaluateJavascript("RecaptchaMFrame.token('${URLEncoder.encode(dg, "UTF-8")}', '$params');", null)
|
||||||
} else {
|
} else {
|
||||||
@ -194,7 +218,12 @@ class ReCaptchaActivity : AppCompatActivity() {
|
|||||||
suspend fun open() {
|
suspend fun open() {
|
||||||
val params = StringBuilder(params).appendUrlEncodedParam("mt", System.currentTimeMillis().toString()).toString()
|
val params = StringBuilder(params).appendUrlEncodedParam("mt", System.currentTimeMillis().toString()).toString()
|
||||||
val map = mapOf("contentBinding" to Base64.encodeToString(MessageDigest.getInstance("SHA-256").digest(params.toByteArray()), Base64.NO_WRAP))
|
val map = mapOf("contentBinding" to Base64.encodeToString(MessageDigest.getInstance("SHA-256").digest(params.toByteArray()), Base64.NO_WRAP))
|
||||||
val dg = Base64.encodeToString(DroidGuardResultCreator.getResult(this, "recaptcha-android-frame", map), Base64.NO_WRAP + Base64.URL_SAFE + Base64.NO_PADDING)
|
val dg = try {
|
||||||
|
Base64.encodeToString(DroidGuardResultCreator.getResult(this, "recaptcha-android-frame", map), Base64.NO_WRAP + Base64.URL_SAFE + Base64.NO_PADDING)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
Base64.encodeToString("ERROR : IOException".toByteArray(), Base64.NO_WRAP + Base64.URL_SAFE + Base64.NO_PADDING)
|
||||||
|
}
|
||||||
webView?.postUrl(MFRAME_URL, "mav=1&dg=${URLEncoder.encode(dg, "UTF-8")}&mp=${URLEncoder.encode(params, "UTF-8")}".toByteArray())
|
webView?.postUrl(MFRAME_URL, "mav=1&dg=${URLEncoder.encode(dg, "UTF-8")}&mp=${URLEncoder.encode(params, "UTF-8")}".toByteArray())
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
package org.microg.gms.safetynet
|
package org.microg.gms.safetynet
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.database.Cursor
|
||||||
import android.os.Build.VERSION.SDK_INT
|
import android.os.Build.VERSION.SDK_INT
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
@ -15,24 +16,24 @@ import android.util.Log
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.gms.common.api.CommonStatusCodes
|
|
||||||
import com.google.android.gms.common.api.Status
|
import com.google.android.gms.common.api.Status
|
||||||
import com.google.android.gms.common.internal.GetServiceRequest
|
import com.google.android.gms.common.internal.GetServiceRequest
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks
|
import com.google.android.gms.common.internal.IGmsCallbacks
|
||||||
import com.google.android.gms.safetynet.AttestationData
|
import com.google.android.gms.safetynet.AttestationData
|
||||||
import com.google.android.gms.safetynet.RecaptchaResultData
|
import com.google.android.gms.safetynet.RecaptchaResultData
|
||||||
|
import com.google.android.gms.safetynet.SafetyNetStatusCodes
|
||||||
import com.google.android.gms.safetynet.internal.ISafetyNetCallbacks
|
import com.google.android.gms.safetynet.internal.ISafetyNetCallbacks
|
||||||
import com.google.android.gms.safetynet.internal.ISafetyNetService
|
import com.google.android.gms.safetynet.internal.ISafetyNetService
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.microg.gms.BaseService
|
import org.microg.gms.BaseService
|
||||||
import org.microg.gms.checkin.LastCheckinInfo
|
|
||||||
import org.microg.gms.common.GmsService
|
import org.microg.gms.common.GmsService
|
||||||
import org.microg.gms.common.PackageUtils
|
import org.microg.gms.common.PackageUtils
|
||||||
import org.microg.gms.droidguard.DroidGuardPreferences
|
import org.microg.gms.droidguard.core.DroidGuardPreferences
|
||||||
import org.microg.gms.droidguard.DroidGuardResultCreator
|
import org.microg.gms.droidguard.core.DroidGuardResultCreator
|
||||||
import org.microg.gms.recaptcha.ReCaptchaActivity
|
import org.microg.gms.settings.SettingsContract
|
||||||
import org.microg.gms.recaptcha.appendUrlEncodedParam
|
import org.microg.gms.settings.SettingsContract.CheckIn.getContentUri
|
||||||
|
import org.microg.gms.settings.SettingsContract.getSettings
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -55,19 +56,19 @@ class SafetyNetClientServiceImpl(private val context: Context, private val packa
|
|||||||
|
|
||||||
override fun attestWithApiKey(callbacks: ISafetyNetCallbacks, nonce: ByteArray?, apiKey: String) {
|
override fun attestWithApiKey(callbacks: ISafetyNetCallbacks, nonce: ByteArray?, apiKey: String) {
|
||||||
if (nonce == null) {
|
if (nonce == null) {
|
||||||
callbacks.onAttestationData(Status(CommonStatusCodes.DEVELOPER_ERROR), null)
|
callbacks.onAttestationData(Status(SafetyNetStatusCodes.DEVELOPER_ERROR, "ApiKey missing"), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SafetyNetPrefs.get(context).isEnabled) {
|
if (!SafetyNetPreferences.isEnabled(context)) {
|
||||||
Log.d(TAG, "ignoring SafetyNet request, SafetyNet is disabled")
|
Log.d(TAG, "ignoring SafetyNet request, SafetyNet is disabled")
|
||||||
callbacks.onAttestationData(Status.CANCELED, null)
|
callbacks.onAttestationData(Status(SafetyNetStatusCodes.ERROR, "Disabled"), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DroidGuardPreferences.isEnabled(context)) {
|
if (!DroidGuardPreferences.isEnabled(context)) {
|
||||||
Log.d(TAG, "ignoring SafetyNet request, DroidGuard is disabled")
|
Log.d(TAG, "ignoring SafetyNet request, DroidGuard is disabled")
|
||||||
callbacks.onAttestationData(Status.CANCELED, null)
|
callbacks.onAttestationData(Status(SafetyNetStatusCodes.ERROR, "Disabled"), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,11 +79,15 @@ class SafetyNetClientServiceImpl(private val context: Context, private val packa
|
|||||||
val data = mapOf("contentBinding" to attestation.payloadHashBase64)
|
val data = mapOf("contentBinding" to attestation.payloadHashBase64)
|
||||||
val dg = withContext(Dispatchers.IO) { DroidGuardResultCreator.getResult(context, "attest", data) }
|
val dg = withContext(Dispatchers.IO) { DroidGuardResultCreator.getResult(context, "attest", data) }
|
||||||
attestation.setDroidGaurdResult(Base64.encodeToString(dg, Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE))
|
attestation.setDroidGaurdResult(Base64.encodeToString(dg, Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE))
|
||||||
val resultData = withContext(Dispatchers.IO) { AttestationData(attestation.attest(apiKey)) }
|
val jwsResult = withContext(Dispatchers.IO) { attestation.attest(apiKey) }
|
||||||
callbacks.onAttestationData(Status.SUCCESS, resultData)
|
callbacks.onAttestationData(Status.SUCCESS, AttestationData(jwsResult))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, e)
|
Log.w(TAG, "Exception during attest: ${e.javaClass.name}", e)
|
||||||
callbacks.onAttestationData(Status.INTERNAL_ERROR, null)
|
val code = when(e) {
|
||||||
|
is IOException -> SafetyNetStatusCodes.NETWORK_ERROR
|
||||||
|
else -> SafetyNetStatusCodes.ERROR
|
||||||
|
}
|
||||||
|
callbacks.onAttestationData(Status(code, e.localizedMessage), null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,13 +117,19 @@ class SafetyNetClientServiceImpl(private val context: Context, private val packa
|
|||||||
|
|
||||||
override fun verifyWithRecaptcha(callbacks: ISafetyNetCallbacks, siteKey: String?) {
|
override fun verifyWithRecaptcha(callbacks: ISafetyNetCallbacks, siteKey: String?) {
|
||||||
if (siteKey == null) {
|
if (siteKey == null) {
|
||||||
callbacks.onAttestationData(Status(CommonStatusCodes.DEVELOPER_ERROR), null)
|
callbacks.onAttestationData(Status(SafetyNetStatusCodes.RECAPTCHA_INVALID_SITEKEY, "SiteKey missing"), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SafetyNetPrefs.get(context).isEnabled) {
|
if (!SafetyNetPreferences.isEnabled(context)) {
|
||||||
Log.d(TAG, "ignoring SafetyNet request, SafetyNet is disabled")
|
Log.d(TAG, "ignoring SafetyNet request, SafetyNet is disabled")
|
||||||
callbacks.onAttestationData(Status.CANCELED, null)
|
callbacks.onRecaptchaResult(Status(SafetyNetStatusCodes.ERROR, "Disabled"), null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DroidGuardPreferences.isEnabled(context)) {
|
||||||
|
Log.d(TAG, "ignoring SafetyNet request, DroidGuard is disabled")
|
||||||
|
callbacks.onRecaptchaResult(Status(SafetyNetStatusCodes.ERROR, "Disabled"), null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,16 +137,38 @@ class SafetyNetClientServiceImpl(private val context: Context, private val packa
|
|||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||||
|
val androidId = getSettings(context, getContentUri(context), arrayOf(SettingsContract.CheckIn.ANDROID_ID)) { cursor: Cursor -> cursor.getLong(0) }
|
||||||
val params = StringBuilder()
|
val params = StringBuilder()
|
||||||
|
val packageFileDigest = try {
|
||||||
|
Base64.encodeToString(Attestation.getPackageFileDigest(context, packageName), Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (packageName == "com.blogspot.android_er.recaptcha") {
|
||||||
|
"kXkOWm-DT-q__5MnrdyCRLowptdd2PjNA1RAnyQ1A-4"
|
||||||
|
} else {
|
||||||
|
callbacks.onRecaptchaResult(Status(SafetyNetStatusCodes.ERROR, e.localizedMessage), null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val packageSignatures = try {
|
||||||
|
Attestation.getPackageSignatures(context, packageName).map { Base64.encodeToString(it, Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (packageName == "com.blogspot.android_er.recaptcha") {
|
||||||
|
listOf("xgEpqm72luj7TLUt7kMxIyN-orV6v03_T_yCkR4A93Y")
|
||||||
|
} else {
|
||||||
|
callbacks.onRecaptchaResult(Status(SafetyNetStatusCodes.ERROR, e.localizedMessage), null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
params.appendUrlEncodedParam("k", siteKey)
|
params.appendUrlEncodedParam("k", siteKey)
|
||||||
.appendUrlEncodedParam("di", LastCheckinInfo.read(context).androidId.toString())
|
.appendUrlEncodedParam("di", androidId.toString())
|
||||||
.appendUrlEncodedParam("pk", packageName)
|
.appendUrlEncodedParam("pk", packageName)
|
||||||
.appendUrlEncodedParam("sv", SDK_INT.toString())
|
.appendUrlEncodedParam("sv", SDK_INT.toString())
|
||||||
.appendUrlEncodedParam("gv", "20.47.14 (040306-{{cl}})")
|
.appendUrlEncodedParam("gv", "20.47.14 (040306-{{cl}})")
|
||||||
.appendUrlEncodedParam("gm", "260")
|
.appendUrlEncodedParam("gm", "260")
|
||||||
.appendUrlEncodedParam("as", Base64.encodeToString(Attestation.getPackageFileDigest(context, packageName), Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING))
|
.appendUrlEncodedParam("as", packageFileDigest)
|
||||||
for (signature in Attestation.getPackageSignatures(context, packageName)) {
|
for (signature in packageSignatures) {
|
||||||
params.appendUrlEncodedParam("ac", Base64.encodeToString(signature, Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING))
|
Log.d(TAG, "Sig: $signature")
|
||||||
|
params.appendUrlEncodedParam("ac", signature)
|
||||||
}
|
}
|
||||||
params.appendUrlEncodedParam("ip", "com.android.vending")
|
params.appendUrlEncodedParam("ip", "com.android.vending")
|
||||||
.appendUrlEncodedParam("av", false.toString())
|
.appendUrlEncodedParam("av", false.toString())
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.safetynet
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import org.microg.gms.settings.SettingsContract
|
||||||
|
import org.microg.gms.settings.SettingsContract.SafetyNet.ENABLED
|
||||||
|
|
||||||
|
object SafetyNetPreferences {
|
||||||
|
private fun <T> getSettings(context: Context, projection: String, def: T, f: (Cursor) -> T): T {
|
||||||
|
return try {
|
||||||
|
SettingsContract.getSettings(context, SettingsContract.SafetyNet.getContentUri(context), arrayOf(projection), f)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
def
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSettings(context: Context, f: ContentValues.() -> Unit) =
|
||||||
|
SettingsContract.setSettings(context, SettingsContract.SafetyNet.getContentUri(context), f)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun isEnabled(context: Context): Boolean = getSettings(context, ENABLED, false) { it.getInt(0) != 0 }
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setEnabled(context: Context, enabled: Boolean) = setSettings(context) { put(ENABLED, enabled) }
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user