diff --git a/src/common/base64.h b/src/common/base64.h index 54e92f2..3a093ad 100644 --- a/src/common/base64.h +++ b/src/common/base64.h @@ -4,4 +4,4 @@ #include #include -std::vector b64_decode(const uint8_t *input, size_t len); +std::vector b64_decode(const uint8_t* input, size_t len); diff --git a/src/qmc2/ekey.cpp b/src/qmc2/ekey.cpp index fbc2857..161de66 100644 --- a/src/qmc2/ekey.cpp +++ b/src/qmc2/ekey.cpp @@ -3,67 +3,65 @@ #include "tc_tea.h" #include -#include #include +#include #include const static std::string kEKeyV2Prefix = "UVFNdXNpYyBFbmNWMixLZXk6"; const static std::array kEKeyV2Key1{ - 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, - 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28, + 0x33, 0x38, 0x36, 0x5A, 0x4A, 0x59, 0x21, 0x40, 0x23, 0x2A, 0x24, 0x25, 0x5E, 0x26, 0x29, 0x28, }; const static std::array kEKeyV2Key2{ - 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, - 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54, + 0x2A, 0x2A, 0x23, 0x21, 0x28, 0x23, 0x24, 0x25, 0x26, 0x5E, 0x61, 0x31, 0x63, 0x5A, 0x2C, 0x54, }; -template std::span ss2span(std::string_view sv) { - auto *data = reinterpret_cast(sv.data()); - return std::span(const_cast(data), sv.size()); +template +std::span ss2span(std::string_view sv) { + auto* data = reinterpret_cast(sv.data()); + return std::span(const_cast(data), sv.size()); } -template std::string_view span2ss(std::span span) { - return std::string_view(reinterpret_cast(span.data()), span.size()); +template +std::string_view span2ss(std::span span) { + return std::string_view(reinterpret_cast(span.data()), span.size()); } -void remove_trailing_zeros(std::vector &vec) { - auto it = std::find_if(vec.rbegin(), vec.rend(), - [](uint8_t value) { return value != 0; }); - vec.erase(it.base(), vec.end()); +void remove_trailing_zeros(std::vector& vec) { + auto it = std::find_if(vec.rbegin(), vec.rend(), [](uint8_t value) { return value != 0; }); + vec.erase(it.base(), vec.end()); } std::vector decrypt_ekey_v1(std::string_view ekey) { - std::vector result = - b64_decode(reinterpret_cast(ekey.data()), ekey.size()); - remove_trailing_zeros(result); + std::vector result = b64_decode(reinterpret_cast(ekey.data()), ekey.size()); + remove_trailing_zeros(result); - uint32_t tea_key[4] = { - 0x69005600 | static_cast(result[0] << 16) | (result[1]), - 0x46003800 | static_cast(result[2] << 16) | (result[3]), - 0x2b002000 | static_cast(result[4] << 16) | (result[5]), - 0x15000b00 | static_cast(result[6] << 16) | (result[7]), - }; - auto decrypted = tc_tea_cbc_decrypt(std::span(result).subspan(8), tea_key); - if (decrypted.empty()) { - return {}; - } - result.resize(8); - result.insert(result.end(), decrypted.begin(), decrypted.end()); - return result; + uint32_t tea_key[4] = { + 0x69005600 | static_cast(result[0] << 16) | (result[1]), + 0x46003800 | static_cast(result[2] << 16) | (result[3]), + 0x2b002000 | static_cast(result[4] << 16) | (result[5]), + 0x15000b00 | static_cast(result[6] << 16) | (result[7]), + }; + auto decrypted = tc_tea_cbc_decrypt(std::span(result).subspan(8), tea_key); + if (decrypted.empty()) { + return {}; + } + result.resize(8); + result.insert(result.end(), decrypted.begin(), decrypted.end()); + return result; } std::vector decrypt_ekey_v2(std::string_view ekey) { - std::vector result; - result = tc_tea_cbc_decrypt(ss2span(ekey), kEKeyV2Key1.data()); - result = tc_tea_cbc_decrypt(std::span(result), kEKeyV2Key2.data()); - return decrypt_ekey_v1(span2ss(std::span(result))); + std::vector result; + result = tc_tea_cbc_decrypt(ss2span(ekey), kEKeyV2Key1.data()); + result = tc_tea_cbc_decrypt(std::span(result), kEKeyV2Key2.data()); + return decrypt_ekey_v1(span2ss(std::span(result))); } std::vector decrypt_ekey(std::string_view ekey) { - if (ekey.starts_with(kEKeyV2Prefix)) { - ekey.remove_prefix(kEKeyV2Prefix.size()); - return decrypt_ekey_v2(ekey); - } + if (ekey.starts_with(kEKeyV2Prefix)) { + ekey.remove_prefix(kEKeyV2Prefix.size()); + return decrypt_ekey_v2(ekey); + } - return decrypt_ekey_v1(ekey); + return decrypt_ekey_v1(ekey); } diff --git a/src/qmc2/qmc2_factory.cpp b/src/qmc2/qmc2_factory.cpp index c4e27ac..dc94e3b 100644 --- a/src/qmc2/qmc2_factory.cpp +++ b/src/qmc2/qmc2_factory.cpp @@ -4,17 +4,17 @@ namespace QMC2 { std::unique_ptr Create(std::string_view ekey) { - auto key = decrypt_ekey(ekey); - auto key_len = key.size(); - if (key_len == 0) { - return nullptr; - } + auto key = decrypt_ekey(ekey); + auto key_len = key.size(); + if (key_len == 0) { + return nullptr; + } - if (key_len < 300) { - return std::make_unique(std::span(key)); - } else { - return std::make_unique(std::span(key)); - } + if (key_len < 300) { + return std::make_unique(std::span(key)); + } else { + return std::make_unique(std::span(key)); + } } -} // namespace QMC2 +} // namespace QMC2 diff --git a/src/tc_tea/tc_tea.cpp b/src/tc_tea/tc_tea.cpp index 1e3748c..5967177 100644 --- a/src/tc_tea/tc_tea.cpp +++ b/src/tc_tea/tc_tea.cpp @@ -10,61 +10,62 @@ constexpr size_t kTeaBlockSize = 8; constexpr size_t kFixedSaltLen = 2; constexpr size_t kZeroPadLen = 7; -inline void decrypt_round(uint8_t *p_plain, const uint8_t *p_cipher, - uint64_t *iv1, uint64_t *iv2, const uint32_t *key) { - uint64_t iv1_next = Endian::be_u64_read(p_cipher); - uint64_t iv2_next = tc_tea_ecb_decrypt(iv1_next ^ *iv2, key); - uint64_t plain = iv2_next ^ *iv1; - *iv1 = iv1_next; - *iv2 = iv2_next; - Endian::be_u64_write(p_plain, plain); +inline void decrypt_round(uint8_t* p_plain, + const uint8_t* p_cipher, + uint64_t* iv1, + uint64_t* iv2, + const uint32_t* key) { + uint64_t iv1_next = Endian::be_u64_read(p_cipher); + uint64_t iv2_next = tc_tea_ecb_decrypt(iv1_next ^ *iv2, key); + uint64_t plain = iv2_next ^ *iv1; + *iv1 = iv1_next; + *iv2 = iv2_next; + Endian::be_u64_write(p_plain, plain); } -std::vector tc_tea_cbc_decrypt(std::span cipher, - const uint32_t *key) { - // It needs to have at least 2 blocks long, due to the nature of the padding - // scheme used. - if (cipher.size() % kTeaBlockSize != 0 || cipher.size() < kTeaBlockSize * 2) { - return {}; - } - - uint64_t iv1 = 0; - uint64_t iv2 = 0; - uint8_t header[kTeaBlockSize * 2]; - const uint8_t *in_cipher = cipher.data(); - decrypt_round(header, in_cipher, &iv1, &iv2, key); - in_cipher += kTeaBlockSize; - decrypt_round(header + kTeaBlockSize, in_cipher, &iv1, &iv2, key); - in_cipher += kTeaBlockSize; - - size_t hdr_skip_len = 1 + (header[0] & 7) + kFixedSaltLen; - size_t real_plain_len = cipher.size() - hdr_skip_len - kZeroPadLen; - std::vector result(real_plain_len); - - auto p_output = result.data(); - - // copy first block of plain text - size_t copy_len = std::min(sizeof(header) - hdr_skip_len, real_plain_len); - std::copy_n(header + hdr_skip_len, real_plain_len, p_output); - p_output += copy_len; - - if (real_plain_len != copy_len) { - // Decrypt the rest of the blocks - for (size_t i = cipher.size() - kTeaBlockSize * 3; i != 0; - i -= kTeaBlockSize) { - decrypt_round(p_output, in_cipher, &iv1, &iv2, key); - in_cipher += kTeaBlockSize; - p_output += kTeaBlockSize; +std::vector tc_tea_cbc_decrypt(std::span cipher, const uint32_t* key) { + // It needs to have at least 2 blocks long, due to the nature of the padding + // scheme used. + if (cipher.size() % kTeaBlockSize != 0 || cipher.size() < kTeaBlockSize * 2) { + return {}; } + uint64_t iv1 = 0; + uint64_t iv2 = 0; + uint8_t header[kTeaBlockSize * 2]; + const uint8_t* in_cipher = cipher.data(); + decrypt_round(header, in_cipher, &iv1, &iv2, key); + in_cipher += kTeaBlockSize; decrypt_round(header + kTeaBlockSize, in_cipher, &iv1, &iv2, key); - p_output[0] = header[kTeaBlockSize]; - } - // Validate zero padding - auto verify = Endian::be_u64_read(header + kTeaBlockSize) << 8; - if (verify != 0) { - result.resize(0); - } + in_cipher += kTeaBlockSize; - return result; + size_t hdr_skip_len = 1 + (header[0] & 7) + kFixedSaltLen; + size_t real_plain_len = cipher.size() - hdr_skip_len - kZeroPadLen; + std::vector result(real_plain_len); + + auto p_output = result.data(); + + // copy first block of plain text + size_t copy_len = std::min(sizeof(header) - hdr_skip_len, real_plain_len); + std::copy_n(header + hdr_skip_len, real_plain_len, p_output); + p_output += copy_len; + + if (real_plain_len != copy_len) { + // Decrypt the rest of the blocks + for (size_t i = cipher.size() - kTeaBlockSize * 3; i != 0; i -= kTeaBlockSize) { + decrypt_round(p_output, in_cipher, &iv1, &iv2, key); + in_cipher += kTeaBlockSize; + p_output += kTeaBlockSize; + } + + decrypt_round(header + kTeaBlockSize, in_cipher, &iv1, &iv2, key); + p_output[0] = header[kTeaBlockSize]; + } + // Validate zero padding + auto verify = Endian::be_u64_read(header + kTeaBlockSize) << 8; + if (verify != 0) { + result.resize(0); + } + + return result; } diff --git a/src/tc_tea/tc_tea.h b/src/tc_tea/tc_tea.h index caee6e7..f02a2ca 100644 --- a/src/tc_tea/tc_tea.h +++ b/src/tc_tea/tc_tea.h @@ -6,14 +6,12 @@ #include #include -std::vector tc_tea_cbc_decrypt(std::span cipher, - const uint32_t *key); +std::vector tc_tea_cbc_decrypt(std::span cipher, const uint32_t* key); -inline std::vector tc_tea_cbc_decrypt(std::span cipher, - const uint8_t *key) { - uint32_t key_u32[4]; - for (int i = 0; i < 4; i++) { - key_u32[i] = Endian::be_u32_read(key + i * 4); - } - return tc_tea_cbc_decrypt(cipher, key_u32); +inline std::vector tc_tea_cbc_decrypt(std::span cipher, const uint8_t* key) { + uint32_t key_u32[4]; + for (int i = 0; i < 4; i++) { + key_u32[i] = Endian::be_u32_read(key + i * 4); + } + return tc_tea_cbc_decrypt(cipher, key_u32); }