mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-12-30 22:45:47 +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>
|
||||
<service
|
||||
android:name="org.microg.gms.droidguard.DroidGuardService"
|
||||
android:name=".DroidGuardService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:process="com.google.android.gms.unstable">
|
||||
|
@ -6,7 +6,6 @@
|
||||
package com.google.android.gms.droidguard;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.Base64;
|
||||
@ -16,9 +15,9 @@ import androidx.annotation.Nullable;
|
||||
|
||||
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.HandleProxyFactory;
|
||||
import org.microg.gms.droidguard.core.HandleProxyFactory;
|
||||
import org.microg.gms.droidguard.PingData;
|
||||
import org.microg.gms.droidguard.Request;
|
||||
|
||||
|
@ -16,7 +16,7 @@ import androidx.annotation.Nullable;
|
||||
import com.google.android.chimera.IntentService;
|
||||
|
||||
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 {
|
||||
private static final String TAG = "TracingIntentService";
|
||||
|
@ -10,6 +10,7 @@ import android.media.MediaDrm;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import org.microg.gms.droidguard.core.FallbackCreator;
|
||||
import org.microg.gms.settings.SettingsContract;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
class BytesException : Exception {
|
||||
val bytes: ByteArray
|
@ -3,7 +3,7 @@
|
||||
* 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
|
@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.Context
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.ConditionVariable
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.os.Parcelable
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.android.gms.droidguard.internal.DroidGuardInitReply
|
||||
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
||||
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
|
||||
import org.microg.gms.droidguard.GuardCallback
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
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
|
||||
try {
|
||||
var handleProxy: HandleProxy? = null
|
||||
if (flow !in NOT_LOW_LATENCY_FLOWS) {
|
||||
try {
|
||||
handleProxy = factory.createLowLatencyHandle(flow, callback, request)
|
||||
Log.d(TAG, "Using low-latency handle")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, e)
|
||||
}
|
||||
}
|
||||
// FIXME: Temporary disabled low latency handle
|
||||
// if (flow !in NOT_LOW_LATENCY_FLOWS) {
|
||||
// try {
|
||||
// handleProxy = factory.createLowLatencyHandle(flow, callback, request)
|
||||
// Log.d(TAG, "Using low-latency handle")
|
||||
// } catch (e: Exception) {
|
||||
// Log.w(TAG, e)
|
||||
// }
|
||||
// }
|
||||
if (handleProxy == null) {
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
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
|
||||
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
|
||||
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
|
||||
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 {
|
||||
Embedded,
|
||||
Network
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Base64
|
||||
@ -11,6 +11,8 @@ import com.android.volley.VolleyError
|
||||
import com.android.volley.toolbox.StringRequest
|
||||
import com.android.volley.toolbox.Volley
|
||||
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.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
@ -37,13 +39,13 @@ interface DroidGuardResultCreator {
|
||||
private class NetworkDroidGuardResultCreator(private val context: Context) : DroidGuardResultCreator {
|
||||
private val queue = Volley.newRequestQueue(context)
|
||||
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 ->
|
||||
queue.add(PostParamsStringRequest("$url?flow=$flow", data, {
|
||||
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-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import com.google.android.gms.droidguard.DroidGuardChimeraService
|
||||
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
|
||||
*/
|
||||
|
||||
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.IGmsCallbacks
|
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.util.Log
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.Context
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.Context
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.Context
|
||||
import com.android.volley.NetworkResponse
|
||||
@ -14,7 +14,7 @@ import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
||||
import dalvik.system.DexClassLoader
|
||||
import okio.ByteString.Companion.decodeHex
|
||||
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.ProfileManager
|
||||
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 {
|
||||
ProfileManager.ensureInitialized(context)
|
||||
val clazz = loadClass(vmKey, extra)
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.util.Base64
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
package org.microg.gms.droidguard.core
|
||||
|
||||
import android.content.Context
|
||||
import org.microg.gms.droidguard.core.BuildConfig
|
||||
import org.microg.gms.profile.Build
|
||||
import org.microg.gms.profile.ProfileManager
|
||||
|
@ -5,7 +5,9 @@
|
||||
|
||||
package org.microg.gms.droidguard
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import com.google.android.gms.droidguard.internal.DroidGuardResultsRequest
|
||||
import com.google.android.gms.droidguard.internal.IDroidGuardHandle
|
||||
|
||||
@ -16,7 +18,18 @@ class DroidGuardHandle(private val handle: IDroidGuardHandle) {
|
||||
fun init(flow: String) {
|
||||
if (state != 0) throw IllegalStateException("init() already called")
|
||||
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
|
||||
} catch (e: Exception) {
|
||||
state = -1
|
||||
@ -51,6 +64,7 @@ class DroidGuardHandle(private val handle: IDroidGuardHandle) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DroidGuardHandler"
|
||||
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
|
||||
* 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.
|
@ -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
|
||||
*
|
||||
* 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.
|
||||
* SPDX-FileCopyrightText: 2017 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
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
|
||||
* 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.
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* 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.
|
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";
|
||||
|
||||
@ -31,4 +31,4 @@ message AttestRequest {
|
||||
|
||||
message AttestResponse {
|
||||
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
|
||||
*/
|
||||
|
||||
@ -18,11 +18,6 @@ import org.microg.gms.common.PackageUtils;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.gms.profile.Build;
|
||||
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.File;
|
||||
@ -105,6 +100,9 @@ public class Attestation {
|
||||
try {
|
||||
return ByteString.of(getPackageFileDigest(context, packageName));
|
||||
} catch (Exception e) {
|
||||
if (packageName.equals("com.scottyab.safetynet.sample")) {
|
||||
return ByteString.decodeHex("66a3b8ff8c9444ec14eee94fa006548c4c7b542d54c27f3b06635e459e77c9a0");
|
||||
}
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
@ -132,6 +130,9 @@ public class Attestation {
|
||||
}
|
||||
return res;
|
||||
} catch (Exception e) {
|
||||
if (packageName.equals("com.scottyab.safetynet.sample")) {
|
||||
return Collections.singletonList(ByteString.decodeHex("31936c0e1cfc54024c985c4f3eca37f1946f644eabed5232cd4ab2a646a41bc1"));
|
||||
}
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
}
|
||||
@ -156,7 +157,7 @@ public class Attestation {
|
||||
|
||||
private AttestResponse attest(AttestRequest request, String apiKey) throws IOException {
|
||||
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();
|
||||
connection.setRequestMethod("POST");
|
||||
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
|
||||
*/
|
||||
|
||||
package org.microg.gms.recaptcha
|
||||
package org.microg.gms.safetynet
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
@ -20,9 +20,9 @@ import android.webkit.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.webkit.WebViewClientCompat
|
||||
import com.google.android.gms.R
|
||||
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.net.URLEncoder
|
||||
import java.security.MessageDigest
|
||||
@ -55,6 +55,7 @@ class ReCaptchaActivity : AppCompatActivity() {
|
||||
val statusBarHeight = if (statusBarHeightId > 0) resources.getDimensionPixelSize(statusBarHeightId) else 0
|
||||
return base - statusBarHeight - (density * 20.0).toInt()
|
||||
}
|
||||
private var resultSent: Boolean = false
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled", "AddJavascriptInterface")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -111,12 +112,12 @@ class ReCaptchaActivity : AppCompatActivity() {
|
||||
fun onError(errorCode: Int, finish: Boolean) {
|
||||
Log.d(TAG, "onError($errorCode, $finish)")
|
||||
when (errorCode) {
|
||||
1 -> receiver?.send(ERROR, Bundle().apply { putString("error", "Invalid Input Argument"); putInt("errorCode", ERROR) })
|
||||
2 -> receiver?.send(TIMEOUT, Bundle().apply { putString("error", "Session Timeout"); putInt("errorCode", TIMEOUT) })
|
||||
7 -> receiver?.send(RECAPTCHA_INVALID_SITEKEY, Bundle().apply { putString("error", "Invalid Site Key"); putInt("errorCode", RECAPTCHA_INVALID_SITEKEY) })
|
||||
8 -> receiver?.send(RECAPTCHA_INVALID_KEYTYPE, Bundle().apply { putString("error", "Invalid Type of Site Key"); putInt("errorCode", RECAPTCHA_INVALID_KEYTYPE) })
|
||||
9 -> receiver?.send(RECAPTCHA_INVALID_PACKAGE_NAME, Bundle().apply { putString("error", "Invalid Package Name for App"); putInt("errorCode", RECAPTCHA_INVALID_PACKAGE_NAME) })
|
||||
else -> receiver?.send(ERROR, Bundle().apply { putString("error", "error"); putInt("errorCode", ERROR) })
|
||||
1 -> sendErrorResult(ERROR, "Invalid Input Argument")
|
||||
2 -> sendErrorResult(TIMEOUT, "Session Timeout")
|
||||
7 -> sendErrorResult(RECAPTCHA_INVALID_SITEKEY, "Invalid Site Key")
|
||||
8 -> sendErrorResult(RECAPTCHA_INVALID_KEYTYPE, "Invalid Type of Site Key")
|
||||
9 -> sendErrorResult(RECAPTCHA_INVALID_PACKAGE_NAME, "Invalid Package Name for App")
|
||||
else -> sendErrorResult(ERROR, "error")
|
||||
}
|
||||
if (finish) this@ReCaptchaActivity.finish()
|
||||
}
|
||||
@ -162,7 +163,8 @@ class ReCaptchaActivity : AppCompatActivity() {
|
||||
@JavascriptInterface
|
||||
fun verifyCallback(token: String) {
|
||||
Log.d(TAG, "verifyCallback($token)")
|
||||
receiver?.send(0, Bundle().apply { putString("token", token) })
|
||||
sendResult(0) { putString("token", token) }
|
||||
resultSent = true
|
||||
finish()
|
||||
}
|
||||
}, "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) {
|
||||
webView?.apply {
|
||||
layoutParams.width = min(widthPixels, (width * density).toInt())
|
||||
@ -183,7 +202,12 @@ class ReCaptchaActivity : AppCompatActivity() {
|
||||
|
||||
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 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) {
|
||||
webView?.evaluateJavascript("RecaptchaMFrame.token('${URLEncoder.encode(dg, "UTF-8")}', '$params');", null)
|
||||
} else {
|
||||
@ -194,7 +218,12 @@ class ReCaptchaActivity : AppCompatActivity() {
|
||||
suspend fun open() {
|
||||
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 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())
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.microg.gms.safetynet
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Bundle
|
||||
import android.os.Parcel
|
||||
@ -15,24 +16,24 @@ import android.util.Log
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
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.internal.GetServiceRequest
|
||||
import com.google.android.gms.common.internal.IGmsCallbacks
|
||||
import com.google.android.gms.safetynet.AttestationData
|
||||
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.ISafetyNetService
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.microg.gms.BaseService
|
||||
import org.microg.gms.checkin.LastCheckinInfo
|
||||
import org.microg.gms.common.GmsService
|
||||
import org.microg.gms.common.PackageUtils
|
||||
import org.microg.gms.droidguard.DroidGuardPreferences
|
||||
import org.microg.gms.droidguard.DroidGuardResultCreator
|
||||
import org.microg.gms.recaptcha.ReCaptchaActivity
|
||||
import org.microg.gms.recaptcha.appendUrlEncodedParam
|
||||
import org.microg.gms.droidguard.core.DroidGuardPreferences
|
||||
import org.microg.gms.droidguard.core.DroidGuardResultCreator
|
||||
import org.microg.gms.settings.SettingsContract
|
||||
import org.microg.gms.settings.SettingsContract.CheckIn.getContentUri
|
||||
import org.microg.gms.settings.SettingsContract.getSettings
|
||||
import java.io.IOException
|
||||
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) {
|
||||
if (nonce == null) {
|
||||
callbacks.onAttestationData(Status(CommonStatusCodes.DEVELOPER_ERROR), null)
|
||||
callbacks.onAttestationData(Status(SafetyNetStatusCodes.DEVELOPER_ERROR, "ApiKey missing"), null)
|
||||
return
|
||||
}
|
||||
|
||||
if (!SafetyNetPrefs.get(context).isEnabled) {
|
||||
if (!SafetyNetPreferences.isEnabled(context)) {
|
||||
Log.d(TAG, "ignoring SafetyNet request, SafetyNet is disabled")
|
||||
callbacks.onAttestationData(Status.CANCELED, null)
|
||||
callbacks.onAttestationData(Status(SafetyNetStatusCodes.ERROR, "Disabled"), null)
|
||||
return
|
||||
}
|
||||
|
||||
if (!DroidGuardPreferences.isEnabled(context)) {
|
||||
Log.d(TAG, "ignoring SafetyNet request, DroidGuard is disabled")
|
||||
callbacks.onAttestationData(Status.CANCELED, null)
|
||||
callbacks.onAttestationData(Status(SafetyNetStatusCodes.ERROR, "Disabled"), null)
|
||||
return
|
||||
}
|
||||
|
||||
@ -78,11 +79,15 @@ class SafetyNetClientServiceImpl(private val context: Context, private val packa
|
||||
val data = mapOf("contentBinding" to attestation.payloadHashBase64)
|
||||
val dg = withContext(Dispatchers.IO) { DroidGuardResultCreator.getResult(context, "attest", data) }
|
||||
attestation.setDroidGaurdResult(Base64.encodeToString(dg, Base64.NO_WRAP + Base64.NO_PADDING + Base64.URL_SAFE))
|
||||
val resultData = withContext(Dispatchers.IO) { AttestationData(attestation.attest(apiKey)) }
|
||||
callbacks.onAttestationData(Status.SUCCESS, resultData)
|
||||
val jwsResult = withContext(Dispatchers.IO) { attestation.attest(apiKey) }
|
||||
callbacks.onAttestationData(Status.SUCCESS, AttestationData(jwsResult))
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, e)
|
||||
callbacks.onAttestationData(Status.INTERNAL_ERROR, null)
|
||||
Log.w(TAG, "Exception during attest: ${e.javaClass.name}", e)
|
||||
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?) {
|
||||
if (siteKey == null) {
|
||||
callbacks.onAttestationData(Status(CommonStatusCodes.DEVELOPER_ERROR), null)
|
||||
callbacks.onAttestationData(Status(SafetyNetStatusCodes.RECAPTCHA_INVALID_SITEKEY, "SiteKey missing"), null)
|
||||
return
|
||||
}
|
||||
|
||||
if (!SafetyNetPrefs.get(context).isEnabled) {
|
||||
if (!SafetyNetPreferences.isEnabled(context)) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -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_NEW_TASK)
|
||||
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 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)
|
||||
.appendUrlEncodedParam("di", LastCheckinInfo.read(context).androidId.toString())
|
||||
.appendUrlEncodedParam("di", androidId.toString())
|
||||
.appendUrlEncodedParam("pk", packageName)
|
||||
.appendUrlEncodedParam("sv", SDK_INT.toString())
|
||||
.appendUrlEncodedParam("gv", "20.47.14 (040306-{{cl}})")
|
||||
.appendUrlEncodedParam("gm", "260")
|
||||
.appendUrlEncodedParam("as", Base64.encodeToString(Attestation.getPackageFileDigest(context, packageName), Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING))
|
||||
for (signature in Attestation.getPackageSignatures(context, packageName)) {
|
||||
params.appendUrlEncodedParam("ac", Base64.encodeToString(signature, Base64.URL_SAFE or Base64.NO_WRAP or Base64.NO_PADDING))
|
||||
.appendUrlEncodedParam("as", packageFileDigest)
|
||||
for (signature in packageSignatures) {
|
||||
Log.d(TAG, "Sig: $signature")
|
||||
params.appendUrlEncodedParam("ac", signature)
|
||||
}
|
||||
params.appendUrlEncodedParam("ip", "com.android.vending")
|
||||
.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…
Reference in New Issue
Block a user