manager: Continue to improve module signatures
This commit is contained in:
@@ -110,11 +110,18 @@ data class ModuleBottomSheetMenuItem(
|
|||||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||||
val viewModel = viewModel<ModuleViewModel>()
|
val viewModel = viewModel<ModuleViewModel>()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val prefs = context.getSharedPreferences("settings",MODE_PRIVATE)
|
||||||
val snackBarHost = LocalSnackbarHost.current
|
val snackBarHost = LocalSnackbarHost.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val confirmDialog = rememberConfirmDialog()
|
val confirmDialog = rememberConfirmDialog()
|
||||||
var lastClickTime by remember { mutableStateOf(0L) }
|
var lastClickTime by remember { mutableStateOf(0L) }
|
||||||
|
|
||||||
|
// 签名验证弹窗状态
|
||||||
|
var showSignatureDialog by remember { mutableStateOf(false) }
|
||||||
|
var signatureDialogMessage by remember { mutableStateOf("") }
|
||||||
|
var isForceVerificationFailed by remember { mutableStateOf(false) }
|
||||||
|
var pendingInstallAction by remember { mutableStateOf<(() -> Unit)?>(null) }
|
||||||
|
|
||||||
// 初始化缓存系统
|
// 初始化缓存系统
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.initializeCache(context)
|
viewModel.initializeCache(context)
|
||||||
@@ -175,13 +182,51 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (confirmResult == ConfirmResult.Confirmed) {
|
if (confirmResult == ConfirmResult.Confirmed) {
|
||||||
try {
|
// 验证模块签名
|
||||||
// 批量安装模块
|
val forceVerification = prefs.getBoolean("force_signature_verification", false)
|
||||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
|
val verificationResults = mutableMapOf<Uri, Boolean>()
|
||||||
viewModel.markNeedRefresh()
|
|
||||||
} catch (e: Exception) {
|
for (uri in selectedModules) {
|
||||||
Log.e("ModuleScreen", "Error navigating to FlashScreen: ${e.message}")
|
val isVerified = verifyModuleSignature(context, uri)
|
||||||
snackBarHost.showSnackbar("Error while installing module: ${e.message}")
|
verificationResults[uri] = isVerified
|
||||||
|
|
||||||
|
if (forceVerification && !isVerified) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
signatureDialogMessage = context.getString(R.string.module_signature_invalid_message)
|
||||||
|
isForceVerificationFailed = true
|
||||||
|
showSignatureDialog = true
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
} else if (!isVerified) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
signatureDialogMessage = context.getString(R.string.module_signature_verification_failed)
|
||||||
|
isForceVerificationFailed = false
|
||||||
|
pendingInstallAction = {
|
||||||
|
try {
|
||||||
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
|
||||||
|
viewModel.markNeedRefresh()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("ModuleScreen", "Error navigating to FlashScreen: ${e.message}")
|
||||||
|
scope.launch {
|
||||||
|
snackBarHost.showSnackbar("Error while installing module: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showSignatureDialog = true
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有模块签名验证通过,直接安装
|
||||||
|
if (verificationResults.all { it.value }) {
|
||||||
|
try {
|
||||||
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
|
||||||
|
viewModel.markNeedRefresh()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("ModuleScreen", "Error navigating to FlashScreen: ${e.message}")
|
||||||
|
snackBarHost.showSnackbar("Error while installing module: ${e.message}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -205,6 +250,26 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (confirmResult == ConfirmResult.Confirmed) {
|
if (confirmResult == ConfirmResult.Confirmed) {
|
||||||
|
// 验证模块签名
|
||||||
|
val forceVerification = prefs.getBoolean("force_signature_verification", false)
|
||||||
|
val isVerified = verifyModuleSignature(context, uri)
|
||||||
|
|
||||||
|
if (forceVerification && !isVerified) {
|
||||||
|
signatureDialogMessage = context.getString(R.string.module_signature_invalid_message)
|
||||||
|
isForceVerificationFailed = true
|
||||||
|
showSignatureDialog = true
|
||||||
|
return@launch
|
||||||
|
} else if (!isVerified) {
|
||||||
|
signatureDialogMessage = context.getString(R.string.module_signature_verification_failed)
|
||||||
|
isForceVerificationFailed = false
|
||||||
|
pendingInstallAction = {
|
||||||
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
||||||
|
viewModel.markNeedRefresh()
|
||||||
|
}
|
||||||
|
showSignatureDialog = true
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
||||||
viewModel.markNeedRefresh()
|
viewModel.markNeedRefresh()
|
||||||
}
|
}
|
||||||
@@ -219,7 +284,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
val backupLauncher = ModuleModify.rememberModuleBackupLauncher(context, snackBarHost)
|
val backupLauncher = ModuleModify.rememberModuleBackupLauncher(context, snackBarHost)
|
||||||
val restoreLauncher = ModuleModify.rememberModuleRestoreLauncher(context, snackBarHost)
|
val restoreLauncher = ModuleModify.rememberModuleRestoreLauncher(context, snackBarHost)
|
||||||
|
|
||||||
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
||||||
@@ -450,6 +514,64 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 签名验证弹窗
|
||||||
|
if (showSignatureDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showSignatureDialog = false },
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Warning,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.module_signature_invalid),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = signatureDialogMessage)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
if (isForceVerificationFailed) {
|
||||||
|
// 强制验证失败,只显示确定按钮
|
||||||
|
TextButton(
|
||||||
|
onClick = { showSignatureDialog = false }
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.confirm))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 非强制验证失败,显示继续安装按钮
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
showSignatureDialog = false
|
||||||
|
pendingInstallAction?.invoke()
|
||||||
|
pendingInstallAction = null
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.install))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = if (!isForceVerificationFailed) {
|
||||||
|
{
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
showSignatureDialog = false
|
||||||
|
pendingInstallAction = null
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,20 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// 强制签名验证开关
|
||||||
|
var forceSignatureVerification by rememberSaveable {
|
||||||
|
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
||||||
|
}
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Filled.Security,
|
||||||
|
title = stringResource(R.string.module_signature_verification),
|
||||||
|
summary = stringResource(R.string.module_signature_verification_summary),
|
||||||
|
checked = forceSignatureVerification,
|
||||||
|
onCheckedChange = { enabled ->
|
||||||
|
prefs.edit { putBoolean("force_signature_verification", enabled) }
|
||||||
|
forceSignatureVerification = enabled
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import java.nio.charset.StandardCharsets
|
|||||||
import java.util.zip.ZipInputStream
|
import java.util.zip.ZipInputStream
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.sukisu.ultra.Natives
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
object ModuleUtils {
|
object ModuleUtils {
|
||||||
@@ -103,3 +106,40 @@ object ModuleUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 模块签名验证工具类
|
||||||
|
object ModuleSignatureUtils {
|
||||||
|
private const val TAG = "ModuleSignatureUtils"
|
||||||
|
|
||||||
|
fun verifyModuleSignature(context: Context, moduleUri: Uri): Boolean {
|
||||||
|
return try {
|
||||||
|
// 创建临时文件
|
||||||
|
val tempFile = File(context.cacheDir, "temp_module_${System.currentTimeMillis()}.zip")
|
||||||
|
|
||||||
|
// 复制URI内容到临时文件
|
||||||
|
context.contentResolver.openInputStream(moduleUri)?.use { inputStream ->
|
||||||
|
FileOutputStream(tempFile).use { outputStream ->
|
||||||
|
inputStream.copyTo(outputStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用native方法验证签名
|
||||||
|
val isVerified = Natives.verifyModuleSignature(tempFile.absolutePath)
|
||||||
|
|
||||||
|
// 清理临时文件
|
||||||
|
tempFile.delete()
|
||||||
|
|
||||||
|
Log.d(TAG, "Module signature verification result: $isVerified")
|
||||||
|
isVerified
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error verifying module signature", e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证模块签名
|
||||||
|
fun verifyModuleSignature(context: Context, moduleUri: Uri): Boolean {
|
||||||
|
return ModuleSignatureUtils.verifyModuleSignature(context, moduleUri)
|
||||||
|
}
|
||||||
|
|||||||
@@ -606,4 +606,9 @@
|
|||||||
<string name="sus_loop_path_feature_label">SUS循环路径</string>
|
<string name="sus_loop_path_feature_label">SUS循环路径</string>
|
||||||
<string name="sus_loop_paths_description_title">循环路径配置</string>
|
<string name="sus_loop_paths_description_title">循环路径配置</string>
|
||||||
<string name="sus_loop_paths_description_text">循环路径会在每次非root用户应用或隔离服务启动时重新标记为SUS_PATH。这有助于解决添加的路径可能因inode状态重置或内核中inode重新创建而失效的问题</string>
|
<string name="sus_loop_paths_description_text">循环路径会在每次非root用户应用或隔离服务启动时重新标记为SUS_PATH。这有助于解决添加的路径可能因inode状态重置或内核中inode重新创建而失效的问题</string>
|
||||||
|
<string name="module_signature_verification">模块签名验证</string>
|
||||||
|
<string name="module_signature_verification_summary">强制验证模块安装时的签名</string>
|
||||||
|
<string name="module_signature_invalid">模块签名验证未通过</string>
|
||||||
|
<string name="module_signature_invalid_message">由于安全设置,安装将被阻止</string>
|
||||||
|
<string name="module_signature_verification_failed">安装将继续,请注意模块是否存在未知加密脚本</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -609,4 +609,9 @@
|
|||||||
<string name="sus_loop_path_feature_label">SUS Loop Path</string>
|
<string name="sus_loop_path_feature_label">SUS Loop Path</string>
|
||||||
<string name="sus_loop_paths_description_title">Loop Path Configuration</string>
|
<string name="sus_loop_paths_description_title">Loop Path Configuration</string>
|
||||||
<string name="sus_loop_paths_description_text">Loop paths are re-flagged as SUS_PATH on each non-root user app or isolated service startup. This helps address issues where added paths may have their inode status reset or inode re-created in the kernel.</string>
|
<string name="sus_loop_paths_description_text">Loop paths are re-flagged as SUS_PATH on each non-root user app or isolated service startup. This helps address issues where added paths may have their inode status reset or inode re-created in the kernel.</string>
|
||||||
|
<string name="module_signature_verification">Module signature verification</string>
|
||||||
|
<string name="module_signature_verification_summary">Force signature verification for module installation</string>
|
||||||
|
<string name="module_signature_invalid">Module signature verification failed</string>
|
||||||
|
<string name="module_signature_invalid_message">Installation will be blocked due to security settings</string>
|
||||||
|
<string name="module_signature_verification_failed">Installation will continue, please note the presence of unknown encryption scripts in the module</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user