Feat: sign TOKEN_AUTH response and add TOKEN_SERVER_VERIFY to prevent fake server

TOKEN_AUTH: when the server has a V2 private key, signs "SN|valid(0/1)"
with ECDSA P-256 and places "sig:<base64>" in the response reserved field.
Clients can verify server identity without changing the request format.

TOKEN_SERVER_VERIFY (251): added constant to commands.h; handler already
present in 2015RemoteDlg.cpp for the challenge-response server identity check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
yuanyuanxiang
2026-06-18 21:42:28 +02:00
parent 103123f533
commit 851fed4739
2 changed files with 64 additions and 2 deletions

View File

@@ -5223,6 +5223,48 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
PostMessageA(WM_SHOWERRORMSG, (WPARAM)new CString(_L(msg->text)), (LPARAM)new CString(_L(msg->title)));
break;
}
case TOKEN_SERVER_VERIFY: {
// 客户端发送SN|时间戳
// 服务端查询授权列表验证此SN存在且有效用私钥对授权状态进行签名返回: SN|时间戳|Status|Signature
// 客户端用公钥验证消息
if (len <= 1 || m_v2KeyPath.empty())
break;
// 解析 payload: szBuffer[1..] 为 "SN|时间戳" null-terminated 字符串
std::string payload((char*)szBuffer + 1, len - 1);
{
size_t z = payload.find('\0');
if (z != std::string::npos) payload.resize(z);
}
size_t sep = payload.find('|');
if (sep == std::string::npos || sep == 0 || sep + 1 >= payload.size())
break;
std::string sn = payload.substr(0, sep);
std::string ts = payload.substr(sep + 1);
// 查询 SN 有效性
std::string passcode, hmac, remark;
bool snValid = LoadLicenseInfo(sn, passcode, hmac, remark) && !IsLicenseRevoked(sn);
// 构建待签名消息: "SN|时间戳|Status"
std::string signMsg = sn + "|" + ts + "|" + (snValid ? "1" : "0");
// 用私钥签名
BYTE signature[V2_SIGNATURE_SIZE];
if (!SignMessageV2(m_v2KeyPath.c_str(), (const BYTE*)signMsg.c_str(), (int)signMsg.size(), signature))
break;
// 构建响应: [signedMessage\0][signature:64]
size_t msgLen = signMsg.size() + 1;
std::vector<BYTE> resp(msgLen + V2_SIGNATURE_SIZE);
memcpy(resp.data(), signMsg.c_str(), msgLen);
memcpy(resp.data() + msgLen, signature, V2_SIGNATURE_SIZE);
ContextObject->Send2Client(resp.data(), (ULONG)resp.size());
break;
}
case TOKEN_AUTH: {
BOOL valid = FALSE;
std::string version;
@@ -5349,8 +5391,27 @@ VOID CMy2015RemoteDlg::MessageHandle(CONTEXT_OBJECT* ContextObject)
offset += 1; // 空的 frpConfig
}
// Reserved 字段(预留,当前为空)
offset += 1; // 空的 reserved
// Reserved 字段:签名 "sig:<base64(64字节ECDSA签名)>" 防假服务器
// 签名消息: "SN|valid(0/1)",客户端可用已知信息重建并验签
if (!m_v2KeyPath.empty() && len > 20) {
std::string snForSig(szBuffer + 1, szBuffer + 20);
while (!snForSig.empty() && snForSig.back() == '\0') snForSig.pop_back();
std::string signMsg = snForSig + "|" + (valid ? "1" : "0");
BYTE sig[V2_SIGNATURE_SIZE];
if (SignMessageV2(m_v2KeyPath.c_str(), (const BYTE*)signMsg.c_str(), (int)signMsg.size(), sig)) {
std::string reservedStr = "sig:" + base64Encode(sig, V2_SIGNATURE_SIZE);
if (offset + reservedStr.size() + 1 < sizeof(resp)) {
memcpy(resp + offset, reservedStr.c_str(), reservedStr.size() + 1);
offset += reservedStr.size() + 1;
} else {
offset += 1;
}
} else {
offset += 1;
}
} else {
offset += 1;
}
ContextObject->Send2Client((PBYTE)resp, sizeof(resp));
break;