ccy
6 天以前 5e1e4a4b57e298806dba97175fa1fbd5c771c7cd
licensedata.cpp
@@ -1,5 +1,7 @@
#include "licensedata.h"
#include <QDataStream>
#include <QCryptographicHash>
#include <QRandomGenerator>
#include <QtDebug>
LicenseData::LicenseData()
@@ -105,6 +107,116 @@
        .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);