Skip to content

Commit f75e5b4

Browse files
committed
签名验证失败时阻止加载配置文件
1 parent c53fdf0 commit f75e5b4

File tree

7 files changed

+75
-16
lines changed

7 files changed

+75
-16
lines changed

MaiChartManager/Controllers/Mod/ConfigurationController.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using AquaMai.Config.HeadlessLoader;
44
using AquaMai.Config.Interfaces;
55
using MaiChartManager.Models;
6+
using MaiChartManager.Utils;
67
using Microsoft.AspNetCore.Mvc;
78
using Mono.Cecil;
89
using YamlDotNet.Serialization;
@@ -19,6 +20,8 @@ public class ConfigCorruptedException() : Exception(Locale.AquaMaiConfigCorrupte
1920

2021
public class AquaMaiNotInstalledException() : Exception(Locale.AquaMaiNotInstalled);
2122

23+
public class AquaMaiSignatureVerificationFailedException() : Exception("AquaMaiSignatureVerificationFailed");
24+
2225
[NonAction]
2326
private static void CheckConfigApiVersion(HeadlessConfigInterface configInterface)
2427
{
@@ -36,14 +39,23 @@ private static void CheckConfigApiVersion(HeadlessConfigInterface configInterfac
3639
}
3740

3841
[NonAction]
39-
public static IConfig GetCurrentAquaMaiConfig(bool forceDefault = false)
42+
public static IConfig GetCurrentAquaMaiConfig(bool forceDefault = false, bool skipSignatureCheck = false)
4043
{
4144
if (!System.IO.File.Exists(ModPaths.AquaMaiDllInstalledPath))
4245
{
4346
throw new AquaMaiNotInstalledException();
4447
}
4548

46-
var configInterface = HeadlessConfigLoader.LoadFromPacked(ModPaths.AquaMaiDllInstalledPath);
49+
var binary = System.IO.File.ReadAllBytes(ModPaths.AquaMaiDllInstalledPath);
50+
if (!skipSignatureCheck)
51+
{
52+
var sigResult = AquaMaiSignatureV2.VerifySignature(binary);
53+
if (sigResult.Status != AquaMaiSignatureV2.VerifyStatus.Valid)
54+
{
55+
throw new AquaMaiSignatureVerificationFailedException();
56+
}
57+
}
58+
var configInterface = HeadlessConfigLoader.LoadFromPacked(binary);
4759
var config = configInterface.CreateConfig();
4860
CheckConfigApiVersion(configInterface);
4961
if (System.IO.File.Exists(ModPaths.AquaMaiConfigPath) && !forceDefault)
@@ -61,6 +73,7 @@ public static IConfig GetCurrentAquaMaiConfig(bool forceDefault = false)
6173

6274
var parser = configInterface.GetConfigParser();
6375
parser.Parse(config, view);
76+
StaticSettings.UpdateAssetPathsFromAquaMaiConfig(config);
6477
}
6578
catch (Exception ex)
6679
{
@@ -80,7 +93,7 @@ public static IConfig GetCurrentAquaMaiConfig(bool forceDefault = false)
8093
}
8194

8295
[HttpGet]
83-
public AquaMaiConfigDto.ConfigDto GetAquaMaiConfig(bool forceDefault = false)
96+
public AquaMaiConfigDto.ConfigDto GetAquaMaiConfig(bool forceDefault = false, bool skipSignatureCheck = false)
8497
{
8598
Dictionary<string, string[]>? configSort = null;
8699
using (var stream = new FileStream(ModPaths.AquaMaiDllInstalledPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
@@ -95,7 +108,7 @@ public AquaMaiConfigDto.ConfigDto GetAquaMaiConfig(bool forceDefault = false)
95108
configSort = deserializer.Deserialize<Dictionary<string, string[]>>(yaml);
96109
}
97110
}
98-
var config = GetCurrentAquaMaiConfig(forceDefault);
111+
var config = GetCurrentAquaMaiConfig(forceDefault, skipSignatureCheck);
99112
return new AquaMaiConfigDto.ConfigDto(
100113
config.ReflectionManager.Sections.Select(section =>
101114
{

MaiChartManager/Front/src/client/apiGen.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,8 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
10021002
query?: {
10031003
/** @default false */
10041004
forceDefault?: boolean;
1005+
/** @default false */
1006+
skipSignatureCheck?: boolean;
10051007
},
10061008
params: RequestParams = {},
10071009
) =>

MaiChartManager/Front/src/components/ModManager/AquaMaiSignatureStatusDisplay.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import { defineComponent, PropType, ref, computed, watch } from 'vue';
22
import { NFlex, NPopover } from 'naive-ui';
33
import { modInfo } from '@/store/refs';
44
import { PubKeyId, VerifyStatus } from '@/client/apiGen';
5+
import { useI18n } from 'vue-i18n';
56

67
export default defineComponent({
78
// props: {
89
// },
910
setup(props, { emit }) {
11+
const { t } = useI18n();
1012

1113
return () => modInfo.value?.signature && <NPopover trigger="hover">
1214
{{
@@ -15,13 +17,13 @@ export default defineComponent({
1517
: <div class="text-red-5 i-tabler:certificate-off text-2em" />,
1618
default: () => <NFlex vertical>
1719
{modInfo.value?.signature?.status === VerifyStatus.Valid && modInfo.value.signature?.keyId === PubKeyId.Local &&
18-
<div>已验证的 AquaMai 官方版本</div>}
20+
<div>{t('mod.signature.verifiedOfficial')}</div>}
1921
{modInfo.value?.signature?.status === VerifyStatus.Valid && modInfo.value.signature?.keyId === PubKeyId.CI &&
20-
<div>已验证的 AquaMai 官方持续集成构建</div>}
22+
<div>{t('mod.signature.verifiedCI')}</div>}
2123
{modInfo.value?.signature?.status === VerifyStatus.NotFound &&
22-
<div>这个 AquaMai 没有有效的签名,很可能不是官方版本</div>}
24+
<div>{t('mod.signature.notFound')}</div>}
2325
{modInfo.value?.signature?.status === VerifyStatus.InvalidSignature &&
24-
<div>这个 AquaMai 的签名无效,很可能不是官方版本</div>}
26+
<div>{t('mod.signature.invalid')}</div>}
2527
</NFlex>
2628
}}
2729
</NPopover>;

MaiChartManager/Front/src/components/ModManager/ConfigEditor.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ export default defineComponent({
2929
const installingMelonLoader = ref(false)
3030
const message = useMessage();
3131
const { t } = useI18n();
32-
const isConfigCorrupted = ref(false);
3332
const errTitle = ref('');
3433

35-
const updateAquaMaiConfig = async (forceDefault = false) => {
34+
const updateAquaMaiConfig = async (forceDefault = false, skipSignatureCheck = false) => {
3635
try {
3736
configReadErr.value = ''
3837
configReadErrTitle.value = ''
39-
config.value = (await api.GetAquaMaiConfig({forceDefault})).data;
40-
isConfigCorrupted.value = false;
38+
config.value = (await api.GetAquaMaiConfig({forceDefault, skipSignatureCheck})).data;
4139
} catch (err: any) {
4240
errTitle.value = t('mod.needInstallOrUpdate');
4341
if (err instanceof Response && !err.bodyUsed) {
@@ -53,9 +51,6 @@ export default defineComponent({
5351
if(configReadErrTitle.value === 'System.Reflection.TargetInvocationException' && compareVersions(modInfo.value?.aquaMaiVersion || '0.0.0', '1.6.0') < 0) {
5452
configReadErr.value = t('mod.versionTooLow');
5553
}
56-
if(configReadErrTitle.value.includes('ConfigCorruptedException')) {
57-
isConfigCorrupted.value = true;
58-
}
5954
if(configReadErr.value.includes('Could not migrate the config')) {
6055
errTitle.value = t('mod.configVersionHigher');
6156
}
@@ -114,11 +109,15 @@ export default defineComponent({
114109
await saveImpl();
115110
}
116111

112+
const loadConfigIgnoreSignature = async () => {
113+
await updateAquaMaiConfig(false, true);
114+
}
115+
117116

118117
return () => {
119118

120119
let editorPart = <></>;
121-
if (isConfigCorrupted.value) {
120+
if (configReadErrTitle.value.includes('ConfigCorruptedException')) {
122121
editorPart = <NFlex vertical justify="center" align="center" class="min-h-100">
123122
<div class="text-8">{t('mod.configCorrupted')}</div>
124123
<div class="c-gray-5 text-lg">{t('mod.configCorruptedMessage')}</div>
@@ -129,6 +128,17 @@ export default defineComponent({
129128
</div>
130129
</NFlex>
131130
}
131+
else if (configReadErrTitle.value.includes('AquaMaiSignatureVerificationFailedException')) {
132+
editorPart = <NFlex vertical justify="center" align="center" class="min-h-100">
133+
<div class="text-8">{t('mod.aquaMaiSignatureVerificationFailed')}</div>
134+
<div class="c-gray-5 text-lg">{t('mod.aquaMaiSignatureVerificationFailedMessage')}</div>
135+
<div>
136+
<NButton secondary onClick={loadConfigIgnoreSignature}>
137+
{t('mod.loadConfigIgnoreSignature')}
138+
</NButton>
139+
</div>
140+
</NFlex>
141+
}
132142
else if (configReadErr.value) {
133143
editorPart = <NFlex vertical justify="center" align="center" class="min-h-100">
134144
<div class="text-8">{errTitle.value}</div>

MaiChartManager/Front/src/locales/en.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,18 @@ mod:
358358
359359
If your cabinet has corresponding host software, it's recommended to use
360360
it for adjustments
361+
configCorrupted: AquaMai config file corrupted
362+
configCorruptedMessage: Please manually check AquaMai.toml and try to fix possible issues
363+
resetToDefault: Reset to default settings
364+
configVersionHigher: Config file version higher than AquaMai version
365+
aquaMaiSignatureVerificationFailed: AquaMai signature verification failed
366+
aquaMaiSignatureVerificationFailedMessage: Unable to verify AquaMai is from official source. Loading config requires executing code in it, which may pose security risks.
367+
loadConfigIgnoreSignature: Ignore signature verification and load
368+
signature:
369+
verifiedOfficial: Verified official AquaMai version
370+
verifiedCI: Verified official AquaMai CI build
371+
notFound: This AquaMai has no valid signature, likely not an official version
372+
invalid: This AquaMai has an invalid signature, likely not an official version
361373
tools:
362374
title: Tools
363375
audioConvert: Audio Convert (ACB + AWB)

MaiChartManager/Front/src/locales/zh-TW.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,18 @@ mod:
323323
使用自訂的觸控靈敏度
324324
這裡啟用之後 Test 裡的設定就不再起作用了,不過還是可以使用 Test 測試(使用測試需要關閉 1P 模式)
325325
如果你的手台有對應的上位機,建議使用上位機調整
326+
configCorrupted: AquaMai 設定檔損壞
327+
configCorruptedMessage: 請手動檢查 AquaMai.toml 並嘗試修復可能的問題
328+
resetToDefault: 重設回預設設定
329+
configVersionHigher: 設定檔版本高於 AquaMai 版本
330+
aquaMaiSignatureVerificationFailed: AquaMai 簽章驗證失敗
331+
aquaMaiSignatureVerificationFailedMessage: 無法驗證 AquaMai 來自官方來源。載入設定需要執行其中的程式碼,這可能存在安全風險。
332+
loadConfigIgnoreSignature: 忽略簽章驗證並載入
333+
signature:
334+
verifiedOfficial: 已驗證的 AquaMai 官方版本
335+
verifiedCI: 已驗證的 AquaMai 官方持續整合建置
336+
notFound: 這個 AquaMai 沒有有效的簽章,很可能不是官方版本
337+
invalid: 這個 AquaMai 的簽章無效,很可能不是官方版本
326338
tools:
327339
title: 工具
328340
audioConvert: 音訊轉換(ACB + AWB)

MaiChartManager/Front/src/locales/zh.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,14 @@ mod:
327327
configCorruptedMessage: 请手动检查 AquaMai.toml 并尝试修复可能的问题
328328
resetToDefault: 重置回默认设置
329329
configVersionHigher: 配置文件版本高于 AquaMai 版本
330+
aquaMaiSignatureVerificationFailed: AquaMai 签名验证失败
331+
aquaMaiSignatureVerificationFailedMessage: 无法验证 AquaMai 来自官方来源。加载配置需要执行其中的代码,这可能存在安全风险。
332+
loadConfigIgnoreSignature: 忽略签名验证并加载
333+
signature:
334+
verifiedOfficial: 已验证的 AquaMai 官方版本
335+
verifiedCI: 已验证的 AquaMai 官方持续集成构建
336+
notFound: 这个 AquaMai 没有有效的签名,很可能不是官方版本
337+
invalid: 这个 AquaMai 的签名无效,很可能不是官方版本
330338
tools:
331339
title: 工具
332340
audioConvert: 音频转换(ACB + AWB)

0 commit comments

Comments
 (0)