Init: Migrate SimpleRemoter (Since v1.3.1) to Gitea

This commit is contained in:
yuanyuanxiang
2026-04-19 19:55:01 +02:00
commit 5a325a202b
744 changed files with 235562 additions and 0 deletions

276
common/VerifyV2.h Normal file
View File

@@ -0,0 +1,276 @@
#pragma once
// VerifyV2.h - V2 授权验证(仅验证,不含签名功能)
// 用于客户端离线验证 V2 签名
//
// ============================================================================
// 授权校验逻辑说明
// ============================================================================
//
// 授权分为 V1 和 V2 两种:
// - V1: 需要在线连接授权服务器校验
// - V2: 支持离线校验(使用 ECDSA P-256 签名)
//
// V2 离线校验流程:
// 1. 读取配置中的 SN (设备ID)、Password (授权码)、PwdHmac (签名)
// 2. 验证签名:使用内嵌公钥验证 PwdHmac 是否为 "SN|Password" 的有效签名
// 3. 检查有效期:解析 Password 中的结束日期,判断是否过期
//
// Password 格式: "YYYYMMDD-YYYYMMDD-NNNN-XXXXXXXX"
// 开始日期 结束日期 数量 校验码
//
// 授权行为:
// +---------------------------+-------------------------------------------+
// | 情况 | 行为 |
// +---------------------------+-------------------------------------------+
// | V2 + 签名有效 + 未过期 | SetAuthorized() → 离线OK无需连接服务器 |
// | V2 + 签名有效 + 已过期 | 需要连接服务器续期 |
// | V2 + 签名无效 | 需要连接服务器 |
// | V1 | 需要连接服务器 |
// | 试用版 | 保持连接WAN检测+端口限制 |
// | 未授权/连不上服务器 | 循环弹窗警告 |
// +---------------------------+-------------------------------------------+
//
// 服务器授权成功后:
// - 试用版: 保持连接,执行 WAN 检测和端口限制
// - 正式版: AuthKernelManager 退出
//
// ============================================================================
#include <windows.h>
#include <bcrypt.h>
#include <string>
#include <vector>
#pragma comment(lib, "bcrypt.lib")
// 包含公钥
#include "key.h"
// V2 签名长度 (ECDSA P-256: 64 bytes)
#define V2_SIGNATURE_SIZE 64
// V2 公钥长度 (ECDSA P-256: 64 bytes, X+Y coordinates)
#define V2_PUBKEY_SIZE 64
namespace VerifyV2
{
// Base64 解码
inline bool base64Decode(const std::string& encoded, BYTE* dataOut, size_t* lenOut)
{
static const BYTE DECODE_TABLE[256] = {
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
};
size_t inLen = encoded.length();
if (inLen == 0 || inLen % 4 != 0) return false;
size_t outLen = inLen / 4 * 3;
if (encoded[inLen - 1] == '=') outLen--;
if (inLen >= 2 && encoded[inLen - 2] == '=') outLen--;
size_t j = 0;
for (size_t i = 0; i < inLen; i += 4) {
BYTE a = DECODE_TABLE[(BYTE)encoded[i]];
BYTE b = DECODE_TABLE[(BYTE)encoded[i + 1]];
BYTE c = DECODE_TABLE[(BYTE)encoded[i + 2]];
BYTE d = DECODE_TABLE[(BYTE)encoded[i + 3]];
if (a == 255 || b == 255) return false;
dataOut[j++] = (a << 2) | (b >> 4);
if (j < outLen) dataOut[j++] = (b << 4) | (c >> 2);
if (j < outLen) dataOut[j++] = (c << 6) | d;
}
*lenOut = outLen;
return true;
}
// 计算 SHA256 哈希 (使用 BCrypt)
inline bool computeSHA256(const BYTE* data, size_t len, BYTE* hashOut)
{
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_HASH_HANDLE hHash = nullptr;
DWORD hashObjectSize = 0, dataLen = 0;
PBYTE hashObject = nullptr;
bool success = false;
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA256_ALGORITHM, nullptr, 0) != 0)
return false;
if (BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hashObjectSize, sizeof(DWORD), &dataLen, 0) != 0)
goto cleanup;
hashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hashObjectSize);
if (!hashObject) goto cleanup;
if (BCryptCreateHash(hAlg, &hHash, hashObject, hashObjectSize, nullptr, 0, 0) != 0)
goto cleanup;
if (BCryptHashData(hHash, (PUCHAR)data, (ULONG)len, 0) != 0)
goto cleanup;
if (BCryptFinishHash(hHash, hashOut, 32, 0) != 0)
goto cleanup;
success = true;
cleanup:
if (hHash) BCryptDestroyHash(hHash);
if (hashObject) HeapFree(GetProcessHeap(), 0, hashObject);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
return success;
}
// 使用公钥验证 ECDSA P-256 签名
inline bool VerifySignature(const BYTE* publicKey, const BYTE* msg, int len, const BYTE* signature)
{
BCRYPT_ALG_HANDLE hAlg = nullptr;
BCRYPT_KEY_HANDLE hKey = nullptr;
BYTE hash[32];
bool success = false;
// 计算消息的 SHA256 哈希
if (!computeSHA256(msg, len, hash))
return false;
// 构建公钥 blob
std::vector<BYTE> publicBlob(sizeof(BCRYPT_ECCKEY_BLOB) + V2_PUBKEY_SIZE);
BCRYPT_ECCKEY_BLOB* header = (BCRYPT_ECCKEY_BLOB*)publicBlob.data();
header->dwMagic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
header->cbKey = 32;
memcpy(publicBlob.data() + sizeof(BCRYPT_ECCKEY_BLOB), publicKey, V2_PUBKEY_SIZE);
// 打开 ECDSA 算法
if (BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_ECDSA_P256_ALGORITHM, nullptr, 0) != 0)
return false;
// 导入公钥
if (BCryptImportKeyPair(hAlg, nullptr, BCRYPT_ECCPUBLIC_BLOB, &hKey,
publicBlob.data(), (ULONG)publicBlob.size(), 0) != 0)
goto cleanup;
// 验证签名
success = (BCryptVerifySignature(hKey, nullptr, hash, 32,
(PUCHAR)signature, V2_SIGNATURE_SIZE, 0) == 0);
cleanup:
if (hKey) BCryptDestroyKey(hKey);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
return success;
}
// 验证口令签名 (V2)
// deviceId: 设备ID (如 "XXXX-XXXX-XXXX-XXXX")
// password: 口令字符串 (如 "20250301-20261231-0002-XXXXXXXX")
// hmacField: HMAC 字段值 (应以 "v2:" 开头)
// publicKey: 公钥 (64 bytes),默认使用内嵌公钥
// 返回: true 签名验证通过
inline bool verifyPasswordV2(const std::string& deviceId, const std::string& password,
const std::string& hmacField, const BYTE* publicKey = g_LicensePublicKey)
{
// 检查 v2: 前缀
if (hmacField.length() < 3 || hmacField.substr(0, 3) != "v2:") {
return false;
}
// 提取 Base64 签名
std::string sigBase64 = hmacField.substr(3);
// 解码签名
BYTE signature[V2_SIGNATURE_SIZE];
size_t sigLen = 0;
if (!base64Decode(sigBase64, signature, &sigLen) || sigLen != V2_SIGNATURE_SIZE) {
return false;
}
// 构建待验证数据: "deviceId|password"
std::string payload = deviceId + "|" + password;
// 验证签名
return VerifySignature(publicKey, (const BYTE*)payload.c_str(), (int)payload.length(), signature);
}
// 获取设备ID (从配置读取或从硬件ID计算)
// 供外部调用,简化验证流程
inline std::string getDeviceIdFromHardwareId(const std::string& hardwareId)
{
// 与 pwd_gen.cpp 中的 getDeviceID 逻辑一致
// SHA256(hardwareId) -> 取前16字节 -> 格式化为 XXXX-XXXX-XXXX-XXXX
BYTE hash[32];
if (!computeSHA256((const BYTE*)hardwareId.c_str(), hardwareId.length(), hash))
return "";
char deviceId[20];
snprintf(deviceId, sizeof(deviceId), "%02X%02X-%02X%02X-%02X%02X-%02X%02X",
hash[0], hash[1], hash[2], hash[3],
hash[4], hash[5], hash[6], hash[7]);
return deviceId;
}
// 检查 V2 密码是否过期
// password 格式: "20250301-20261231-0002-XXXXXXXX" (startDate-endDate-...)
// 返回: true 如果未过期(在有效期内)
inline bool isPasswordValid(const std::string& password)
{
// 解析结束日期 (位置 9-16, 格式 YYYYMMDD)
if (password.length() < 17 || password[8] != '-') {
return false;
}
std::string endDateStr = password.substr(9, 8);
if (endDateStr.length() != 8) {
return false;
}
// 解析 YYYYMMDD
int endYear = 0, endMonth = 0, endDay = 0;
if (sscanf(endDateStr.c_str(), "%4d%2d%2d", &endYear, &endMonth, &endDay) != 3) {
return false;
}
// 获取当前日期
SYSTEMTIME st;
GetLocalTime(&st);
// 比较日期 (转换为 YYYYMMDD 整数比较)
int endDate = endYear * 10000 + endMonth * 100 + endDay;
int currentDate = st.wYear * 10000 + st.wMonth * 100 + st.wDay;
return currentDate <= endDate;
}
}
// 便捷宏:从配置验证 V2 授权并设置授权状态
// 需要包含 iniFile.h 和 LANChecker.h
// 注意:只有签名有效且未过期才会跳过超时检测,过期则需要连接服务器续期
#define VERIFY_V2_AND_SET_AUTHORIZED() \
do { \
config* _v2cfg = IsDebug ? new config : new iniFile; \
std::string _pwdHmac = _v2cfg->GetStr("settings", "PwdHmac", ""); \
if (_pwdHmac.length() >= 3 && _pwdHmac.substr(0, 3) == "v2:") { \
std::string _deviceId = _v2cfg->GetStr("settings", "SN", ""); \
std::string _password = _v2cfg->GetStr("settings", "Password", ""); \
if (!_deviceId.empty() && !_password.empty() && \
VerifyV2::verifyPasswordV2(_deviceId, _password, _pwdHmac) && \
VerifyV2::isPasswordValid(_password)) { \
AuthTimeoutChecker::SetAuthorized(); \
} \
} \
delete _v2cfg; \
} while(0)