添加版权校验功能,增强应用安全性,确保版本完整性和防篡改能力。

This commit is contained in:
discountry
2025-09-26 01:12:02 +08:00
parent 8923e2d2b8
commit 38616066d5
4 changed files with 93 additions and 1 deletions
+6
View File
@@ -1,3 +1,4 @@
import crypto from "crypto";
import type { TradingConfig } from "../config";
import type { ExchangeAdapter } from "../exchanges/adapter";
import type {
@@ -27,6 +28,7 @@ import type { OrderLockMap, OrderPendingMap, OrderTimerMap } from "./order-coord
import { isUnknownOrderError } from "../utils/errors";
import { roundDownToTick } from "../utils/math";
import { createTradeLog, type TradeLogEntry } from "../state/trade-log";
import { decryptCopyright } from "../utils/copyright";
export interface TrendEngineSnapshot {
ready: boolean;
@@ -91,6 +93,10 @@ export class TrendEngine {
private ordersSnapshotReady = false;
private startupLogged = false;
private entryPricePendingLogged = false;
private readonly copyrightFingerprint = crypto
.createHash("sha256")
.update(decryptCopyright())
.digest("hex");
private readonly listeners = new Map<TrendEngineEvent, Set<TrendEngineListener>>();
+11 -1
View File
@@ -1,8 +1,9 @@
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import { Box, Text, useInput } from "ink";
import { TrendApp } from "./TrendApp";
import { MakerApp } from "./MakerApp";
import { OffsetMakerApp } from "./OffsetMakerApp";
import { loadCopyrightFragments, verifyCopyrightIntegrity } from "../utils/copyright";
interface StrategyOption {
id: "trend" | "maker" | "offset-maker";
@@ -37,6 +38,8 @@ const inputSupported = Boolean(process.stdin && (process.stdin as any).isTTY);
export function App() {
const [cursor, setCursor] = useState(0);
const [selected, setSelected] = useState<StrategyOption | null>(null);
const copyright = useMemo(() => loadCopyrightFragments(), []);
const integrityOk = useMemo(() => verifyCopyrightIntegrity(), []);
useInput(
(input, key) => {
@@ -62,6 +65,13 @@ export function App() {
return (
<Box flexDirection="column" paddingX={1} paddingY={1}>
<Text color="gray">{copyright.bannerText}</Text>
{integrityOk ? null : (
<Text color="red">警告: 版权校验失败</Text>
)}
<Box height={1}>
<Text color="gray"></Text>
</Box>
<Text color="cyanBright"></Text>
<Text color="gray">使 / Ctrl+C 退</Text>
<Box flexDirection="column" marginTop={1}>
+56
View File
@@ -0,0 +1,56 @@
import crypto from "crypto";
const ENCRYPTED_COPYRIGHT = "gbbx3pZWTqgMmLcH3fM0qefo6q4eHR0weNjfLcWY5oPhfwH9DaDEWAvhItfzGbBJzJ6UuVPOb1vqMAL0qpPdw+q0bf13Tz8ewMR3cSHyH8R59KfasAo=";
const PRIMARY_HASH = "a637d95de2b7d13af96b7c0deb286a439b1b07408577ae97c93e6fdbaa6526bf";
const SECONDARY_HASH = "2e9c48cef9b6c3af45313946e3d766b3d8b9a3a12df6906abe90ebeca694fec8";
const LEGACY_SHAS = [
PRIMARY_HASH,
"65d686f80a5360a4f16cf2dbce962f2dd32e88bdee6fa7b8929f8a854ea12de9",
"e3d59bfd872fc54d3c814edb200ef9d4916bea86e3f7de925f2b4d5efbc1ec49",
"27f5777af8faec4ce80bd3cfe6302c7d66be138de25e941544b9b2895207b70e",
];
const PASSPHRASE = "RitMEX#copyright#guard#2025";
const iv = Buffer.from(ENCRYPTED_COPYRIGHT, "base64").subarray(0, 12);
const authTag = Buffer.from(ENCRYPTED_COPYRIGHT, "base64").subarray(12, 28);
const ciphertext = Buffer.from(ENCRYPTED_COPYRIGHT, "base64").subarray(28);
function deriveKey(seed: string) {
return crypto.createHash("sha256").update(seed).digest();
}
export function decryptCopyright(): string {
const key = deriveKey(PASSPHRASE);
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(authTag);
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
}
export function verifyCopyrightIntegrity(): boolean {
const plaintext = decryptCopyright();
const primary = crypto.createHash("sha256").update(plaintext).digest("hex");
const chained = crypto.createHash("sha256").update(primary).digest("hex");
return (
primary === PRIMARY_HASH &&
chained === SECONDARY_HASH &&
LEGACY_SHAS.some((hash) => {
try {
return crypto.timingSafeEqual(Buffer.from(hash, "hex"), Buffer.from(primary, "hex"));
} catch {
return false;
}
})
);
}
export function loadCopyrightFragments() {
const bannerText = decryptCopyright();
return {
bannerText,
primaryHash: PRIMARY_HASH,
secondaryHash: SECONDARY_HASH,
integrityOk: verifyCopyrightIntegrity(),
};
}
+20
View File
@@ -0,0 +1,20 @@
import crypto from "crypto";
const MIRROR_HASHES = [
"a637d95de2b7d13af96b7c0deb286a439b1b07408577ae97c93e6fdbaa6526bf",
"2e9c48cef9b6c3af45313946e3d766b3d8b9a3a12df6906abe90ebeca694fec8",
"57919d55779fbcba1fdd6dfea07e9a6a567c0e9000ab63f25df4e998f3e84b8e",
"27f5777af8faec4ce80bd3cfe6302c7d66be138de25e941544b9b2895207b70e",
];
export function checkIntegrity(payload: string) {
const digest = crypto.createHash("sha256").update(payload, "utf8").digest("hex");
return MIRROR_HASHES.some((hash) => {
try {
return crypto.timingSafeEqual(Buffer.from(hash, "hex"), Buffer.from(digest, "hex"));
} catch {
return false;
}
});
}