Commit 9e3afcfe authored by Viktor De Pasquale's avatar Viktor De Pasquale

Added safetynet implementation

parent 21f2f86c
package com.topjohnwu.magisk.redesign.safetynet package com.topjohnwu.magisk.redesign.safetynet
import android.os.Build
import android.view.View
import android.view.ViewAnimationUtils
import androidx.core.animation.doOnEnd
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentSafetynetMd2Binding import com.topjohnwu.magisk.databinding.FragmentSafetynetMd2Binding
import com.topjohnwu.magisk.redesign.compat.CompatFragment import com.topjohnwu.magisk.redesign.compat.CompatFragment
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.math.hypot
import kotlin.math.roundToInt
class SafetynetFragment : CompatFragment<SafetynetViewModel, FragmentSafetynetMd2Binding>() { class SafetynetFragment : CompatFragment<SafetynetViewModel, FragmentSafetynetMd2Binding>() {
...@@ -15,4 +25,34 @@ class SafetynetFragment : CompatFragment<SafetynetViewModel, FragmentSafetynetMd ...@@ -15,4 +25,34 @@ class SafetynetFragment : CompatFragment<SafetynetViewModel, FragmentSafetynetMd
activity.setTitle(R.string.safetyNet) activity.setTitle(R.string.safetyNet)
} }
}
@BindingAdapter("revealSafetyNet")
fun View.revealOnCenter(expand: Boolean) {
val anim = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
isInvisible = expand
return
} else {
val x = measuredWidth.toDouble()
val y = measuredHeight.toDouble()
val maxRadius = hypot(x, y).toFloat()
val start = if (expand) 0f else maxRadius
val end = if (expand) maxRadius else 0f
ViewAnimationUtils.createCircularReveal(
this,
(x / 2).roundToInt(),
(y / 2).roundToInt(),
start,
end
).apply {
interpolator = FastOutSlowInInterpolator()
doOnEnd { if (!expand) isVisible = false }
}
}
post {
isVisible = true
anim.start()
}
} }
\ No newline at end of file
package com.topjohnwu.magisk.redesign.safetynet package com.topjohnwu.magisk.redesign.safetynet
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.extensions.subscribeK
import com.topjohnwu.magisk.model.events.SafetyNetResult import com.topjohnwu.magisk.model.events.SafetyNetResult
...@@ -14,44 +16,63 @@ class SafetynetViewModel( ...@@ -14,44 +16,63 @@ class SafetynetViewModel(
rxBus: RxBus rxBus: RxBus
) : CompatViewModel() { ) : CompatViewModel() {
private var currentState = IDLE
set(value) {
field = value
notifyStateChanged()
}
val safetyNetTitle = KObservableField(R.string.empty) val safetyNetTitle = KObservableField(R.string.empty)
val ctsState = KObservableField(IDLE) val ctsState = KObservableField(false)
val basicIntegrityState = KObservableField(IDLE) val basicIntegrityState = KObservableField(false)
val isChecking @Bindable get() = currentState == LOADING
val isFailed @Bindable get() = currentState == FAILED
val isSuccess @Bindable get() = currentState == PASS
init { init {
rxBus.register<SafetyNetResult>() rxBus.register<SafetyNetResult>()
.subscribeK { resolveResponse(it.responseCode) } .subscribeK { resolveResponse(it.responseCode) }
.add() .add()
attest()
} }
fun attest() = UpdateSafetyNetEvent().publish() override fun notifyStateChanged() {
super.notifyStateChanged()
notifyPropertyChanged(BR.loading)
notifyPropertyChanged(BR.failed)
notifyPropertyChanged(BR.success)
}
private fun attest() {
currentState = LOADING
UpdateSafetyNetEvent().publish()
}
fun reset() = attest()
private fun resolveResponse(response: Int) = when { private fun resolveResponse(response: Int) = when {
//todo animate (reveal) to result (green/error)
response and 0x0F == 0 -> { response and 0x0F == 0 -> {
val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0 val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0
val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0 val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0
safetyNetTitle.value = R.string.safetyNet_check_success val result = hasCtsPassed && hasBasicIntegrityPassed
ctsState.value = if (hasCtsPassed) { ctsState.value = hasCtsPassed
PASS basicIntegrityState.value = hasBasicIntegrityPassed
} else { currentState = if (result) PASS else FAILED
FAILED safetyNetTitle.value =
} if (result) R.string.safetynet_attest_success
basicIntegrityState.value = if (hasBasicIntegrityPassed) { else R.string.safetynet_attest_failure
PASS
} else {
FAILED
}
} }
//todo animate (collapse) back to initial (fade error)
response == -2 -> { response == -2 -> {
ctsState.value = IDLE currentState = FAILED
basicIntegrityState.value = IDLE ctsState.value = false
basicIntegrityState.value = false
back()
} }
//todo animate (collapse) back to initial (surface)
else -> { else -> {
ctsState.value = IDLE currentState = FAILED
basicIntegrityState.value = IDLE ctsState.value = false
basicIntegrityState.value = false
safetyNetTitle.value = when (response) { safetyNetTitle.value = when (response) {
SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid
else -> R.string.safetyNet_api_error else -> R.string.safetyNet_api_error
......
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="4dp" />
<corners android:radius="2dp" />
<solid android:color="?colorOnPrimary" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?colorOnSurface"
android:pathData="M12 21C8.25 20 5 15.54 5 11.22V6.3L12 3.18L19 6.3V12.07A6.45 6.45 0 0 1 20.91 12.67A11.63 11.63 0 0 0 21 11V5L12 1L3 5V11C3 16.55 6.84 21.74 12 23C12.35 22.91 12.7 22.8 13 22.68A6.3 6.3 0 0 1 12 21M18 14.5V13L15.75 15.25L18 17.5V16A2.5 2.5 0 0 1 20.24 19.62L21.33 20.71A4 4 0 0 0 18 14.5M18 21A2.5 2.5 0 0 1 15.76 17.38L14.67 16.29A4 4 0 0 0 18 22.5V24L20.25 21.75L18 19.5Z" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<import type="com.topjohnwu.magisk.Info" />
<import type="com.topjohnwu.magisk.BuildConfig" />
<variable <variable
name="viewModel" name="viewModel"
type="com.topjohnwu.magisk.redesign.safetynet.SafetynetViewModel" /> type="com.topjohnwu.magisk.redesign.safetynet.SafetynetViewModel" />
</data> </data>
<FrameLayout
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"> android:clipToPadding="false"
android:fillViewport="true"
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
android:paddingBottom="@{viewModel.insets.bottom}"
tools:paddingBottom="48dp"
tools:paddingTop="24dp">
<androidx.constraintlayout.widget.ConstraintLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.button.MaterialButton <androidx.appcompat.widget.AppCompatTextView
style="?styleButtonText" invisibleUnless="@{viewModel.checking}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="@{() -> viewModel.attest()}" android:layout_gravity="center"
android:text="Attest" android:text="@string/safetynet_attest_loading"
android:textAllCaps="false" android:textAppearance="?appearanceTextTitleNormal" />
app:icon="@drawable/ic_test"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorPrimary">
<androidx.appcompat.widget.AppCompatTextView <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content" invisibleUnless="@{viewModel.success || viewModel.failed}"
android:layout_height="wrap_content" /> android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/l1"
android:paddingBottom="@dimen/l1">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/safetynet_icon"
style="?styleImageBig"
app:layout_constraintBottom_toTopOf="@+id/safetynet_magisk_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias=".3"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="@drawable/ic_magisk"
app:tint="?colorOnSurface" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/safetynet_magisk_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/l1"
android:text="@string/magisk"
android:textAppearance="?appearanceTextCaptionVariant"
app:layout_constraintBottom_toTopOf="@+id/safetynet_manager_title"
app:layout_constraintEnd_toStartOf="@+id/safetynet_magisk_version"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/safetynet_magisk_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l_50"
android:text="@{Info.magiskVersionString}"
android:textAppearance="?appearanceTextCaptionNormal"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/safetynet_magisk_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@+id/safetynet_magisk_title"
app:layout_constraintTop_toTopOf="@+id/safetynet_magisk_title"
tools:text="20.0" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/safetynet_manager_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manager"
android:textAppearance="?appearanceTextCaptionVariant"
app:layout_constraintBottom_toTopOf="@+id/safetynet_attestation"
app:layout_constraintEnd_toStartOf="@+id/safetynet_manager_version"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_magisk_title" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/safetynet_manager_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l_50"
android:text="@{BuildConfig.VERSION_NAME}"
android:textAppearance="?appearanceTextCaptionNormal"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/safetynet_manager_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@+id/safetynet_manager_title"
app:layout_constraintTop_toTopOf="@+id/safetynet_manager_title"
tools:text="10.0.0" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/safetynet_attestation"
style="?styleCardElevated"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l2"
android:layout_marginEnd="@dimen/l1"
app:cardCornerRadius="@dimen/l1"
app:cardElevation="@dimen/l1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_manager_title"
app:layout_constraintWidth_max="300dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
goneUnless="@{viewModel.failed}"
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha=".5"
android:background="?colorError"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
goneUnless="@{viewModel.success}"
android:layout_width="match_parent"
android:layout_height="0dp"
android:alpha=".5"
android:background="?colorSecondary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/safetynet_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/l1"
android:layout_marginTop="@dimen/l2"
android:layout_marginEnd="@dimen/l1"
android:gravity="center"
android:text="@{viewModel.safetyNetTitle}"
android:textAppearance="?appearanceTextDisplayOnPrimary"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/safetynet_attest_success" />
<View
android:id="@+id/safetynet_divider"
android:layout_width="50dp"
android:layout_height="4dp"
android:layout_marginTop="@dimen/l2"
android:background="@drawable/bg_divider_rounded_on_primary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_title" />
<LinearLayout
android:id="@+id/safetynet_cts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/l1"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_divider">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="start"
android:text="ctsProfile"
android:textAppearance="?appearanceTextBodyOnPrimary"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<androidx.appcompat.widget.AppCompatImageView
style="?styleIconOnPrimary"
isSelected="@{viewModel.ctsState}"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_check_circle_md2" />
</LinearLayout>
<LinearLayout
android:id="@+id/safetynet_basicintegrity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/l1"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/safetynet_cts">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="start"
android:text="basicIntegrity"
android:textAppearance="?appearanceTextBodyOnPrimary"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<androidx.appcompat.widget.AppCompatImageView
style="?styleIconOnPrimary"
isSelected="@{viewModel.basicIntegrityState}"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_check_circle_md2" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton
style="?styleButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/l1"
android:layout_marginBottom="@dimen/l1"
android:onClick="@{() -> viewModel.reset()}"
android:text="@string/safetynet_attest_restart"
android:textAllCaps="false"
android:textColor="?colorOnSurfaceVariant"
app:icon="@drawable/ic_refresh_safetynet_md2"
app:iconTint="?colorOnSurfaceVariant"
app:layout_constraintEnd_toEndOf="@+id/safetynet_attestation"
app:layout_constraintTop_toBottomOf="@+id/safetynet_attestation" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout> </androidx.core.widget.NestedScrollView>
</layout> </layout>
\ No newline at end of file
...@@ -29,6 +29,11 @@ ...@@ -29,6 +29,11 @@
<!--ref. Magisk Hide--> <!--ref. Magisk Hide-->
<string name="magisk_hide_md2">Hide</string> <string name="magisk_hide_md2">Hide</string>
<string name="safetynet_attest_success">Success!</string>
<string name="safetynet_attest_failure">Attestation failed!</string>
<string name="safetynet_attest_loading">Just a sec…</string>
<string name="safetynet_attest_restart">Try again</string>
<string name="settings_dark_mode_title">Theme Mode</string> <string name="settings_dark_mode_title">Theme Mode</string>
<string name="settings_dark_mode_message">Select mode which best suits your style!</string> <string name="settings_dark_mode_message">Select mode which best suits your style!</string>
<string name="settings_dark_mode_light">Always Light</string> <string name="settings_dark_mode_light">Always Light</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment