#include "dbdcrypt.h" #include #include #include #include #include #include #include #include std::string DBDCrypt::decrypt(const std::string& data, const std::string& accessKey, PayloadType* outType) { if (outType) *outType = NONE; if (data.starts_with("DbdDAQEB")) { if (outType && *outType == NONE) *outType = TYPE_1; return decType1(data, accessKey, outType); } if (data.starts_with("DbdDAgAC")) { if (outType && *outType == NONE) *outType = TYPE_2; return decType2(data, accessKey, outType); } if (data.starts_with("DbdDAwAC")) { if (outType && *outType == NONE) *outType = TYPE_3; return decType3(data, accessKey, outType); } Log::warning("Attempted to decrypt non encrypted string"); return data; } std::string DBDCrypt::encrypt(const std::string& data, const std::string& accessKey, PayloadType type, std::string keyId) { if (type == TYPE_1) { auto compressed = zLibCompress(data); // Type 1 is UTF-8 if (compressed.empty()) return ""; uint32_t rawSize = (uint32_t)data.size(); std::vector fullPayload(4); std::memcpy(fullPayload.data(), &rawSize, 4); fullPayload.insert(fullPayload.end(), compressed.begin(), compressed.end()); return "DbdDAQEB" + b64Enc(fullPayload); } if (type == TYPE_2) { auto transformedKey = transformCDNKey(CDN_KEY_BASE64); std::string utf16Data = utf8ToUtf16(data); for (char& c : utf16Data) c = (char)((unsigned char)c - 1); std::vector padded(utf16Data.begin(), utf16Data.end()); int padLen = 16 - (padded.size() % 16); if (padLen < 16) padded.insert(padded.end(), padLen, 0); auto encryptedBody = aesECBEncrypt(padded, transformedKey); if (encryptedBody.empty()) return ""; std::vector fullPayload(encryptedBody.begin(), encryptedBody.end()); return "DbdDAgAC" + b64Enc(fullPayload); } if (type == TYPE_3) { auto decodedKey = b64Dec(accessKey); if (decodedKey.empty()) return ""; std::string utf16Data = utf8ToUtf16(data); for (char& c : utf16Data) c = (char)((unsigned char)c - 1); std::vector padded(utf16Data.begin(), utf16Data.end()); int padLen = 16 - (padded.size() % 16); if (padLen < 16) padded.insert(padded.end(), padLen, 0); auto encryptedBody = aesECBEncrypt(padded, decodedKey); if (encryptedBody.empty()) return ""; std::string shiftedId = shiftKeyID(keyId, -1); std::vector fullData(shiftedId.begin(), shiftedId.end()); fullData.push_back(0); // Null terminator fullData.insert(fullData.end(), (uint8_t*)encryptedBody.data(), (uint8_t*)encryptedBody.data() + encryptedBody.size()); return "DbdDAwAC" + b64Enc(fullData); } return std::string(); } std::string DBDCrypt::decType1(const std::string& data, const std::string& key, PayloadType* outType) { if (data.length() < 8) return data; auto decoded = b64Dec(data.substr(8)); if (decoded.size() < 4) { Log::error("Type 1 base64 too short ({})", decoded.size()); return ""; } std::vector body(decoded.begin() + 4, decoded.end()); std::string decompressed = zlibDecompress(body); if (decompressed.length() >= 2 && decompressed[1] == '\0') decompressed = utf16ToUtf8(decompressed); if (decompressed.starts_with("DbdD")) return decrypt(decompressed, key, outType); return decompressed; } std::string DBDCrypt::decType2(const std::string& data, const std::string& key, PayloadType* outType) { if (data.length() < 8) return data; auto decoded = b64Dec(data.substr(8)); if (decoded.empty()) return ""; std::vector body = decoded; auto transformedKey = transformCDNKey(CDN_KEY_BASE64); std::string decrypted = aesECBDecrypt(body, transformedKey); if (decrypted.empty()) return ""; for (char& c : decrypted) c = (char)((unsigned char)c + 1); if (decrypted.length() >= 2 && decrypted[1] == '\0') decrypted = utf16ToUtf8(decrypted); decrypted.erase(std::remove(decrypted.begin(), decrypted.end(), (char)0x01), decrypted.end()); decrypted.erase(std::remove(decrypted.begin(), decrypted.end(), (char)0x00), decrypted.end()); for (size_t offset : {0ULL, 4ULL}) { if (offset + 1 < decrypted.size() && (unsigned char)decrypted[offset] == 0x78) { std::vector zlibPart((uint8_t*)decrypted.data() + offset, (uint8_t*)decrypted.data() + decrypted.size()); std::string decompressed = zlibDecompress(zlibPart); if (!decompressed.empty()) return decompressed; } } if (decrypted.starts_with("DbdD")) return decrypt(decrypted, key, outType); return decrypted; } std::string DBDCrypt::decType3(const std::string& data, const std::string& key, PayloadType* outType) { if (data.length() < 8) return data; auto rawKey = b64Dec(key); auto decoded = b64Dec(data.substr(8)); if (decoded.empty()) return ""; auto it = std::find(decoded.begin(), decoded.end(), 0); if (it == decoded.end()) return ""; std::vector body(it + 1, decoded.end()); std::string decrypted = aesECBDecrypt(body, rawKey); if (decrypted.empty()) { Log::error("AES decryption failed (body size: {})", body.size()); return ""; } for (char& c : decrypted) c = (char)((unsigned char)c + 1); if (decrypted.length() >= 2 && decrypted.at(1) == '\0') decrypted = utf16ToUtf8(decrypted); decrypted.erase(std::remove(decrypted.begin(), decrypted.end(), (char)0x01), decrypted.end()); decrypted.erase(std::remove(decrypted.begin(), decrypted.end(), (char)0x00), decrypted.end()); for (size_t offset : {0ULL, 4ULL}) { if (offset + 1 < decrypted.size() && (unsigned char)decrypted[offset] == 0x78) { //Log::verbose("nested zlib at offset {}", offset); std::vector zlibPart((uint8_t*)decrypted.data() + offset, (uint8_t*)decrypted.data() + decrypted.size()); std::string decompressed = zlibDecompress(zlibPart); if (!decompressed.empty()) { //Log::verbose("nested zlib decompressed, size: {}", decompressed.length()); return decompressed; } } } if (decrypted.starts_with("DbdD")) return decrypt(decrypted, key, outType); return decrypted; } std::string DBDCrypt::aesECBDecrypt(const std::vector& cipherText, const std::vector& key) { if (key.size() < 32) return ""; EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key.data(), NULL); EVP_CIPHER_CTX_set_padding(ctx, 0); std::string plaintext; plaintext.resize(cipherText.size()); int len = 0; if (EVP_DecryptUpdate(ctx, (unsigned char*)plaintext.data(), &len, cipherText.data(), (int)cipherText.size()) != 1) { EVP_CIPHER_CTX_free(ctx); return ""; } int outLen = len; int finalLen = 0; if (EVP_DecryptFinal_ex(ctx, (unsigned char*)plaintext.data() + len, &finalLen) != 1) { // ignore? } outLen += finalLen; EVP_CIPHER_CTX_free(ctx); plaintext.resize(outLen); return plaintext; } std::string DBDCrypt::aesECBEncrypt(const std::vector& plainText, const std::vector& key) { if (key.size() < 32) return ""; EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key.data(), NULL); EVP_CIPHER_CTX_set_padding(ctx, 0); std::string ciphertext; ciphertext.resize(plainText.size() + 16); int len = 0; if (EVP_EncryptUpdate(ctx, (unsigned char*)ciphertext.data(), &len, plainText.data(), (int)plainText.size()) != 1) { EVP_CIPHER_CTX_free(ctx); return ""; } int outLen = len; int finalLen = 0; if (EVP_EncryptFinal_ex(ctx, (unsigned char*)ciphertext.data() + outLen, &finalLen) == 1) { outLen += finalLen; } EVP_CIPHER_CTX_free(ctx); ciphertext.resize(outLen); return ciphertext; } std::vector DBDCrypt::b64Dec(const std::string& input) { std::string in = input; std::replace(in.begin(), in.end(), '-', '+'); std::replace(in.begin(), in.end(), '_', '/'); BIO *bio, *b64; int decodeLen = (int)in.length(); std::vector buffer(decodeLen); bio = BIO_new_mem_buf(in.data(), decodeLen); b64 = BIO_new(BIO_f_base64()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); int len = BIO_read(bio, buffer.data(), decodeLen); BIO_free_all(bio); if (len < 0) return {}; buffer.resize(len); return buffer; } std::string DBDCrypt::b64Enc(const std::vector& input) { if (input.empty()) return ""; BIO *bio, *b64; BUF_MEM* bufferPtr; b64 = BIO_new(BIO_f_base64()); bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); BIO_write(bio, input.data(), (int)input.size()); BIO_flush(bio); BIO_get_mem_ptr(bio, &bufferPtr); std::string result(bufferPtr->data, bufferPtr->length); BIO_free_all(bio); return result; } std::string DBDCrypt::zlibDecompress(const std::vector& compressed) { if (compressed.empty()) return ""; z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = (uInt)compressed.size(); strm.next_in = (Bytef*)compressed.data(); if (inflateInit(&strm) != Z_OK) return ""; std::string result; char buffer[32768]; do { strm.avail_out = sizeof(buffer); strm.next_out = (Bytef*)buffer; int ret = inflate(&strm, Z_NO_FLUSH); if (ret != Z_OK && ret != Z_STREAM_END) { inflateEnd(&strm); return ""; } result.append(buffer, sizeof(buffer) - strm.avail_out); } while (strm.avail_out == 0); inflateEnd(&strm); return result; } std::vector DBDCrypt::zLibCompress(const std::string& data) { if (data.empty()) return {}; z_stream strm; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return {}; strm.avail_in = (uInt)data.size(); strm.next_in = (Bytef*)data.data(); std::vector result; uint8_t buffer[32768]; do { strm.avail_out = sizeof(buffer); strm.next_out = (Bytef*)buffer; deflate(&strm, Z_FINISH); result.insert(result.end(), buffer, buffer + (sizeof(buffer) - strm.avail_out)); } while (strm.avail_out == 0); deflateEnd(&strm); return result; } std::vector DBDCrypt::transformCDNKey(const std::string& b64CDNKey) { auto encryptedKey = b64Dec(b64CDNKey); std::vector uuidKey(32, 0); std::memcpy(uuidKey.data(), CDN_UUID, std::min((size_t)32, strlen(CDN_UUID))); std::string decrypted = aesECBDecrypt(encryptedKey, uuidKey); std::vector finalKey(decrypted.begin(), decrypted.end()); if (finalKey.size() > 32) finalKey.resize(32); else if (finalKey.size() < 32) finalKey.resize(32, 0); return finalKey; } std::string DBDCrypt::shiftKeyID(const std::string& id, int shift) { std::string res = id; for (char& c : res) c = (char)((unsigned char)c + shift); return res; } std::string DBDCrypt::utf16ToUtf8(const std::string& utf16) { if (utf16.empty()) return ""; std::string utf8; for (size_t i = 0; i < utf16.length(); i += 2) { uint16_t cp = *(uint16_t*)(utf16.data() + i); if (cp == 0) break; if (cp < 0x80) utf8 += (char)cp; else if (cp < 0x800) { utf8 += (char)(0xC0 | (cp >> 6)); utf8 += (char)(0x80 | (cp & 0x3F)); } else { utf8 += (char)(0xE0 | (cp >> 12)); utf8 += (char)(0x80 | ((cp >> 6) & 0x3F)); utf8 += (char)(0x80 | (cp & 0x3F)); } } return utf8; } std::string DBDCrypt::utf8ToUtf16(const std::string& utf8) { if (utf8.empty()) return ""; std::string utf16; for (size_t i = 0; i < utf8.length();) { uint32_t cp = 0; unsigned char c = utf8[i]; if (c < 0x80) { cp = c; i += 1; } else if (c < 0xE0) { cp = ((c & 0x1F) << 6) | (utf8[i + 1] & 0x3F); i += 2; } else if (c < 0xF0) { cp = ((c & 0x0F) << 12) | ((utf8[i + 1] & 0x3F) << 6) | (utf8[i + 2] & 0x3F); i += 3; } else { cp = ((c & 0x07) << 18) | ((utf8[i + 1] & 0x3F) << 12) | ((utf8[i + 2] & 0x3F) << 6) | (utf8[i + 3] & 0x3F); i += 4; } if (cp < 0x10000) { utf16.push_back((char)(cp & 0xFF)); utf16.push_back((char)(cp >> 8)); } else { cp -= 0x10000; uint16_t high = (uint16_t)(0xD800 | (cp >> 10)); uint16_t low = (uint16_t)(0xDC00 | (cp & 0x3FF)); utf16.push_back((char)(high & 0xFF)); utf16.push_back((char)(high >> 8)); utf16.push_back((char)(low & 0xFF)); utf16.push_back((char)(low >> 8)); } } return utf16; }