156 lines
5.3 KiB
C++
156 lines
5.3 KiB
C++
#include "AuthManager.h"
|
||
#include "AuthConfig.h"
|
||
#include "VrLog.h"
|
||
#include <QCryptographicHash>
|
||
#include <QString>
|
||
#include <QFile>
|
||
#include <QDir>
|
||
#include <QDateTime>
|
||
#include <QProcess>
|
||
|
||
static QString md5(const QString& s) {
|
||
return QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Md5).toHex();
|
||
}
|
||
|
||
static QString encrypt(const QString& text, const QString& key) {
|
||
QByteArray t = text.toUtf8(), k = key.toUtf8(), r;
|
||
for (int i = 0; i < t.size(); ++i) r.append(t[i] ^ k[i % k.size()]);
|
||
return r.toBase64();
|
||
}
|
||
|
||
static QString decrypt(const QString& text, const QString& key) {
|
||
QByteArray t = QByteArray::fromBase64(text.toUtf8()), k = key.toUtf8(), r;
|
||
for (int i = 0; i < t.size(); ++i) r.append(t[i] ^ k[i % k.size()]);
|
||
return QString::fromUtf8(r);
|
||
}
|
||
|
||
static QString exec(const QString& cmd) {
|
||
QProcess p;
|
||
#ifdef _WIN32
|
||
p.start("cmd", {"/c", cmd});
|
||
#else
|
||
p.start("sh", {"-c", cmd});
|
||
#endif
|
||
p.waitForFinished();
|
||
return QString(p.readAllStandardOutput()).remove('\r').remove('\n').remove(' ');
|
||
}
|
||
|
||
static QString cpuSerial() {
|
||
#ifdef _WIN32
|
||
QString r = exec(WMIC_CMD_CPU_SERIAL);
|
||
r.remove("ProcessorId");
|
||
#else
|
||
QString r = exec("cat /proc/cpuinfo | grep Serial | awk '{print $3}'");
|
||
#endif
|
||
return r;
|
||
}
|
||
|
||
static QString diskSerial() {
|
||
#ifdef _WIN32
|
||
QString r = exec(WMIC_CMD_DISK_SERIAL);
|
||
r.remove("SerialNumber");
|
||
#else
|
||
// ARM: 优先读取 eMMC/SD 卡 CID,其次读取 SATA 硬盘序列号
|
||
QString r = exec("cat /sys/block/mmcblk0/device/cid 2>/dev/null || "
|
||
"cat /sys/block/sda/device/serial 2>/dev/null || "
|
||
"cat /sys/class/dmi/id/product_serial 2>/dev/null || echo 'unknown'");
|
||
#endif
|
||
return r.remove("0000").remove('_').remove('.');
|
||
}
|
||
|
||
static QString getFilePath() {
|
||
return QDir::homePath() + "/" + AUTH_FILE_PATH;
|
||
}
|
||
|
||
std::string AuthManager::GetMachineCode() {
|
||
QString cpu = cpuSerial(), disk = diskSerial();
|
||
LOG_INFO("cpu: %s, disk: %s\n", qPrintable(cpu), qPrintable(disk));
|
||
|
||
// 使用 CPU + DISK 的 MD5 哈希(32位)
|
||
QString hash = md5(cpu + disk);
|
||
|
||
// 前16位和后16位异或,生成16位机器码
|
||
QString front = hash.left(16);
|
||
QString back = hash.right(16);
|
||
QString xorResult;
|
||
for (int i = 0; i < 16; ++i) {
|
||
int f = front[i].toLatin1();
|
||
int b = back[i].toLatin1();
|
||
int x = f ^ b;
|
||
// 将异或结果映射到 0-F 范围
|
||
xorResult += QString::number(x % 16, 16);
|
||
}
|
||
xorResult = xorResult.toUpper();
|
||
|
||
// 格式化为 xxxx-xxxx-xxxx-xxxx
|
||
QString result;
|
||
for (int i = 0; i < 16; i += 4) {
|
||
if (!result.isEmpty()) result += "-";
|
||
result += xorResult.mid(i, 4);
|
||
}
|
||
return result.toStdString();
|
||
}
|
||
|
||
std::string AuthManager::GenerateLicenseKey(const std::string& machineCode, const std::string& expireDate) {
|
||
QString mc = QString::fromStdString(machineCode), ed = QString::fromStdString(expireDate);
|
||
QString key = md5(mc + "|" + ed);
|
||
QString hash = md5(AUTH_KEY_SEED + key + ed).toUpper();
|
||
return encrypt(ed + "|" + hash, AUTH_KEY_SEED).toStdString();
|
||
}
|
||
|
||
bool AuthManager::SaveLicenseInfo(const std::string& licenseKey, const std::string& /*expireDate*/) {
|
||
#ifndef _WIN32
|
||
QDir().mkpath(QDir::homePath() + "/.config");
|
||
#endif
|
||
QString filePath = getFilePath();
|
||
QFile f(filePath);
|
||
if (!f.open(QIODevice::WriteOnly)) {
|
||
LOG_WARN("Failed to open file %s for writing", qPrintable(filePath));
|
||
return false;
|
||
}
|
||
f.write(QString::fromStdString(licenseKey).toUtf8());
|
||
return true;
|
||
}
|
||
|
||
bool AuthManager::LoadLicenseInfo(std::string& licenseKey, std::string& expireDate) {
|
||
QString filePath = getFilePath();
|
||
QFile f(filePath);
|
||
if (!f.open(QIODevice::ReadOnly)) {
|
||
LOG_WARN("Failed to open file %s for reading\n", qPrintable(filePath));
|
||
return false;
|
||
}
|
||
licenseKey = QString(f.readAll()).trimmed().toStdString();
|
||
// 从授权码中解析过期日期
|
||
QString decoded = decrypt(QString::fromStdString(licenseKey), AUTH_KEY_SEED);
|
||
int pos = decoded.indexOf('|');
|
||
if (pos == -1) return false;
|
||
expireDate = decoded.left(pos).toStdString();
|
||
return true;
|
||
}
|
||
|
||
bool AuthManager::CheckLicenseValid() {
|
||
std::string lk, ed;
|
||
if (!LoadLicenseInfo(lk, ed)) return false;
|
||
return ValidateLicenseKey(lk, ed);
|
||
}
|
||
|
||
bool AuthManager::ValidateLicenseKey(const std::string& licenseKey, std::string& expireDate) {
|
||
QString decoded = decrypt(QString::fromStdString(licenseKey), AUTH_KEY_SEED);
|
||
int pos = decoded.indexOf('|');
|
||
if (pos == -1) return false;
|
||
|
||
QString expDate = decoded.left(pos);
|
||
// 验证过期日期格式
|
||
if (expDate != PERMANENT_LICENSE_DATE && expDate.length() != 8) return false;
|
||
|
||
// 验证授权码是否匹配当前机器
|
||
std::string expectedKey = GenerateLicenseKey(GetMachineCode(), expDate.toStdString());
|
||
if (QString::fromStdString(expectedKey).toUpper() != QString::fromStdString(licenseKey).toUpper()) return false;
|
||
|
||
// 验证是否过期
|
||
if (expDate != PERMANENT_LICENSE_DATE && QDate::currentDate().toString("yyyyMMdd") > expDate) return false;
|
||
|
||
expireDate = expDate.toStdString();
|
||
return true;
|
||
}
|