| | |
| | | const char *ShortCrypto::V3_CHARS = |
| | | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| | | |
| | | // V2 字符集:大小写字母(52字符),设备ID只含字母、不含数字与特殊字符 |
| | | const char *ShortCrypto::BASE52_CHARS = |
| | | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
| | | |
| | | namespace { |
| | | QByteArray pkcs7Pad(const QByteArray &data, int blockSize) { |
| | | int padLen = blockSize - (data.size() % blockSize); |
| | |
| | | reinterpret_cast<const uint8_t*>(adjustedIv.constData())); |
| | | AES_CBC_encrypt_buffer(&ctx, reinterpret_cast<uint8_t*>(encrypted.data()), encrypted.size()); |
| | | |
| | | // 3. Base85 编码,缩短字符数 |
| | | QByteArray encoded = base85Encode(encrypted); |
| | | // 3. 纯字母编码(52字符集):48字节密文 → 68字符 |
| | | QByteArray encoded = baseNEncode(encrypted, BASE52_CHARS, 52); |
| | | return QString::fromLatin1(encoded); |
| | | } |
| | | |
| | |
| | | adjustedIv.append(QByteArray(16 - adjustedIv.size(), '\0')); |
| | | } |
| | | |
| | | // 1. Base85 解码 |
| | | QByteArray encrypted = base85Decode(ciphertext); |
| | | // 1. 优先按纯字母表(52字符集)解码;52^字符数 与 256字节界 的头空间不足1字节, |
| | | // 真值至多丢失1个前导零字节,长度不对齐时补回 |
| | | QByteArray encrypted = baseNDecodeVar(ciphertext, BASE52_CHARS, 52); |
| | | if (!encrypted.isEmpty() && encrypted.size() % 16 != 0) { |
| | | encrypted.prepend('\0'); |
| | | } |
| | | if (encrypted.isEmpty() || encrypted.size() % 16 != 0) { |
| | | // 回退:早期Base85版密文 |
| | | encrypted = base85Decode(ciphertext); |
| | | } |
| | | if (encrypted.isEmpty()) { |
| | | qWarning() << "ShortCrypto::decrypt: base85 decode failed"; |
| | | qWarning() << "ShortCrypto::decrypt: decode failed"; |
| | | return QByteArray(); |
| | | } |
| | | |
| | |
| | | return num; |
| | | } |
| | | |
| | | QByteArray ShortCrypto::baseNDecodeVar(const QString &text, const char *alphabet, int base) |
| | | { |
| | | // 建立字符索引表 |
| | | int charToVal[256]; |
| | | memset(charToVal, -1, sizeof(charToVal)); |
| | | for (int i = 0; i < base; ++i) { |
| | | charToVal[static_cast<unsigned char>(alphabet[i])] = i; |
| | | } |
| | | |
| | | // 小端动态字节数组执行 Horner:num = num * base + v |
| | | // num[0] 为最低字节,进位向尾部(高位)传播 |
| | | QByteArray num(1, '\0'); |
| | | QByteArray input = text.toLatin1(); |
| | | for (int i = 0; i < input.size(); ++i) { |
| | | int v = charToVal[static_cast<unsigned char>(input.at(i))]; |
| | | if (v < 0) { |
| | | qWarning() << "ShortCrypto::baseNDecodeVar: invalid character:" << QChar(input.at(i)); |
| | | return QByteArray(); |
| | | } |
| | | unsigned int carry = static_cast<unsigned int>(v); |
| | | for (int b = 0; b < num.size(); ++b) { |
| | | unsigned int cur = (static_cast<unsigned int>(static_cast<unsigned char>(num.at(b))) * static_cast<unsigned int>(base)) + carry; |
| | | num[b] = static_cast<char>(cur & 0xFF); |
| | | carry = cur >> 8; |
| | | } |
| | | while (carry != 0) { |
| | | num.append(static_cast<char>(carry & 0xFF)); |
| | | carry >>= 8; |
| | | } |
| | | } |
| | | |
| | | // 转大端并去掉前导零(变长模式下前导零的真实个数由调用方补齐规则恢复) |
| | | std::reverse(num.begin(), num.end()); |
| | | int firstNonZero = 0; |
| | | while (firstNonZero < num.size() - 1 && num.at(firstNonZero) == 0) ++firstNonZero; |
| | | return num.mid(firstNonZero); |
| | | } |
| | | |
| | | QByteArray ShortCrypto::base85Encode(const QByteArray &data) |
| | | { |
| | | QByteArray result; |