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) {
val viewModel = viewModel<ModuleViewModel>()
val context = LocalContext.current
val prefs = context.getSharedPreferences("settings",MODE_PRIVATE)
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
val confirmDialog = rememberConfirmDialog()
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) {
viewModel.initializeCache(context)
@@ -175,13 +182,51 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
)
if (confirmResult == ConfirmResult.Confirmed) {
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}")
// 验证模块签名
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 {
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 {
@@ -205,6 +250,26 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
)
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)))
viewModel.markNeedRefresh()
}
@@ -219,7 +284,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
val backupLauncher = ModuleModify.rememberModuleBackupLauncher(context, snackBarHost)
val restoreLauncher = ModuleModify.rememberModuleRestoreLauncher(context, snackBarHost)
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
LaunchedEffect(Unit) {
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 com.sukisu.ultra.R
import android.util.Log
import com.sukisu.ultra.Natives
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
object ModuleUtils {
@@ -102,4 +105,41 @@ object ModuleUtils {
Log.e(TAG, "Unable to get persistent permissions on URIs: $uri, Error: ${e.message}")
}
}
}
}
// 模块签名验证工具类
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)
}