Added safetynet implementation

The implementation itself was moved from fragment to self contained event. The result resolution might be moved to the event as well
This commit is contained in:
Viktor De Pasquale
2019-10-23 21:17:53 +02:00
parent 04576ca828
commit 21f2f86cb8
6 changed files with 201 additions and 7 deletions

View File

@@ -22,7 +22,7 @@ val redesignModule = module {
viewModel { LogViewModel() }
viewModel { ModuleViewModel() }
viewModel { RequestViewModel() }
viewModel { SafetynetViewModel() }
viewModel { SafetynetViewModel(get()) }
viewModel { SettingsViewModel() }
viewModel { SuperuserViewModel(get(), get(), get(), get()) }
viewModel { ThemeViewModel() }

View File

@@ -15,3 +15,5 @@ sealed class PolicyUpdateEvent(val item: MagiskPolicy) : RxBus.Event {
}
class ModuleUpdatedEvent(val item: ModuleRvItem) : RxBus.Event
data class SafetyNetResult(val responseCode: Int) : RxBus.Event

View File

@@ -1,6 +1,7 @@
package com.topjohnwu.magisk.model.events
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.karumi.dexter.Dexter
@@ -8,9 +9,25 @@ import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.extensions.DynamicClassLoader
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.extensions.writeTo
import com.topjohnwu.magisk.model.entity.module.Repo
import com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
import com.topjohnwu.magisk.utils.RxBus
import com.topjohnwu.magisk.utils.SafetyNetHelper
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.superuser.Shell
import dalvik.system.DexFile
import io.reactivex.Completable
import io.reactivex.subjects.PublishSubject
import org.koin.core.KoinComponent
import org.koin.core.inject
import java.io.File
import java.lang.reflect.InvocationHandler
/**
* Class for passing events from ViewModels to Activities/Fragments
@@ -34,7 +51,88 @@ class MagiskChangelogEvent : ViewEvent()
class UninstallEvent : ViewEvent()
class EnvFixEvent : ViewEvent()
class UpdateSafetyNetEvent : ViewEvent()
class UpdateSafetyNetEvent : ViewEvent(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback {
private val magiskRepo by inject<MagiskRepository>()
private val rxBus by inject<RxBus>()
private lateinit var EXT_APK: File
private lateinit var EXT_DEX: File
override fun invoke(context: Context) {
val die = ::EXT_APK.isInitialized
EXT_APK = File("${context.filesDir.parent}/snet", "snet.jar")
EXT_DEX = File(EXT_APK.parent, "snet.dex")
Completable.fromAction {
val loader = DynamicClassLoader(EXT_APK)
val dex = DexFile.loadDex(EXT_APK.path, EXT_DEX.path, 0)
// Scan through the dex and find our helper class
var helperClass: Class<*>? = null
for (className in dex.entries()) {
if (className.startsWith("x.")) {
val cls = loader.loadClass(className)
if (InvocationHandler::class.java.isAssignableFrom(cls)) {
helperClass = cls
break
}
}
}
helperClass ?: throw Exception()
val helper = helperClass.getMethod(
"get",
Class::class.java, Context::class.java, Any::class.java
)
.invoke(null, SafetyNetHelper::class.java, context, this) as SafetyNetHelper
if (helper.version < Const.SNET_EXT_VER)
throw Exception()
helper.attest()
}.subscribeK(onError = {
if (die) {
rxBus.post(SafetyNetResult(-1))
} else {
Shell.sh("rm -rf " + EXT_APK.parent).exec()
EXT_APK.parentFile?.mkdir()
download(context, true)
}
})
}
@Suppress("SameParameterValue")
private fun download(context: Context, askUser: Boolean) {
fun downloadInternal() = magiskRepo.fetchSafetynet()
.map { it.byteStream().writeTo(EXT_APK) }
.subscribeK { invoke(context) }
if (!askUser) {
downloadInternal()
return
}
MagiskDialog(context)
.applyTitle(R.string.proprietary_title)
.applyMessage(R.string.proprietary_notice)
.cancellable(false)
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.yes
onClick { downloadInternal() }
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.no_thanks
onClick { rxBus.post(SafetyNetResult(-2)) }
}
.reveal()
}
override fun onResponse(responseCode: Int) {
rxBus.post(SafetyNetResult(responseCode))
}
}
class ViewActionEvent(val action: Activity.() -> Unit) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: AppCompatActivity) = activity.run(action)

View File

@@ -1,5 +1,62 @@
package com.topjohnwu.magisk.redesign.safetynet
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.events.SafetyNetResult
import com.topjohnwu.magisk.model.events.UpdateSafetyNetEvent
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
import com.topjohnwu.magisk.ui.home.SafetyNetState.*
import com.topjohnwu.magisk.utils.KObservableField
import com.topjohnwu.magisk.utils.RxBus
import com.topjohnwu.magisk.utils.SafetyNetHelper
class SafetynetViewModel : CompatViewModel()
class SafetynetViewModel(
rxBus: RxBus
) : CompatViewModel() {
val safetyNetTitle = KObservableField(R.string.empty)
val ctsState = KObservableField(IDLE)
val basicIntegrityState = KObservableField(IDLE)
init {
rxBus.register<SafetyNetResult>()
.subscribeK { resolveResponse(it.responseCode) }
.add()
}
fun attest() = UpdateSafetyNetEvent().publish()
private fun resolveResponse(response: Int) = when {
//todo animate (reveal) to result (green/error)
response and 0x0F == 0 -> {
val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0
val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0
safetyNetTitle.value = R.string.safetyNet_check_success
ctsState.value = if (hasCtsPassed) {
PASS
} else {
FAILED
}
basicIntegrityState.value = if (hasBasicIntegrityPassed) {
PASS
} else {
FAILED
}
}
//todo animate (collapse) back to initial (fade error)
response == -2 -> {
ctsState.value = IDLE
basicIntegrityState.value = IDLE
}
//todo animate (collapse) back to initial (surface)
else -> {
ctsState.value = IDLE
basicIntegrityState.value = IDLE
safetyNetTitle.value = when (response) {
SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid
else -> R.string.safetyNet_api_error
}
}
}
}