/* * MPQCryptography.c * MPQKit * * Created by Jean-Francois Roy on Sat Oct 05 2002. * Copyright (c) 2002-2007 MacStorm. All rights reserved. * */ /* * Ripped for mpqlib - all endinaness stuff has been killed. * Other than that, this is the exact same code. */ #include #include #include #ifdef USE_SSL #include #include #include #include #include #endif #include "MPQCryptography.h" #ifdef WIN32 #include "stdint.h" #else #include "inttypes.h" #endif static int crypt_table_initialized = 0; static uint32_t crypt_table[0x500]; /****TODO****/ /* Re-implement various endianess fixes. */ #ifdef USE_SSL static void memrev(unsigned char *buf, size_t count) { unsigned char *r; for (r = buf + count - 1; buf < r; buf++, r--) { *buf ^= *r; *r ^= *buf; *buf ^= *r; } } #endif const uint32_t *__mpqlib_get_cryptography_table() { assert(crypt_table_initialized); return crypt_table; } void __mpqlib_init_cryptography() { // Prepare crypt_table uint32_t seed = 0x00100001; uint32_t index1 = 0; uint32_t index2 = 0; int32_t i; if (!crypt_table_initialized) { crypt_table_initialized = 1; for (index1 = 0; index1 < 0x100; index1++) { for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100) { uint32_t temp1, temp2; seed = (seed * 125 + 3) % 0x2AAAAB; temp1 = (seed & 0xFFFF) << 0x10; seed = (seed * 125 + 3) % 0x2AAAAB; temp2 = (seed & 0xFFFF); crypt_table[index2] = (temp1 | temp2); } } } // Load up OpenSSL #ifdef USE_SSL OpenSSL_add_all_digests(); OpenSSL_add_all_algorithms(); OpenSSL_add_all_ciphers(); ERR_load_crypto_strings(); #endif } void __mpqlib_encrypt(void *_data, uint32_t length, uint32_t key, bool disable_input_swapping) { char * data = (char *) _data; uint32_t *buffer32 = (uint32_t *) data; uint32_t seed = 0xEEEEEEEE; uint32_t ch; assert(crypt_table_initialized); assert(data); // Round to 4 bytes length = length / 4; // We duplicate the loop to avoid costly branches if (disable_input_swapping) { while (length-- > 0) { seed += crypt_table[0x400 + (key & 0xFF)]; ch = *buffer32 ^ (key + seed); key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); seed = *buffer32 + seed + (seed << 5) + 3; *buffer32++ = ch; } } else { while (length-- > 0) { seed += crypt_table[0x400 + (key & 0xFF)]; ch = *buffer32 ^ (key + seed); key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); seed = *buffer32 + seed + (seed << 5) + 3; *buffer32++ = ch; } } } void __mpqlib_decrypt(void *_data, uint32_t length, uint32_t key, bool disable_output_swapping) { char * data = (char *) _data; uint32_t *buffer32 = (uint32_t *) data; uint32_t seed = 0xEEEEEEEE; uint32_t ch; assert(crypt_table_initialized); assert(data); // Round to 4 bytes length = length / 4; if (disable_output_swapping) { while (length-- > 0) { ch = *buffer32; seed += crypt_table[0x400 + (key & 0xFF)]; ch = ch ^ (key + seed); key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); seed = ch + seed + (seed << 5) + 3; *buffer32++ = ch; } } else { while (length-- > 0) { ch = *buffer32; seed += crypt_table[0x400 + (key & 0xFF)]; ch = ch ^ (key + seed); key = ((~key << 0x15) + 0x11111111) | (key >> 0x0B); seed = ch + seed + (seed << 5) + 3; *buffer32++ = ch; } } } uint32_t __mpqlib_hash_cstring(const char *string, uint32_t type) { uint32_t seed1 = 0x7FED7FED; uint32_t seed2 = 0xEEEEEEEE; uint32_t shifted_type = (type << 8); int32_t ch; assert(crypt_table_initialized); assert(string); while (*string != 0) { ch = *string++; if (ch > 0x60 && ch < 0x7b) ch -= 0x20; seed1 = crypt_table[shifted_type + ch] ^ (seed1 + seed2); seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3; } return seed1; } uint32_t __mpqlib_hash_data(const char *data, uint32_t length, uint32_t type) { uint32_t seed1 = 0x7FED7FED; uint32_t seed2 = 0xEEEEEEEE; uint32_t shifted_type = (type << 8); int32_t ch; assert(crypt_table_initialized); assert(data); while (length > 0) { ch = *data++; seed1 = crypt_table[shifted_type + ch] ^ (seed1 + seed2); seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3; length--; } return seed1; } void __mpqlib_crc32(const unsigned char *buffer, uint32_t length, uint32_t * crc, uint32_t flags) { uint32_t local_crc = 0; const uint32_t *crc_table = (uint32_t *) get_crc_table(); const unsigned char *buffer_end = buffer + length; if (crc) local_crc = *crc; if (flags & __MPQLIB_CRC_INIT) local_crc = 0xFFFFFFFF; if (flags & __MPQLIB_CRC_UPDATE) { while (buffer < buffer_end) { local_crc = ((local_crc >> 8) & 0x00FFFFFF) ^ crc_table[(local_crc ^ *buffer) & 0xFF]; buffer++; } } if (flags & __MPQLIB_CRC_FINALIZE) local_crc = local_crc ^ 0xFFFFFFFF; if (crc) *crc = local_crc; } #ifdef USE_SSL int __mpqlib_verify_weak_signature(RSA * public_key, const unsigned char *signature, const unsigned char *digest) { unsigned char reversed_signature[__MPQLIB_WEAK_SIGNATURE_SIZE]; memcpy(reversed_signature, signature + 8, __MPQLIB_WEAK_SIGNATURE_SIZE); memrev(reversed_signature, __MPQLIB_WEAK_SIGNATURE_SIZE); return RSA_verify(NID_md5, digest, MD5_DIGEST_LENGTH, reversed_signature, __MPQLIB_WEAK_SIGNATURE_SIZE, public_key); } int __mpqlib_verify_strong_signature(RSA * public_key, const unsigned char *signature, const unsigned char *digest) { unsigned char reversed_signature[__MPQLIB_STRONG_SIGNATURE_SIZE]; memcpy(reversed_signature, signature + 4, __MPQLIB_STRONG_SIGNATURE_SIZE); memrev(reversed_signature, __MPQLIB_STRONG_SIGNATURE_SIZE); unsigned char real_digest[__MPQLIB_STRONG_SIGNATURE_SIZE]; memset(real_digest, 0xbb, sizeof(real_digest)); real_digest[0] = 0x0b; uint32_t digest_offset = sizeof(real_digest) - SHA_DIGEST_LENGTH; memcpy(real_digest + digest_offset, digest, SHA_DIGEST_LENGTH); memrev(real_digest + digest_offset, SHA_DIGEST_LENGTH); RSA_public_decrypt(__MPQLIB_STRONG_SIGNATURE_SIZE, reversed_signature, reversed_signature, public_key, RSA_NO_PADDING); unsigned long error = ERR_get_error(); return (!error && memcmp(reversed_signature, real_digest, __MPQLIB_STRONG_SIGNATURE_SIZE) == 0); } #endif