manager: Continue to improve module signatures

This commit is contained in:
ShirkNeko
2025-08-03 05:39:35 +08:00
parent 48d7a13028
commit d225f0bae9
5 changed files with 195 additions and 9 deletions

View File

@@ -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,8 +182,45 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
) )
if (confirmResult == ConfirmResult.Confirmed) { if (confirmResult == ConfirmResult.Confirmed) {
// 验证模块签名
val forceVerification = prefs.getBoolean("force_signature_verification", false)
val verificationResults = mutableMapOf<Uri, Boolean>()
for (uri in selectedModules) {
val isVerified = verifyModuleSignature(context, uri)
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 { try {
// 批量安装模块
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules))) navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
viewModel.markNeedRefresh() viewModel.markNeedRefresh()
} catch (e: Exception) { } catch (e: Exception) {
@@ -184,6 +228,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
snackBarHost.showSnackbar("Error while installing module: ${e.message}") snackBarHost.showSnackbar("Error while installing module: ${e.message}")
} }
} }
}
} else { } else {
val uri = data.data ?: return@launch val uri = data.data ?: return@launch
// 单个安装模块 // 单个安装模块
@@ -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
}
)
}
} }
} }

View File

@@ -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
}
)
} }
) )
} }

View File

@@ -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)
}

View File

@@ -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>

View File

@@ -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>