From aeb01ce9025155bbfcc5d96462e5292dc3146c39 Mon Sep 17 00:00:00 2001 From: Rogiel Sulzbach Date: Thu, 7 Jan 2016 01:54:34 -0200 Subject: [PATCH] Improves testing and documentation --- .gitignore | 4 +- composer.json | 8 + phpunit.xml | 29 +++ src/Compression/BZIPCompression.php | 20 ++- src/Compression/Compression.php | 21 +++ src/Compression/DeflateCompression.php | 20 ++- src/Encryption/DefaultEncryption.php | 80 ++++++--- src/Encryption/Encryption.php | 33 ++++ .../Compression/CompressionException.php | 35 ++++ .../Compression/InvalidInputDataException.php | 34 ++++ .../Encryption/EncryptionException.php | 35 ++++ .../Encryption/InvalidBlockSizeException.php | 33 ++++ .../Encryption/InvalidKeyException.php | 33 ++++ src/Exception/Hashing/HashingException.php | 35 ++++ src/Exception/MPQException.php | 34 ++++ src/Metadata/Hash.php | 3 - tests/AbstractTestCase.php | 42 +++++ tests/Compression/BZIPCompressionTest.php | 86 +++++++++ tests/Compression/DeflateCompressionTest.php | 85 +++++++++ tests/Encryption/DefaultEncryptionTest.php | 122 +++++++++++++ tests/Hashing/FileKeyHashingTest.php | 63 +++++++ tests/Hashing/NameAHashingTest.php | 63 +++++++ tests/Hashing/NameBHashingTest.php | 63 +++++++ tests/Hashing/TableOffsetHashingTest.php | 63 +++++++ tests/Metadata/BlockTest.php | 170 ++++++++++++++++++ tests/Metadata/HashTest.php | 54 ++++++ tests/Metadata/UserDataTest.php | 55 ++++++ .../Stream/Parser/BinaryStreamParserTest.php | 82 +++++++++ 28 files changed, 1377 insertions(+), 28 deletions(-) create mode 100644 phpunit.xml create mode 100644 src/Exception/Compression/CompressionException.php create mode 100644 src/Exception/Compression/InvalidInputDataException.php create mode 100644 src/Exception/Encryption/EncryptionException.php create mode 100644 src/Exception/Encryption/InvalidBlockSizeException.php create mode 100644 src/Exception/Encryption/InvalidKeyException.php create mode 100644 src/Exception/Hashing/HashingException.php create mode 100644 src/Exception/MPQException.php create mode 100644 tests/AbstractTestCase.php create mode 100644 tests/Compression/BZIPCompressionTest.php create mode 100644 tests/Compression/DeflateCompressionTest.php create mode 100644 tests/Encryption/DefaultEncryptionTest.php create mode 100644 tests/Hashing/FileKeyHashingTest.php create mode 100644 tests/Hashing/NameAHashingTest.php create mode 100644 tests/Hashing/NameBHashingTest.php create mode 100644 tests/Hashing/TableOffsetHashingTest.php create mode 100644 tests/Metadata/BlockTest.php create mode 100644 tests/Metadata/HashTest.php create mode 100644 tests/Metadata/UserDataTest.php create mode 100644 tests/Stream/Parser/BinaryStreamParserTest.php diff --git a/.gitignore b/.gitignore index d1ec8e5..2e50f42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -# Created by .ignore support plugin (hsz.mobi) ### Composer template composer.phar vendor/ composer.lock +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio +.idea/ diff --git a/composer.json b/composer.json index f3caff2..10a18da 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,15 @@ "Rogiel\\MPQ\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "Rogiel\\MPQ\\Tests\\": "tests/" + } + }, "require": { "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.1" } } diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..f27ca37 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,29 @@ + + + + + + ./tests + + + + + + /src + + /src/Exception + + + + + \ No newline at end of file diff --git a/src/Compression/BZIPCompression.php b/src/Compression/BZIPCompression.php index 9cdfae7..c6137fd 100644 --- a/src/Compression/BZIPCompression.php +++ b/src/Compression/BZIPCompression.php @@ -29,14 +29,30 @@ namespace Rogiel\MPQ\Compression; +use Rogiel\MPQ\Exception\Compression\InvalidInputDataException; + class BZIPCompression implements Compression { + /** + * {@inheritdoc} + */ public function compress($data, $length) { - return bzcompress(substr($data, 0, $length)); + $output = @bzcompress(substr($data, 0, $length)); + if(!is_string($output)) { + throw new InvalidInputDataException('The compression input data is invalid.', $output); + } + return $output; } + /** + * {@inheritdoc} + */ public function decompress($data, $length) { - return bzdecompress(substr($data, 0, $length)); + $output = @bzdecompress(substr($data, 0, $length)); + if(!is_string($output)) { + throw new InvalidInputDataException('The decompression input data is invalid.', $output); + } + return $output; } } \ No newline at end of file diff --git a/src/Compression/Compression.php b/src/Compression/Compression.php index 0152e68..ce3e07f 100644 --- a/src/Compression/Compression.php +++ b/src/Compression/Compression.php @@ -29,9 +29,30 @@ namespace Rogiel\MPQ\Compression; +use Rogiel\MPQ\Exception\Compression\InvalidInputDataException; + interface Compression { + /** + * Compresses a block of data + * + * @param $data string the block of data to be compressed + * @param $length integer the length of the block to be compressed + * @return string the compressed data + * + * @throws InvalidInputDataException if the uncompressed data is invalid and cannot be compressed + */ public function compress($data, $length); + + /** + * Decompresses a block of data + * + * @param $data string the block of data to be decompressed + * @param $length integer the length of the block to be decompressed + * @return string the decompressed data + * + * @throws InvalidInputDataException if the compressed data is invalid and cannot be decompressed + */ public function decompress($data, $length); } \ No newline at end of file diff --git a/src/Compression/DeflateCompression.php b/src/Compression/DeflateCompression.php index b6dd345..3f7275d 100644 --- a/src/Compression/DeflateCompression.php +++ b/src/Compression/DeflateCompression.php @@ -29,14 +29,30 @@ namespace Rogiel\MPQ\Compression; +use Rogiel\MPQ\Exception\Compression\InvalidInputDataException; + class DeflateCompression implements Compression { + /** + * {@inheritdoc} + */ public function compress($data, $length) { - return gzdeflate(substr($data, 0, $length)); + $output = @gzdeflate(substr($data, 0, $length)); + if(!is_string($output)) { + throw new InvalidInputDataException('The compression input data is invalid.', $output); + } + return $output; } + /** + * {@inheritdoc} + */ public function decompress($data, $length) { - return gzinflate($data, $length); + $output = @gzinflate(substr($data, 0, $length), $length); + if(!is_string($output)) { + throw new InvalidInputDataException('The decompression input data is invalid.', $output); + } + return $output; } } \ No newline at end of file diff --git a/src/Encryption/DefaultEncryption.php b/src/Encryption/DefaultEncryption.php index 7a1d839..1dc38c6 100644 --- a/src/Encryption/DefaultEncryption.php +++ b/src/Encryption/DefaultEncryption.php @@ -29,56 +29,96 @@ namespace Rogiel\MPQ\Encryption; +use Rogiel\MPQ\Exception\Encryption\InvalidBlockSizeException; +use Rogiel\MPQ\Exception\Encryption\InvalidKeyException; use Rogiel\MPQ\Util\CryptoUtils; class DefaultEncryption implements Encryption { + /** + * The encryption/decryption key + * + * @var string + */ private $key; + + /** + * The encryption/decryption seed + * + * @var string + */ private $seed; + /** + * DefaultEncryption constructor. + * @param $key string the encryption key. The key must have exactly 10 bytes + */ public function __construct($key) { - $this->key = $key; - $this->seed = ((0xEEEE << 16) | 0xEEEE); CryptoUtils::initTable(); + $this->reset($key); } + /** + * {@inheritdoc} + */ public function reset($key) { + if(strlen($key) != 10) { + throw new InvalidKeyException(sprintf('The key is expected to have 10 bytes, %i given.', strlen($key))); + } + $this->key = $key; $this->seed = ((0xEEEE << 16) | 0xEEEE); } + /** + * {@inheritdoc} + */ public function decrypt($string, $length) { + if($length % 4 != 0) { + throw new InvalidBlockSizeException(sprintf('The block size is invalid. Input expected to be a multiple of 4, %s given', $length)); + } + $data = $this->createBlockArray($string, $length); - $datalen = $length / 4; - for($i = 0;$i < $datalen;$i++) { + $blocks = $length / 4; + for($block = 0;$block < $blocks; $block++) { $this->seed = CryptoUtils::uPlus($this->seed,CryptoUtils::$cryptTable[0x400 + ($this->key & 0xFF)]); - $ch = $data[$i] ^ (CryptoUtils::uPlus($this->key,$this->seed)); + $ch = $data[$block] ^ (CryptoUtils::uPlus($this->key,$this->seed)); $this->key = (CryptoUtils::uPlus(((~$this->key) << 0x15), 0x11111111)) | (CryptoUtils::rShift($this->key,0x0B)); $this->seed = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($ch,$this->seed),($this->seed << 5)),3); - $data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF); + $data[$block] = $ch & ((0xFFFF << 16) | 0xFFFF); } - return $this->createDataStream($data, $length / 4); + return $this->createDataStream($data, $length); } - public function encrypt($data, $length) { - $key = clone $this->key; - - $seed = ((0xEEEE << 16) | 0xEEEE); - $datalen = $length; - for($i = 0;$i < $datalen;$i++) { - $seed = CryptoUtils::uPlus($seed,CryptoUtils::$cryptTable[0x400 + ($key & 0xFF)]); - $ch = $data[$i] ^ (CryptoUtils::uPlus($key,$seed)); - - $key = (CryptoUtils::uPlus(((~$key) << 0x15), 0x11111111)) | (CryptoUtils::rShift($key,0x0B)); - $seed = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($data[$i],$seed),($seed << 5)),3); - $data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF); + /** + * {@inheritdoc} + */ + public function encrypt($string, $length) { + if($length % 4 != 0) { + throw new InvalidBlockSizeException(sprintf('The block size is invalid. Input expected to be a multiple of 4, %s given', $length)); } - return $data; + + $data = $this->createBlockArray($string, $length); + + $blocks = $length / 4; + for($block = 0;$block < $blocks; $block++) { + $this->seed = CryptoUtils::uPlus($this->seed,CryptoUtils::$cryptTable[0x400 + ($this->key & 0xFF)]); + $ch = $data[$block] ^ (CryptoUtils::uPlus($this->key,$this->seed)); + + $this->key = (CryptoUtils::uPlus(((~$this->key) << 0x15), 0x11111111)) | (CryptoUtils::rShift($this->key,0x0B)); + $this->seed = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($data[$block],$this->seed),($this->seed << 5)),3); + $data[$block] = $ch & ((0xFFFF << 16) | 0xFFFF); + } + + return $this->createDataStream($data, $length); } + /** + * {@inheritdoc} + */ public function getBlockSize() { return 4; } diff --git a/src/Encryption/Encryption.php b/src/Encryption/Encryption.php index 566cef1..efaa7ec 100644 --- a/src/Encryption/Encryption.php +++ b/src/Encryption/Encryption.php @@ -28,14 +28,47 @@ namespace Rogiel\MPQ\Encryption; +use Rogiel\MPQ\Exception\Encryption\InvalidBlockSizeException; +use Rogiel\MPQ\Exception\Encryption\InvalidKeyException; interface Encryption { + /** + * Resets the encryption context + * + * @param $key string the new encryption key + * + * @throws InvalidKeyException if the given key is invalid + */ public function reset($key); + /** + * Encrypts a block of data + * + * @param $data string the data block + * @param $length integer the data block size + * @return string the encrypted block + * + * @throws InvalidBlockSizeException if the block size is incorrect + */ public function encrypt($data, $length); + + /** + * Decrypts a block of data + * + * @param $data string the data block + * @param $length integer the data block size + * @return string the decrypted block + * + * @throws InvalidBlockSizeException if the block size is incorrect + */ public function decrypt($data, $length); + /** + * Gets the cipher block size + * + * @return integer + */ public function getBlockSize(); } \ No newline at end of file diff --git a/src/Exception/Compression/CompressionException.php b/src/Exception/Compression/CompressionException.php new file mode 100644 index 0000000..1e8b05b --- /dev/null +++ b/src/Exception/Compression/CompressionException.php @@ -0,0 +1,35 @@ +name1 = $parser->readUInt32(); $hash->name2 = $parser->readUInt32(); $hash->locale = $parser->readUInt16(); -// echo $hash->locale; -// die(); - $hash->platform = $parser->readUInt16(); $hash->blockIndex = $parser->readUInt32(); diff --git a/tests/AbstractTestCase.php b/tests/AbstractTestCase.php new file mode 100644 index 0000000..58fcaf5 --- /dev/null +++ b/tests/AbstractTestCase.php @@ -0,0 +1,42 @@ +compress( + self::TEST_UNCOMPRESSED_DATA, strlen(self::TEST_UNCOMPRESSED_DATA) + ); + + $this->assertEquals(hex2bin(self::TEST_COMPRESSED_DATA), $compressed); + } + + public function testDecompress() { + $compression = new BZIPCompression(); + $compressed = $compression->decompress( + hex2bin(self::TEST_COMPRESSED_DATA), strlen(hex2bin(self::TEST_COMPRESSED_DATA)) + ); + + $this->assertEquals(self::TEST_UNCOMPRESSED_DATA, $compressed); + } + + const TEST_EMPTY_COMPRESSED_DATA = "425a683417724538509000000000"; + + public function testEmptyCompress() { + $compression = new BZIPCompression(); + $compressed = $compression->compress("", 0); + $this->assertEquals(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), $compressed); + } + + public function testEmptyDecompress() { + $compression = new BZIPCompression(); + $compressed = $compression->decompress( + hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), strlen(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA)) + ); + $this->assertEquals("", $compressed); + } + + const TEST_INVALID_COMPRESSED_DATA = "425a6417724538509000000000"; + + public function testInvalidDecompress() { + $this->setExpectedException(InvalidInputDataException::class); + $compression = new BZIPCompression(); + $compression->decompress( + hex2bin(self::TEST_INVALID_COMPRESSED_DATA), strlen(hex2bin(self::TEST_INVALID_COMPRESSED_DATA)) + ); + } + + +} diff --git a/tests/Compression/DeflateCompressionTest.php b/tests/Compression/DeflateCompressionTest.php new file mode 100644 index 0000000..4b1a1fa --- /dev/null +++ b/tests/Compression/DeflateCompressionTest.php @@ -0,0 +1,85 @@ +compress( + self::TEST_UNCOMPRESSED_DATA, strlen(self::TEST_UNCOMPRESSED_DATA) + ); + + $this->assertEquals(hex2bin(self::TEST_COMPRESSED_DATA), $compressed); + } + + public function testDecompress() { + $compression = new DeflateCompression(); + $compressed = $compression->decompress( + hex2bin(self::TEST_COMPRESSED_DATA), strlen(hex2bin(self::TEST_COMPRESSED_DATA)) + ); + + $this->assertEquals(self::TEST_UNCOMPRESSED_DATA, $compressed); + } + + const TEST_EMPTY_COMPRESSED_DATA = "0300"; + + public function testEmptyCompress() { + $compression = new DeflateCompression(); + $compressed = $compression->compress("", 0); + $this->assertEquals(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), $compressed); + } + + public function testEmptyDecompress() { + $compression = new DeflateCompression(); + $compressed = $compression->decompress( + hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), strlen(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA)) + ); + $this->assertEquals("", $compressed); + } + + const TEST_INVALID_COMPRESSED_DATA = NULL; + + public function testInvalidDecompress() { + $this->setExpectedException(InvalidInputDataException::class); + $compression = new DeflateCompression(); + $compression->decompress( + hex2bin(self::TEST_INVALID_COMPRESSED_DATA), strlen(hex2bin(self::TEST_INVALID_COMPRESSED_DATA)) + ); + } + +} diff --git a/tests/Encryption/DefaultEncryptionTest.php b/tests/Encryption/DefaultEncryptionTest.php new file mode 100644 index 0000000..2c2ffed --- /dev/null +++ b/tests/Encryption/DefaultEncryptionTest.php @@ -0,0 +1,122 @@ +encrypt( + hex2bin(self::TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING), strlen(hex2bin(self::TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING)) + ); + $this->assertEquals(hex2bin(self::TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING), $cipher); + } + + public function testSingleBlockDencrypt() { + $encryption = new DefaultEncryption(hex2bin(self::TEST_KEY)); + $plain = $encryption->decrypt( + hex2bin(self::TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING), strlen(hex2bin(self::TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING)) + ); + $this->assertEquals(hex2bin(self::TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING), $plain); + } + + const TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING = "87d1d07454135b170e89045025ed1e8309ee2f14aaaab586d9e81505a8ba72c5"; + const TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING = "0144f97cc3492b88aed652bb914e8748c58871e1aea753ff04fe731f7cce9a38"; + + public function testMultipleBlocksEncrypt() { + $encryption = new DefaultEncryption(hex2bin(self::TEST_KEY)); + $cipher = $encryption->encrypt( + hex2bin(self::TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING), strlen(hex2bin(self::TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING)) + ); + $this->assertEquals(hex2bin(self::TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING), $cipher); + } + + public function testMultipleBlocksDencrypt() { + $encryption = new DefaultEncryption(hex2bin(self::TEST_KEY)); + $plain = $encryption->decrypt( + hex2bin(self::TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING), strlen(hex2bin(self::TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING)) + ); + $this->assertEquals(hex2bin(self::TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING), $plain); + } + + // ----------------------------------------------------------------------------------------------------------------- + + public function testBlocklSize() { + $encryption = new DefaultEncryption(hex2bin(self::TEST_KEY)); + $this->assertEquals(4, $encryption->getBlockSize()); + } + + // ----------------------------------------------------------------------------------------------------------------- + + const TEST_SMALL_KEY = "c89a7de0c5fea9ca"; + + public function testSmallKey() { + $this->setExpectedException(InvalidKeyException::class); + new DefaultEncryption(hex2bin(self::TEST_SMALL_KEY)); + } + + const TEST_LARGE_KEY = "c89a7de0c5fea9cac89a7de0c5fea9ca"; + + public function testLargeKey() { + $this->setExpectedException(InvalidKeyException::class); + new DefaultEncryption(hex2bin(self::TEST_LARGE_KEY)); + } + + const TEST_INVALID_BLOCK_SIZE_PLAIN_TEXT_STRING = "3ef8cc"; + const TEST_INVALID_BLOCK_SIZE_CIPHER_TEXT_STRING = "b86de5"; + + public function testInvalidBlockSizeEncrypt() { + $this->setExpectedException(InvalidBlockSizeException::class); + + $encryption = new DefaultEncryption(hex2bin(self::TEST_KEY)); + $plain = $encryption->encrypt( + hex2bin(self::TEST_INVALID_BLOCK_SIZE_PLAIN_TEXT_STRING), strlen(hex2bin(self::TEST_INVALID_BLOCK_SIZE_PLAIN_TEXT_STRING)) + ); + } + + public function testInvalidBlockSizeDencrypt() { + $this->setExpectedException(InvalidBlockSizeException::class); + + $encryption = new DefaultEncryption(hex2bin(self::TEST_KEY)); + $plain = $encryption->decrypt( + hex2bin(self::TEST_INVALID_BLOCK_SIZE_CIPHER_TEXT_STRING), strlen(hex2bin(self::TEST_INVALID_BLOCK_SIZE_CIPHER_TEXT_STRING)) + ); + } + +} diff --git a/tests/Hashing/FileKeyHashingTest.php b/tests/Hashing/FileKeyHashingTest.php new file mode 100644 index 0000000..2970107 --- /dev/null +++ b/tests/Hashing/FileKeyHashingTest.php @@ -0,0 +1,63 @@ +hashing = new FileKeyHashing(); + } + + const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592"; + const TEST_HASHED_TEXT = 3812245591; + + public function testHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT)); + $this->assertEquals(self::TEST_HASHED_TEXT, $hash); + } + + const TEST_EMPTY_TEXT = ""; + const TEST_HASHED_EMPTY = 2146271213; + + public function testEmptyHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT)); + $this->assertEquals(self::TEST_HASHED_EMPTY, $hash); + } + +} diff --git a/tests/Hashing/NameAHashingTest.php b/tests/Hashing/NameAHashingTest.php new file mode 100644 index 0000000..88f1a42 --- /dev/null +++ b/tests/Hashing/NameAHashingTest.php @@ -0,0 +1,63 @@ +hashing = new NameAHashing(); + } + + const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592"; + const TEST_HASHED_TEXT = 1258300789; + + public function testHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT)); + $this->assertEquals(self::TEST_HASHED_TEXT, $hash); + } + + const TEST_EMPTY_TEXT = ""; + const TEST_HASHED_EMPTY = 2146271213; + + public function testEmptyHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT)); + $this->assertEquals(self::TEST_HASHED_EMPTY, $hash); + } + +} diff --git a/tests/Hashing/NameBHashingTest.php b/tests/Hashing/NameBHashingTest.php new file mode 100644 index 0000000..85cba19 --- /dev/null +++ b/tests/Hashing/NameBHashingTest.php @@ -0,0 +1,63 @@ +hashing = new NameBHashing(); + } + + const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592"; + const TEST_HASHED_TEXT = 2342994917; + + public function testHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT)); + $this->assertEquals(self::TEST_HASHED_TEXT, $hash); + } + + const TEST_EMPTY_TEXT = ""; + const TEST_HASHED_EMPTY = 2146271213; + + public function testEmptyHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT)); + $this->assertEquals(self::TEST_HASHED_EMPTY, $hash); + } + +} diff --git a/tests/Hashing/TableOffsetHashingTest.php b/tests/Hashing/TableOffsetHashingTest.php new file mode 100644 index 0000000..3bb769c --- /dev/null +++ b/tests/Hashing/TableOffsetHashingTest.php @@ -0,0 +1,63 @@ +hashing = new TableOffsetHashing(); + } + + const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592"; + const TEST_HASHED_TEXT = 4211952812; + + public function testHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT)); + $this->assertEquals(self::TEST_HASHED_TEXT, $hash); + } + + const TEST_EMPTY_TEXT = ""; + const TEST_HASHED_EMPTY = 2146271213; + + public function testEmptyHash() { + $hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT)); + $this->assertEquals(self::TEST_HASHED_EMPTY, $hash); + } + +} diff --git a/tests/Metadata/BlockTest.php b/tests/Metadata/BlockTest.php new file mode 100644 index 0000000..547e051 --- /dev/null +++ b/tests/Metadata/BlockTest.php @@ -0,0 +1,170 @@ +createMemoryParser(hex2bin(self::TEST_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(8208, $block->getFilePos()); + $this->assertEquals(1048576, $block->getCompressedSize()); + $this->assertEquals(1048576, $block->getSize()); + $this->assertEquals(0, $block->getFlags()); + } + + const TEST_IMPLODED_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00010000"; /* flags */ + + public function testImplodedFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_IMPLODED_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isImploded()); + } + + const TEST_COMPRESSED_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00020000"; /* flags */ + + public function testCompressedFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_COMPRESSED_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isCompressed()); + } + + const TEST_ENCRYPTED_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00000100"; /* flags */ + + public function testEncryptedFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_ENCRYPTED_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isEncrypted()); + } + + const TEST_FIX_KEY_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00000200"; /* flags */ + + public function testKeyBasedOnPositionFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_FIX_KEY_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isKeyBasedOnPosition()); + } + + const TEST_PATCHED_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00001000"; /* flags */ + + public function testPatchedFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_PATCHED_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isPatched()); + } + + const TEST_SINGLE_UNIT_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00000001"; /* flags */ + + public function testSingleUnitFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_SINGLE_UNIT_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isSingleUnit()); + } + + const TEST_DELETE_MARKER_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00000002"; /* flags */ + + public function testDeleteMarkerFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_DELETE_MARKER_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isDeleted()); + } + + const TEST_CHECKSUMED_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00000004"; /* flags */ + + public function testChecksumedFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_CHECKSUMED_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isChecksumed()); + } + + const TEST_EXISTS_BLOCK_DATA = + "10200000". /* file position */ + "00001000". /* compressed size */ + "00001000". /* size */ + "00000080"; /* flags */ + + public function testExistsFlag() { + $parser = $this->createMemoryParser(hex2bin(self::TEST_EXISTS_BLOCK_DATA)); + $block = Block::parse($parser); + + $this->assertEquals(true, $block->isExisting()); + } + +} \ No newline at end of file diff --git a/tests/Metadata/HashTest.php b/tests/Metadata/HashTest.php new file mode 100644 index 0000000..5546c73 --- /dev/null +++ b/tests/Metadata/HashTest.php @@ -0,0 +1,54 @@ +createMemoryParser(hex2bin(self::TEST_HASH_DATA)); + $hash = Hash::parse($parser); + + $this->assertEquals(8208, $hash->getName1()); + $this->assertEquals(1048576, $hash->getName2()); + $this->assertEquals(0, $hash->getLocale()); + $this->assertEquals(16, $hash->getPlatform()); + $this->assertEquals(44510, $hash->getBlockIndex()); + } + +} \ No newline at end of file diff --git a/tests/Metadata/UserDataTest.php b/tests/Metadata/UserDataTest.php new file mode 100644 index 0000000..c667457 --- /dev/null +++ b/tests/Metadata/UserDataTest.php @@ -0,0 +1,55 @@ +createMemoryParser(hex2bin(self::TEST_USER_DATA)); + $userData = UserData::parse($parser); + + $this->assertEquals(512, $userData->getSize()); + $this->assertEquals(1024, $userData->getHeaderOffset()); + $this->assertEquals(1, $userData->getHeader()); + $this->assertEquals(hex2bin(self::TEST_USER_RAW_DATA), $userData->getRawContent()); + } + +} diff --git a/tests/Stream/Parser/BinaryStreamParserTest.php b/tests/Stream/Parser/BinaryStreamParserTest.php new file mode 100644 index 0000000..48ba73c --- /dev/null +++ b/tests/Stream/Parser/BinaryStreamParserTest.php @@ -0,0 +1,82 @@ +createMemoryParser( + hex2bin("00FF") + ); + $this->assertEquals(0x00, $parser->readByte()); + $this->assertEquals(0xFF, $parser->readByte()); + } + + public function testReadBytes() { + $parser = $this->createMemoryParser( + hex2bin("00FF") + ); + $this->assertEquals(hex2bin("00FF"), $parser->readBytes(2)); + } + + public function testReadUInt32() { + $parser = $this->createMemoryParser( + hex2bin("0000BEEF") + ); + $this->assertEquals(4022206464, $parser->readUInt32()); + } + + public function testReadUInt16() { + $parser = $this->createMemoryParser( + hex2bin("0E0A") + ); + $this->assertEquals(2574, $parser->readUInt16()); + } + + public function testSkip() { + $parser = $this->createMemoryParser( + hex2bin("DEADBEEF") + ); + $parser->skip(2); + $this->assertEquals(0xBE, $parser->readByte()); + } + + public function testSeek() { + $parser = $this->createMemoryParser( + hex2bin("DEADBEEF") + ); + $parser->seek(3); + $this->assertEquals(0xEF, $parser->readByte()); + } + + +}