139 lines
4.8 KiB
C++
139 lines
4.8 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 md5(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();
|
|||
|
|
QString combined = (cpu + disk).left(40).toUpper();
|
|||
|
|
LOG_INFO("cpu: %s, disk: %s \n", qPrintable(cpu), qPrintable(disk));
|
|||
|
|
QString result;
|
|||
|
|
for (int i = 0; i < combined.length(); i += 8) {
|
|||
|
|
if (!result.isEmpty()) result += "-";
|
|||
|
|
result += combined.mid(i, 8);
|
|||
|
|
}
|
|||
|
|
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;
|
|||
|
|
}
|