| | |
| | | #include "licensedata.h" |
| | | #include <QDataStream> |
| | | #include <QCryptographicHash> |
| | | #include <QRandomGenerator> |
| | | #include <QtDebug> |
| | | |
| | | LicenseData::LicenseData() |
| | |
| | | .arg(activeState); |
| | | } |
| | | |
| | | // ===== V3 紧凑格式(20位许可密钥用)===== |
| | | |
| | | QByteArray LicenseData::machineFingerprint(const QByteArray &uuid, const QByteArray &cpu, const QByteArray &key) |
| | | { |
| | | // 与toBinary相同的定长规整,保证授权端和验证端对同一台机器算出同一指纹 |
| | | QByteArray u = uuid.left(16); |
| | | u.append(QByteArray(16 - u.size(), '\0')); |
| | | QByteArray c = cpu.left(8); |
| | | c.append(QByteArray(8 - c.size(), '\0')); |
| | | |
| | | QCryptographicHash hash(QCryptographicHash::Sha256); |
| | | hash.addData(key); |
| | | hash.addData(u); |
| | | hash.addData(c); |
| | | return hash.result().left(8); |
| | | } |
| | | |
| | | quint16 LicenseData::crc16(const QByteArray &data) |
| | | { |
| | | // CRC16-CCITT (0xFFFF 初值,0x1021 多项式) |
| | | quint16 crc = 0xFFFF; |
| | | for (int i = 0; i < data.size(); ++i) { |
| | | crc ^= static_cast<quint16>(static_cast<unsigned char>(data.at(i))) << 8; |
| | | for (int b = 0; b < 8; ++b) { |
| | | crc = (crc & 0x8000) ? static_cast<quint16>((crc << 1) ^ 0x1021) |
| | | : static_cast<quint16>(crc << 1); |
| | | } |
| | | } |
| | | return crc; |
| | | } |
| | | |
| | | QByteArray LicenseData::toBinaryV3(const QByteArray &key) const |
| | | { |
| | | QByteArray data(16, '\0'); |
| | | |
| | | // [0..7] 机器指纹 |
| | | QByteArray fp = machineFingerprint(boardUuid, cpuSerial, key); |
| | | memcpy(data.data(), fp.constData(), 8); |
| | | |
| | | // [8..9] 失效日期(与V2同一2000-01-01基准) |
| | | quint16 days = dateToDays(expireDate); |
| | | data[8] = static_cast<char>((days >> 8) & 0xFF); |
| | | data[9] = static_cast<char>(days & 0xFF); |
| | | |
| | | // [10] 产品号 |
| | | data[10] = static_cast<char>(productId); |
| | | |
| | | // [11] 版本:高4位主版本 | 低4位次版本 |
| | | data[11] = static_cast<char>(((verMajor & 0x0F) << 4) | (verMinor & 0x0F)); |
| | | |
| | | // [12] 标志:高4位格式版本(1),低4位激活状态 |
| | | data[12] = static_cast<char>(0x10 | (activeState & 0x0F)); |
| | | |
| | | // [13..14] CRC16(覆盖[0..12],随机盐不参与) |
| | | quint16 crc = crc16(data.left(13)); |
| | | data[13] = static_cast<char>((crc >> 8) & 0xFF); |
| | | data[14] = static_cast<char>(crc & 0xFF); |
| | | |
| | | // [15] 随机盐 |
| | | data[15] = static_cast<char>(QRandomGenerator::global()->generate() & 0xFF); |
| | | |
| | | return data; |
| | | } |
| | | |
| | | bool LicenseData::fromBinaryV3(const QByteArray &data) |
| | | { |
| | | if (data.size() != 16) { |
| | | return false; |
| | | } |
| | | |
| | | quint8 flags = static_cast<quint8>(data.at(12)); |
| | | if ((flags >> 4) != 0x01) { |
| | | qWarning() << "LicenseData::fromBinaryV3: not a V3 payload, format version =" |
| | | << (flags >> 4); |
| | | return false; |
| | | } |
| | | |
| | | quint16 expectCrc = static_cast<quint16>((static_cast<quint8>(data.at(13)) << 8) |
| | | | static_cast<quint8>(data.at(14))); |
| | | if (crc16(data.left(13)) != expectCrc) { |
| | | qWarning() << "LicenseData::fromBinaryV3: CRC check failed, wrong key or corrupted data"; |
| | | return false; |
| | | } |
| | | |
| | | machineFp = data.left(8); |
| | | |
| | | quint16 days = static_cast<quint16>((static_cast<quint8>(data.at(8)) << 8) |
| | | | static_cast<quint8>(data.at(9))); |
| | | expireDate = daysToDate(days); |
| | | |
| | | productId = static_cast<quint8>(data.at(10)); |
| | | |
| | | quint8 ver = static_cast<quint8>(data.at(11)); |
| | | verMajor = ver >> 4; |
| | | verMinor = ver & 0x0F; |
| | | |
| | | activeState = flags & 0x0F; |
| | | return true; |
| | | } |
| | | |
| | | QString LicenseData::toDebugStringV3() const |
| | | { |
| | | return QString("{FP:%1, Expire:%2, Prod:%3, Ver:%4.%5, State:%6}") |
| | | .arg(binaryToHexString(machineFp)) |
| | | .arg(expireDate.toString("yyyy-MM-dd")) |
| | | .arg(productId) |
| | | .arg(verMajor).arg(verMinor) |
| | | .arg(activeState); |
| | | } |
| | | |
| | | quint16 LicenseData::dateToDays(const QDate &date) const |
| | | { |
| | | QDate base(BASE_YEAR, BASE_MONTH, BASE_DAY); |