ccy
6 天以前 5e1e4a4b57e298806dba97175fa1fbd5c771c7cd
shortcrypto.cpp
@@ -1,10 +1,16 @@
#include "shortcrypto.h"
#include "tinyaes.h"
#include <QtDebug>
#include <QtMath>
#include <algorithm>
// Base85 字符集:85个可打印字符,避开容易在URL/JSON里出问题的字符
const char *ShortCrypto::BASE85_CHARS =
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
// V3 字符集:纯大写字母(26字符),许可密钥只含 A-Z,28位
const char *ShortCrypto::V3_CHARS =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
namespace {
    QByteArray pkcs7Pad(const QByteArray &data, int blockSize) {
@@ -105,6 +111,129 @@
    return pkcs7Unpad(decrypted);
}
QString ShortCrypto::encryptRaw(const QByteArray &plaintext, const QByteArray &key)
{
    if (key.size() != 16) {
        qWarning() << "ShortCrypto::encryptRaw: key must be 16 bytes for AES-128";
        return QString();
    }
    if (plaintext.isEmpty() || plaintext.size() % 16 != 0) {
        qWarning() << "ShortCrypto::encryptRaw: plaintext size must be a non-zero multiple of 16, got"
                   << plaintext.size();
        return QString();
    }
    // 单块时全零IV的CBC等价于ECB;多块时为CBC链接
    QByteArray adjustedIv(16, '\0');
    QByteArray encrypted = plaintext;
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, reinterpret_cast<const uint8_t*>(key.constData()),
                    reinterpret_cast<const uint8_t*>(adjustedIv.constData()));
    AES_CBC_encrypt_buffer(&ctx, reinterpret_cast<uint8_t*>(encrypted.data()), encrypted.size());
    // 纯大写字母编码:16字节密文 → 28字符(若允许数字换36字符集,长度降到25)
    QByteArray encoded = baseNEncode(encrypted, V3_CHARS, 26);
    return QString::fromLatin1(encoded);
}
QByteArray ShortCrypto::decryptRaw(const QString &ciphertext, const QByteArray &key)
{
    if (key.size() != 16) {
        qWarning() << "ShortCrypto::decryptRaw: key must be 16 bytes for AES-128";
        return QByteArray();
    }
    // 优先按纯字母表解码;若含表外字符(早期Base85版V3密钥)则回退Base85
    QByteArray encrypted = baseNDecode(ciphertext, V3_CHARS, 26);
    if (encrypted.isEmpty()) {
        encrypted = base85Decode(ciphertext);
    }
    if (encrypted.isEmpty()) {
        qWarning() << "ShortCrypto::decryptRaw: decode failed";
        return QByteArray();
    }
    if (encrypted.size() % 16 != 0) {
        qWarning() << "ShortCrypto::decryptRaw: decoded data size not aligned to block size:"
                   << encrypted.size();
        return QByteArray();
    }
    QByteArray adjustedIv(16, '\0');
    QByteArray decrypted = encrypted;
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, reinterpret_cast<const uint8_t*>(key.constData()),
                    reinterpret_cast<const uint8_t*>(adjustedIv.constData()));
    AES_CBC_decrypt_buffer(&ctx, reinterpret_cast<uint8_t*>(decrypted.data()), decrypted.size());
    return decrypted;
}
QByteArray ShortCrypto::baseNEncode(const QByteArray &data, const char *alphabet, int base)
{
    // 把整段大端字节序当作一个大整数,逐次除以base取余数
    QByteArray num = data;
    QByteArray remainders;
    bool isZero = false;
    while (!isZero) {
        isZero = true;
        unsigned int carry = 0;
        for (int i = 0; i < num.size(); ++i) {
            unsigned int cur = (carry << 8) | static_cast<unsigned char>(num.at(i));
            num[i] = static_cast<char>(cur / static_cast<unsigned int>(base));
            carry = cur % static_cast<unsigned int>(base);
            if (num.at(i) != 0) isZero = false;
        }
        if (isZero && remainders.isEmpty()) break; // 全零输入
        remainders.append(alphabet[static_cast<int>(carry)]);
        if (isZero) break;
    }
    if (remainders.isEmpty()) remainders.append(alphabet[0]);
    // 还原为高位在前,并用alphabet[0]补齐固定位数(前导零)
    QByteArray result = remainders;
    std::reverse(result.begin(), result.end());
    int minimalLen = static_cast<int>(qCeil(data.size() * 8.0 * qLn(2.0) / qLn(static_cast<double>(base))));
    if (result.size() < minimalLen) {
        result.prepend(QByteArray(minimalLen - result.size(), alphabet[0]));
    }
    return result;
}
QByteArray ShortCrypto::baseNDecode(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(大端字节序)
    QByteArray num(16, '\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::baseNDecode: invalid character:" << QChar(input.at(i));
            return QByteArray();
        }
        unsigned int carry = static_cast<unsigned int>(v);
        for (int b = num.size() - 1; b >= 0; --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;
        }
        if (carry != 0) {
            qWarning() << "ShortCrypto::baseNDecode: value overflows 128 bits";
            return QByteArray();
        }
    }
    // 保持16字节定宽(前导零有意义),调用方按固定块长处理
    return num;
}
QByteArray ShortCrypto::base85Encode(const QByteArray &data)
{
    QByteArray result;